Bodil Stokke

This commit is contained in:
Jakub Fojtl
2015-04-11 20:35:16 +02:00
parent 05a6aa6520
commit a1fb71b7d8
107 changed files with 51635 additions and 1 deletions

View File

@@ -0,0 +1,7 @@
/node_modules
#*
*~
.tern-port
npm-debug.log
/old
/build

View File

@@ -0,0 +1,596 @@
GNU GENERAL PUBLIC LICENSE
==========================
Version 3, 29 June 2007
Copyright &copy; 2007 Free Software Foundation, Inc. &lt;<http://fsf.org/>&gt;
Everyone is permitted to copy and distribute verbatim copies of this license
document, but changing it is not allowed.
## Preamble
The GNU General Public License is a free, copyleft license for software and other
kinds of works.
The licenses for most software and other practical works are designed to take away
your freedom to share and change the works. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change all versions of a
program--to make sure it remains free software for all its users. We, the Free
Software Foundation, use the GNU General Public License for most of our software; it
applies also to any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General
Public Licenses are designed to make sure that you have the freedom to distribute
copies of free software (and charge for them if you wish), that you receive source
code or can get it if you want it, that you can change the software or use pieces of
it in new free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you these rights or
asking you to surrender the rights. Therefore, you have certain responsibilities if
you distribute copies of the software, or if you modify it: responsibilities to
respect the freedom of others.
For example, if you distribute copies of such a program, whether gratis or for a fee,
you must pass on to the recipients the same freedoms that you received. You must make
sure that they, too, receive or can get the source code. And you must show them these
terms so they know their rights.
Developers that use the GNU GPL protect your rights with two steps: (1) assert
copyright on the software, and (2) offer you this License giving you legal permission
to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains that there is
no warranty for this free software. For both users' and authors' sake, the GPL
requires that modified versions be marked as changed, so that their problems will not
be attributed erroneously to authors of previous versions.
Some devices are designed to deny users access to install or run modified versions of
the software inside them, although the manufacturer can do so. This is fundamentally
incompatible with the aim of protecting users' freedom to change the software. The
systematic pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we have designed
this version of the GPL to prohibit the practice for those products. If such problems
arise substantially in other domains, we stand ready to extend this provision to
those domains in future versions of the GPL, as needed to protect the freedom of
users.
Finally, every program is threatened constantly by software patents. States should
not allow patents to restrict development and use of software on general-purpose
computers, but in those that do, we wish to avoid the special danger that patents
applied to a free program could make it effectively proprietary. To prevent this, the
GPL assures that patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and modification follow.
## TERMS AND CONDITIONS
### 0. Definitions.
&ldquo;This License&rdquo; refers to version 3 of the GNU General Public License.
&ldquo;Copyright&rdquo; also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
&ldquo;The Program&rdquo; refers to any copyrightable work licensed under this
License. Each licensee is addressed as &ldquo;you&rdquo;. &ldquo;Licensees&rdquo; and
&ldquo;recipients&rdquo; may be individuals or organizations.
To &ldquo;modify&rdquo; a work means to copy from or adapt all or part of the work in
a fashion requiring copyright permission, other than the making of an exact copy. The
resulting work is called a &ldquo;modified version&rdquo; of the earlier work or a
work &ldquo;based on&rdquo; the earlier work.
A &ldquo;covered work&rdquo; means either the unmodified Program or a work based on
the Program.
To &ldquo;propagate&rdquo; a work means to do anything with it that, without
permission, would make you directly or secondarily liable for infringement under
applicable copyright law, except executing it on a computer or modifying a private
copy. Propagation includes copying, distribution (with or without modification),
making available to the public, and in some countries other activities as well.
To &ldquo;convey&rdquo; a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through a computer
network, with no transfer of a copy, is not conveying.
An interactive user interface displays &ldquo;Appropriate Legal Notices&rdquo; to the
extent that it includes a convenient and prominently visible feature that (1)
displays an appropriate copyright notice, and (2) tells the user that there is no
warranty for the work (except to the extent that warranties are provided), that
licensees may convey the work under this License, and how to view a copy of this
License. If the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
### 1. Source Code.
The &ldquo;source code&rdquo; for a work means the preferred form of the work for
making modifications to it. &ldquo;Object code&rdquo; means any non-source form of a
work.
A &ldquo;Standard Interface&rdquo; means an interface that either is an official
standard defined by a recognized standards body, or, in the case of interfaces
specified for a particular programming language, one that is widely used among
developers working in that language.
The &ldquo;System Libraries&rdquo; of an executable work include anything, other than
the work as a whole, that (a) is included in the normal form of packaging a Major
Component, but which is not part of that Major Component, and (b) serves only to
enable use of the work with that Major Component, or to implement a Standard
Interface for which an implementation is available to the public in source code form.
A &ldquo;Major Component&rdquo;, in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system (if any) on which
the executable work runs, or a compiler used to produce the work, or an object code
interpreter used to run it.
The &ldquo;Corresponding Source&rdquo; for a work in object code form means all the
source code needed to generate, install, and (for an executable work) run the object
code and to modify the work, including scripts to control those activities. However,
it does not include the work's System Libraries, or general-purpose tools or
generally available free programs which are used unmodified in performing those
activities but which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for the work, and
the source code for shared libraries and dynamically linked subprograms that the work
is specifically designed to require, such as by intimate data communication or
control flow between those subprograms and other parts of the work.
The Corresponding Source need not include anything that users can regenerate
automatically from other parts of the Corresponding Source.
The Corresponding Source for a work in source code form is that same work.
### 2. Basic Permissions.
All rights granted under this License are granted for the term of copyright on the
Program, and are irrevocable provided the stated conditions are met. This License
explicitly affirms your unlimited permission to run the unmodified Program. The
output from running a covered work is covered by this License only if the output,
given its content, constitutes a covered work. This License acknowledges your rights
of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not convey, without
conditions so long as your license otherwise remains in force. You may convey covered
works to others for the sole purpose of having them make modifications exclusively
for you, or provide you with facilities for running those works, provided that you
comply with the terms of this License in conveying all material for which you do not
control copyright. Those thus making or running the covered works for you must do so
exclusively on your behalf, under your direction and control, on terms that prohibit
them from making any copies of your copyrighted material outside their relationship
with you.
Conveying under any other circumstances is permitted solely under the conditions
stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological measure under any
applicable law fulfilling obligations under article 11 of the WIPO copyright treaty
adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention
of such measures.
When you convey a covered work, you waive any legal power to forbid circumvention of
technological measures to the extent such circumvention is effected by exercising
rights under this License with respect to the covered work, and you disclaim any
intention to limit operation or modification of the work as a means of enforcing,
against the work's users, your or third parties' legal rights to forbid circumvention
of technological measures.
### 4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you receive it, in any
medium, provided that you conspicuously and appropriately publish on each copy an
appropriate copyright notice; keep intact all notices stating that this License and
any non-permissive terms added in accord with section 7 apply to the code; keep
intact all notices of the absence of any warranty; and give all recipients a copy of
this License along with the Program.
You may charge any price or no price for each copy that you convey, and you may offer
support or warranty protection for a fee.
### 5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to produce it from
the Program, in the form of source code under the terms of section 4, provided that
you also meet all of these conditions:
* **a)** The work must carry prominent notices stating that you modified it, and giving a
relevant date.
* **b)** The work must carry prominent notices stating that it is released under this
License and any conditions added under section 7. This requirement modifies the
requirement in section 4 to &ldquo;keep intact all notices&rdquo;.
* **c)** You must license the entire work, as a whole, under this License to anyone who
comes into possession of a copy. This License will therefore apply, along with any
applicable section 7 additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no permission to license the
work in any other way, but it does not invalidate such permission if you have
separately received it.
* **d)** If the work has interactive user interfaces, each must display Appropriate Legal
Notices; however, if the Program has interactive interfaces that do not display
Appropriate Legal Notices, your work need not make them do so.
A compilation of a covered work with other separate and independent works, which are
not by their nature extensions of the covered work, and which are not combined with
it such as to form a larger program, in or on a volume of a storage or distribution
medium, is called an &ldquo;aggregate&rdquo; if the compilation and its resulting
copyright are not used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work in an aggregate
does not cause this License to apply to the other parts of the aggregate.
### 6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms of sections 4 and
5, provided that you also convey the machine-readable Corresponding Source under the
terms of this License, in one of these ways:
* **a)** Convey the object code in, or embodied in, a physical product (including a
physical distribution medium), accompanied by the Corresponding Source fixed on a
durable physical medium customarily used for software interchange.
* **b)** Convey the object code in, or embodied in, a physical product (including a
physical distribution medium), accompanied by a written offer, valid for at least
three years and valid for as long as you offer spare parts or customer support for
that product model, to give anyone who possesses the object code either (1) a copy of
the Corresponding Source for all the software in the product that is covered by this
License, on a durable physical medium customarily used for software interchange, for
a price no more than your reasonable cost of physically performing this conveying of
source, or (2) access to copy the Corresponding Source from a network server at no
charge.
* **c)** Convey individual copies of the object code with a copy of the written offer to
provide the Corresponding Source. This alternative is allowed only occasionally and
noncommercially, and only if you received the object code with such an offer, in
accord with subsection 6b.
* **d)** Convey the object code by offering access from a designated place (gratis or for
a charge), and offer equivalent access to the Corresponding Source in the same way
through the same place at no further charge. You need not require recipients to copy
the Corresponding Source along with the object code. If the place to copy the object
code is a network server, the Corresponding Source may be on a different server
(operated by you or a third party) that supports equivalent copying facilities,
provided you maintain clear directions next to the object code saying where to find
the Corresponding Source. Regardless of what server hosts the Corresponding Source,
you remain obligated to ensure that it is available for as long as needed to satisfy
these requirements.
* **e)** Convey the object code using peer-to-peer transmission, provided you inform
other peers where the object code and Corresponding Source of the work are being
offered to the general public at no charge under subsection 6d.
A separable portion of the object code, whose source code is excluded from the
Corresponding Source as a System Library, need not be included in conveying the
object code work.
A &ldquo;User Product&rdquo; is either (1) a &ldquo;consumer product&rdquo;, which
means any tangible personal property which is normally used for personal, family, or
household purposes, or (2) anything designed or sold for incorporation into a
dwelling. In determining whether a product is a consumer product, doubtful cases
shall be resolved in favor of coverage. For a particular product received by a
particular user, &ldquo;normally used&rdquo; refers to a typical or common use of
that class of product, regardless of the status of the particular user or of the way
in which the particular user actually uses, or expects or is expected to use, the
product. A product is a consumer product regardless of whether the product has
substantial commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
&ldquo;Installation Information&rdquo; for a User Product means any methods,
procedures, authorization keys, or other information required to install and execute
modified versions of a covered work in that User Product from a modified version of
its Corresponding Source. The information must suffice to ensure that the continued
functioning of the modified object code is in no case prevented or interfered with
solely because modification has been made.
If you convey an object code work under this section in, or with, or specifically for
use in, a User Product, and the conveying occurs as part of a transaction in which
the right of possession and use of the User Product is transferred to the recipient
in perpetuity or for a fixed term (regardless of how the transaction is
characterized), the Corresponding Source conveyed under this section must be
accompanied by the Installation Information. But this requirement does not apply if
neither you nor any third party retains the ability to install modified object code
on the User Product (for example, the work has been installed in ROM).
The requirement to provide Installation Information does not include a requirement to
continue to provide support service, warranty, or updates for a work that has been
modified or installed by the recipient, or for the User Product in which it has been
modified or installed. Access to a network may be denied when the modification itself
materially and adversely affects the operation of the network or violates the rules
and protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided, in accord with
this section must be in a format that is publicly documented (and with an
implementation available to the public in source code form), and must require no
special password or key for unpacking, reading or copying.
### 7. Additional Terms.
&ldquo;Additional permissions&rdquo; are terms that supplement the terms of this
License by making exceptions from one or more of its conditions. Additional
permissions that are applicable to the entire Program shall be treated as though they
were included in this License, to the extent that they are valid under applicable
law. If additional permissions apply only to part of the Program, that part may be
used separately under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option remove any
additional permissions from that copy, or from any part of it. (Additional
permissions may be written to require their own removal in certain cases when you
modify the work.) You may place additional permissions on material, added by you to a
covered work, for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you add to a
covered work, you may (if authorized by the copyright holders of that material)
supplement the terms of this License with terms:
* **a)** Disclaiming warranty or limiting liability differently from the terms of
sections 15 and 16 of this License; or
* **b)** Requiring preservation of specified reasonable legal notices or author
attributions in that material or in the Appropriate Legal Notices displayed by works
containing it; or
* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that
modified versions of such material be marked in reasonable ways as different from the
original version; or
* **d)** Limiting the use for publicity purposes of names of licensors or authors of the
material; or
* **e)** Declining to grant rights under trademark law for use of some trade names,
trademarks, or service marks; or
* **f)** Requiring indemnification of licensors and authors of that material by anyone
who conveys the material (or modified versions of it) with contractual assumptions of
liability to the recipient, for any liability that these contractual assumptions
directly impose on those licensors and authors.
All other non-permissive additional terms are considered &ldquo;further
restrictions&rdquo; within the meaning of section 10. If the Program as you received
it, or any part of it, contains a notice stating that it is governed by this License
along with a term that is a further restriction, you may remove that term. If a
license document contains a further restriction but permits relicensing or conveying
under this License, you may add to a covered work material governed by the terms of
that license document, provided that the further restriction does not survive such
relicensing or conveying.
If you add terms to a covered work in accord with this section, you must place, in
the relevant source files, a statement of the additional terms that apply to those
files, or a notice indicating where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the form of a
separately written license, or stated as exceptions; the above requirements apply
either way.
### 8. Termination.
You may not propagate or modify a covered work except as expressly provided under
this License. Any attempt otherwise to propagate or modify it is void, and will
automatically terminate your rights under this License (including any patent licenses
granted under the third paragraph of section 11).
However, if you cease all violation of this License, then your license from a
particular copyright holder is reinstated (a) provisionally, unless and until the
copyright holder explicitly and finally terminates your license, and (b) permanently,
if the copyright holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is reinstated permanently
if the copyright holder notifies you of the violation by some reasonable means, this
is the first time you have received notice of violation of this License (for any
work) from that copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the licenses of
parties who have received copies or rights from you under this License. If your
rights have been terminated and not permanently reinstated, you do not qualify to
receive new licenses for the same material under section 10.
### 9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or run a copy of the
Program. Ancillary propagation of a covered work occurring solely as a consequence of
using peer-to-peer transmission to receive a copy likewise does not require
acceptance. However, nothing other than this License grants you permission to
propagate or modify any covered work. These actions infringe copyright if you do not
accept this License. Therefore, by modifying or propagating a covered work, you
indicate your acceptance of this License to do so.
### 10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically receives a license
from the original licensors, to run, modify and propagate that work, subject to this
License. You are not responsible for enforcing compliance by third parties with this
License.
An &ldquo;entity transaction&rdquo; is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an organization, or
merging organizations. If propagation of a covered work results from an entity
transaction, each party to that transaction who receives a copy of the work also
receives whatever licenses to the work the party's predecessor in interest had or
could give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if the predecessor
has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the rights granted or
affirmed under this License. For example, you may not impose a license fee, royalty,
or other charge for exercise of rights granted under this License, and you may not
initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging
that any patent claim is infringed by making, using, selling, offering for sale, or
importing the Program or any portion of it.
### 11. Patents.
A &ldquo;contributor&rdquo; is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The work thus
licensed is called the contributor's &ldquo;contributor version&rdquo;.
A contributor's &ldquo;essential patent claims&rdquo; are all patent claims owned or
controlled by the contributor, whether already acquired or hereafter acquired, that
would be infringed by some manner, permitted by this License, of making, using, or
selling its contributor version, but do not include claims that would be infringed
only as a consequence of further modification of the contributor version. For
purposes of this definition, &ldquo;control&rdquo; includes the right to grant patent
sublicenses in a manner consistent with the requirements of this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license
under the contributor's essential patent claims, to make, use, sell, offer for sale,
import and otherwise run, modify and propagate the contents of its contributor
version.
In the following three paragraphs, a &ldquo;patent license&rdquo; is any express
agreement or commitment, however denominated, not to enforce a patent (such as an
express permission to practice a patent or covenant not to sue for patent
infringement). To &ldquo;grant&rdquo; such a patent license to a party means to make
such an agreement or commitment not to enforce a patent against the party.
If you convey a covered work, knowingly relying on a patent license, and the
Corresponding Source of the work is not available for anyone to copy, free of charge
and under the terms of this License, through a publicly available network server or
other readily accessible means, then you must either (1) cause the Corresponding
Source to be so available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner consistent with
the requirements of this License, to extend the patent license to downstream
recipients. &ldquo;Knowingly relying&rdquo; means you have actual knowledge that, but
for the patent license, your conveying the covered work in a country, or your
recipient's use of the covered work in a country, would infringe one or more
identifiable patents in that country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or arrangement, you
convey, or propagate by procuring conveyance of, a covered work, and grant a patent
license to some of the parties receiving the covered work authorizing them to use,
propagate, modify or convey a specific copy of the covered work, then the patent
license you grant is automatically extended to all recipients of the covered work and
works based on it.
A patent license is &ldquo;discriminatory&rdquo; if it does not include within the
scope of its coverage, prohibits the exercise of, or is conditioned on the
non-exercise of one or more of the rights that are specifically granted under this
License. You may not convey a covered work if you are a party to an arrangement with
a third party that is in the business of distributing software, under which you make
payment to the third party based on the extent of your activity of conveying the
work, and under which the third party grants, to any of the parties who would receive
the covered work from you, a discriminatory patent license (a) in connection with
copies of the covered work conveyed by you (or copies made from those copies), or (b)
primarily for and in connection with specific products or compilations that contain
the covered work, unless you entered into that arrangement, or that patent license
was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting any implied
license or other defenses to infringement that may otherwise be available to you
under applicable patent law.
### 12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or otherwise)
that contradict the conditions of this License, they do not excuse you from the
conditions of this License. If you cannot convey a covered work so as to satisfy
simultaneously your obligations under this License and any other pertinent
obligations, then as a consequence you may not convey it at all. For example, if you
agree to terms that obligate you to collect a royalty for further conveying from
those to whom you convey the Program, the only way you could satisfy both those terms
and this License would be to refrain entirely from conveying the Program.
### 13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have permission to link or
combine any covered work with a work licensed under version 3 of the GNU Affero
General Public License into a single combined work, and to convey the resulting work.
The terms of this License will continue to apply to the part which is the covered
work, but the special requirements of the GNU Affero General Public License, section
13, concerning interaction through a network will apply to the combination as such.
### 14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of the GNU
General Public License from time to time. Such new versions will be similar in spirit
to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program specifies that
a certain numbered version of the GNU General Public License &ldquo;or any later
version&rdquo; applies to it, you have the option of following the terms and
conditions either of that numbered version or of any later version published by the
Free Software Foundation. If the Program does not specify a version number of the GNU
General Public License, you may choose any version ever published by the Free
Software Foundation.
If the Program specifies that a proxy can decide which future versions of the GNU
General Public License can be used, that proxy's public statement of acceptance of a
version permanently authorizes you to choose that version for the Program.
Later license versions may give you additional or different permissions. However, no
additional obligations are imposed on any author or copyright holder as a result of
your choosing to follow a later version.
### 15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM &ldquo;AS IS&rdquo; WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
### 16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
### 17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided above cannot be
given local legal effect according to their terms, reviewing courts shall apply local
law that most closely approximates an absolute waiver of all civil liability in
connection with the Program, unless a warranty or assumption of liability accompanies
a copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
## How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible use to
the public, the best way to achieve this is to make it free software which everyone
can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach them
to the start of each source file to most effectively state the exclusion of warranty;
and each file should have at least the &ldquo;copyright&rdquo; line and a pointer to
where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like this
when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'show c' for details.
The hypothetical commands 'show w' and 'show c' should show the appropriate parts of
the General Public License. Of course, your program's commands might be different;
for a GUI interface, you would use an &ldquo;about box&rdquo;.
You should also get your employer (if you work as a programmer) or school, if any, to
sign a &ldquo;copyright disclaimer&rdquo; for the program, if necessary. For more
information on this, and how to apply and follow the GNU GPL, see
&lt;<http://www.gnu.org/licenses/>&gt;.
The GNU General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may consider it
more useful to permit linking proprietary applications with the library. If this is
what you want to do, use the GNU Lesser General Public License instead of this
License. But first, please read
&lt;<http://www.gnu.org/philosophy/why-not-lgpl.html>&gt;.

View File

