You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@jspwiki.apache.org by Janne Jalkanen <ja...@ecyrd.com> on 2009/06/22 21:36:50 UTC

Re: svn commit: r787361 [1/2] - in /incubator/jspwiki/trunk: ./ src/java/org/apache/wiki/ tests/JSSpec/ tests/JSSpec/Assets/ tests/JSSpec/Assets/Scripts/ tests/JSSpec/Assets/Styles/

We can't have LGPL dependencies (forbidden by Apache rules), even for  
tests. Please roll back.

/Janne


On 22 Jun 2009, at 22:23, brushed@apache.org wrote:

> Author: brushed
> Date: Mon Jun 22 19:23:30 2009
> New Revision: 787361
>
> URL: http://svn.apache.org/viewvc?rev=787361&view=rev
> Log:
> v3.0.0-svn-133: added javascript test-suite based on JSSpec.
>
> Added:
>    incubator/jspwiki/trunk/tests/JSSpec/
>    incubator/jspwiki/trunk/tests/JSSpec/Assets/
>    incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/
>    incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/COPYING    
> (with props)
>    incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpec.js    
> (with props)
>    incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
> JSSpecSpecs.js   (with props)
>    incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
> diff_match_patch.js   (with props)
>    incubator/jspwiki/trunk/tests/JSSpec/Assets/Styles/
>    incubator/jspwiki/trunk/tests/JSSpec/Assets/Styles/JSSpec- 
> jspwiki.css
>    incubator/jspwiki/trunk/tests/JSSpec/Assets/Styles/JSSpec.css    
> (with props)
>    incubator/jspwiki/trunk/tests/JSSpec/JSSpec-dialog.js
>    incubator/jspwiki/trunk/tests/JSSpec/JSSpec-index.html
>    incubator/jspwiki/trunk/tests/JSSpec/JSSpec-jspwiki-common.js
>    incubator/jspwiki/trunk/tests/JSSpec/JSSpec-jspwiki-edit.js
> Modified:
>    incubator/jspwiki/trunk/ChangeLog
>    incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java
>
> Modified: incubator/jspwiki/trunk/ChangeLog
> URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/ChangeLog?rev=787361&r1=787360&r2=787361&view=diff
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- incubator/jspwiki/trunk/ChangeLog (original)
> +++ incubator/jspwiki/trunk/ChangeLog Mon Jun 22 19:23:30 2009
> @@ -1,3 +1,12 @@
> +2009-06-22 Dirk Frederickx <brushed AT apache DOT org>
> +
> +        * 3.0.0-svn-133
> +
> +        * Added javascript unit-tests based on JSSpec. Not all  
> tests are running
> +        yet and the test-suite definitely needs further expansions.
> +        Just open tests/JSSpec/JSSpec-index.html in your browser to  
> run the test-suite.
> +
> +
> 2009-06-07 Janne Jalkanen <ja...@apache.org>
>
>         * 3.0.0-svn-132
>
> Modified: incubator/jspwiki/trunk/src/java/org/apache/wiki/ 
> Release.java
> URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java?rev=787361&r1=787360&r2=787361&view=diff
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java  
> (original)
> +++ incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java  
> Mon Jun 22 19:23:30 2009
> @@ -77,7 +77,7 @@
>      *  <p>
>      *  If the build identifier is empty, it is not added.
>      */
> -    public static final String     BUILD         = "132";
> +    public static final String     BUILD         = "133";
>
>     /**
>      *  This is the generic version string you should use
>
> Added: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/COPYING
> URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/COPYING?rev=787361&view=auto
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/COPYING  
> (added)
> +++ incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/COPYING Mon  
> Jun 22 19:23:30 2009
> @@ -0,0 +1,459 @@
> +		  GNU LESSER GENERAL PUBLIC LICENSE
> +		       Version 2.1, February 1999
> +
> + Copyright (C) 1991, 1999 Free Software Foundation, Inc.
> + 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
> + Everyone is permitted to copy and distribute verbatim copies
> + of this license document, but changing it is not allowed.
> +
> +[This is the first released version of the Lesser GPL.  It also  
> counts
> + as the successor of the GNU Library Public License, version 2, hence
> + the version number 2.1.]
> +
> +			    Preamble
> +
> +  The licenses for most software are designed to take away your
> +freedom to share and change it.  By contrast, the GNU General Public
> +Licenses are intended to guarantee your freedom to share and change
> +free software--to make sure the software is free for all its users.
> +
> +  This license, the Lesser General Public License, applies to some
> +specially designated software packages--typically libraries--of the
> +Free Software Foundation and other authors who decide to use it.  You
> +can use it too, but we suggest you first think carefully about  
> whether
> +this license or the ordinary General Public License is the better
> +strategy to use in any particular case, based on the explanations  
> below.
> +
> +  When we speak of free software, we are referring to freedom of use,
> +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 this service if you wish); that you receive source code or can  
> get
> +it if you want it; that you can change the software and use pieces of
> +it in new free programs; and that you are informed that you can do
> +these things.
> +
> +  To protect your rights, we need to make restrictions that forbid
> +distributors to deny you these rights or to ask you to surrender  
> these
> +rights.  These restrictions translate to certain responsibilities for
> +you if you distribute copies of the library or if you modify it.
> +
> +  For example, if you distribute copies of the library, whether  
> gratis
> +or for a fee, you must give the recipients all the rights that we  
> gave
> +you.  You must make sure that they, too, receive or can get the  
> source
> +code.  If you link other code with the library, you must provide
> +complete object files to the recipients, so that they can relink them
> +with the library after making changes to the library and recompiling
> +it.  And you must show them these terms so they know their rights.
> +
> +  We protect your rights with a two-step method: (1) we copyright the
> +library, and (2) we offer you this license, which gives you legal
> +permission to copy, distribute and/or modify the library.
> +
> +  To protect each distributor, we want to make it very clear that
> +there is no warranty for the free library.  Also, if the library is
> +modified by someone else and passed on, the recipients should know
> +that what they have is not the original version, so that the original
> +author's reputation will not be affected by problems that might be
> +introduced by others.
> +
> +  Finally, software patents pose a constant threat to the existence  
> of
> +any free program.  We wish to make sure that a company cannot
> +effectively restrict the users of a free program by obtaining a
> +restrictive license from a patent holder.  Therefore, we insist that
> +any patent license obtained for a version of the library must be
> +consistent with the full freedom of use specified in this license.
> +
> +  Most GNU software, including some libraries, is covered by the
> +ordinary GNU General Public License.  This license, the GNU Lesser
> +General Public License, applies to certain designated libraries, and
> +is quite different from the ordinary General Public License.  We use
> +this license for certain libraries in order to permit linking those
> +libraries into non-free programs.
> +
> +  When a program is linked with a library, whether statically or  
> using
> +a shared library, the combination of the two is legally speaking a
> +combined work, a derivative of the original library.  The ordinary
> +General Public License therefore permits such linking only if the
> +entire combination fits its criteria of freedom.  The Lesser General
> +Public License permits more lax criteria for linking other code with
> +the library.
> +
> +  We call this license the "Lesser" General Public License because it
> +does Less to protect the user's freedom than the ordinary General
> +Public License.  It also provides other free software developers Less
> +of an advantage over competing non-free programs.  These  
> disadvantages
> +are the reason we use the ordinary General Public License for many
> +libraries.  However, the Lesser license provides advantages in  
> certain
> +special circumstances.
> +
> +  For example, on rare occasions, there may be a special need to
> +encourage the widest possible use of a certain library, so that it  
> becomes
> +a de-facto standard.  To achieve this, non-free programs must be
> +allowed to use the library.  A more frequent case is that a free
> +library does the same job as widely used non-free libraries.  In this
> +case, there is little to gain by limiting the free library to free
> +software only, so we use the Lesser General Public License.
> +
> +  In other cases, permission to use a particular library in non-free
> +programs enables a greater number of people to use a large body of
> +free software.  For example, permission to use the GNU C Library in
> +non-free programs enables many more people to use the whole GNU
> +operating system, as well as its variant, the GNU/Linux operating
> +system.
> +
> +  Although the Lesser General Public License is Less protective of  
> the
> +users' freedom, it does ensure that the user of a program that is
> +linked with the Library has the freedom and the wherewithal to run
> +that program using a modified version of the Library.
> +
> +  The precise terms and conditions for copying, distribution and
> +modification follow.  Pay close attention to the difference between a
> +"work based on the library" and a "work that uses the library".  The
> +former contains code derived from the library, whereas the latter  
> must
> +be combined with the library in order to run.
> +
> +		  GNU LESSER GENERAL PUBLIC LICENSE
> +   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
> +
> +  0. This License Agreement applies to any software library or other
> +program which contains a notice placed by the copyright holder or
> +other authorized party saying it may be distributed under the terms  
> of
> +this Lesser General Public License (also called "this License").
> +Each licensee is addressed as "you".
> +
> +  A "library" means a collection of software functions and/or data
> +prepared so as to be conveniently linked with application programs
> +(which use some of those functions and data) to form executables.
> +
> +  The "Library", below, refers to any such software library or work
> +which has been distributed under these terms.  A "work based on the
> +Library" means either the Library or any derivative work under
> +copyright law: that is to say, a work containing the Library or a
> +portion of it, either verbatim or with modifications and/or  
> translated
> +straightforwardly into another language.  (Hereinafter, translation  
> is
> +included without limitation in the term "modification".)
> +
> +  "Source code" for a work means the preferred form of the work for
> +making modifications to it.  For a library, complete source code  
> means
> +all the source code for all modules it contains, plus any associated
> +interface definition files, plus the scripts used to control  
> compilation
> +and installation of the library.
> +
> +  Activities other than copying, distribution and modification are  
> not
> +covered by this License; they are outside its scope.  The act of
> +running a program using the Library is not restricted, and output  
> from
> +such a program is covered only if its contents constitute a work  
> based
> +on the Library (independent of the use of the Library in a tool for
> +writing it).  Whether that is true depends on what the Library does
> +and what the program that uses the Library does.
> +
> +  1. You may copy and distribute verbatim copies of the Library's
> +complete source code as you receive it, in any medium, provided that
> +you conspicuously and appropriately publish on each copy an
> +appropriate copyright notice and disclaimer of warranty; keep intact
> +all the notices that refer to this License and to the absence of any
> +warranty; and distribute a copy of this License along with the
> +Library.
> +
> +  You may charge a fee for the physical act of transferring a copy,
> +and you may at your option offer warranty protection in exchange  
> for a
> +fee.
> +
> +  2. You may modify your copy or copies of the Library or any portion
> +of it, thus forming a work based on the Library, and copy and
> +distribute such modifications or work under the terms of Section 1
> +above, provided that you also meet all of these conditions:
> +
> +    a) The modified work must itself be a software library.
> +
> +    b) You must cause the files modified to carry prominent notices
> +    stating that you changed the files and the date of any change.
> +
> +    c) You must cause the whole of the work to be licensed at no
> +    charge to all third parties under the terms of this License.
> +
> +    d) If a facility in the modified Library refers to a function  
> or a
> +    table of data to be supplied by an application program that uses
> +    the facility, other than as an argument passed when the facility
> +    is invoked, then you must make a good faith effort to ensure  
> that,
> +    in the event an application does not supply such function or
> +    table, the facility still operates, and performs whatever part of
> +    its purpose remains meaningful.
> +
> +    (For example, a function in a library to compute square roots has
> +    a purpose that is entirely well-defined independent of the
> +    application.  Therefore, Subsection 2d requires that any
> +    application-supplied function or table used by this function must
> +    be optional: if the application does not supply it, the square
> +    root function must still compute square roots.)
> +
> +These requirements apply to the modified work as a whole.  If
> +identifiable sections of that work are not derived from the Library,
> +and can be reasonably considered independent and separate works in
> +themselves, then this License, and its terms, do not apply to those
> +sections when you distribute them as separate works.  But when you
> +distribute the same sections as part of a whole which is a work based
> +on the Library, the distribution of the whole must be on the terms of
> +this License, whose permissions for other licensees extend to the
> +entire whole, and thus to each and every part regardless of who wrote
> +it.
> +
> +Thus, it is not the intent of this section to claim rights or contest
> +your rights to work written entirely by you; rather, the intent is to
> +exercise the right to control the distribution of derivative or
> +collective works based on the Library.
> +
> +In addition, mere aggregation of another work not based on the  
> Library
> +with the Library (or with a work based on the Library) on a volume of
> +a storage or distribution medium does not bring the other work under
> +the scope of this License.
> +
> +  3. You may opt to apply the terms of the ordinary GNU General  
> Public
> +License instead of this License to a given copy of the Library.  To  
> do
> +this, you must alter all the notices that refer to this License, so
> +that they refer to the ordinary GNU General Public License, version  
> 2,
> +instead of to this License.  (If a newer version than version 2 of  
> the
> +ordinary GNU General Public License has appeared, then you can  
> specify
> +that version instead if you wish.)  Do not make any other change in
> +these notices.
> +
> +  Once this change is made in a given copy, it is irreversible for
> +that copy, so the ordinary GNU General Public License applies to all
> +subsequent copies and derivative works made from that copy.
> +
> +  This option is useful when you wish to copy part of the code of
> +the Library into a program that is not a library.
> +
> +  4. You may copy and distribute the Library (or a portion or
> +derivative of it, under Section 2) in object code or executable form
> +under the terms of Sections 1 and 2 above provided that you accompany
> +it with the complete corresponding machine-readable source code,  
> which
> +must be distributed under the terms of Sections 1 and 2 above on a
> +medium customarily used for software interchange.
> +
> +  If distribution of object code is made by offering access to copy
> +from a designated place, then offering equivalent access to copy the
> +source code from the same place satisfies the requirement to
> +distribute the source code, even though third parties are not
> +compelled to copy the source along with the object code.
> +
> +  5. A program that contains no derivative of any portion of the
> +Library, but is designed to work with the Library by being compiled  
> or
> +linked with it, is called a "work that uses the Library".  Such a
> +work, in isolation, is not a derivative work of the Library, and
> +therefore falls outside the scope of this License.
> +
> +  However, linking a "work that uses the Library" with the Library
> +creates an executable that is a derivative of the Library (because it
> +contains portions of the Library), rather than a "work that uses the
> +library".  The executable is therefore covered by this License.
> +Section 6 states terms for distribution of such executables.
> +
> +  When a "work that uses the Library" uses material from a header  
> file
> +that is part of the Library, the object code for the work may be a
> +derivative work of the Library even though the source code is not.
> +Whether this is true is especially significant if the work can be
> +linked without the Library, or if the work is itself a library.  The
> +threshold for this to be true is not precisely defined by law.
> +
> +  If such an object file uses only numerical parameters, data
> +structure layouts and accessors, and small macros and small inline
> +functions (ten lines or less in length), then the use of the object
> +file is unrestricted, regardless of whether it is legally a  
> derivative
> +work.  (Executables containing this object code plus portions of the
> +Library will still fall under Section 6.)
> +
> +  Otherwise, if the work is a derivative of the Library, you may
> +distribute the object code for the work under the terms of Section 6.
> +Any executables containing that work also fall under Section 6,
> +whether or not they are linked directly with the Library itself.
> +
> +  6. As an exception to the Sections above, you may also combine or
> +link a "work that uses the Library" with the Library to produce a
> +work containing portions of the Library, and distribute that work
> +under terms of your choice, provided that the terms permit
> +modification of the work for the customer's own use and reverse
> +engineering for debugging such modifications.
> +
> +  You must give prominent notice with each copy of the work that the
> +Library is used in it and that the Library and its use are covered by
> +this License.  You must supply a copy of this License.  If the work
> +during execution displays copyright notices, you must include the
> +copyright notice for the Library among them, as well as a reference
> +directing the user to the copy of this License.  Also, you must do  
> one
> +of these things:
> +
> +    a) Accompany the work with the complete corresponding
> +    machine-readable source code for the Library including whatever
> +    changes were used in the work (which must be distributed under
> +    Sections 1 and 2 above); and, if the work is an executable linked
> +    with the Library, with the complete machine-readable "work that
> +    uses the Library", as object code and/or source code, so that the
> +    user can modify the Library and then relink to produce a modified
> +    executable containing the modified Library.  (It is understood
> +    that the user who changes the contents of definitions files in  
> the
> +    Library will not necessarily be able to recompile the application
> +    to use the modified definitions.)
> +
> +    b) Use a suitable shared library mechanism for linking with the
> +    Library.  A suitable mechanism is one that (1) uses at run time a
> +    copy of the library already present on the user's computer  
> system,
> +    rather than copying library functions into the executable, and  
> (2)
> +    will operate properly with a modified version of the library, if
> +    the user installs one, as long as the modified version is
> +    interface-compatible with the version that the work was made  
> with.
> +
> +    c) Accompany the work with a written offer, valid for at
> +    least three years, to give the same user the materials
> +    specified in Subsection 6a, above, for a charge no more
> +    than the cost of performing this distribution.
> +
> +    d) If distribution of the work is made by offering access to copy
> +    from a designated place, offer equivalent access to copy the  
> above
> +    specified materials from the same place.
> +
> +    e) Verify that the user has already received a copy of these
> +    materials or that you have already sent this user a copy.
> +
> +  For an executable, the required form of the "work that uses the
> +Library" must include any data and utility programs needed for
> +reproducing the executable from it.  However, as a special exception,
> +the materials to be distributed need not include anything that is
> +normally distributed (in either source or binary form) with the major
> +components (compiler, kernel, and so on) of the operating system on
> +which the executable runs, unless that component itself accompanies
> +the executable.
> +
> +  It may happen that this requirement contradicts the license
> +restrictions of other proprietary libraries that do not normally
> +accompany the operating system.  Such a contradiction means you  
> cannot
> +use both them and the Library together in an executable that you
> +distribute.
> +
> +  7. You may place library facilities that are a work based on the
> +Library side-by-side in a single library together with other library
> +facilities not covered by this License, and distribute such a  
> combined
> +library, provided that the separate distribution of the work based on
> +the Library and of the other library facilities is otherwise
> +permitted, and provided that you do these two things:
> +
> +    a) Accompany the combined library with a copy of the same work
> +    based on the Library, uncombined with any other library
> +    facilities.  This must be distributed under the terms of the
> +    Sections above.
> +
> +    b) Give prominent notice with the combined library of the fact
> +    that part of it is a work based on the Library, and explaining
> +    where to find the accompanying uncombined form of the same work.
> +
> +  8. You may not copy, modify, sublicense, link with, or distribute
> +the Library except as expressly provided under this License.  Any
> +attempt otherwise to copy, modify, sublicense, link with, or
> +distribute the Library is void, and will automatically terminate your
> +rights under this License.  However, parties who have received  
> copies,
> +or rights, from you under this License will not have their licenses
> +terminated so long as such parties remain in full compliance.
> +
> +  9. You are not required to accept this License, since you have not
> +signed it.  However, nothing else grants you permission to modify or
> +distribute the Library or its derivative works.  These actions are
> +prohibited by law if you do not accept this License.  Therefore, by
> +modifying or distributing the Library (or any work based on the
> +Library), you indicate your acceptance of this License to do so, and
> +all its terms and conditions for copying, distributing or modifying
> +the Library or works based on it.
> +
> +  10. Each time you redistribute the Library (or any work based on  
> the
> +Library), the recipient automatically receives a license from the
> +original licensor to copy, distribute, link with or modify the  
> Library
> +subject to these terms and conditions.  You may not impose any  
> further
> +restrictions on the recipients' exercise of the rights granted  
> herein.
> +You are not responsible for enforcing compliance by third parties  
> with
> +this License.
> +
> +  11. If, as a consequence of a court judgment or allegation of  
> patent
> +infringement or for any other reason (not limited to patent issues),
> +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
> +distribute so as to satisfy simultaneously your obligations under  
> this
> +License and any other pertinent obligations, then as a consequence  
> you
> +may not distribute the Library at all.  For example, if a patent
> +license would not permit royalty-free redistribution of the Library  
> by
> +all those who receive copies directly or indirectly through you, then
> +the only way you could satisfy both it and this License would be to
> +refrain entirely from distribution of the Library.
> +
> +If any portion of this section is held invalid or unenforceable  
> under any
> +particular circumstance, the balance of the section is intended to  
> apply,
> +and the section as a whole is intended to apply in other  
> circumstances.
> +
> +It is not the purpose of this section to induce you to infringe any
> +patents or other property right claims or to contest validity of any
> +such claims; this section has the sole purpose of protecting the
> +integrity of the free software distribution system which is
> +implemented by public license practices.  Many people have made
> +generous contributions to the wide range of software distributed
> +through that system in reliance on consistent application of that
> +system; it is up to the author/donor to decide if he or she is  
> willing
> +to distribute software through any other system and a licensee cannot
> +impose that choice.
> +
> +This section is intended to make thoroughly clear what is believed to
> +be a consequence of the rest of this License.
> +
> +  12. If the distribution and/or use of the Library is restricted in
> +certain countries either by patents or by copyrighted interfaces, the
> +original copyright holder who places the Library under this License  
> may add
> +an explicit geographical distribution limitation excluding those  
> countries,
> +so that distribution is permitted only in or among countries not thus
> +excluded.  In such case, this License incorporates the limitation  
> as if
> +written in the body of this License.
> +
> +  13. The Free Software Foundation may publish revised and/or new
> +versions of the Lesser 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  
> Library
> +specifies a version number of this License which applies to it and
> +"any later version", you have the option of following the terms and
> +conditions either of that version or of any later version published  
> by
> +the Free Software Foundation.  If the Library does not specify a
> +license version number, you may choose any version ever published by
> +the Free Software Foundation.
> +
> +  14. If you wish to incorporate parts of the Library into other free
> +programs whose distribution conditions are incompatible with these,
> +write to the author to ask for permission.  For software which is
> +copyrighted by the Free Software Foundation, write to the Free
> +Software Foundation; we sometimes make exceptions for this.  Our
> +decision will be guided by the two goals of preserving the free  
> status
> +of all derivatives of our free software and of promoting the sharing
> +and reuse of software generally.
> +
> +			    NO WARRANTY
> +
> +  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
> +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
> +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
> +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" 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
> +LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
> +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
> +
> +  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
> +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
> +AND/OR REDISTRIBUTE THE LIBRARY 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
> +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
> +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF  
> SUCH
> +DAMAGES.
> +
> +		     END OF TERMS AND CONDITIONS
> +
>
> Propchange: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
> COPYING
> ------------------------------------------------------------------------------
>    svn:executable = *
>
> Added: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpec.js
> URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpec.js?rev=787361&view=auto
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpec.js  
> (added)
> +++ incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpec.js  
> Mon Jun 22 19:23:30 2009
> @@ -0,0 +1,1552 @@
> +/**
> + * JSSpec
> + *
> + * Copyright 2007 Alan Kang
> + *  - mailto:jania902@gmail.com
> + *  - http://jania.pe.kr
> + *
> + * http://jania.pe.kr/aw/moin.cgi/JSSpec
> + *
> + * Dependencies:
> + *  - diff_match_patch.js ( http://code.google.com/p/google-diff-match-patch 
>  )
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free  
> Software
> + * Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA  
> 02110-1301, USA
> + */
> +
> +/**
> + * Namespace
> + */
> +
> +var JSSpec = {
> +	specs: [],
> +	
> +	EMPTY_FUNCTION: function() {},
> +	
> +	Browser: {
> +		// By Rendering Engines
> +		Trident: navigator.appName === "Microsoft Internet Explorer",
> +		Webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
> +		Gecko: navigator.userAgent.indexOf('Gecko') > -1 &&  
> navigator.userAgent.indexOf('KHTML') === -1,
> +		KHTML: navigator.userAgent.indexOf('KHTML') !== -1,
> +		Presto: navigator.appName === "Opera",
> +		
> +		// By Platforms
> +		Mac: navigator.userAgent.indexOf("Macintosh") !== -1,
> +		Ubuntu: navigator.userAgent.indexOf('Ubuntu') !== -1,
> +		Win: navigator.userAgent.indexOf('Windows') !== -1,
> +		
> +		// By Browsers
> +		IE: navigator.appName === "Microsoft Internet Explorer",
> +		IE6: navigator.userAgent.indexOf('MSIE 6') !== -1,
> +		IE7: navigator.userAgent.indexOf('MSIE 7') !== -1,
> +		IE8: navigator.userAgent.indexOf('MSIE 8') !== -1,
> +		
> +		FF: navigator.userAgent.indexOf('Firefox') !== -1,
> +		FF2: navigator.userAgent.indexOf('Firefox/2') !== -1,
> +		FF3: navigator.userAgent.indexOf('Firefox/3') !== -1,
> +		Safari: navigator.userAgent.indexOf('Safari') !== -1
> +	}
> +};
> +
> +
> +
> +/**
> + * Executor
> + */
> +JSSpec.Executor = function(target, onSuccess, onException) {
> +	this.target = target;
> +	this.onSuccess = typeof onSuccess == 'function' ? onSuccess :  
> JSSpec.EMPTY_FUNCTION;
> +	this.onException = typeof onException == 'function' ?  
> onException : JSSpec.EMPTY_FUNCTION;
> +	
> +	if(JSSpec.Browser.Trident) {
> +		// Exception handler for Trident. It helps to collect exact line  
> number where exception occured.
> +		window.onerror = function(message, fileName, lineNumber) {
> +			var self = window._curExecutor;
> +			var ex = {message:message, fileName:fileName,  
> lineNumber:lineNumber};
> +
> +			if(JSSpec._secondPass)  {
> +				ex = self.mergeExceptions(JSSpec._assertionFailure, ex);
> +				delete JSSpec._secondPass;
> +				delete JSSpec._assertionFailure;
> +				
> +				ex.type = "failure";
> +				self.onException(self, ex);
> +			} else if(JSSpec._assertionFailure) {
> +				JSSpec._secondPass = true;
> +				self.run();
> +			} else {
> +				self.onException(self, ex);
> +			}
> +			
> +			return true;
> +		};
> +	}
> +};
> +JSSpec.Executor.prototype.mergeExceptions =  
> function(assertionFailure, normalException) {
> +	var merged = {
> +		message:assertionFailure.message,
> +		fileName:normalException.fileName,
> +		lineNumber:normalException.lineNumber
> +	};
> +	
> +	return merged;
> +};
> +
> +JSSpec.Executor.prototype.run = function() {
> +	var self = this;
> +	var target = this.target;
> +	var onSuccess = this.onSuccess;
> +	var onException = this.onException;
> +	
> +	window.setTimeout(
> +		function() {
> +			var result;
> +			if(JSSpec.Browser.Trident) {
> +				try{
> +					window._curExecutor = self;
> +					
> +					result = self.target();
> +					self.onSuccess(self, result);
> +				}catch(ex){
> +					self.onException(self, ex);
> +				};
> +			} else {
> +				try {
> +					result = self.target();
> +					self.onSuccess(self, result);
> +				} catch(ex) {
> +					if(JSSpec.Browser.Webkit) ex = {message:ex.message,  
> fileName:ex.sourceURL, lineNumber:ex.line};
> +					
> +					if(JSSpec._secondPass)  {
> +						ex = self.mergeExceptions(JSSpec._assertionFailure, ex);
> +						delete JSSpec._secondPass;
> +						delete JSSpec._assertionFailure;
> +						
> +						ex.type = "failure";
> +						self.onException(self, ex);
> +					} else if(JSSpec._assertionFailure) {
> +						JSSpec._secondPass = true;
> +						self.run();
> +					} else {
> +						self.onException(self, ex);
> +					}
> +				}
> +			}
> +		},
> +		0
> +	);
> +};
> +
> +
> +
> +/**
> + * CompositeExecutor composites one or more executors and execute  
> them sequencially.
> + */
> +JSSpec.CompositeExecutor = function(onSuccess, onException,  
> continueOnException) {
> +	this.queue = [];
> +	this.onSuccess = typeof onSuccess == 'function' ? onSuccess :  
> JSSpec.EMPTY_FUNCTION;
> +	this.onException = typeof onException == 'function' ?  
> onException : JSSpec.EMPTY_FUNCTION;
> +	this.continueOnException = !!continueOnException;
> +};
> +
> +JSSpec.CompositeExecutor.prototype.addFunction = function(func) {
> +	this.addExecutor(new JSSpec.Executor(func));
> +};
> +
> +JSSpec.CompositeExecutor.prototype.addExecutor = function(executor) {
> +	var last = this.queue.length == 0 ? null :  
> this.queue[this.queue.length - 1];
> +	if(last) {
> +		last.next = executor;
> +	}
> +	
> +	executor.parent = this;
> +	executor.onSuccessBackup = executor.onSuccess;
> +	executor.onSuccess = function(result) {
> +		this.onSuccessBackup(result);
> +		if(this.next) {
> +			this.next.run();
> +		} else {
> +			this.parent.onSuccess();
> +		}
> +	};
> +	executor.onExceptionBackup = executor.onException;
> +	executor.onException = function(executor, ex) {
> +		this.onExceptionBackup(executor, ex);
> +
> +		if(this.parent.continueOnException) {
> +			if(this.next) {
> +				this.next.run();
> +			} else {
> +				this.parent.onSuccess();
> +			}
> +		} else {
> +			this.parent.onException(executor, ex);
> +		}
> +	};
> +
> +	this.queue.push(executor);
> +};
> +
> +JSSpec.CompositeExecutor.prototype.run = function() {
> +	if(this.queue.length > 0) {
> +		this.queue[0].run();
> +	}
> +};
> +
> +/**
> + * Spec is a set of Examples in a specific context
> + */
> +JSSpec.Spec = function(context, entries) {
> +	this.id = JSSpec.Spec.id++;
> +	this.context = context;
> +	this.url = location.href;
> +	
> +	this.filterEntriesByEmbeddedExpressions(entries);
> +	this.extractOutSpecialEntries(entries);
> +	this.examples = this.makeExamplesFromEntries(entries);
> +	this.examplesMap = this.makeMapFromExamples(this.examples);
> +};
> +
> +JSSpec.Spec.id = 0;
> +JSSpec.Spec.prototype.getExamples = function() {
> +	return this.examples;
> +};
> +
> +JSSpec.Spec.prototype.hasException = function() {
> +	return this.getTotalFailures() > 0 || this.getTotalErrors() > 0;
> +};
> +
> +JSSpec.Spec.prototype.getTotalFailures = function() {
> +	var examples = this.examples;
> +	var failures = 0;
> +	for(var i = 0; i < examples.length; i++) {
> +		if(examples[i].isFailure()) failures++;
> +	}
> +	return failures;
> +};
> +
> +JSSpec.Spec.prototype.getTotalErrors = function() {
> +	var examples = this.examples;
> +	var errors = 0;
> +	for(var i = 0; i < examples.length; i++) {
> +		if(examples[i].isError()) errors++;
> +	}
> +	return errors;
> +};
> +
> +JSSpec.Spec.prototype.filterEntriesByEmbeddedExpressions =  
> function(entries) {
> +	var isTrue;
> +	for(name in entries) if(entries.hasOwnProperty(name)) {
> +		var m = name.match(/\[\[(.+)\]\]/);
> +		if(m && m[1]) {
> +			eval("isTrue = (" + m[1] + ")");
> +			if(!isTrue) delete entries[name];
> +		}
> +	}
> +};
> +
> +JSSpec.Spec.prototype.extractOutSpecialEntries = function(entries) {
> +	this.beforeEach = JSSpec.EMPTY_FUNCTION;
> +	this.beforeAll = JSSpec.EMPTY_FUNCTION;
> +	this.afterEach = JSSpec.EMPTY_FUNCTION;
> +	this.afterAll = JSSpec.EMPTY_FUNCTION;
> +	
> +	for(name in entries) if(entries.hasOwnProperty(name)) {
> +		if(name == 'before' || name == 'before each' || name ==  
> 'before_each') {
> +			this.beforeEach = entries[name];
> +		} else if(name == 'before all' || name == 'before_all') {
> +			this.beforeAll = entries[name];
> +		} else if(name == 'after' || name == 'after each' || name ==  
> 'after_each') {
> +			this.afterEach = entries[name];
> +		} else if(name == 'after all' || name == 'after_all') {
> +			this.afterAll = entries[name];
> +		}
> +	}
> +	
> +	delete entries['before'];
> +	delete entries['before each'];
> +	delete entries['before_each'];
> +	delete entries['before all'];
> +	delete entries['before_all'];
> +	delete entries['after'];
> +	delete entries['after each'];
> +	delete entries['after_each'];
> +	delete entries['after all'];
> +	delete entries['after_all'];
> +};
> +
> +JSSpec.Spec.prototype.makeExamplesFromEntries = function(entries) {
> +	var examples = [];
> +	for(name in entries) if(entries.hasOwnProperty(name)) {
> +		examples.push(new JSSpec.Example(name, entries[name],  
> this.beforeEach, this.afterEach));
> +	}
> +	return examples;
> +};
> +
> +JSSpec.Spec.prototype.makeMapFromExamples = function(examples) {
> +	var map = {};
> +	for(var i = 0; i < examples.length; i++) {
> +		var example = examples[i];
> +		map[example.id] = examples[i];
> +	}
> +	return map;
> +};
> +
> +JSSpec.Spec.prototype.getExampleById = function(id) {
> +	return this.examplesMap[id];
> +};
> +
> +JSSpec.Spec.prototype.getExecutor = function() {
> +	var self = this;
> +	var onException = function(executor, ex) {
> +		self.exception = ex;
> +	};
> +	
> +	var composite = new JSSpec.CompositeExecutor();
> +	composite.addFunction(function() {JSSpec.log.onSpecStart(self);});
> +	composite.addExecutor(new JSSpec.Executor(this.beforeAll, null,  
> function(exec, ex) {
> +		self.exception = ex;
> +		JSSpec.log.onSpecEnd(self);
> +	}));
> +	
> +	var exampleAndAfter = new JSSpec.CompositeExecutor(null,null,true);
> +	for(var i = 0; i < this.examples.length; i++) {
> +		exampleAndAfter.addExecutor(this.examples[i].getExecutor());
> +	}
> +	exampleAndAfter.addExecutor(new JSSpec.Executor(this.afterAll,  
> null, onException));
> +	exampleAndAfter.addFunction(function()  
> {JSSpec.log.onSpecEnd(self);});
> +	composite.addExecutor(exampleAndAfter);
> +	
> +	return composite;
> +};
> +
> +/**
> + * Example
> + */
> +JSSpec.Example = function(name, target, before, after) {
> +	this.id = JSSpec.Example.id++;
> +	this.name = name;
> +	this.target = target;
> +	this.before = before;
> +	this.after = after;
> +};
> +
> +JSSpec.Example.id = 0;
> +JSSpec.Example.prototype.isFailure = function() {
> +	return this.exception && this.exception.type == "failure";
> +};
> +
> +JSSpec.Example.prototype.isError = function() {
> +	return this.exception && !this.exception.type;
> +};
> +
> +JSSpec.Example.prototype.getExecutor = function() {
> +	var self = this;
> +	var onException = function(executor, ex) {
> +		self.exception = ex;
> +	};
> +	
> +	var composite = new JSSpec.CompositeExecutor();
> +	composite.addFunction(function()  
> {JSSpec.log.onExampleStart(self);});
> +	composite.addExecutor(new JSSpec.Executor(this.before, null,  
> function(exec, ex) {
> +		self.exception = ex;
> +		JSSpec.log.onExampleEnd(self);
> +	}));
> +	
> +	var targetAndAfter = new JSSpec.CompositeExecutor(null,null,true);
> +	
> +	targetAndAfter.addExecutor(new JSSpec.Executor(this.target, null,  
> onException));
> +	targetAndAfter.addExecutor(new JSSpec.Executor(this.after, null,  
> onException));
> +	targetAndAfter.addFunction(function()  
> {JSSpec.log.onExampleEnd(self);});
> +	
> +	composite.addExecutor(targetAndAfter);
> +	
> +	return composite;
> +};
> +
> +/**
> + * Runner
> + */
> +JSSpec.Runner = function(specs, logger) {
> +	JSSpec.log = logger;
> +	
> +	this.totalExamples = 0;
> +	this.specs = [];
> +	this.specsMap = {};
> +	this.addAllSpecs(specs);
> +};
> +
> +JSSpec.Runner.prototype.addAllSpecs = function(specs) {
> +	for(var i = 0; i < specs.length; i++) {
> +		this.addSpec(specs[i]);
> +	}
> +};
> +
> +JSSpec.Runner.prototype.addSpec = function(spec) {
> +	this.specs.push(spec);
> +	this.specsMap[spec.id] = spec;
> +	this.totalExamples += spec.getExamples().length;
> +};
> +
> +JSSpec.Runner.prototype.getSpecById = function(id) {
> +	return this.specsMap[id];
> +};
> +
> +JSSpec.Runner.prototype.getSpecByContext = function(context) {
> +	for(var i = 0; i < this.specs.length; i++) {
> +		if(this.specs[i].context == context) return this.specs[i];
> +	}
> +	return null;
> +};
> +
> +JSSpec.Runner.prototype.getSpecs = function() {
> +	return this.specs;
> +};
> +
> +JSSpec.Runner.prototype.hasException = function() {
> +	return this.getTotalFailures() > 0 || this.getTotalErrors() > 0;
> +};
> +
> +JSSpec.Runner.prototype.getTotalFailures = function() {
> +	var specs = this.specs;
> +	var failures = 0;
> +	for(var i = 0; i < specs.length; i++) {
> +		failures += specs[i].getTotalFailures();
> +	}
> +	return failures;
> +};
> +
> +JSSpec.Runner.prototype.getTotalErrors = function() {
> +	var specs = this.specs;
> +	var errors = 0;
> +	for(var i = 0; i < specs.length; i++) {
> +		errors += specs[i].getTotalErrors();
> +	}
> +	return errors;
> +};
> +
> +
> +JSSpec.Runner.prototype.run = function() {
> +	JSSpec.log.onRunnerStart();
> +	var executor = new JSSpec.CompositeExecutor(function()  
> {JSSpec.log.onRunnerEnd()},null,true);
> +	for(var i = 0; i < this.specs.length; i++) {
> +		executor.addExecutor(this.specs[i].getExecutor());
> +	}
> +	executor.run();
> +};
> +
> +
> +JSSpec.Runner.prototype.rerun = function(context) {
> +	JSSpec.runner = new  
> JSSpec.Runner([this.getSpecByContext(context)], JSSpec.log);
> +	JSSpec.runner.run();
> +};
> +
> +/**
> + * Logger
> + */
> +JSSpec.Logger = function() {
> +	this.finishedExamples = 0;
> +	this.startedAt = null;
> +};
> +
> +JSSpec.Logger.prototype.onRunnerStart = function() {
> +	this._title = document.title;
> +
> +	this.startedAt = new Date();
> +	var container = document.getElementById('jsspec_container');
> +	if(container) {
> +		container.innerHTML = "";
> +	} else {
> +		container = document.createElement("DIV");
> +		container.id = "jsspec_container";
> +		document.body.appendChild(container);
> +	}
> +	
> +	var title = document.createElement("DIV");
> +	title.id = "title";
> +	title.innerHTML = [
> +		'<h1>JSSpec</h1>',
> +		'<ul>',
> +		JSSpec.options.rerun ? '<li>[<a href="?" title="rerun all  
> specs">X</a>] ' +  
> JSSpec.util.escapeTags(decodeURIComponent(JSSpec.options.rerun)) +  
> '</li>' : '',
> +		'	<li><span id="total_examples">' + JSSpec.runner.totalExamples +  
> '</span> examples</li>',
> +		'	<li><span id="total_failures">0</span> failures</li>',
> +		'	<li><span id="total_errors">0</span> errors</li>',
> +		'	<li><span id="progress">0</span>% done</li>',
> +		'	<li><span id="total_elapsed">0</span> secs</li>',
> +		'</ul>',
> +		'<p><a href="http://jania.pe.kr/aw/moin.cgi/JSSpec">JSSpec  
> homepage</a></p>',
> +	].join("");
> +	container.appendChild(title);
> +
> +	var list = document.createElement("DIV");
> +	list.id = "list";
> +	list.innerHTML = [
> +		'<h2>List</h2>',
> +		'<ul class="specs">',
> +		function() {
> +			var specs = JSSpec.runner.getSpecs();
> +			var sb = [];
> +			for(var i = 0; i < specs.length; i++) {
> +				var spec = specs[i];
> +				sb.push('<li id="spec_' + specs[i].id + '_list"><h3><a  
> href="#spec_' + specs[i].id + '">' +  
> JSSpec.util.escapeTags(specs[i].context) + '</a> [<a href="?rerun='  
> + encodeURIComponent(specs[i].context) + '">rerun '+  
> specs[i].examples.length + '</a>]</h3> </li>');
> +			}
> +			return sb.join("");
> +		}(),
> +		'</ul>'
> +	].join("");
> +	container.appendChild(list);
> +	
> +	var log = document.createElement("DIV");
> +	log.id = "log";
> +	log.innerHTML = [
> +		'<h2>Log</h2>',
> +		'<ul class="specs">',
> +		function() {
> +			var specs = JSSpec.runner.getSpecs();
> +			var sb = [];
> +			for(var i = 0; i < specs.length; i++) {
> +				var spec = specs[i];
> +				sb.push('	<li id="spec_' + specs[i].id + '">');
> +				sb.push('		<h3>' + JSSpec.util.escapeTags(specs[i].context) +  
> ' [<a href="?rerun=' + encodeURIComponent(specs[i].context) +  
> '">rerun '+ specs[i].examples.length + '</a>]</h3>');
> +				sb.push('		<ul id="spec_' + specs[i].id + '_examples"  
> class="examples">');
> +				for(var j = 0; j < spec.examples.length; j++) {
> +					var example = spec.examples[j];
> +					sb.push('			<li id="example_' + example.id + '">');
> +					sb.push('				<h4>' + JSSpec.util.escapeTags(example.name) + '</ 
> h4>');
> +					sb.push('				<pre class="examples- 
> code"><code>'+JSSpec.util.escapeTags(example.target.toString())+'</ 
> code></pre>');
> +					sb.push('			</li>');
> +				}
> +				sb.push('		</ul>');
> +				sb.push('	</li>');
> +			}
> +			return sb.join("");
> +		}(),
> +		'</ul>'
> +	].join("");
> +	
> +	container.appendChild(log);
> +	
> +	// add event handler for toggling
> +	var specs = JSSpec.runner.getSpecs();
> +	var sb = [];
> +	for(var i = 0; i < specs.length; i++) {
> +		var spec = document.getElementById("spec_" + specs[i].id);
> +		var title = spec.getElementsByTagName("H3")[0];
> +		title.onclick = function(e) {
> +			var target = document.getElementById(this.parentNode.id +  
> "_examples");
> +			target.style.display = target.style.display == "none" ?  
> "block" : "none";
> +			return true;
> +		}
> +	}
> +};
> +
> +JSSpec.Logger.prototype.onRunnerEnd = function() {
> +	if(JSSpec.runner.hasException()) {
> +		var times = 4;
> +		var title1 = "*" + this._title;
> +		var title2 = "*F" + JSSpec.runner.getTotalFailures() + " E" +  
> JSSpec.runner.getTotalErrors() + "* " + this._title;
> +	} else {
> +		var times = 2;
> +		var title1 = this._title;
> +		var title2 = "Success";
> +	}
> +	this.blinkTitle(times,title1,title2);
> +};
> +
> +JSSpec.Logger.prototype.blinkTitle = function(times, title1,  
> title2) {
> +	var times = times * 2;
> +	var mode = true;
> +	
> +	var f = function() {
> +		if(times > 0) {
> +			document.title = mode ? title1 : title2;
> +			mode = !mode;
> +			times--;
> +			window.setTimeout(f, 500);
> +		} else {
> +			document.title = title1;
> +		}
> +	};
> +	
> +	f();
> +};
> +
> +JSSpec.Logger.prototype.onSpecStart = function(spec) {
> +	var spec_list = document.getElementById("spec_" + spec.id +  
> "_list");
> +	var spec_log = document.getElementById("spec_" + spec.id);
> +	
> +	spec_list.className = "ongoing";
> +	spec_log.className = "ongoing";
> +};
> +
> +JSSpec.Logger.prototype.onSpecEnd = function(spec) {
> +	var spec_list = document.getElementById("spec_" + spec.id +  
> "_list");
> +	var spec_log = document.getElementById("spec_" + spec.id);
> +	var examples = document.getElementById("spec_" + spec.id +  
> "_examples");
> +	var className = spec.hasException() ? "exception" : "success";
> +
> +	spec_list.className = className;
> +	spec_log.className = className;
> +
> +	if(JSSpec.options.autocollapse && !spec.hasException())  
> examples.style.display = "none";
> +	
> +	if(spec.exception) {
> +		spec_log.appendChild(document.createTextNode(" - " +  
> spec.exception.message));
> +	}
> +};
> +
> +JSSpec.Logger.prototype.onExampleStart = function(example) {
> +	var li = document.getElementById("example_" + example.id);
> +	li.className = "ongoing";
> +};
> +
> +JSSpec.Logger.prototype.onExampleEnd = function(example) {
> +	var li = document.getElementById("example_" + example.id);
> +	li.className = example.exception ? "exception" : "success";
> +	
> +	if(example.exception) {
> +		var div = document.createElement("DIV");
> +		div.innerHTML = example.exception.message + "<p><br />" + " at "  
> + example.exception.fileName + ", line " +  
> example.exception.lineNumber + "</p>";
> +		li.appendChild(div);
> +	}
> +	
> +	var title = document.getElementById("title");
> +	var runner = JSSpec.runner;
> +	
> +	title.className = runner.hasException() ? "exception" : "success";
> +	
> +	this.finishedExamples++;
> +	document.getElementById("total_failures").innerHTML =  
> runner.getTotalFailures();
> +	document.getElementById("total_errors").innerHTML =  
> runner.getTotalErrors();
> +	var progress = parseInt(this.finishedExamples /  
> runner.totalExamples * 100);
> +	document.getElementById("progress").innerHTML = progress;
> +	document.getElementById("total_elapsed").innerHTML = (new  
> Date().getTime() - this.startedAt.getTime()) / 1000;
> +	
> +	document.title = progress + "%: " + this._title;
> +};
> +
> +/**
> + * IncludeMatcher
> + */
> +JSSpec.IncludeMatcher = function(actual, expected, condition) {
> +	this.actual = actual;
> +	this.expected = expected;
> +	this.condition = condition;
> +	this.match = false;
> +	this.explaination = this.makeExplain();
> +};
> +
> +JSSpec.IncludeMatcher.createInstance = function(actual, expected,  
> condition) {
> +	return new JSSpec.IncludeMatcher(actual, expected, condition);
> +};
> +
> +JSSpec.IncludeMatcher.prototype.matches = function() {
> +	return this.match;
> +};
> +
> +JSSpec.IncludeMatcher.prototype.explain = function() {
> +	return this.explaination;
> +};
> +
> +JSSpec.IncludeMatcher.prototype.makeExplain = function() {
> +	if(typeof this.actual.length == 'undefined') {
> +		return this.makeExplainForNotArray();
> +	} else {
> +		return this.makeExplainForArray();
> +	}
> +};
> +
> +JSSpec.IncludeMatcher.prototype.makeExplainForNotArray = function() {
> +	if(this.condition) {
> +		this.match = !!this.actual[this.expected];
> +	} else {
> +		this.match = !this.actual[this.expected];
> +	}
> +	
> +	var sb = [];
> +	sb.push('<p>actual value:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.actual, false, this.expected) + '</p>');
> +	sb.push('<p>should ' + (this.condition ? '' : 'not') + ' include:</ 
> p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.expected) + '</p>');
> +	return sb.join("");
> +};
> +
> +JSSpec.IncludeMatcher.prototype.makeExplainForArray = function() {
> +	var matches;
> +	if(this.condition) {
> +		for(var i = 0; i < this.actual.length; i++) {
> +			matches = JSSpec.EqualityMatcher.createInstance(this.expected,  
> this.actual[i]).matches();
> +			if(matches) {
> +				this.match = true;
> +				break;
> +			}
> +		}
> +	} else {
> +		for(var i = 0; i < this.actual.length; i++) {
> +			matches = JSSpec.EqualityMatcher.createInstance(this.expected,  
> this.actual[i]).matches();
> +			if(matches) {
> +				this.match = false;
> +				break;
> +			}
> +		}
> +	}
> +	
> +	if(this.match) return "";
> +	
> +	var sb = [];
> +	sb.push('<p>actual value:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.actual, false, this.condition ? null : i) +  
> '</p>');
> +	sb.push('<p>should ' + (this.condition ? '' : 'not') + ' include:</ 
> p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.expected) + '</p>');
> +	return sb.join("");
> +};
> +
> +/**
> + * PropertyLengthMatcher
> + */
> +JSSpec.PropertyLengthMatcher = function(num, property, o,  
> condition) {
> +	this.num = num;
> +	this.o = o;
> +	this.property = property;
> +	if((property == 'characters' || property == 'items') && typeof  
> o.length != 'undefined') {
> +		this.property = 'length';
> +	}
> +	
> +	this.condition = condition;
> +	this.conditionMet = function(x) {
> +		if(condition == 'exactly') return x.length == num;
> +		if(condition == 'at least') return x.length >= num;
> +		if(condition == 'at most') return x.length <= num;
> +
> +		throw "Unknown condition '" + condition + "'";
> +	};
> +	this.match = false;
> +	this.explaination = this.makeExplain();
> +};
> +
> +JSSpec.PropertyLengthMatcher.prototype.makeExplain = function() {
> +	if(this.o._type == 'String' && this.property == 'length') {
> +		this.match = this.conditionMet(this.o);
> +		return this.match ? '' : this.makeExplainForString();
> +	} else if(typeof this.o.length != 'undefined' && this.property ==  
> "length") {
> +		this.match = this.conditionMet(this.o);
> +		return this.match ? '' : this.makeExplainForArray();
> +	} else if(typeof this.o[this.property] != 'undefined' &&  
> this.o[this.property] != null) {
> +		this.match = this.conditionMet(this.o[this.property]);
> +		return this.match ? '' : this.makeExplainForObject();
> +	} else if(typeof this.o[this.property] == 'undefined' ||  
> this.o[this.property] == null) {
> +		this.match = false;
> +		return this.makeExplainForNoProperty();
> +	}
> +
> +	this.match = true;
> +};
> +
> +JSSpec.PropertyLengthMatcher.prototype.makeExplainForString =  
> function() {
> +	var sb = [];
> +	
> +	var exp = this.num == 0 ?
> +		'be an <strong>empty string</strong>' :
> +		'have <strong>' + this.condition + ' ' + this.num + ' characters</ 
> strong>';
> +	
> +	sb.push('<p>actual value has <strong>' + this.o.length + '  
> characters</strong>:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.o) + '</p>');
> +	sb.push('<p>but it should ' + exp + '.</p>');
> +	
> +	return sb.join("");
> +};
> +
> +JSSpec.PropertyLengthMatcher.prototype.makeExplainForArray =  
> function() {
> +	var sb = [];
> +	
> +	var exp = this.num == 0 ?
> +		'be an <strong>empty array</strong>' :
> +		'have <strong>' + this.condition + ' ' + this.num + ' items</ 
> strong>';
> +
> +	sb.push('<p>actual value has <strong>' + this.o.length + ' items</ 
> strong>:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.o) + '</p>');
> +	sb.push('<p>but it should ' + exp + '.</p>');
> +	
> +	return sb.join("");
> +};
> +
> +JSSpec.PropertyLengthMatcher.prototype.makeExplainForObject =  
> function() {
> +	var sb = [];
> +
> +	var exp = this.num == 0 ?
> +		'be <strong>empty</strong>' :
> +		'have <strong>' + this.condition + ' ' + this.num + ' ' +  
> this.property + '.</strong>';
> +
> +	sb.push('<p>actual value has <strong>' +  
> this.o[this.property].length + ' ' + this.property + '</strong>:</ 
> p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.o, false, this.property) + '</p>');
> +	sb.push('<p>but it should ' + exp + '.</p>');
> +	
> +	return sb.join("");
> +};
> +
> +JSSpec.PropertyLengthMatcher.prototype.makeExplainForNoProperty =  
> function() {
> +	var sb = [];
> +	
> +	sb.push('<p>actual value:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.o) + '</p>');
> +	sb.push('<p>should have <strong>' + this.condition + ' ' +  
> this.num + ' ' + this.property + '</strong> but there\'s no such  
> property.</p>');
> +	
> +	return sb.join("");
> +};
> +
> +JSSpec.PropertyLengthMatcher.prototype.matches = function() {
> +	return this.match;
> +};
> +
> +JSSpec.PropertyLengthMatcher.prototype.explain = function() {
> +	return this.explaination;
> +};
> +
> +JSSpec.PropertyLengthMatcher.createInstance = function(num,  
> property, o, condition) {
> +	return new JSSpec.PropertyLengthMatcher(num, property, o,  
> condition);
> +};
> +
> +/**
> + * EqualityMatcher
> + */
> +JSSpec.EqualityMatcher = {};
> +
> +JSSpec.EqualityMatcher.createInstance = function(expected, actual) {
> +	if(expected == null || actual == null) {
> +		return new JSSpec.NullEqualityMatcher(expected, actual);
> +	} else if(expected._type && expected._type == actual._type) {
> +		if(expected._type == "String") {
> +			return new JSSpec.StringEqualityMatcher(expected, actual);
> +		} else if(expected._type == "Date") {
> +			return new JSSpec.DateEqualityMatcher(expected, actual);
> +		} else if(expected._type == "Number") {
> +			return new JSSpec.NumberEqualityMatcher(expected, actual);
> +		} else if(expected._type == "Array") {
> +			return new JSSpec.ArrayEqualityMatcher(expected, actual);
> +		} else if(expected._type == "Boolean") {
> +			return new JSSpec.BooleanEqualityMatcher(expected, actual);
> +		}
> +	}
> +	
> +	return new JSSpec.ObjectEqualityMatcher(expected, actual);
> +};
> +
> +JSSpec.EqualityMatcher.basicExplain = function(expected, actual,  
> expectedDesc, actualDesc) {
> +	var sb = [];
> +	
> +	sb.push(actualDesc || '<p>actual value:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(actual) + '</p>');
> +	sb.push(expectedDesc || '<p>should be:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(expected) + '</p>');
> +	
> +	return sb.join("");
> +};
> +
> +JSSpec.EqualityMatcher.diffExplain = function(expected, actual) {
> +	var sb = [];
> +
> +	sb.push('<p>diff:</p>');
> +	sb.push('<p style="margin-left:2em;">');
> +	
> +	var dmp = new diff_match_patch();
> +	var diff = dmp.diff_main(expected, actual);
> +	dmp.diff_cleanupEfficiency(diff);
> +	
> +	sb.push(JSSpec.util.inspect(dmp.diff_prettyHtml(diff), true));
> +	
> +	sb.push('</p>');
> +	
> +	return sb.join("");
> +};
> +
> +/**
> + * BooleanEqualityMatcher
> + */
> +JSSpec.BooleanEqualityMatcher = function(expected, actual) {
> +	this.expected = expected;
> +	this.actual = actual;
> +};
> +
> +JSSpec.BooleanEqualityMatcher.prototype.explain = function() {
> +	var sb = [];
> +	
> +	sb.push('<p>actual value:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.actual) + '</p>');
> +	sb.push('<p>should be:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.expected) + '</p>');
> +	
> +	return sb.join("");
> +};
> +
> +JSSpec.BooleanEqualityMatcher.prototype.matches = function() {
> +	return this.expected == this.actual;
> +};
> +
> +/**
> + * NullEqualityMatcher
> + */
> +JSSpec.NullEqualityMatcher = function(expected, actual) {
> +	this.expected = expected;
> +	this.actual = actual;
> +};
> +
> +JSSpec.NullEqualityMatcher.prototype.matches = function() {
> +	return this.expected == this.actual && typeof this.expected ==  
> typeof this.actual;
> +};
> +
> +JSSpec.NullEqualityMatcher.prototype.explain = function() {
> +	return JSSpec.EqualityMatcher.basicExplain(this.expected,  
> this.actual);
> +};
> +
> +JSSpec.DateEqualityMatcher = function(expected, actual) {
> +	this.expected = expected;
> +	this.actual = actual;
> +};
> +
> +JSSpec.DateEqualityMatcher.prototype.matches = function() {
> +	return this.expected.getTime() == this.actual.getTime();
> +};
> +
> +JSSpec.DateEqualityMatcher.prototype.explain = function() {
> +	var sb = [];
> +	
> +	sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected,  
> this.actual));
> +	 
> sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected.toString(),  
> this.actual.toString()));
> +
> +	return sb.join("");
> +};
> +
> +/**
> + * ObjectEqualityMatcher
> + */
> +JSSpec.ObjectEqualityMatcher = function(expected, actual) {
> +	this.expected = expected;
> +	this.actual = actual;
> +	this.match = this.expected == this.actual;
> +	this.explaination = this.makeExplain();
> +};
> +
> +JSSpec.ObjectEqualityMatcher.prototype.matches = function() {return  
> this.match};
> +
> +JSSpec.ObjectEqualityMatcher.prototype.explain = function() {return  
> this.explaination};
> +
> +JSSpec.ObjectEqualityMatcher.prototype.makeExplain = function() {
> +	if(this.expected == this.actual) {
> +		this.match = true;
> +		return "";
> +	}
> +	
> +	if(JSSpec.util.isDomNode(this.expected)) {
> +		return this.makeExplainForDomNode();
> +	}
> +	
> +	var key, expectedHasItem, actualHasItem;
> +
> +	for(key in this.expected) {
> +		expectedHasItem = this.expected[key] != null && typeof  
> this.expected[key] != 'undefined';
> +		actualHasItem = this.actual[key] != null && typeof  
> this.actual[key] != 'undefined';
> +		if(expectedHasItem && !actualHasItem) return  
> this.makeExplainForMissingItem(key);
> +	}
> +	for(key in this.actual) {
> +		expectedHasItem = this.expected[key] != null && typeof  
> this.expected[key] != 'undefined';
> +		actualHasItem = this.actual[key] != null && typeof  
> this.actual[key] != 'undefined';
> +		if(actualHasItem && !expectedHasItem) return  
> this.makeExplainForUnknownItem(key);
> +	}
> +	
> +	for(key in this.expected) {
> +		var matcher =  
> JSSpec.EqualityMatcher.createInstance(this.expected[key],  
> this.actual[key]);
> +		if(!matcher.matches()) return this.makeExplainForItemMismatch(key);
> +	}
> +		
> +	this.match = true;
> +};
> +
> +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForDomNode =  
> function(key) {
> +	var sb = [];
> +	
> +	sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected,  
> this.actual));
> +	
> +	return sb.join("");
> +};
> +
> +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForMissingItem =  
> function(key) {
> +	var sb = [];
> +
> +	sb.push('<p>actual value has no item named <strong>' +  
> JSSpec.util.inspect(key) + '</strong></p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.actual, false, key) + '</p>');
> +	sb.push('<p>but it should have the item whose value is <strong>' +  
> JSSpec.util.inspect(this.expected[key]) + '</strong></p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.expected, false, key) + '</p>');
> +	
> +	return sb.join("");
> +};
> +
> +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForUnknownItem =  
> function(key) {
> +	var sb = [];
> +
> +	sb.push('<p>actual value has item named <strong>' +  
> JSSpec.util.inspect(key) + '</strong></p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.actual, false, key) + '</p>');
> +	sb.push('<p>but there should be no such item</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.expected, false, key) + '</p>');
> +	
> +	return sb.join("");
> +};
> +
> +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForItemMismatch =  
> function(key) {
> +	var sb = [];
> +
> +	sb.push('<p>actual value has an item named <strong>' +  
> JSSpec.util.inspect(key) + '</strong> whose value is <strong>' +  
> JSSpec.util.inspect(this.actual[key]) + '</strong></p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.actual, false, key) + '</p>');
> +	sb.push('<p>but it\'s value should be <strong>' +  
> JSSpec.util.inspect(this.expected[key]) + '</strong></p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.expected, false, key) + '</p>');
> +	
> +	return sb.join("");
> +};
> +
> +
> +
> +
> +/**
> + * ArrayEqualityMatcher
> + */
> +JSSpec.ArrayEqualityMatcher = function(expected, actual) {
> +	this.expected = expected;
> +	this.actual = actual;
> +	this.match = this.expected == this.actual;
> +	this.explaination = this.makeExplain();
> +};
> +
> +JSSpec.ArrayEqualityMatcher.prototype.matches = function() {return  
> this.match};
> +
> +JSSpec.ArrayEqualityMatcher.prototype.explain = function() {return  
> this.explaination};
> +
> +JSSpec.ArrayEqualityMatcher.prototype.makeExplain = function() {
> +	if(this.expected.length != this.actual.length) return  
> this.makeExplainForLengthMismatch();
> +	
> +	for(var i = 0; i < this.expected.length; i++) {
> +		var matcher =  
> JSSpec.EqualityMatcher.createInstance(this.expected[i],  
> this.actual[i]);
> +		if(!matcher.matches()) return this.makeExplainForItemMismatch(i);
> +	}
> +		
> +	this.match = true;
> +};
> +
> +JSSpec.ArrayEqualityMatcher.prototype.makeExplainForLengthMismatch  
> = function() {
> +	return JSSpec.EqualityMatcher.basicExplain(
> +		this.expected,
> +		this.actual,
> +		'<p>but it should be <strong>' + this.expected.length + '</ 
> strong></p>',
> +		'<p>actual value has <strong>' + this.actual.length + '</strong>  
> items</p>'
> +	);
> +};
> +
> +JSSpec.ArrayEqualityMatcher.prototype.makeExplainForItemMismatch =  
> function(index) {
> +	var postfix = ["th", "st", "nd", "rd", "th"][Math.min((index + 1)  
> % 10,4)];
> +	
> +	var sb = [];
> +
> +	sb.push('<p>' + (index + 1) + postfix + ' item (index ' + index +  
> ') of actual value is <strong>' +  
> JSSpec.util.inspect(this.actual[index]) + '</strong>:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.actual, false, index) + '</p>');
> +	sb.push('<p>but it should be <strong>' +  
> JSSpec.util.inspect(this.expected[index]) + '</strong>:</p>');
> +	sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.expected, false, index) + '</p>');
> +	
> +	return sb.join("");
> +};
> +
> +/**
> + * NumberEqualityMatcher
> + */
> +JSSpec.NumberEqualityMatcher = function(expected, actual) {
> +	this.expected = expected;
> +	this.actual = actual;
> +};
> +
> +JSSpec.NumberEqualityMatcher.prototype.matches = function() {
> +	if(this.expected == this.actual) return true;
> +};
> +
> +JSSpec.NumberEqualityMatcher.prototype.explain = function() {
> +	return JSSpec.EqualityMatcher.basicExplain(this.expected,  
> this.actual);
> +};
> +
> +/**
> + * StringEqualityMatcher
> + */
> +JSSpec.StringEqualityMatcher = function(expected, actual) {
> +	this.expected = expected;
> +	this.actual = actual;
> +};
> +
> +JSSpec.StringEqualityMatcher.prototype.matches = function() {
> +	return this.expected == this.actual;
> +};
> +
> +JSSpec.StringEqualityMatcher.prototype.explain = function() {
> +	var sb = [];
> +
> +	sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected,  
> this.actual));
> +	sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected,  
> this.actual));	
> +	return sb.join("");
> +};
> +
> +/**
> + * PatternMatcher
> + */
> +JSSpec.PatternMatcher = function(actual, pattern, condition) {
> +	this.actual = actual;
> +	this.pattern = pattern;
> +	this.condition = condition;
> +	this.match = false;
> +	this.explaination = this.makeExplain();
> +};
> +
> +JSSpec.PatternMatcher.createInstance = function(actual, pattern,  
> condition) {
> +	return new JSSpec.PatternMatcher(actual, pattern, condition);
> +};
> +
> +JSSpec.PatternMatcher.prototype.makeExplain = function() {
> +	var sb;
> +	if(this.actual == null || this.actual._type != 'String') {
> +		sb = [];
> +		sb.push('<p>actual value:</p>');
> +		sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.actual) + '</p>');
> +		sb.push('<p>should ' + (this.condition ? '' : 'not') + ' match  
> with pattern:</p>');
> +		sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.pattern) + '</p>');
> +		sb.push('<p>but pattern matching cannot be performed.</p>');
> +		return sb.join("");
> +	} else {
> +		this.match = this.condition == !!this.actual.match(this.pattern);
> +		if(this.match) return "";
> +		
> +		sb = [];
> +		sb.push('<p>actual value:</p>');
> +		sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.actual) + '</p>');
> +		sb.push('<p>should ' + (this.condition ? '' : 'not') + ' match  
> with pattern:</p>');
> +		sb.push('<p style="margin-left:2em;">' +  
> JSSpec.util.inspect(this.pattern) + '</p>');
> +		return sb.join("");
> +	}
> +};
> +
> +JSSpec.PatternMatcher.prototype.matches = function() {
> +	return this.match;
> +};
> +
> +JSSpec.PatternMatcher.prototype.explain = function() {
> +	return this.explaination;
> +};
> +
> +/**
> + * Domain Specific Languages
> + */
> +JSSpec.DSL = {};
> +
> +JSSpec.DSL.forString = {
> +	normalizeHtml: function() {
> +		var html = this;
> +		
> +		// Uniformize quotation, turn tag names and attribute names into  
> lower case
> +		html = html.replace(/<(\/?)(\w+)([^>]*?)>/img, function(str,  
> closingMark, tagName, attrs) {
> +			var sortedAttrs =  
> JSSpec 
> .util 
> .sortHtmlAttrs 
> (JSSpec.util.correctHtmlAttrQuotation(attrs).toLowerCase())
> +			return "<" + closingMark + tagName.toLowerCase() + sortedAttrs +  
> ">"
> +		});
> +		
> +		// validation self-closing tags
> +		html = html.replace(/<(br|hr|img)([^>]*?)>/mg, function(str, tag,  
> attrs) {
> +			return "<" + tag + attrs + " />";
> +		});
> +		
> +		// append semi-colon at the end of style value
> +		html = html.replace(/style="(.*?)"/mg, function(str, styleStr) {
> +			styleStr = JSSpec.util.sortStyleEntries(styleStr.strip()); //  
> for Safari
> +			if(styleStr.charAt(styleStr.length - 1) != ';') styleStr += ";"
> +			
> +			return 'style="' + styleStr + '"'
> +		});
> +		
> +		// sort style entries
> +		
> +		// remove empty style attributes
> +		html = html.replace(/ style=";"/mg, "");
> +		
> +		// remove new-lines
> +		html = html.replace(/\r/mg, '');
> +		html = html.replace(/\n/mg, '');
> +			
> +		return html;
> +	}
> +};
> +
> +
> +JSSpec.DSL.describe = function(context, entries, base) {
> +	if(base) {
> +		for(var i = 0; i < JSSpec.specs.length; i++) {
> +			if(JSSpec.specs[i].context === base) {
> +				base = JSSpec.specs[i];
> +				break;
> +			}
> +		}
> +		
> +		for(var i = 0; i < base.examples.length; i++) {
> +			var example = base.examples[i];
> +			
> +			if(!entries[example.name]) entries[example.name] = example.target;
> +		}
> +	}
> +	
> +	JSSpec.specs.push(new JSSpec.Spec(context, entries));
> +};
> +
> +JSSpec.DSL.value_of = function(target) {
> +	if(JSSpec._secondPass) return {};
> +	
> +	var subject = new JSSpec.DSL.Subject(target);
> +	return subject;
> +};
> +
> +JSSpec.DSL.Subject = function(target) {
> +	this.target = target;
> +};
> +
> +JSSpec.DSL.Subject.prototype._type = 'Subject';
> +
> +JSSpec.DSL.Subject.prototype.should_fail = function(message) {
> +	JSSpec._assertionFailure = {message:message};
> +	throw JSSpec._assertionFailure;
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_be = function(expected) {
> +	var matcher = JSSpec.EqualityMatcher.createInstance(expected,  
> this.target);
> +	if(!matcher.matches()) {
> +		JSSpec._assertionFailure = {message:matcher.explain()};
> +		throw JSSpec._assertionFailure;
> +	}
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_not_be = function(expected) {
> +	// TODO JSSpec.EqualityMatcher should support 'condition'
> +	var matcher = JSSpec.EqualityMatcher.createInstance(expected,  
> this.target);
> +	if(matcher.matches()) {
> +		JSSpec._assertionFailure = {message:"'" + this.target + "' should  
> not be '" + expected + "'"};
> +		throw JSSpec._assertionFailure;
> +	}
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_be_empty = function() {
> +	this.should_have(0, this.getType() == 'String' ? 'characters' :  
> 'items');
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_not_be_empty = function() {
> +	this.should_have_at_least(1, this.getType() == 'String' ?  
> 'characters' : 'items');
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_be_true = function() {
> +	this.should_be(true);
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_be_false = function() {
> +	this.should_be(false);
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_be_null = function() {
> +	this.should_be(null);
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_be_undefined = function() {
> +	this.should_be(undefined);
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_not_be_null = function() {
> +	this.should_not_be(null);
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_not_be_undefined = function() {
> +	this.should_not_be(undefined);
> +};
> +
> +JSSpec.DSL.Subject.prototype._should_have = function(num, property,  
> condition) {
> +	var matcher = JSSpec.PropertyLengthMatcher.createInstance(num,  
> property, this.target, condition);
> +	if(!matcher.matches()) {
> +		JSSpec._assertionFailure = {message:matcher.explain()};
> +		throw JSSpec._assertionFailure;
> +	}
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_have = function(num, property) {
> +	this._should_have(num, property, "exactly");
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_have_exactly = function(num,  
> property) {
> +	this._should_have(num, property, "exactly");
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_have_at_least = function(num,  
> property) {
> +	this._should_have(num, property, "at least");
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_have_at_most = function(num,  
> property) {
> +	this._should_have(num, property, "at most");
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_include = function(expected) {
> +	var matcher = JSSpec.IncludeMatcher.createInstance(this.target,  
> expected, true);
> +	if(!matcher.matches()) {
> +		JSSpec._assertionFailure = {message:matcher.explain()};
> +		throw JSSpec._assertionFailure;
> +	}
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_not_include =  
> function(expected) {
> +	var matcher = JSSpec.IncludeMatcher.createInstance(this.target,  
> expected, false);
> +	if(!matcher.matches()) {
> +		JSSpec._assertionFailure = {message:matcher.explain()};
> +		throw JSSpec._assertionFailure;
> +	}
> +};
> +
> +JSSpec.DSL.Subject.prototype.should_match = function(pattern) {
> +	var matcher = JSSpec.PatternMatcher.createInstance(this.target,  
> pattern, true);
> +	if(!matcher.matches()) {
> +		JSSpec._assertionFailure = {message:matcher.explain()};
> +		throw JSSpec._assertionFailure;
> +	}
> +}
> +JSSpec.DSL.Subject.prototype.should_not_match = function(pattern) {
> +	var matcher = JSSpec.PatternMatcher.createInstance(this.target,  
> pattern, false);
> +	if(!matcher.matches()) {
> +		JSSpec._assertionFailure = {message:matcher.explain()};
> +		throw JSSpec._assertionFailure;
> +	}
> +};
> +
> +JSSpec.DSL.Subject.prototype.getType = function() {
> +	if(typeof this.target == 'undefined') {
> +		return 'undefined';
> +	} else if(this.target == null) {
> +		return 'null';
> +	} else if(this.target._type) {
> +		return this.target._type;
> +	} else if(JSSpec.util.isDomNode(this.target)) {
> +		return 'DomNode';
> +	} else {
> +		return 'object';
> +	}
> +};
> +
> +/**
> + * Utilities
> + */
> +JSSpec.util = {
> +	escapeTags: function(string) {
> +		return string.replace(/</img, '&lt;').replace(/>/img, '&gt;');
> +	},
> +	escapeMetastring: function(string) {
> +		return string.replace(/\r/img, '\\r').replace(/\n/img, '\ 
> \n').replace(/\&para\;\<BR\>/img, '\\n').replace(/\t/img, '\\t');
> +	},
> +	parseOptions: function(defaults) {
> +		var options = defaults;
> +		
> +		var url = location.href;
> +		var queryIndex = url.indexOf('?');
> +		if(queryIndex == -1) return options;
> +		
> +		var query = url.substring(queryIndex + 1).split('#')[0];
> +		var pairs = query.split('&');
> +		for(var i = 0; i < pairs.length; i++) {
> +			var tokens = pairs[i].split('=');
> +			options[tokens[0]] = tokens[1];
> +		}
> +		
> +		return options;
> +	},
> +	correctHtmlAttrQuotation: function(html) {
> +		html = html.replace(/(\w+)=['"]([^'"]+)['"]/mg,function (str,  
> name, value) {return name + '=' + '"' + value + '"';});
> +		html = html.replace(/(\w+)=([^ '"]+)/mg,function (str, name,  
> value) {return name + '=' + '"' + value + '"';});
> +		html = html.replace(/'/mg, '"');
> +		
> +		return html;
> +	},
> +	sortHtmlAttrs: function(html) {
> +		var attrs = [];
> +		html.replace(/((\w+)="[^"]+")/mg, function(str, matched) {
> +			attrs.push(matched);
> +		});
> +		return attrs.length == 0 ? "" : " " + attrs.sort().join(" ");
> +	},
> +	sortStyleEntries: function(styleText) {
> +		var entries = styleText.split(/; /);
> +		return entries.sort().join("; ");
> +	},
> +	escapeHtml: function(str) {
> +		if(!this._div) {
> +			this._div = document.createElement("DIV");
> +			this._text = document.createTextNode('');
> +			this._div.appendChild(this._text);
> +		}
> +		this._text.data = str;
> +		return this._div.innerHTML;
> +	},
> +	isDomNode: function(o) {
> +		// TODO: make it more stricter
> +		return (typeof o.nodeName == 'string') && (typeof o.nodeType ==  
> 'number');
> +	},
> +	inspectDomPath: function(o) {
> +		var sb = [];
> +		while(o && o.nodeName != '#document' && o.parent) {
> +			var siblings = o.parentNode.childNodes;
> +			for(var i = 0; i < siblings.length; i++) {
> +				if(siblings[i] == o) {
> +					sb.push(o.nodeName + (i == 0 ? '' : '[' + i + ']'));
> +					break;
> +				}
> +			}
> +			o = o.parentNode;
> +		}
> +		return sb.join(" &gt; ");
> +	},
> +	inspectDomNode: function(o) {
> +		if(o.nodeType == 1) {
> +			var nodeName = o.nodeName.toLowerCase();
> +			var sb = [];
> +			sb.push('<span class="dom_value">');
> +			sb.push("&lt;");
> +			sb.push(nodeName);
> +			
> +			var attrs = o.attributes;
> +			for(var i = 0; i < attrs.length; i++) {
> +				if(
> +					attrs[i].nodeValue &&
> +					attrs[i].nodeName != 'contentEditable' &&
> +					attrs[i].nodeName != 'style' &&
> +					typeof attrs[i].nodeValue != 'function'
> +				) sb.push(' <span class="dom_attr_name">' +  
> attrs[i].nodeName.toLowerCase() + '</span>=<span  
> class="dom_attr_value">"' + attrs[i].nodeValue + '"</span>');
> +			}
> +			if(o.style && o.style.cssText) {
> +				sb.push(' <span class="dom_attr_name">style</span>=<span  
> class="dom_attr_value">"' + o.style.cssText + '"</span>');
> +			}
> +			sb.push('&gt;');
> +			sb.push(JSSpec.util.escapeHtml(o.innerHTML));
> +			sb.push('&lt;/' + nodeName + '&gt;');
> +			sb.push(' <span class="dom_path">(' +  
> JSSpec.util.inspectDomPath(o) + ')</span>' );
> +			sb.push('</span>');
> +			return sb.join("");
> +		} else if(o.nodeType == 3) {
> +			return '<span class="dom_value">#text ' + o.nodeValue + '</span>';
> +		} else {
> +			return '<span class="dom_value">UnknownDomNode</span>';
> +		}
> +	},
> +	inspect: function(o, dontEscape, emphasisKey) {
> +		var sb, inspected;
> +
> +		if(typeof o == 'undefined') return '<span  
> class="undefined_value">undefined</span>';
> +		if(o == null) return '<span class="null_value">null</span>';
> +		if(o._type == 'String') return '<span class="string_value">"' +  
> (dontEscape ? JSSpec.util.escapeMetastring(o) :  
> JSSpec.util.escapeHtml(JSSpec.util.escapeMetastring(o))) + '"</span>';
> +
> +		if(o._type == 'Date') {
> +			return '<span class="date_value">"' + o.toString() + '"</span>';
> +		}
> +		
> +		if(o._type == 'Number') return '<span class="number_value">' +  
> (dontEscape ? o : JSSpec.util.escapeHtml(o)) + '</span>';
> +		
> +		if(o._type == 'Boolean') return '<span class="boolean_value">' +  
> o + '</span>';
> +
> +		if(o._type == 'RegExp') return '<span class="regexp_value">' +  
> JSSpec.util.escapeHtml(o.toString()) + '</span>';
> +
> +		if(JSSpec.util.isDomNode(o)) return JSSpec.util.inspectDomNode(o);
> +
> +		if(o._type == 'Array' || typeof o.length != 'undefined') {
> +			sb = [];
> +			for(var i = 0; i < o.length; i++) {
> +				inspected = JSSpec.util.inspect(o[i]);
> +				sb.push(i == emphasisKey ? ('<strong>' + inspected + '</ 
> strong>') : inspected);
> +			}
> +			return '<span class="array_value">[' + sb.join(', ') + ']</span>';
> +		}
> +		
> +		// object
> +		sb = [];
> +		for(var key in o) {
> +			if(key == 'should') continue;
> +			
> +			inspected = JSSpec.util.inspect(key) + ":" +  
> JSSpec.util.inspect(o[key]);
> +			sb.push(key == emphasisKey ? ('<strong>' + inspected + '</ 
> strong>') : inspected);
> +		}
> +		return '<span class="object_value">{' + sb.join(', ') + '}</span>';
> +	}
> +};
> +
> +describe = JSSpec.DSL.describe;
> +behavior_of = JSSpec.DSL.describe;
> +value_of = JSSpec.DSL.value_of;
> +expect = JSSpec.DSL.value_of; // @deprecated
> +
> +String.prototype._type = "String";
> +Number.prototype._type = "Number";
> +Date.prototype._type = "Date";
> +Array.prototype._type = "Array";
> +Boolean.prototype._type = "Boolean";
> +RegExp.prototype._type = "RegExp";
> +
> +var targets = [Array.prototype, Date.prototype, Number.prototype,  
> String.prototype, Boolean.prototype, RegExp.prototype];
> +
> +String.prototype.normalizeHtml = JSSpec.DSL.forString.normalizeHtml;
> +String.prototype.asHtml = String.prototype.normalizeHtml; // 
> @deprecated
> +String.prototype.strip = function() {return this.replace(/^\s+/,  
> '').replace(/\s+$/, '');}
> +
> +
> +/**
> + * Main
> + */
> +JSSpec.defaultOptions = {
> +	autorun: 1,
> +	specIdBeginsWith: 0,
> +	exampleIdBeginsWith: 0,
> +	autocollapse: 1
> +};
> +JSSpec.options = JSSpec.util.parseOptions(JSSpec.defaultOptions);
> +
> +JSSpec.Spec.id = JSSpec.options.specIdBeginsWith;
> +JSSpec.Example.id = JSSpec.options.exampleIdBeginsWith;
> +
> +
> +
> +window.onload = function() {
> +	if(JSSpec.specs.length > 0) {
> +		if(!JSSpec.options.inSuite) {
> +			JSSpec.runner = new JSSpec.Runner(JSSpec.specs, new  
> JSSpec.Logger());
> +			if(JSSpec.options.rerun) {
> +				JSSpec.runner.rerun(decodeURIComponent(JSSpec.options.rerun));
> +			} else {
> +				JSSpec.runner.run();
> +			}
> +		} else {
> +			// in suite, send all specs to parent
> +			var parentWindow = window.frames.parent.window;
> +			for(var i = 0; i < JSSpec.specs.length; i++) {
> +				parentWindow.JSSpec.specs.push(JSSpec.specs[i]);
> +			}
> +		}
> +	} else {
> +		var links =  
> document.getElementById('list').getElementsByTagName('A');
> +		var frameContainer = document.createElement('DIV');
> +		frameContainer.style.display = 'none';
> +		document.body.appendChild(frameContainer);
> +		
> +		for(var i = 0; i < links.length; i++) {
> +			var frame = document.createElement('IFRAME');
> +			frame.src = links[i].href + '?inSuite=0&specIdBeginsWith=' + (i  
> * 10000) + '&exampleIdBeginsWith=' + (i * 10000);
> +			frameContainer.appendChild(frame);
> +		}
> +	}
> +}
> \ No newline at end of file
>
> Propchange: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
> JSSpec.js
> ------------------------------------------------------------------------------
>    svn:executable = *
>
> Added: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
> JSSpecSpecs.js
> URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpecSpecs.js?rev=787361&view=auto
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
> JSSpecSpecs.js (added)
> +++ incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
> JSSpecSpecs.js Mon Jun 22 19:23:30 2009
> @@ -0,0 +1,140 @@
> +/*
> +Script: JSSpecSpecs.js
> +	Failure Examples for JSSpec.js
> +
> +License:
> +	MIT-style license.
> +
> +Note:
> +	All examples should fail to demonstrate the usage and output of  
> all the availible types of specs in JSSpec.
> +*/
> +
> +describe('Should be', {
> +	'Array should be (Array) 1': function() {
> +		value_of(['ab','cd','ef']).should_be(['ab','bd','ef']);
> +	},
> +	'Array should be (Array) 2': function() {
> +		value_of(['a',2,'4',5]).should_be([1,2,[4,5,6],6,7]);
> +	},
> +	'Boolean should be (Boolean)': function() {
> +		value_of(true).should_be(false);
> +	},
> +	'Boolean should be false': function() {
> +		value_of(true).should_be_false();
> +	},
> +	'Boolean should be true': function() {
> +		value_of(false).should_be_true();
> +	},
> +	'Date should be (Date)': function() {
> +		value_of(new Date(1979, 3, 27)).should_be(new Date(1976, 7, 23));
> +	},
> +	'Null should be (String)': function() {
> +		value_of(null).should_be("Test");
> +	},
> +	'Null should be (undefined)': function() {
> +		value_of(null).should_be(undefined);
> +	},
> +	'Number should be (Number)': function() {
> +		value_of(1+2).should_be(4);
> +	},
> +	'Object should be (Object) 1': function() {
> +		var actual = {a:1, b:2};
> +		var expected = {a:1, b:2, d:3};
> +		value_of(actual).should_be(expected);
> +	},
> +	'Object should be (Object) 2': function() {
> +		var actual = {a:1, b:2, c:3, d:4};
> +		var expected = {a:1, b:2, c:3};
> +		value_of(actual).should_be(expected);
> +	},
> +	'Object should be (Object) 3': function() {
> +		var actual = {a:1, b:4, c:3};
> +		var expected = {a:1, b:2, c:3};
> +		value_of(actual).should_be(expected);
> +	},
> +	'String should be (String)': function() {
> +		value_of("Hello world").should_be("Good-bye world");
> +	},
> +	'String should be (undefined)': function() {
> +		value_of("Test").should_be(undefined);
> +	}
> +});
> +
> +describe('Should be empty', {
> +	'String should be empty': function() {
> +		value_of("").should_be_empty();
> +		value_of("Hello").should_be_empty();
> +	},
> +	'String should notbe empty': function() {
> +		value_of("Hello").should_not_be_empty();
> +		value_of("").should_not_be_empty();
> +	},
> +	'Array should be empty': function() {
> +		value_of([]).should_be_empty();
> +		value_of([1,2,3]).should_be_empty();
> +	},
> +	'Array should not be empty': function() {
> +		value_of([1,2,3]).should_not_be_empty();
> +		value_of([]).should_not_be_empty();
> +	},
> +	'Object\'s item should be empty': function() {
> +		value_of({name:'Alan Kang', email:'jania902@gmail.com', accounts: 
> ['A', 'B']}).should_have(0, "accounts");
> +	},
> +	'Object\'s item should not be empty': function() {
> +		value_of({name:'Alan Kang', email:'jania902@gmail.com', accounts: 
> []}).should_have(2, "accounts");
> +	}
> +});
> +
> +describe('Should have', {
> +	'Array should have (Number, "items")': function() {
> +		value_of([1,2,3]).should_have(4, "items");
> +	},
> +	'Array should have exactly (Number, "items")': function() {
> +		value_of([1,2,3]).should_have_exactly(2, "items");
> +	},
> +	'Array should have at least (Number, "items")': function() {
> +		value_of([1,2,3]).should_have_at_least(4, "items");
> +	},
> +	'Array should have at most (Number, "items")': function() {
> +		value_of([1,2,3]).should_have_at_most(2, "items");
> +	},
> +	'Object\'s item should have (Number, "[property]s")': function() {
> +		value_of({name:'Alan Kang', email:'jania902@gmail.com', accounts: 
> ['A', 'B']}).should_have(3, "accounts");
> +	},
> +	'String should have (Number, "characters")': function() {
> +		value_of("Hello").should_have(4, "characters");
> +	},
> +	'String should have (Number, "[No match]s")': function() {
> +		value_of("This is a string").should_have(5, "players");
> +	}
> +});
> +
> +describe('Should include', {
> +	'Array should include (mixed)': function() {
> +		value_of([1,2,3]).should_include(4);
> +	},
> +	'Array should not include (mixed)': function() {
> +		value_of([1,2,3]).should_not_include(2);
> +	},
> +	'Non-Array should include (mixed)': function() {
> +		value_of(new Date()).should_include(4);
> +	},
> +	'Non-Array should not include (mixed)': function() {
> +		value_of(new Date()).should_not_include(4);
> +	}
> +});
> +
> +describe('Should match', {
> +	'String should match (RegExp)': function() {
> +		value_of("Hello").should_match(/x/);
> +	},
> +	'String should not match (RegExp)': function() {
> +		value_of("Hello").should_not_match(/ell/);
> +	},
> +	'Array should match (RegExp)': function() {
> +		value_of([1,2,3]).should_match(/x/);
> +	},
> +	'Array should not match (RegExp)': function() {
> +		value_of([1,2,3]).should_not_match(/x/);
> +	}
> +});
> \ No newline at end of file
>
> Propchange: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
> JSSpecSpecs.js
> ------------------------------------------------------------------------------
>    svn:executable = *
>


Re: svn commit: r787361 [1/2] - in /incubator/jspwiki/trunk: ./ src/java/org/apache/wiki/ tests/JSSpec/ tests/JSSpec/Assets/ tests/JSSpec/Assets/Scripts/ tests/JSSpec/Assets/Styles/

Posted by Janne Jalkanen <ja...@ecyrd.com>.
OK, done.

/Janne

On 22 Jun 2009, at 22:36, Janne Jalkanen wrote:

>
> We can't have LGPL dependencies (forbidden by Apache rules), even  
> for tests. Please roll back.
>
> /Janne
>
>
> On 22 Jun 2009, at 22:23, brushed@apache.org wrote:
>
>> Author: brushed
>> Date: Mon Jun 22 19:23:30 2009
>> New Revision: 787361
>>
>> URL: http://svn.apache.org/viewvc?rev=787361&view=rev
>> Log:
>> v3.0.0-svn-133: added javascript test-suite based on JSSpec.
>>
>> Added:
>>   incubator/jspwiki/trunk/tests/JSSpec/
>>   incubator/jspwiki/trunk/tests/JSSpec/Assets/
>>   incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/
>>   incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/COPYING    
>> (with props)
>>   incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpec.js    
>> (with props)
>>   incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
>> JSSpecSpecs.js   (with props)
>>   incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
>> diff_match_patch.js   (with props)
>>   incubator/jspwiki/trunk/tests/JSSpec/Assets/Styles/
>>   incubator/jspwiki/trunk/tests/JSSpec/Assets/Styles/JSSpec- 
>> jspwiki.css
>>   incubator/jspwiki/trunk/tests/JSSpec/Assets/Styles/JSSpec.css    
>> (with props)
>>   incubator/jspwiki/trunk/tests/JSSpec/JSSpec-dialog.js
>>   incubator/jspwiki/trunk/tests/JSSpec/JSSpec-index.html
>>   incubator/jspwiki/trunk/tests/JSSpec/JSSpec-jspwiki-common.js
>>   incubator/jspwiki/trunk/tests/JSSpec/JSSpec-jspwiki-edit.js
>> Modified:
>>   incubator/jspwiki/trunk/ChangeLog
>>   incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java
>>
>> Modified: incubator/jspwiki/trunk/ChangeLog
>> URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/ChangeLog?rev=787361&r1=787360&r2=787361&view=diff
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> =====================================================================
>> --- incubator/jspwiki/trunk/ChangeLog (original)
>> +++ incubator/jspwiki/trunk/ChangeLog Mon Jun 22 19:23:30 2009
>> @@ -1,3 +1,12 @@
>> +2009-06-22 Dirk Frederickx <brushed AT apache DOT org>
>> +
>> +        * 3.0.0-svn-133
>> +
>> +        * Added javascript unit-tests based on JSSpec. Not all  
>> tests are running
>> +        yet and the test-suite definitely needs further expansions.
>> +        Just open tests/JSSpec/JSSpec-index.html in your browser  
>> to run the test-suite.
>> +
>> +
>> 2009-06-07 Janne Jalkanen <ja...@apache.org>
>>
>>        * 3.0.0-svn-132
>>
>> Modified: incubator/jspwiki/trunk/src/java/org/apache/wiki/ 
>> Release.java
>> URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java?rev=787361&r1=787360&r2=787361&view=diff
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> =====================================================================
>> --- incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java  
>> (original)
>> +++ incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java  
>> Mon Jun 22 19:23:30 2009
>> @@ -77,7 +77,7 @@
>>     *  <p>
>>     *  If the build identifier is empty, it is not added.
>>     */
>> -    public static final String     BUILD         = "132";
>> +    public static final String     BUILD         = "133";
>>
>>    /**
>>     *  This is the generic version string you should use
>>
>> Added: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/COPYING
>> URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/COPYING?rev=787361&view=auto
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> =====================================================================
>> --- incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/COPYING  
>> (added)
>> +++ incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/COPYING Mon  
>> Jun 22 19:23:30 2009
>> @@ -0,0 +1,459 @@
>> +		  GNU LESSER GENERAL PUBLIC LICENSE
>> +		       Version 2.1, February 1999
>> +
>> + Copyright (C) 1991, 1999 Free Software Foundation, Inc.
>> + 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
>> + Everyone is permitted to copy and distribute verbatim copies
>> + of this license document, but changing it is not allowed.
>> +
>> +[This is the first released version of the Lesser GPL.  It also  
>> counts
>> + as the successor of the GNU Library Public License, version 2,  
>> hence
>> + the version number 2.1.]
>> +
>> +			    Preamble
>> +
>> +  The licenses for most software are designed to take away your
>> +freedom to share and change it.  By contrast, the GNU General Public
>> +Licenses are intended to guarantee your freedom to share and change
>> +free software--to make sure the software is free for all its users.
>> +
>> +  This license, the Lesser General Public License, applies to some
>> +specially designated software packages--typically libraries--of the
>> +Free Software Foundation and other authors who decide to use it.   
>> You
>> +can use it too, but we suggest you first think carefully about  
>> whether
>> +this license or the ordinary General Public License is the better
>> +strategy to use in any particular case, based on the explanations  
>> below.
>> +
>> +  When we speak of free software, we are referring to freedom of  
>> use,
>> +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 this service if you wish); that you receive source code or can  
>> get
>> +it if you want it; that you can change the software and use pieces  
>> of
>> +it in new free programs; and that you are informed that you can do
>> +these things.
>> +
>> +  To protect your rights, we need to make restrictions that forbid
>> +distributors to deny you these rights or to ask you to surrender  
>> these
>> +rights.  These restrictions translate to certain responsibilities  
>> for
>> +you if you distribute copies of the library or if you modify it.
>> +
>> +  For example, if you distribute copies of the library, whether  
>> gratis
>> +or for a fee, you must give the recipients all the rights that we  
>> gave
>> +you.  You must make sure that they, too, receive or can get the  
>> source
>> +code.  If you link other code with the library, you must provide
>> +complete object files to the recipients, so that they can relink  
>> them
>> +with the library after making changes to the library and recompiling
>> +it.  And you must show them these terms so they know their rights.
>> +
>> +  We protect your rights with a two-step method: (1) we copyright  
>> the
>> +library, and (2) we offer you this license, which gives you legal
>> +permission to copy, distribute and/or modify the library.
>> +
>> +  To protect each distributor, we want to make it very clear that
>> +there is no warranty for the free library.  Also, if the library is
>> +modified by someone else and passed on, the recipients should know
>> +that what they have is not the original version, so that the  
>> original
>> +author's reputation will not be affected by problems that might be
>> +introduced by others.
>> +
>> +  Finally, software patents pose a constant threat to the  
>> existence of
>> +any free program.  We wish to make sure that a company cannot
>> +effectively restrict the users of a free program by obtaining a
>> +restrictive license from a patent holder.  Therefore, we insist that
>> +any patent license obtained for a version of the library must be
>> +consistent with the full freedom of use specified in this license.
>> +
>> +  Most GNU software, including some libraries, is covered by the
>> +ordinary GNU General Public License.  This license, the GNU Lesser
>> +General Public License, applies to certain designated libraries, and
>> +is quite different from the ordinary General Public License.  We use
>> +this license for certain libraries in order to permit linking those
>> +libraries into non-free programs.
>> +
>> +  When a program is linked with a library, whether statically or  
>> using
>> +a shared library, the combination of the two is legally speaking a
>> +combined work, a derivative of the original library.  The ordinary
>> +General Public License therefore permits such linking only if the
>> +entire combination fits its criteria of freedom.  The Lesser General
>> +Public License permits more lax criteria for linking other code with
>> +the library.
>> +
>> +  We call this license the "Lesser" General Public License because  
>> it
>> +does Less to protect the user's freedom than the ordinary General
>> +Public License.  It also provides other free software developers  
>> Less
>> +of an advantage over competing non-free programs.  These  
>> disadvantages
>> +are the reason we use the ordinary General Public License for many
>> +libraries.  However, the Lesser license provides advantages in  
>> certain
>> +special circumstances.
>> +
>> +  For example, on rare occasions, there may be a special need to
>> +encourage the widest possible use of a certain library, so that it  
>> becomes
>> +a de-facto standard.  To achieve this, non-free programs must be
>> +allowed to use the library.  A more frequent case is that a free
>> +library does the same job as widely used non-free libraries.  In  
>> this
>> +case, there is little to gain by limiting the free library to free
>> +software only, so we use the Lesser General Public License.
>> +
>> +  In other cases, permission to use a particular library in non-free
>> +programs enables a greater number of people to use a large body of
>> +free software.  For example, permission to use the GNU C Library in
>> +non-free programs enables many more people to use the whole GNU
>> +operating system, as well as its variant, the GNU/Linux operating
>> +system.
>> +
>> +  Although the Lesser General Public License is Less protective of  
>> the
>> +users' freedom, it does ensure that the user of a program that is
>> +linked with the Library has the freedom and the wherewithal to run
>> +that program using a modified version of the Library.
>> +
>> +  The precise terms and conditions for copying, distribution and
>> +modification follow.  Pay close attention to the difference  
>> between a
>> +"work based on the library" and a "work that uses the library".  The
>> +former contains code derived from the library, whereas the latter  
>> must
>> +be combined with the library in order to run.
>> +
>> +		  GNU LESSER GENERAL PUBLIC LICENSE
>> +   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
>> +
>> +  0. This License Agreement applies to any software library or other
>> +program which contains a notice placed by the copyright holder or
>> +other authorized party saying it may be distributed under the  
>> terms of
>> +this Lesser General Public License (also called "this License").
>> +Each licensee is addressed as "you".
>> +
>> +  A "library" means a collection of software functions and/or data
>> +prepared so as to be conveniently linked with application programs
>> +(which use some of those functions and data) to form executables.
>> +
>> +  The "Library", below, refers to any such software library or work
>> +which has been distributed under these terms.  A "work based on the
>> +Library" means either the Library or any derivative work under
>> +copyright law: that is to say, a work containing the Library or a
>> +portion of it, either verbatim or with modifications and/or  
>> translated
>> +straightforwardly into another language.  (Hereinafter,  
>> translation is
>> +included without limitation in the term "modification".)
>> +
>> +  "Source code" for a work means the preferred form of the work for
>> +making modifications to it.  For a library, complete source code  
>> means
>> +all the source code for all modules it contains, plus any associated
>> +interface definition files, plus the scripts used to control  
>> compilation
>> +and installation of the library.
>> +
>> +  Activities other than copying, distribution and modification are  
>> not
>> +covered by this License; they are outside its scope.  The act of
>> +running a program using the Library is not restricted, and output  
>> from
>> +such a program is covered only if its contents constitute a work  
>> based
>> +on the Library (independent of the use of the Library in a tool for
>> +writing it).  Whether that is true depends on what the Library does
>> +and what the program that uses the Library does.
>> +
>> +  1. You may copy and distribute verbatim copies of the Library's
>> +complete source code as you receive it, in any medium, provided that
>> +you conspicuously and appropriately publish on each copy an
>> +appropriate copyright notice and disclaimer of warranty; keep intact
>> +all the notices that refer to this License and to the absence of any
>> +warranty; and distribute a copy of this License along with the
>> +Library.
>> +
>> +  You may charge a fee for the physical act of transferring a copy,
>> +and you may at your option offer warranty protection in exchange  
>> for a
>> +fee.
>> +
>> +  2. You may modify your copy or copies of the Library or any  
>> portion
>> +of it, thus forming a work based on the Library, and copy and
>> +distribute such modifications or work under the terms of Section 1
>> +above, provided that you also meet all of these conditions:
>> +
>> +    a) The modified work must itself be a software library.
>> +
>> +    b) You must cause the files modified to carry prominent notices
>> +    stating that you changed the files and the date of any change.
>> +
>> +    c) You must cause the whole of the work to be licensed at no
>> +    charge to all third parties under the terms of this License.
>> +
>> +    d) If a facility in the modified Library refers to a function  
>> or a
>> +    table of data to be supplied by an application program that uses
>> +    the facility, other than as an argument passed when the facility
>> +    is invoked, then you must make a good faith effort to ensure  
>> that,
>> +    in the event an application does not supply such function or
>> +    table, the facility still operates, and performs whatever part  
>> of
>> +    its purpose remains meaningful.
>> +
>> +    (For example, a function in a library to compute square roots  
>> has
>> +    a purpose that is entirely well-defined independent of the
>> +    application.  Therefore, Subsection 2d requires that any
>> +    application-supplied function or table used by this function  
>> must
>> +    be optional: if the application does not supply it, the square
>> +    root function must still compute square roots.)
>> +
>> +These requirements apply to the modified work as a whole.  If
>> +identifiable sections of that work are not derived from the Library,
>> +and can be reasonably considered independent and separate works in
>> +themselves, then this License, and its terms, do not apply to those
>> +sections when you distribute them as separate works.  But when you
>> +distribute the same sections as part of a whole which is a work  
>> based
>> +on the Library, the distribution of the whole must be on the terms  
>> of
>> +this License, whose permissions for other licensees extend to the
>> +entire whole, and thus to each and every part regardless of who  
>> wrote
>> +it.
>> +
>> +Thus, it is not the intent of this section to claim rights or  
>> contest
>> +your rights to work written entirely by you; rather, the intent is  
>> to
>> +exercise the right to control the distribution of derivative or
>> +collective works based on the Library.
>> +
>> +In addition, mere aggregation of another work not based on the  
>> Library
>> +with the Library (or with a work based on the Library) on a volume  
>> of
>> +a storage or distribution medium does not bring the other work under
>> +the scope of this License.
>> +
>> +  3. You may opt to apply the terms of the ordinary GNU General  
>> Public
>> +License instead of this License to a given copy of the Library.   
>> To do
>> +this, you must alter all the notices that refer to this License, so
>> +that they refer to the ordinary GNU General Public License,  
>> version 2,
>> +instead of to this License.  (If a newer version than version 2 of  
>> the
>> +ordinary GNU General Public License has appeared, then you can  
>> specify
>> +that version instead if you wish.)  Do not make any other change in
>> +these notices.
>> +
>> +  Once this change is made in a given copy, it is irreversible for
>> +that copy, so the ordinary GNU General Public License applies to all
>> +subsequent copies and derivative works made from that copy.
>> +
>> +  This option is useful when you wish to copy part of the code of
>> +the Library into a program that is not a library.
>> +
>> +  4. You may copy and distribute the Library (or a portion or
>> +derivative of it, under Section 2) in object code or executable form
>> +under the terms of Sections 1 and 2 above provided that you  
>> accompany
>> +it with the complete corresponding machine-readable source code,  
>> which
>> +must be distributed under the terms of Sections 1 and 2 above on a
>> +medium customarily used for software interchange.
>> +
>> +  If distribution of object code is made by offering access to copy
>> +from a designated place, then offering equivalent access to copy the
>> +source code from the same place satisfies the requirement to
>> +distribute the source code, even though third parties are not
>> +compelled to copy the source along with the object code.
>> +
>> +  5. A program that contains no derivative of any portion of the
>> +Library, but is designed to work with the Library by being  
>> compiled or
>> +linked with it, is called a "work that uses the Library".  Such a
>> +work, in isolation, is not a derivative work of the Library, and
>> +therefore falls outside the scope of this License.
>> +
>> +  However, linking a "work that uses the Library" with the Library
>> +creates an executable that is a derivative of the Library (because  
>> it
>> +contains portions of the Library), rather than a "work that uses the
>> +library".  The executable is therefore covered by this License.
>> +Section 6 states terms for distribution of such executables.
>> +
>> +  When a "work that uses the Library" uses material from a header  
>> file
>> +that is part of the Library, the object code for the work may be a
>> +derivative work of the Library even though the source code is not.
>> +Whether this is true is especially significant if the work can be
>> +linked without the Library, or if the work is itself a library.  The
>> +threshold for this to be true is not precisely defined by law.
>> +
>> +  If such an object file uses only numerical parameters, data
>> +structure layouts and accessors, and small macros and small inline
>> +functions (ten lines or less in length), then the use of the object
>> +file is unrestricted, regardless of whether it is legally a  
>> derivative
>> +work.  (Executables containing this object code plus portions of the
>> +Library will still fall under Section 6.)
>> +
>> +  Otherwise, if the work is a derivative of the Library, you may
>> +distribute the object code for the work under the terms of Section  
>> 6.
>> +Any executables containing that work also fall under Section 6,
>> +whether or not they are linked directly with the Library itself.
>> +
>> +  6. As an exception to the Sections above, you may also combine or
>> +link a "work that uses the Library" with the Library to produce a
>> +work containing portions of the Library, and distribute that work
>> +under terms of your choice, provided that the terms permit
>> +modification of the work for the customer's own use and reverse
>> +engineering for debugging such modifications.
>> +
>> +  You must give prominent notice with each copy of the work that the
>> +Library is used in it and that the Library and its use are covered  
>> by
>> +this License.  You must supply a copy of this License.  If the work
>> +during execution displays copyright notices, you must include the
>> +copyright notice for the Library among them, as well as a reference
>> +directing the user to the copy of this License.  Also, you must do  
>> one
>> +of these things:
>> +
>> +    a) Accompany the work with the complete corresponding
>> +    machine-readable source code for the Library including whatever
>> +    changes were used in the work (which must be distributed under
>> +    Sections 1 and 2 above); and, if the work is an executable  
>> linked
>> +    with the Library, with the complete machine-readable "work that
>> +    uses the Library", as object code and/or source code, so that  
>> the
>> +    user can modify the Library and then relink to produce a  
>> modified
>> +    executable containing the modified Library.  (It is understood
>> +    that the user who changes the contents of definitions files in  
>> the
>> +    Library will not necessarily be able to recompile the  
>> application
>> +    to use the modified definitions.)
>> +
>> +    b) Use a suitable shared library mechanism for linking with the
>> +    Library.  A suitable mechanism is one that (1) uses at run  
>> time a
>> +    copy of the library already present on the user's computer  
>> system,
>> +    rather than copying library functions into the executable, and  
>> (2)
>> +    will operate properly with a modified version of the library, if
>> +    the user installs one, as long as the modified version is
>> +    interface-compatible with the version that the work was made  
>> with.
>> +
>> +    c) Accompany the work with a written offer, valid for at
>> +    least three years, to give the same user the materials
>> +    specified in Subsection 6a, above, for a charge no more
>> +    than the cost of performing this distribution.
>> +
>> +    d) If distribution of the work is made by offering access to  
>> copy
>> +    from a designated place, offer equivalent access to copy the  
>> above
>> +    specified materials from the same place.
>> +
>> +    e) Verify that the user has already received a copy of these
>> +    materials or that you have already sent this user a copy.
>> +
>> +  For an executable, the required form of the "work that uses the
>> +Library" must include any data and utility programs needed for
>> +reproducing the executable from it.  However, as a special  
>> exception,
>> +the materials to be distributed need not include anything that is
>> +normally distributed (in either source or binary form) with the  
>> major
>> +components (compiler, kernel, and so on) of the operating system on
>> +which the executable runs, unless that component itself accompanies
>> +the executable.
>> +
>> +  It may happen that this requirement contradicts the license
>> +restrictions of other proprietary libraries that do not normally
>> +accompany the operating system.  Such a contradiction means you  
>> cannot
>> +use both them and the Library together in an executable that you
>> +distribute.
>> +
>> +  7. You may place library facilities that are a work based on the
>> +Library side-by-side in a single library together with other library
>> +facilities not covered by this License, and distribute such a  
>> combined
>> +library, provided that the separate distribution of the work based  
>> on
>> +the Library and of the other library facilities is otherwise
>> +permitted, and provided that you do these two things:
>> +
>> +    a) Accompany the combined library with a copy of the same work
>> +    based on the Library, uncombined with any other library
>> +    facilities.  This must be distributed under the terms of the
>> +    Sections above.
>> +
>> +    b) Give prominent notice with the combined library of the fact
>> +    that part of it is a work based on the Library, and explaining
>> +    where to find the accompanying uncombined form of the same work.
>> +
>> +  8. You may not copy, modify, sublicense, link with, or distribute
>> +the Library except as expressly provided under this License.  Any
>> +attempt otherwise to copy, modify, sublicense, link with, or
>> +distribute the Library is void, and will automatically terminate  
>> your
>> +rights under this License.  However, parties who have received  
>> copies,
>> +or rights, from you under this License will not have their licenses
>> +terminated so long as such parties remain in full compliance.
>> +
>> +  9. You are not required to accept this License, since you have not
>> +signed it.  However, nothing else grants you permission to modify or
>> +distribute the Library or its derivative works.  These actions are
>> +prohibited by law if you do not accept this License.  Therefore, by
>> +modifying or distributing the Library (or any work based on the
>> +Library), you indicate your acceptance of this License to do so, and
>> +all its terms and conditions for copying, distributing or modifying
>> +the Library or works based on it.
>> +
>> +  10. Each time you redistribute the Library (or any work based on  
>> the
>> +Library), the recipient automatically receives a license from the
>> +original licensor to copy, distribute, link with or modify the  
>> Library
>> +subject to these terms and conditions.  You may not impose any  
>> further
>> +restrictions on the recipients' exercise of the rights granted  
>> herein.
>> +You are not responsible for enforcing compliance by third parties  
>> with
>> +this License.
>> +
>> +  11. If, as a consequence of a court judgment or allegation of  
>> patent
>> +infringement or for any other reason (not limited to patent issues),
>> +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
>> +distribute so as to satisfy simultaneously your obligations under  
>> this
>> +License and any other pertinent obligations, then as a consequence  
>> you
>> +may not distribute the Library at all.  For example, if a patent
>> +license would not permit royalty-free redistribution of the  
>> Library by
>> +all those who receive copies directly or indirectly through you,  
>> then
>> +the only way you could satisfy both it and this License would be to
>> +refrain entirely from distribution of the Library.
>> +
>> +If any portion of this section is held invalid or unenforceable  
>> under any
>> +particular circumstance, the balance of the section is intended to  
>> apply,
>> +and the section as a whole is intended to apply in other  
>> circumstances.
>> +
>> +It is not the purpose of this section to induce you to infringe any
>> +patents or other property right claims or to contest validity of any
>> +such claims; this section has the sole purpose of protecting the
>> +integrity of the free software distribution system which is
>> +implemented by public license practices.  Many people have made
>> +generous contributions to the wide range of software distributed
>> +through that system in reliance on consistent application of that
>> +system; it is up to the author/donor to decide if he or she is  
>> willing
>> +to distribute software through any other system and a licensee  
>> cannot
>> +impose that choice.
>> +
>> +This section is intended to make thoroughly clear what is believed  
>> to
>> +be a consequence of the rest of this License.
>> +
>> +  12. If the distribution and/or use of the Library is restricted in
>> +certain countries either by patents or by copyrighted interfaces,  
>> the
>> +original copyright holder who places the Library under this  
>> License may add
>> +an explicit geographical distribution limitation excluding those  
>> countries,
>> +so that distribution is permitted only in or among countries not  
>> thus
>> +excluded.  In such case, this License incorporates the limitation  
>> as if
>> +written in the body of this License.
>> +
>> +  13. The Free Software Foundation may publish revised and/or new
>> +versions of the Lesser 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  
>> Library
>> +specifies a version number of this License which applies to it and
>> +"any later version", you have the option of following the terms and
>> +conditions either of that version or of any later version  
>> published by
>> +the Free Software Foundation.  If the Library does not specify a
>> +license version number, you may choose any version ever published by
>> +the Free Software Foundation.
>> +
>> +  14. If you wish to incorporate parts of the Library into other  
>> free
>> +programs whose distribution conditions are incompatible with these,
>> +write to the author to ask for permission.  For software which is
>> +copyrighted by the Free Software Foundation, write to the Free
>> +Software Foundation; we sometimes make exceptions for this.  Our
>> +decision will be guided by the two goals of preserving the free  
>> status
>> +of all derivatives of our free software and of promoting the sharing
>> +and reuse of software generally.
>> +
>> +			    NO WARRANTY
>> +
>> +  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
>> +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
>> +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
>> +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" 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
>> +LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
>> +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
>> +
>> +  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
>> +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
>> +AND/OR REDISTRIBUTE THE LIBRARY 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
>> +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
>> +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF  
>> SUCH
>> +DAMAGES.
>> +
>> +		     END OF TERMS AND CONDITIONS
>> +
>>
>> Propchange: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
>> COPYING
>> ------------------------------------------------------------------------------
>>   svn:executable = *
>>
>> Added: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpec.js
>> URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpec.js?rev=787361&view=auto
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> =====================================================================
>> --- incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpec.js  
>> (added)
>> +++ incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpec.js  
>> Mon Jun 22 19:23:30 2009
>> @@ -0,0 +1,1552 @@
>> +/**
>> + * JSSpec
>> + *
>> + * Copyright 2007 Alan Kang
>> + *  - mailto:jania902@gmail.com
>> + *  - http://jania.pe.kr
>> + *
>> + * http://jania.pe.kr/aw/moin.cgi/JSSpec
>> + *
>> + * Dependencies:
>> + *  - diff_match_patch.js ( http://code.google.com/p/google-diff-match-patch 
>>  )
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later  
>> version.
>> + *
>> + * This library 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
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, write to the Free  
>> Software
>> + * Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA  
>> 02110-1301, USA
>> + */
>> +
>> +/**
>> + * Namespace
>> + */
>> +
>> +var JSSpec = {
>> +	specs: [],
>> +	
>> +	EMPTY_FUNCTION: function() {},
>> +	
>> +	Browser: {
>> +		// By Rendering Engines
>> +		Trident: navigator.appName === "Microsoft Internet Explorer",
>> +		Webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
>> +		Gecko: navigator.userAgent.indexOf('Gecko') > -1 &&  
>> navigator.userAgent.indexOf('KHTML') === -1,
>> +		KHTML: navigator.userAgent.indexOf('KHTML') !== -1,
>> +		Presto: navigator.appName === "Opera",
>> +		
>> +		// By Platforms
>> +		Mac: navigator.userAgent.indexOf("Macintosh") !== -1,
>> +		Ubuntu: navigator.userAgent.indexOf('Ubuntu') !== -1,
>> +		Win: navigator.userAgent.indexOf('Windows') !== -1,
>> +		
>> +		// By Browsers
>> +		IE: navigator.appName === "Microsoft Internet Explorer",
>> +		IE6: navigator.userAgent.indexOf('MSIE 6') !== -1,
>> +		IE7: navigator.userAgent.indexOf('MSIE 7') !== -1,
>> +		IE8: navigator.userAgent.indexOf('MSIE 8') !== -1,
>> +		
>> +		FF: navigator.userAgent.indexOf('Firefox') !== -1,
>> +		FF2: navigator.userAgent.indexOf('Firefox/2') !== -1,
>> +		FF3: navigator.userAgent.indexOf('Firefox/3') !== -1,
>> +		Safari: navigator.userAgent.indexOf('Safari') !== -1
>> +	}
>> +};
>> +
>> +
>> +
>> +/**
>> + * Executor
>> + */
>> +JSSpec.Executor = function(target, onSuccess, onException) {
>> +	this.target = target;
>> +	this.onSuccess = typeof onSuccess == 'function' ? onSuccess :  
>> JSSpec.EMPTY_FUNCTION;
>> +	this.onException = typeof onException == 'function' ?  
>> onException : JSSpec.EMPTY_FUNCTION;
>> +	
>> +	if(JSSpec.Browser.Trident) {
>> +		// Exception handler for Trident. It helps to collect exact line  
>> number where exception occured.
>> +		window.onerror = function(message, fileName, lineNumber) {
>> +			var self = window._curExecutor;
>> +			var ex = {message:message, fileName:fileName,  
>> lineNumber:lineNumber};
>> +
>> +			if(JSSpec._secondPass)  {
>> +				ex = self.mergeExceptions(JSSpec._assertionFailure, ex);
>> +				delete JSSpec._secondPass;
>> +				delete JSSpec._assertionFailure;
>> +				
>> +				ex.type = "failure";
>> +				self.onException(self, ex);
>> +			} else if(JSSpec._assertionFailure) {
>> +				JSSpec._secondPass = true;
>> +				self.run();
>> +			} else {
>> +				self.onException(self, ex);
>> +			}
>> +			
>> +			return true;
>> +		};
>> +	}
>> +};
>> +JSSpec.Executor.prototype.mergeExceptions =  
>> function(assertionFailure, normalException) {
>> +	var merged = {
>> +		message:assertionFailure.message,
>> +		fileName:normalException.fileName,
>> +		lineNumber:normalException.lineNumber
>> +	};
>> +	
>> +	return merged;
>> +};
>> +
>> +JSSpec.Executor.prototype.run = function() {
>> +	var self = this;
>> +	var target = this.target;
>> +	var onSuccess = this.onSuccess;
>> +	var onException = this.onException;
>> +	
>> +	window.setTimeout(
>> +		function() {
>> +			var result;
>> +			if(JSSpec.Browser.Trident) {
>> +				try{
>> +					window._curExecutor = self;
>> +					
>> +					result = self.target();
>> +					self.onSuccess(self, result);
>> +				}catch(ex){
>> +					self.onException(self, ex);
>> +				};
>> +			} else {
>> +				try {
>> +					result = self.target();
>> +					self.onSuccess(self, result);
>> +				} catch(ex) {
>> +					if(JSSpec.Browser.Webkit) ex = {message:ex.message,  
>> fileName:ex.sourceURL, lineNumber:ex.line};
>> +					
>> +					if(JSSpec._secondPass)  {
>> +						ex = self.mergeExceptions(JSSpec._assertionFailure, ex);
>> +						delete JSSpec._secondPass;
>> +						delete JSSpec._assertionFailure;
>> +						
>> +						ex.type = "failure";
>> +						self.onException(self, ex);
>> +					} else if(JSSpec._assertionFailure) {
>> +						JSSpec._secondPass = true;
>> +						self.run();
>> +					} else {
>> +						self.onException(self, ex);
>> +					}
>> +				}
>> +			}
>> +		},
>> +		0
>> +	);
>> +};
>> +
>> +
>> +
>> +/**
>> + * CompositeExecutor composites one or more executors and execute  
>> them sequencially.
>> + */
>> +JSSpec.CompositeExecutor = function(onSuccess, onException,  
>> continueOnException) {
>> +	this.queue = [];
>> +	this.onSuccess = typeof onSuccess == 'function' ? onSuccess :  
>> JSSpec.EMPTY_FUNCTION;
>> +	this.onException = typeof onException == 'function' ?  
>> onException : JSSpec.EMPTY_FUNCTION;
>> +	this.continueOnException = !!continueOnException;
>> +};
>> +
>> +JSSpec.CompositeExecutor.prototype.addFunction = function(func) {
>> +	this.addExecutor(new JSSpec.Executor(func));
>> +};
>> +
>> +JSSpec.CompositeExecutor.prototype.addExecutor =  
>> function(executor) {
>> +	var last = this.queue.length == 0 ? null :  
>> this.queue[this.queue.length - 1];
>> +	if(last) {
>> +		last.next = executor;
>> +	}
>> +	
>> +	executor.parent = this;
>> +	executor.onSuccessBackup = executor.onSuccess;
>> +	executor.onSuccess = function(result) {
>> +		this.onSuccessBackup(result);
>> +		if(this.next) {
>> +			this.next.run();
>> +		} else {
>> +			this.parent.onSuccess();
>> +		}
>> +	};
>> +	executor.onExceptionBackup = executor.onException;
>> +	executor.onException = function(executor, ex) {
>> +		this.onExceptionBackup(executor, ex);
>> +
>> +		if(this.parent.continueOnException) {
>> +			if(this.next) {
>> +				this.next.run();
>> +			} else {
>> +				this.parent.onSuccess();
>> +			}
>> +		} else {
>> +			this.parent.onException(executor, ex);
>> +		}
>> +	};
>> +
>> +	this.queue.push(executor);
>> +};
>> +
>> +JSSpec.CompositeExecutor.prototype.run = function() {
>> +	if(this.queue.length > 0) {
>> +		this.queue[0].run();
>> +	}
>> +};
>> +
>> +/**
>> + * Spec is a set of Examples in a specific context
>> + */
>> +JSSpec.Spec = function(context, entries) {
>> +	this.id = JSSpec.Spec.id++;
>> +	this.context = context;
>> +	this.url = location.href;
>> +	
>> +	this.filterEntriesByEmbeddedExpressions(entries);
>> +	this.extractOutSpecialEntries(entries);
>> +	this.examples = this.makeExamplesFromEntries(entries);
>> +	this.examplesMap = this.makeMapFromExamples(this.examples);
>> +};
>> +
>> +JSSpec.Spec.id = 0;
>> +JSSpec.Spec.prototype.getExamples = function() {
>> +	return this.examples;
>> +};
>> +
>> +JSSpec.Spec.prototype.hasException = function() {
>> +	return this.getTotalFailures() > 0 || this.getTotalErrors() > 0;
>> +};
>> +
>> +JSSpec.Spec.prototype.getTotalFailures = function() {
>> +	var examples = this.examples;
>> +	var failures = 0;
>> +	for(var i = 0; i < examples.length; i++) {
>> +		if(examples[i].isFailure()) failures++;
>> +	}
>> +	return failures;
>> +};
>> +
>> +JSSpec.Spec.prototype.getTotalErrors = function() {
>> +	var examples = this.examples;
>> +	var errors = 0;
>> +	for(var i = 0; i < examples.length; i++) {
>> +		if(examples[i].isError()) errors++;
>> +	}
>> +	return errors;
>> +};
>> +
>> +JSSpec.Spec.prototype.filterEntriesByEmbeddedExpressions =  
>> function(entries) {
>> +	var isTrue;
>> +	for(name in entries) if(entries.hasOwnProperty(name)) {
>> +		var m = name.match(/\[\[(.+)\]\]/);
>> +		if(m && m[1]) {
>> +			eval("isTrue = (" + m[1] + ")");
>> +			if(!isTrue) delete entries[name];
>> +		}
>> +	}
>> +};
>> +
>> +JSSpec.Spec.prototype.extractOutSpecialEntries = function(entries) {
>> +	this.beforeEach = JSSpec.EMPTY_FUNCTION;
>> +	this.beforeAll = JSSpec.EMPTY_FUNCTION;
>> +	this.afterEach = JSSpec.EMPTY_FUNCTION;
>> +	this.afterAll = JSSpec.EMPTY_FUNCTION;
>> +	
>> +	for(name in entries) if(entries.hasOwnProperty(name)) {
>> +		if(name == 'before' || name == 'before each' || name ==  
>> 'before_each') {
>> +			this.beforeEach = entries[name];
>> +		} else if(name == 'before all' || name == 'before_all') {
>> +			this.beforeAll = entries[name];
>> +		} else if(name == 'after' || name == 'after each' || name ==  
>> 'after_each') {
>> +			this.afterEach = entries[name];
>> +		} else if(name == 'after all' || name == 'after_all') {
>> +			this.afterAll = entries[name];
>> +		}
>> +	}
>> +	
>> +	delete entries['before'];
>> +	delete entries['before each'];
>> +	delete entries['before_each'];
>> +	delete entries['before all'];
>> +	delete entries['before_all'];
>> +	delete entries['after'];
>> +	delete entries['after each'];
>> +	delete entries['after_each'];
>> +	delete entries['after all'];
>> +	delete entries['after_all'];
>> +};
>> +
>> +JSSpec.Spec.prototype.makeExamplesFromEntries = function(entries) {
>> +	var examples = [];
>> +	for(name in entries) if(entries.hasOwnProperty(name)) {
>> +		examples.push(new JSSpec.Example(name, entries[name],  
>> this.beforeEach, this.afterEach));
>> +	}
>> +	return examples;
>> +};
>> +
>> +JSSpec.Spec.prototype.makeMapFromExamples = function(examples) {
>> +	var map = {};
>> +	for(var i = 0; i < examples.length; i++) {
>> +		var example = examples[i];
>> +		map[example.id] = examples[i];
>> +	}
>> +	return map;
>> +};
>> +
>> +JSSpec.Spec.prototype.getExampleById = function(id) {
>> +	return this.examplesMap[id];
>> +};
>> +
>> +JSSpec.Spec.prototype.getExecutor = function() {
>> +	var self = this;
>> +	var onException = function(executor, ex) {
>> +		self.exception = ex;
>> +	};
>> +	
>> +	var composite = new JSSpec.CompositeExecutor();
>> +	composite.addFunction(function() {JSSpec.log.onSpecStart(self);});
>> +	composite.addExecutor(new JSSpec.Executor(this.beforeAll, null,  
>> function(exec, ex) {
>> +		self.exception = ex;
>> +		JSSpec.log.onSpecEnd(self);
>> +	}));
>> +	
>> +	var exampleAndAfter = new JSSpec.CompositeExecutor(null,null,true);
>> +	for(var i = 0; i < this.examples.length; i++) {
>> +		exampleAndAfter.addExecutor(this.examples[i].getExecutor());
>> +	}
>> +	exampleAndAfter.addExecutor(new JSSpec.Executor(this.afterAll,  
>> null, onException));
>> +	exampleAndAfter.addFunction(function()  
>> {JSSpec.log.onSpecEnd(self);});
>> +	composite.addExecutor(exampleAndAfter);
>> +	
>> +	return composite;
>> +};
>> +
>> +/**
>> + * Example
>> + */
>> +JSSpec.Example = function(name, target, before, after) {
>> +	this.id = JSSpec.Example.id++;
>> +	this.name = name;
>> +	this.target = target;
>> +	this.before = before;
>> +	this.after = after;
>> +};
>> +
>> +JSSpec.Example.id = 0;
>> +JSSpec.Example.prototype.isFailure = function() {
>> +	return this.exception && this.exception.type == "failure";
>> +};
>> +
>> +JSSpec.Example.prototype.isError = function() {
>> +	return this.exception && !this.exception.type;
>> +};
>> +
>> +JSSpec.Example.prototype.getExecutor = function() {
>> +	var self = this;
>> +	var onException = function(executor, ex) {
>> +		self.exception = ex;
>> +	};
>> +	
>> +	var composite = new JSSpec.CompositeExecutor();
>> +	composite.addFunction(function()  
>> {JSSpec.log.onExampleStart(self);});
>> +	composite.addExecutor(new JSSpec.Executor(this.before, null,  
>> function(exec, ex) {
>> +		self.exception = ex;
>> +		JSSpec.log.onExampleEnd(self);
>> +	}));
>> +	
>> +	var targetAndAfter = new JSSpec.CompositeExecutor(null,null,true);
>> +	
>> +	targetAndAfter.addExecutor(new JSSpec.Executor(this.target, null,  
>> onException));
>> +	targetAndAfter.addExecutor(new JSSpec.Executor(this.after, null,  
>> onException));
>> +	targetAndAfter.addFunction(function()  
>> {JSSpec.log.onExampleEnd(self);});
>> +	
>> +	composite.addExecutor(targetAndAfter);
>> +	
>> +	return composite;
>> +};
>> +
>> +/**
>> + * Runner
>> + */
>> +JSSpec.Runner = function(specs, logger) {
>> +	JSSpec.log = logger;
>> +	
>> +	this.totalExamples = 0;
>> +	this.specs = [];
>> +	this.specsMap = {};
>> +	this.addAllSpecs(specs);
>> +};
>> +
>> +JSSpec.Runner.prototype.addAllSpecs = function(specs) {
>> +	for(var i = 0; i < specs.length; i++) {
>> +		this.addSpec(specs[i]);
>> +	}
>> +};
>> +
>> +JSSpec.Runner.prototype.addSpec = function(spec) {
>> +	this.specs.push(spec);
>> +	this.specsMap[spec.id] = spec;
>> +	this.totalExamples += spec.getExamples().length;
>> +};
>> +
>> +JSSpec.Runner.prototype.getSpecById = function(id) {
>> +	return this.specsMap[id];
>> +};
>> +
>> +JSSpec.Runner.prototype.getSpecByContext = function(context) {
>> +	for(var i = 0; i < this.specs.length; i++) {
>> +		if(this.specs[i].context == context) return this.specs[i];
>> +	}
>> +	return null;
>> +};
>> +
>> +JSSpec.Runner.prototype.getSpecs = function() {
>> +	return this.specs;
>> +};
>> +
>> +JSSpec.Runner.prototype.hasException = function() {
>> +	return this.getTotalFailures() > 0 || this.getTotalErrors() > 0;
>> +};
>> +
>> +JSSpec.Runner.prototype.getTotalFailures = function() {
>> +	var specs = this.specs;
>> +	var failures = 0;
>> +	for(var i = 0; i < specs.length; i++) {
>> +		failures += specs[i].getTotalFailures();
>> +	}
>> +	return failures;
>> +};
>> +
>> +JSSpec.Runner.prototype.getTotalErrors = function() {
>> +	var specs = this.specs;
>> +	var errors = 0;
>> +	for(var i = 0; i < specs.length; i++) {
>> +		errors += specs[i].getTotalErrors();
>> +	}
>> +	return errors;
>> +};
>> +
>> +
>> +JSSpec.Runner.prototype.run = function() {
>> +	JSSpec.log.onRunnerStart();
>> +	var executor = new JSSpec.CompositeExecutor(function()  
>> {JSSpec.log.onRunnerEnd()},null,true);
>> +	for(var i = 0; i < this.specs.length; i++) {
>> +		executor.addExecutor(this.specs[i].getExecutor());
>> +	}
>> +	executor.run();
>> +};
>> +
>> +
>> +JSSpec.Runner.prototype.rerun = function(context) {
>> +	JSSpec.runner = new  
>> JSSpec.Runner([this.getSpecByContext(context)], JSSpec.log);
>> +	JSSpec.runner.run();
>> +};
>> +
>> +/**
>> + * Logger
>> + */
>> +JSSpec.Logger = function() {
>> +	this.finishedExamples = 0;
>> +	this.startedAt = null;
>> +};
>> +
>> +JSSpec.Logger.prototype.onRunnerStart = function() {
>> +	this._title = document.title;
>> +
>> +	this.startedAt = new Date();
>> +	var container = document.getElementById('jsspec_container');
>> +	if(container) {
>> +		container.innerHTML = "";
>> +	} else {
>> +		container = document.createElement("DIV");
>> +		container.id = "jsspec_container";
>> +		document.body.appendChild(container);
>> +	}
>> +	
>> +	var title = document.createElement("DIV");
>> +	title.id = "title";
>> +	title.innerHTML = [
>> +		'<h1>JSSpec</h1>',
>> +		'<ul>',
>> +		JSSpec.options.rerun ? '<li>[<a href="?" title="rerun all  
>> specs">X</a>] ' +  
>> JSSpec.util.escapeTags(decodeURIComponent(JSSpec.options.rerun)) +  
>> '</li>' : '',
>> +		'	<li><span id="total_examples">' + JSSpec.runner.totalExamples  
>> + '</span> examples</li>',
>> +		'	<li><span id="total_failures">0</span> failures</li>',
>> +		'	<li><span id="total_errors">0</span> errors</li>',
>> +		'	<li><span id="progress">0</span>% done</li>',
>> +		'	<li><span id="total_elapsed">0</span> secs</li>',
>> +		'</ul>',
>> +		'<p><a href="http://jania.pe.kr/aw/moin.cgi/JSSpec">JSSpec  
>> homepage</a></p>',
>> +	].join("");
>> +	container.appendChild(title);
>> +
>> +	var list = document.createElement("DIV");
>> +	list.id = "list";
>> +	list.innerHTML = [
>> +		'<h2>List</h2>',
>> +		'<ul class="specs">',
>> +		function() {
>> +			var specs = JSSpec.runner.getSpecs();
>> +			var sb = [];
>> +			for(var i = 0; i < specs.length; i++) {
>> +				var spec = specs[i];
>> +				sb.push('<li id="spec_' + specs[i].id + '_list"><h3><a  
>> href="#spec_' + specs[i].id + '">' +  
>> JSSpec.util.escapeTags(specs[i].context) + '</a> [<a href="?rerun='  
>> + encodeURIComponent(specs[i].context) + '">rerun '+  
>> specs[i].examples.length + '</a>]</h3> </li>');
>> +			}
>> +			return sb.join("");
>> +		}(),
>> +		'</ul>'
>> +	].join("");
>> +	container.appendChild(list);
>> +	
>> +	var log = document.createElement("DIV");
>> +	log.id = "log";
>> +	log.innerHTML = [
>> +		'<h2>Log</h2>',
>> +		'<ul class="specs">',
>> +		function() {
>> +			var specs = JSSpec.runner.getSpecs();
>> +			var sb = [];
>> +			for(var i = 0; i < specs.length; i++) {
>> +				var spec = specs[i];
>> +				sb.push('	<li id="spec_' + specs[i].id + '">');
>> +				sb.push('		<h3>' + JSSpec.util.escapeTags(specs[i].context) +  
>> ' [<a href="?rerun=' + encodeURIComponent(specs[i].context) +  
>> '">rerun '+ specs[i].examples.length + '</a>]</h3>');
>> +				sb.push('		<ul id="spec_' + specs[i].id + '_examples"  
>> class="examples">');
>> +				for(var j = 0; j < spec.examples.length; j++) {
>> +					var example = spec.examples[j];
>> +					sb.push('			<li id="example_' + example.id + '">');
>> +					sb.push('				<h4>' + JSSpec.util.escapeTags(example.name) +  
>> '</h4>');
>> +					sb.push('				<pre class="examples- 
>> code"><code>'+JSSpec.util.escapeTags(example.target.toString())+'</ 
>> code></pre>');
>> +					sb.push('			</li>');
>> +				}
>> +				sb.push('		</ul>');
>> +				sb.push('	</li>');
>> +			}
>> +			return sb.join("");
>> +		}(),
>> +		'</ul>'
>> +	].join("");
>> +	
>> +	container.appendChild(log);
>> +	
>> +	// add event handler for toggling
>> +	var specs = JSSpec.runner.getSpecs();
>> +	var sb = [];
>> +	for(var i = 0; i < specs.length; i++) {
>> +		var spec = document.getElementById("spec_" + specs[i].id);
>> +		var title = spec.getElementsByTagName("H3")[0];
>> +		title.onclick = function(e) {
>> +			var target = document.getElementById(this.parentNode.id +  
>> "_examples");
>> +			target.style.display = target.style.display == "none" ?  
>> "block" : "none";
>> +			return true;
>> +		}
>> +	}
>> +};
>> +
>> +JSSpec.Logger.prototype.onRunnerEnd = function() {
>> +	if(JSSpec.runner.hasException()) {
>> +		var times = 4;
>> +		var title1 = "*" + this._title;
>> +		var title2 = "*F" + JSSpec.runner.getTotalFailures() + " E" +  
>> JSSpec.runner.getTotalErrors() + "* " + this._title;
>> +	} else {
>> +		var times = 2;
>> +		var title1 = this._title;
>> +		var title2 = "Success";
>> +	}
>> +	this.blinkTitle(times,title1,title2);
>> +};
>> +
>> +JSSpec.Logger.prototype.blinkTitle = function(times, title1,  
>> title2) {
>> +	var times = times * 2;
>> +	var mode = true;
>> +	
>> +	var f = function() {
>> +		if(times > 0) {
>> +			document.title = mode ? title1 : title2;
>> +			mode = !mode;
>> +			times--;
>> +			window.setTimeout(f, 500);
>> +		} else {
>> +			document.title = title1;
>> +		}
>> +	};
>> +	
>> +	f();
>> +};
>> +
>> +JSSpec.Logger.prototype.onSpecStart = function(spec) {
>> +	var spec_list = document.getElementById("spec_" + spec.id +  
>> "_list");
>> +	var spec_log = document.getElementById("spec_" + spec.id);
>> +	
>> +	spec_list.className = "ongoing";
>> +	spec_log.className = "ongoing";
>> +};
>> +
>> +JSSpec.Logger.prototype.onSpecEnd = function(spec) {
>> +	var spec_list = document.getElementById("spec_" + spec.id +  
>> "_list");
>> +	var spec_log = document.getElementById("spec_" + spec.id);
>> +	var examples = document.getElementById("spec_" + spec.id +  
>> "_examples");
>> +	var className = spec.hasException() ? "exception" : "success";
>> +
>> +	spec_list.className = className;
>> +	spec_log.className = className;
>> +
>> +	if(JSSpec.options.autocollapse && !spec.hasException())  
>> examples.style.display = "none";
>> +	
>> +	if(spec.exception) {
>> +		spec_log.appendChild(document.createTextNode(" - " +  
>> spec.exception.message));
>> +	}
>> +};
>> +
>> +JSSpec.Logger.prototype.onExampleStart = function(example) {
>> +	var li = document.getElementById("example_" + example.id);
>> +	li.className = "ongoing";
>> +};
>> +
>> +JSSpec.Logger.prototype.onExampleEnd = function(example) {
>> +	var li = document.getElementById("example_" + example.id);
>> +	li.className = example.exception ? "exception" : "success";
>> +	
>> +	if(example.exception) {
>> +		var div = document.createElement("DIV");
>> +		div.innerHTML = example.exception.message + "<p><br />" + " at "  
>> + example.exception.fileName + ", line " +  
>> example.exception.lineNumber + "</p>";
>> +		li.appendChild(div);
>> +	}
>> +	
>> +	var title = document.getElementById("title");
>> +	var runner = JSSpec.runner;
>> +	
>> +	title.className = runner.hasException() ? "exception" : "success";
>> +	
>> +	this.finishedExamples++;
>> +	document.getElementById("total_failures").innerHTML =  
>> runner.getTotalFailures();
>> +	document.getElementById("total_errors").innerHTML =  
>> runner.getTotalErrors();
>> +	var progress = parseInt(this.finishedExamples /  
>> runner.totalExamples * 100);
>> +	document.getElementById("progress").innerHTML = progress;
>> +	document.getElementById("total_elapsed").innerHTML = (new  
>> Date().getTime() - this.startedAt.getTime()) / 1000;
>> +	
>> +	document.title = progress + "%: " + this._title;
>> +};
>> +
>> +/**
>> + * IncludeMatcher
>> + */
>> +JSSpec.IncludeMatcher = function(actual, expected, condition) {
>> +	this.actual = actual;
>> +	this.expected = expected;
>> +	this.condition = condition;
>> +	this.match = false;
>> +	this.explaination = this.makeExplain();
>> +};
>> +
>> +JSSpec.IncludeMatcher.createInstance = function(actual, expected,  
>> condition) {
>> +	return new JSSpec.IncludeMatcher(actual, expected, condition);
>> +};
>> +
>> +JSSpec.IncludeMatcher.prototype.matches = function() {
>> +	return this.match;
>> +};
>> +
>> +JSSpec.IncludeMatcher.prototype.explain = function() {
>> +	return this.explaination;
>> +};
>> +
>> +JSSpec.IncludeMatcher.prototype.makeExplain = function() {
>> +	if(typeof this.actual.length == 'undefined') {
>> +		return this.makeExplainForNotArray();
>> +	} else {
>> +		return this.makeExplainForArray();
>> +	}
>> +};
>> +
>> +JSSpec.IncludeMatcher.prototype.makeExplainForNotArray =  
>> function() {
>> +	if(this.condition) {
>> +		this.match = !!this.actual[this.expected];
>> +	} else {
>> +		this.match = !this.actual[this.expected];
>> +	}
>> +	
>> +	var sb = [];
>> +	sb.push('<p>actual value:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.actual, false, this.expected) + '</p>');
>> +	sb.push('<p>should ' + (this.condition ? '' : 'not') + '  
>> include:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.expected) + '</p>');
>> +	return sb.join("");
>> +};
>> +
>> +JSSpec.IncludeMatcher.prototype.makeExplainForArray = function() {
>> +	var matches;
>> +	if(this.condition) {
>> +		for(var i = 0; i < this.actual.length; i++) {
>> +			matches = JSSpec.EqualityMatcher.createInstance(this.expected,  
>> this.actual[i]).matches();
>> +			if(matches) {
>> +				this.match = true;
>> +				break;
>> +			}
>> +		}
>> +	} else {
>> +		for(var i = 0; i < this.actual.length; i++) {
>> +			matches = JSSpec.EqualityMatcher.createInstance(this.expected,  
>> this.actual[i]).matches();
>> +			if(matches) {
>> +				this.match = false;
>> +				break;
>> +			}
>> +		}
>> +	}
>> +	
>> +	if(this.match) return "";
>> +	
>> +	var sb = [];
>> +	sb.push('<p>actual value:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.actual, false, this.condition ? null : i)  
>> + '</p>');
>> +	sb.push('<p>should ' + (this.condition ? '' : 'not') + '  
>> include:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.expected) + '</p>');
>> +	return sb.join("");
>> +};
>> +
>> +/**
>> + * PropertyLengthMatcher
>> + */
>> +JSSpec.PropertyLengthMatcher = function(num, property, o,  
>> condition) {
>> +	this.num = num;
>> +	this.o = o;
>> +	this.property = property;
>> +	if((property == 'characters' || property == 'items') && typeof  
>> o.length != 'undefined') {
>> +		this.property = 'length';
>> +	}
>> +	
>> +	this.condition = condition;
>> +	this.conditionMet = function(x) {
>> +		if(condition == 'exactly') return x.length == num;
>> +		if(condition == 'at least') return x.length >= num;
>> +		if(condition == 'at most') return x.length <= num;
>> +
>> +		throw "Unknown condition '" + condition + "'";
>> +	};
>> +	this.match = false;
>> +	this.explaination = this.makeExplain();
>> +};
>> +
>> +JSSpec.PropertyLengthMatcher.prototype.makeExplain = function() {
>> +	if(this.o._type == 'String' && this.property == 'length') {
>> +		this.match = this.conditionMet(this.o);
>> +		return this.match ? '' : this.makeExplainForString();
>> +	} else if(typeof this.o.length != 'undefined' && this.property ==  
>> "length") {
>> +		this.match = this.conditionMet(this.o);
>> +		return this.match ? '' : this.makeExplainForArray();
>> +	} else if(typeof this.o[this.property] != 'undefined' &&  
>> this.o[this.property] != null) {
>> +		this.match = this.conditionMet(this.o[this.property]);
>> +		return this.match ? '' : this.makeExplainForObject();
>> +	} else if(typeof this.o[this.property] == 'undefined' ||  
>> this.o[this.property] == null) {
>> +		this.match = false;
>> +		return this.makeExplainForNoProperty();
>> +	}
>> +
>> +	this.match = true;
>> +};
>> +
>> +JSSpec.PropertyLengthMatcher.prototype.makeExplainForString =  
>> function() {
>> +	var sb = [];
>> +	
>> +	var exp = this.num == 0 ?
>> +		'be an <strong>empty string</strong>' :
>> +		'have <strong>' + this.condition + ' ' + this.num + '  
>> characters</strong>';
>> +	
>> +	sb.push('<p>actual value has <strong>' + this.o.length + '  
>> characters</strong>:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.o) + '</p>');
>> +	sb.push('<p>but it should ' + exp + '.</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +JSSpec.PropertyLengthMatcher.prototype.makeExplainForArray =  
>> function() {
>> +	var sb = [];
>> +	
>> +	var exp = this.num == 0 ?
>> +		'be an <strong>empty array</strong>' :
>> +		'have <strong>' + this.condition + ' ' + this.num + ' items</ 
>> strong>';
>> +
>> +	sb.push('<p>actual value has <strong>' + this.o.length + ' items</ 
>> strong>:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.o) + '</p>');
>> +	sb.push('<p>but it should ' + exp + '.</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +JSSpec.PropertyLengthMatcher.prototype.makeExplainForObject =  
>> function() {
>> +	var sb = [];
>> +
>> +	var exp = this.num == 0 ?
>> +		'be <strong>empty</strong>' :
>> +		'have <strong>' + this.condition + ' ' + this.num + ' ' +  
>> this.property + '.</strong>';
>> +
>> +	sb.push('<p>actual value has <strong>' +  
>> this.o[this.property].length + ' ' + this.property + '</strong>:</ 
>> p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.o, false, this.property) + '</p>');
>> +	sb.push('<p>but it should ' + exp + '.</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +JSSpec.PropertyLengthMatcher.prototype.makeExplainForNoProperty =  
>> function() {
>> +	var sb = [];
>> +	
>> +	sb.push('<p>actual value:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.o) + '</p>');
>> +	sb.push('<p>should have <strong>' + this.condition + ' ' +  
>> this.num + ' ' + this.property + '</strong> but there\'s no such  
>> property.</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +JSSpec.PropertyLengthMatcher.prototype.matches = function() {
>> +	return this.match;
>> +};
>> +
>> +JSSpec.PropertyLengthMatcher.prototype.explain = function() {
>> +	return this.explaination;
>> +};
>> +
>> +JSSpec.PropertyLengthMatcher.createInstance = function(num,  
>> property, o, condition) {
>> +	return new JSSpec.PropertyLengthMatcher(num, property, o,  
>> condition);
>> +};
>> +
>> +/**
>> + * EqualityMatcher
>> + */
>> +JSSpec.EqualityMatcher = {};
>> +
>> +JSSpec.EqualityMatcher.createInstance = function(expected, actual) {
>> +	if(expected == null || actual == null) {
>> +		return new JSSpec.NullEqualityMatcher(expected, actual);
>> +	} else if(expected._type && expected._type == actual._type) {
>> +		if(expected._type == "String") {
>> +			return new JSSpec.StringEqualityMatcher(expected, actual);
>> +		} else if(expected._type == "Date") {
>> +			return new JSSpec.DateEqualityMatcher(expected, actual);
>> +		} else if(expected._type == "Number") {
>> +			return new JSSpec.NumberEqualityMatcher(expected, actual);
>> +		} else if(expected._type == "Array") {
>> +			return new JSSpec.ArrayEqualityMatcher(expected, actual);
>> +		} else if(expected._type == "Boolean") {
>> +			return new JSSpec.BooleanEqualityMatcher(expected, actual);
>> +		}
>> +	}
>> +	
>> +	return new JSSpec.ObjectEqualityMatcher(expected, actual);
>> +};
>> +
>> +JSSpec.EqualityMatcher.basicExplain = function(expected, actual,  
>> expectedDesc, actualDesc) {
>> +	var sb = [];
>> +	
>> +	sb.push(actualDesc || '<p>actual value:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(actual) + '</p>');
>> +	sb.push(expectedDesc || '<p>should be:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(expected) + '</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +JSSpec.EqualityMatcher.diffExplain = function(expected, actual) {
>> +	var sb = [];
>> +
>> +	sb.push('<p>diff:</p>');
>> +	sb.push('<p style="margin-left:2em;">');
>> +	
>> +	var dmp = new diff_match_patch();
>> +	var diff = dmp.diff_main(expected, actual);
>> +	dmp.diff_cleanupEfficiency(diff);
>> +	
>> +	sb.push(JSSpec.util.inspect(dmp.diff_prettyHtml(diff), true));
>> +	
>> +	sb.push('</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +/**
>> + * BooleanEqualityMatcher
>> + */
>> +JSSpec.BooleanEqualityMatcher = function(expected, actual) {
>> +	this.expected = expected;
>> +	this.actual = actual;
>> +};
>> +
>> +JSSpec.BooleanEqualityMatcher.prototype.explain = function() {
>> +	var sb = [];
>> +	
>> +	sb.push('<p>actual value:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.actual) + '</p>');
>> +	sb.push('<p>should be:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.expected) + '</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +JSSpec.BooleanEqualityMatcher.prototype.matches = function() {
>> +	return this.expected == this.actual;
>> +};
>> +
>> +/**
>> + * NullEqualityMatcher
>> + */
>> +JSSpec.NullEqualityMatcher = function(expected, actual) {
>> +	this.expected = expected;
>> +	this.actual = actual;
>> +};
>> +
>> +JSSpec.NullEqualityMatcher.prototype.matches = function() {
>> +	return this.expected == this.actual && typeof this.expected ==  
>> typeof this.actual;
>> +};
>> +
>> +JSSpec.NullEqualityMatcher.prototype.explain = function() {
>> +	return JSSpec.EqualityMatcher.basicExplain(this.expected,  
>> this.actual);
>> +};
>> +
>> +JSSpec.DateEqualityMatcher = function(expected, actual) {
>> +	this.expected = expected;
>> +	this.actual = actual;
>> +};
>> +
>> +JSSpec.DateEqualityMatcher.prototype.matches = function() {
>> +	return this.expected.getTime() == this.actual.getTime();
>> +};
>> +
>> +JSSpec.DateEqualityMatcher.prototype.explain = function() {
>> +	var sb = [];
>> +	
>> +	sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected,  
>> this.actual));
>> +	 
>> sb 
>> .push(JSSpec.EqualityMatcher.diffExplain(this.expected.toString(),  
>> this.actual.toString()));
>> +
>> +	return sb.join("");
>> +};
>> +
>> +/**
>> + * ObjectEqualityMatcher
>> + */
>> +JSSpec.ObjectEqualityMatcher = function(expected, actual) {
>> +	this.expected = expected;
>> +	this.actual = actual;
>> +	this.match = this.expected == this.actual;
>> +	this.explaination = this.makeExplain();
>> +};
>> +
>> +JSSpec.ObjectEqualityMatcher.prototype.matches = function()  
>> {return this.match};
>> +
>> +JSSpec.ObjectEqualityMatcher.prototype.explain = function()  
>> {return this.explaination};
>> +
>> +JSSpec.ObjectEqualityMatcher.prototype.makeExplain = function() {
>> +	if(this.expected == this.actual) {
>> +		this.match = true;
>> +		return "";
>> +	}
>> +	
>> +	if(JSSpec.util.isDomNode(this.expected)) {
>> +		return this.makeExplainForDomNode();
>> +	}
>> +	
>> +	var key, expectedHasItem, actualHasItem;
>> +
>> +	for(key in this.expected) {
>> +		expectedHasItem = this.expected[key] != null && typeof  
>> this.expected[key] != 'undefined';
>> +		actualHasItem = this.actual[key] != null && typeof  
>> this.actual[key] != 'undefined';
>> +		if(expectedHasItem && !actualHasItem) return  
>> this.makeExplainForMissingItem(key);
>> +	}
>> +	for(key in this.actual) {
>> +		expectedHasItem = this.expected[key] != null && typeof  
>> this.expected[key] != 'undefined';
>> +		actualHasItem = this.actual[key] != null && typeof  
>> this.actual[key] != 'undefined';
>> +		if(actualHasItem && !expectedHasItem) return  
>> this.makeExplainForUnknownItem(key);
>> +	}
>> +	
>> +	for(key in this.expected) {
>> +		var matcher =  
>> JSSpec.EqualityMatcher.createInstance(this.expected[key],  
>> this.actual[key]);
>> +		if(!matcher.matches()) return  
>> this.makeExplainForItemMismatch(key);
>> +	}
>> +		
>> +	this.match = true;
>> +};
>> +
>> +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForDomNode =  
>> function(key) {
>> +	var sb = [];
>> +	
>> +	sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected,  
>> this.actual));
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForMissingItem =  
>> function(key) {
>> +	var sb = [];
>> +
>> +	sb.push('<p>actual value has no item named <strong>' +  
>> JSSpec.util.inspect(key) + '</strong></p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.actual, false, key) + '</p>');
>> +	sb.push('<p>but it should have the item whose value is <strong>'  
>> + JSSpec.util.inspect(this.expected[key]) + '</strong></p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.expected, false, key) + '</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForUnknownItem =  
>> function(key) {
>> +	var sb = [];
>> +
>> +	sb.push('<p>actual value has item named <strong>' +  
>> JSSpec.util.inspect(key) + '</strong></p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.actual, false, key) + '</p>');
>> +	sb.push('<p>but there should be no such item</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.expected, false, key) + '</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +JSSpec.ObjectEqualityMatcher.prototype.makeExplainForItemMismatch  
>> = function(key) {
>> +	var sb = [];
>> +
>> +	sb.push('<p>actual value has an item named <strong>' +  
>> JSSpec.util.inspect(key) + '</strong> whose value is <strong>' +  
>> JSSpec.util.inspect(this.actual[key]) + '</strong></p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.actual, false, key) + '</p>');
>> +	sb.push('<p>but it\'s value should be <strong>' +  
>> JSSpec.util.inspect(this.expected[key]) + '</strong></p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.expected, false, key) + '</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +
>> +
>> +
>> +/**
>> + * ArrayEqualityMatcher
>> + */
>> +JSSpec.ArrayEqualityMatcher = function(expected, actual) {
>> +	this.expected = expected;
>> +	this.actual = actual;
>> +	this.match = this.expected == this.actual;
>> +	this.explaination = this.makeExplain();
>> +};
>> +
>> +JSSpec.ArrayEqualityMatcher.prototype.matches = function() {return  
>> this.match};
>> +
>> +JSSpec.ArrayEqualityMatcher.prototype.explain = function() {return  
>> this.explaination};
>> +
>> +JSSpec.ArrayEqualityMatcher.prototype.makeExplain = function() {
>> +	if(this.expected.length != this.actual.length) return  
>> this.makeExplainForLengthMismatch();
>> +	
>> +	for(var i = 0; i < this.expected.length; i++) {
>> +		var matcher =  
>> JSSpec.EqualityMatcher.createInstance(this.expected[i],  
>> this.actual[i]);
>> +		if(!matcher.matches()) return this.makeExplainForItemMismatch(i);
>> +	}
>> +		
>> +	this.match = true;
>> +};
>> +
>> +JSSpec.ArrayEqualityMatcher.prototype.makeExplainForLengthMismatch  
>> = function() {
>> +	return JSSpec.EqualityMatcher.basicExplain(
>> +		this.expected,
>> +		this.actual,
>> +		'<p>but it should be <strong>' + this.expected.length + '</ 
>> strong></p>',
>> +		'<p>actual value has <strong>' + this.actual.length + '</strong>  
>> items</p>'
>> +	);
>> +};
>> +
>> +JSSpec.ArrayEqualityMatcher.prototype.makeExplainForItemMismatch =  
>> function(index) {
>> +	var postfix = ["th", "st", "nd", "rd", "th"][Math.min((index + 1)  
>> % 10,4)];
>> +	
>> +	var sb = [];
>> +
>> +	sb.push('<p>' + (index + 1) + postfix + ' item (index ' + index +  
>> ') of actual value is <strong>' +  
>> JSSpec.util.inspect(this.actual[index]) + '</strong>:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.actual, false, index) + '</p>');
>> +	sb.push('<p>but it should be <strong>' +  
>> JSSpec.util.inspect(this.expected[index]) + '</strong>:</p>');
>> +	sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.expected, false, index) + '</p>');
>> +	
>> +	return sb.join("");
>> +};
>> +
>> +/**
>> + * NumberEqualityMatcher
>> + */
>> +JSSpec.NumberEqualityMatcher = function(expected, actual) {
>> +	this.expected = expected;
>> +	this.actual = actual;
>> +};
>> +
>> +JSSpec.NumberEqualityMatcher.prototype.matches = function() {
>> +	if(this.expected == this.actual) return true;
>> +};
>> +
>> +JSSpec.NumberEqualityMatcher.prototype.explain = function() {
>> +	return JSSpec.EqualityMatcher.basicExplain(this.expected,  
>> this.actual);
>> +};
>> +
>> +/**
>> + * StringEqualityMatcher
>> + */
>> +JSSpec.StringEqualityMatcher = function(expected, actual) {
>> +	this.expected = expected;
>> +	this.actual = actual;
>> +};
>> +
>> +JSSpec.StringEqualityMatcher.prototype.matches = function() {
>> +	return this.expected == this.actual;
>> +};
>> +
>> +JSSpec.StringEqualityMatcher.prototype.explain = function() {
>> +	var sb = [];
>> +
>> +	sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected,  
>> this.actual));
>> +	sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected,  
>> this.actual));	
>> +	return sb.join("");
>> +};
>> +
>> +/**
>> + * PatternMatcher
>> + */
>> +JSSpec.PatternMatcher = function(actual, pattern, condition) {
>> +	this.actual = actual;
>> +	this.pattern = pattern;
>> +	this.condition = condition;
>> +	this.match = false;
>> +	this.explaination = this.makeExplain();
>> +};
>> +
>> +JSSpec.PatternMatcher.createInstance = function(actual, pattern,  
>> condition) {
>> +	return new JSSpec.PatternMatcher(actual, pattern, condition);
>> +};
>> +
>> +JSSpec.PatternMatcher.prototype.makeExplain = function() {
>> +	var sb;
>> +	if(this.actual == null || this.actual._type != 'String') {
>> +		sb = [];
>> +		sb.push('<p>actual value:</p>');
>> +		sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.actual) + '</p>');
>> +		sb.push('<p>should ' + (this.condition ? '' : 'not') + ' match  
>> with pattern:</p>');
>> +		sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.pattern) + '</p>');
>> +		sb.push('<p>but pattern matching cannot be performed.</p>');
>> +		return sb.join("");
>> +	} else {
>> +		this.match = this.condition == !!this.actual.match(this.pattern);
>> +		if(this.match) return "";
>> +		
>> +		sb = [];
>> +		sb.push('<p>actual value:</p>');
>> +		sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.actual) + '</p>');
>> +		sb.push('<p>should ' + (this.condition ? '' : 'not') + ' match  
>> with pattern:</p>');
>> +		sb.push('<p style="margin-left:2em;">' +  
>> JSSpec.util.inspect(this.pattern) + '</p>');
>> +		return sb.join("");
>> +	}
>> +};
>> +
>> +JSSpec.PatternMatcher.prototype.matches = function() {
>> +	return this.match;
>> +};
>> +
>> +JSSpec.PatternMatcher.prototype.explain = function() {
>> +	return this.explaination;
>> +};
>> +
>> +/**
>> + * Domain Specific Languages
>> + */
>> +JSSpec.DSL = {};
>> +
>> +JSSpec.DSL.forString = {
>> +	normalizeHtml: function() {
>> +		var html = this;
>> +		
>> +		// Uniformize quotation, turn tag names and attribute names into  
>> lower case
>> +		html = html.replace(/<(\/?)(\w+)([^>]*?)>/img, function(str,  
>> closingMark, tagName, attrs) {
>> +			var sortedAttrs =  
>> JSSpec 
>> .util 
>> .sortHtmlAttrs 
>> (JSSpec.util.correctHtmlAttrQuotation(attrs).toLowerCase())
>> +			return "<" + closingMark + tagName.toLowerCase() + sortedAttrs  
>> + ">"
>> +		});
>> +		
>> +		// validation self-closing tags
>> +		html = html.replace(/<(br|hr|img)([^>]*?)>/mg, function(str,  
>> tag, attrs) {
>> +			return "<" + tag + attrs + " />";
>> +		});
>> +		
>> +		// append semi-colon at the end of style value
>> +		html = html.replace(/style="(.*?)"/mg, function(str, styleStr) {
>> +			styleStr = JSSpec.util.sortStyleEntries(styleStr.strip()); //  
>> for Safari
>> +			if(styleStr.charAt(styleStr.length - 1) != ';') styleStr += ";"
>> +			
>> +			return 'style="' + styleStr + '"'
>> +		});
>> +		
>> +		// sort style entries
>> +		
>> +		// remove empty style attributes
>> +		html = html.replace(/ style=";"/mg, "");
>> +		
>> +		// remove new-lines
>> +		html = html.replace(/\r/mg, '');
>> +		html = html.replace(/\n/mg, '');
>> +			
>> +		return html;
>> +	}
>> +};
>> +
>> +
>> +JSSpec.DSL.describe = function(context, entries, base) {
>> +	if(base) {
>> +		for(var i = 0; i < JSSpec.specs.length; i++) {
>> +			if(JSSpec.specs[i].context === base) {
>> +				base = JSSpec.specs[i];
>> +				break;
>> +			}
>> +		}
>> +		
>> +		for(var i = 0; i < base.examples.length; i++) {
>> +			var example = base.examples[i];
>> +			
>> +			if(!entries[example.name]) entries[example.name] =  
>> example.target;
>> +		}
>> +	}
>> +	
>> +	JSSpec.specs.push(new JSSpec.Spec(context, entries));
>> +};
>> +
>> +JSSpec.DSL.value_of = function(target) {
>> +	if(JSSpec._secondPass) return {};
>> +	
>> +	var subject = new JSSpec.DSL.Subject(target);
>> +	return subject;
>> +};
>> +
>> +JSSpec.DSL.Subject = function(target) {
>> +	this.target = target;
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype._type = 'Subject';
>> +
>> +JSSpec.DSL.Subject.prototype.should_fail = function(message) {
>> +	JSSpec._assertionFailure = {message:message};
>> +	throw JSSpec._assertionFailure;
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_be = function(expected) {
>> +	var matcher = JSSpec.EqualityMatcher.createInstance(expected,  
>> this.target);
>> +	if(!matcher.matches()) {
>> +		JSSpec._assertionFailure = {message:matcher.explain()};
>> +		throw JSSpec._assertionFailure;
>> +	}
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_not_be = function(expected) {
>> +	// TODO JSSpec.EqualityMatcher should support 'condition'
>> +	var matcher = JSSpec.EqualityMatcher.createInstance(expected,  
>> this.target);
>> +	if(matcher.matches()) {
>> +		JSSpec._assertionFailure = {message:"'" + this.target + "'  
>> should not be '" + expected + "'"};
>> +		throw JSSpec._assertionFailure;
>> +	}
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_be_empty = function() {
>> +	this.should_have(0, this.getType() == 'String' ? 'characters' :  
>> 'items');
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_not_be_empty = function() {
>> +	this.should_have_at_least(1, this.getType() == 'String' ?  
>> 'characters' : 'items');
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_be_true = function() {
>> +	this.should_be(true);
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_be_false = function() {
>> +	this.should_be(false);
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_be_null = function() {
>> +	this.should_be(null);
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_be_undefined = function() {
>> +	this.should_be(undefined);
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_not_be_null = function() {
>> +	this.should_not_be(null);
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_not_be_undefined = function() {
>> +	this.should_not_be(undefined);
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype._should_have = function(num,  
>> property, condition) {
>> +	var matcher = JSSpec.PropertyLengthMatcher.createInstance(num,  
>> property, this.target, condition);
>> +	if(!matcher.matches()) {
>> +		JSSpec._assertionFailure = {message:matcher.explain()};
>> +		throw JSSpec._assertionFailure;
>> +	}
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_have = function(num, property) {
>> +	this._should_have(num, property, "exactly");
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_have_exactly = function(num,  
>> property) {
>> +	this._should_have(num, property, "exactly");
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_have_at_least = function(num,  
>> property) {
>> +	this._should_have(num, property, "at least");
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_have_at_most = function(num,  
>> property) {
>> +	this._should_have(num, property, "at most");
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_include = function(expected) {
>> +	var matcher = JSSpec.IncludeMatcher.createInstance(this.target,  
>> expected, true);
>> +	if(!matcher.matches()) {
>> +		JSSpec._assertionFailure = {message:matcher.explain()};
>> +		throw JSSpec._assertionFailure;
>> +	}
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_not_include =  
>> function(expected) {
>> +	var matcher = JSSpec.IncludeMatcher.createInstance(this.target,  
>> expected, false);
>> +	if(!matcher.matches()) {
>> +		JSSpec._assertionFailure = {message:matcher.explain()};
>> +		throw JSSpec._assertionFailure;
>> +	}
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.should_match = function(pattern) {
>> +	var matcher = JSSpec.PatternMatcher.createInstance(this.target,  
>> pattern, true);
>> +	if(!matcher.matches()) {
>> +		JSSpec._assertionFailure = {message:matcher.explain()};
>> +		throw JSSpec._assertionFailure;
>> +	}
>> +}
>> +JSSpec.DSL.Subject.prototype.should_not_match = function(pattern) {
>> +	var matcher = JSSpec.PatternMatcher.createInstance(this.target,  
>> pattern, false);
>> +	if(!matcher.matches()) {
>> +		JSSpec._assertionFailure = {message:matcher.explain()};
>> +		throw JSSpec._assertionFailure;
>> +	}
>> +};
>> +
>> +JSSpec.DSL.Subject.prototype.getType = function() {
>> +	if(typeof this.target == 'undefined') {
>> +		return 'undefined';
>> +	} else if(this.target == null) {
>> +		return 'null';
>> +	} else if(this.target._type) {
>> +		return this.target._type;
>> +	} else if(JSSpec.util.isDomNode(this.target)) {
>> +		return 'DomNode';
>> +	} else {
>> +		return 'object';
>> +	}
>> +};
>> +
>> +/**
>> + * Utilities
>> + */
>> +JSSpec.util = {
>> +	escapeTags: function(string) {
>> +		return string.replace(/</img, '&lt;').replace(/>/img, '&gt;');
>> +	},
>> +	escapeMetastring: function(string) {
>> +		return string.replace(/\r/img, '\\r').replace(/\n/img, '\ 
>> \n').replace(/\&para\;\<BR\>/img, '\\n').replace(/\t/img, '\\t');
>> +	},
>> +	parseOptions: function(defaults) {
>> +		var options = defaults;
>> +		
>> +		var url = location.href;
>> +		var queryIndex = url.indexOf('?');
>> +		if(queryIndex == -1) return options;
>> +		
>> +		var query = url.substring(queryIndex + 1).split('#')[0];
>> +		var pairs = query.split('&');
>> +		for(var i = 0; i < pairs.length; i++) {
>> +			var tokens = pairs[i].split('=');
>> +			options[tokens[0]] = tokens[1];
>> +		}
>> +		
>> +		return options;
>> +	},
>> +	correctHtmlAttrQuotation: function(html) {
>> +		html = html.replace(/(\w+)=['"]([^'"]+)['"]/mg,function (str,  
>> name, value) {return name + '=' + '"' + value + '"';});
>> +		html = html.replace(/(\w+)=([^ '"]+)/mg,function (str, name,  
>> value) {return name + '=' + '"' + value + '"';});
>> +		html = html.replace(/'/mg, '"');
>> +		
>> +		return html;
>> +	},
>> +	sortHtmlAttrs: function(html) {
>> +		var attrs = [];
>> +		html.replace(/((\w+)="[^"]+")/mg, function(str, matched) {
>> +			attrs.push(matched);
>> +		});
>> +		return attrs.length == 0 ? "" : " " + attrs.sort().join(" ");
>> +	},
>> +	sortStyleEntries: function(styleText) {
>> +		var entries = styleText.split(/; /);
>> +		return entries.sort().join("; ");
>> +	},
>> +	escapeHtml: function(str) {
>> +		if(!this._div) {
>> +			this._div = document.createElement("DIV");
>> +			this._text = document.createTextNode('');
>> +			this._div.appendChild(this._text);
>> +		}
>> +		this._text.data = str;
>> +		return this._div.innerHTML;
>> +	},
>> +	isDomNode: function(o) {
>> +		// TODO: make it more stricter
>> +		return (typeof o.nodeName == 'string') && (typeof o.nodeType ==  
>> 'number');
>> +	},
>> +	inspectDomPath: function(o) {
>> +		var sb = [];
>> +		while(o && o.nodeName != '#document' && o.parent) {
>> +			var siblings = o.parentNode.childNodes;
>> +			for(var i = 0; i < siblings.length; i++) {
>> +				if(siblings[i] == o) {
>> +					sb.push(o.nodeName + (i == 0 ? '' : '[' + i + ']'));
>> +					break;
>> +				}
>> +			}
>> +			o = o.parentNode;
>> +		}
>> +		return sb.join(" &gt; ");
>> +	},
>> +	inspectDomNode: function(o) {
>> +		if(o.nodeType == 1) {
>> +			var nodeName = o.nodeName.toLowerCase();
>> +			var sb = [];
>> +			sb.push('<span class="dom_value">');
>> +			sb.push("&lt;");
>> +			sb.push(nodeName);
>> +			
>> +			var attrs = o.attributes;
>> +			for(var i = 0; i < attrs.length; i++) {
>> +				if(
>> +					attrs[i].nodeValue &&
>> +					attrs[i].nodeName != 'contentEditable' &&
>> +					attrs[i].nodeName != 'style' &&
>> +					typeof attrs[i].nodeValue != 'function'
>> +				) sb.push(' <span class="dom_attr_name">' +  
>> attrs[i].nodeName.toLowerCase() + '</span>=<span  
>> class="dom_attr_value">"' + attrs[i].nodeValue + '"</span>');
>> +			}
>> +			if(o.style && o.style.cssText) {
>> +				sb.push(' <span class="dom_attr_name">style</span>=<span  
>> class="dom_attr_value">"' + o.style.cssText + '"</span>');
>> +			}
>> +			sb.push('&gt;');
>> +			sb.push(JSSpec.util.escapeHtml(o.innerHTML));
>> +			sb.push('&lt;/' + nodeName + '&gt;');
>> +			sb.push(' <span class="dom_path">(' +  
>> JSSpec.util.inspectDomPath(o) + ')</span>' );
>> +			sb.push('</span>');
>> +			return sb.join("");
>> +		} else if(o.nodeType == 3) {
>> +			return '<span class="dom_value">#text ' + o.nodeValue + '</ 
>> span>';
>> +		} else {
>> +			return '<span class="dom_value">UnknownDomNode</span>';
>> +		}
>> +	},
>> +	inspect: function(o, dontEscape, emphasisKey) {
>> +		var sb, inspected;
>> +
>> +		if(typeof o == 'undefined') return '<span  
>> class="undefined_value">undefined</span>';
>> +		if(o == null) return '<span class="null_value">null</span>';
>> +		if(o._type == 'String') return '<span class="string_value">"' +  
>> (dontEscape ? JSSpec.util.escapeMetastring(o) :  
>> JSSpec.util.escapeHtml(JSSpec.util.escapeMetastring(o))) + '"</ 
>> span>';
>> +
>> +		if(o._type == 'Date') {
>> +			return '<span class="date_value">"' + o.toString() + '"</span>';
>> +		}
>> +		
>> +		if(o._type == 'Number') return '<span class="number_value">' +  
>> (dontEscape ? o : JSSpec.util.escapeHtml(o)) + '</span>';
>> +		
>> +		if(o._type == 'Boolean') return '<span class="boolean_value">' +  
>> o + '</span>';
>> +
>> +		if(o._type == 'RegExp') return '<span class="regexp_value">' +  
>> JSSpec.util.escapeHtml(o.toString()) + '</span>';
>> +
>> +		if(JSSpec.util.isDomNode(o)) return JSSpec.util.inspectDomNode(o);
>> +
>> +		if(o._type == 'Array' || typeof o.length != 'undefined') {
>> +			sb = [];
>> +			for(var i = 0; i < o.length; i++) {
>> +				inspected = JSSpec.util.inspect(o[i]);
>> +				sb.push(i == emphasisKey ? ('<strong>' + inspected + '</ 
>> strong>') : inspected);
>> +			}
>> +			return '<span class="array_value">[' + sb.join(', ') + ']</ 
>> span>';
>> +		}
>> +		
>> +		// object
>> +		sb = [];
>> +		for(var key in o) {
>> +			if(key == 'should') continue;
>> +			
>> +			inspected = JSSpec.util.inspect(key) + ":" +  
>> JSSpec.util.inspect(o[key]);
>> +			sb.push(key == emphasisKey ? ('<strong>' + inspected + '</ 
>> strong>') : inspected);
>> +		}
>> +		return '<span class="object_value">{' + sb.join(', ') + '}</ 
>> span>';
>> +	}
>> +};
>> +
>> +describe = JSSpec.DSL.describe;
>> +behavior_of = JSSpec.DSL.describe;
>> +value_of = JSSpec.DSL.value_of;
>> +expect = JSSpec.DSL.value_of; // @deprecated
>> +
>> +String.prototype._type = "String";
>> +Number.prototype._type = "Number";
>> +Date.prototype._type = "Date";
>> +Array.prototype._type = "Array";
>> +Boolean.prototype._type = "Boolean";
>> +RegExp.prototype._type = "RegExp";
>> +
>> +var targets = [Array.prototype, Date.prototype, Number.prototype,  
>> String.prototype, Boolean.prototype, RegExp.prototype];
>> +
>> +String.prototype.normalizeHtml = JSSpec.DSL.forString.normalizeHtml;
>> +String.prototype.asHtml = String.prototype.normalizeHtml; // 
>> @deprecated
>> +String.prototype.strip = function() {return this.replace(/^\s+/,  
>> '').replace(/\s+$/, '');}
>> +
>> +
>> +/**
>> + * Main
>> + */
>> +JSSpec.defaultOptions = {
>> +	autorun: 1,
>> +	specIdBeginsWith: 0,
>> +	exampleIdBeginsWith: 0,
>> +	autocollapse: 1
>> +};
>> +JSSpec.options = JSSpec.util.parseOptions(JSSpec.defaultOptions);
>> +
>> +JSSpec.Spec.id = JSSpec.options.specIdBeginsWith;
>> +JSSpec.Example.id = JSSpec.options.exampleIdBeginsWith;
>> +
>> +
>> +
>> +window.onload = function() {
>> +	if(JSSpec.specs.length > 0) {
>> +		if(!JSSpec.options.inSuite) {
>> +			JSSpec.runner = new JSSpec.Runner(JSSpec.specs, new  
>> JSSpec.Logger());
>> +			if(JSSpec.options.rerun) {
>> +				JSSpec.runner.rerun(decodeURIComponent(JSSpec.options.rerun));
>> +			} else {
>> +				JSSpec.runner.run();
>> +			}
>> +		} else {
>> +			// in suite, send all specs to parent
>> +			var parentWindow = window.frames.parent.window;
>> +			for(var i = 0; i < JSSpec.specs.length; i++) {
>> +				parentWindow.JSSpec.specs.push(JSSpec.specs[i]);
>> +			}
>> +		}
>> +	} else {
>> +		var links =  
>> document.getElementById('list').getElementsByTagName('A');
>> +		var frameContainer = document.createElement('DIV');
>> +		frameContainer.style.display = 'none';
>> +		document.body.appendChild(frameContainer);
>> +		
>> +		for(var i = 0; i < links.length; i++) {
>> +			var frame = document.createElement('IFRAME');
>> +			frame.src = links[i].href + '?inSuite=0&specIdBeginsWith=' + (i  
>> * 10000) + '&exampleIdBeginsWith=' + (i * 10000);
>> +			frameContainer.appendChild(frame);
>> +		}
>> +	}
>> +}
>> \ No newline at end of file
>>
>> Propchange: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
>> JSSpec.js
>> ------------------------------------------------------------------------------
>>   svn:executable = *
>>
>> Added: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
>> JSSpecSpecs.js
>> URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/JSSpecSpecs.js?rev=787361&view=auto
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> = 
>> =====================================================================
>> --- incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
>> JSSpecSpecs.js (added)
>> +++ incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
>> JSSpecSpecs.js Mon Jun 22 19:23:30 2009
>> @@ -0,0 +1,140 @@
>> +/*
>> +Script: JSSpecSpecs.js
>> +	Failure Examples for JSSpec.js
>> +
>> +License:
>> +	MIT-style license.
>> +
>> +Note:
>> +	All examples should fail to demonstrate the usage and output of  
>> all the availible types of specs in JSSpec.
>> +*/
>> +
>> +describe('Should be', {
>> +	'Array should be (Array) 1': function() {
>> +		value_of(['ab','cd','ef']).should_be(['ab','bd','ef']);
>> +	},
>> +	'Array should be (Array) 2': function() {
>> +		value_of(['a',2,'4',5]).should_be([1,2,[4,5,6],6,7]);
>> +	},
>> +	'Boolean should be (Boolean)': function() {
>> +		value_of(true).should_be(false);
>> +	},
>> +	'Boolean should be false': function() {
>> +		value_of(true).should_be_false();
>> +	},
>> +	'Boolean should be true': function() {
>> +		value_of(false).should_be_true();
>> +	},
>> +	'Date should be (Date)': function() {
>> +		value_of(new Date(1979, 3, 27)).should_be(new Date(1976, 7, 23));
>> +	},
>> +	'Null should be (String)': function() {
>> +		value_of(null).should_be("Test");
>> +	},
>> +	'Null should be (undefined)': function() {
>> +		value_of(null).should_be(undefined);
>> +	},
>> +	'Number should be (Number)': function() {
>> +		value_of(1+2).should_be(4);
>> +	},
>> +	'Object should be (Object) 1': function() {
>> +		var actual = {a:1, b:2};
>> +		var expected = {a:1, b:2, d:3};
>> +		value_of(actual).should_be(expected);
>> +	},
>> +	'Object should be (Object) 2': function() {
>> +		var actual = {a:1, b:2, c:3, d:4};
>> +		var expected = {a:1, b:2, c:3};
>> +		value_of(actual).should_be(expected);
>> +	},
>> +	'Object should be (Object) 3': function() {
>> +		var actual = {a:1, b:4, c:3};
>> +		var expected = {a:1, b:2, c:3};
>> +		value_of(actual).should_be(expected);
>> +	},
>> +	'String should be (String)': function() {
>> +		value_of("Hello world").should_be("Good-bye world");
>> +	},
>> +	'String should be (undefined)': function() {
>> +		value_of("Test").should_be(undefined);
>> +	}
>> +});
>> +
>> +describe('Should be empty', {
>> +	'String should be empty': function() {
>> +		value_of("").should_be_empty();
>> +		value_of("Hello").should_be_empty();
>> +	},
>> +	'String should notbe empty': function() {
>> +		value_of("Hello").should_not_be_empty();
>> +		value_of("").should_not_be_empty();
>> +	},
>> +	'Array should be empty': function() {
>> +		value_of([]).should_be_empty();
>> +		value_of([1,2,3]).should_be_empty();
>> +	},
>> +	'Array should not be empty': function() {
>> +		value_of([1,2,3]).should_not_be_empty();
>> +		value_of([]).should_not_be_empty();
>> +	},
>> +	'Object\'s item should be empty': function() {
>> +		value_of({name:'Alan Kang', email:'jania902@gmail.com', accounts: 
>> ['A', 'B']}).should_have(0, "accounts");
>> +	},
>> +	'Object\'s item should not be empty': function() {
>> +		value_of({name:'Alan Kang', email:'jania902@gmail.com', accounts: 
>> []}).should_have(2, "accounts");
>> +	}
>> +});
>> +
>> +describe('Should have', {
>> +	'Array should have (Number, "items")': function() {
>> +		value_of([1,2,3]).should_have(4, "items");
>> +	},
>> +	'Array should have exactly (Number, "items")': function() {
>> +		value_of([1,2,3]).should_have_exactly(2, "items");
>> +	},
>> +	'Array should have at least (Number, "items")': function() {
>> +		value_of([1,2,3]).should_have_at_least(4, "items");
>> +	},
>> +	'Array should have at most (Number, "items")': function() {
>> +		value_of([1,2,3]).should_have_at_most(2, "items");
>> +	},
>> +	'Object\'s item should have (Number, "[property]s")': function() {
>> +		value_of({name:'Alan Kang', email:'jania902@gmail.com', accounts: 
>> ['A', 'B']}).should_have(3, "accounts");
>> +	},
>> +	'String should have (Number, "characters")': function() {
>> +		value_of("Hello").should_have(4, "characters");
>> +	},
>> +	'String should have (Number, "[No match]s")': function() {
>> +		value_of("This is a string").should_have(5, "players");
>> +	}
>> +});
>> +
>> +describe('Should include', {
>> +	'Array should include (mixed)': function() {
>> +		value_of([1,2,3]).should_include(4);
>> +	},
>> +	'Array should not include (mixed)': function() {
>> +		value_of([1,2,3]).should_not_include(2);
>> +	},
>> +	'Non-Array should include (mixed)': function() {
>> +		value_of(new Date()).should_include(4);
>> +	},
>> +	'Non-Array should not include (mixed)': function() {
>> +		value_of(new Date()).should_not_include(4);
>> +	}
>> +});
>> +
>> +describe('Should match', {
>> +	'String should match (RegExp)': function() {
>> +		value_of("Hello").should_match(/x/);
>> +	},
>> +	'String should not match (RegExp)': function() {
>> +		value_of("Hello").should_not_match(/ell/);
>> +	},
>> +	'Array should match (RegExp)': function() {
>> +		value_of([1,2,3]).should_match(/x/);
>> +	},
>> +	'Array should not match (RegExp)': function() {
>> +		value_of([1,2,3]).should_not_match(/x/);
>> +	}
>> +});
>> \ No newline at end of file
>>
>> Propchange: incubator/jspwiki/trunk/tests/JSSpec/Assets/Scripts/ 
>> JSSpecSpecs.js
>> ------------------------------------------------------------------------------
>>   svn:executable = *
>>