@@ -0,0 +1,48 @@
What Every Hipster Should Know About Functional Reactive Programming
====================================================================
Original source at [github.com/bodil/boogaloo](https://github.com/bodil/boogaloo).
It is a slide deck. Much code.
Navigate through slides using `PgUp` and `PgDn`.
Editor cheat sheet:
* `Ctrl-S` evaluates the whole buffer.
* `Ctrl-D` evaluates the top level expression at cursor point.
* `Ctrl-R` reloads the HTML document without evaluating the buffer.
* `Ctrl-,` toggles commenting of the current line or selection.
* `Ctrl-'` selects the token at point.
* `Ctrl-\` invokes autocomplete on the token at point.
* `Ctrl-I` shows the inferred type of the token at point.
* `Ctrl-Q` renames the variable at point.
* `Alt-.` jumps to the definition of the token at point.
* `Alt-,` jumps back.
* `Ctrl-K` and `Ctrl-Y` kills to end of line and yanks from the kill buffer as in Most Holy Emacs.
* `Ctrl-A` and `Ctrl-E` similarly moves cursor to start and end of line.
* `Tab` will indent the current line to where it's supposed to be.
* `Alt-Space` sends a space bar keypress event to the HTML document.
* etc. for enter and cursor keys.
* Finally, `Esc` moves focus from the editor back to the slide deck, so you can change slides etc. (Also, if the slide deck is active, `Tab` should focus the editor.)
License
-------
Unless otherwise noted in source code, the GPL v3 applies:
Copyright 2014 Bodil Stokke
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).

View File

@@ -0,0 +1,29 @@
/*global Buffer */
var traceur = require("traceur");
function throwErrors(errs) {
return "throw new Error(\'ES6 compilation failed.\\n" +
errs.map(function(l) {
return l.replace(/'/g, "\\\'").replace(/"/g, "\\\"");
}).join("\\n") + "\');\n";
}
module.exports = function(source) {
var callback = this.async();
this.cacheable();
var out = traceur.compile(source, {
filename: this.resourcePath,
modules: "commonjs",
sourceMap: true,
blockBinding: true,
symbols: true,
deferredFunctions: true,
types: true,
annotations: true
});
out.errors.forEach(function(err) {
this.emitError(err);
}, this);
this.callback(null, out.js || throwErrors(out.errors), out.sourceMap);
};

View File

@@ -0,0 +1,49 @@
var OccurenceOrderPlugin = require("webpack/lib/optimize/OccurenceOrderPlugin");
var DedupePlugin = require("webpack/lib/optimize/DedupePlugin");
var UglifyPlugin = require("webpack/lib/optimize/UglifyJsPlugin");
var srcPath = require("path").join(__dirname, "..", "src");
var buildPath = require("path").join(__dirname, "..", "build");
module.exports = {
bail: true,
cache: true,
context: srcPath,
entry: "./main.es6",
output: {
path: buildPath,
filename: "slide.js",
publicPath: "build/"
},
module: {
loaders: [
{ test: /\.less$/, loader: "style!css!less" },
{ test: /\.css$/, loader: "style!css" },
{ test: /\.json$/, loader: "json" },
{ test: /\.png$/, loader: "url?limit=10000&mimetype=image/png" },
{ test: /\.svg$/, loader: "url?limit=10000&mimetype=image/svg" },
{ test: /\.otf$/, loader: "url?limit=10000&mimetype=application/x-font-otf" },
{ test: /\.ttf$/, loader: "url?limit=10000&mimetype=application/x-font-ttf" },
{ test: /\.mp3$/, loader: "url?limit=10000&mimetype=audio/mpeg" },
{ test: /\.html$/, loader: "url?limit=10000&mimetype=text/html" },
{ test: /\.es6$/, loader: require("path").join(__dirname, "es6-loader.js") }
]
},
devtool: "source-map",
resolve: {
extensions: ["", ".es6", ".js"]
// Webpack seems to have trouble resolving some Rx modules; workaround:
// fallback: require("path").join(__dirname, "..", "node_modules/rx")
},
node: {
"global": true,
"process": true,
"__filename": true,
"__dirname": true
},
plugins: [
new OccurenceOrderPlugin(),
new DedupePlugin(),
new UglifyPlugin()
]
};

View File

@@ -0,0 +1,161 @@
/*global React, Rx, Audio */
function deepCopy(target, obj) {
for (let prop in obj)
if (obj.hasOwnProperty(prop))
if (typeof obj[prop] === "object")
target[prop] = deepCopy({}, obj[prop]);
else target[prop] = obj[prop];
}
function assoc(...args) {
let out = {};
for (let i = 0; i < args.length; i++)
deepCopy(out, args[i]);
return out;
}
function bounds(me) {
return {
x1: me.x + (me.baseX || 0),
y1: me.y + (me.baseY || 0),
x2: me.x + (me.baseX || 0) + 100,
y2: me.y + (me.baseY || 0) + 100
};
}
function intersects(me, target) {
let b1 = bounds(me), b2 = bounds(target);
return !(b2.x1 > b1.x2 || b2.x2 < b1.x1 ||
b2.y1 > b1.y2 || b2.y2 < b1.y1);
}
const canvas = document.getElementById("canvas");
function onscreen(node) {
return !(node.x < -300 || node.y < -1000 || node.y > 1000);
}
function makeElement(node) {
return React.DOM.div({
className: node.id,
style: {
left: Math.floor(node.x + (node.baseX || 0)) + "px",
top: Math.floor(node.y + (node.baseY || 0)) + "px"
}
});
}
function renderScene(nodes) {
return React.renderComponent(
React.DOM.div(null, nodes.map(makeElement)),
canvas
);
}
function bindKey(key) {
let sub = new Rx.Subject();
Mousetrap.bind(key, () => {
sub.onNext(key);
});
return sub;
}
let groundStream = Rx.Observable.interval(33)
.map((x) => ({
id: "ground",
baseX: -128,
x: ((x % 64) * -8), y: 384
}));
function velocity(node) {
return assoc(node, {
x: node.x + node.vx,
y: node.y + node.vy
});
}
let tick = bindKey("space")
.buffer(Rx.Observable.interval(33));
let initialHater = {
id: "hater",
vx: -8, vy: 0,
x: 1600, y: 300
};
let haterStream = tick.scan(initialHater, (c, keys) => {
// Apply velocity to position.
c = velocity(c);
return onscreen(c) ? c : initialHater;
});
let pinkieStream = Rx.Observable.zipArray(tick, haterStream).scan({
id: "pinkie",
baseY: 276,
x: 0, y: 0,
vx: 0, vy: 0,
gameOver: false
}, (p, [keys, hater]) => {
p = velocity(p);
if (intersects(p, hater)) {
p.gameOver = true;
p.id = "pinkie gameover";
new Audio("sfx/gameover.mp3").play();
p.vy = -15;
}
if (p.gameOver) {
p.vy += 0.5;
return p;
}
// Apply gravity to Pinkie's velocity.
p.vy += 0.98;
// AS Pinkie Pie,
// GIVEN that I'm falling
// WHEN I hit the ground
// THEN I stop.
if (p.y >= 0 && p.vy > 0) {
p.y = 0; p.vy = 0;
}
// If Pinkie is on the ground and space has been pressed, JUMP.
if (keys[0] === "space") {
// p.vy = -20; // wheeee infinite jump
if (p.y === 0) {
p.vy = -20;
new Audio("sfx/jump.mp3").play();
}
}
p.id = (p.y < 0) ? "pinkie jumping" : "pinkie";
return p;
}).takeWhile(onscreen);
let initialCoin = {
id: "coin",
vx: -6, vy: 0,
x: 1600, y: 40
};
let coinStream = pinkieStream.scan(initialCoin, (c, pinkie) => {
// Apply velocity to position.
c = velocity(c);
// If Pinkie touches the coin, ding it!
if (c.vy === 0 && intersects(c, pinkie)) {
new Audio("sfx/coin.mp3").play();
c.vx = 0; c.vy = -1;
}
if (c.vy < 0) {
c.vy = c.vy * 2;
}
return onscreen(c) ? c : initialCoin;
});
Rx.Observable.zipArray(pinkieStream, groundStream, coinStream, haterStream)
.subscribe(renderScene);

View File

@@ -0,0 +1,65 @@
*, *:before, *:after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
border: 0;
}
#canvas {
position: absolute;
top: 0;
left: 0;
width: 512px;
height: 512px;
background: url(gfx/clouds.png);
background-size: cover;
overflow: hidden;
-moz-transform-origin: 0 0;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
.pinkie {
position: absolute;
width: 140px;
height: 126px;
top: 276px;
left: 64px;
background: url(gfx/pie-dash.gif);
}
.pinkie.jumping {
background: url(gfx/pie-jump.gif) !important;
}
.pinkie.gameover {
background: url(gfx/pie-oops.gif) !important;
}
.ground {
position: absolute;
width: 3840px;
height: 256px;
top: 384px;
left: -128px;
background: url(gfx/map.png);
z-index: -1;
}
.hater {
position: absolute;
width: 150px;
height: 113px;
left: -512px;
background: url(gfx/trollface.gif);
}
.coin {
position: absolute;
width: 128px;
height: 128px;
left: -512px;
background: url(gfx/coin.gif);
}

View File

@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>hai lol</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" href="game.css">
<script type="text/javascript" src="lib/traceur-runtime.js"></script>
<script type="text/javascript" src="lib/mousetrap.js"></script>
<script type="text/javascript" src="lib/react.js"></script>
<script type="text/javascript" src="lib/rx.js"></script>
<script type="text/javascript" src="lib/rx.async.js"></script>
<script type="text/javascript" src="lib/rx.binding.js"></script>
<script type="text/javascript" src="lib/rx.time.js"></script>
<script type="text/javascript" src="lib/rx.coincidence.js"></script>
</head>
<body>
<div id="canvas"></div>
<script type="text/javascript">
(function() {
function resizeCanvas() {
var canvas = document.getElementById("canvas");
var scale = window.innerHeight / canvas.clientHeight;
canvas.style.mozTransform = "scale(" + scale + ")";
canvas.style.webkitTransform = "scale(" + scale + ")";
canvas.style.transform = "scale(" + scale + ")";
canvas.style.width = (window.innerWidth / scale) + "px";
}
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
function bounds(me) {
return {
x1: me.x + (me.baseX || 0),
y1: me.y + (me.baseY || 0),
x2: me.x + (me.baseX || 0) + 64,
y2: me.y + (me.baseY || 0) + 64
};
}
function intersects(me, target) {
var b1 = bounds(me), b2 = bounds(target);
return !(b2.x1 > b1.x2 || b2.x2 < b1.x1 ||
b2.y1 > b1.y2 || b2.y2 < b1.y1);
}
window.intersects = intersects;
})();
</script>
<script type="text/javascript" src="../src/modules/editor/client.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

View File

@@ -0,0 +1,9 @@
/* mousetrap v1.4.6 craig.is/killing/mice */
(function(J,r,f){function s(a,b,d){a.addEventListener?a.addEventListener(b,d,!1):a.attachEvent("on"+b,d)}function A(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return h[a.which]?h[a.which]:B[a.which]?B[a.which]:String.fromCharCode(a.which).toLowerCase()}function t(a){a=a||{};var b=!1,d;for(d in n)a[d]?b=!0:n[d]=0;b||(u=!1)}function C(a,b,d,c,e,v){var g,k,f=[],h=d.type;if(!l[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(g=0;g<l[a].length;++g)if(k=
l[a][g],!(!c&&k.seq&&n[k.seq]!=k.level||h!=k.action||("keypress"!=h||d.metaKey||d.ctrlKey)&&b.sort().join(",")!==k.modifiers.sort().join(","))){var m=c&&k.seq==c&&k.level==v;(!c&&k.combo==e||m)&&l[a].splice(g,1);f.push(k)}return f}function K(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function x(a,b,d,c){m.stopCallback(b,b.target||b.srcElement,d,c)||!1!==a(b,d)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?
b.stopPropagation():b.cancelBubble=!0)}function y(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=A(a);b&&("keyup"==a.type&&z===b?z=!1:m.handleKey(b,K(a),a))}function w(a){return"shift"==a||"ctrl"==a||"alt"==a||"meta"==a}function L(a,b,d,c){function e(b){return function(){u=b;++n[a];clearTimeout(D);D=setTimeout(t,1E3)}}function v(b){x(d,b,a);"keyup"!==c&&(z=A(b));setTimeout(t,10)}for(var g=n[a]=0;g<b.length;++g){var f=g+1===b.length?v:e(c||E(b[g+1]).action);F(b[g],f,c,a,g)}}function E(a,b){var d,
c,e,f=[];d="+"===a?["+"]:a.split("+");for(e=0;e<d.length;++e)c=d[e],G[c]&&(c=G[c]),b&&"keypress"!=b&&H[c]&&(c=H[c],f.push("shift")),w(c)&&f.push(c);d=c;e=b;if(!e){if(!p){p={};for(var g in h)95<g&&112>g||h.hasOwnProperty(g)&&(p[h[g]]=g)}e=p[d]?"keydown":"keypress"}"keypress"==e&&f.length&&(e="keydown");return{key:c,modifiers:f,action:e}}function F(a,b,d,c,e){q[a+":"+d]=b;a=a.replace(/\s+/g," ");var f=a.split(" ");1<f.length?L(a,f,b,d):(d=E(a,d),l[d.key]=l[d.key]||[],C(d.key,d.modifiers,{type:d.action},
c,a,e),l[d.key][c?"unshift":"push"]({callback:b,modifiers:d.modifiers,action:d.action,seq:c,level:e,combo:a}))}var h={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},B={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},H={"~":"`","!":"1",
"@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},G={option:"alt",command:"meta","return":"enter",escape:"esc",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p,l={},q={},n={},D,z=!1,I=!1,u=!1;for(f=1;20>f;++f)h[111+f]="f"+f;for(f=0;9>=f;++f)h[f+96]=f;s(r,"keypress",y);s(r,"keydown",y);s(r,"keyup",y);var m={bind:function(a,b,d){a=a instanceof Array?a:[a];for(var c=0;c<a.length;++c)F(a[c],b,d);return this},
unbind:function(a,b){return m.bind(a,function(){},b)},trigger:function(a,b){if(q[a+":"+b])q[a+":"+b]({},a);return this},reset:function(){l={};q={};return this},stopCallback:function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable},handleKey:function(a,b,d){var c=C(a,b,d),e;b={};var f=0,g=!1;for(e=0;e<c.length;++e)c[e].seq&&(f=Math.max(f,c[e].level));for(e=0;e<c.length;++e)c[e].seq?c[e].level==f&&(g=!0,
b[c[e].seq]=1,x(c[e].callback,d,c[e].combo,c[e].seq)):g||x(c[e].callback,d,c[e].combo);c="keypress"==d.type&&I;d.type!=u||w(a)||c||t(b);I=g&&"keydown"==d.type}};J.Mousetrap=m;"function"===typeof define&&define.amd&&define(m)})(window,document);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,687 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
var root = (objectTypes[typeof window] && window) || this,
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx', 'exports'], function (Rx, exports) {
root.Rx = factory(root, exports, Rx);
return root.Rx;
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
// References
var Observable = Rx.Observable,
observableProto = Observable.prototype,
CompositeDisposable = Rx.CompositeDisposable,
AnonymousObservable = Rx.Internals.AnonymousObservable,
isEqual = Rx.Internals.isEqual;
// Defaults
var argumentOutOfRange = 'Argument out of range';
var sequenceContainsNoElements = "Sequence contains no elements.";
function defaultComparer(x, y) { return isEqual(x, y); }
function identity(x) { return x; }
function subComparer(x, y) {
if (x > y) {
return 1;
}
if (x < y) {
return -1
}
return 0;
}
function extremaBy(source, keySelector, comparer) {
return new AnonymousObservable(function (observer) {
var hasValue = false, lastKey = null, list = [];
return source.subscribe(function (x) {
var comparison, key;
try {
key = keySelector(x);
} catch (ex) {
observer.onError(ex);
return;
}
comparison = 0;
if (!hasValue) {
hasValue = true;
lastKey = key;
} else {
try {
comparison = comparer(key, lastKey);
} catch (ex1) {
observer.onError(ex1);
return;
}
}
if (comparison > 0) {
lastKey = key;
list = [];
}
if (comparison >= 0) {
list.push(x);
}
}, observer.onError.bind(observer), function () {
observer.onNext(list);
observer.onCompleted();
});
});
}
function firstOnly(x) {
if (x.length === 0) {
throw new Error(sequenceContainsNoElements);
}
return x[0];
}
/**
* Applies an accumulator function over an observable sequence, returning the result of the aggregation as a single element in the result sequence. The specified seed value is used as the initial accumulator value.
* For aggregation behavior with incremental intermediate results, see Observable.scan.
* @example
* 1 - res = source.aggregate(function (acc, x) { return acc + x; });
* 2 - res = source.aggregate(0, function (acc, x) { return acc + x; });
* @param {Mixed} [seed] The initial accumulator value.
* @param {Function} accumulator An accumulator function to be invoked on each element.
* @returns {Observable} An observable sequence containing a single element with the final accumulator value.
*/
observableProto.aggregate = function () {
var seed, hasSeed, accumulator;
if (arguments.length === 2) {
seed = arguments[0];
hasSeed = true;
accumulator = arguments[1];
} else {
accumulator = arguments[0];
}
return hasSeed ? this.scan(seed, accumulator).startWith(seed).finalValue() : this.scan(accumulator).finalValue();
};
/**
* Applies an accumulator function over an observable sequence, returning the result of the aggregation as a single element in the result sequence. The specified seed value is used as the initial accumulator value.
* For aggregation behavior with incremental intermediate results, see Observable.scan.
* @example
* 1 - res = source.reduce(function (acc, x) { return acc + x; });
* 2 - res = source.reduce(function (acc, x) { return acc + x; }, 0);
* @param {Function} accumulator An accumulator function to be invoked on each element.
* @param {Any} [seed] The initial accumulator value.
* @returns {Observable} An observable sequence containing a single element with the final accumulator value.
*/
observableProto.reduce = function (accumulator) {
var seed, hasSeed;
if (arguments.length === 2) {
hasSeed = true;
seed = arguments[1];
}
return hasSeed ? this.scan(seed, accumulator).startWith(seed).finalValue() : this.scan(accumulator).finalValue();
};
/**
* Determines whether any element of an observable sequence satisfies a condition if present, else if any items are in the sequence.
* @example
* var result = source.any();
* var result = source.any(function (x) { return x > 3; });
* @param {Function} [predicate] A function to test each element for a condition.
* @returns {Observable} An observable sequence containing a single element determining whether any elements in the source sequence pass the test in the specified predicate if given, else if any items are in the sequence.
*/
observableProto.some = observableProto.any = function (predicate, thisArg) {
var source = this;
return predicate ?
source.where(predicate, thisArg).any() :
new AnonymousObservable(function (observer) {
return source.subscribe(function () {
observer.onNext(true);
observer.onCompleted();
}, observer.onError.bind(observer), function () {
observer.onNext(false);
observer.onCompleted();
});
});
};
/**
* Determines whether an observable sequence is empty.
*
* @memberOf Observable#
* @returns {Observable} An observable sequence containing a single element determining whether the source sequence is empty.
*/
observableProto.isEmpty = function () {
return this.any().select(function (b) { return !b; });
};
/**
* Determines whether all elements of an observable sequence satisfy a condition.
*
* 1 - res = source.all(function (value) { return value.length > 3; });
* @memberOf Observable#
* @param {Function} [predicate] A function to test each element for a condition.
* @param {Any} [thisArg] Object to use as this when executing callback.
* @returns {Observable} An observable sequence containing a single element determining whether all elements in the source sequence pass the test in the specified predicate.
*/
observableProto.every = observableProto.all = function (predicate, thisArg) {
return this.where(function (v) {
return !predicate(v);
}, thisArg).any().select(function (b) {
return !b;
});
};
/**
* Determines whether an observable sequence contains a specified element with an optional equality comparer.
* @example
* 1 - res = source.contains(42);
* 2 - res = source.contains({ value: 42 }, function (x, y) { return x.value === y.value; });
* @param value The value to locate in the source sequence.
* @param {Function} [comparer] An equality comparer to compare elements.
* @returns {Observable} An observable sequence containing a single element determining whether the source sequence contains an element that has the specified value.
*/
observableProto.contains = function (value, comparer) {
comparer || (comparer = defaultComparer);
return this.where(function (v) {
return comparer(v, value);
}).any();
};
/**
* Returns an observable sequence containing a value that represents how many elements in the specified observable sequence satisfy a condition if provided, else the count of items.
* @example
* res = source.count();
* res = source.count(function (x) { return x > 3; });
* @param {Function} [predicate]A function to test each element for a condition.
* @param {Any} [thisArg] Object to use as this when executing callback.
* @returns {Observable} An observable sequence containing a single element with a number that represents how many elements in the input sequence satisfy the condition in the predicate function if provided, else the count of items in the sequence.
*/
observableProto.count = function (predicate, thisArg) {
return predicate ?
this.where(predicate, thisArg).count() :
this.aggregate(0, function (count) {
return count + 1;
});
};
/**
* Computes the sum of a sequence of values that are obtained by invoking an optional transform function on each element of the input sequence, else if not specified computes the sum on each item in the sequence.
* @example
* var res = source.sum();
* var res = source.sum(function (x) { return x.value; });
* @param {Function} [selector] A transform function to apply to each element.
* @param {Any} [thisArg] Object to use as this when executing callback.
* @returns {Observable} An observable sequence containing a single element with the sum of the values in the source sequence.
*/
observableProto.sum = function (keySelector, thisArg) {
return keySelector ?
this.select(keySelector, thisArg).sum() :
this.aggregate(0, function (prev, curr) {
return prev + curr;
});
};
/**
* Returns the elements in an observable sequence with the minimum key value according to the specified comparer.
* @example
* var res = source.minBy(function (x) { return x.value; });
* var res = source.minBy(function (x) { return x.value; }, function (x, y) { return x - y; });
* @param {Function} keySelector Key selector function.
* @param {Function} [comparer] Comparer used to compare key values.
* @returns {Observable} An observable sequence containing a list of zero or more elements that have a minimum key value.
*/
observableProto.minBy = function (keySelector, comparer) {
comparer || (comparer = subComparer);
return extremaBy(this, keySelector, function (x, y) {
return comparer(x, y) * -1;
});
};
/**
* Returns the minimum element in an observable sequence according to the optional comparer else a default greater than less than check.
* @example
* var res = source.min();
* var res = source.min(function (x, y) { return x.value - y.value; });
* @param {Function} [comparer] Comparer used to compare elements.
* @returns {Observable} An observable sequence containing a single element with the minimum element in the source sequence.
*/
observableProto.min = function (comparer) {
return this.minBy(identity, comparer).select(function (x) {
return firstOnly(x);
});
};
/**
* Returns the elements in an observable sequence with the maximum key value according to the specified comparer.
* @example
* var res = source.maxBy(function (x) { return x.value; });
* var res = source.maxBy(function (x) { return x.value; }, function (x, y) { return x - y;; });
* @param {Function} keySelector Key selector function.
* @param {Function} [comparer] Comparer used to compare key values.
* @returns {Observable} An observable sequence containing a list of zero or more elements that have a maximum key value.
*/
observableProto.maxBy = function (keySelector, comparer) {
comparer || (comparer = subComparer);
return extremaBy(this, keySelector, comparer);
};
/**
* Returns the maximum value in an observable sequence according to the specified comparer.
* @example
* var res = source.max();
* var res = source.max(function (x, y) { return x.value - y.value; });
* @param {Function} [comparer] Comparer used to compare elements.
* @returns {Observable} An observable sequence containing a single element with the maximum element in the source sequence.
*/
observableProto.max = function (comparer) {
return this.maxBy(identity, comparer).select(function (x) {
return firstOnly(x);
});
};
/**
* Computes the average of an observable sequence of values that are in the sequence or obtained by invoking a transform function on each element of the input sequence if present.
* @example
* var res = res = source.average();
* var res = res = source.average(function (x) { return x.value; });
* @param {Function} [selector] A transform function to apply to each element.
* @param {Any} [thisArg] Object to use as this when executing callback.
* @returns {Observable} An observable sequence containing a single element with the average of the sequence of values.
*/
observableProto.average = function (keySelector, thisArg) {
return keySelector ?
this.select(keySelector, thisArg).average() :
this.scan({
sum: 0,
count: 0
}, function (prev, cur) {
return {
sum: prev.sum + cur,
count: prev.count + 1
};
}).finalValue().select(function (s) {
if (s.count === 0) {
throw new Error('The input sequence was empty');
}
return s.sum / s.count;
});
};
function sequenceEqualArray(first, second, comparer) {
return new AnonymousObservable(function (observer) {
var count = 0, len = second.length;
return first.subscribe(function (value) {
var equal = false;
try {
if (count < len) {
equal = comparer(value, second[count++]);
}
} catch (e) {
observer.onError(e);
return;
}
if (!equal) {
observer.onNext(false);
observer.onCompleted();
}
}, observer.onError.bind(observer), function () {
observer.onNext(count === len);
observer.onCompleted();
});
});
}
/**
* Determines whether two sequences are equal by comparing the elements pairwise using a specified equality comparer.
*
* @example
* var res = res = source.sequenceEqual([1,2,3]);
* var res = res = source.sequenceEqual([{ value: 42 }], function (x, y) { return x.value === y.value; });
* 3 - res = source.sequenceEqual(Rx.Observable.returnValue(42));
* 4 - res = source.sequenceEqual(Rx.Observable.returnValue({ value: 42 }), function (x, y) { return x.value === y.value; });
* @param {Observable} second Second observable sequence or array to compare.
* @param {Function} [comparer] Comparer used to compare elements of both sequences.
* @returns {Observable} An observable sequence that contains a single element which indicates whether both sequences are of equal length and their corresponding elements are equal according to the specified equality comparer.
*/
observableProto.sequenceEqual = function (second, comparer) {
var first = this;
comparer || (comparer = defaultComparer);
if (Array.isArray(second)) {
return sequenceEqualArray(first, second, comparer);
}
return new AnonymousObservable(function (observer) {
var donel = false, doner = false, ql = [], qr = [];
var subscription1 = first.subscribe(function (x) {
var equal, v;
if (qr.length > 0) {
v = qr.shift();
try {
equal = comparer(v, x);
} catch (e) {
observer.onError(e);
return;
}
if (!equal) {
observer.onNext(false);
observer.onCompleted();
}
} else if (doner) {
observer.onNext(false);
observer.onCompleted();
} else {
ql.push(x);
}
}, observer.onError.bind(observer), function () {
donel = true;
if (ql.length === 0) {
if (qr.length > 0) {
observer.onNext(false);
observer.onCompleted();
} else if (doner) {
observer.onNext(true);
observer.onCompleted();
}
}
});
var subscription2 = second.subscribe(function (x) {
var equal, v;
if (ql.length > 0) {
v = ql.shift();
try {
equal = comparer(v, x);
} catch (exception) {
observer.onError(exception);
return;
}
if (!equal) {
observer.onNext(false);
observer.onCompleted();
}
} else if (donel) {
observer.onNext(false);
observer.onCompleted();
} else {
qr.push(x);
}
}, observer.onError.bind(observer), function () {
doner = true;
if (qr.length === 0) {
if (ql.length > 0) {
observer.onNext(false);
observer.onCompleted();
} else if (donel) {
observer.onNext(true);
observer.onCompleted();
}
}
});
return new CompositeDisposable(subscription1, subscription2);
});
};
function elementAtOrDefault(source, index, hasDefault, defaultValue) {
if (index < 0) {
throw new Error(argumentOutOfRange);
}
return new AnonymousObservable(function (observer) {
var i = index;
return source.subscribe(function (x) {
if (i === 0) {
observer.onNext(x);
observer.onCompleted();
}
i--;
}, observer.onError.bind(observer), function () {
if (!hasDefault) {
observer.onError(new Error(argumentOutOfRange));
} else {
observer.onNext(defaultValue);
observer.onCompleted();
}
});
});
}
/**
* Returns the element at a specified index in a sequence.
* @example
* var res = source.elementAt(5);
* @param {Number} index The zero-based index of the element to retrieve.
* @returns {Observable} An observable sequence that produces the element at the specified position in the source sequence.
*/
observableProto.elementAt = function (index) {
return elementAtOrDefault(this, index, false);
};
/**
* Returns the element at a specified index in a sequence or a default value if the index is out of range.
* @example
* var res = source.elementAtOrDefault(5);
* var res = source.elementAtOrDefault(5, 0);
* @param {Number} index The zero-based index of the element to retrieve.
* @param [defaultValue] The default value if the index is outside the bounds of the source sequence.
* @returns {Observable} An observable sequence that produces the element at the specified position in the source sequence, or a default value if the index is outside the bounds of the source sequence.
*/
observableProto.elementAtOrDefault = function (index, defaultValue) {
return elementAtOrDefault(this, index, true, defaultValue);
};
function singleOrDefaultAsync(source, hasDefault, defaultValue) {
return new AnonymousObservable(function (observer) {
var value = defaultValue, seenValue = false;
return source.subscribe(function (x) {
if (seenValue) {
observer.onError(new Error('Sequence contains more than one element'));
} else {
value = x;
seenValue = true;
}
}, observer.onError.bind(observer), function () {
if (!seenValue && !hasDefault) {
observer.onError(new Error(sequenceContainsNoElements));
} else {
observer.onNext(value);
observer.onCompleted();
}
});
});
}
/**
* Returns the only element of an observable sequence that satisfies the condition in the optional predicate, and reports an exception if there is not exactly one element in the observable sequence.
* @example
* var res = res = source.single();
* var res = res = source.single(function (x) { return x === 42; });
* @param {Function} [predicate] A predicate function to evaluate for elements in the source sequence.
* @param {Any} [thisArg] Object to use as `this` when executing the predicate.
* @returns {Observable} Sequence containing the single element in the observable sequence that satisfies the condition in the predicate.
*/
observableProto.single = function (predicate, thisArg) {
return predicate ?
this.where(predicate, thisArg).single() :
singleOrDefaultAsync(this, false);
};
/**
* Returns the only element of an observable sequence that matches the predicate, or a default value if no such element exists; this method reports an exception if there is more than one element in the observable sequence.
* @example
* var res = res = source.singleOrDefault();
* var res = res = source.singleOrDefault(function (x) { return x === 42; });
* res = source.singleOrDefault(function (x) { return x === 42; }, 0);
* res = source.singleOrDefault(null, 0);
* @memberOf Observable#
* @param {Function} predicate A predicate function to evaluate for elements in the source sequence.
* @param [defaultValue] The default value if the index is outside the bounds of the source sequence.
* @param {Any} [thisArg] Object to use as `this` when executing the predicate.
* @returns {Observable} Sequence containing the single element in the observable sequence that satisfies the condition in the predicate, or a default value if no such element exists.
*/
observableProto.singleOrDefault = function (predicate, defaultValue, thisArg) {
return predicate?
this.where(predicate, thisArg).singleOrDefault(null, defaultValue) :
singleOrDefaultAsync(this, true, defaultValue)
};
function firstOrDefaultAsync(source, hasDefault, defaultValue) {
return new AnonymousObservable(function (observer) {
return source.subscribe(function (x) {
observer.onNext(x);
observer.onCompleted();
}, observer.onError.bind(observer), function () {
if (!hasDefault) {
observer.onError(new Error(sequenceContainsNoElements));
} else {
observer.onNext(defaultValue);
observer.onCompleted();
}
});
});
}
/**
* Returns the first element of an observable sequence that satisfies the condition in the predicate if present else the first item in the sequence.
* @example
* var res = res = source.first();
* var res = res = source.first(function (x) { return x > 3; });
* @param {Function} [predicate] A predicate function to evaluate for elements in the source sequence.
* @param {Any} [thisArg] Object to use as `this` when executing the predicate.
* @returns {Observable} Sequence containing the first element in the observable sequence that satisfies the condition in the predicate if provided, else the first item in the sequence.
*/
observableProto.first = function (predicate, thisArg) {
return predicate ?
this.where(predicate, thisArg).first() :
firstOrDefaultAsync(this, false);
};
/**
* Returns the first element of an observable sequence that satisfies the condition in the predicate, or a default value if no such element exists.
* @example
* var res = res = source.firstOrDefault();
* var res = res = source.firstOrDefault(function (x) { return x > 3; });
* var res = source.firstOrDefault(function (x) { return x > 3; }, 0);
* var res = source.firstOrDefault(null, 0);
* @param {Function} [predicate] A predicate function to evaluate for elements in the source sequence.
* @param {Any} [defaultValue] The default value if no such element exists. If not specified, defaults to null.
* @param {Any} [thisArg] Object to use as `this` when executing the predicate.
* @returns {Observable} Sequence containing the first element in the observable sequence that satisfies the condition in the predicate, or a default value if no such element exists.
*/
observableProto.firstOrDefault = function (predicate, defaultValue, thisArg) {
return predicate ?
this.where(predicate).firstOrDefault(null, defaultValue) :
firstOrDefaultAsync(this, true, defaultValue);
};
function lastOrDefaultAsync(source, hasDefault, defaultValue) {
return new AnonymousObservable(function (observer) {
var value = defaultValue, seenValue = false;
return source.subscribe(function (x) {
value = x;
seenValue = true;
}, observer.onError.bind(observer), function () {
if (!seenValue && !hasDefault) {
observer.onError(new Error(sequenceContainsNoElements));
} else {
observer.onNext(value);
observer.onCompleted();
}
});
});
}
/**
* Returns the last element of an observable sequence that satisfies the condition in the predicate if specified, else the last element.
* @example
* var res = source.last();
* var res = source.last(function (x) { return x > 3; });
* @param {Function} [predicate] A predicate function to evaluate for elements in the source sequence.
* @param {Any} [thisArg] Object to use as `this` when executing the predicate.
* @returns {Observable} Sequence containing the last element in the observable sequence that satisfies the condition in the predicate.
*/
observableProto.last = function (predicate, thisArg) {
return predicate ?
this.where(predicate, thisArg).last() :
lastOrDefaultAsync(this, false);
};
/**
* Returns the last element of an observable sequence that satisfies the condition in the predicate, or a default value if no such element exists.
* @example
* var res = source.lastOrDefault();
* var res = source.lastOrDefault(function (x) { return x > 3; });
* var res = source.lastOrDefault(function (x) { return x > 3; }, 0);
* var res = source.lastOrDefault(null, 0);
* @param {Function} [predicate] A predicate function to evaluate for elements in the source sequence.
* @param [defaultValue] The default value if no such element exists. If not specified, defaults to null.
* @param {Any} [thisArg] Object to use as `this` when executing the predicate.
* @returns {Observable} Sequence containing the last element in the observable sequence that satisfies the condition in the predicate, or a default value if no such element exists.
*/
observableProto.lastOrDefault = function (predicate, defaultValue, thisArg) {
return predicate ?
this.where(predicate, thisArg).lastOrDefault(null, defaultValue) :
lastOrDefaultAsync(this, true, defaultValue);
};
function findValue (source, predicate, thisArg, yieldIndex) {
return new AnonymousObservable(function (observer) {
var i = 0;
return source.subscribe(function (x) {
var shouldRun;
try {
shouldRun = predicate.call(thisArg, x, i, source);
} catch(e) {
observer.onError(e);
return;
}
if (shouldRun) {
observer.onNext(yieldIndex ? i : x);
observer.onCompleted();
} else {
i++;
}
}, observer.onError.bind(observer), function () {
observer.onNext(yieldIndex ? -1 : undefined);
observer.onCompleted();
});
});
}
/**
* Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire Observable sequence.
* @param {Function} predicate The predicate that defines the conditions of the element to search for.
* @param {Any} [thisArg] Object to use as `this` when executing the predicate.
* @returns {Observable} An Observable sequence with the first element that matches the conditions defined by the specified predicate, if found; otherwise, undefined.
*/
observableProto.find = function (predicate, thisArg) {
return findValue(this, predicate, thisArg, false);
};
/**
* Searches for an element that matches the conditions defined by the specified predicate, and returns
* an Observable sequence with the zero-based index of the first occurrence within the entire Observable sequence.
* @param {Function} predicate The predicate that defines the conditions of the element to search for.
* @param {Any} [thisArg] Object to use as `this` when executing the predicate.
* @returns {Observable} An Observable sequence with the zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, 1.
*/
observableProto.findIndex = function (predicate, thisArg) {
return findValue(this, predicate, thisArg, true);
};
return Rx;
}));

View File

@@ -0,0 +1,397 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
var root = (objectTypes[typeof window] && window) || this,
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx.binding', 'exports'], function (Rx, exports) {
root.Rx = factory(root, exports, Rx);
return root.Rx;
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
// Aliases
var Observable = Rx.Observable,
observableProto = Observable.prototype,
AnonymousObservable = Rx.Internals.AnonymousObservable,
AsyncSubject = Rx.AsyncSubject,
disposableCreate = Rx.Disposable.create,
CompositeDisposable= Rx.CompositeDisposable,
immediateScheduler = Rx.Scheduler.immediate,
slice = Array.prototype.slice;
/**
* Invokes the specified function asynchronously on the specified scheduler, surfacing the result through an observable sequence.
*
* @example
* var res = Rx.Observable.start(function () { console.log('hello'); });
* var res = Rx.Observable.start(function () { console.log('hello'); }, Rx.Scheduler.timeout);
* var res = Rx.Observable.start(function () { this.log('hello'); }, Rx.Scheduler.timeout, console);
*
* @param {Function} func Function to run asynchronously.
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @returns {Observable} An observable sequence exposing the function's result value, or an exception.
*
* Remarks
* * The function is called immediately, not during the subscription of the resulting sequence.
* * Multiple subscriptions to the resulting sequence can observe the function's result.
*/
Observable.start = function (func, scheduler, context) {
return observableToAsync(func, scheduler, context)();
};
/**
* Converts the function into an asynchronous function. Each invocation of the resulting asynchronous function causes an invocation of the original synchronous function on the specified scheduler.
*
* @example
* var res = Rx.Observable.toAsync(function (x, y) { return x + y; })(4, 3);
* var res = Rx.Observable.toAsync(function (x, y) { return x + y; }, Rx.Scheduler.timeout)(4, 3);
* var res = Rx.Observable.toAsync(function (x) { this.log(x); }, Rx.Scheduler.timeout, console)('hello');
*
* @param {Function} function Function to convert to an asynchronous function.
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @returns {Function} Asynchronous function.
*/
var observableToAsync = Observable.toAsync = function (func, scheduler, context) {
scheduler || (scheduler = timeoutScheduler);
return function () {
var args = arguments,
subject = new AsyncSubject();
scheduler.schedule(function () {
var result;
try {
result = func.apply(context, args);
} catch (e) {
subject.onError(e);
return;
}
subject.onNext(result);
subject.onCompleted();
});
return subject.asObservable();
};
};
/**
* Converts a callback function to an observable sequence.
*
* @param {Function} function Function with a callback as the last parameter to convert to an Observable sequence.
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @param {Function} [selector] A selector which takes the arguments from the callback to produce a single item to yield on next.
* @returns {Function} A function, when executed with the required parameters minus the callback, produces an Observable sequence with a single value of the arguments to the callback as an array.
*/
Observable.fromCallback = function (func, scheduler, context, selector) {
scheduler || (scheduler = immediateScheduler);
return function () {
var args = slice.call(arguments, 0);
return new AnonymousObservable(function (observer) {
return scheduler.schedule(function () {
function handler(e) {
var results = e;
if (selector) {
try {
results = selector(arguments);
} catch (err) {
observer.onError(err);
return;
}
} else {
if (results.length === 1) {
results = results[0];
}
}
observer.onNext(results);
observer.onCompleted();
}
args.push(handler);
func.apply(context, args);
});
});
};
};
/**
* Converts a Node.js callback style function to an observable sequence. This must be in function (err, ...) format.
* @param {Function} func The function to call
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @param {Function} [selector] A selector which takes the arguments from the callback minus the error to produce a single item to yield on next.
* @returns {Function} An async function which when applied, returns an observable sequence with the callback arguments as an array.
*/
Observable.fromNodeCallback = function (func, scheduler, context, selector) {
scheduler || (scheduler = immediateScheduler);
return function () {
var args = slice.call(arguments, 0);
return new AnonymousObservable(function (observer) {
return scheduler.schedule(function () {
function handler(err) {
if (err) {
observer.onError(err);
return;
}
var results = slice.call(arguments, 1);
if (selector) {
try {
results = selector(results);
} catch (e) {
observer.onError(e);
return;
}
} else {
if (results.length === 1) {
results = results[0];
}
}
observer.onNext(results);
observer.onCompleted();
}
args.push(handler);
func.apply(context, args);
});
});
};
};
function fixEvent(event) {
var stopPropagation = function () {
this.cancelBubble = true;
};
var preventDefault = function () {
this.bubbledKeyCode = this.keyCode;
if (this.ctrlKey) {
try {
this.keyCode = 0;
} catch (e) { }
}
this.defaultPrevented = true;
this.returnValue = false;
this.modified = true;
};
event || (event = window.event);
if (!event.target) {
event.target = event.target || event.srcElement;
if (event.type == 'mouseover') {
event.relatedTarget = event.fromElement;
}
if (event.type == 'mouseout') {
event.relatedTarget = event.toElement;
}
// Adding stopPropogation and preventDefault to IE
if (!event.stopPropagation){
event.stopPropagation = stopPropagation;
event.preventDefault = preventDefault;
}
// Normalize key events
switch(event.type){
case 'keypress':
var c = ('charCode' in event ? event.charCode : event.keyCode);
if (c == 10) {
c = 0;
event.keyCode = 13;
} else if (c == 13 || c == 27) {
c = 0;
} else if (c == 3) {
c = 99;
}
event.charCode = c;
event.keyChar = event.charCode ? String.fromCharCode(event.charCode) : '';
break;
}
}
return event;
}
function createListener (element, name, handler) {
// Node.js specific
if (element.addListener) {
element.addListener(name, handler);
return disposableCreate(function () {
element.removeListener(name, handler);
});
}
// Standards compliant
if (element.addEventListener) {
element.addEventListener(name, handler, false);
return disposableCreate(function () {
element.removeEventListener(name, handler, false);
});
} else if (element.attachEvent) {
// IE Specific
var innerHandler = function (event) {
handler(fixEvent(event));
};
element.attachEvent('on' + name, innerHandler);
return disposableCreate(function () {
element.detachEvent('on' + name, innerHandler);
});
} else {
// Level 1 DOM Events
element['on' + name] = handler;
return disposableCreate(function () {
element['on' + name] = null;
});
}
}
function createEventListener (el, eventName, handler) {
var disposables = new CompositeDisposable();
// Asume NodeList
if (el && el.length) {
for (var i = 0, len = el.length; i < len; i++) {
disposables.add(createEventListener(el[i], eventName, handler));
}
} else if (el) {
disposables.add(createListener(el, eventName, handler));
}
return disposables;
}
/**
* Creates an observable sequence by adding an event listener to the matching DOMElement or each item in the NodeList.
*
* @example
* var source = Rx.Observable.fromEvent(element, 'mouseup');
*
* @param {Object} element The DOMElement or NodeList to attach a listener.
* @param {String} eventName The event name to attach the observable sequence.
* @param {Function} [selector] A selector which takes the arguments from the event handler to produce a single item to yield on next.
* @returns {Observable} An observable sequence of events from the specified element and the specified event.
*/
Observable.fromEvent = function (element, eventName, selector) {
return new AnonymousObservable(function (observer) {
return createEventListener(
element,
eventName,
function handler (e) {
var results = e;
if (selector) {
try {
results = selector(arguments);
} catch (err) {
observer.onError(err);
return
}
}
observer.onNext(results);
});
}).publish().refCount();
};
/**
* Creates an observable sequence from an event emitter via an addHandler/removeHandler pair.
* @param {Function} addHandler The function to add a handler to the emitter.
* @param {Function} [removeHandler] The optional function to remove a handler from an emitter.
* @param {Function} [selector] A selector which takes the arguments from the event handler to produce a single item to yield on next.
* @returns {Observable} An observable sequence which wraps an event from an event emitter
*/
Observable.fromEventPattern = function (addHandler, removeHandler, selector) {
return new AnonymousObservable(function (observer) {
function innerHandler (e) {
var result = e;
if (selector) {
try {
result = selector(arguments);
} catch (err) {
observer.onError(err);
return;
}
}
observer.onNext(result);
}
var returnValue = addHandler(innerHandler);
return disposableCreate(function () {
if (removeHandler) {
removeHandler(innerHandler, returnValue);
}
});
}).publish().refCount();
};
/**
* Converts a Promise to an Observable sequence
* @param {Promise} A Promises A+ implementation instance.
* @returns {Observable} An Observable sequence which wraps the existing promise success and failure.
*/
Observable.fromPromise = function (promise) {
return new AnonymousObservable(function (observer) {
promise.then(
function (value) {
observer.onNext(value);
observer.onCompleted();
},
function (reason) {
observer.onError(reason);
});
});
};
/*
* Converts an existing observable sequence to an ES6 Compatible Promise
* @example
* var promise = Rx.Observable.return(42).toPromise(RSVP.Promise);
* @param {Function} The constructor of the promise
* @returns {Promise} An ES6 compatible promise with the last value from the observable sequence.
*/
observableProto.toPromise = function (promiseCtor) {
var source = this;
return new promiseCtor(function (resolve, reject) {
// No cancellation can be done
var value, hasValue = false;
source.subscribe(function (v) {
value = v;
hasValue = true;
}, function (err) {
reject(err);
}, function () {
if (hasValue) {
resolve(value);
}
});
});
};
return Rx;
}));

View File

@@ -0,0 +1,327 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
var root = (objectTypes[typeof window] && window) || this,
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx.binding', 'exports'], function (Rx, exports) {
root.Rx = factory(root, exports, Rx);
return root.Rx;
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
// Aliases
var Observable = Rx.Observable,
observableProto = Observable.prototype,
AnonymousObservable = Rx.Internals.AnonymousObservable,
AsyncSubject = Rx.AsyncSubject,
disposableCreate = Rx.Disposable.create,
CompositeDisposable= Rx.CompositeDisposable,
immediateScheduler = Rx.Scheduler.immediate,
slice = Array.prototype.slice;
/**
* Invokes the specified function asynchronously on the specified scheduler, surfacing the result through an observable sequence.
*
* @example
* var res = Rx.Observable.start(function () { console.log('hello'); });
* var res = Rx.Observable.start(function () { console.log('hello'); }, Rx.Scheduler.timeout);
* var res = Rx.Observable.start(function () { this.log('hello'); }, Rx.Scheduler.timeout, console);
*
* @param {Function} func Function to run asynchronously.
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @returns {Observable} An observable sequence exposing the function's result value, or an exception.
*
* Remarks
* * The function is called immediately, not during the subscription of the resulting sequence.
* * Multiple subscriptions to the resulting sequence can observe the function's result.
*/
Observable.start = function (func, scheduler, context) {
return observableToAsync(func, scheduler, context)();
};
/**
* Converts the function into an asynchronous function. Each invocation of the resulting asynchronous function causes an invocation of the original synchronous function on the specified scheduler.
*
* @example
* var res = Rx.Observable.toAsync(function (x, y) { return x + y; })(4, 3);
* var res = Rx.Observable.toAsync(function (x, y) { return x + y; }, Rx.Scheduler.timeout)(4, 3);
* var res = Rx.Observable.toAsync(function (x) { this.log(x); }, Rx.Scheduler.timeout, console)('hello');
*
* @param {Function} function Function to convert to an asynchronous function.
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @returns {Function} Asynchronous function.
*/
var observableToAsync = Observable.toAsync = function (func, scheduler, context) {
scheduler || (scheduler = timeoutScheduler);
return function () {
var args = arguments,
subject = new AsyncSubject();
scheduler.schedule(function () {
var result;
try {
result = func.apply(context, args);
} catch (e) {
subject.onError(e);
return;
}
subject.onNext(result);
subject.onCompleted();
});
return subject.asObservable();
};
};
/**
* Converts a callback function to an observable sequence.
*
* @param {Function} function Function with a callback as the last parameter to convert to an Observable sequence.
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @param {Function} [selector] A selector which takes the arguments from the callback to produce a single item to yield on next.
* @returns {Function} A function, when executed with the required parameters minus the callback, produces an Observable sequence with a single value of the arguments to the callback as an array.
*/
Observable.fromCallback = function (func, scheduler, context, selector) {
scheduler || (scheduler = immediateScheduler);
return function () {
var args = slice.call(arguments, 0);
return new AnonymousObservable(function (observer) {
return scheduler.schedule(function () {
function handler(e) {
var results = e;
if (selector) {
try {
results = selector(arguments);
} catch (err) {
observer.onError(err);
return;
}
} else {
if (results.length === 1) {
results = results[0];
}
}
observer.onNext(results);
observer.onCompleted();
}
args.push(handler);
func.apply(context, args);
});
});
};
};
/**
* Converts a Node.js callback style function to an observable sequence. This must be in function (err, ...) format.
* @param {Function} func The function to call
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @param {Function} [selector] A selector which takes the arguments from the callback minus the error to produce a single item to yield on next.
* @returns {Function} An async function which when applied, returns an observable sequence with the callback arguments as an array.
*/
Observable.fromNodeCallback = function (func, scheduler, context, selector) {
scheduler || (scheduler = immediateScheduler);
return function () {
var args = slice.call(arguments, 0);
return new AnonymousObservable(function (observer) {
return scheduler.schedule(function () {
function handler(err) {
if (err) {
observer.onError(err);
return;
}
var results = slice.call(arguments, 1);
if (selector) {
try {
results = selector(results);
} catch (e) {
observer.onError(e);
return;
}
} else {
if (results.length === 1) {
results = results[0];
}
}
observer.onNext(results);
observer.onCompleted();
}
args.push(handler);
func.apply(context, args);
});
});
};
};
function createListener (element, name, handler) {
// Node.js specific
if (element.addListener) {
element.addListener(name, handler);
return disposableCreate(function () {
element.removeListener(name, handler);
});
} else if (element.addEventListener) {
element.addEventListener(name, handler, false);
return disposableCreate(function () {
element.removeEventListener(name, handler, false);
});
}
}
function createEventListener (el, eventName, handler) {
var disposables = new CompositeDisposable();
// Asume NodeList
if (el && el.length) {
for (var i = 0, len = el.length; i < len; i++) {
disposables.add(createEventListener(el[i], eventName, handler));
}
} else if (el) {
disposables.add(createListener(el, eventName, handler));
}
return disposables;
}
/**
* Creates an observable sequence by adding an event listener to the matching DOMElement or each item in the NodeList.
*
* @example
* var source = Rx.Observable.fromEvent(element, 'mouseup');
*
* @param {Object} element The DOMElement or NodeList to attach a listener.
* @param {String} eventName The event name to attach the observable sequence.
* @param {Function} [selector] A selector which takes the arguments from the event handler to produce a single item to yield on next.
* @returns {Observable} An observable sequence of events from the specified element and the specified event.
*/
Observable.fromEvent = function (element, eventName, selector) {
return new AnonymousObservable(function (observer) {
return createEventListener(
element,
eventName,
function handler (e) {
var results = e;
if (selector) {
try {
results = selector(arguments);
} catch (err) {
observer.onError(err);
return
}
}
observer.onNext(results);
});
}).publish().refCount();
};
/**
* Creates an observable sequence from an event emitter via an addHandler/removeHandler pair.
* @param {Function} addHandler The function to add a handler to the emitter.
* @param {Function} [removeHandler] The optional function to remove a handler from an emitter.
* @param {Function} [selector] A selector which takes the arguments from the event handler to produce a single item to yield on next.
* @returns {Observable} An observable sequence which wraps an event from an event emitter
*/
Observable.fromEventPattern = function (addHandler, removeHandler, selector) {
return new AnonymousObservable(function (observer) {
function innerHandler (e) {
var result = e;
if (selector) {
try {
result = selector(arguments);
} catch (err) {
observer.onError(err);
return;
}
}
observer.onNext(result);
}
var returnValue = addHandler(innerHandler);
return disposableCreate(function () {
if (removeHandler) {
removeHandler(innerHandler, returnValue);
}
});
}).publish().refCount();
};
/**
* Converts a Promise to an Observable sequence
* @param {Promise} A Promises A+ implementation instance.
* @returns {Observable} An Observable sequence which wraps the existing promise success and failure.
*/
Observable.fromPromise = function (promise) {
return new AnonymousObservable(function (observer) {
promise.then(
function (value) {
observer.onNext(value);
observer.onCompleted();
},
function (reason) {
observer.onError(reason);
});
});
};
/*
* Converts an existing observable sequence to an ES6 Compatible Promise
* @example
* var promise = Rx.Observable.return(42).toPromise(RSVP.Promise);
* @param {Function} The constructor of the promise
* @returns {Promise} An ES6 compatible promise with the last value from the observable sequence.
*/
observableProto.toPromise = function (promiseCtor) {
var source = this;
return new promiseCtor(function (resolve, reject) {
// No cancellation can be done
var value, hasValue = false;
source.subscribe(function (v) {
value = v;
hasValue = true;
}, function (err) {
reject(err);
}, function () {
if (hasValue) {
resolve(value);
}
});
});
};
return Rx;
}));

View File

@@ -0,0 +1,561 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
var root = (objectTypes[typeof window] && window) || this,
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx', 'exports'], function (Rx, exports) {
root.Rx = factory(root, exports, Rx);
return root.Rx;
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
var Observable = Rx.Observable,
observableProto = Observable.prototype,
AnonymousObservable = Rx.Internals.AnonymousObservable,
Subject = Rx.Subject,
AsyncSubject = Rx.AsyncSubject,
Observer = Rx.Observer,
ScheduledObserver = Rx.Internals.ScheduledObserver,
disposableCreate = Rx.Disposable.create,
disposableEmpty = Rx.Disposable.empty,
CompositeDisposable = Rx.CompositeDisposable,
currentThreadScheduler = Rx.Scheduler.currentThread,
inherits = Rx.Internals.inherits,
addProperties = Rx.Internals.addProperties;
// Utilities
var objectDisposed = 'Object has been disposed';
function checkDisposed() {
if (this.isDisposed) {
throw new Error(objectDisposed);
}
}
/**
* Multicasts the source sequence notifications through an instantiated subject into all uses of the sequence within a selector function. Each
* subscription to the resulting sequence causes a separate multicast invocation, exposing the sequence resulting from the selector function's
* invocation. For specializations with fixed subject types, see Publish, PublishLast, and Replay.
*
* @example
* 1 - res = source.multicast(observable);
* 2 - res = source.multicast(function () { return new Subject(); }, function (x) { return x; });
*
* @param {Function|Subject} subjectOrSubjectSelector
* Factory function to create an intermediate subject through which the source sequence's elements will be multicast to the selector function.
* Or:
* Subject to push source elements into.
*
* @param {Function} [selector] Optional selector function which can use the multicasted source sequence subject to the policies enforced by the created subject. Specified only if <paramref name="subjectOrSubjectSelector" is a factory function.
* @returns {Observable} An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function.
*/
observableProto.multicast = function (subjectOrSubjectSelector, selector) {
var source = this;
return typeof subjectOrSubjectSelector === 'function' ?
new AnonymousObservable(function (observer) {
var connectable = source.multicast(subjectOrSubjectSelector());
return new CompositeDisposable(selector(connectable).subscribe(observer), connectable.connect());
}) :
new ConnectableObservable(source, subjectOrSubjectSelector);
};
/**
* Returns an observable sequence that is the result of invoking the selector on a connectable observable sequence that shares a single subscription to the underlying sequence.
* This operator is a specialization of Multicast using a regular Subject.
*
* @example
* var resres = source.publish();
* var res = source.publish(function (x) { return x; });
*
* @param {Function} [selector] Selector function which can use the multicasted source sequence as many times as needed, without causing multiple subscriptions to the source sequence. Subscribers to the given source will receive all notifications of the source from the time of the subscription on.
* @returns {Observable} An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function.
*/
observableProto.publish = function (selector) {
return !selector ?
this.multicast(new Subject()) :
this.multicast(function () {
return new Subject();
}, selector);
};
/**
* Returns an observable sequence that shares a single subscription to the underlying sequence.
* This operator is a specialization of publish which creates a subscription when the number of observers goes from zero to one, then shares that subscription with all subsequent observers until the number of observers returns to zero, at which point the subscription is disposed.
*
* @example
* var res = source.share();
*
* @returns {Observable} An observable sequence that contains the elements of a sequence produced by multicasting the source sequence.
*/
observableProto.share = function () {
return this.publish(null).refCount();
};
/**
* Returns an observable sequence that is the result of invoking the selector on a connectable observable sequence that shares a single subscription to the underlying sequence containing only the last notification.
* This operator is a specialization of Multicast using a AsyncSubject.
*
* @example
* var res = source.publishLast();
* var res = source.publishLast(function (x) { return x; });
*
* @param selector [Optional] Selector function which can use the multicasted source sequence as many times as needed, without causing multiple subscriptions to the source sequence. Subscribers to the given source will only receive the last notification of the source.
* @returns {Observable} An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function.
*/
observableProto.publishLast = function (selector) {
return !selector ?
this.multicast(new AsyncSubject()) :
this.multicast(function () {
return new AsyncSubject();
}, selector);
};
/**
* Returns an observable sequence that is the result of invoking the selector on a connectable observable sequence that shares a single subscription to the underlying sequence and starts with initialValue.
* This operator is a specialization of Multicast using a BehaviorSubject.
*
* @example
* var res = source.publishValue(42);
* var res = source.publishValue(function (x) { return x.select(function (y) { return y * y; }) }, 42);
*
* @param {Function} [selector] Optional selector function which can use the multicasted source sequence as many times as needed, without causing multiple subscriptions to the source sequence. Subscribers to the given source will receive immediately receive the initial value, followed by all notifications of the source from the time of the subscription on.
* @param {Mixed} initialValue Initial value received by observers upon subscription.
* @returns {Observable} An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function.
*/
observableProto.publishValue = function (initialValueOrSelector, initialValue) {
return arguments.length === 2 ?
this.multicast(function () {
return new BehaviorSubject(initialValue);
}, initialValueOrSelector) :
this.multicast(new BehaviorSubject(initialValueOrSelector));
};
/**
* Returns an observable sequence that shares a single subscription to the underlying sequence and starts with an initialValue.
* This operator is a specialization of publishValue which creates a subscription when the number of observers goes from zero to one, then shares that subscription with all subsequent observers until the number of observers returns to zero, at which point the subscription is disposed.
*
* @example
* var res = source.shareValue(42);
*
* @param {Mixed} initialValue Initial value received by observers upon subscription.
* @returns {Observable} An observable sequence that contains the elements of a sequence produced by multicasting the source sequence.
*/
observableProto.shareValue = function (initialValue) {
return this.publishValue(initialValue).
refCount();
};
/**
* Returns an observable sequence that is the result of invoking the selector on a connectable observable sequence that shares a single subscription to the underlying sequence replaying notifications subject to a maximum time length for the replay buffer.
* This operator is a specialization of Multicast using a ReplaySubject.
*
* @example
* var res = source.replay(null, 3);
* var res = source.replay(null, 3, 500);
* var res = source.replay(null, 3, 500, scheduler);
* var res = source.replay(function (x) { return x.take(6).repeat(); }, 3, 500, scheduler);
*
* @param selector [Optional] Selector function which can use the multicasted source sequence as many times as needed, without causing multiple subscriptions to the source sequence. Subscribers to the given source will receive all the notifications of the source subject to the specified replay buffer trimming policy.
* @param bufferSize [Optional] Maximum element count of the replay buffer.
* @param window [Optional] Maximum time length of the replay buffer.
* @param scheduler [Optional] Scheduler where connected observers within the selector function will be invoked on.
* @returns {Observable} An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function.
*/
observableProto.replay = function (selector, bufferSize, window, scheduler) {
return !selector ?
this.multicast(new ReplaySubject(bufferSize, window, scheduler)) :
this.multicast(function () {
return new ReplaySubject(bufferSize, window, scheduler);
}, selector);
};
/**
* Returns an observable sequence that shares a single subscription to the underlying sequence replaying notifications subject to a maximum time length for the replay buffer.
* This operator is a specialization of replay which creates a subscription when the number of observers goes from zero to one, then shares that subscription with all subsequent observers until the number of observers returns to zero, at which point the subscription is disposed.
*
* @example
* var res = source.replayWhileObserved(3);
* var res = source.replayWhileObserved(3, 500);
* var res = source.replayWhileObserved(3, 500, scheduler);
*
* @param bufferSize [Optional] Maximum element count of the replay buffer.
* @param window [Optional] Maximum time length of the replay buffer.
* @param scheduler [Optional] Scheduler where connected observers within the selector function will be invoked on.
* @returns {Observable} An observable sequence that contains the elements of a sequence produced by multicasting the source sequence.
*/
observableProto.replayWhileObserved = function (bufferSize, window, scheduler) {
return this.replay(null, bufferSize, window, scheduler).refCount();
};
/** @private */
var InnerSubscription = function (subject, observer) {
this.subject = subject;
this.observer = observer;
};
/**
* @private
* @memberOf InnerSubscription
*/
InnerSubscription.prototype.dispose = function () {
if (!this.subject.isDisposed && this.observer !== null) {
var idx = this.subject.observers.indexOf(this.observer);
this.subject.observers.splice(idx, 1);
this.observer = null;
}
};
/**
* Represents a value that changes over time.
* Observers can subscribe to the subject to receive the last (or initial) value and all subsequent notifications.
*/
var BehaviorSubject = Rx.BehaviorSubject = (function (_super) {
function subscribe(observer) {
checkDisposed.call(this);
if (!this.isStopped) {
this.observers.push(observer);
observer.onNext(this.value);
return new InnerSubscription(this, observer);
}
var ex = this.exception;
if (ex) {
observer.onError(ex);
} else {
observer.onCompleted();
}
return disposableEmpty;
}
inherits(BehaviorSubject, _super);
/**
* @constructor
* Initializes a new instance of the BehaviorSubject class which creates a subject that caches its last value and starts with the specified value.
* @param {Mixed} value Initial value sent to observers when no other value has been received by the subject yet.
*/
function BehaviorSubject(value) {
_super.call(this, subscribe);
this.value = value,
this.observers = [],
this.isDisposed = false,
this.isStopped = false,
this.exception = null;
}
addProperties(BehaviorSubject.prototype, Observer, {
/**
* Indicates whether the subject has observers subscribed to it.
* @returns {Boolean} Indicates whether the subject has observers subscribed to it.
*/
hasObservers: function () {
return this.observers.length > 0;
},
/**
* Notifies all subscribed observers about the end of the sequence.
*/
onCompleted: function () {
checkDisposed.call(this);
if (!this.isStopped) {
var os = this.observers.slice(0);
this.isStopped = true;
for (var i = 0, len = os.length; i < len; i++) {
os[i].onCompleted();
}
this.observers = [];
}
},
/**
* Notifies all subscribed observers about the exception.
* @param {Mixed} error The exception to send to all observers.
*/
onError: function (error) {
checkDisposed.call(this);
if (!this.isStopped) {
var os = this.observers.slice(0);
this.isStopped = true;
this.exception = error;
for (var i = 0, len = os.length; i < len; i++) {
os[i].onError(error);
}
this.observers = [];
}
},
/**
* Notifies all subscribed observers about the arrival of the specified element in the sequence.
* @param {Mixed} value The value to send to all observers.
*/
onNext: function (value) {
checkDisposed.call(this);
if (!this.isStopped) {
this.value = value;
var os = this.observers.slice(0);
for (var i = 0, len = os.length; i < len; i++) {
os[i].onNext(value);
}
}
},
/**
* Unsubscribe all observers and release resources.
*/
dispose: function () {
this.isDisposed = true;
this.observers = null;
this.value = null;
this.exception = null;
}
});
return BehaviorSubject;
}(Observable));
/**
* Represents an object that is both an observable sequence as well as an observer.
* Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies.
*/
var ReplaySubject = Rx.ReplaySubject = (function (_super) {
function RemovableDisposable (subject, observer) {
this.subject = subject;
this.observer = observer;
};
RemovableDisposable.prototype.dispose = function () {
this.observer.dispose();
if (!this.subject.isDisposed) {
var idx = this.subject.observers.indexOf(this.observer);
this.subject.observers.splice(idx, 1);
}
};
function subscribe(observer) {
var so = new ScheduledObserver(this.scheduler, observer),
subscription = new RemovableDisposable(this, so);
checkDisposed.call(this);
this._trim(this.scheduler.now());
this.observers.push(so);
var n = this.q.length;
for (var i = 0, len = this.q.length; i < len; i++) {
so.onNext(this.q[i].value);
}
if (this.hasError) {
n++;
so.onError(this.error);
} else if (this.isStopped) {
n++;
so.onCompleted();
}
so.ensureActive(n);
return subscription;
}
inherits(ReplaySubject, _super);
/**
* Initializes a new instance of the ReplaySubject class with the specified buffer size, window size and scheduler.
* @param {Number} [bufferSize] Maximum element count of the replay buffer.
* @param {Number} [windowSize] Maximum time length of the replay buffer.
* @param {Scheduler} [scheduler] Scheduler the observers are invoked on.
*/
function ReplaySubject(bufferSize, windowSize, scheduler) {
this.bufferSize = bufferSize == null ? Number.MAX_VALUE : bufferSize;
this.windowSize = windowSize == null ? Number.MAX_VALUE : windowSize;
this.scheduler = scheduler || currentThreadScheduler;
this.q = [];
this.observers = [];
this.isStopped = false;
this.isDisposed = false;
this.hasError = false;
this.error = null;
_super.call(this, subscribe);
}
addProperties(ReplaySubject.prototype, Observer, {
/**
* Indicates whether the subject has observers subscribed to it.
* @returns {Boolean} Indicates whether the subject has observers subscribed to it.
*/
hasObservers: function () {
return this.observers.length > 0;
},
/* @private */
_trim: function (now) {
while (this.q.length > this.bufferSize) {
this.q.shift();
}
while (this.q.length > 0 && (now - this.q[0].interval) > this.windowSize) {
this.q.shift();
}
},
/**
* Notifies all subscribed observers about the arrival of the specified element in the sequence.
* @param {Mixed} value The value to send to all observers.
*/
onNext: function (value) {
var observer;
checkDisposed.call(this);
if (!this.isStopped) {
var now = this.scheduler.now();
this.q.push({ interval: now, value: value });
this._trim(now);
var o = this.observers.slice(0);
for (var i = 0, len = o.length; i < len; i++) {
observer = o[i];
observer.onNext(value);
observer.ensureActive();
}
}
},
/**
* Notifies all subscribed observers about the exception.
* @param {Mixed} error The exception to send to all observers.
*/
onError: function (error) {
var observer;
checkDisposed.call(this);
if (!this.isStopped) {
this.isStopped = true;
this.error = error;
this.hasError = true;
var now = this.scheduler.now();
this._trim(now);
var o = this.observers.slice(0);
for (var i = 0, len = o.length; i < len; i++) {
observer = o[i];
observer.onError(error);
observer.ensureActive();
}
this.observers = [];
}
},
/**
* Notifies all subscribed observers about the end of the sequence.
*/
onCompleted: function () {
var observer;
checkDisposed.call(this);
if (!this.isStopped) {
this.isStopped = true;
var now = this.scheduler.now();
this._trim(now);
var o = this.observers.slice(0);
for (var i = 0, len = o.length; i < len; i++) {
observer = o[i];
observer.onCompleted();
observer.ensureActive();
}
this.observers = [];
}
},
/**
* Unsubscribe all observers and release resources.
*/
dispose: function () {
this.isDisposed = true;
this.observers = null;
}
});
return ReplaySubject;
}(Observable));
/** @private */
var ConnectableObservable = Rx.ConnectableObservable = (function (_super) {
inherits(ConnectableObservable, _super);
/**
* @constructor
* @private
*/
function ConnectableObservable(source, subject) {
var state = {
subject: subject,
source: source.asObservable(),
hasSubscription: false,
subscription: null
};
this.connect = function () {
if (!state.hasSubscription) {
state.hasSubscription = true;
state.subscription = new CompositeDisposable(state.source.subscribe(state.subject), disposableCreate(function () {
state.hasSubscription = false;
}));
}
return state.subscription;
};
function subscribe(observer) {
return state.subject.subscribe(observer);
}
_super.call(this, subscribe);
}
/**
* @private
* @memberOf ConnectableObservable
*/
ConnectableObservable.prototype.connect = function () { return this.connect(); };
/**
* @private
* @memberOf ConnectableObservable
*/
ConnectableObservable.prototype.refCount = function () {
var connectableSubscription = null, count = 0, source = this;
return new AnonymousObservable(function (observer) {
var shouldConnect, subscription;
count++;
shouldConnect = count === 1;
subscription = source.subscribe(observer);
if (shouldConnect) {
connectableSubscription = source.connect();
}
return disposableCreate(function () {
subscription.dispose();
count--;
if (count === 0) {
connectableSubscription.dispose();
}
});
});
};
return ConnectableObservable;
}(Observable));
return Rx;
}));

View File

@@ -0,0 +1,691 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
var root = (objectTypes[typeof window] && window) || this,
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx', 'exports'], function (Rx, exports) {
root.Rx = factory(root, exports, Rx);
return root.Rx;
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
var Observable = Rx.Observable,
CompositeDisposable = Rx.CompositeDisposable,
RefCountDisposable = Rx.RefCountDisposable,
SingleAssignmentDisposable = Rx.SingleAssignmentDisposable,
SerialDisposable = Rx.SerialDisposable,
Subject = Rx.Subject,
observableProto = Observable.prototype,
observableEmpty = Observable.empty,
AnonymousObservable = Rx.Internals.AnonymousObservable,
observerCreate = Rx.Observer.create,
addRef = Rx.Internals.addRef;
// defaults
function noop() { }
function defaultComparer(x, y) { return x === y; }
// Real Dictionary
var primes = [1, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647];
var noSuchkey = "no such key";
var duplicatekey = "duplicate key";
function isPrime(candidate) {
if (candidate & 1 === 0) {
return candidate === 2;
}
var num1 = Math.sqrt(candidate),
num2 = 3;
while (num2 <= num1) {
if (candidate % num2 === 0) {
return false;
}
num2 += 2;
}
return true;
}
function getPrime(min) {
var index, num, candidate;
for (index = 0; index < primes.length; ++index) {
num = primes[index];
if (num >= min) {
return num;
}
}
candidate = min | 1;
while (candidate < primes[primes.length - 1]) {
if (isPrime(candidate)) {
return candidate;
}
candidate += 2;
}
return min;
}
function stringHashFn(str) {
var hash = 757602046;
if (!str.length) {
return hash;
}
for (var i = 0, len = str.length; i < len; i++) {
var character = str.charCodeAt(i);
hash = ((hash<<5)-hash)+character;
hash = hash & hash;
}
return hash;
}
function numberHashFn(key) {
var c2 = 0x27d4eb2d;
key = (key ^ 61) ^ (key >>> 16);
key = key + (key << 3);
key = key ^ (key >>> 4);
key = key * c2;
key = key ^ (key >>> 15);
return key;
}
var getHashCode = (function () {
var uniqueIdCounter = 0;
return function (obj) {
if (obj == null) {
throw new Error(noSuchkey);
}
// Check for built-ins before tacking on our own for any object
if (typeof obj === 'string') {
return stringHashFn(obj);
}
if (typeof obj === 'number') {
return numberHashFn(obj);
}
if (typeof obj === 'boolean') {
return obj === true ? 1 : 0;
}
if (obj instanceof Date) {
return obj.getTime();
}
if (obj.getHashCode) {
return obj.getHashCode();
}
var id = 17 * uniqueIdCounter++;
obj.getHashCode = function () { return id; };
return id;
};
} ());
function newEntry() {
return { key: null, value: null, next: 0, hashCode: 0 };
}
// Dictionary implementation
var Dictionary = function (capacity, comparer) {
if (capacity < 0) {
throw new Error('out of range')
}
if (capacity > 0) {
this._initialize(capacity);
}
this.comparer = comparer || defaultComparer;
this.freeCount = 0;
this.size = 0;
this.freeList = -1;
};
Dictionary.prototype._initialize = function (capacity) {
var prime = getPrime(capacity), i;
this.buckets = new Array(prime);
this.entries = new Array(prime);
for (i = 0; i < prime; i++) {
this.buckets[i] = -1;
this.entries[i] = newEntry();
}
this.freeList = -1;
};
Dictionary.prototype.count = function () {
return this.size;
};
Dictionary.prototype.add = function (key, value) {
return this._insert(key, value, true);
};
Dictionary.prototype._insert = function (key, value, add) {
if (!this.buckets) {
this._initialize(0);
}
var index3;
var num = getHashCode(key) & 2147483647;
var index1 = num % this.buckets.length;
for (var index2 = this.buckets[index1]; index2 >= 0; index2 = this.entries[index2].next) {
if (this.entries[index2].hashCode === num && this.comparer(this.entries[index2].key, key)) {
if (add) {
throw new Error(duplicatekey);
}
this.entries[index2].value = value;
return;
}
}
if (this.freeCount > 0) {
index3 = this.freeList;
this.freeList = this.entries[index3].next;
--this.freeCount;
} else {
if (this.size === this.entries.length) {
this._resize();
index1 = num % this.buckets.length;
}
index3 = this.size;
++this.size;
}
this.entries[index3].hashCode = num;
this.entries[index3].next = this.buckets[index1];
this.entries[index3].key = key;
this.entries[index3].value = value;
this.buckets[index1] = index3;
};
Dictionary.prototype._resize = function () {
var prime = getPrime(this.size * 2),
numArray = new Array(prime);
for (index = 0; index < numArray.length; ++index) {
numArray[index] = -1;
}
var entryArray = new Array(prime);
for (index = 0; index < this.size; ++index) {
entryArray[index] = this.entries[index];
}
for (var index = this.size; index < prime; ++index) {
entryArray[index] = newEntry();
}
for (var index1 = 0; index1 < this.size; ++index1) {
var index2 = entryArray[index1].hashCode % prime;
entryArray[index1].next = numArray[index2];
numArray[index2] = index1;
}
this.buckets = numArray;
this.entries = entryArray;
};
Dictionary.prototype.remove = function (key) {
if (this.buckets) {
var num = getHashCode(key) & 2147483647;
var index1 = num % this.buckets.length;
var index2 = -1;
for (var index3 = this.buckets[index1]; index3 >= 0; index3 = this.entries[index3].next) {
if (this.entries[index3].hashCode === num && this.comparer(this.entries[index3].key, key)) {
if (index2 < 0) {
this.buckets[index1] = this.entries[index3].next;
} else {
this.entries[index2].next = this.entries[index3].next;
}
this.entries[index3].hashCode = -1;
this.entries[index3].next = this.freeList;
this.entries[index3].key = null;
this.entries[index3].value = null;
this.freeList = index3;
++this.freeCount;
return true;
} else {
index2 = index3;
}
}
}
return false;
};
Dictionary.prototype.clear = function () {
var index, len;
if (this.size <= 0) {
return;
}
for (index = 0, len = this.buckets.length; index < len; ++index) {
this.buckets[index] = -1;
}
for (index = 0; index < this.size; ++index) {
this.entries[index] = newEntry();
}
this.freeList = -1;
this.size = 0;
};
Dictionary.prototype._findEntry = function (key) {
if (this.buckets) {
var num = getHashCode(key) & 2147483647;
for (var index = this.buckets[num % this.buckets.length]; index >= 0; index = this.entries[index].next) {
if (this.entries[index].hashCode === num && this.comparer(this.entries[index].key, key)) {
return index;
}
}
}
return -1;
};
Dictionary.prototype.count = function () {
return this.size - this.freeCount;
};
Dictionary.prototype.tryGetValue = function (key) {
var entry = this._findEntry(key);
if (entry >= 0) {
return this.entries[entry].value;
}
return undefined;
};
Dictionary.prototype.getValues = function () {
var index = 0, results = [];
if (this.entries) {
for (var index1 = 0; index1 < this.size; index1++) {
if (this.entries[index1].hashCode >= 0) {
results[index++] = this.entries[index1].value;
}
}
}
return results;
};
Dictionary.prototype.get = function (key) {
var entry = this._findEntry(key);
if (entry >= 0) {
return this.entries[entry].value;
}
throw new Error(noSuchkey);
};
Dictionary.prototype.set = function (key, value) {
this._insert(key, value, false);
};
Dictionary.prototype.containskey = function (key) {
return this._findEntry(key) >= 0;
};
/**
* Correlates the elements of two sequences based on overlapping durations.
*
* @param {Observable} right The right observable sequence to join elements for.
* @param {Function} leftDurationSelector A function to select the duration (expressed as an observable sequence) of each element of the left observable sequence, used to determine overlap.
* @param {Function} rightDurationSelector A function to select the duration (expressed as an observable sequence) of each element of the right observable sequence, used to determine overlap.
* @param {Function} resultSelector A function invoked to compute a result element for any two overlapping elements of the left and right observable sequences. The parameters passed to the function correspond with the elements from the left and right source sequences for which overlap occurs.
* @returns {Observable} An observable sequence that contains result elements computed from source elements that have an overlapping duration.
*/
observableProto.join = function (right, leftDurationSelector, rightDurationSelector, resultSelector) {
var left = this;
return new AnonymousObservable(function (observer) {
var group = new CompositeDisposable(),
leftDone = false,
leftId = 0,
leftMap = new Dictionary(),
rightDone = false,
rightId = 0,
rightMap = new Dictionary();
group.add(left.subscribe(function (value) {
var duration,
expire,
id = leftId++,
md = new SingleAssignmentDisposable(),
result,
values;
leftMap.add(id, value);
group.add(md);
expire = function () {
if (leftMap.remove(id) && leftMap.count() === 0 && leftDone) {
observer.onCompleted();
}
return group.remove(md);
};
try {
duration = leftDurationSelector(value);
} catch (e) {
observer.onError(e);
return;
}
md.setDisposable(duration.take(1).subscribe(noop, observer.onError.bind(observer), function () { expire(); }));
values = rightMap.getValues();
for (var i = 0; i < values.length; i++) {
try {
result = resultSelector(value, values[i]);
} catch (exception) {
observer.onError(exception);
return;
}
observer.onNext(result);
}
}, observer.onError.bind(observer), function () {
leftDone = true;
if (rightDone || leftMap.count() === 0) {
observer.onCompleted();
}
}));
group.add(right.subscribe(function (value) {
var duration,
expire,
id = rightId++,
md = new SingleAssignmentDisposable(),
result,
values;
rightMap.add(id, value);
group.add(md);
expire = function () {
if (rightMap.remove(id) && rightMap.count() === 0 && rightDone) {
observer.onCompleted();
}
return group.remove(md);
};
try {
duration = rightDurationSelector(value);
} catch (exception) {
observer.onError(exception);
return;
}
md.setDisposable(duration.take(1).subscribe(noop, observer.onError.bind(observer), function () { expire(); }));
values = leftMap.getValues();
for (var i = 0; i < values.length; i++) {
try {
result = resultSelector(values[i], value);
} catch (exception) {
observer.onError(exception);
return;
}
observer.onNext(result);
}
}, observer.onError.bind(observer), function () {
rightDone = true;
if (leftDone || rightMap.count() === 0) {
observer.onCompleted();
}
}));
return group;
});
};
/**
* Correlates the elements of two sequences based on overlapping durations, and groups the results.
*
* @param {Observable} right The right observable sequence to join elements for.
* @param {Function} leftDurationSelector A function to select the duration (expressed as an observable sequence) of each element of the left observable sequence, used to determine overlap.
* @param {Function} rightDurationSelector A function to select the duration (expressed as an observable sequence) of each element of the right observable sequence, used to determine overlap.
* @param {Function} resultSelector A function invoked to compute a result element for any element of the left sequence with overlapping elements from the right observable sequence. The first parameter passed to the function is an element of the left sequence. The second parameter passed to the function is an observable sequence with elements from the right sequence that overlap with the left sequence's element.
* @returns {Observable} An observable sequence that contains result elements computed from source elements that have an overlapping duration.
*/
observableProto.groupJoin = function (right, leftDurationSelector, rightDurationSelector, resultSelector) {
var left = this;
return new AnonymousObservable(function (observer) {
var nothing = function () {};
var group = new CompositeDisposable();
var r = new RefCountDisposable(group);
var leftMap = new Dictionary();
var rightMap = new Dictionary();
var leftID = 0;
var rightID = 0;
group.add(left.subscribe(
function (value) {
var s = new Subject();
var id = leftID++;
leftMap.add(id, s);
var i, len, leftValues, rightValues;
var result;
try {
result = resultSelector(value, addRef(s, r));
} catch (e) {
leftValues = leftMap.getValues();
for (i = 0, len = leftValues.length; i < len; i++) {
leftValues[i].onError(e);
}
observer.onError(e);
return;
}
observer.onNext(result);
rightValues = rightMap.getValues();
for (i = 0, len = rightValues.length; i < len; i++) {
s.onNext(rightValues[i]);
}
var md = new SingleAssignmentDisposable();
group.add(md);
var expire = function () {
if (leftMap.remove(id)) {
s.onCompleted();
}
group.remove(md);
};
var duration;
try {
duration = leftDurationSelector(value);
} catch (e) {
leftValues = leftMap.getValues();
for (i = 0, len = leftMap.length; i < len; i++) {
leftValues[i].onError(e);
}
observer.onError(e);
return;
}
md.setDisposable(duration.take(1).subscribe(
nothing,
function (e) {
leftValues = leftMap.getValues();
for (i = 0, len = leftValues.length; i < len; i++) {
leftValues[i].onError(e);
}
observer.onError(e);
},
expire)
);
},
function (e) {
var leftValues = leftMap.getValues();
for (var i = 0, len = leftValues.length; i < len; i++) {
leftValues[i].onError(e);
}
observer.onError(e);
},
observer.onCompleted.bind(observer)));
group.add(right.subscribe(
function (value) {
var leftValues, i, len;
var id = rightID++;
rightMap.add(id, value);
var md = new SingleAssignmentDisposable();
group.add(md);
var expire = function () {
rightMap.remove(id);
group.remove(md);
};
var duration;
try {
duration = rightDurationSelector(value);
} catch (e) {
leftValues = leftMap.getValues();
for (i = 0, len = leftMap.length; i < len; i++) {
leftValues[i].onError(e);
}
observer.onError(e);
return;
}
md.setDisposable(duration.take(1).subscribe(
nothing,
function (e) {
leftValues = leftMap.getValues();
for (i = 0, len = leftMap.length; i < len; i++) {
leftValues[i].onError(e);
}
observer.onError(e);
},
expire)
);
leftValues = leftMap.getValues();
for (i = 0, len = leftValues.length; i < len; i++) {
leftValues[i].onNext(value);
}
},
function (e) {
var leftValues = leftMap.getValues();
for (var i = 0, len = leftValues.length; i < len; i++) {
leftValues[i].onError(e);
}
observer.onError(e);
}));
return r;
});
};
/**
* Projects each element of an observable sequence into zero or more buffers.
*
* @param {Mixed} bufferOpeningsOrClosingSelector Observable sequence whose elements denote the creation of new windows, or, a function invoked to define the boundaries of the produced windows (a new window is started when the previous one is closed, resulting in non-overlapping windows).
* @param {Function} [bufferClosingSelector] A function invoked to define the closing of each produced window. If a closing selector function is specified for the first parameter, this parameter is ignored.
* @returns {Observable} An observable sequence of windows.
*/
observableProto.buffer = function (bufferOpeningsOrClosingSelector, bufferClosingSelector) {
return this.window.apply(this, arguments).selectMany(function (x) { return x.toArray(); });
};
/**
* Projects each element of an observable sequence into zero or more windows.
*
* @param {Mixed} windowOpeningsOrClosingSelector Observable sequence whose elements denote the creation of new windows, or, a function invoked to define the boundaries of the produced windows (a new window is started when the previous one is closed, resulting in non-overlapping windows).
* @param {Function} [windowClosingSelector] A function invoked to define the closing of each produced window. If a closing selector function is specified for the first parameter, this parameter is ignored.
* @returns {Observable} An observable sequence of windows.
*/
observableProto.window = function (windowOpeningsOrClosingSelector, windowClosingSelector) {
if (arguments.length === 1 && typeof arguments[0] !== 'function') {
return observableWindowWithBounaries.call(this, windowOpeningsOrClosingSelector);
}
return typeof windowOpeningsOrClosingSelector === 'function' ?
observableWindowWithClosingSelector.call(this, windowOpeningsOrClosingSelector) :
observableWindowWithOpenings.call(this, windowOpeningsOrClosingSelector, windowClosingSelector);
};
function observableWindowWithOpenings(windowOpenings, windowClosingSelector) {
return windowOpenings.groupJoin(this, windowClosingSelector, function () {
return observableEmpty();
}, function (_, window) {
return window;
});
}
function observableWindowWithBounaries(windowBoundaries) {
var source = this;
return new AnonymousObservable(function (observer) {
var window = new Subject(),
d = new CompositeDisposable(),
r = new RefCountDisposable(d);
observer.onNext(addRef(window, r));
d.add(source.subscribe(function (x) {
window.onNext(x);
}, function (err) {
window.onError(err);
observer.onError(err);
}, function () {
window.onCompleted();
observer.onCompleted();
}));
d.add(windowBoundaries.subscribe(function (w) {
window.onCompleted();
window = new Subject();
observer.onNext(addRef(window, r));
}, function (err) {
window.onError(err);
observer.onError(err);
}, function () {
window.onCompleted();
observer.onCompleted();
}));
return r;
});
}
function observableWindowWithClosingSelector(windowClosingSelector) {
var source = this;
return new AnonymousObservable(function (observer) {
var createWindowClose,
m = new SerialDisposable(),
d = new CompositeDisposable(m),
r = new RefCountDisposable(d),
window = new Subject();
observer.onNext(addRef(window, r));
d.add(source.subscribe(function (x) {
window.onNext(x);
}, function (ex) {
window.onError(ex);
observer.onError(ex);
}, function () {
window.onCompleted();
observer.onCompleted();
}));
createWindowClose = function () {
var m1, windowClose;
try {
windowClose = windowClosingSelector();
} catch (exception) {
observer.onError(exception);
return;
}
m1 = new SingleAssignmentDisposable();
m.setDisposable(m1);
m1.setDisposable(windowClose.take(1).subscribe(noop, function (ex) {
window.onError(ex);
observer.onError(ex);
}, function () {
window.onCompleted();
window = new Subject();
observer.onNext(addRef(window, r));
createWindowClose();
}));
};
createWindowClose();
return r;
});
}
return Rx;
}));

View File

@@ -0,0 +1,453 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
var root = (objectTypes[typeof window] && window) || this,
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx', 'exports'], function (Rx, exports) {
root.Rx = factory(root, exports, Rx);
return root.Rx;
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
// Aliases
var Observable = Rx.Observable,
observableProto = Observable.prototype,
AnonymousObservable = Rx.Internals.AnonymousObservable,
observableConcat = Observable.concat,
observableDefer = Observable.defer,
observableEmpty = Observable.empty,
disposableEmpty = Rx.Disposable.empty,
BinaryObserver = Rx.Internals.BinaryObserver,
CompositeDisposable = Rx.CompositeDisposable,
SerialDisposable = Rx.SerialDisposable,
SingleAssignmentDisposable = Rx.SingleAssignmentDisposable,
enumeratorCreate = Rx.Internals.Enumerator.create,
Enumerable = Rx.Internals.Enumerable,
enumerableForEach = Enumerable.forEach,
immediateScheduler = Rx.Scheduler.immediate,
currentThreadScheduler = Rx.Scheduler.currentThread,
slice = Array.prototype.slice,
AsyncSubject = Rx.AsyncSubject,
Observer = Rx.Observer,
inherits = Rx.Internals.inherits,
addProperties = Rx.Internals.addProperties;
// Utilities
function nothing () { }
function argsOrArray(args, idx) {
return args.length === 1 && Array.isArray(args[idx]) ?
args[idx] :
slice.call(args);
}
function enumerableWhile(condition, source) {
return new Enumerable(function () {
var current;
return enumeratorCreate(function () {
if (condition()) {
current = source;
return true;
}
return false;
}, function () { return current; });
});
}
/**
* Returns an observable sequence that is the result of invoking the selector on the source sequence, without sharing subscriptions.
* This operator allows for a fluent style of writing queries that use the same sequence multiple times.
*
* @param {Function} selector Selector function which can use the source sequence as many times as needed, without sharing subscriptions to the source sequence.
* @returns {Observable} An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function.
*/
observableProto.letBind = observableProto['let'] = function (func) {
return func(this);
};
/**
* Determines whether an observable collection contains values. There is an alias for this method called 'ifThen' for browsers <IE9
*
* @example
* 1 - res = Rx.Observable.if(condition, obs1);
* 2 - res = Rx.Observable.if(condition, obs1, obs2);
* 3 - res = Rx.Observable.if(condition, obs1, scheduler);
* @param {Function} condition The condition which determines if the thenSource or elseSource will be run.
* @param {Observable} thenSource The observable sequence that will be run if the condition function returns true.
* @param {Observable} [elseSource] The observable sequence that will be run if the condition function returns false. If this is not provided, it defaults to Rx.Observabe.Empty with the specified scheduler.
* @returns {Observable} An observable sequence which is either the thenSource or elseSource.
*/
Observable['if'] = Observable.ifThen = function (condition, thenSource, elseSourceOrScheduler) {
return observableDefer(function () {
elseSourceOrScheduler || (elseSourceOrScheduler = observableEmpty());
if (elseSourceOrScheduler.now) {
var scheduler = elseSourceOrScheduler;
elseSourceOrScheduler = observableEmpty(scheduler);
}
return condition() ? thenSource : elseSourceOrScheduler;
});
};
/**
* Concatenates the observable sequences obtained by running the specified result selector for each element in source.
* There is an alias for this method called 'forIn' for browsers <IE9
* @param {Array} sources An array of values to turn into an observable sequence.
* @param {Function} resultSelector A function to apply to each item in the sources array to turn it into an observable sequence.
* @returns {Observable} An observable sequence from the concatenated observable sequences.
*/
Observable['for'] = Observable.forIn = function (sources, resultSelector) {
return enumerableForEach(sources, resultSelector).concat();
};
/**
* Repeats source as long as condition holds emulating a while loop.
* There is an alias for this method called 'whileDo' for browsers <IE9
*
* @param {Function} condition The condition which determines if the source will be repeated.
* @param {Observable} source The observable sequence that will be run if the condition function returns true.
* @returns {Observable} An observable sequence which is repeated as long as the condition holds.
*/
var observableWhileDo = Observable['while'] = Observable.whileDo = function (condition, source) {
return enumerableWhile(condition, source).concat();
};
/**
* Repeats source as long as condition holds emulating a do while loop.
*
* @param {Function} condition The condition which determines if the source will be repeated.
* @param {Observable} source The observable sequence that will be run if the condition function returns true.
* @returns {Observable} An observable sequence which is repeated as long as the condition holds.
*/
observableProto.doWhile = function (condition) {
return observableConcat([this, observableWhileDo(condition, this)]);
};
/**
* Uses selector to determine which source in sources to use.
* There is an alias 'switchCase' for browsers <IE9.
*
* @example
* 1 - res = Rx.Observable.case(selector, { '1': obs1, '2': obs2 });
* 1 - res = Rx.Observable.case(selector, { '1': obs1, '2': obs2 }, obs0);
* 1 - res = Rx.Observable.case(selector, { '1': obs1, '2': obs2 }, scheduler);
*
* @param {Function} selector The function which extracts the value for to test in a case statement.
* @param {Array} sources A object which has keys which correspond to the case statement labels.
* @param {Observable} [elseSource] The observable sequence that will be run if the sources are not matched. If this is not provided, it defaults to Rx.Observabe.Empty with the specified scheduler.
*
* @returns {Observable} An observable sequence which is determined by a case statement.
*/
Observable['case'] = Observable.switchCase = function (selector, sources, defaultSourceOrScheduler) {
return observableDefer(function () {
defaultSourceOrScheduler || (defaultSourceOrScheduler = observableEmpty());
if (defaultSourceOrScheduler.now) {
var scheduler = defaultSourceOrScheduler;
defaultSourceOrScheduler = observableEmpty(scheduler);
}
var result = sources[selector()];
return result !== undefined ? result : defaultSourceOrScheduler;
});
};
/**
* Expands an observable sequence by recursively invoking selector.
*
* @param {Function} selector Selector function to invoke for each produced element, resulting in another sequence to which the selector will be invoked recursively again.
* @param {Scheduler} [scheduler] Scheduler on which to perform the expansion. If not provided, this defaults to the current thread scheduler.
* @returns {Observable} An observable sequence containing all the elements produced by the recursive expansion.
*/
observableProto.expand = function (selector, scheduler) {
scheduler || (scheduler = immediateScheduler);
var source = this;
return new AnonymousObservable(function (observer) {
var q = [],
m = new SerialDisposable(),
d = new CompositeDisposable(m),
activeCount = 0,
isAcquired = false;
var ensureActive = function () {
var isOwner = false;
if (q.length > 0) {
isOwner = !isAcquired;
isAcquired = true;
}
if (isOwner) {
m.setDisposable(scheduler.scheduleRecursive(function (self) {
var work;
if (q.length > 0) {
work = q.shift();
} else {
isAcquired = false;
return;
}
var m1 = new SingleAssignmentDisposable();
d.add(m1);
m1.setDisposable(work.subscribe(function (x) {
observer.onNext(x);
var result = null;
try {
result = selector(x);
} catch (e) {
observer.onError(e);
}
q.push(result);
activeCount++;
ensureActive();
}, observer.onError.bind(observer), function () {
d.remove(m1);
activeCount--;
if (activeCount === 0) {
observer.onCompleted();
}
}));
self();
}));
}
};
q.push(source);
activeCount++;
ensureActive();
return d;
});
};
/**
* Runs all observable sequences in parallel and collect their last elements.
*
* @example
* 1 - res = Rx.Observable.forkJoin([obs1, obs2]);
* 1 - res = Rx.Observable.forkJoin(obs1, obs2, ...);
* @returns {Observable} An observable sequence with an array collecting the last elements of all the input sequences.
*/
Observable.forkJoin = function () {
var allSources = argsOrArray(arguments, 0);
return new AnonymousObservable(function (subscriber) {
var count = allSources.length;
if (count === 0) {
subscriber.onCompleted();
return disposableEmpty;
}
var group = new CompositeDisposable(),
finished = false,
hasResults = new Array(count),
hasCompleted = new Array(count),
results = new Array(count);
for (var idx = 0; idx < count; idx++) {
(function (i) {
var source = allSources[i];
group.add(source.subscribe(function (value) {
if (!finished) {
hasResults[i] = true;
results[i] = value;
}
}, function (e) {
finished = true;
subscriber.onError(e);
group.dispose();
}, function () {
if (!finished) {
if (!hasResults[i]) {
subscriber.onCompleted();
return;
}
hasCompleted[i] = true;
for (var ix = 0; ix < count; ix++) {
if (!hasCompleted[ix]) {
return;
}
}
finished = true;
subscriber.onNext(results);
subscriber.onCompleted();
}
}));
})(idx);
}
return group;
});
};
/**
* Runs two observable sequences in parallel and combines their last elemenets.
*
* @param {Observable} second Second observable sequence.
* @param {Function} resultSelector Result selector function to invoke with the last elements of both sequences.
* @returns {Observable} An observable sequence with the result of calling the selector function with the last elements of both input sequences.
*/
observableProto.forkJoin = function (second, resultSelector) {
var first = this;
return new AnonymousObservable(function (observer) {
var leftStopped = false, rightStopped = false,
hasLeft = false, hasRight = false,
lastLeft, lastRight,
leftSubscription = new SingleAssignmentDisposable(), rightSubscription = new SingleAssignmentDisposable();
leftSubscription.setDisposable(
first.subscribe(function (left) {
hasLeft = true;
lastLeft = left;
}, function (err) {
rightSubscription.dispose();
observer.onError(err);
}, function () {
leftStopped = true;
if (rightStopped) {
if (!hasLeft) {
observer.onCompleted();
} else if (!hasRight) {
observer.onCompleted();
} else {
var result;
try {
result = resultSelector(lastLeft, lastRight);
} catch (e) {
observer.onError(e);
return;
}
observer.onNext(result);
observer.onCompleted();
}
}
})
);
rightSubscription.setDisposable(
second.subscribe(function (right) {
hasRight = true;
lastRight = right;
}, function (err) {
leftSubscription.dispose();
observer.onError(err);
}, function () {
rightStopped = true;
if (leftStopped) {
if (!hasLeft) {
observer.onCompleted();
} else if (!hasRight) {
observer.onCompleted();
} else {
var result;
try {
result = resultSelector(lastLeft, lastRight);
} catch (e) {
observer.onError(e);
return;
}
observer.onNext(result);
observer.onCompleted();
}
}
})
);
return new CompositeDisposable(leftSubscription, rightSubscription);
});
};
/**
* Comonadic bind operator.
* @param {Function} selector A transform function to apply to each element.
* @param {Object} scheduler Scheduler used to execute the operation. If not specified, defaults to the ImmediateScheduler.
* @returns {Observable} An observable sequence which results from the comonadic bind operation.
*/
observableProto.manySelect = function (selector, scheduler) {
scheduler || (scheduler = immediateScheduler);
var source = this;
return observableDefer(function () {
var chain;
return source
.select(
function (x) {
var curr = new ChainObservable(x);
if (chain) {
chain.onNext(x);
}
chain = curr;
return curr;
})
.doAction(
nothing,
function (e) {
if (chain) {
chain.onError(e);
}
},
function () {
if (chain) {
chain.onCompleted();
}
})
.observeOn(scheduler)
.select(function (x, i, o) { return selector(x, i, o); });
});
};
var ChainObservable = (function (_super) {
function subscribe (observer) {
var self = this, g = new CompositeDisposable();
g.add(currentThreadScheduler.schedule(function () {
observer.onNext(self.head);
g.add(self.tail.mergeObservable().subscribe(observer));
}));
return g;
}
inherits(ChainObservable, _super);
function ChainObservable(head) {
_super.call(this, subscribe);
this.head = head;
this.tail = new AsyncSubject();
}
addProperties(ChainObservable.prototype, Observer, {
onCompleted: function () {
this.onNext(Observable.empty());
},
onError: function (e) {
this.onNext(Observable.throwException(e));
},
onNext: function (v) {
this.tail.onNext(v);
this.tail.onCompleted();
}
});
return ChainObservable;
}(Observable));
return Rx;
}));

View File

@@ -0,0 +1,418 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
var root = (objectTypes[typeof window] && window) || this,
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx', 'exports'], function (Rx, exports) {
root.Rx = factory(root, exports, Rx);
return root.Rx;
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
// Aliases
var Observable = Rx.Observable,
observableProto = Observable.prototype,
AnonymousObservable = Rx.Internals.AnonymousObservable,
observableThrow = Observable.throwException,
observerCreate = Rx.Observer.create,
SingleAssignmentDisposable = Rx.SingleAssignmentDisposable,
CompositeDisposable = Rx.CompositeDisposable,
AbstractObserver = Rx.Internals.AbstractObserver,
isEqual = Rx.Internals.isEqual;
// Defaults
function defaultComparer(x, y) { return isEqual(x, y); }
function noop() { }
// Utilities
var inherits = Rx.Internals.inherits;
var slice = Array.prototype.slice;
function argsOrArray(args, idx) {
return args.length === 1 && Array.isArray(args[idx]) ?
args[idx] :
slice.call(args);
}
/** @private */
var Map = (function () {
/**
* @constructor
* @private
*/
function Map() {
this.keys = [];
this.values = [];
}
/**
* @private
* @memberOf Map#
*/
Map.prototype['delete'] = function (key) {
var i = this.keys.indexOf(key);
if (i !== -1) {
this.keys.splice(i, 1);
this.values.splice(i, 1);
}
return i !== -1;
};
/**
* @private
* @memberOf Map#
*/
Map.prototype.get = function (key, fallback) {
var i = this.keys.indexOf(key);
return i !== -1 ? this.values[i] : fallback;
};
/**
* @private
* @memberOf Map#
*/
Map.prototype.set = function (key, value) {
var i = this.keys.indexOf(key);
if (i !== -1) {
this.values[i] = value;
}
this.values[this.keys.push(key) - 1] = value;
};
/**
* @private
* @memberOf Map#
*/
Map.prototype.size = function () { return this.keys.length; };
/**
* @private
* @memberOf Map#
*/
Map.prototype.has = function (key) {
return this.keys.indexOf(key) !== -1;
};
/**
* @private
* @memberOf Map#
*/
Map.prototype.getKeys = function () { return this.keys.slice(0); };
/**
* @private
* @memberOf Map#
*/
Map.prototype.getValues = function () { return this.values.slice(0); };
return Map;
}());
/**
* @constructor
* Represents a join pattern over observable sequences.
*/
function Pattern(patterns) {
this.patterns = patterns;
}
/**
* Creates a pattern that matches the current plan matches and when the specified observable sequences has an available value.
*
* @param other Observable sequence to match in addition to the current pattern.
* @return Pattern object that matches when all observable sequences in the pattern have an available value.
*/
Pattern.prototype.and = function (other) {
var patterns = this.patterns.slice(0);
patterns.push(other);
return new Pattern(patterns);
};
/**
* Matches when all observable sequences in the pattern (specified using a chain of and operators) have an available value and projects the values.
*
* @param selector Selector that will be invoked with available values from the source sequences, in the same order of the sequences in the pattern.
* @return Plan that produces the projected values, to be fed (with other plans) to the when operator.
*/
Pattern.prototype.then = function (selector) {
return new Plan(this, selector);
};
function Plan(expression, selector) {
this.expression = expression;
this.selector = selector;
}
Plan.prototype.activate = function (externalSubscriptions, observer, deactivate) {
var self = this;
var joinObservers = [];
for (var i = 0, len = this.expression.patterns.length; i < len; i++) {
joinObservers.push(planCreateObserver(externalSubscriptions, this.expression.patterns[i], observer.onError.bind(observer)));
}
var activePlan = new ActivePlan(joinObservers, function () {
var result;
try {
result = self.selector.apply(self, arguments);
} catch (exception) {
observer.onError(exception);
return;
}
observer.onNext(result);
}, function () {
for (var j = 0, jlen = joinObservers.length; j < jlen; j++) {
joinObservers[j].removeActivePlan(activePlan);
}
deactivate(activePlan);
});
for (i = 0, len = joinObservers.length; i < len; i++) {
joinObservers[i].addActivePlan(activePlan);
}
return activePlan;
};
function planCreateObserver(externalSubscriptions, observable, onError) {
var entry = externalSubscriptions.get(observable);
if (!entry) {
var observer = new JoinObserver(observable, onError);
externalSubscriptions.set(observable, observer);
return observer;
}
return entry;
}
// Active Plan
function ActivePlan(joinObserverArray, onNext, onCompleted) {
var i, joinObserver;
this.joinObserverArray = joinObserverArray;
this.onNext = onNext;
this.onCompleted = onCompleted;
this.joinObservers = new Map();
for (i = 0; i < this.joinObserverArray.length; i++) {
joinObserver = this.joinObserverArray[i];
this.joinObservers.set(joinObserver, joinObserver);
}
}
ActivePlan.prototype.dequeue = function () {
var values = this.joinObservers.getValues();
for (var i = 0, len = values.length; i < len; i++) {
values[i].queue.shift();
}
};
ActivePlan.prototype.match = function () {
var firstValues, i, len, isCompleted, values, hasValues = true;
for (i = 0, len = this.joinObserverArray.length; i < len; i++) {
if (this.joinObserverArray[i].queue.length === 0) {
hasValues = false;
break;
}
}
if (hasValues) {
firstValues = [];
isCompleted = false;
for (i = 0, len = this.joinObserverArray.length; i < len; i++) {
firstValues.push(this.joinObserverArray[i].queue[0]);
if (this.joinObserverArray[i].queue[0].kind === 'C') {
isCompleted = true;
}
}
if (isCompleted) {
this.onCompleted();
} else {
this.dequeue();
values = [];
for (i = 0; i < firstValues.length; i++) {
values.push(firstValues[i].value);
}
this.onNext.apply(this, values);
}
}
};
/** @private */
var JoinObserver = (function (_super) {
inherits(JoinObserver, _super);
/**
* @constructor
* @private
*/
function JoinObserver(source, onError) {
_super.call(this);
this.source = source;
this.onError = onError;
this.queue = [];
this.activePlans = [];
this.subscription = new SingleAssignmentDisposable();
this.isDisposed = false;
}
var JoinObserverPrototype = JoinObserver.prototype;
/**
* @memberOf JoinObserver#
* @private
*/
JoinObserverPrototype.next = function (notification) {
if (!this.isDisposed) {
if (notification.kind === 'E') {
this.onError(notification.exception);
return;
}
this.queue.push(notification);
var activePlans = this.activePlans.slice(0);
for (var i = 0, len = activePlans.length; i < len; i++) {
activePlans[i].match();
}
}
};
/**
* @memberOf JoinObserver#
* @private
*/
JoinObserverPrototype.error = noop;
/**
* @memberOf JoinObserver#
* @private
*/
JoinObserverPrototype.completed = noop;
/**
* @memberOf JoinObserver#
* @private
*/
JoinObserverPrototype.addActivePlan = function (activePlan) {
this.activePlans.push(activePlan);
};
/**
* @memberOf JoinObserver#
* @private
*/
JoinObserverPrototype.subscribe = function () {
this.subscription.setDisposable(this.source.materialize().subscribe(this));
};
/**
* @memberOf JoinObserver#
* @private
*/
JoinObserverPrototype.removeActivePlan = function (activePlan) {
var idx = this.activePlans.indexOf(activePlan);
this.activePlans.splice(idx, 1);
if (this.activePlans.length === 0) {
this.dispose();
}
};
/**
* @memberOf JoinObserver#
* @private
*/
JoinObserverPrototype.dispose = function () {
_super.prototype.dispose.call(this);
if (!this.isDisposed) {
this.isDisposed = true;
this.subscription.dispose();
}
};
return JoinObserver;
} (AbstractObserver));
/**
* Creates a pattern that matches when both observable sequences have an available value.
*
* @param right Observable sequence to match with the current sequence.
* @return {Pattern} Pattern object that matches when both observable sequences have an available value.
*/
observableProto.and = function (right) {
return new Pattern([this, right]);
};
/**
* Matches when the observable sequence has an available value and projects the value.
*
* @param selector Selector that will be invoked for values in the source sequence.
* @returns {Plan} Plan that produces the projected values, to be fed (with other plans) to the when operator.
*/
observableProto.then = function (selector) {
return new Pattern([this]).then(selector);
};
/**
* Joins together the results from several patterns.
*
* @param plans A series of plans (specified as an Array of as a series of arguments) created by use of the Then operator on patterns.
* @returns {Observable} Observable sequence with the results form matching several patterns.
*/
Observable.when = function () {
var plans = argsOrArray(arguments, 0);
return new AnonymousObservable(function (observer) {
var activePlans = [],
externalSubscriptions = new Map(),
group,
i, len,
joinObserver,
joinValues,
outObserver;
outObserver = observerCreate(observer.onNext.bind(observer), function (exception) {
var values = externalSubscriptions.getValues();
for (var j = 0, jlen = values.length; j < jlen; j++) {
values[j].onError(exception);
}
observer.onError(exception);
}, observer.onCompleted.bind(observer));
try {
for (i = 0, len = plans.length; i < len; i++) {
activePlans.push(plans[i].activate(externalSubscriptions, outObserver, function (activePlan) {
var idx = activePlans.indexOf(activePlan);
activePlans.splice(idx, 1);
if (activePlans.length === 0) {
outObserver.onCompleted();
}
}));
}
} catch (e) {
observableThrow(e).subscribe(observer);
}
group = new CompositeDisposable();
joinValues = externalSubscriptions.getValues();
for (i = 0, len = joinValues.length; i < len; i++) {
joinObserver = joinValues[i];
joinObserver.subscribe();
group.add(joinObserver);
}
return group;
});
};
return Rx;
}));

View File

@@ -0,0 +1,505 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
var root = (objectTypes[typeof window] && window) || this,
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx.virtualtime', 'exports'], function (Rx, exports) {
root.Rx = factory(root, exports, Rx);
return root.Rx;
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
// Defaults
var Observer = Rx.Observer,
Observable = Rx.Observable,
Notification = Rx.Notification,
VirtualTimeScheduler = Rx.VirtualTimeScheduler,
Disposable = Rx.Disposable,
disposableEmpty = Disposable.empty,
disposableCreate = Disposable.create,
CompositeDisposable = Rx.CompositeDisposable,
SingleAssignmentDisposable = Rx.SingleAssignmentDisposable,
slice = Array.prototype.slice,
inherits = Rx.Internals.inherits,
isEqual = Rx.Internals.isEqual;
// Utilities
function defaultComparer(x, y) {
return isEqual(x, y);
}
function argsOrArray(args, idx) {
return args.length === 1 && Array.isArray(args[idx]) ?
args[idx] :
slice.call(args);
}
/**
* @private
* @constructor
*/
function OnNextPredicate(predicate) {
this.predicate = predicate;
};
/**
* @private
* @memberOf OnNextPredicate#
*/
OnNextPredicate.prototype.equals = function (other) {
if (other === this) { return true; }
if (other == null) { return false; }
if (other.kind !== 'N') { return false; }
return this.predicate(other.value);
};
/**
* @private
* @constructor
*/
function OnErrorPredicate(predicate) {
this.predicate = predicate;
};
/**
* @private
* @memberOf OnErrorPredicate#
*/
OnErrorPredicate.prototype.equals = function (other) {
if (other === this) { return true; }
if (other == null) { return false; }
if (other.kind !== 'E') { return false; }
return this.predicate(other.exception);
};
/**
* @static
* type Object
*/
var ReactiveTest = Rx.ReactiveTest = {
/** Default virtual time used for creation of observable sequences in unit tests. */
created: 100,
/** Default virtual time used to subscribe to observable sequences in unit tests. */
subscribed: 200,
/** Default virtual time used to dispose subscriptions in unit tests. */
disposed: 1000,
/**
* Factory method for an OnNext notification record at a given time with a given value or a predicate function.
*
* 1 - ReactiveTest.onNext(200, 42);
* 2 - ReactiveTest.onNext(200, function (x) { return x.length == 2; });
*
* @param ticks Recorded virtual time the OnNext notification occurs.
* @param value Recorded value stored in the OnNext notification or a predicate.
* @return Recorded OnNext notification.
*/
onNext: function (ticks, value) {
if (typeof value === 'function') {
return new Recorded(ticks, new OnNextPredicate(value));
}
return new Recorded(ticks, Notification.createOnNext(value));
},
/**
* Factory method for an OnError notification record at a given time with a given error.
*
* 1 - ReactiveTest.onNext(200, new Error('error'));
* 2 - ReactiveTest.onNext(200, function (e) { return e.message === 'error'; });
*
* @param ticks Recorded virtual time the OnError notification occurs.
* @param exception Recorded exception stored in the OnError notification.
* @return Recorded OnError notification.
*/
onError: function (ticks, exception) {
if (typeof exception === 'function') {
return new Recorded(ticks, new OnErrorPredicate(exception));
}
return new Recorded(ticks, Notification.createOnError(exception));
},
/**
* Factory method for an OnCompleted notification record at a given time.
*
* @param ticks Recorded virtual time the OnCompleted notification occurs.
* @return Recorded OnCompleted notification.
*/
onCompleted: function (ticks) {
return new Recorded(ticks, Notification.createOnCompleted());
},
/**
* Factory method for a subscription record based on a given subscription and disposal time.
*
* @param start Virtual time indicating when the subscription was created.
* @param end Virtual time indicating when the subscription was disposed.
* @return Subscription object.
*/
subscribe: function (start, end) {
return new Subscription(start, end);
}
};
/**
* Creates a new object recording the production of the specified value at the given virtual time.
*
* @constructor
* @param {Number} time Virtual time the value was produced on.
* @param {Mixed} value Value that was produced.
* @param {Function} comparer An optional comparer.
*/
var Recorded = Rx.Recorded = function (time, value, comparer) {
this.time = time;
this.value = value;
this.comparer = comparer || defaultComparer;
};
/**
* Checks whether the given recorded object is equal to the current instance.
*
* @param {Recorded} other Recorded object to check for equality.
* @returns {Boolean} true if both objects are equal; false otherwise.
*/
Recorded.prototype.equals = function (other) {
return this.time === other.time && this.comparer(this.value, other.value);
};
/**
* Returns a string representation of the current Recorded value.
*
* @returns {String} String representation of the current Recorded value.
*/
Recorded.prototype.toString = function () {
return this.value.toString() + '@' + this.time;
};
/**
* Creates a new subscription object with the given virtual subscription and unsubscription time.
*
* @constructor
* @param {Number} subscribe Virtual time at which the subscription occurred.
* @param {Number} unsubscribe Virtual time at which the unsubscription occurred.
*/
var Subscription = Rx.Subscription = function (start, end) {
this.subscribe = start;
this.unsubscribe = end || Number.MAX_VALUE;
};
/**
* Checks whether the given subscription is equal to the current instance.
* @param other Subscription object to check for equality.
* @returns {Boolean} true if both objects are equal; false otherwise.
*/
Subscription.prototype.equals = function (other) {
return this.subscribe === other.subscribe && this.unsubscribe === other.unsubscribe;
};
/**
* Returns a string representation of the current Subscription value.
* @returns {String} String representation of the current Subscription value.
*/
Subscription.prototype.toString = function () {
return '(' + this.subscribe + ', ' + this.unsubscribe === Number.MAX_VALUE ? 'Infinite' : this.unsubscribe + ')';
};
/** @private */
var MockDisposable = Rx.MockDisposable = function (scheduler) {
this.scheduler = scheduler;
this.disposes = [];
this.disposes.push(this.scheduler.clock);
};
/*
* @memberOf MockDisposable#
* @prviate
*/
MockDisposable.prototype.dispose = function () {
this.disposes.push(this.scheduler.clock);
};
/** @private */
var MockObserver = (function (_super) {
inherits(MockObserver, _super);
/*
* @constructor
* @prviate
*/
function MockObserver(scheduler) {
_super.call(this);
this.scheduler = scheduler;
this.messages = [];
}
var MockObserverPrototype = MockObserver.prototype;
/*
* @memberOf MockObserverPrototype#
* @prviate
*/
MockObserverPrototype.onNext = function (value) {
this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnNext(value)));
};
/*
* @memberOf MockObserverPrototype#
* @prviate
*/
MockObserverPrototype.onError = function (exception) {
this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnError(exception)));
};
/*
* @memberOf MockObserverPrototype#
* @prviate
*/
MockObserverPrototype.onCompleted = function () {
this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnCompleted()));
};
return MockObserver;
})(Observer);
/** @private */
var HotObservable = (function (_super) {
function subscribe(observer) {
var observable = this;
this.observers.push(observer);
this.subscriptions.push(new Subscription(this.scheduler.clock));
var index = this.subscriptions.length - 1;
return disposableCreate(function () {
var idx = observable.observers.indexOf(observer);
observable.observers.splice(idx, 1);
observable.subscriptions[index] = new Subscription(observable.subscriptions[index].subscribe, observable.scheduler.clock);
});
}
inherits(HotObservable, _super);
/**
* @private
* @constructor
*/
function HotObservable(scheduler, messages) {
_super.call(this, subscribe);
var message, notification, observable = this;
this.scheduler = scheduler;
this.messages = messages;
this.subscriptions = [];
this.observers = [];
for (var i = 0, len = this.messages.length; i < len; i++) {
message = this.messages[i];
notification = message.value;
(function (innerNotification) {
scheduler.scheduleAbsoluteWithState(null, message.time, function () {
var obs = observable.observers.slice(0);
for (var j = 0, jLen = obs.length; j < jLen; j++) {
innerNotification.accept(obs[j]);
}
return disposableEmpty;
});
})(notification);
}
}
return HotObservable;
})(Observable);
/** @private */
var ColdObservable = (function (_super) {
function subscribe(observer) {
var message, notification, observable = this;
this.subscriptions.push(new Subscription(this.scheduler.clock));
var index = this.subscriptions.length - 1;
var d = new CompositeDisposable();
for (var i = 0, len = this.messages.length; i < len; i++) {
message = this.messages[i];
notification = message.value;
(function (innerNotification) {
d.add(observable.scheduler.scheduleRelativeWithState(null, message.time, function () {
innerNotification.accept(observer);
return disposableEmpty;
}));
})(notification);
}
return disposableCreate(function () {
observable.subscriptions[index] = new Subscription(observable.subscriptions[index].subscribe, observable.scheduler.clock);
d.dispose();
});
}
inherits(ColdObservable, _super);
/**
* @private
* @constructor
*/
function ColdObservable(scheduler, messages) {
_super.call(this, subscribe);
this.scheduler = scheduler;
this.messages = messages;
this.subscriptions = [];
}
return ColdObservable;
})(Observable);
/** Virtual time scheduler used for testing applications and libraries built using Reactive Extensions. */
Rx.TestScheduler = (function (_super) {
inherits(TestScheduler, _super);
function baseComparer(x, y) {
return x > y ? 1 : (x < y ? -1 : 0);
}
/** @constructor */
function TestScheduler() {
_super.call(this, 0, baseComparer);
}
/**
* Schedules an action to be executed at the specified virtual time.
*
* @param state State passed to the action to be executed.
* @param dueTime Absolute virtual time at which to execute the action.
* @param action Action to be executed.
* @return Disposable object used to cancel the scheduled action (best effort).
*/
TestScheduler.prototype.scheduleAbsoluteWithState = function (state, dueTime, action) {
if (dueTime <= this.clock) {
dueTime = this.clock + 1;
}
return _super.prototype.scheduleAbsoluteWithState.call(this, state, dueTime, action);
};
/**
* Adds a relative virtual time to an absolute virtual time value.
*
* @param absolute Absolute virtual time value.
* @param relative Relative virtual time value to add.
* @return Resulting absolute virtual time sum value.
*/
TestScheduler.prototype.add = function (absolute, relative) {
return absolute + relative;
};
/**
* Converts the absolute virtual time value to a DateTimeOffset value.
*
* @param absolute Absolute virtual time value to convert.
* @return Corresponding DateTimeOffset value.
*/
TestScheduler.prototype.toDateTimeOffset = function (absolute) {
return new Date(absolute).getTime();
};
/**
* Converts the TimeSpan value to a relative virtual time value.
*
* @param timeSpan TimeSpan value to convert.
* @return Corresponding relative virtual time value.
*/
TestScheduler.prototype.toRelative = function (timeSpan) {
return timeSpan;
};
/**
* Starts the test scheduler and uses the specified virtual times to invoke the factory function, subscribe to the resulting sequence, and dispose the subscription.
*
* @param create Factory method to create an observable sequence.
* @param created Virtual time at which to invoke the factory to create an observable sequence.
* @param subscribed Virtual time at which to subscribe to the created observable sequence.
* @param disposed Virtual time at which to dispose the subscription.
* @return Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
*/
TestScheduler.prototype.startWithTiming = function (create, created, subscribed, disposed) {
var observer = this.createObserver(), source, subscription;
this.scheduleAbsoluteWithState(null, created, function () {
source = create();
return disposableEmpty;
});
this.scheduleAbsoluteWithState(null, subscribed, function () {
subscription = source.subscribe(observer);
return disposableEmpty;
});
this.scheduleAbsoluteWithState(null, disposed, function () {
subscription.dispose();
return disposableEmpty;
});
this.start();
return observer;
};
/**
* Starts the test scheduler and uses the specified virtual time to dispose the subscription to the sequence obtained through the factory function.
* Default virtual times are used for factory invocation and sequence subscription.
*
* @param create Factory method to create an observable sequence.
* @param disposed Virtual time at which to dispose the subscription.
* @return Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
*/
TestScheduler.prototype.startWithDispose = function (create, disposed) {
return this.startWithTiming(create, ReactiveTest.created, ReactiveTest.subscribed, disposed);
};
/**
* Starts the test scheduler and uses default virtual times to invoke the factory function, to subscribe to the resulting sequence, and to dispose the subscription.
*
* @param create Factory method to create an observable sequence.
* @return Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
*/
TestScheduler.prototype.startWithCreate = function (create) {
return this.startWithTiming(create, ReactiveTest.created, ReactiveTest.subscribed, ReactiveTest.disposed);
};
/**
* Creates a hot observable using the specified timestamped notification messages either as an array or arguments.
*
* @param messages Notifications to surface through the created sequence at their specified absolute virtual times.
* @return Hot observable sequence that can be used to assert the timing of subscriptions and notifications.
*/
TestScheduler.prototype.createHotObservable = function () {
var messages = argsOrArray(arguments, 0);
return new HotObservable(this, messages);
};
/**
* Creates a cold observable using the specified timestamped notification messages either as an array or arguments.
*
* @param messages Notifications to surface through the created sequence at their specified virtual time offsets from the sequence subscription time.
* @return Cold observable sequence that can be used to assert the timing of subscriptions and notifications.
*/
TestScheduler.prototype.createColdObservable = function () {
var messages = argsOrArray(arguments, 0);
return new ColdObservable(this, messages);
};
/**
* Creates an observer that records received notification messages and timestamps those.
*
* @return Observer that can be used to assert the timing of received notifications.
*/
TestScheduler.prototype.createObserver = function () {
return new MockObserver(this);
};
return TestScheduler;
})(VirtualTimeScheduler);
return Rx;
}));

View File

@@ -0,0 +1,336 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
var root = (objectTypes[typeof window] && window) || this,
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx', 'exports'], function (Rx, exports) {
root.Rx = factory(root, exports, Rx);
return root.Rx;
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
// Aliases
var Scheduler = Rx.Scheduler,
PriorityQueue = Rx.Internals.PriorityQueue,
ScheduledItem = Rx.Internals.ScheduledItem,
SchedulePeriodicRecursive = Rx.Internals.SchedulePeriodicRecursive,
disposableEmpty = Rx.Disposable.empty,
inherits = Rx.Internals.inherits;
function defaultSubComparer(x, y) { return x - y; }
/** Provides a set of extension methods for virtual time scheduling. */
Rx.VirtualTimeScheduler = (function (_super) {
function notImplemented() {
throw new Error('Not implemented');
}
function localNow() {
return this.toDateTimeOffset(this.clock);
}
function scheduleNow(state, action) {
return this.scheduleAbsoluteWithState(state, this.clock, action);
}
function scheduleRelative(state, dueTime, action) {
return this.scheduleRelativeWithState(state, this.toRelative(dueTime), action);
}
function scheduleAbsolute(state, dueTime, action) {
return this.scheduleRelativeWithState(state, this.toRelative(dueTime - this.now()), action);
}
function invokeAction(scheduler, action) {
action();
return disposableEmpty;
}
inherits(VirtualTimeScheduler, _super);
/**
* Creates a new virtual time scheduler with the specified initial clock value and absolute time comparer.
*
* @constructor
* @param {Number} initialClock Initial value for the clock.
* @param {Function} comparer Comparer to determine causality of events based on absolute time.
*/
function VirtualTimeScheduler(initialClock, comparer) {
this.clock = initialClock;
this.comparer = comparer;
this.isEnabled = false;
this.queue = new PriorityQueue(1024);
_super.call(this, localNow, scheduleNow, scheduleRelative, scheduleAbsolute);
}
var VirtualTimeSchedulerPrototype = VirtualTimeScheduler.prototype;
/**
* Adds a relative time value to an absolute time value.
* @param {Number} absolute Absolute virtual time value.
* @param {Number} relative Relative virtual time value to add.
* @return {Number} Resulting absolute virtual time sum value.
*/
VirtualTimeSchedulerPrototype.add = notImplemented;
/**
* Converts an absolute time to a number
* @param {Any} The absolute time.
* @returns {Number} The absolute time in ms
*/
VirtualTimeSchedulerPrototype.toDateTimeOffset = notImplemented;
/**
* Converts the TimeSpan value to a relative virtual time value.
* @param {Number} timeSpan TimeSpan value to convert.
* @return {Number} Corresponding relative virtual time value.
*/
VirtualTimeSchedulerPrototype.toRelative = notImplemented;
/**
* Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities. The periodic task will be emulated using recursive scheduling.
* @param {Mixed} state Initial state passed to the action upon the first iteration.
* @param {Number} period Period for running the work periodically.
* @param {Function} action Action to be executed, potentially updating the state.
* @returns {Disposable} The disposable object used to cancel the scheduled recurring action (best effort).
*/
VirtualTimeSchedulerPrototype.schedulePeriodicWithState = function (state, period, action) {
var s = new SchedulePeriodicRecursive(this, state, period, action);
return s.start();
};
/**
* Schedules an action to be executed after dueTime.
* @param {Mixed} state State passed to the action to be executed.
* @param {Number} dueTime Relative time after which to execute the action.
* @param {Function} action Action to be executed.
* @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
*/
VirtualTimeSchedulerPrototype.scheduleRelativeWithState = function (state, dueTime, action) {
var runAt = this.add(this.clock, dueTime);
return this.scheduleAbsoluteWithState(state, runAt, action);
};
/**
* Schedules an action to be executed at dueTime.
* @param {Number} dueTime Relative time after which to execute the action.
* @param {Function} action Action to be executed.
* @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
*/
VirtualTimeSchedulerPrototype.scheduleRelative = function (dueTime, action) {
return this.scheduleRelativeWithState(action, dueTime, invokeAction);
};
/**
* Starts the virtual time scheduler.
*/
VirtualTimeSchedulerPrototype.start = function () {
var next;
if (!this.isEnabled) {
this.isEnabled = true;
do {
next = this.getNext();
if (next !== null) {
if (this.comparer(next.dueTime, this.clock) > 0) {
this.clock = next.dueTime;
}
next.invoke();
} else {
this.isEnabled = false;
}
} while (this.isEnabled);
}
};
/**
* Stops the virtual time scheduler.
*/
VirtualTimeSchedulerPrototype.stop = function () {
this.isEnabled = false;
};
/**
* Advances the scheduler's clock to the specified time, running all work till that point.
* @param {Number} time Absolute time to advance the scheduler's clock to.
*/
VirtualTimeSchedulerPrototype.advanceTo = function (time) {
var next;
var dueToClock = this.comparer(this.clock, time);
if (this.comparer(this.clock, time) > 0) {
throw new Error(argumentOutOfRange);
}
if (dueToClock === 0) {
return;
}
if (!this.isEnabled) {
this.isEnabled = true;
do {
next = this.getNext();
if (next !== null && this.comparer(next.dueTime, time) <= 0) {
if (this.comparer(next.dueTime, this.clock) > 0) {
this.clock = next.dueTime;
}
next.invoke();
} else {
this.isEnabled = false;
}
} while (this.isEnabled);
this.clock = time;
}
};
/**
* Advances the scheduler's clock by the specified relative time, running all work scheduled for that timespan.
* @param {Number} time Relative time to advance the scheduler's clock by.
*/
VirtualTimeSchedulerPrototype.advanceBy = function (time) {
var dt = this.add(this.clock, time);
var dueToClock = this.comparer(this.clock, dt);
if (dueToClock > 0) {
throw new Error(argumentOutOfRange);
}
if (dueToClock === 0) {
return;
}
this.advanceTo(dt);
};
/**
* Advances the scheduler's clock by the specified relative time.
* @param {Number} time Relative time to advance the scheduler's clock by.
*/
VirtualTimeSchedulerPrototype.sleep = function (time) {
var dt = this.add(this.clock, time);
if (this.comparer(this.clock, dt) >= 0) {
throw new Error(argumentOutOfRange);
}
this.clock = dt;
};
/**
* Gets the next scheduled item to be executed.
* @returns {ScheduledItem} The next scheduled item.
*/
VirtualTimeSchedulerPrototype.getNext = function () {
var next;
while (this.queue.length > 0) {
next = this.queue.peek();
if (next.isCancelled()) {
this.queue.dequeue();
} else {
return next;
}
}
return null;
};
/**
* Schedules an action to be executed at dueTime.
* @param {Scheduler} scheduler Scheduler to execute the action on.
* @param {Number} dueTime Absolute time at which to execute the action.
* @param {Function} action Action to be executed.
* @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
*/
VirtualTimeSchedulerPrototype.scheduleAbsolute = function (dueTime, action) {
return this.scheduleAbsoluteWithState(action, dueTime, invokeAction);
};
/**
* Schedules an action to be executed at dueTime.
* @param {Mixed} state State passed to the action to be executed.
* @param {Number} dueTime Absolute time at which to execute the action.
* @param {Function} action Action to be executed.
* @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
*/
VirtualTimeSchedulerPrototype.scheduleAbsoluteWithState = function (state, dueTime, action) {
var self = this,
run = function (scheduler, state1) {
self.queue.remove(si);
return action(scheduler, state1);
},
si = new ScheduledItem(self, state, run, dueTime, self.comparer);
self.queue.enqueue(si);
return si.disposable;
};
return VirtualTimeScheduler;
}(Scheduler));
/** Provides a virtual time scheduler that uses Date for absolute time and number for relative time. */
Rx.HistoricalScheduler = (function (_super) {
inherits(HistoricalScheduler, _super);
/**
* Creates a new historical scheduler with the specified initial clock value.
*
* @constructor
* @param {Number} initialClock Initial value for the clock.
* @param {Function} comparer Comparer to determine causality of events based on absolute time.
*/
function HistoricalScheduler(initialClock, comparer) {
var clock = initialClock == null ? 0 : initialClock;
var cmp = comparer || defaultSubComparer;
_super.call(this, clock, cmp);
}
var HistoricalSchedulerProto = HistoricalScheduler.prototype;
/**
* Adds a relative time value to an absolute time value.
* @param {Number} absolute Absolute virtual time value.
* @param {Number} relative Relative virtual time value to add.
* @return {Number} Resulting absolute virtual time sum value.
*/
HistoricalSchedulerProto.add = function (absolute, relative) {
return absolute + relative;
};
/**
* @private
*/
HistoricalSchedulerProto.toDateTimeOffset = function (absolute) {
return new Date(absolute).getTime();
};
/**
* Converts the TimeSpan value to a relative virtual time value.
*
* @memberOf HistoricalScheduler
* @param {Number} timeSpan TimeSpan value to convert.
* @return {Number} Corresponding relative virtual time value.
*/
HistoricalSchedulerProto.toRelative = function (timeSpan) {
return timeSpan;
};
return HistoricalScheduler;
}(Rx.VirtualTimeScheduler));
return Rx;
}));

View File

@@ -0,0 +1,63 @@
@import "../../src/css/fonts/Helvetica/helvetica.css";
*, *:before, *:after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
border: 0;
}
body {
font-family: "Helvetica Neue LT Std";
font-size: 24pt;
line-height: 1.2em;
}
#canvas {
position: absolute;
top: 0;
left: 0;
width: 512px;
height: 512px;
padding: 0.5em 1em;
background-image: url(../../m/dhh-sees-all.jpg);
background-size: cover;
background-position: 50% 50%;
overflow: hidden;
-moz-transform-origin: 0 0;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-filter: saturate(200%) grayscale(25%) sepia(10%);
-moz-filter: saturate(200%) grayscale(25%) sepia(10%);
filter: saturate(200%) grayscale(25%) sepia(10%);
}
#log {
position: absolute;
width: 100%;
padding-right: 2em;
bottom: 0;
}
p {
background: rgba(255, 255, 255, 0.8);
padding: 0.3em 0.8em;
margin-bottom: 0.3em;
}
p.output:before {
color: orange;
content: "Output: ";
}
p.error:before {
color: red;
content: "Error: ";
}
p.complete:before {
color: green;
content: "Completed. ";
}

View File

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>hai lol</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" href="rx.css">
<script type="text/javascript" src="../lib/traceur-runtime.js"></script>
<script type="text/javascript" src="../lib/rx.js"></script>
<script type="text/javascript" src="../lib/rx.async.js"></script>
<script type="text/javascript" src="../lib/rx.binding.js"></script>
<script type="text/javascript" src="../lib/rx.time.js"></script>
<script type="text/javascript" src="../lib/rx.coincidence.js"></script>
</head>
<body>
<div id="canvas">
<div id="log">
</div>
</div>
<script type="text/javascript">
(function() {
function resizeCanvas() {
var canvas = document.getElementById("canvas");
var scale = window.innerHeight / canvas.clientHeight;
canvas.style.mozTransform = "scale(" + scale + ")";
canvas.style.webkitTransform = "scale(" + scale + ")";
canvas.style.transform = "scale(" + scale + ")";
canvas.style.width = (window.innerWidth / scale) + "px";
}
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
var log = document.getElementById("log");
Rx.Observable.prototype.log = function() {
this.subscribe(function(i) {
var p = document.createElement("p");
p.className = "output";
var t = document.createTextNode(JSON.stringify(i));
p.appendChild(t);
log.appendChild(p);
}, function(err) {
var p = document.createElement("p");
p.className = "error";
var t = document.createTextNode(JSON.stringify(err));
p.appendChild(t);
log.appendChild(p);
}, function() {
var p = document.createElement("p");
p.className = "complete";
log.appendChild(p);
})
};
})();
</script>
<script type="text/javascript" src="../../src/modules/editor/client.js"></script>
</body>
</html>

View File

@@ -0,0 +1,309 @@
<!-- Copyright 2014 Bodil Stokke <bodil@bodil.org>
-
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Reactive Game Development For The Discerning Hipster</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<style type="text/css">
body { visibility: hidden; background: black; }
</style>
<!-- <script type="text/javascript"> -->
<!-- var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-332358-13']); _gaq.push(['_setDomainName', 'bodil.org']); _gaq.push(['_trackPageview']); -->
<!-- (function() { -->
<!-- var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; -->
<!-- ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; -->
<!-- var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); -->
<!-- })(); -->
<!-- </script> -->
</head>
<body>
<div id="slides" data-background="m/glasses.jpg">
<section data-image="m/have-fixie-will-hip.jpg" class="white photo">
<h3>REACTIVE GAME DEVELOPMENT</h3>
<h4>for the</h4>
<h2>DISCERNING HIPSTER</h2>
<h4 class="attribution">@bodil</h4>
</section>
<section data-image="m/steve.jpg" class="photo">
</section>
<section data-image="m/beard-off.jpg" class="white photo">
<h6>kleisli arrows</h6>
<h6>yoneda lemma</h6>
<h6>cofree coburritos</h6>
<h6>zygohistomorphic prepromorphisms</h6>
</section>
<section data-image="m/joe.jpg" class="white photo align-bottom erlang-the-movie">
<p>FUNCTIONAL REACTIVE PROGRAMMING</p>
<h2>NOW!</h2>
</section>
<section data-image="m/erik.jpg" class="photo white align-bottom">
<h1>Reactive Extensions</h1>
</section>
<section data-image="m/dhh.jpg" class="photo white align-bottom">
<h1>RxJS</h1>
</section>
<section data-image="m/dhh-racer.jpg" class="photo white align-bottom">
<h1>React.JS</h1>
</section>
<section data-editor="text/javascript" data-href="game/rxtut/rx.html">
var ponies = Rx.Observable.fromArray([
"Fluttershy",
"Twilight Sparkle",
"Applejack",
"Rarity",
"Rainbow Dash",
"Pinkie Pie"
]);
</section>
<section data-editor="text/javascript" data-href="game/rxtut/rx.html">
var ponies = Rx.Observable.fromArray([
"Fluttershy",
"Twilight Sparkle",
"Applejack",
"Rarity",
"Rainbow Dash",
"Pinkie Pie"
]);
var ponyTime = Rx.Observable.zip(
Rx.Observable.interval(500),
ponies, (tick, i) => i
).log();
// ponyTime
// .filter((pony) => /e$/.test(pony))
// .map((pony) => "I love " + pony)
// .log();
</section>
<section data-editor="text/javascript" data-href="game/game.html" data-reload>
const canvas = document.getElementById("canvas");
function extend(target, obj) {
for (let prop in obj) target[prop] = obj[prop];
}
function assoc() {
let out = {};
for (let i = 0; i < arguments.length; i++)
extend(out, arguments[i]);
return out;
}
function onscreen(node) {
return !(node.x < -300 || node.y < -1000 || node.y > 1000);
}
function bindKey(key) {
let sub = new Rx.Subject();
Mousetrap.bind(key, () => {
sub.onNext(key);
});
return sub;
}
</section>
<section data-editor="text/javascript" data-href="game/game.html" data-reload>
const canvas = document.getElementById("canvas");
function extend(target, obj) {
for (let prop in obj) target[prop] = obj[prop];
}
function assoc() {
let out = {};
for (let i = 0; i < arguments.length; i++)
extend(out, arguments[i]);
return out;
}
function onscreen(node) {
return !(node.x < -300 || node.y < -1000 || node.y > 1000);
}
function bindKey(key) {
let sub = new Rx.Subject();
Mousetrap.bind(key, () => {
sub.onNext(key);
});
return sub;
}
function makeElement(node) {
return React.DOM.div({
className: node.id,
style: {
left: (node.x + (node.baseX || 0)) | 0 + "px",
top: (node.y + (node.baseY || 0)) | 0 + "px"
}
});
}
function renderScene(nodes) {
return React.renderComponent(
React.DOM.div(null, nodes.map(makeElement)),
canvas
);
}
let groundStream = Rx.Observable.interval(33)
.map((x) => ({
id: "ground",
baseX: -128,
x: ((x % 64) * -8), y: 384
}));
function velocity(node) {
return assoc(node, {
x: node.x + node.vx,
y: node.y + node.vy
});
}
let tick = bindKey("space")
.buffer(Rx.Observable.interval(33));
let initialHater = {
id: "hater",
vx: -8, vy: 0,
x: 1600, y: 300
};
let haterStream = tick.scan(initialHater, (c, keys) => {
// Apply velocity to position.
c = velocity(c);
return onscreen(c) ? c : initialHater;
});
let pinkieStream = Rx.Observable.zipArray(tick, haterStream).scan({
id: "pinkie",
baseY: 276,
x: 0, y: 0,
vx: 0, vy: 0,
gameOver: false
}, (p, [keys, hater]) => {
p = velocity(p);
if (intersects(p, hater) && !p.gameOver) {
p.gameOver = true;
p.id = "pinkie gameover";
new Audio("sfx/gameover.mp3").play();
p.vy = -15;
}
if (p.gameOver) {
p.vy += 0.5;
return p;
}
// Apply gravity to Pinkie's velocity.
p.vy += 0.98;
// AS Pinkie Pie,
// GIVEN that I'm falling
// WHEN I hit the ground
// THEN I stop.
if (p.y >= 0 && p.vy > 0) {
p.y = 0; p.vy = 0;
}
// If Pinkie is on the ground and space has been pressed, JUMP.
if (keys[0] === "space") {
// p.vy = -20; // wheeee infinite jump
if (p.y === 0) {
p.vy = -20;
new Audio("sfx/jump.mp3").play();
}
}
p.id = (p.y < 0) ? "pinkie jumping" : "pinkie";
return p;
}).takeWhile(onscreen);
let initialCoin = {
id: "coin",
vx: -6, vy: 0,
x: 1600, y: 40
};
let coinStream = pinkieStream.scan(initialCoin, (c, pinkie) => {
// Apply velocity to position.
c = velocity(c);
// If Pinkie touches the coin, ding it!
if (c.vy === 0 && intersects(c, pinkie)) {
new Audio("sfx/coin.mp3").play();
c.vx = 0; c.vy = -1;
}
if (c.vy < 0) {
c.vy = c.vy * 2;
}
return onscreen(c) ? c : initialCoin;
});
Rx.Observable.zipArray(pinkieStream, groundStream, coinStream, haterStream)
.subscribe(renderScene);
</section>
<section data-image="m/bad-doge.jpg" class="white">
<h1>RxJS</h1>
<p><a style="font-size: 0.9em" href="http://reactive-extensions.github.io/RxJS/">http://reactive-extensions.github.io/RxJS/</a></p>
</section>
<section data-image="m/mad-doge.jpg" class="white">
<h1>Bacon.js</h1>
<p><a style="font-size: 0.9em" href="http://baconjs.github.io/">http://baconjs.github.io/</a></p>
</section>
<section data-image="m/doge.gif" class="white">
<h1>Elm</h1>
<p><a style="font-size: 0.9em" href="http://elm-lang.org/">http://elm-lang.org/</a></p>
</section>
<section data-background="m/muchmine.gif" class="white" style="text-align: center; background: rgba(0,0,0,0)">
<!-- <h3 style="margin: 0">谢谢!</h3> -->
<h3 style="margin-top: 0; margin-bottom: 1em">Thank you!</h3>
<!-- <h3 style="margin-top: 0; margin-bottom: 1em">qatlho'!</h3> -->
<p>@bodil</p>
<p style="font-size: 0.8em">
<a style="text-decoration: none" href="http://bodil.org/boogaloo/">bodil.org/boogaloo/</a>
</p>
<div style="position: absolute; left: 0.5em; bottom: 0.5em"
class="doge-widget-wrapper">
<form method='get' action='https://www.dogeapi.com/checkout'>
<input type='hidden' name='widget_type' value='donate'>
<input type='hidden' name='payment_address' value='D99Ayqb1Q9nm5bzpvmDpc34CsrxYWyjL1b'>
<div class='doge-widget' style='display:none;'></div>
</form>
</div>
</section>
<section data-background="m/moby-dick.jpg" class="photo white">
<h3>QUESTIONS?</h3>
</section>
</div>
<script type="text/javascript" src="build/slide.js"></script>
<!-- <script async src="https://www.dogeapi.com/widget/dogeapi.js" type="text/javascript"></script> -->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

View File

@@ -0,0 +1,117 @@
// --- Actual game:
function makeElement(node) {
return React.DOM.div({
className: node.id,
style: {
left: (node.x + (node.baseX || 0)) | 0 + "px",
top: (node.y + (node.baseY || 0)) | 0 + "px"
}
});
}
function renderScene(nodes) {
return React.renderComponent(
React.DOM.div(null, nodes.map(makeElement)),
canvas
);
}
let groundStream = Rx.Observable.interval(33)
.map((x) => ({
id: "ground",
baseX: -128,
x: ((x % 64) * -8), y: 384
}));
function velocity(node) {
return assoc(node, {
x: node.x + node.vx,
y: node.y + node.vy
});
}
let tick = bindKey("space")
.buffer(Rx.Observable.interval(33));
let initialHater = {
id: "hater",
vx: -8, vy: 0,
x: 1600, y: 300
};
let haterStream = tick.scan(initialHater, (c, keys) => {
// Apply velocity to position.
c = velocity(c);
return onscreen(c) ? c : initialHater;
});
let pinkieStream = Rx.Observable.zipArray(tick, haterStream).scan({
id: "pinkie",
baseY: 276,
x: 0, y: 0,
vx: 0, vy: 0,
gameOver: false
}, (p, [keys, hater]) => {
p = velocity(p);
if (intersects(p, hater) && !p.gameOver) {
p.gameOver = true;
p.id = "pinkie gameover";
new Audio("sfx/gameover.mp3").play();
p.vy = -15;
}
if (p.gameOver) {
p.vy += 0.5;
return p;
}
// Apply gravity to Pinkie's velocity.
p.vy += 0.98;
// AS Pinkie Pie,
// GIVEN that I'm falling
// WHEN I hit the ground
// THEN I stop.
if (p.y >= 0 && p.vy > 0) {
p.y = 0; p.vy = 0;
}
// If Pinkie is on the ground and space has been pressed, JUMP.
if (keys[0] === "space") {
// p.vy = -20; // wheeee infinite jump
if (p.y === 0) {
p.vy = -20;
new Audio("sfx/jump.mp3").play();
}
}
p.id = (p.y < 0) ? "pinkie jumping" : "pinkie";
return p;
}).takeWhile(onscreen);
let initialCoin = {
id: "coin",
vx: -6, vy: 0,
x: 1600, y: 40
};
let coinStream = pinkieStream.scan(initialCoin, (c, pinkie) => {
// Apply velocity to position.
c = velocity(c);
// If Pinkie touches the coin, ding it!
if (c.vy === 0 && intersects(c, pinkie)) {
new Audio("sfx/coin.mp3").play();
c.vx = 0; c.vy = -1;
}
if (c.vy < 0) {
c.vy = c.vy * 2;
}
return onscreen(c) ? c : initialCoin;
});
Rx.Observable.zipArray(pinkieStream, groundStream, coinStream, haterStream)
.subscribe(renderScene);

View File

@@ -0,0 +1,37 @@
{
"name": "boogaloo",
"version": "0.0.1",
"description": "A slide deck",
"homepage": "https://github.com/bodil/boogaloo",
"author": "Bodil Stokke",
"licenses": [
"GPL-3",
"MIT"
],
"repository": {
"type": "git",
"url": "https://github.com/bodil/boogaloo.git"
},
"scripts": {
"webpack": "webpack --config config/webpack.config.js --colors --progress",
"install": "npm run webpack",
"start": "webpack-dev-server --config config/webpack.config.js --port 1337"
},
"devDependencies": {
"webpack": "~1.0.5",
"webpack-dev-server": "~1.2.3",
"less-loader": "~0.7.1",
"css-loader": "~0.6.8",
"style-loader": "~0.6.3",
"url-loader": "~0.5.3",
"json-loader": "~0.5.0",
"raw-loader": "~0.5.1"
},
"dependencies": {
"traceur": "~0.0.25",
"codemirror": "~3.22.0",
"spin.js": "~1.3.2",
"tern": "~0.5.0",
"underscore": "~1.6.0"
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -0,0 +1,6 @@
@font-face {
font-family: 'Dijkstra';
font-style: normal;
font-weight: 400;
src: local('Dijkstra'), url(dijkstra.ttf) format('truetype');
}

View File

@@ -0,0 +1,24 @@
@font-face {
font-family: 'Helvetica Neue LT Std';
font-style: normal;
font-weight: 400;
src: local('Helvetica Neue LT Std Regular'), url(HelveticaNeueLTStd-Md.otf) format('opentype');
}
@font-face {
font-family: 'Helvetica Neue LT Std';
font-style: normal;
font-weight: 700;
src: local('Helvetica Neue LT Std Bold'), url(HelveticaNeueLTStd-Bd.otf) format('opentype');
}
@font-face {
font-family: 'Helvetica Neue LT Std';
font-style: italic;
font-weight: 400;
src: local('Helvetica Neue LT Std Regular Italic'), url(HelveticaNeueLTStd-MdIt.otf) format('opentype');
}
@font-face {
font-family: 'Helvetica Neue LT Std';
font-style: italic;
font-weight: 700;
src: local('Helvetica Neue LT Std Bold Italic'), url(HelveticaNeueLTStd-BdIt.otf) format('opentype');
}

View File

@@ -0,0 +1,92 @@
Copyright (c) 2011, Raph Levien (firstname.lastname@gmail.com), Copyright (c) 2012, Cyreal (cyreal.org)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -0,0 +1,12 @@
@font-face {
font-family: 'Inconsolata';
font-style: normal;
font-weight: 400;
src: local('Inconsolata'), url(Inconsolata-Regular.ttf) format('truetype');
}
@font-face {
font-family: 'Inconsolata';
font-style: normal;
font-weight: 700;
src: local('Inconsolata Bold'), local('Inconsolata-Bold'), url(Inconsolata-Bold.ttf) format('truetype');
}

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -0,0 +1,18 @@
@font-face {
font-family: 'Roboto Slab';
font-style: normal;
font-weight: 300;
src: local('Roboto Slab Light'), local('RobotoSlab-Light'), url(RobotoSlab-Light.ttf) format('truetype');
}
@font-face {
font-family: 'Roboto Slab';
font-style: normal;
font-weight: 400;
src: local('Roboto Slab Regular'), local('RobotoSlab-Regular'), url(RobotoSlab-Regular.ttf) format('truetype');
}
@font-face {
font-family: 'Roboto Slab';
font-style: normal;
font-weight: 700;
src: local('Roboto Slab Bold'), local('RobotoSlab-Bold'), url(RobotoSlab-Bold.ttf) format('truetype');
}

View File

@@ -0,0 +1,93 @@
Copyright (c) 2011 by Anna Giedryś (http://ancymonic.com),
with Reserved Font Names "Signika".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -0,0 +1,24 @@
@font-face {
font-family: 'Signika';
font-style: normal;
font-weight: 300;
src: local('Signika-Light'), url(Signika-Light.ttf) format('truetype');
}
@font-face {
font-family: 'Signika';
font-style: normal;
font-weight: 400;
src: local('Signika'), local('Signika-Regular'), url(Signika-Regular.ttf) format('truetype');
}
@font-face {
font-family: 'Signika';
font-style: normal;
font-weight: 600;
src: local('Signika-Semibold'), url(Signika-Semibold.ttf) format('truetype');
}
@font-face {
font-family: 'Signika';
font-style: normal;
font-weight: 700;
src: local('Signika-Bold'), url(Signika-Bold.ttf) format('truetype');
}

View File

@@ -0,0 +1,6 @@
@font-face {
font-family: 'StAndrew';
font-style: normal;
font-weight: 400;
src: local('StAndrew'), url(st-andrew.ttf) format('truetype');
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -0,0 +1,60 @@
/**
* Global Reset of all HTML Elements
*
* Resetting all of our HTML Elements ensures a smoother
* visual transition between browsers. If you don't believe me,
* try temporarily commenting out this block of code, then go
* and look at Mozilla versus Safari, both good browsers with
* a good implementation of CSS. The thing is, all browser CSS
* defaults are different and at the end of the day if visual
* consistency is what we're shooting for, then we need to
* make sure we're resetting all spacing elements.
*
*/
html, body {
border: 0;
font-family: "Helvetica-Neue", "Helvetica", Arial, sans-serif;
line-height: 1.5;
margin: 0;
padding: 0;
}
div, span, object, iframe, img, table, caption, thead, tbody,
tfoot, tr, tr, td, article, aside, canvas, details, figure, hgroup, menu,
nav, footer, header, section, summary, mark, audio, video {
border: 0;
margin: 0;
padding: 0;
}
h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, address, cit, code,
del, dfn, em, ins, q, samp, small, strong, sub, sup, b, i, hr, dl, dt, dd,
ol, ul, li, fieldset, legend, label {
border: 0;
font-size: 100%;
vertical-align: baseline;
margin: 0;
padding: 0;
}
article, aside, canvas, figure, figure img, figcaption, hgroup,
footer, header, nav, section, audio, video {
display: block;
}
table {
border-collapse: separate;
border-spacing: 0;
caption, th, td {
text-align: left;
vertical-align: middle;
}
}
a img {
border: 0;
}
:focus {
outline: 0;
}

View File

@@ -0,0 +1,306 @@
@import "./reset.less";
@import "./fonts/Helvetica/helvetica.css";
@import "./fonts/Inconsolata/inconsolata.css";
*, *:before, *:after {
/* OMG YES YOU CAN UNBREAK THE BOX MODEL <3 <3 <3 <3 <3 <3 */
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
color: #222;
margin: 0;
padding: 0;
border: 0;
font-family: "Helvetica Neue LT Std";
font-size: 48pt;
line-height: 1.2em;
visibility: visible;
}
p, a, li, li:before, h1, h2, h3, h4, h5, h6 {
text-shadow: 0 0 0.1em rgba(0,0,0,0.5);
}
h1, h2, h3, h4, h5, h6 {
text-align: center;
font-weight: bold;
background-color: rgba(0,0,0,0.1);
margin: 0.4em -100%;
}
h1 {
font-size: 1.6em;
padding-top: 0.25em;
}
h2 {
font-size: 1.4em;
padding-top: 0.3em;
}
h3 {
font-size: 1.2em;
padding-top: 0.3em;
}
h4, h5, h6 {
font-size: 1.1em;
padding-top: 0.3em;
}
ul, ol {
li {
list-style: none;
position: relative;
&:before {
color: #888;
text-align: right;
position: absolute;
left: 0;
}
}
}
ul {
li:before {
content: ' ';
width: 1em;
height: 1em;
margin-left: -1em;
background: url(doge.svg);
background-size: contain;
}
}
strong {
letter-spacing: 0.05em;
}
a {
color: black;
}
.attribution {
font-size: 0.9em;
}
#slides {
z-index: 1;
position: fixed;
background: #ccc;
}
#slides, #slides .background {
background-size: cover;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border: 0;
}
#slides .background {
z-index: 2;
position: absolute;
opacity: 0;
-webkit-transition: opacity 0.3s ease;
-moz-transition: opacity 0.3s ease;
transition: opacity 0.3s ease;
&.active {
opacity: 1;
}
}
#slides section {
background-color: rgba(255, 255, 255, 0.8);
background-size: cover;
background-position: 50% 50%;
z-index: 10;
visibility: hidden;
position: absolute;
width: 80%;
height: 80%;
left: 10%;
top: 10%;
margin: 0;
padding: 0 10%;
border: 0;
border-radius: 0.2em;
display: -webkit-box;
display: -moz-box;
display: box;
-webkit-box-pack: center;
-webkit-box-align: center;
-moz-box-pack: center;
-moz-box-align: center;
box-pack: center;
box-align: center;
opacity: 0;
-webkit-transform: scale(1.25);
-webkit-filter: grayscale(100%);
-webkit-transition: -webkit-filter 0.3s ease, -webkit-transform 0.3s ease, opacity 0.3s ease;
-moz-transform: scale(1.25);
-moz-filter: grayscale(100%);
-moz-transition: -moz-filter 0.3s ease, -moz-transform 0.3s ease, opacity 0.3s ease;
transform: scale(1.25);
filter: grayscale(100%);
transition: filter 0.3s ease, transform 0.3s ease, opacity 0.3s ease;
.slideContainer {
width: 100%;
}
&.current, &.out {
visibility: visible;
}
&.current {
opacity: 1;
-webkit-filter: grayscale(0);
-webkit-transform: scale(1);
-moz-filter: grayscale(0);
-moz-transform: scale(1);
filter: grayscale(0);
transform: scale(1);
}
&.out {
-webkit-transform: scale(0.75);
-moz-transform: scale(0.75);
transform: scale(0.75);
}
&.editor {
left: 0;
top: 0;
width: 100%;
height: 100%;
border-radius: 0;
background: none;
.editorFrame, .targetFrame {
position: absolute;
width: 50%;
height: 100%;
top: 0;
padding: 0.5em;
}
.editorFrame {
left: 0;
padding-right: 0;
}
.targetFrame {
left: 50%;
}
.targetFrame iframe {
z-index: 14;
background-color: #444;
background-image: url(doge.svg);
background-size: contain;
background-repeat: no-repeat;
background-position: 50% 50%;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.loaderFrame {
z-index: 15;
background: #444;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.CodeMirror {
font-family: "Inconsolata";
line-height: 1.2em;
font-size: 0.5em;
}
.CodeMirror, .loaderFrame, .targetFrame iframe {
box-shadow: 0 0.1em 0.2em rgba(0, 0, 0, 0.6);
border-radius: 0.1em;
}
}
&.photo {
background-size: contain;
background-color: rgba(0,0,0,0);
background-repeat: no-repeat;
}
&.white {
text-align: center;
h1, h2, h3, h4, h5, h6, p, a {
color: white;
text-shadow: 0 -0.05em 0.1em black, 0 0.05em 0.1em black, 0 0.1em 0.3em black;
background: none;
font-weight: normal;
}
}
&.align-bottom {
-webkit-box-pack: end;
-webkit-box-align: end;
-moz-box-pack: end;
-moz-box-align: end;
box-pack: end;
box-align: end;
padding-bottom: 0.5em;
}
&.erlang-the-movie {
h1, h2, h3, h4, h5, h6, p, a {
font-weight: bolder;
-webkit-transform: scale(0.8, 1);
-moz-transform: scale(0.8, 1);
transform: scale(0.8, 1);
margin: 0;
}
p {
font-size: 0.9em;
margin: 0 -6em;
}
}
}
/* CodeMirror extras for the editor plugin */
.CodeMirror-hints,
.CodeMirror-Tern-tooltip {
font-family: "Inconsolata";
line-height: 1.2em;
font-size: 0.5em !important;
text-shadow: none;
& li {
text-shadow: none;
}
}
.cm-errors {
width: 1em;
}
.cm-error {
width: 1em;
height: 1em;
background: url(red-doge.svg);
background-size: contain;
}

View File

@@ -0,0 +1,128 @@
/*global setTimeout */
var events = require("./lib/events");
var mousetrap = require("./lib/mousetrap");
function toArray(indexable) {
const out = [], l = indexable.length;
for (let i = 0; i < l; i++) {
out.push(indexable[i]);
}
return out;
}
function Deck(container, deckModules) {
const slides = toArray(container.querySelectorAll("section"));
this.currentSlide = null;
slides.forEach((slide) => {
const children = toArray(slide.childNodes);
const container = document.createElement("div");
container.classList.add("slideContainer");
children.forEach((child) => {
slide.removeChild(child);
container.appendChild(child);
});
slide.appendChild(container);
});
function slideIndex(slide) {
return slides.indexOf(slide);
}
this.deactivateSlide = (slide) => {
if (slide.classList.contains("current")) {
slide.classList.add("out");
slide.classList.remove("current");
}
this.currentSlide = null;
}
this.activateSlide = (slide) => {
if (slide.classList.contains("out")) {
this.cleanupModules(slide);
slide.classList.remove("out");
}
if (this.currentSlide !== null) this.deactivateSlide(slides[this.currentSlide]);
this.currentSlide = slideIndex(slide);
this.activateModules(slide);
slide.classList.add("current");
slide.classList.add("in");
window.location.hash = "" + this.currentSlide;
}
this.nextSlide = () => {
let nextSlide = this.currentSlide !== null ? this.currentSlide + 1 : 0;
if (nextSlide >= slides.length) nextSlide = slides.length - 1;
if (nextSlide !== this.currentSlide) this.activateSlide(slides[nextSlide]);
}
this.previousSlide = () => {
let prevSlide = this.currentSlide !== null ? this.currentSlide - 1 : 0;
if (prevSlide < 0) prevSlide = 0;
if (prevSlide !== this.currentSlide) this.activateSlide(slides[prevSlide]);
}
this.initModules = (slide) => {
let slideData = slide.dataset,
deckData = container.dataset;
let mods = [], mod;
for (mod in deckModules) {
if (deckModules.hasOwnProperty(mod)) {
let arg = slideData.hasOwnProperty(mod) ? slideData[mod] :
deckData.hasOwnProperty(mod) ? deckData[mod] : null;
if (arg) mods.push(new deckModules[mod](slide, arg));
}
}
slide._deck_modules = mods;
}
this.activateModules = (slide) => {
slide._deck_modules.forEach((mod) => mod.activate && mod.activate());
}
this.stabiliseModules = (slide) => {
slide._deck_modules.forEach((mod) => mod.stabilise && mod.stabilise());
}
this.cleanupModules = (slide) => {
slide._deck_modules.forEach((mod) => mod.cleanup && mod.cleanup());
}
this.transitionEnd = (e) => {
let slide = e.target;
if (slide.classList.contains("out")) {
slide.classList.remove("out");
this.cleanupModules(slide);
} else if (slide.classList.contains("in")) {
slide.classList.remove("in");
this.stabiliseModules(slide);
}
}
slides.forEach(((slide) => this.initModules(slide)).bind(this));
events.on(container, events.vendorPrefix("TransitionEnd"), this.transitionEnd, this);
this.bind = (binding, callback) => {
mousetrap.bind(binding, callback.bind(this));
};
this.bind(["pageup", "left"], this.previousSlide);
this.bind(["pagedown", "space", "right"], this.nextSlide);
setTimeout(() => {
let match = /^#(\d+)$/.exec(window.location.hash);
if (match) {
this.activateSlide(slides[parseInt(match[1], 10)]);
} else {
this.nextSlide();
}
}, 1);
}
module.exports = Deck;

View File

@@ -0,0 +1,68 @@
// Thanks http://lea.verou.me/2009/02/find-the-vendor-prefix-of-the-current-browser/
var cachedPrefix = null;
function getPrefix() {
if (cachedPrefix) return cachedPrefix;
const regex = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/;
const elem = document.body;
for (let prop in elem.style) {
if (regex.test(prop)) {
return cachedPrefix = prop.match(regex)[0];
}
}
if ("WebkitOpacity" in elem.style) {
return cachedPrefix = "Webkit";
}
if ("KhtmlOpacity" in elem.style) {
return cachedPrefix = "Khtml";
}
return cachedPrefix = "";
}
var cachedEvents = {};
function vendorPrefix(prop) {
if (cachedEvents.hasOwnProperty(prop)) return cachedEvents[prop];
const vp = getPrefix().toLowerCase();
const pp = (vp) ? (vp + prop) : prop.toLowerCase();
cachedEvents[prop] = pp;
return pp;
}
// Register to receive events.
function on(emitter, eventName, handler, context) {
handler = context ? handler.bind(context) : handler;
emitter.addEventListener(eventName, handler);
return handler;
}
// Register to receive one single event.
function once(emitter, eventName, handler, context) {
handler = context ? handler.bind(context) : handler;
let wrapper = function onceHandler(event) {
emitter.removeEventListener(eventName, onceHandler);
handler(event);
};
emitter.addEventListener(eventName, wrapper);
return wrapper;
}
// Register to receive events until the handler function returns true.
function until(emitter, eventName, handler, context) {
handler = context ? handler.bind(context) : handler;
let wrapper = function untilHandler(event) {
if (handler(event))
emitter.removeEventListener(eventName, untilHandler);
};
emitter.addEventListener(eventName, wrapper);
return wrapper;
}
// Unregister an event handler.
function off(emitter, eventName, handler) {
emitter.removeEventListener(eventName, handler);
}
module.exports = {
on: on, once: once, until: until, off: off, vendorPrefix: vendorPrefix
};

View File

@@ -0,0 +1,944 @@
/*global define:false */
/**
* Copyright 2013 Craig Campbell
*
* 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.
*
* Mousetrap is a simple keyboard shortcut library for Javascript with
* no external dependencies
*
* @version 1.4.6
* @url craig.is/killing/mice
*/
/**
* mapping of special keycodes to their corresponding keys
*
* everything in this dictionary cannot use keypress events
* so it has to be here to map to the correct keycodes for
* keyup/keydown events
*
* @type {Object}
*/
var _MAP = {
8: 'backspace',
9: 'tab',
13: 'enter',
16: 'shift',
17: 'ctrl',
18: 'alt',
20: 'capslock',
27: 'esc',
32: 'space',
33: 'pageup',
34: 'pagedown',
35: 'end',
36: 'home',
37: 'left',
38: 'up',
39: 'right',
40: 'down',
45: 'ins',
46: 'del',
91: 'meta',
93: 'meta',
224: 'meta'
},
/**
* mapping for special characters so they can support
*
* this dictionary is only used incase you want to bind a
* keyup or keydown event to one of these keys
*
* @type {Object}
*/
_KEYCODE_MAP = {
106: '*',
107: '+',
109: '-',
110: '.',
111 : '/',
186: ';',
187: '=',
188: ',',
189: '-',
190: '.',
191: '/',
192: '`',
219: '[',
220: '\\',
221: ']',
222: '\''
},
/**
* this is a mapping of keys that require shift on a US keypad
* back to the non shift equivelents
*
* this is so you can use keyup events with these keys
*
* note that this will only work reliably on US keyboards
*
* @type {Object}
*/
_SHIFT_MAP = {
'~': '`',
'!': '1',
'@': '2',
'#': '3',
'$': '4',
'%': '5',
'^': '6',
'&': '7',
'*': '8',
'(': '9',
')': '0',
'_': '-',
'+': '=',
':': ';',
'\"': '\'',
'<': ',',
'>': '.',
'?': '/',
'|': '\\'
},
/**
* this is a list of special strings you can use to map
* to modifier keys when you specify your keyboard shortcuts
*
* @type {Object}
*/
_SPECIAL_ALIASES = {
'option': 'alt',
'command': 'meta',
'return': 'enter',
'escape': 'esc',
'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'
},
/**
* variable to store the flipped version of _MAP from above
* needed to check if we should use keypress or not when no action
* is specified
*
* @type {Object|undefined}
*/
_REVERSE_MAP,
/**
* a list of all the callbacks setup via Mousetrap.bind()
*
* @type {Object}
*/
_callbacks = {},
/**
* direct map of string combinations to callbacks used for trigger()
*
* @type {Object}
*/
_directMap = {},
/**
* keeps track of what level each sequence is at since multiple
* sequences can start out with the same sequence
*
* @type {Object}
*/
_sequenceLevels = {},
/**
* variable to store the setTimeout call
*
* @type {null|number}
*/
_resetTimer,
/**
* temporary state where we will ignore the next keyup
*
* @type {boolean|string}
*/
_ignoreNextKeyup = false,
/**
* temporary state where we will ignore the next keypress
*
* @type {boolean}
*/
_ignoreNextKeypress = false,
/**
* are we currently inside of a sequence?
* type of action ("keyup" or "keydown" or "keypress") or false
*
* @type {boolean|string}
*/
_nextExpectedAction = false;
/**
* loop through the f keys, f1 to f19 and add them to the map
* programatically
*/
for (var i = 1; i < 20; ++i) {
_MAP[111 + i] = 'f' + i;
}
/**
* loop through to map numbers on the numeric keypad
*/
for (i = 0; i <= 9; ++i) {
_MAP[i + 96] = i;
}
/**
* cross browser add event method
*
* @param {Element|HTMLDocument} object
* @param {string} type
* @param {Function} callback
* @returns void
*/
function _addEvent(object, type, callback) {
if (object.addEventListener) {
object.addEventListener(type, callback, false);
return;
}
object.attachEvent('on' + type, callback);
}
/**
* takes the event and returns the key character
*
* @param {Event} e
* @return {string}
*/
function _characterFromEvent(e) {
// for keypress events we should return the character as is
if (e.type == 'keypress') {
var character = String.fromCharCode(e.which);
// if the shift key is not pressed then it is safe to assume
// that we want the character to be lowercase. this means if
// you accidentally have caps lock on then your key bindings
// will continue to work
//
// the only side effect that might not be desired is if you
// bind something like 'A' cause you want to trigger an
// event when capital A is pressed caps lock will no longer
// trigger the event. shift+a will though.
if (!e.shiftKey) {
character = character.toLowerCase();
}
return character;
}
// for non keypress events the special maps are needed
if (_MAP[e.which]) {
return _MAP[e.which];
}
if (_KEYCODE_MAP[e.which]) {
return _KEYCODE_MAP[e.which];
}
// if it is not in the special map
// with keydown and keyup events the character seems to always
// come in as an uppercase character whether you are pressing shift
// or not. we should make sure it is always lowercase for comparisons
return String.fromCharCode(e.which).toLowerCase();
}
/**
* checks if two arrays are equal
*
* @param {Array} modifiers1
* @param {Array} modifiers2
* @returns {boolean}
*/
function _modifiersMatch(modifiers1, modifiers2) {
return modifiers1.sort().join(',') === modifiers2.sort().join(',');
}
/**
* resets all sequence counters except for the ones passed in
*
* @param {Object} doNotReset
* @returns void
*/
function _resetSequences(doNotReset) {
doNotReset = doNotReset || {};
var activeSequences = false,
key;
for (key in _sequenceLevels) {
if (doNotReset[key]) {
activeSequences = true;
continue;
}
_sequenceLevels[key] = 0;
}
if (!activeSequences) {
_nextExpectedAction = false;
}
}
/**
* finds all callbacks that match based on the keycode, modifiers,
* and action
*
* @param {string} character
* @param {Array} modifiers
* @param {Event|Object} e
* @param {string=} sequenceName - name of the sequence we are looking for
* @param {string=} combination
* @param {number=} level
* @returns {Array}
*/
function _getMatches(character, modifiers, e, sequenceName, combination, level) {
var i,
callback,
matches = [],
action = e.type;
// if there are no events related to this keycode
if (!_callbacks[character]) {
return [];
}
// if a modifier key is coming up on its own we should allow it
if (action == 'keyup' && _isModifier(character)) {
modifiers = [character];
}
// loop through all callbacks for the key that was pressed
// and see if any of them match
for (i = 0; i < _callbacks[character].length; ++i) {
callback = _callbacks[character][i];
// if a sequence name is not specified, but this is a sequence at
// the wrong level then move onto the next match
if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {
continue;
}
// if the action we are looking for doesn't match the action we got
// then we should keep going
if (action != callback.action) {
continue;
}
// if this is a keypress event and the meta key and control key
// are not pressed that means that we need to only look at the
// character, otherwise check the modifiers as well
//
// chrome will not fire a keypress if meta or control is down
// safari will fire a keypress if meta or meta+shift is down
// firefox will fire a keypress if meta or control is down
if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) {
// when you bind a combination or sequence a second time it
// should overwrite the first one. if a sequenceName or
// combination is specified in this call it does just that
//
// @todo make deleting its own method?
var deleteCombo = !sequenceName && callback.combo == combination;
var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;
if (deleteCombo || deleteSequence) {
_callbacks[character].splice(i, 1);
}
matches.push(callback);
}
}
return matches;
}
/**
* takes a key event and figures out what the modifiers are
*
* @param {Event} e
* @returns {Array}
*/
function _eventModifiers(e) {
var modifiers = [];
if (e.shiftKey) {
modifiers.push('shift');
}
if (e.altKey) {
modifiers.push('alt');
}
if (e.ctrlKey) {
modifiers.push('ctrl');
}
if (e.metaKey) {
modifiers.push('meta');
}
return modifiers;
}
/**
* prevents default for this event
*
* @param {Event} e
* @returns void
*/
function _preventDefault(e) {
if (e.preventDefault) {
e.preventDefault();
return;
}
e.returnValue = false;
}
/**
* stops propogation for this event
*
* @param {Event} e
* @returns void
*/
function _stopPropagation(e) {
if (e.stopPropagation) {
e.stopPropagation();
return;
}
e.cancelBubble = true;
}
/**
* actually calls the callback function
*
* if your callback function returns false this will use the jquery
* convention - prevent default and stop propogation on the event
*
* @param {Function} callback
* @param {Event} e
* @returns void
*/
function _fireCallback(callback, e, combo, sequence) {
// if this event should not happen stop here
if (Mousetrap.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
return;
}
if (callback(e, combo) === false) {
_preventDefault(e);
_stopPropagation(e);
}
}
/**
* handles a character key event
*
* @param {string} character
* @param {Array} modifiers
* @param {Event} e
* @returns void
*/
function _handleKey(character, modifiers, e) {
var callbacks = _getMatches(character, modifiers, e),
i,
doNotReset = {},
maxLevel = 0,
processedSequenceCallback = false;
// Calculate the maxLevel for sequences so we can only execute the longest callback sequence
for (i = 0; i < callbacks.length; ++i) {
if (callbacks[i].seq) {
maxLevel = Math.max(maxLevel, callbacks[i].level);
}
}
// loop through matching callbacks for this key event
for (i = 0; i < callbacks.length; ++i) {
// fire for all sequence callbacks
// this is because if for example you have multiple sequences
// bound such as "g i" and "g t" they both need to fire the
// callback for matching g cause otherwise you can only ever
// match the first one
if (callbacks[i].seq) {
// only fire callbacks for the maxLevel to prevent
// subsequences from also firing
//
// for example 'a option b' should not cause 'option b' to fire
// even though 'option b' is part of the other sequence
//
// any sequences that do not match here will be discarded
// below by the _resetSequences call
if (callbacks[i].level != maxLevel) {
continue;
}
processedSequenceCallback = true;
// keep a list of which sequences were matches for later
doNotReset[callbacks[i].seq] = 1;
_fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);
continue;
}
// if there were no sequence matches but we are still here
// that means this is a regular match so we should fire that
if (!processedSequenceCallback) {
_fireCallback(callbacks[i].callback, e, callbacks[i].combo);
}
}
// if the key you pressed matches the type of sequence without
// being a modifier (ie "keyup" or "keypress") then we should
// reset all sequences that were not matched by this event
//
// this is so, for example, if you have the sequence "h a t" and you
// type "h e a r t" it does not match. in this case the "e" will
// cause the sequence to reset
//
// modifier keys are ignored because you can have a sequence
// that contains modifiers such as "enter ctrl+space" and in most
// cases the modifier key will be pressed before the next key
//
// also if you have a sequence such as "ctrl+b a" then pressing the
// "b" key will trigger a "keypress" and a "keydown"
//
// the "keydown" is expected when there is a modifier, but the
// "keypress" ends up matching the _nextExpectedAction since it occurs
// after and that causes the sequence to reset
//
// we ignore keypresses in a sequence that directly follow a keydown
// for the same character
var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;
if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) {
_resetSequences(doNotReset);
}
_ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';
}
/**
* handles a keydown event
*
* @param {Event} e
* @returns void
*/
function _handleKeyEvent(e) {
// normalize e.which for key events
// @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
if (typeof e.which !== 'number') {
e.which = e.keyCode;
}
var character = _characterFromEvent(e);
// no character found then stop
if (!character) {
return;
}
// need to use === for the character check because the character can be 0
if (e.type == 'keyup' && _ignoreNextKeyup === character) {
_ignoreNextKeyup = false;
return;
}
Mousetrap.handleKey(character, _eventModifiers(e), e);
}
/**
* determines if the keycode specified is a modifier key or not
*
* @param {string} key
* @returns {boolean}
*/
function _isModifier(key) {
return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
}
/**
* called to set a 1 second timeout on the specified sequence
*
* this is so after each key press in the sequence you have 1 second
* to press the next key before you have to start over
*
* @returns void
*/
function _resetSequenceTimer() {
clearTimeout(_resetTimer);
_resetTimer = setTimeout(_resetSequences, 1000);
}
/**
* reverses the map lookup so that we can look for specific keys
* to see what can and can't use keypress
*
* @return {Object}
*/
function _getReverseMap() {
if (!_REVERSE_MAP) {
_REVERSE_MAP = {};
for (var key in _MAP) {
// pull out the numeric keypad from here cause keypress should
// be able to detect the keys from the character
if (key > 95 && key < 112) {
continue;
}
if (_MAP.hasOwnProperty(key)) {
_REVERSE_MAP[_MAP[key]] = key;
}
}
}
return _REVERSE_MAP;
}
/**
* picks the best action based on the key combination
*
* @param {string} key - character for key
* @param {Array} modifiers
* @param {string=} action passed in
*/
function _pickBestAction(key, modifiers, action) {
// if no action was picked in we should try to pick the one
// that we think would work best for this key
if (!action) {
action = _getReverseMap()[key] ? 'keydown' : 'keypress';
}
// modifier keys don't work as expected with keypress,
// switch to keydown
if (action == 'keypress' && modifiers.length) {
action = 'keydown';
}
return action;
}
/**
* binds a key sequence to an event
*
* @param {string} combo - combo specified in bind call
* @param {Array} keys
* @param {Function} callback
* @param {string=} action
* @returns void
*/
function _bindSequence(combo, keys, callback, action) {
// start off by adding a sequence level record for this combination
// and setting the level to 0
_sequenceLevels[combo] = 0;
/**
* callback to increase the sequence level for this sequence and reset
* all other sequences that were active
*
* @param {string} nextAction
* @returns {Function}
*/
function _increaseSequence(nextAction) {
return function() {
_nextExpectedAction = nextAction;
++_sequenceLevels[combo];
_resetSequenceTimer();
};
}
/**
* wraps the specified callback inside of another function in order
* to reset all sequence counters as soon as this sequence is done
*
* @param {Event} e
* @returns void
*/
function _callbackAndReset(e) {
_fireCallback(callback, e, combo);
// we should ignore the next key up if the action is key down
// or keypress. this is so if you finish a sequence and
// release the key the final key will not trigger a keyup
if (action !== 'keyup') {
_ignoreNextKeyup = _characterFromEvent(e);
}
// weird race condition if a sequence ends with the key
// another sequence begins with
setTimeout(_resetSequences, 10);
}
// loop through keys one at a time and bind the appropriate callback
// function. for any key leading up to the final one it should
// increase the sequence. after the final, it should reset all sequences
//
// if an action is specified in the original bind call then that will
// be used throughout. otherwise we will pass the action that the
// next key in the sequence should match. this allows a sequence
// to mix and match keypress and keydown events depending on which
// ones are better suited to the key provided
for (var i = 0; i < keys.length; ++i) {
var isFinal = i + 1 === keys.length;
var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);
_bindSingle(keys[i], wrappedCallback, action, combo, i);
}
}
/**
* Converts from a string key combination to an array
*
* @param {string} combination like "command+shift+l"
* @return {Array}
*/
function _keysFromString(combination) {
if (combination === '+') {
return ['+'];
}
return combination.split('+');
}
/**
* Gets info for a specific key combination
*
* @param {string} combination key combination ("command+s" or "a" or "*")
* @param {string=} action
* @returns {Object}
*/
function _getKeyInfo(combination, action) {
var keys,
key,
i,
modifiers = [];
// take the keys from this pattern and figure out what the actual
// pattern is all about
keys = _keysFromString(combination);
for (i = 0; i < keys.length; ++i) {
key = keys[i];
// normalize key names
if (_SPECIAL_ALIASES[key]) {
key = _SPECIAL_ALIASES[key];
}
// if this is not a keypress event then we should
// be smart about using shift keys
// this will only work for US keyboards however
if (action && action != 'keypress' && _SHIFT_MAP[key]) {
key = _SHIFT_MAP[key];
modifiers.push('shift');
}
// if this key is a modifier then add it to the list of modifiers
if (_isModifier(key)) {
modifiers.push(key);
}
}
// depending on what the key combination is
// we will try to pick the best event for it
action = _pickBestAction(key, modifiers, action);
return {
key: key,
modifiers: modifiers,
action: action
};
}
/**
* binds a single keyboard combination
*
* @param {string} combination
* @param {Function} callback
* @param {string=} action
* @param {string=} sequenceName - name of sequence if part of sequence
* @param {number=} level - what part of the sequence the command is
* @returns void
*/
function _bindSingle(combination, callback, action, sequenceName, level) {
// store a direct mapped reference for use with Mousetrap.trigger
_directMap[combination + ':' + action] = callback;
// make sure multiple spaces in a row become a single space
combination = combination.replace(/\s+/g, ' ');
var sequence = combination.split(' '),
info;
// if this pattern is a sequence of keys then run through this method
// to reprocess each pattern one key at a time
if (sequence.length > 1) {
_bindSequence(combination, sequence, callback, action);
return;
}
info = _getKeyInfo(combination, action);
// make sure to initialize array if this is the first time
// a callback is added for this key
_callbacks[info.key] = _callbacks[info.key] || [];
// remove an existing match if there is one
_getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level);
// add this call back to the array
// if it is a sequence put it at the beginning
// if not put it at the end
//
// this is important because the way these are processed expects
// the sequence ones to come first
_callbacks[info.key][sequenceName ? 'unshift' : 'push']({
callback: callback,
modifiers: info.modifiers,
action: info.action,
seq: sequenceName,
level: level,
combo: combination
});
}
/**
* binds multiple combinations to the same callback
*
* @param {Array} combinations
* @param {Function} callback
* @param {string|undefined} action
* @returns void
*/
function _bindMultiple(combinations, callback, action) {
for (var i = 0; i < combinations.length; ++i) {
_bindSingle(combinations[i], callback, action);
}
}
// start!
_addEvent(document, 'keypress', _handleKeyEvent);
_addEvent(document, 'keydown', _handleKeyEvent);
_addEvent(document, 'keyup', _handleKeyEvent);
var Mousetrap = {
/**
* binds an event to mousetrap
*
* can be a single key, a combination of keys separated with +,
* an array of keys, or a sequence of keys separated by spaces
*
* be sure to list the modifier keys first to make sure that the
* correct key ends up getting bound (the last key in the pattern)
*
* @param {string|Array} keys
* @param {Function} callback
* @param {string=} action - 'keypress', 'keydown', or 'keyup'
* @returns void
*/
bind: function(keys, callback, action) {
keys = keys instanceof Array ? keys : [keys];
_bindMultiple(keys, callback, action);
return this;
},
/**
* unbinds an event to mousetrap
*
* the unbinding sets the callback function of the specified key combo
* to an empty function and deletes the corresponding key in the
* _directMap dict.
*
* TODO: actually remove this from the _callbacks dictionary instead
* of binding an empty function
*
* the keycombo+action has to be exactly the same as
* it was defined in the bind method
*
* @param {string|Array} keys
* @param {string} action
* @returns void
*/
unbind: function(keys, action) {
return Mousetrap.bind(keys, function() {}, action);
},
/**
* triggers an event that has already been bound
*
* @param {string} keys
* @param {string=} action
* @returns void
*/
trigger: function(keys, action) {
if (_directMap[keys + ':' + action]) {
_directMap[keys + ':' + action]({}, keys);
}
return this;
},
/**
* resets the library back to its initial state. this is useful
* if you want to clear out the current keyboard shortcuts and bind
* new ones - for example if you switch to another page
*
* @returns void
*/
reset: function() {
_callbacks = {};
_directMap = {};
return this;
},
/**
* should we stop this event before firing off callbacks
*
* @param {Event} e
* @param {Element} element
* @return {boolean}
*/
stopCallback: function(e, element) {
// if the element has the class "mousetrap" then no need to stop
if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
return false;
}
// stop for input, select, and textarea
return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;
},
/**
* exposes _handleKey publicly so it can be overwritten by extensions
*/
handleKey: _handleKey
};
module.exports = Mousetrap;

View File

@@ -0,0 +1,26 @@
function minIndent(text) {
return text.split("\n").reduce(function(min, line) {
if (line.trim().length === 0) return min;
var indent = line.length - line.trimLeft().length;
return min === null ? indent : Math.min(min, indent);
}, null);
}
function alignIndents(text) {
var indent = minIndent(text);
return text.split("\n").map(function(line) {
return line.slice(indent).trimRight();
}).join("\n");
}
function cleanText(text, type) {
text = alignIndents(text);
while (text[0] === "\n") text = text.slice(1);
while (text[text.length-1] === "\n") text = text.slice(0, text.length - 1);
if (type === "html") {
text = text.replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&amp;/g, "&");
}
return text + "\n";
}
module.exports = { cleanText: cleanText };

View File

@@ -0,0 +1,9 @@
window.global = window;
require("traceur/bin/traceur-runtime");
require("./css/screen.less");
var Deck = require("./deck");
window.deck = new Deck(document.getElementById("slides"), {
"editor": require("./modules/editor"),
"background": require("./modules/background"),
"image": require("./modules/image")
});

View File

@@ -0,0 +1,35 @@
/*global setTimeout */
var events = require("../lib/events");
function Background(slide, url) {
const preload = document.createElement("img");
preload.src = url;
// --- activate
this.activate = () => {
if (this.background) this.background.parentNode.removeChild(this.background);
this.background = document.createElement("div");
this.background.classList.add("background");
this.background.style.backgroundImage = "url(" + url + ")";
slide.parentNode.appendChild(this.background);
setTimeout((() => {
this.background.classList.add("active");
}).bind(this), 1);
}
// --- cleanup
this.cleanup = () => {
events.once(this.background, events.vendorPrefix("TransitionEnd"), () => {
this.background.parentNode.removeChild(this.background);
this.background = null;
}, this);
this.background.classList.remove("active");
}
}
module.exports = Background;

View File

@@ -0,0 +1,258 @@
/*global setTimeout, Audio */
require("codemirror/lib/codemirror.js");
require("codemirror/lib/codemirror.css");
require("codemirror/addon/edit/matchbrackets.js");
require("codemirror/addon/edit/closebrackets.js");
require("codemirror/addon/hint/show-hint.js");
require("codemirror/addon/hint/show-hint.css");
require("codemirror/addon/hint/anyword-hint.js");
require("codemirror/addon/selection/active-line.js");
require("codemirror/addon/comment/comment.js");
require("codemirror/addon/tern/tern.js");
require("codemirror/addon/tern/tern.css");
require("codemirror/mode/javascript/javascript.js");
require("codemirror/theme/xq-light.css");
let CodeMirror = window.CodeMirror; // :(
window.tern = require("tern"); // ;(((
var events = require("../lib/events");
var text = require("../lib/text");
var emacs = require("./editor/emacs");
var es6ify = require("./editor/es6ify");
var esprima = require("./editor/esprima");
var Spinner = require("spin.js");
var _ = require("underscore");
function Editor(slide, mode) {
const args = slide.dataset;
const target = slide.querySelector(".slideContainer");
const initialCode = target.innerHTML;
this.onTabClose = () => {
if (!this.cm.isClean()) {
return "The current buffer has modifications.";
}
};
// --- Comms
this.send = (message) => {
this.targetFrame.contentWindow.postMessage(JSON.stringify(message), "*");
};
this.compile = () => {
var js = es6ify.compile(this.cm.getDoc().getValue());
this.cm.clearGutter();
if (js.errors.length) {
new Audio(require("./editor/smb_bump.mp3")).play();
for (let i = 0; i < js.errors.length; i++) {
let [all, file, line, ch, msg] = /^([^:]*):(\d+):(\d+): (.*)$/.exec(js.errors[i]);
line = parseInt(line, 10) -1; ch = parseInt(ch, 10) -1;
let marker = document.createElement("img");
marker.title = msg;
marker.classList.add("cm-error");
this.cm.setGutterMarker(line, "cm-errors", marker);
}
return null;
} else {
return js.js;
}
};
this.evalInFrame = (cm) => {
// FIXME select behaviour by language, don't assume JS
const js = this.compile();
if (!js) return;
if (args.reload !== undefined) {
this.targetFrame.src = args.href;
setTimeout((() => {
events.until(this.targetFrame.contentWindow, "message", function(e) {
if (e.data === "rdy lol") {
this.send({code: js});
return true;
}
}, this);
}).bind(this), 100);
} else {
this.send({code: js});
}
};
this.evalFormAtPoint = (cm) => {
// FIXME select behaviour by language, don't assume JS
const src = cm.getDoc().getValue();
const tree = esprima.parse(src, {
tolerant: true, range: true
});
const cur = cm.indexFromPos(cm.getCursor());
tree.body
.filter((n) => cur >= n.range[0] && cur <= n.range[1])
.forEach((n) => {
cm.getDoc().setSelection(cm.posFromIndex(n.range[0]),
cm.posFromIndex(n.range[1]));
var js = es6ify.compile(cm.getDoc().getSelection());
if (js.errors.length) {
new Audio(require("./editor/smb_bump.mp3")).play();
let marker = document.createElement("img");
marker.title = js.errors[0];
marker.classList.add("cm-error");
this.cm.setGutterMarker(cm.posFromIndex(n.range[1]).line, "cm-errors", marker);
} else {
console.log(js.js);
this.send({code: js.js});
}
});
};
this.reloadFrame = (cm) => {
this.targetFrame.src = args.href;
};
// --- keybindings
this.iframeBind = (key) => {
return (function() { this.send({ key: key }); }).bind(this);
};
const keymap = {};
keymap["Ctrl-S"] = this.evalInFrame.bind(this);
keymap["Ctrl-D"] = this.evalFormAtPoint.bind(this);
keymap["Ctrl-R"] = this.reloadFrame.bind(this);
keymap["Alt-Space"] = this.iframeBind("space");
keymap["Alt-Enter"] = this.iframeBind("enter");
keymap["Alt-Up"] = this.iframeBind("up");
keymap["Alt-Down"] = this.iframeBind("down");
keymap["Alt-Left"] = this.iframeBind("left");
keymap["Alt-Right"] = this.iframeBind("right");
keymap["Ctrl-K"] = emacs.kill;
keymap["Ctrl-Y"] = emacs.yank;
keymap["Ctrl-A"] = "goLineStartSmart";
keymap["Ctrl-E"] = "goLineEnd";
keymap["Ctrl-,"] = "toggleComment";
keymap.Tab = (cm) => cm.indentLine(cm.getDoc().getCursor().line);
keymap["Ctrl-\\"] = (cm) => CodeMirror.showHint(cm);
keymap["Ctrl-="] = (cm) => CodeMirror.showHint(cm);
keymap["Ctrl-'"] = (cm) => {
const cur = cm.getDoc().getCursor();
const token = cm.getTokenAt(cur, true);
cm.getDoc().extendSelection({line: cur.line, ch: token.start},
{line: cur.line, ch: token.end});
}
keymap.Esc = (cm) => {
// wow, much hack
const input = document.createElement("input");
input.setAttribute("type", "text");
document.body.appendChild(input);
input.focus();
input.parentNode.removeChild(input);
};
// --- CodeMirror config
const options = {
value: text.cleanText(initialCode, "html"),
mode: mode,
extraKeys: keymap,
gutters: ["cm-errors"],
// lineNumbers: true,
lineWrapping: false,
// matchBrackets: true,
autoCloseBrackets: true,
styleActiveLine: true,
theme: "xq-light"
};
// --- activate
this.activate = () => {
slide.classList.add("editor");
target.innerHTML = "";
this.editorFrame = document.createElement("div");
this.editorFrame.classList.add("editorFrame");
target.appendChild(this.editorFrame);
this.targetContainer = document.createElement("div");
this.targetContainer.classList.add("targetFrame");
this.targetFrame = document.createElement("iframe");
this.loaderFrame = document.createElement("div");
this.loaderFrame.classList.add("loaderFrame");
this.targetContainer.appendChild(this.loaderFrame);
target.appendChild(this.targetContainer);
const factor = Math.min(this.loaderFrame.clientWidth,
this.loaderFrame.clientHeight) / 13.25;
this.spinner = new Spinner({
color: "white",
shadow: true,
hwaccel: true,
length: factor * 2,
radius: factor * 2,
width: factor,
trail: 40,
lines: 12
}).spin(this.loaderFrame);
this.cm = CodeMirror(this.editorFrame, options);
this.cm.setSize("100%", "100%");
if (mode === "text/javascript") {
let tern = new CodeMirror.TernServer({
defs: [ require("tern/defs/ecma5.json"),
require("tern/defs/browser.json"),
require("./editor/mousetrap.json"),
require("./editor/rxjs.json") ]
});
let ternKeymap = _.extend({}, keymap);
ternKeymap["Ctrl-\\"] = (cm) => tern.complete(cm);
ternKeymap["Ctrl-I"] = (cm) => tern.showType(cm);
ternKeymap["Alt-."] = (cm) => tern.jumpToDef(cm);
ternKeymap["Alt-,"] = (cm) => tern.jumpBack(cm);
ternKeymap["Ctrl-Q"] = (cm) => tern.rename(cm);
this.cm.setOption("extraKeys", ternKeymap);
this.cm.on("cursorActivity", (cm) => tern.updateArgHints(cm));
}
}
// --- stabilise
this.stabilise = () => {
this.targetFrame.style.display = "none";
this.targetFrame.src = args.href;
this.targetContainer.appendChild(this.targetFrame);
events.until(this.targetFrame.contentWindow, "message", function(e) {
if (e.data === "rdy lol") {
this.spinner.stop();
this.targetFrame.style.display = "";
this.targetContainer.removeChild(this.loaderFrame);
this.loaderFrame = this.spinner = null;
return true;
}
}, this);
this.cm.refresh();
this.cleanupHandler = events.on(window, "beforeunload", this.onTabClose, this);
}
// --- cleanup
this.cleanup = () => {
if (this.cleanupHandler) {
events.off(this.cleanupHandler);
this.cleanupHandler = null;
}
this.cm = null;
target.innerHTML = initialCode;
target.classList.remove("editor");
}
}
module.exports = Editor;

View File

@@ -0,0 +1,31 @@
/*global setTimeout */
(function() {
var preservedState = {};
function evalCode(code) {
setTimeout(function() {
var module = preservedState;
eval(code);
}, 1);
}
function onMessage(e) {
var message;
try {
message = JSON.parse(e.data);
} catch(e) {
return;
}
if (message.hasOwnProperty("key") && window.Mousetrap) {
Mousetrap.trigger(message.key);
}
if (message.hasOwnProperty("code")) {
evalCode(message.code);
}
}
window.addEventListener("message", onMessage);
window.postMessage("rdy lol", "*");
})();

View File

@@ -0,0 +1,90 @@
/*global CodeMirror */
// Copied from CodeMirror's Emacs keymap:
var killRing = [];
function addToRing(str) {
killRing.push(str);
if (killRing.length > 50) killRing.shift();
}
function growRingTop(str) {
if (!killRing.length) return addToRing(str);
killRing[killRing.length - 1] += str;
}
function getFromRing(n) { return killRing[killRing.length - (n ? Math.min(n, 1) : 1)] || ""; }
function popFromRing() { if (killRing.length > 1) killRing.pop(); return getFromRing(); }
var lastKill = null;
function posEq(a, b) { return a.line == b.line && a.ch == b.ch; }
function kill(cm, from, to, mayGrow, text) {
if (text == null) text = cm.getRange(from, to);
if (mayGrow && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen))
growRingTop(text);
else
addToRing(text);
cm.replaceRange("", from, to, "+delete");
if (mayGrow) lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()};
else lastKill = null;
}
function getPrefix(cm, precise) {
var digits = cm.state.emacsPrefix;
if (!digits) return precise ? null : 1;
clearPrefix(cm);
return digits == "-" ? -1 : Number(digits);
}
var prefixPreservingKeys = {"Alt-G": true, "Ctrl-X": true, "Ctrl-Q": true, "Ctrl-U": true};
function maybeClearPrefix(cm, arg) {
if (!cm.state.emacsPrefixMap && !prefixPreservingKeys.hasOwnProperty(arg))
clearPrefix(cm);
}
function clearPrefix(cm) {
cm.state.emacsPrefix = null;
cm.off("keyHandled", maybeClearPrefix);
cm.off("inputRead", maybeDuplicateInput);
}
function maybeDuplicateInput(cm, event) {
var dup = getPrefix(cm);
if (dup > 1 && event.origin == "+input") {
var one = event.text.join("\n"), txt = "";
for (var i = 1; i < dup; ++i) txt += one;
cm.replaceSelection(txt, "end", "+input");
}
}
function repeated(cmd) {
var f = typeof cmd == "string" ? function(cm) { cm.execCommand(cmd); } : cmd;
return function(cm) {
var prefix = getPrefix(cm);
f(cm);
for (var i = 1; i < prefix; ++i) f(cm);
};
}
const killKey = repeated((cm) => {
var start = cm.getCursor(), end = cm.clipPos(CodeMirror.Pos(start.line));
var text = cm.getRange(start, end);
if (!/\S/.test(text)) {
text += "\n";
end = CodeMirror.Pos(start.line + 1, 0);
}
kill(cm, start, end, true, text);
});
const yankKey = (cm) => {
var start = cm.getCursor();
cm.replaceRange(getFromRing(getPrefix(cm)), start, start, "paste");
cm.setSelection(start, cm.getCursor());
};
module.exports = {
kill: killKey,
yank: yankKey
};

View File

@@ -0,0 +1,42 @@
/*global traceur */
var _ = require("underscore");
var traceurCode = require("raw!traceur/bin/traceur.js");
('global', eval)(traceurCode); // I don't even
const traceurOptions = {
modules: "commonjs",
filename: "repl.js",
blockBinding: true,
symbols: true,
deferredFunctions: true,
types: true,
annotations: true
};
function compile(code) {
traceur.options.reset();
_.extend(traceur.options, traceurOptions);
var errorReporter = new traceur.util.TestErrorReporter();
var sourceFile = new traceur.syntax.SourceFile(traceurOptions.filename, code);
var parser = new traceur.syntax.Parser(sourceFile, errorReporter);
var tree = parser.parseModule();
var transformer = new traceur.codegeneration.FromOptionsTransformer(errorReporter);
var transformedTree = transformer.transform(tree);
if (errorReporter.hadError()) {
return {
js: null,
errors: errorReporter.errors
};
} else {
return {
js: traceur.outputgeneration.TreeWriter.write(transformedTree),
errors: errorReporter.errors
};
}
}
module.exports = { compile: compile };

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