commit 2e423d95174cf2918be68b2eb8c8673a5bce111e Author: Mike Hamburg Date: Wed May 26 15:34:42 2010 -0700 initial commit for GitHub diff --git a/README/COPYRIGHT b/README/COPYRIGHT new file mode 100644 index 00000000..d573a057 --- /dev/null +++ b/README/COPYRIGHT @@ -0,0 +1,32 @@ +SJCL used to be in the public domain. Now it's: + +Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh, Stanford University. + +This is for liability reasons. (Speaking of which, SJCL comes with NO +WARRANTY WHATSOEVER, express or implied, to the limit of applicable +law.) + +SJCL is dual-licensed under the GNU GPL version 2.0 or higher, and a +2-clause BSD license. You may use SJCL under the terms of either of +these licenses. For your convenience, the GPL versions 2.0 and 3.0 +and the 2-clause BSD license are included here. Additionally, you may +serve "crunched" copies of sjcl (i.e. those with comments removed, +and other transformations to reduce code size) without any copyright +notice. + +SJCL includes JsDoc toolkit, YUI compressor, Closure compressor, +JSLint and the CodeView template in its build system. These programs' +copyrights are owned by other people. They are distributed here under +the MPL, MIT, BSD, Apache and JSLint licenses. Codeview is "free for +download" but has no license attached; it is Copyright 2010 Wouter Bos. + +The BSD license is (almost?) strictly more permissive, but the +additionally licensing under the GPL allows us to use OCB 2.0 code +royalty-free (at least, if OCB 2.0's creator Phil Rogaway has anything +to say about it). Note that if you redistribute SJCL under a license +other than the GPL, you or your users may need to pay patent licensing +fees for OCB 2.0. + +There may be patents which apply to SJCL other than Phil Rogaway's OCB +patents. We suggest that you consult legal counsel before using SJCL +in a commercial project. diff --git a/README/INSTALL b/README/INSTALL new file mode 100644 index 00000000..a5898c40 --- /dev/null +++ b/README/INSTALL @@ -0,0 +1,36 @@ +SJCL comes with a file sjcl.js pre-built. This default build includes +all the modules except for sjcl.codec.bytes (because the demo site doesn't +use it). All you need to do to install is copy this file to your web +server and start using it. + +SJCL is divided into modules implementing various cryptographic and +convenience functions. If you don't need them all for your application, +you can reconfigure SJCL for a smaller code size. To do this, you can +run + +./configure --without-all --with-aes --with-sha256 ... + +Then type + +make + +to rebuild sjcl.js. This will also create a few intermediate files +core*.js; you can delete these automatically by typing + +make sjcl.js tidy + +instead. You will need make, perl, bash and java to rebuild SJCL. + +Some of the modules depend on other modules; configure should handle this +automatically unless you tell it --without-FOO --with-BAR, where BAR +depends on FOO. If you do this, configure will yell at you. + +SJCL is compressed by stripping comments, shortening variable names, etc. +You can also pass a --compress argument to configure to change the +compressor. By default SJCL uses some perl/sh scripts and Google's +Closure compressor. + +If you reconfigure SJCL, it is recommended that you run the included test +suite by typing "make test". If this prints "FAIL" or segfaults, SJCL +doesn't work; please file a bug. + diff --git a/README/bsd.txt b/README/bsd.txt new file mode 100644 index 00000000..a0559ed0 --- /dev/null +++ b/README/bsd.txt @@ -0,0 +1,30 @@ +Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation +are those of the authors and should not be interpreted as representing +official policies, either expressed or implied, of the authors. diff --git a/README/gpl-2.0.txt b/README/gpl-2.0.txt new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/README/gpl-2.0.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies 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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README/gpl-3.0.txt b/README/gpl-3.0.txt new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/README/gpl-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/browserTest/browserTest.html b/browserTest/browserTest.html new file mode 100644 index 00000000..be8d5348 --- /dev/null +++ b/browserTest/browserTest.html @@ -0,0 +1,16 @@ + + + + + SJCL browser test + + + + + +

SJCL browser test

+
Waiting for tests to begin...
+
+ + diff --git a/browserTest/browserUtil.js b/browserTest/browserUtil.js new file mode 100644 index 00000000..3b2fc99d --- /dev/null +++ b/browserTest/browserUtil.js @@ -0,0 +1,128 @@ +browserUtil = {}; + +browserUtil.isRhino = (typeof(window) === 'undefined'); + +/** + * Pause (for the graphics to update and the script timer to clear), then run the + * specified action. + */ +browserUtil.pauseAndThen = function (cb) { + cb && window.setTimeout(cb, 1); +}; + +/** + * Iterate using continuation-passing style. + */ +browserUtil.cpsIterate = function (f, start, end, pause, callback) { + var pat = pause ? browserUtil.pauseAndThen : function (cb) { cb && cb(); }; + function go() { + var called = false; + if (start >= end) { + pat(callback); + } else { + pat(function () { f(start, function () { + if (!called) { called = true; start++; go(); } + }); }); + } + } + go (start); +}; + +/** + * Map a function over an array using continuation-passing style. + */ +browserUtil.cpsMap = function (map, list, pause, callback) { + browserUtil.cpsIterate(function (i, cb) { map(list[i], i, list.length, cb); }, + 0, list.length, pause, callback); +} + +/** Cache for remotely loaded scripts. */ +browserUtil.scriptCache = {} + +/** Load several scripts, then call back */ +browserUtil.loadScripts = function(scriptNames, cbSuccess, cbError) { + var head = document.getElementsByTagName('head')[0]; + browserUtil.cpsMap(function (script, i, n, cb) { + var scriptE = document.createElement('script'), xhr, loaded = false; + + browserUtil.status("Loading script " + script); + + if (window.location.protocol === "file:") { + /* Can't make an AJAX request for files. + * But, we know the load time will be short, so timeout-based error + * detection is fine. + */ + scriptE.onload = function () { + loaded = true; + cb(); + }; + scriptE.onerror = function(err) { + cbError && cbError(script, err, cb); + }; + script.onreadystatechange = function() { + if (this.readyState == 'complete' || this.readyState == 'loaded') { + loaded = true; + cb(); + } + }; + scriptE.type = 'text/javascript'; + scriptE.src = script+"?"+(new Date().valueOf()); + window.setTimeout(function () { + loaded || cbError && cbError(script, "timeout expired", cb); + }, 100); + head.appendChild(scriptE); + } else if (browserUtil.scriptCache[script] !== undefined) { + try { + scriptE.appendChild(document.createTextNode(browserUtil.scriptCache[script])); + } catch (e) { + scriptE.text = browserUtil.scriptCache[script]; + } + head.appendChild(scriptE); + cb(); + } else { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + if (xhr.status == 200) { + browserUtil.scriptCache[script] = xhr.responseText; + try { + scriptE.appendChild(document.createTextNode(xhr.responseText)); + } catch (e) { + scriptE.text = xhr.responseText; + } + head.appendChild(scriptE); + cb(); + } else { + cbError && cbError(script, xhr.status, cb); + } + } + } + xhr.open("GET", script+"?"+(new Date().valueOf()), true); + xhr.send(); + } + }, scriptNames, false, cbSuccess); +}; + +/** Write a message to the console */ +browserUtil.write = function(type, message) { + var d1 = document.getElementById("print"), d2 = document.createElement("div"), d3 = document.createElement("div"); + d3.className = type; + d3.appendChild(document.createTextNode(message)); + d2.appendChild(d3); + d1.appendChild(d2); + return { update: function (type2, message2) { + var d4 = document.createElement("div"); + d4.className = type2 + " also"; + d4.appendChild(document.createTextNode(message2)); + d2.insertBefore(d4, d3); + }}; +}; + +/** Write a newline. Does nothing in the browser. */ +browserUtil.writeNewline = function () { }; + +/** Write a message to the status line */ +browserUtil.status = function(message) { + var d1 = document.getElementById("status"); + d1.replaceChild(document.createTextNode(message), d1.firstChild); +}; diff --git a/browserTest/rhinoUtil.js b/browserTest/rhinoUtil.js new file mode 100644 index 00000000..3107104d --- /dev/null +++ b/browserTest/rhinoUtil.js @@ -0,0 +1,44 @@ +browserUtil = { + isRhino: true, + + pauseAndThen: function (cb) { cb(); }, + + cpsIterate: function (f, start, end, pause, callback) { + function go() { + var called = false; + if (start >= end) { + callback && callback(); + } else { + f(start, function () { + if (!called) { called = true; start++; go(); } + }); + } + } + go (start); + }, + + cpsMap: function (map, list, pause, callback) { + browserUtil.cpsIterate(function (i, cb) { map(list[i], i, list.length, cb); }, + 0, list.length, pause, callback); + }, + + loadScripts: function(scriptNames, callback) { + for (i=0; i .begin, *+* > .all { + margin-top: 1em; +} + +.also { + float: right; + width: 17em; + text-align: right; +} + +h1 { + text-align: center; + background: #8A0000; + padding: 5px; + color: white; +} + +#status { + padding: 3px 10px 3px 5px; + background: #d5c490; + color: #444; + font-size: 0.8em; + margin-bottom: 1em; + height: 1.3em; + vertical-align: middle; +} diff --git a/compress/compiler.jar b/compress/compiler.jar new file mode 100644 index 00000000..c087e922 Binary files /dev/null and b/compress/compiler.jar differ diff --git a/compress/compress_with_closure.sh b/compress/compress_with_closure.sh new file mode 100755 index 00000000..07985c87 --- /dev/null +++ b/compress/compress_with_closure.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +DIR=`dirname $0` + +$DIR/remove_constants.pl $1 | $DIR/opacify.pl > ._tmpRC.js + +echo -n '"use strict";' +java -jar $DIR/compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS \ + --js ._tmpRC.js \ + | $DIR/digitize.pl \ + | $DIR/dewindowize.pl + + +rm -f ._tmpRC.js + diff --git a/compress/compress_with_yui.sh b/compress/compress_with_yui.sh new file mode 100755 index 00000000..9dfecdac --- /dev/null +++ b/compress/compress_with_yui.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Compress $1 with YUI Compressor 2.4.2, returning the compressed script on stdout + +DIR=`dirname $0` + +$DIR/remove_constants.pl $1 > ._tmpRC.js + +java -jar $DIR/yuicompressor-2.4.2.jar ._tmpRC.js \ + | $DIR/digitize.pl + +rm -f ._tmpRC.js + diff --git a/compress/dewindowize.pl b/compress/dewindowize.pl new file mode 100755 index 00000000..c8843825 --- /dev/null +++ b/compress/dewindowize.pl @@ -0,0 +1,8 @@ +#!/usr/bin/env perl + +while (<>) { + s/window\.sjcl\s*=/var sjcl=/g; + s/window\.sjcl/sjcl/g; + print; +} + diff --git a/compress/digitize.pl b/compress/digitize.pl new file mode 100755 index 00000000..98db16af --- /dev/null +++ b/compress/digitize.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl + +# Convert numbers to hex, when doing so is likely to increase compressibility. +# This actually makes the script slightly longer, but generally makes it compress +# to something shorter. +# +# Here we're targeting constants like 0xFF, 0xFFFF0000, 0x10101, 0x100000000, etc. + +sub digitize { + my $number = shift; + if ($number >= 256) { + my $nn = sprintf("%x", $number); + if ($nn =~ /^[01f]+$/i) { return "0x$nn"; } + } + return $number; +} + +while (<>) { + s/([^a-zA-Z0-9_])(\d+)/$1 . digitize $2/eg; + print; +} + diff --git a/compress/opacify.pl b/compress/opacify.pl new file mode 100755 index 00000000..d17c914b --- /dev/null +++ b/compress/opacify.pl @@ -0,0 +1,32 @@ +#!/usr/bin/env perl + +# This script is a hack. +# +# Opacify all non-private names by turning them into strings. +# That way, the Google compressor won't rename them. +# +# The script ignores properties whose names begin with _, because they +# are believed to be private. +# +# XXX TODO FIXME: this messes with strings, so it screws up exceptions. + +my $script = join '', <>; + +# remove comments +$script =~ s=/\*([^\*]|\*+[^\/])*\*/==g; +$script =~ s=//.*==g; + +# stringify property names +$script =~ s=\.([a-zA-Z0-9][_a-zA-Z0-9]*)=['$1']=g; + +# stringify sjcl +$script =~ s=(?:var\s+)?sjcl(\.|\s*\=)=window['sjcl']$1=g; + +# stringify object notation +$script =~ s=([\{,]\s*)([a-zA-Z0-9][_a-zA-Z0-9]*):=$1'$2':=g; + +# Export sjcl. This is a bit of a hack, and might get replaced later. +print $script; + +# not necessary with windowization. +# print "window\['sjcl'\] = sjcl;\n"; diff --git a/compress/remove_constants.pl b/compress/remove_constants.pl new file mode 100755 index 00000000..c9b0f22d --- /dev/null +++ b/compress/remove_constants.pl @@ -0,0 +1,69 @@ +#!/usr/bin/env perl + +# This script is a hack. It identifies things which it believes to be +# constant, then replaces them throughout the code. +# +# Constants are identified as properties declared in object notation +# with values consisting only of capital letters and underscores. If +# the first character is an underscore, the constant is private, and +# can be removed entirely. +# +# The script dies if any two constants have the same property name but +# different values. +my $script = join '', <>; + +# remove comments +$script =~ s=/\*([^\*]|\*+[^\/])*\*/==g; +$script =~ s=//.*==g; + +sub preserve { + my $stuff = shift; + $stuff =~ s/,//; + return $stuff; +} + +my %constants = (); + +sub add_constant { + my ($name, $value) = @_; + if (defined $constants{$name} && $constants{$name} ne $value) { + print STDERR "variant constant $name = $value"; + die; + } else { + $constants{$name} = $value; + #print STDERR "constant: $name = $value\n"; + } +} + +# find private constants +while ($script =~ + s/([,\{]) \s* # indicator that this is part of an object + (_[A-Z0-9_]+) \s* : \s* # all-caps variable name beginning with _ + (\d+|0x[0-9A-Fa-f]+) \s* # numeric value + ([,\}]) # next part of object + /preserve "$1$4"/ex) { + add_constant $2, $3; +} + +my $script2 = ''; + +# find public constants +while ($script =~ + s/^(.*?) # beginning of script + ([,\{]) \s* # indicator that this is part of an object + ([A-Z0-9_]+) \s* : \s* # all-caps variable name + (\d+|0x[0-9A-Fa-f]+) \s* # numeric value + ([,\}]) # next part of object([,\{]) \s* + /$5/esx) { + $script2 .= "$1$2$3:$4"; + add_constant $3, $4; +} + +$script = "$script2$script"; + +foreach (keys %constants) { + my $value = $constants{$_}; + $script =~ s/(?:[a-zA-Z0-9_]+\.)+$_(?=[^a-zA-Z0-9_])/$value/g; +} + +print $script; diff --git a/compress/yuicompressor-2.4.2.jar b/compress/yuicompressor-2.4.2.jar new file mode 100644 index 00000000..c29470bd Binary files /dev/null and b/compress/yuicompressor-2.4.2.jar differ diff --git a/core/aes.js b/core/aes.js new file mode 100644 index 00000000..144cd248 --- /dev/null +++ b/core/aes.js @@ -0,0 +1,208 @@ +/** @fileOverview Low-level AES implementation. + * + * This file contains a low-level implementation of AES, optimized for + * size and for efficiency on several browsers. It is based on + * OpenSSL's aes_core.c, a public-domain implementation by Vincent + * Rijmen, Antoon Bosselaers and Paulo Barreto. + * + * An older version of this implementation is available in the public + * domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh, + * Stanford University 2008-2010 and BSD-licensed for liability + * reasons. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +/** + * Schedule out an AES key for both encryption and decryption. This + * is a low-level class. Use a cipher mode to do bulk encryption. + * + * @constructor + * @param {Array} key The key as an array of 4, 6 or 8 words. + * + * @class Advanced Encryption Standard (low-level interface) + */ +sjcl.cipher.aes = function (key) { + if (!this._tables[0][0][0]) { + this._precompute(); + } + + var i, j, tmp, + encKey, decKey, + sbox = this._tables[0][4], decTable = this._tables[1], + keyLen = key.length, rcon = 1; + + if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) { + throw new sjcl.exception.invalid("invalid aes key size"); + } + + this._key = [encKey = key.slice(0), decKey = []]; + + // schedule encryption keys + for (i = keyLen; i < 4 * keyLen + 28; i++) { + tmp = encKey[i-1]; + + // apply sbox + if (i%keyLen === 0 || (keyLen === 8 && i%keyLen === 4)) { + tmp = sbox[tmp>>>24]<<24 ^ sbox[tmp>>16&255]<<16 ^ sbox[tmp>>8&255]<<8 ^ sbox[tmp&255]; + + // shift rows and add rcon + if (i%keyLen === 0) { + tmp = tmp<<8 ^ tmp>>>24 ^ rcon<<24; + rcon = rcon<<1 ^ (rcon>>7)*283; + } + } + + encKey[i] = encKey[i-keyLen] ^ tmp; + } + + // schedule decryption keys + for (j = 0; i; j++, i--) { + tmp = encKey[j&3 ? i : i - 4]; + if (i<=4 || j<4) { + decKey[j] = tmp; + } else { + decKey[j] = decTable[0][sbox[tmp>>>24 ]] ^ + decTable[1][sbox[tmp>>16 & 255]] ^ + decTable[2][sbox[tmp>>8 & 255]] ^ + decTable[3][sbox[tmp & 255]]; + } + } +}; + +sjcl.cipher.aes.prototype = { + // public + /* Something like this might appear here eventually + name: "AES", + blockSize: 4, + keySizes: [4,6,8], + */ + + /** + * Encrypt an array of 4 big-endian words. + * @param {Array} data The plaintext. + * @return {Array} The ciphertext. + */ + encrypt:function (data) { return this._crypt(data,0); }, + + /** + * Decrypt an array of 4 big-endian words. + * @param {Array} data The ciphertext. + * @return {Array} The plaintext. + */ + decrypt:function (data) { return this._crypt(data,1); }, + + /** + * The expanded S-box and inverse S-box tables. These will be computed + * on the client so that we don't have to send them down the wire. + * + * There are two tables, _tables[0] is for encryption and + * _tables[1] is for decryption. + * + * The first 4 sub-tables are the expanded S-box with MixColumns. The + * last (_tables[01][4]) is the S-box itself. + * + * @private + */ + _tables: [[[],[],[],[],[]],[[],[],[],[],[]]], + + /** + * Expand the S-box tables. + * + * @private + */ + _precompute: function () { + var encTable = this._tables[0], decTable = this._tables[1], + sbox = encTable[4], sboxInv = decTable[4], + i, x, xInv, d=[], th=[], x2, x4, x8, s, tEnc, tDec; + + // Compute double and third tables + for (i = 0; i < 256; i++) { + th[( d[i] = i<<1 ^ (i>>7)*283 )^i]=i; + } + + for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) { + // Compute sbox + s = xInv ^ xInv<<1 ^ xInv<<2 ^ xInv<<3 ^ xInv<<4; + s = s>>8 ^ s&255 ^ 99; + sbox[x] = s; + sboxInv[s] = x; + + // Compute MixColumns + x8 = d[x4 = d[x2 = d[x]]]; + tDec = x8*0x1010101 ^ x4*0x10001 ^ x2*0x101 ^ x*0x1010100; + tEnc = d[s]*0x101 ^ s*0x1010100; + + for (i = 0; i < 4; i++) { + encTable[i][x] = tEnc = tEnc<<24 ^ tEnc>>>8; + decTable[i][s] = tDec = tDec<<24 ^ tDec>>>8; + } + } + + // Compactify. Considerable speedup on Firefox. + for (i = 0; i < 5; i++) { + encTable[i] = encTable[i].slice(0); + decTable[i] = decTable[i].slice(0); + } + }, + + /** + * Encryption and decryption core. + * @param {Array} input Four words to be encrypted or decrypted. + * @param dir The direction, 0 for encrypt and 1 for decrypt. + * @return {Array} The four encrypted or decrypted words. + * @private + */ + _crypt:function (input, dir) { + if (input.length !== 4) { + throw new sjcl.exception.invalid("invalid aes block size"); + } + + var key = this._key[dir], + // state variables a,b,c,d are loaded with pre-whitened data + a = input[0] ^ key[0], + b = input[dir ? 3 : 1] ^ key[1], + c = input[2] ^ key[2], + d = input[dir ? 1 : 3] ^ key[3], + a2, b2, c2, + + nInnerRounds = key.length/4 - 2, + i, + kIndex = 4, + out = [0,0,0,0], + table = this._tables[dir], + + // load up the tables + t0 = table[0], + t1 = table[1], + t2 = table[2], + t3 = table[3], + sbox = table[4]; + + // Inner rounds. Cribbed from OpenSSL. + for (i = 0; i < nInnerRounds; i++) { + a2 = t0[a>>>24] ^ t1[b>>16 & 255] ^ t2[c>>8 & 255] ^ t3[d & 255] ^ key[kIndex]; + b2 = t0[b>>>24] ^ t1[c>>16 & 255] ^ t2[d>>8 & 255] ^ t3[a & 255] ^ key[kIndex + 1]; + c2 = t0[c>>>24] ^ t1[d>>16 & 255] ^ t2[a>>8 & 255] ^ t3[b & 255] ^ key[kIndex + 2]; + d = t0[d>>>24] ^ t1[a>>16 & 255] ^ t2[b>>8 & 255] ^ t3[c & 255] ^ key[kIndex + 3]; + kIndex += 4; + a=a2; b=b2; c=c2; + } + + // Last round. + for (i = 0; i < 4; i++) { + out[dir ? 3&-i : i] = + sbox[a>>>24 ]<<24 ^ + sbox[b>>16 & 255]<<16 ^ + sbox[c>>8 & 255]<<8 ^ + sbox[d & 255] ^ + key[kIndex++]; + a2=a; a=b; b=c; c=d; d=a2; + } + + return out; + } +}; + diff --git a/core/bitArray.js b/core/bitArray.js new file mode 100644 index 00000000..40e4acd0 --- /dev/null +++ b/core/bitArray.js @@ -0,0 +1,166 @@ +/** @fileOverview Arrays of bits, encoded as arrays of Numbers. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +/** @namespace Arrays of bits, encoded as arrays of Numbers. + * + * @description + *

+ * These objects are the currency accepted by SJCL's crypto functions. + *

+ * + *

+ * Most of our crypto primitives operate on arrays of 4-byte words internally, + * but many of them can take arguments that are not a multiple of 4 bytes. + * This library encodes arrays of bits (whose size need not be a multiple of 8 + * bits) as arrays of 32-bit words. The bits are packed, big-endian, into an + * array of words, 32 bits at a time. Since the words are double-precision + * floating point numbers, they fit some extra data. We use this (in a private, + * possibly-changing manner) to encode the number of bits actually present + * in the last word of the array. + *

+ * + *

+ * Because bitwise ops clear this out-of-band data, these arrays can be passed + * to ciphers like AES which want arrays of words. + *

+ */ +sjcl.bitArray = { + /** + * Array slices in units of bits. + * @param {bitArray a} The array to slice. + * @param {Number} bstart The offset to the start of the slice, in bits. + * @param {Number} bend The offset to the end of the slice, in bits. If this is undefined, + * slice until the end of the array. + * @return {bitArray} The requested slice. + */ + bitSlice: function (a, bstart, bend) { + a = sjcl.bitArray._shiftRight(a.slice(bstart/32), 32 - (bstart & 31)).slice(1); + return (bend === undefined) ? a : sjcl.bitArray.clamp(a, bend-bstart); + }, + + /** + * Concatenate two bit arrays. + * @param {bitArray} a1 The first array. + * @param {bitArray} a2 The second array. + * @return {bitArray} The concatenation of a1 and a2. + */ + concat: function (a1, a2) { + if (a1.length === 0 || a2.length === 0) { + return a1.concat(a2); + } + + var out, i, last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last); + if (shift === 32) { + return a1.concat(a2); + } else { + return sjcl.bitArray._shiftRight(a2, shift, last|0, a1.slice(0,a1.length-1)); + } + }, + + /** + * Find the length of an array of bits. + * @param {bitArray} a The array. + * @return {Number} The length of a, in bits. + */ + bitLength: function (a) { + var l = a.length, x; + if (l === 0) { return 0; } + x = a[l - 1]; + return (l-1) * 32 + sjcl.bitArray.getPartial(x); + }, + + /** + * Truncate an array. + * @param {bitArray} a The array. + * @param {Number} len The length to truncate to, in bits. + * @return {bitArray} A new array, truncated to len bits. + */ + clamp: function (a, len) { + if (a.length * 32 < len) { return a; } + a = a.slice(0, Math.ceil(len / 32)); + var l = a.length; + len = len & 31; + if (l > 0 && len) { + a[l-1] = sjcl.bitArray.partial(len, a[l-1] & 0x80000000 >> (len-1), 1); + } + return a; + }, + + /** + * Make a partial word for a bit array. + * @param {Number} len The number of bits in the word. + * @param {Number} x The bits. + * @param {Number} [0] _end Pass 1 if x has already been shifted to the high side. + * @return {Number} The partial word. + */ + partial: function (len, x, _end) { + if (len === 32) { return x; } + return (_end ? x|0 : x << (32-len)) + len * 0x10000000000; + }, + + /** + * Get the number of bits used by a partial word. + * @param {Number} x The partial word. + * @return {Number} The number of bits used by the partial word. + */ + getPartial: function (x) { + return Math.round(x/0x10000000000) || 32; + }, + + /** + * Compare two arrays for equality in a predictable amount of time. + * @param {bitArray} a The first array. + * @param {bitArray} b The second array. + * @return {boolean} true if a == b; false otherwise. + */ + equal: function (a, b) { + if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b)) { + return false; + } + var x = 0, i; + for (i=0; i= 32; shift -= 32) { + out.push(carry); + carry = 0; + } + if (shift === 0) { + return out.concat(a); + } + + for (i=0; i>>shift); + carry = a[i] << (32-shift); + } + last2 = a.length ? a[a.length-1] : 0; + shift2 = sjcl.bitArray.getPartial(last2); + out.push(sjcl.bitArray.partial(shift+shift2 & 31, (shift + shift2 > 32) ? carry : out.pop(),1)); + return out; + }, + + /** xor a block of 4 words together. + * @private + */ + _xor4: function(x,y) { + return [x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]]; + } +}; diff --git a/core/ccm.js b/core/ccm.js new file mode 100644 index 00000000..c51db006 --- /dev/null +++ b/core/ccm.js @@ -0,0 +1,185 @@ +/** @fileOverview CCM mode implementation. + * + * Special thanks to Roy Nicholson for pointing out a bug in our + * implementation. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +/** @namespace CTR mode with CBC MAC. */ +sjcl.mode.ccm = { + /** The name of the mode. + * @constant + */ + name: "ccm", + + /** Encrypt in CCM mode. + * @static + * @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes. + * @param {bitArray} plaintext The plaintext data. + * @param {bitArray} iv The initialization value. + * @param {bitArray} [adata=[]] The authenticated data. + * @param {Number} [tlen=64] the desired tag length, in bits. + * @return {bitArray} The encrypted data, an array of bytes. + */ + encrypt: function(prf, plaintext, iv, adata, tlen) { + var L, i, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8; + tlen = tlen || 64; + adata = adata || []; + + if (ivl < 7) { + throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"); + } + + // compute the length of the length + for (L=2; L<4 && ol >>> 8*L; L++) {} + if (L < 15 - ivl) { L = 15-ivl; } + iv = w.clamp(iv,8*(15-L)); + + // compute the tag + tag = sjcl.mode.ccm._computeTag(prf, plaintext, iv, adata, tlen, L); + + // encrypt + out = sjcl.mode.ccm._ctrMode(prf, out, iv, tag, tlen, L); + + return w.concat(out.data, out.tag); + }, + + /** Decrypt in CCM mode. + * @static + * @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes. + * @param {bitArray} ciphertext The ciphertext data. + * @param {bitArray} iv The initialization value. + * @param {bitArray} [[]] adata The authenticated data. + * @param {Number} [64] tlen the desired tag length, in bits. + * @return {bitArray} The decrypted data. + */ + decrypt: function(prf, ciphertext, iv, adata, tlen) { + tlen = tlen || 64; + adata = adata || []; + var L, i, + w=sjcl.bitArray, + ivl = w.bitLength(iv) / 8, + ol = w.bitLength(ciphertext), + out = w.clamp(ciphertext, ol - tlen), + tag = w.bitSlice(ciphertext, ol - tlen), tag2; + + + ol = (ol - tlen) / 8; + + if (ivl < 7) { + throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"); + } + + // compute the length of the length + for (L=2; L<4 && ol >>> 8*L; L++) {} + if (L < 15 - ivl) { L = 15-ivl; } + iv = w.clamp(iv,8*(15-L)); + + // decrypt + out = sjcl.mode.ccm._ctrMode(prf, out, iv, tag, tlen, L); + + // check the tag + tag2 = sjcl.mode.ccm._computeTag(prf, out.data, iv, adata, tlen, L); + if (!w.equal(out.tag, tag2)) { + throw new sjcl.exception.corrupt("ccm: tag doesn't match"); + } + + return out.data; + }, + + /* Compute the (unencrypted) authentication tag, according to the CCM specification + * @param {Object} prf The pseudorandom function. + * @param {bitArray} plaintext The plaintext data. + * @param {bitArray} iv The initialization value. + * @param {bitArray} adata The authenticated data. + * @param {Number} tlen the desired tag length, in bits. + * @return {bitArray} The tag, but not yet encrypted. + * @private + */ + _computeTag: function(prf, plaintext, iv, adata, tlen, L) { + // compute B[0] + var q, mac, field = 0, offset = 24, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4; + + tlen /= 8; + + // check tag length and message length + if (tlen % 2 || tlen < 4 || tlen > 16) { + throw new sjcl.exception.invalid("ccm: invalid tag length"); + } + + if (adata.length > 0xFFFFFFFF || plaintext.length > 0xFFFFFFFF) { + // I don't want to deal with extracting high words from doubles. + throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"); + } + + // mac the flags + mac = [w.partial(8, (adata.length ? 1<<6 : 0) | (tlen-2) << 2 | L-1)]; + + // mac the iv and length + mac = w.concat(mac, iv); + mac[3] |= w.bitLength(plaintext)/8; + mac = prf.encrypt(mac); + + + if (adata.length) { + // mac the associated data. start with its length... + tmp = w.bitLength(adata)/8; + if (tmp <= 0xFEFF) { + macData = [w.partial(16, tmp)]; + } else if (tmp <= 0xFFFFFFFF) { + macData = w.concat([w.partial(16,0xFFFE)], [tmp]); + } // else ... + + // mac the data itself + macData = w.concat(macData, adata); + for (i=0; i>>bits) >>> 26); + if (bits < 6) { + ta = arr[i] << (6-bits); + bits += 26; + i++; + } else { + ta <<= 6; + bits -= 6; + } + } + while ((out.length & 3) && !_noEquals) { out += "="; } + return out; + }, + + /** Convert from a base64 string to a bitArray */ + toBits: function(str) { + str = str.replace(/\s|=/g,''); + var out = [], i, bits=0, c = sjcl.codec.base64._chars, ta=0, x; + for (i=0; i 26) { + bits -= 26; + out.push(ta ^ x>>>bits); + ta = x << (32-bits); + } else { + bits += 6; + ta ^= x << (32-bits); + } + } + if (bits&56) { + out.push(sjcl.bitArray.partial(bits&56, ta, 1)); + } + return out; + } +}; diff --git a/core/codecBytes.js b/core/codecBytes.js new file mode 100644 index 00000000..cde2af3a --- /dev/null +++ b/core/codecBytes.js @@ -0,0 +1,37 @@ +/** @fileOverview Bit array codec implementations. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +/** @namespace Arrays of bytes */ +sjcl.codec.bytes = { + /** Convert from a bitArray to an array of bytes. */ + fromBits: function (arr) { + var out = [], bl = sjcl.bitArray.bitLength(arr), i, tmp; + for (i=0; i>> 24); + tmp <<= 8; + } + return out; + }, + /** Convert from an array of bytes to a bitArray. */ + toBits: function (bytes) { + var out = [], i, tmp=0; + for (i=0; i>> 24); + tmp <<= 8; + } + return decodeURIComponent(escape(out)); + }, + + /** Convert from a UTF-8 string to a bitArray. */ + toBits: function (str) { + str = unescape(encodeURIComponent(str)); + var out = [], i, tmp=0; + for (i=0; i 4)) { + throw new sjcl.exception.invalid("json encrypt: invalid parameters"); + } + + if (typeof password === "string") { + tmp = sjcl.misc.cachedPbkdf2(password, p); + password = tmp.key.slice(0,p.ks/32); + p.salt = tmp.salt; + } + if (typeof plaintext === "string") { + plaintext = sjcl.codec.utf8String.toBits(plaintext); + } + prp = new sjcl.cipher[p.cipher](password); + + /* return the json data */ + j._add(rp, p); + rp.key = password; + + /* do the encryption */ + p.ct = sjcl.mode[p.mode].encrypt(prp, plaintext, p.iv, p.adata, p.tag); + + return j.encode(j._subtract(p, j.defaults)); + }, + + /** Simple decryption function. + * @param {String|bitArray} password The password or key. + * @param {String} ciphertext The ciphertext to decrypt. + * @param {Object} [params] Additional non-default parameters. + * @param {Object} [rp] A returned object with filled parameters. + * @return {String} The plaintext. + * @throws {sjcl.exception.invalid} if a parameter is invalid. + * @throws {sjcl.exception.corrupt} if the ciphertext is corrupt. + */ + decrypt: function (password, ciphertext, params, rp) { + params = params || {}; + rp = rp || {}; + + var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),j.decode(ciphertext)), params, true), ct, tmp, prp; + if (typeof p.salt === "string") { + p.salt = sjcl.codec.base64.toBits(p.salt); + } + if (typeof p.iv === "string") { + p.iv = sjcl.codec.base64.toBits(p.iv); + } + + if (!sjcl.mode[p.mode] || + !sjcl.cipher[p.cipher] || + (typeof password === "string" && p.iter <= 100) || + (p.ts !== 64 && p.ts !== 96 && p.ts !== 128) || + (p.ks !== 128 && p.ks !== 192 && p.ks !== 256) || + (!p.iv) || + (p.iv.length < 2 || p.iv.length > 4)) { + throw new sjcl.exception.invalid("json decrypt: invalid parameters"); + } + + if (typeof password === "string") { + tmp = sjcl.misc.cachedPbkdf2(password, p); + password = tmp.key.slice(0,p.ks/32); + p.salt = tmp.salt; + } + prp = new sjcl.cipher[p.cipher](password); + + /* do the decryption */ + ct = sjcl.mode[p.mode].decrypt(prp, p.ct, p.iv, p.adata, p.tag); + + /* return the json data */ + j._add(rp, p); + rp.key = password; + + return sjcl.codec.utf8String.fromBits(ct); + }, + + /** Encode a flat structure into a JSON string. + * @param {Object} obj The structure to encode. + * @return {String} A JSON string. + * @throws {sjcl.exception.invalid} if obj has a non-alphanumeric property. + * @throws {sjcl.exception.bug} if a parameter has an unsupported type. + */ + encode: function (obj) { + var i, out='{', comma=''; + for (i in obj) { + if (obj.hasOwnProperty(i)) { + if (!i.match(/^[a-z0-9]+$/i)) { + throw new sjcl.exception.invalid("json encode: invalid property name"); + } + out += comma + i + ':'; + comma = ','; + + switch (typeof obj[i]) { + case 'number': + case 'boolean': + out += obj[i]; + break; + + case 'string': + out += '"' + escape(obj[i]) + '"'; + break; + + case 'object': + out += '"' + sjcl.codec.base64.fromBits(obj[i],1) + '"'; + break; + + default: + throw new sjcl.exception.bug("json encode: unsupported type"); + } + } + } + return out+'}'; + }, + + /** Decode a simple (flat) JSON string into a structure. The ciphertext, + * adata, salt and iv will be base64-decoded. + * @param {String} str The string. + * @return {Object} The decoded structure. + * @throws {sjcl.exception.invalid} if str isn't (simple) JSON. + */ + decode: function (str) { + str = str.replace(/\s/g,''); + if (!str.match(/^\{.*\}$/)) { + throw new sjcl.exception.invalid("json decode: this isn't json!"); + } + var a = str.replace(/^\{|\}$/g, '').split(/,/), out={}, i, m; + for (i=0; i bs) { + key = Hash.hash(key); + } + + for (i=0; i>>31, + x[1]<<1 ^ x[2]>>>31, + x[2]<<1 ^ x[3]>>>31, + x[3]<<1 ^ (x[0]>>>31)*0x87]; + } +}; diff --git a/core/pbkdf2.js b/core/pbkdf2.js new file mode 100644 index 00000000..869e4dfa --- /dev/null +++ b/core/pbkdf2.js @@ -0,0 +1,54 @@ +/** @fileOverview Password-based key-derivation function, version 2.0. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +/** Password-Based Key-Derivation Function, version 2.0. + * + * Generate keys from passwords using PBKDF2-HMAC-SHA256. + * + * This is the method specified by RSA's PKCS #5 standard. + * + * @param {bitArray|String} password The password. + * @param {bitArray} salt The salt. Should have lots of entropy. + * @param {Number} [count=1000] The number of iterations. Higher numbers make the function slower but more secure. + * @param {Number} [length] The length of the derived key. Defaults to the + output size of the hash function. + * @param {Object} [Prff=sjcl.misc.hmac] The pseudorandom function family. + * @return {bitArray} the derived key. + */ +sjcl.misc.pbkdf2 = function (password, salt, count, length, Prff) { + count = count || 1000; + + if (length < 0 || count < 0) { + throw sjcl.exception.invalid("invalid params to pbkdf2"); + } + + if (typeof password === "string") { + password = sjcl.codec.utf8String.toBits(password); + } + + Prff = Prff || sjcl.misc.hmac; + + var prf = new Prff(password), + u, ui, i, j, k, out = [], b = sjcl.bitArray; + + for (k = 1; 32 * out.length < (length || 1); k++) { + u = ui = prf.encrypt(b.concat(salt,[k])); + + for (i=1; i + * This random number generator is a derivative of Ferguson and Schneier's + * generator Fortuna. It collects entropy from various events into several + * pools, implemented by streaming SHA-256 instances. It differs from + * ordinary Fortuna in a few ways, though. + *

+ * + *

+ * Most importantly, it has an entropy estimator. This is present because + * there is a strong conflict here between making the generator available + * as soon as possible, and making sure that it doesn't "run on empty". + * In Fortuna, there is a saved state file, and the system is likely to have + * time to warm up. + *

+ * + *

+ * Second, because users are unlikely to stay on the page for very long, + * and to speed startup time, the number of pools increases logarithmically: + * a new pool is created when the previous one is actually used for a reseed. + * This gives the same asymptotic guarantees as Fortuna, but gives more + * entropy to early reseeds. + *

+ * + *

+ * The entire mechanism here feels pretty klunky. Furthermore, there are + * several improvements that should be made, including support for + * dedicated cryptographic functions that may be present in some browsers; + * state files in local storage; cookies containing randomness; etc. So + * look for improvements in future versions. + *

+ */ +sjcl.random = { + /** Generate several random words, and return them in an array + * @param {Number} nwords The number of words to generate. + */ + randomWords: function (nwords, paranoia) { + var out = [], i, readiness = this.isReady(paranoia), g; + + if (readiness === this._NOT_READY) { + throw new sjcl.exception.notready("generator isn't seeded"); + } else if (readiness & this._REQUIRES_RESEED) { + this._reseedFromPools(!(readiness & this._READY)); + } + + for (i=0; i0) { + estimatedEntropy++; + tmp = tmp >>> 1; + } + } + } + this._pools[robin].update([id,this._eventId++,ty||2,estimatedEntropy,t,data.length].concat(data)); + break; + + case "string": + if (estimatedEntropy === undefined) { + /* English text has just over 1 bit per character of entropy. + * But this might be HTML or something, and have far less + * entropy than English... Oh well, let's just say one bit. + */ + estimatedEntropy = data.length; + } + this._pools[robin].update([id,this._eventId++,3,estimatedEntropy,t,data.length]); + this._pools[robin].update(data); + break; + + default: + + throw new sjcl.exception.bug("random: addEntropy only supports number, array or string"); + } + + /* record the new strength */ + this._poolEntropy[robin] += estimatedEntropy; + this._poolStrength += estimatedEntropy; + + /* fire off events */ + if (oldReady === this._NOT_READY) { + if (this.isReady() !== this._NOT_READY) { + this._fireEvent("seeded", Math.max(this._strength, this._poolStrength)); + } + this._fireEvent("progress", this.getProgress()); + } + }, + + /** Is the generator ready? */ + isReady: function (paranoia) { + var entropyRequired = this._PARANOIA_LEVELS[ (paranoia !== undefined) ? paranoia : this._defaultParanoia ]; + + if (this._strength && this._strength >= entropyRequired) { + return (this._poolEntropy[0] > this._BITS_PER_RESEED && (new Date()).valueOf() > this._nextReseed) ? + this._REQUIRES_RESEED | this._READY : + this._READY; + } else { + return (this._poolStrength >= entropyRequired) ? + this._REQUIRES_RESEED | this._NOT_READY : + this._NOT_READY; + } + }, + + /** Get the generator's progress toward readiness, as a fraction */ + getProgress: function (paranoia) { + var entropyRequired = this._PARANOIA_LEVELS[ paranoia ? paranoia : this._defaultParanoia ]; + + if (this._strength >= entropyRequired) { + return 1.0; + } else { + return (this._poolStrength > entropyRequired) ? + 1.0 : + this._poolStrength / entropyRequired; + } + }, + + /** start the built-in entropy collectors */ + startCollectors: function () { + if (this._collectorsStarted) { return; } + + if (window.addEventListener) { + window.addEventListener("load", this._loadTimeCollector, false); + window.addEventListener("mousemove", this._mouseCollector, false); + } else if (document.attachEvent) { + document.attachEvent("onload", this._loadTimeCollector); + document.attachEvent("onmousemove", this._mouseCollector); + } + else { + throw new sjcl.exception.bug("can't attach event"); + } + + this._collectorsStarted = true; + }, + + /** stop the built-in entropy collectors */ + stopCollectors: function () { + if (!this._collectorsStarted) { return; } + + if (window.removeEventListener) { + window.removeEventListener("load", this._loadTimeCollector); + window.removeEventListener("mousemove", this._mouseCollector); + } else if (window.detachEvent) { + window.detachEvent("onload", this._loadTimeCollector); + window.detachEvent("onmousemove", this._mouseCollector); + } + this._collectorsStarted = false; + }, + + /* use a cookie to store entropy. + useCookie: function (all_cookies) { + throw new sjcl.exception.bug("random: useCookie is unimplemented"); + },*/ + + /** add an event listener for progress or seeded-ness. */ + addEventListener: function (name, callback) { + this._callbacks[name][this._callbackI++] = callback; + }, + + /** remove an event listener for progress or seeded-ness */ + removeEventListener: function (name, cb) { + var i, j, cbs=this._callbacks[name], jsTemp=[]; + + /* I'm not sure if this is necessary; in C++, iterating over a + * collection and modifying it at the same time is a no-no. + */ + + for (j in cbs) { + if (cbs.hasOwnProperty[j] && cbs[j] === cb) { + jsTemp.push(j); + } + } + + for (i=0; i= 1 << this._pools.length) { + this._pools.push(new sjcl.hash.sha256()); + this._poolEntropy.push(0); + } + + /* how strong was this reseed? */ + this._poolStrength -= strength; + if (strength > this._strength) { + this._strength = strength; + } + + this._reseedCount ++; + this._reseed(reseedData); + }, + + _mouseCollector: function (ev) { + var x = ev.x || ev.clientX || ev.offsetX, y = ev.y || ev.clientY || ev.offsetY; + sjcl.random.addEntropy([x,y], 2, "mouse"); + }, + + _loadTimeCollector: function (ev) { + var d = new Date(); + sjcl.random.addEntropy(d, 2, "loadtime"); + }, + + _fireEvent: function (name, arg) { + var j, cbs=sjcl.random._callbacks[name], cbsTemp=[]; + /* TODO: there is a race condition between removing collectors and firing them */ + + /* I'm not sure if this is necessary; in C++, iterating over a + * collection and modifying it at the same time is a no-no. + */ + + for (j in cbs) { + if (cbs.hasOwnProperty(j)) { + cbsTemp.push(cbs[j]); + } + } + + for (j=0; j>>7 ^ a>>>18 ^ a>>>3 ^ a<<25 ^ a<<14) + + (b>>>17 ^ b>>>19 ^ b>>>10 ^ b<<15 ^ b<<13) + + w[i&15] + w[(i+9) & 15]) | 0; + } + + tmp = (tmp + h7 + (h4>>>6 ^ h4>>>11 ^ h4>>>25 ^ h4<<26 ^ h4<<21 ^ h4<<7) + (h6 ^ h4&(h5^h6)) + k[i]); // | 0; + + // shift register + h7 = h6; h6 = h5; h5 = h4; + h4 = h3 + tmp | 0; + h3 = h2; h2 = h1; h1 = h0; + + h0 = (tmp + ((h1&h2) ^ (h3&(h1^h2))) + (h1>>>2 ^ h1>>>13 ^ h1>>>22 ^ h1<<30 ^ h1<<19 ^ h1<<10)) | 0; + } + + h[0] = h[0]+h0 | 0; + h[1] = h[1]+h1 | 0; + h[2] = h[2]+h2 | 0; + h[3] = h[3]+h3 | 0; + h[4] = h[4]+h4 | 0; + h[5] = h[5]+h5 | 0; + h[6] = h[6]+h6 | 0; + h[7] = h[7]+h7 | 0; + } +}; + + diff --git a/core/sjcl.js b/core/sjcl.js new file mode 100644 index 00000000..e3644217 --- /dev/null +++ b/core/sjcl.js @@ -0,0 +1,60 @@ +/** @fileOverview Javascript cryptography implementation. + * + * Crush to remove comments, shorten variable names and + * generally reduce transmission size. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +"use strict"; +/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */ +/*global document, window, escape, unescape */ + +/** @namespace The Stanford Javascript Crypto Library, top-level namespace. */ +var sjcl = { + /** @namespace Symmetric ciphers. */ + cipher: {}, + + /** @namespace Hash functions. Right now only SHA256 is implemented. */ + hash: {}, + + /** @namespace Block cipher modes of operation. */ + mode: {}, + + /** @namespace Miscellaneous. HMAC and PBKDF2. */ + misc: {}, + + /** + * @namespace Bit array encoders and decoders. + * + * @description + * The members of this namespace are functions which translate between + * SJCL's bitArrays and other objects (usually strings). Because it + * isn't always clear which direction is encoding and which is decoding, + * the method names are "fromBits" and "toBits". + */ + codec: {}, + + /** @namespace Exceptions. */ + exception: { + /** @class Ciphertext is corrupt. */ + corrupt: function(message) { + this.toString = function() { return "CORRUPT: "+this.message; }; + this.message = message; + }, + + /** @class Invalid parameter. */ + invalid: function(message) { + this.toString = function() { return "INVALID: "+this.message; }; + this.message = message; + }, + + /** @class Bug or missing feature in SJCL. */ + bug: function(message) { + this.toString = function() { return "BUG: "+this.message; }; + this.message = message; + } + } +}; diff --git a/demo/alpha-arrow.png b/demo/alpha-arrow.png new file mode 100644 index 00000000..e4334a7d Binary files /dev/null and b/demo/alpha-arrow.png differ diff --git a/demo/example.css b/demo/example.css new file mode 100644 index 00000000..7fcfb737 --- /dev/null +++ b/demo/example.css @@ -0,0 +1,179 @@ +* { + margin: 0px; + padding: 0px; + font-family: Arial, Helvetica, FreeSans, sans; +} + +h1 { + text-align: center; + background: #eee; + padding: 5px; + margin-bottom: 0.6em; + font-size: 1.5em; +} + +.header { + width: 650px; + margin: 0px auto 1em; +} + +p+p { + margin-top: 1em; +} + +.explanation { + color: #555; + margin-top: 0.3em; +} + +.section+.section, .explanation+.section { + margin-top: 1.5em; +} + +.hex { + text-transform: uppercase; +} + +.hex, .base64, #ciphertext { + font-family: 'Courier', mono; +} + +.wide, textarea { + width: 100%; + margin: 0px -4px; + font-size: inherit; + text-align: left; +} + +textarea+*, .wide+* { + margin-top: 0.3em; +} + +/* bulk object placement */ +#theForm { + position: relative; + width: 940px; + margin: 0px auto; + font-size: 0.8em; +} + +.column { + top: 0px; + width: 300px; +} + +.box { + border: 2px solid #999; + padding: 7px; + margin-bottom: 20px; + -moz-border-radius: 7px; + -webkit-border-radius: 7px; +} + +#cmode { position: absolute; left: 640px; } +#ctexts { position: absolute; left: 320px; } + +.floatright { + float: right; + text-align: right; +} + +a { + cursor: pointer; + color: #282; +} + +a.random, #buttons a { text-decoration: none; } +a.random:hover, a.random:focus { text-decoration: underline; } + +h2 { + margin: -7px -7px 3px -7px; + text-align: center; + font-size: 1.2em; + color: white; + background: #999; +} + +#pplaintext { border-color: #f65; } +#pplaintext h2 { background: #f65; } + +#ppassword { border-color: #4a4; } +#ppassword h2 { background: #4a4; } + +#pciphertext { border-color: #78f; } +#pciphertext h2 { background: #78f; } + +#buttons { text-align: center; margin-top: -20px; } + +a#doPbkdf2, a#encrypt, a#decrypt { + display: inline-block; + text-align: center; + height: 43px; + padding-top: 20px; + width: 50px; + background: url('alpha-arrow.png') no-repeat bottom center; + vertical-align: middle; + border: none; + color: white; + overflow: hidden; +} + +.turnDown { + display: inline-block; + padding-bottom: 3px; + -moz-transform: rotate(90deg); + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + background-color: inherit; +} + +.turnUp { + display: inline-block; + padding-bottom: 3px; + -moz-transform: rotate(-90deg); + -webkit-transform: rotate(-90deg); + transform: rotate(-90deg); + background-color: inherit; +} + +.buttons a.disabled { + background-color: #ccc ! important; + cursor: inherit ! important; +} + +a#encrypt { background-color: #f65; margin-bottom: 2px; } +a#encrypt:hover, a#encrypt:focus { background-color: #f76; } +a#encrypt:active { background-color: #f87; } + +a#decrypt { + height: 36px; + padding-top: 27px; + background: url('alpha-arrow.png') no-repeat top center; + background-color: #78f; + margin-top: 2px; +} +a#decrypt:hover { background-color: #89f; } +a#decrypt:focus { background-color: #89f; } +a#decrypt:active { background-color: #9af; } + +#ppassword, #pkey, #pmode, #pplaintext, #pciphertext { + -moz-border-radius: 7px; + -webkit-border-radius: 7px; +} +input[type='text'], input[type='password'], textarea { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + font-size: inherit; + border: 1px solid #444; + padding: 3px; +} + +input[type='text']:focus, input[type='password']:focus, textarea:focus { + border-color: red; +} + +input[type="radio"], input[type="checkbox"] { + position: relative; + top: 0.15em; + margin-right: -0.15em; +} diff --git a/demo/example.js b/demo/example.js new file mode 100644 index 00000000..99363d5d --- /dev/null +++ b/demo/example.js @@ -0,0 +1,153 @@ +/* keep track of which salts have been used. */ +var form, usedIvs = {'':1}, usedSalts = {'':1}; + +/* enter actions */ +var enterActions = { + password: doPbkdf2, + salt: doPbkdf2, + iter: doPbkdf2 +}; + +function loaded() { + form = new formHandler('theForm', enterActions); + form._extendedKey = []; + sjcl.random.startCollectors(); + document.getElementById("password").focus(); +} + +/* there's probaby a better way to tell the user something, but oh well */ +function error(x) { + alert(x); +} + +/* compute PBKDF2 on the password. */ +function doPbkdf2(decrypting) { + var v = form.get(), salt=v.salt, key, hex = sjcl.codec.hex.fromBits, p={}, + password = v.password; + + p.iter = v.iter; + + if (password.length == 0) { + if (decrypting) { error("Can't decrypt: need a password!"); } + return; + } + + if (salt.length === 0 && decrypting) { + error("Can't decrypt: need a salt for PBKDF2!"); + return; + } + + if (decrypting || !v.freshsalt || !usedSalts[v.salt]) { + p.salt = v.salt; + } + + p = sjcl.misc.cachedPbkdf2(password, p); + form._extendedKey = p.key; + v.key = p.key.slice(0, v.keysize/32); + v.salt = p.salt; + + form.set(v); + form.plaintext.el.select(); +} +/* Encrypt a message */ +function doEncrypt() { + var v = form.get(), iv = v.iv, password = v.password, key = v.key, adata = v.adata, aes, plaintext=v.plaintext, rp = {}, ct, p; + + if (plaintext === '' && v.ciphertext.length) { return; } + if (key.length == 0 && password.length == 0) { + error("need a password or key!"); + return; + } + + p = { adata:v.adata, + iter:v.iter, + mode:v.mode, + ts:parseInt(v.tag), + ks:parseInt(v.keysize) }; + if (!v.freshiv || !usedIvs[v.iv]) { iv:v.iv; } + if (!v.freshsalt || !usedSalts[v.salt]) { p.salt = v.salt; } + ct = sjcl.encrypt(password || key, plaintext, p, rp).replace(/,/g,",\n"); + + v.iv = rp.iv; + usedIvs[rp.iv] = 1; + if (rp.salt) { + v.salt = rp.salt; + usedSalts[rp.salt] = 1; + } + v.key = rp.key; + + if (v.json) { + v.ciphertext = ct; + v.adata = ''; + } else { + v.ciphertext = ct.match(/ct:"([^"]*)"/)[1]; //" + } + + v.plaintext = ''; + + form.set(v); + form.ciphertext.el.select(); +} + +/* Decrypt a message */ +function doDecrypt() { + var v = form.get(), iv = v.iv, key = v.key, adata = v.adata, aes, ciphertext=v.ciphertext, rp = {}; + + if (ciphertext.length === 0) { return; } + if (!v.password && !v.key.length) { + error("Can't decrypt: need a password or key!"); return; + } + + if (ciphertext.match("{")) { + /* it's jsonized */ + try { + v.plaintext = sjcl.decrypt(v.password || v.key, ciphertext, {}, rp); + } catch(e) { + error("Can't decrypt: "+e); + return; + } + v.mode = rp.mode; + v.iv = rp.iv; + v.adata = rp.adata; + if (v.password) { + v.salt = rp.salt; + v.iter = rp.iter; + v.keysize = rp.ks; + v.tag = rp.ts; + } + v.key = rp.key; + v.ciphertext = ""; + document.getElementById('plaintext').select(); + } else { + /* it's raw */ + ciphertext = sjcl.codec.base64.toBits(ciphertext); + if (iv.length === 0) { + error("Can't decrypt: need an IV!"); return; + } + if (key.length === 0) { + if (v.password.length) { + doPbkdf2(true); + key = v.key; + } + } + aes = new sjcl.cipher.aes(key); + + try { + v.plaintext = sjcl.codec.utf8String.fromBits(sjcl.mode[v.mode].decrypt(aes, ciphertext, iv, v.adata, v.tag)); + v.ciphertext = ""; + document.getElementById('plaintext').select(); + } catch (e) { + error("Can't decrypt: " + e); + } + } + form.set(v); +} + +function extendKey(size) { + form.key.set(form._extendedKey.slice(0,size)); +} + +function randomize(field, words, paranoia) { + form[field].set(sjcl.random.randomWords(words, paranoia)); + if (field == 'salt') { form.key.set([]); } +} diff --git a/demo/form.js b/demo/form.js new file mode 100644 index 00000000..0cd4224f --- /dev/null +++ b/demo/form.js @@ -0,0 +1,137 @@ +/* Hackish form handling system. */ +function hasClass(e, cl) { + return (" "+e.className+" ").match(" "+cl+" "); +} + +function stopPropagation(e) { + e.preventDefault && e.preventDefault(); + e.cancelBubble = true; +} + +/* proxy for a form object, with appropriate encoder/decoder */ +function formElement(el) { + this.el = el; +} +formElement.prototype = { + get: function() { + var el = this.el; + if (el.type == "checkbox") { + return el.checked; + } else if (hasClass(el, "numeric")) { + return parseInt(el.value); + } else if (hasClass(el, "hex")) { + return sjcl.codec.hex.toBits(el.value); + } else if (hasClass(el, "base64")) { + return sjcl.codec.base64.toBits(el.value); + } else { + return el.value; + } + }, + + set: function(x) { + var el = this.el; + if (el.type == "checkbox") { + el.checked = x; return; + } else if (hasClass(el, "hex")) { + if (typeof x !== 'string') { + x = sjcl.codec.hex.fromBits(x); + } + x = x.toUpperCase().replace(/ /g,'').replace(/(.{8})/g, "$1 ").replace(/ $/, ''); + } else if (hasClass(el, "base64")) { + if (typeof x !== 'string') { + x = sjcl.codec.base64.fromBits(x); + } + x = x.replace(/\s/g,'').replace(/(.{32})/g, "$1\n").replace(/\n$/, ''); + } + el.value = x; + } +} + +function radioGroup(name) { + this.name = name; +} +radioGroup.prototype = { + get: function() { + var els = document.getElementsByName(this.name), i; + for (i=0; i + + + + SJCL demo + + + + + + +

SJCL demo

+ +
+

This page is a demo of the Stanford Javascript Crypto Library. To get started, just type in a password in the left pane and a secret message in the middle pane, then click "encrypt". Encryption takes place in your browser and we never see the plaintext.

+ +

SJCL has lots of other options, many of which are shown in the grey boxes.

+
+ +
+
+ +
+

Password

+
+ + +

+ Choose a strong, random password. +

+
+
+ +
+

Key Derivation

+
+
+ + random +
+ + + +

+ Salt adds more variability to your key, and prevents attackers + from using rainbow tables to attack it. +

+
+ +
+ + +

+ Strengthening makes it slower to compute the key corresponding to your + password. This makes it take much longer for an attacker to guess it. +

+
+ +
+ Key size: + + + + + + +

+ 128 bits should be secure enough, but you can generate a longer + key if you wish. +

+
+ + +
+
+ + +
+ +

+ This key is computed from your password, salt and strengthening factor. It + will be used internally by the cipher. Instead of using a password, you can + enter a key here directly. If you do, it should be 32, 48 or 64 hexadecimal + digits (128, 192 or 256 bits). +

+
+ +
+
+ + +
+
+

Cipher Parameters

+

+ SJCL encrypts your data with the AES block cipher. +

+
+ Cipher mode: + + + + +

+ The cipher mode is a standard for how to use AES and other + algorithms to encrypt and authenticate your message. + OCB2 mode + is slightly faster and has more features, but + CCM mode has wider + support because it is not patented. +

+
+ +
+
+ + random +
+ + + +

+ The IV needs to be different for every message you send. It adds + randomness to your message, so that the same message will look + different each time you send it. +

+

+ Be careful: CCM mode doesn't use + the whole IV, so changing just part of it isn't enough. +

+
+ +
+ Authentication strength: + + + + + + +

+ SJCL adds a an authentication tag to your message to make sure + nobody changes it. The longer the authentication tag, the harder it is + for somebody to change your encrypted message without you noticing. 64 + bits is probably enough. +

+
+ +
+ + +

+ These parameters are required to decrypt your message later. If the + person you're sending the message to knows them, you don't need to send + them so your message will be shorter. +

+

+ Default parameters won't be sent. Your password won't be sent, either. + The salt and iv will be encoded in base64 instead of hex, so they'll + look different from what's in the box. +

+
+
+
+ +
+
+

Plaintext

+
+ + +
+ This message will be encrypted, so that nobody can read it or change it + without your password. +
+
+ +
+ + +
+ This auxilliary message isn't secret, but its integrity will be checked + along with the integrity of the message. +
+
+
+ + + +
+

Ciphertext

+ + +
+ Your message, encrypted and authenticated so that nobody can read it + or change it without your password. +
+
+ + + diff --git a/jsdoc_toolkit-2.3.3-beta/README.txt b/jsdoc_toolkit-2.3.3-beta/README.txt new file mode 100644 index 00000000..3782da88 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/README.txt @@ -0,0 +1,183 @@ +====================================================================== + +DESCRIPTION: + +This is the source code for JsDoc Toolkit, an automatic documentation +generation tool for JavaScript. It is written in JavaScript and is run +from a command line (or terminal) using Java and Mozilla's Rhino +JavaScript runtime engine. + +Using this tool you can automatically turn JavaDoc-like comments in +your JavaScript source code into published output files, such as HTML +or XML. + +For more information, to report a bug, or to browse the technical +documentation for this tool please visit the official JsDoc Toolkit +project homepage at http://code.google.com/p/jsdoc-toolkit/ + +For the most up-to-date documentation on JsDoc Toolkit see the +official wiki at http://code.google.com/p/jsdoc-toolkit/w/list + +====================================================================== + +REQUIREMENTS: + +JsDoc Toolkit is known to work with: +java version "1.6.0_03" +Java(TM) SE Runtime Environment (build 1.6.0_03-b05) +on Windows XP, +and java version "1.5.0_19" +Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_19-b02-304) +on Mac OS X 10.5. + +Other versions of java may or may not work with JsDoc Toolkit. + +====================================================================== + +USAGE: + +Running JsDoc Toolkit requires you to have Java installed on your +computer. For more information see http://www.java.com/getjava/ + +Before running the JsDoc Toolkit app you should change your current +working directory to the jsdoc-toolkit folder. Then follow the +examples below, or as shown on the project wiki. + +On a computer running Windows a valid command line to run JsDoc +Toolkit might look like this: + +> java -jar jsrun.jar app\run.js -a -t=templates\jsdoc mycode.js + +On Mac OS X or Linux the same command would look like this: + +$ java -jar jsrun.jar app/run.js -a -t=templates/jsdoc mycode.js + +The above assumes your current working directory contains jsrun.jar, +the "app" and "templates" subdirectories from the standard JsDoc +Toolkit distribution and that the relative path to the code you wish +to document is "mycode.js". + +The output documentation files will be saved to a new directory named +"out" (by default) in the current directory, or if you specify a +-d=somewhere_else option, to the somewhere_else directory. + +For help (usage notes) enter this on the command line: + +$ java -jar jsrun.jar app/run.js --help + +More information about the various command line options used by JsDoc +Toolkit are available on the project wiki. + +====================================================================== + +RUNNING VIA SHELL SCRIPT + +Avi Deitcher has contributed the file jsrun.sh with the following usage notes: + +A script to simplify running jsdoc from the command-line, especially when +running from within a development or build environment such as ant. + +Normally, to run jsdoc, you need a command-line as the following: +java -Djsdoc.dir=/some/long/dir/path/to/jsdoc -jar +/some/long/dir/path/to/jsdoc/jsrun.jar /some/long/dir/path/to/jsdoc/app/run.js +-t=template -r=4 /some/long/dir/path/to/my/src/code + +This can get tedious to redo time and again, and difficult to use from within a build environment. + +To simplify the process, jsrun.sh will automatically run this path, as well as passing through any arguments. + +Usage: jsrun.sh + +All will be passed through. +Additionally, jsrun.sh will take the following actions: +1) If the environment variable JSDOCDIR is set, it will add +"-Djsdoc.dir=$JSDOCDIR" to the command-line +2) If the environment variable JSDOCTEMPLATEDIR is set, it will add +"-Djsdoc.template.dir=$JSDOCTEMPLATEDIR" to the command-line +3) java with the appropriate path to jsrun.jar and run.js will be instantiated + +If not variables are set, it is assumed that the path to jsrun.jar and app/ is in the current working directory. + +Example: +# jsrun.sh ./src/ +Assuming JSDOCDIR=/some/path/to/my/jsdoc will cause the following command to +execute: +java -Djsdoc.dir=/some/path/to/my/jsdoc -jar /some/path/to/my/jsdoc/jsrun.jar +/some/path/to/my/jsdoc/app/run.js ./src/ + +====================================================================== + +TESTING: + +To run the suite of unit tests included with JsDoc Toolkit enter this +on the command line: + +$ java -jar jsrun.jar app/run.js -T + +To see a dump of the internal data structure that JsDoc Toolkit has +built from your source files use this command: + +$ java -jar jsrun.jar app/run.js mycode.js -Z + +====================================================================== + +LICENSE: + +JSDoc.pm + +This project is based on the JSDoc.pm tool, created by Michael +Mathews and Gabriel Reid. More information on JsDoc.pm can +be found on the JSDoc.pm homepage: http://jsdoc.sourceforge.net/ + +Complete documentation on JsDoc Toolkit can be found on the project +wiki at http://code.google.com/p/jsdoc-toolkit/w/list + +Rhino + +Rhino (JavaScript in Java) is open source and licensed by Mozilla +under the MPL 1.1 or later/GPL 2.0 or later licenses, the text of +which is available at http://www.mozilla.org/MPL/ + +You can obtain the source code for Rhino from the Mozilla web site at +http://www.mozilla.org/rhino/download.html + +JsDoc Toolkit is a larger work that uses the Rhino JavaScript engine +but is not derived from it in any way. The Rhino library is used +without modification and without any claims whatsoever. + +The Rhino Debugger + +You can obtain more information about the Rhino Debugger from the +Mozilla web site at http://www.mozilla.org/rhino/debugger.html + +JsDoc Toolkit is a larger work that uses the Rhino Debugger but +is not derived from it in any way. The Rhino Debugger is used +without modification and without any claims whatsoever. + +JsDoc Toolkit + +All code specific to JsDoc Toolkit are free, open source and licensed +for use under the X11/MIT License. + +JsDoc Toolkit is Copyright (c)2009 Michael Mathews + +This program is free software; you can redistribute it and/or +modify it under the terms below. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: The above copyright notice and this +permission notice must be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/jsdoc_toolkit-2.3.3-beta/app/frame.js b/jsdoc_toolkit-2.3.3-beta/app/frame.js new file mode 100644 index 00000000..1beb4055 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/frame.js @@ -0,0 +1,33 @@ +IO.include("frame/Opt.js"); +IO.include("frame/Chain.js"); +IO.include("frame/Link.js"); +IO.include("frame/String.js"); +IO.include("frame/Hash.js"); +IO.include("frame/Namespace.js"); +//IO.include("frame/Reflection.js"); + +/** A few helper functions to make life a little easier. */ + +function defined(o) { + return (o !== undefined); +} + +function copy(o) { // todo check for circular refs + if (o == null || typeof(o) != 'object') return o; + var c = new o.constructor(); + for(var p in o) c[p] = copy(o[p]); + return c; +} + +function isUnique(arr) { + var l = arr.length; + for(var i = 0; i < l; i++ ) { + if (arr.lastIndexOf(arr[i]) > i) return false; + } + return true; +} + +/** Returns the given string with all regex meta characters backslashed. */ +RegExp.escapeMeta = function(str) { + return str.replace(/([$^\\\/()|?+*\[\]{}.-])/g, "\\$1"); +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/frame/Chain.js b/jsdoc_toolkit-2.3.3-beta/app/frame/Chain.js new file mode 100644 index 00000000..506469d1 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/frame/Chain.js @@ -0,0 +1,102 @@ +/**@constructor*/ +function ChainNode(object, link) { + this.value = object; + this.link = link; // describes this node's relationship to the previous node +} + +/**@constructor*/ +function Chain(valueLinks) { + this.nodes = []; + this.cursor = -1; + + if (valueLinks && valueLinks.length > 0) { + this.push(valueLinks[0], "//"); + for (var i = 1, l = valueLinks.length; i < l; i+=2) { + this.push(valueLinks[i+1], valueLinks[i]); + } + } +} + +Chain.prototype.push = function(o, link) { + if (this.nodes.length > 0 && link) this.nodes.push(new ChainNode(o, link)); + else this.nodes.push(new ChainNode(o)); +} + +Chain.prototype.unshift = function(o, link) { + if (this.nodes.length > 0 && link) this.nodes[0].link = link; + this.nodes.unshift(new ChainNode(o)); + this.cursor++; +} + +Chain.prototype.get = function() { + if (this.cursor < 0 || this.cursor > this.nodes.length-1) return null; + return this.nodes[this.cursor]; +} + +Chain.prototype.first = function() { + this.cursor = 0; + return this.get(); +} + +Chain.prototype.last = function() { + this.cursor = this.nodes.length-1; + return this.get(); +} + +Chain.prototype.next = function() { + this.cursor++; + return this.get(); +} + +Chain.prototype.prev = function() { + this.cursor--; + return this.get(); +} + +Chain.prototype.toString = function() { + var string = ""; + for (var i = 0, l = this.nodes.length; i < l; i++) { + if (this.nodes[i].link) string += " -("+this.nodes[i].link+")-> "; + string += this.nodes[i].value.toString(); + } + return string; +} + +Chain.prototype.joinLeft = function() { + var result = ""; + for (var i = 0, l = this.cursor; i < l; i++) { + if (result && this.nodes[i].link) result += this.nodes[i].link; + result += this.nodes[i].value.toString(); + } + return result; +} + + +/* USAGE: + +var path = "one/two/three.four/five-six"; +var pathChain = new Chain(path.split(/([\/.-])/)); +print(pathChain); + +var lineage = new Chain(); +lineage.push("Port"); +lineage.push("Les", "son"); +lineage.push("Dawn", "daughter"); +lineage.unshift("Purdie", "son"); + +print(lineage); + +// walk left +for (var node = lineage.last(); node !== null; node = lineage.prev()) { + print("< "+node.value); +} + +// walk right +var node = lineage.first() +while (node !== null) { + print(node.value); + node = lineage.next(); + if (node && node.link) print("had a "+node.link+" named"); +} + +*/ \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/frame/Dumper.js b/jsdoc_toolkit-2.3.3-beta/app/frame/Dumper.js new file mode 100644 index 00000000..d8b007b1 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/frame/Dumper.js @@ -0,0 +1,144 @@ +/** + * @class +
+This is a lightly modified version of Kevin Jones' JavaScript
+library Data.Dump. To download the original visit:
+    http://openjsan.org/doc/k/ke/kevinj/Data/Dump/
+
+AUTHORS
+
+The Data.Dump JavaScript module is written by Kevin Jones 
+(kevinj@cpan.org), based on Data::Dump by Gisle Aas (gisle@aas.no),
+based on Data::Dumper by Gurusamy Sarathy (gsar@umich.edu).
+
+COPYRIGHT
+
+Copyright 2007 Kevin Jones. Copyright 1998-2000,2003-2004 Gisle Aas.
+Copyright 1996-1998 Gurusamy Sarathy.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the Perl Artistic License
+
+See http://www.perl.com/perl/misc/Artistic.html
+
+ * @static + */ +Dumper = { + /** @param [...] The objects to dump. */ + dump: function () { + if (arguments.length > 1) + return this._dump(arguments); + else if (arguments.length == 1) + return this._dump(arguments[0]); + else + return "()"; + }, + + _dump: function (obj) { + if (typeof obj == 'undefined') return 'undefined'; + var out; + if (obj.serialize) { return obj.serialize(); } + var type = this._typeof(obj); + if (obj.circularReference) obj.circularReference++; + switch (type) { + case 'circular': + out = "{ //circularReference\n}"; + break; + case 'object': + var pairs = new Array; + + for (var prop in obj) { + if (prop != "circularReference" && obj.hasOwnProperty(prop)) { //hide inherited properties + pairs.push(prop + ': ' + this._dump(obj[prop])); + } + } + + out = '{' + this._format_list(pairs) + '}'; + break; + + case 'string': + for (var prop in Dumper.ESC) { + if (Dumper.ESC.hasOwnProperty(prop)) { + obj = obj.replace(prop, Dumper.ESC[prop]); + } + } + + // Escape UTF-8 Strings + if (obj.match(/^[\x00-\x7f]*$/)) { + out = '"' + obj.replace(/\"/g, "\\\"").replace(/([\n\r]+)/g, "\\$1") + '"'; + } + else { + out = "unescape('"+escape(obj)+"')"; + } + break; + + case 'array': + var elems = new Array; + + for (var i=0; i 60 ? '\n' : ' '; + return nl + list.join(',' + nl) + nl; + }, + + _typeof: function (obj) { + if (obj && obj.circularReference && obj.circularReference > 1) return 'circular'; + if (Array.prototype.isPrototypeOf(obj)) return 'array'; + if (Date.prototype.isPrototypeOf(obj)) return 'date'; + if (typeof obj.nodeType != 'undefined') return 'element'; + return typeof(obj); + }, + + _dump_dom: function (obj) { + return '"' + Dumper.nodeTypes[obj.nodeType] + '"'; + } +}; + +Dumper.ESC = { + "\t": "\\t", + "\n": "\\n", + "\f": "\\f" +}; + +Dumper.nodeTypes = { + 1: "ELEMENT_NODE", + 2: "ATTRIBUTE_NODE", + 3: "TEXT_NODE", + 4: "CDATA_SECTION_NODE", + 5: "ENTITY_REFERENCE_NODE", + 6: "ENTITY_NODE", + 7: "PROCESSING_INSTRUCTION_NODE", + 8: "COMMENT_NODE", + 9: "DOCUMENT_NODE", + 10: "DOCUMENT_TYPE_NODE", + 11: "DOCUMENT_FRAGMENT_NODE", + 12: "NOTATION_NODE" +}; \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/frame/Hash.js b/jsdoc_toolkit-2.3.3-beta/app/frame/Hash.js new file mode 100644 index 00000000..62cfad64 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/frame/Hash.js @@ -0,0 +1,84 @@ +/** + @constructor + @example + var _index = new Hash(); + _index.set("a", "apple"); + _index.set("b", "blue"); + _index.set("c", "coffee"); + + for (var p = _index.first(); p; p = _index.next()) { + print(p.key+" is for "+p.value); + } + + */ +var Hash = function() { + this._map = {}; + this._keys = []; + this._vals = []; + this.reset(); +} + +Hash.prototype.set = function(k, v) { + if (k != "") { + this._keys.push(k); + this._map["="+k] = this._vals.length; + this._vals.push(v); + } +} + +Hash.prototype.replace = function(k, k2, v) { + if (k == k2) return; + + var offset = this._map["="+k]; + this._keys[offset] = k2; + if (typeof v != "undefined") this._vals[offset] = v; + this._map["="+k2] = offset; + delete(this._map["="+k]); +} + +Hash.prototype.drop = function(k) { + if (k != "") { + var offset = this._map["="+k]; + this._keys.splice(offset, 1); + this._vals.splice(offset, 1); + delete(this._map["="+k]); + for (var p in this._map) { + if (this._map[p] >= offset) this._map[p]--; + } + if (this._cursor >= offset && this._cursor > 0) this._cursor--; + } +} + +Hash.prototype.get = function(k) { + if (k != "") { + return this._vals[this._map["="+k]]; + } +} + +Hash.prototype.keys = function() { + return this._keys; +} + +Hash.prototype.hasKey = function(k) { + if (k != "") { + return (typeof this._map["="+k] != "undefined"); + } +} + +Hash.prototype.values = function() { + return this._vals; +} + +Hash.prototype.reset = function() { + this._cursor = 0; +} + +Hash.prototype.first = function() { + this.reset(); + return this.next(); +} + +Hash.prototype.next = function() { + if (this._cursor++ < this._keys.length) + return {key: this._keys[this._cursor-1], value: this._vals[this._cursor-1]}; +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/frame/Link.js b/jsdoc_toolkit-2.3.3-beta/app/frame/Link.js new file mode 100644 index 00000000..6133abd1 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/frame/Link.js @@ -0,0 +1,171 @@ +/** Handle the creation of HTML links to documented symbols. + @constructor +*/ +function Link() { + this.alias = ""; + this.src = ""; + this.file = ""; + this.text = ""; + this.innerName = ""; + this.classLink = false; + this.targetName = ""; + + this.target = function(targetName) { + if (defined(targetName)) this.targetName = targetName; + return this; + } + this.inner = function(inner) { + if (defined(inner)) this.innerName = inner; + return this; + } + this.withText = function(text) { + if (defined(text)) this.text = text; + return this; + } + this.toSrc = function(filename) { + if (defined(filename)) this.src = filename; + return this; + } + this.toSymbol = function(alias) { + if (defined(alias)) this.alias = new String(alias); + return this; + } + this.toClass = function(alias) { + this.classLink = true; + return this.toSymbol(alias); + } + this.toFile = function(file) { + if (defined(file)) this.file = file; + return this; + } + + this.toString = function() { + var linkString; + var thisLink = this; + + if (this.alias) { + linkString = this.alias.replace(/(^|[^a-z$0-9_#.:^-])([|a-z$0-9_#.:^-]+)($|[^a-z$0-9_#.:^-])/i, + function(match, prematch, symbolName, postmatch) { + var symbolNames = symbolName.split("|"); + var links = []; + for (var i = 0, l = symbolNames.length; i < l; i++) { + thisLink.alias = symbolNames[i]; + links.push(thisLink._makeSymbolLink(symbolNames[i])); + } + return prematch+links.join("|")+postmatch; + } + ); + } + else if (this.src) { + linkString = thisLink._makeSrcLink(this.src); + } + else if (this.file) { + linkString = thisLink._makeFileLink(this.file); + } + + return linkString; + } +} + +/** prefixed for hashes */ +Link.hashPrefix = ""; + +/** Appended to the front of relative link paths. */ +Link.base = ""; + +Link.symbolNameToLinkName = function(symbol) { + var linker = ""; + if (symbol.isStatic) linker = "."; + else if (symbol.isInner) linker = "-"; + + return Link.hashPrefix+linker+symbol.name; +} + +Link.getSymbol= function(alias) { + var symbol= Link.symbolSet.getSymbol(alias); + + if (symbol) + return symbol; + + if ('#'!==alias.charAt(0) || !Link.currentSymbol) + return null; + + // resolve relative name + var container= Link.currentSymbol; + + while (container) + { + symbol= Link.symbolSet.getSymbol(container.alias + alias); + if (symbol) + return symbol; + + // No superclass + if (!container.augments.length) + return null; + + container= Link.symbolSet.getSymbol(container.augments[0].desc); + } + + return null; +} + +/** Create a link to another symbol. */ +Link.prototype._makeSymbolLink = function(alias) { + var linkBase = Link.base+publish.conf.symbolsDir; + var linkTo = Link.getSymbol(alias); + var linkPath; + var target = (this.targetName)? " target=\""+this.targetName+"\"" : ""; + + // if there is no symbol by that name just return the name unaltered + if (!linkTo) + return this.text || alias; + + // it's a symbol in another file + else { + if (!linkTo.is("CONSTRUCTOR") && !linkTo.isNamespace) { // it's a method or property + linkPath= (Link.filemap) ? Link.filemap[linkTo.memberOf] : + escape(linkTo.memberOf) || "_global_"; + if (linkTo.isEvent) + linkPath += publish.conf.ext + "#event:" + Link.symbolNameToLinkName(linkTo); + else + linkPath += publish.conf.ext + "#" + Link.symbolNameToLinkName(linkTo); + } + else { + linkPath = (Link.filemap)? Link.filemap[linkTo.alias] : escape(linkTo.alias); + linkPath += publish.conf.ext;// + (this.classLink? "":"#" + Link.hashPrefix + "constructor"); + } + linkPath = linkBase + linkPath + } + + var linkText= this.text || alias; + + var link = {linkPath: linkPath, linkText: linkText, linkInner: (this.innerName? "#"+this.innerName : "")}; + + if (typeof JSDOC.PluginManager != "undefined") { + JSDOC.PluginManager.run("onSymbolLink", link); + } + + return ""+link.linkText+""; +} + +/** Create a link to a source file. */ +Link.prototype._makeSrcLink = function(srcFilePath) { + var target = (this.targetName)? " target=\""+this.targetName+"\"" : ""; + + // transform filepath into a filename + var srcFile = srcFilePath.replace(/\.\.?[\\\/]/g, "").replace(/[:\\\/]/g, "_"); + var outFilePath = Link.base + publish.conf.srcDir + srcFile + publish.conf.ext; + + if (!this.text) this.text = FilePath.fileName(srcFilePath); + return ""+this.text+""; +} + +/** Create a link to a source file. */ +Link.prototype._makeFileLink = function(filePath) { + var target = (this.targetName)? " target=\""+this.targetName+"\"" : ""; + + var outFilePath = Link.base + filePath; + + if (!this.text) this.text = filePath; + return ""+this.text+""; +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/frame/Namespace.js b/jsdoc_toolkit-2.3.3-beta/app/frame/Namespace.js new file mode 100644 index 00000000..fa1e41d1 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/frame/Namespace.js @@ -0,0 +1,10 @@ +_global_ = this; + +function Namespace(name, f) { + var n = name.split("."); + for (var o = _global_, i = 0, l = n.length; i < l; i++) { + o = o[n[i]] = o[n[i]] || {}; + } + + if (f) f(); +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/frame/Opt.js b/jsdoc_toolkit-2.3.3-beta/app/frame/Opt.js new file mode 100644 index 00000000..352f1590 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/frame/Opt.js @@ -0,0 +1,134 @@ +/** @namespace */ +Opt = { + /** + * Get commandline option values. + * @param {Array} args Commandline arguments. Like ["-a=xml", "-b", "--class=new", "--debug"] + * @param {object} optNames Map short names to long names. Like {a:"accept", b:"backtrace", c:"class", d:"debug"}. + * @return {object} Short names and values. Like {a:"xml", b:true, c:"new", d:true} + */ + get: function(args, optNames) { + var opt = {"_": []}; // the unnamed option allows multiple values + for (var i = 0; i < args.length; i++) { + var arg = new String(args[i]); + var name; + var value; + if (arg.charAt(0) == "-") { + if (arg.charAt(1) == "-") { // it's a longname like --foo + arg = arg.substring(2); + var m = arg.split("="); + name = m.shift(); + value = m.shift(); + if (typeof value == "undefined") value = true; + + for (var n in optNames) { // convert it to a shortname + if (name == optNames[n]) { + name = n; + } + } + } + else { // it's a shortname like -f + arg = arg.substring(1); + var m = arg.split("="); + name = m.shift(); + value = m.shift(); + if (typeof value == "undefined") value = true; + + for (var n in optNames) { // find the matching key + if (name == n || name+'[]' == n) { + name = n; + break; + } + } + } + if (name.match(/(.+)\[\]$/)) { // it's an array type like n[] + name = RegExp.$1; + if (!opt[name]) opt[name] = []; + } + + if (opt[name] && opt[name].push) { + opt[name].push(value); + } + else { + opt[name] = value; + } + } + else { // not associated with any optname + opt._.push(args[i]); + } + } + return opt; + } +} + +/*t: + plan(11, "Testing Opt."); + + is( + typeof Opt, + "object", + "Opt is an object." + ); + + is( + typeof Opt.get, + "function", + "Opt.get is a function." + ); + + var optNames = {a:"accept", b:"backtrace", c:"class", d:"debug", "e[]":"exceptions"}; + var t_options = Opt.get(["-a=xml", "-b", "--class=new", "--debug", "-e=one", "-e=two", "foo", "bar"], optNames); + + is( + t_options.a, + "xml", + "an option defined with a short name can be accessed by its short name." + ); + + is( + t_options.b, + true, + "an option defined with a short name and no value are true." + ); + + is( + t_options.c, + "new", + "an option defined with a long name can be accessed by its short name." + ); + + is( + t_options.d, + true, + "an option defined with a long name and no value are true." + ); + + is( + typeof t_options.e, + "object", + "an option that can accept multiple values is defined." + ); + + is( + t_options.e.length, + 2, + "an option that can accept multiple values can have more than one value." + ); + + is( + t_options.e[1], + "two", + "an option that can accept multiple values can be accessed as an array." + ); + + is( + typeof t_options._, + "object", + "the property '_' is defined for unnamed options." + ); + + is( + t_options._[0], + "foo", + "the property '_' can be accessed as an array." + ); + */ \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/frame/Reflection.js b/jsdoc_toolkit-2.3.3-beta/app/frame/Reflection.js new file mode 100644 index 00000000..0968f1c6 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/frame/Reflection.js @@ -0,0 +1,26 @@ +/**@constructor*/ +function Reflection(obj) { + this.obj = obj; +} + +Reflection.prototype.getConstructorName = function() { + if (this.obj.constructor.name) return this.obj.constructor.name; + var src = this.obj.constructor.toSource(); + var name = src.substring(name.indexOf("function")+8, src.indexOf('(')).replace(/ /g,''); + return name; +} + +Reflection.prototype.getMethod = function(name) { + for (var p in this.obj) { + if (p == name && typeof(this.obj[p]) == "function") return this.obj[p]; + } + return null; +} + +Reflection.prototype.getParameterNames = function() { + var src = this.obj.toSource(); + src = src.substring( + src.indexOf("(", 8)+1, src.indexOf(")") + ); + return src.split(/, ?/); +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/frame/String.js b/jsdoc_toolkit-2.3.3-beta/app/frame/String.js new file mode 100644 index 00000000..c183c27d --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/frame/String.js @@ -0,0 +1,93 @@ +/** + @name String + @class Additions to the core string object. +*/ + +/** @author Steven Levithan, released as public domain. */ +String.prototype.trim = function() { + var str = this.replace(/^\s+/, ''); + for (var i = str.length - 1; i >= 0; i--) { + if (/\S/.test(str.charAt(i))) { + str = str.substring(0, i + 1); + break; + } + } + return str; +} +/*t: + plan(6, "Testing String.prototype.trim."); + + var s = " a bc ".trim(); + is(s, "a bc", "multiple spaces front and back are trimmed."); + + s = "a bc\n\n".trim(); + is(s, "a bc", "newlines only in back are trimmed."); + + s = "\ta bc".trim(); + is(s, "a bc", "tabs only in front are trimmed."); + + s = "\n \t".trim(); + is(s, "", "an all-space string is trimmed to empty."); + + s = "a b\nc".trim(); + is(s, "a b\nc", "a string with no spaces in front or back is trimmed to itself."); + + s = "".trim(); + is(s, "", "an empty string is trimmed to empty."); + +*/ + +String.prototype.balance = function(open, close) { + var i = 0; + while (this.charAt(i) != open) { + if (i == this.length) return [-1, -1]; + i++; + } + + var j = i+1; + var balance = 1; + while (j < this.length) { + if (this.charAt(j) == open) balance++; + if (this.charAt(j) == close) balance--; + if (balance == 0) break; + j++; + if (j == this.length) return [-1, -1]; + } + + return [i, j]; +} +/*t: + plan(16, "Testing String.prototype.balance."); + + var s = "{abc}".balance("{","}"); + is(s[0], 0, "opener in first is found."); + is(s[1], 4, "closer in last is found."); + + s = "ab{c}de".balance("{","}"); + is(s[0], 2, "opener in middle is found."); + is(s[1], 4, "closer in middle is found."); + + s = "a{b{c}de}f".balance("{","}"); + is(s[0], 1, "nested opener is found."); + is(s[1], 8, "nested closer is found."); + + s = "{}".balance("{","}"); + is(s[0], 0, "opener with no content is found."); + is(s[1], 1, "closer with no content is found."); + + s = "".balance("{","}"); + is(s[0], -1, "empty string opener is -1."); + is(s[1], -1, "empty string closer is -1."); + + s = "{abc".balance("{","}"); + is(s[0], -1, "opener with no closer returns -1."); + is(s[1], -1, "no closer returns -1."); + + s = "abc".balance("{","}"); + is(s[0], -1, "no opener or closer returns -1 for opener."); + is(s[1], -1, "no opener or closer returns -1 for closer."); + + s = "aX11/MIT License + * (See the accompanying README file for full details.) + */ + +/** + Yet another unit testing tool for JavaScript. + @author Michael Mathews micmath@gmail.com + @param {object} testCases Properties are testcase names, values are functions to execute as tests. +*/ +function testrun(testCases) { + var ran = 0; + for (t in testCases) { + var result = testCases[t](); + ran++; + } + + return testrun.reportOut+"-------------------------------\n"+((testrun.fails>0)? ":( Failed "+testrun.fails+"/" : ":) Passed all ")+testrun.count+" test"+((testrun.count == 1)? "":"s")+".\n"; +} + + +testrun.count = 0; +testrun.current = null; +testrun.passes = 0; +testrun.fails = 0; +testrun.reportOut = ""; + +/** @private */ +testrun.report = function(text) { + testrun.reportOut += text+"\n"; +} + +/** + Check if test evaluates to true. + @param {string} test To be evaluated. + @param {string} message Optional. To be displayed in the report. + @return {boolean} True if the string test evaluates to true. +*/ +ok = function(test, message) { + testrun.count++; + + var result; + try { + result = eval(test); + + if (result) { + testrun.passes++; + testrun.report(" OK "+testrun.count+" - "+((message != null)? message : "")); + } + else { + testrun.fails++; + testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : "")); + } + } + catch(e) { + testrun.fails++ + testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : "")); + + } +} + +/** + Check if test is same as expected. + @param {string} test To be evaluated. + @param {string} expected + @param {string} message Optional. To be displayed in the report. + @return {boolean} True if (test == expected). Note that the comparison is not a strict equality check. +*/ +is = function(test, expected, message) { + testrun.count++; + + var result; + try { + result = eval(test); + + if (result == expected) { + testrun.passes++ + testrun.report(" OK "+testrun.count+" - "+((message != null)? message : "")); + } + else { + testrun.fails++ + testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : "")); + testrun.report("expected: "+expected); + testrun.report(" got: "+result); + } + } + catch(e) { + testrun.fails++ + testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : "")); + testrun.report("expected: "+expected); + testrun.report(" got: "+result);} +} + +/** + Check if test matches pattern. + @param {string} test To be evaluated. + @param {string} pattern Used to create a RegExp. + @param {string} message Optional. To be displayed in the report. + @return {boolean} True if test matches pattern. +*/ +like = function(test, pattern, message) { + testrun.count++; + + var result; + try { + result = eval(test); + var rgx = new RegExp(pattern); + + if (rgx.test(result)) { + testrun.passes++ + testrun.report(" OK "+testrun.count+" - "+((message != null)? message : "")); + } + else { + testrun.fails++ + testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : "")); + testrun.report(" this: "+result); + testrun.report("is not like: "+pattern); + } + } + catch(e) { + testrun.fails++ + testrun.report("NOT OK "+testrun.count+" - "+((message != null)? message : "")); + } +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/handlers/FOODOC.js b/jsdoc_toolkit-2.3.3-beta/app/handlers/FOODOC.js new file mode 100644 index 00000000..b208f55b --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/handlers/FOODOC.js @@ -0,0 +1,26 @@ +/** + This is the main container for the FOODOC handler. + @namespace +*/ +FOODOC = { +}; + +/** The current version string of this application. */ +FOODOC.VERSION = "1.0"; + +FOODOC.handle = function(srcFile, src) { + LOG.inform("Handling file '" + srcFile + "'"); + + return [ + new JSDOC.Symbol( + "foo", + [], + "VIRTUAL", + new JSDOC.DocComment("/** This is a foo. */") + ) + ]; +}; + +FOODOC.publish = function(symbolgroup) { + LOG.inform("Publishing symbolgroup."); +}; diff --git a/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC.js b/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC.js new file mode 100755 index 00000000..40f87b35 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC.js @@ -0,0 +1,26 @@ +/** + * This is the main container for the XMLDOC handler. + * @namespace + * @author Brett Fattori (bfattori@fry.com) + * @version $Revision: 498 $ + */ +XMLDOC = { + +}; + +/** The current version string of this application. */ +XMLDOC.VERSION = "1.0"; + +/** Include the library necessary to handle XML files */ +IO.includeDir("handlers/XMLDOC/"); + +/** + * @type Symbol[] + */ +XMLDOC.handle = function(srcFile, src) { + +}; + +XMLDOC.publish = function(symbolgroup) { + +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC/DomReader.js b/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC/DomReader.js new file mode 100755 index 00000000..240563da --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC/DomReader.js @@ -0,0 +1,159 @@ +LOG.inform("XMLDOC.DomReader loaded"); + +XMLDOC.DomReader = function(root) { + + this.dom = root; + + /** + * The current node the reader is on + */ + this.node = root; + + /** + * Get the current node the reader is on + * @type XMLDOC.Parser.node + */ + XMLDOC.DomReader.prototype.getNode = function() { + return this.node; + }; + + /** + * Set the node the reader should be positioned on. + * @param node {XMLDOC.Parser.node} + */ + XMLDOC.DomReader.prototype.setNode = function(node) { + this.node = node; + }; + + /** + * A helper method to make sure the current node will + * never return null, unless null is passed as the root. + * @param step {String} An expression to evaluate - should return a node or null + */ + XMLDOC.DomReader.prototype.navigate = function(step) { + var n; + if ((n = step) != null) + { + this.node = n; + return this.node; + } + return null; + }; + + /** + * Get the root node of the current node's document. + */ + XMLDOC.DomReader.prototype.root = function() { + this.navigate(this.dom); + }; + + /** + * Get the parent of the current node. + */ + XMLDOC.DomReader.prototype.parent = function() { + return this.navigate(this.node.parentNode()); + }; + + /** + * Get the first child of the current node. + */ + XMLDOC.DomReader.prototype.firstChild = function() { + return this.navigate(this.node.firstChild()); + }; + + /** + * Get the last child of the current node. + */ + XMLDOC.DomReader.prototype.lastChild = function() { + return this.navigate(this.node.lastChild()); + }; + + /** + * Get the next sibling of the current node. + */ + XMLDOC.DomReader.prototype.nextSibling = function() { + return this.navigate(this.node.nextSibling()); + }; + + /** + * Get the previous sibling of the current node. + */ + XMLDOC.DomReader.prototype.prevSibling = function() { + return this.navigate(this.node.prevSibling()); + }; + + //=============================================================================================== + // Support methods + + /** + * Walk the tree starting with the current node, calling the plug-in for + * each node visited. Each time the plug-in is called, the DomReader + * is passed as the only parameter. Use the {@link XMLDOC.DomReader#getNode} method + * to access the current node. This method uses a depth first traversal pattern. + * + * @param srcFile {String} The source file being evaluated + */ + XMLDOC.DomReader.prototype.getSymbols = function(srcFile) + { + XMLDOC.DomReader.symbols = []; + XMLDOC.DomReader.currentFile = srcFile; + JSDOC.Symbol.srcFile = (srcFile || ""); + + if (defined(JSDOC.PluginManager)) { + JSDOC.PluginManager.run("onDomGetSymbols", this); + } + + return XMLDOC.DomReader.symbols; + }; + + /** + * Find the node with the given name using a depth first traversal. + * Does not modify the DomReader's current node. + * + * @param name {String} The name of the node to find + * @return the node that was found, or null if not found + */ + XMLDOC.DomReader.prototype.findNode = function(name) + { + var findNode = null; + + // Start at the current node and move into the subtree, + // looking for the node with the given name + function deeper(node, find) + { + var look = null; + + if (node) { + if (node.name == find) + { + return node; + } + + if (node.firstChild()) + { + look = deeper(node.firstChild(), find); + } + + if (!look && node.nextSibling()) + { + look = deeper(node.nextSibling(), find); + } + } + + return look; + } + + return deeper(this.getNode().firstChild(), name); + }; + + /** + * Find the next node with the given name using a depth first traversal. + * + * @param name {String} The name of the node to find + */ + XMLDOC.DomReader.prototype.findPreviousNode = function(name) + { + }; + +}; + diff --git a/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC/XMLDoc.js b/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC/XMLDoc.js new file mode 100755 index 00000000..e9b3e3ce --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC/XMLDoc.js @@ -0,0 +1,16 @@ +LOG.inform("XMLDOC.symbolize loaded"); + +/** + * Convert the source file to a set of symbols + */ +XMLDOC.symbolize = function(srcFile, src) { + + LOG.inform("Symbolizing file '" + srcFile + "'"); + + // XML files already have a defined structure, so we don't need to + // do anything but parse them. The DOM reader can create a symbol + // table from the parsed XML. + var dr = new XMLDOC.DomReader(XMLDOC.Parser.parse(src)); + return dr.getSymbols(srcFile); + +}; diff --git a/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC/XMLParse.js b/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC/XMLParse.js new file mode 100755 index 00000000..78e8f455 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/handlers/XMLDOC/XMLParse.js @@ -0,0 +1,292 @@ +LOG.inform("XMLDOC.Parser loaded"); + +/** + * XML Parser object. Returns an {@link #XMLDOC.Parser.node} which is + * the root element of the parsed document. + *

+ * By default, this parser will only handle well formed XML. To + * allow the parser to handle HTML, set the XMLDOC.Parser.strictMode + * variable to false before calling XMLDOC.Parser.parse(). + *

+ * Note: If you pass poorly formed XML, it will cause the parser to throw + * an exception. + * + * @author Brett Fattori (bfattori@fry.com) + * @author $Author: micmath $ + * @version $Revision: 497 $ + */ +XMLDOC.Parser = {}; + +/** + * Strict mode setting. Setting this to false allows HTML-style source to + * be parsed. Normally, well formed XML has defined end tags, or empty tags + * are properly formed. Default: true + * @type Boolean + */ +XMLDOC.Parser.strictMode = true; + +/** + * A node in an XML Document. Node types are ROOT, ELEMENT, COMMENT, PI, and TEXT. + * @param parent {XMLDOC.Parser.node} The parent node + * @param name {String} The node name + * @param type {String} One of the types + */ +XMLDOC.Parser.node = function(parent, name, type) +{ + this.name = name; + this.type = type || "ELEMENT"; + this.parent = parent; + this.charData = ""; + this.attrs = {}; + this.nodes = []; + this.cPtr = 0; + + XMLDOC.Parser.node.prototype.getAttributeNames = function() { + var a = []; + for (var o in this.attrs) + { + a.push(o); + } + + return a; + }; + + XMLDOC.Parser.node.prototype.getAttribute = function(attr) { + return this.attrs[attr]; + }; + + XMLDOC.Parser.node.prototype.setAttribute = function(attr, val) { + this.attrs[attr] = val; + }; + + XMLDOC.Parser.node.prototype.getChild = function(idx) { + return this.nodes[idx]; + }; + + XMLDOC.Parser.node.prototype.parentNode = function() { + return this.parent; + }; + + XMLDOC.Parser.node.prototype.firstChild = function() { + return this.nodes[0]; + }; + + XMLDOC.Parser.node.prototype.lastChild = function() { + return this.nodes[this.nodes.length - 1]; + }; + + XMLDOC.Parser.node.prototype.nextSibling = function() { + var p = this.parent; + if (p && (p.nodes.indexOf(this) + 1 != p.nodes.length)) + { + return p.getChild(p.nodes.indexOf(this) + 1); + } + return null; + }; + + XMLDOC.Parser.node.prototype.prevSibling = function() { + var p = this.parent; + if (p && (p.nodes.indexOf(this) - 1 >= 0)) + { + return p.getChild(p.nodes.indexOf(this) - 1); + } + return null; + }; +}; + +/** + * Parse an XML Document from the specified source. The XML should be + * well formed, unless strict mode is disabled, then the parser will + * handle HTML-style XML documents. + * @param src {String} The source to parse + */ +XMLDOC.Parser.parse = function(src) +{ + var A = []; + + // Normailize whitespace + A = src.split("\r\n"); + src = A.join("\n"); + A = src.split("\r"); + src = A.join("\n"); + + // Remove XML and DOCTYPE specifier + src.replace(/<\?XML .*\?>/i, ""); + src.replace(//i, ""); + + // The document is the root node and cannot be modified or removed + var doc = new XMLDOC.Parser.node(null, "ROOT", "DOCUMENT"); + + // Let's break it down + XMLDOC.Parser.eat(doc, src); + + return doc; +}; + +/** + * The XML fragment processing routine. This method is private and should not be called + * directly. + * @param parentNode {XMLDOC.Parser.node} The node which is the parent of this fragment + * @param src {String} The source within the fragment to process + * @private + */ +XMLDOC.Parser.eat = function(parentNode, src) +{ + // A simple tag def + var reTag = new RegExp("<(!|)(\\?|--|)((.|\\s)*?)\\2>","g"); + + // Special tag types + var reCommentTag = //; + var rePITag = /<\?((.|\s)*?)\?>/; + + // A start tag (with potential empty marker) + var reStartTag = /<(.*?)( +([\w_\-]*)=(\"|')(.*)\4)*(\/)?>/; + + // An empty HTML style tag (not proper XML, but we'll accept it so we can process HTML) + var reHTMLEmptyTag = /<(.*?)( +([\w_\-]*)=(\"|')(.*)\4)*>/; + + // Fully enclosing tag with nested tags + var reEnclosingTag = /<(.*?)( +([\w_\-]*)=(\"|')(.*?)\4)*>((.|\s)*?)<\/\1>/; + + // Breaks down attributes + var reAttributes = new RegExp(" +([\\w_\\-]*)=(\"|')(.*?)\\2","g"); + + // Find us a tag + var tag; + while ((tag = reTag.exec(src)) != null) + { + if (tag.index > 0) + { + // The next tag has some text before it + var text = src.substring(0, tag.index).replace(/^[ \t\n]+((.|\n)*?)[ \t\n]+$/, "$1"); + + if (text.length > 0 && (text != "\n")) + { + var txtnode = new XMLDOC.Parser.node(parentNode, "", "TEXT"); + txtnode.charData = text; + + // Append the new text node + parentNode.nodes.push(txtnode); + } + + // Reset the lastIndex of reTag + reTag.lastIndex -= src.substring(0, tag.index).length; + + // Eat the text + src = src.substring(tag.index); + } + + if (reCommentTag.test(tag[0])) + { + // Is this a comment? + var comment = new XMLDOC.Parser.node(parentNode, "", "COMMENT"); + comment.charData = reCommentTag.exec(tag[0])[1]; + + // Append the comment + parentNode.nodes.push(comment); + + // Move the lastIndex of reTag + reTag.lastIndex -= tag[0].length; + + // Eat the tag + src = src.replace(reCommentTag, ""); + } + else if (rePITag.test(tag[0])) + { + // Is this a processing instruction? + var pi = new XMLDOC.Parser.node(parentNode, "", "PI"); + pi.charData = rePITag.exec(tag[0])[1]; + + // Append the processing instruction + parentNode.nodes.push(pi); + + // Move the lastIndex of reTag + reTag.lastIndex -= tag[0].length; + + // Eat the tag + src = src.replace(rePITag, ""); + } + else if (reStartTag.test(tag[0])) + { + // Break it down + var e = reStartTag.exec(tag[0]); + var elem = new XMLDOC.Parser.node(parentNode, e[1], "ELEMENT"); + + // Get attributes from the tag + var a; + while ((a = reAttributes.exec(e[2])) != null ) + { + elem.attrs[a[1]] = a[3]; + } + + // Is this an empty XML-style tag? + if (e[6] == "/") + { + // Append the empty element + parentNode.nodes.push(elem); + + // Move the lastIndex of reTag (include the start tag length) + reTag.lastIndex -= e[0].length; + + // Eat the tag + src = src.replace(reStartTag, ""); + } + else + { + // Check for malformed XML tags + var htmlParsed = false; + var htmlStartTag = reHTMLEmptyTag.exec(src); + + // See if there isn't an end tag within this block + var reHTMLEndTag = new RegExp(""); + var htmlEndTag = reHTMLEndTag.exec(src); + + if (XMLDOC.Parser.strictMode && htmlEndTag == null) + { + // Poorly formed XML fails in strict mode + var err = new Error("Malformed XML passed to XMLDOC.Parser... Error contains malformed 'src'"); + err.src = src; + throw err; + } + else if (htmlEndTag == null) + { + // This is an HTML-style empty tag, store the element for it in non-strict mode + parentNode.nodes.push(elem); + + // Eat the tag + src = src.replace(reHTMLEmptyTag, ""); + htmlParsed = true; + } + + // If we didn't parse HTML-style, it must be an enclosing tag + if (!htmlParsed) + { + var enc = reEnclosingTag.exec(src); + + // Go deeper into the document + XMLDOC.Parser.eat(elem, enc[6]); + + // Append the new element node + parentNode.nodes.push(elem); + + // Eat the tag + src = src.replace(reEnclosingTag, ""); + } + } + + // Reset the lastIndex of reTag + reTag.lastIndex = 0; + } + } + + // No tag was found... append the text if there is any + src = src.replace(/^[ \t\n]+((.|\n)*?)[ \t\n]+$/, "$1"); + if (src.length > 0 && (src != "\n")) + { + var txtNode = new XMLDOC.Parser.node(parentNode, "", "TEXT"); + txtNode.charData = src; + + // Append the new text node + parentNode.nodes.push(txtNode); + } +}; diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC.js new file mode 100644 index 00000000..2e0cc206 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC.js @@ -0,0 +1,104 @@ +/** + @overview + @date $Date: 2009-10-28 23:25:32 +0000 (Wed, 28 Oct 2009) $ + @version $Revision: 816 $ + @location $HeadURL: http://jsdoc-toolkit.googlecode.com/svn/trunk/jsdoc-toolkit/app/lib/JSDOC.js $ + @name JSDOC.js + */ + +/** + This is the main container for the JSDOC application. + @namespace +*/ +JSDOC = { +}; + +/** + @requires Opt + */ +if (typeof arguments == "undefined") arguments = []; +JSDOC.opt = Opt.get( + arguments, + { + a: "allfunctions", + c: "conf", + d: "directory", + "D[]": "define", + e: "encoding", + "E[]": "exclude", + h: "help", + n: "nocode", + o: "out", + p: "private", + q: "quiet", + r: "recurse", + S: "securemodules", + s: "suppress", + t: "template", + T: "testmode", + u: "unique", + v: "verbose", + x: "ext" + } +); + +/** The current version string of this application. */ +JSDOC.VERSION = "2.3.3-beta"; + +/** Print out usage information and quit. */ +JSDOC.usage = function() { + print("USAGE: java -jar jsrun.jar app/run.js [OPTIONS] ..."); + print(""); + print("OPTIONS:"); + print(" -a or --allfunctions\n Include all functions, even undocumented ones.\n"); + print(" -c or --conf\n Load a configuration file.\n"); + print(" -d= or --directory=\n Output to this directory (defaults to \"out\").\n"); + print(" -D=\"myVar:My value\" or --define=\"myVar:My value\"\n Multiple. Define a variable, available in JsDoc as JSDOC.opt.D.myVar.\n"); + print(" -e= or --encoding=\n Use this encoding to read and write files.\n"); + print(" -E=\"REGEX\" or --exclude=\"REGEX\"\n Multiple. Exclude files based on the supplied regex.\n"); + print(" -h or --help\n Show this message and exit.\n"); + print(" -n or --nocode\n Ignore all code, only document comments with @name tags.\n"); + print(" -o= or --out=\n Print log messages to a file (defaults to stdout).\n"); + print(" -p or --private\n Include symbols tagged as private, underscored and inner symbols.\n"); + print(" -q or --quiet\n Do not output any messages, not even warnings.\n"); + print(" -r= or --recurse=\n Descend into src directories.\n"); + print(" -s or --suppress\n Suppress source code output.\n"); + print(" -S or --securemodules\n Use Secure Modules mode to parse source code.\n"); + print(" -t= or --template=\n Required. Use this template to format the output.\n"); + print(" -T or --test\n Run all unit tests and exit.\n"); + print(" -u or --unique\n Force file names to be unique, but not based on symbol names.\n"); + print(" -v or --verbose\n Provide verbose feedback about what is happening.\n"); + print(" -x=[,EXT]... or --ext=[,EXT]...\n Scan source files with the given extension/s (defaults to js).\n"); + + quit(); +} + +/*t: + plan(4, "Testing JSDOC namespace."); + + is( + typeof JSDOC, + "object", + "JSDOC.usage is a function." + ); + + is( + typeof JSDOC.VERSION, + "string", + "JSDOC.VERSION is a string." + ); + + is( + typeof JSDOC.usage, + "function", + "JSDOC.usage is a function." + ); + + is( + typeof JSDOC.opt, + "object", + "JSDOC.opt is a object." + ); + */ + +if (this.IO) IO.includeDir("lib/JSDOC/"); diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/DocComment.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/DocComment.js new file mode 100644 index 00000000..4b21cd71 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/DocComment.js @@ -0,0 +1,204 @@ +if (typeof JSDOC == "undefined") JSDOC = {}; + +/** + Create a new DocComment. This takes a raw documentation comment, + and wraps it in useful accessors. + @class Represents a documentation comment object. + */ +JSDOC.DocComment = function(/**String*/comment) { + this.init(); + if (typeof comment != "undefined") { + this.parse(comment); + } +} + +JSDOC.DocComment.prototype.init = function() { + this.isUserComment = true; + this.src = ""; + this.meta = ""; + this.tagTexts = []; + this.tags = []; +} + +/** + @requires JSDOC.DocTag + */ +JSDOC.DocComment.prototype.parse = function(/**String*/comment) { + if (comment == "") { + comment = "/** @desc */"; + this.isUserComment = false; + } + + this.src = JSDOC.DocComment.unwrapComment(comment); + + this.meta = ""; + if (this.src.indexOf("#") == 0) { + this.src.match(/#(.+[+-])([\s\S]*)$/); + if (RegExp.$1) this.meta = RegExp.$1; + if (RegExp.$2) this.src = RegExp.$2; + } + + if (typeof JSDOC.PluginManager != "undefined") { + JSDOC.PluginManager.run("onDocCommentSrc", this); + } + + this.fixDesc(); + + this.src = JSDOC.DocComment.shared+"\n"+this.src; + + this.tagTexts = + this.src + .split(/(^|[\r\n])\s*@/) + .filter(function($){return $.match(/\S/)}); + + /** + The tags found in the comment. + @type JSDOC.DocTag[] + */ + this.tags = this.tagTexts.map(function($){return new JSDOC.DocTag($)}); + + if (typeof JSDOC.PluginManager != "undefined") { + JSDOC.PluginManager.run("onDocCommentTags", this); + } +} + +/*t: + plan(5, "testing JSDOC.DocComment"); + requires("../frame/String.js"); + requires("../lib/JSDOC/DocTag.js"); + + var com = new JSDOC.DocComment("/**@foo some\n* comment here*"+"/"); + is(com.tagTexts[0], "foo some\ncomment here", "first tag text is found."); + is(com.tags[0].title, "foo", "the title is found in a comment with one tag."); + + var com = new JSDOC.DocComment("/** @foo first\n* @bar second*"+"/"); + is(com.getTag("bar").length, 1, "getTag() returns one tag by that title."); + + JSDOC.DocComment.shared = "@author John Smith"; + var com = new JSDOC.DocComment("/**@foo some\n* comment here*"+"/"); + is(com.tags[0].title, "author", "shared comment is added."); + is(com.tags[1].title, "foo", "shared comment is added to existing tag."); +*/ + +/** + If no @desc tag is provided, this function will add it. + */ +JSDOC.DocComment.prototype.fixDesc = function() { + if (this.meta && this.meta != "@+") return; + if (/^\s*[^@\s]/.test(this.src)) { + this.src = "@desc "+this.src; + } +} + +/*t: + plan(5, "testing JSDOC.DocComment#fixDesc"); + + var com = new JSDOC.DocComment(); + + com.src = "this is a desc\n@author foo"; + com.fixDesc(); + is(com.src, "@desc this is a desc\n@author foo", "if no @desc tag is provided one is added."); + + com.src = "x"; + com.fixDesc(); + is(com.src, "@desc x", "if no @desc tag is provided one is added to a single character."); + + com.src = "\nx"; + com.fixDesc(); + is(com.src, "@desc \nx", "if no @desc tag is provided one is added to return and character."); + + com.src = " "; + com.fixDesc(); + is(com.src, " ", "if no @desc tag is provided one is not added to just whitespace."); + + com.src = ""; + com.fixDesc(); + is(com.src, "", "if no @desc tag is provided one is not added to empty."); +*/ + +/** + Remove slash-star comment wrapper from a raw comment string. + @type String + */ +JSDOC.DocComment.unwrapComment = function(/**String*/comment) { + if (!comment) return ""; + var unwrapped = comment.replace(/(^\/\*\*|\*\/$)/g, "").replace(/^\s*\* ?/gm, ""); + return unwrapped; +} + +/*t: + plan(5, "testing JSDOC.DocComment.unwrapComment"); + + var com = "/**x*"+"/"; + var unwrapped = JSDOC.DocComment.unwrapComment(com); + is(unwrapped, "x", "a single character jsdoc is found."); + + com = "/***x*"+"/"; + unwrapped = JSDOC.DocComment.unwrapComment(com); + is(unwrapped, "x", "three stars are allowed in the opener."); + + com = "/****x*"+"/"; + unwrapped = JSDOC.DocComment.unwrapComment(com); + is(unwrapped, "*x", "fourth star in the opener is kept."); + + com = "/**x\n * y\n*"+"/"; + unwrapped = JSDOC.DocComment.unwrapComment(com); + is(unwrapped, "x\ny\n", "leading stars and spaces are trimmed."); + + com = "/**x\n * y\n*"+"/"; + unwrapped = JSDOC.DocComment.unwrapComment(com); + is(unwrapped, "x\n y\n", "only first space after leading stars are trimmed."); +*/ + +/** + Provides a printable version of the comment. + @type String + */ +JSDOC.DocComment.prototype.toString = function() { + return this.src; +} + +/*t: + plan(1, "testing JSDOC.DocComment#fixDesc"); + var com = new JSDOC.DocComment(); + com.src = "foo"; + is(""+com, "foo", "stringifying a comment returns the unwrapped src."); +*/ + +/** + Given the title of a tag, returns all tags that have that title. + @type JSDOC.DocTag[] + */ +JSDOC.DocComment.prototype.getTag = function(/**String*/tagTitle) { + return this.tags.filter(function($){return $.title == tagTitle}); +} + +JSDOC.DocComment.prototype.deleteTag = function(/**String*/tagTitle) { + this.tags = this.tags.filter(function($){return $.title != tagTitle}) +} + +/*t: + plan(1, "testing JSDOC.DocComment#getTag"); + requires("../frame/String.js"); + requires("../lib/JSDOC/DocTag.js"); + + var com = new JSDOC.DocComment("/**@foo some\n* @bar\n* @bar*"+"/"); + is(com.getTag("bar").length, 2, "getTag returns expected number of tags."); +*/ + +/** + Used to store the currently shared tag text. +*/ +JSDOC.DocComment.shared = ""; + +/*t: + plan(2, "testing JSDOC.DocComment.shared"); + requires("../frame/String.js"); + requires("../lib/JSDOC/DocTag.js"); + + JSDOC.DocComment.shared = "@author Michael"; + + var com = new JSDOC.DocComment("/**@foo\n* @foo*"+"/"); + is(com.getTag("author").length, 1, "getTag returns shared tag."); + is(com.getTag("foo").length, 2, "getTag returns unshared tags too."); +*/ \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/DocTag.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/DocTag.js new file mode 100644 index 00000000..77ec07ca --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/DocTag.js @@ -0,0 +1,294 @@ +if (typeof JSDOC == "undefined") JSDOC = {}; + +/** + @constructor + */ +JSDOC.DocTag = function(src) { + this.init(); + if (typeof src != "undefined") { + this.parse(src); + } +} + +/** + Create and initialize the properties of this. + */ +JSDOC.DocTag.prototype.init = function() { + this.title = ""; + this.type = ""; + this.name = ""; + this.isOptional = false; + this.defaultValue = ""; + this.desc = ""; + + return this; +} + +/** + Populate the properties of this from the given tag src. + @param {string} src + */ +JSDOC.DocTag.prototype.parse = function(src) { + if (typeof src != "string") throw "src must be a string not "+(typeof src); + + try { + src = this.nibbleTitle(src); + if (JSDOC.PluginManager) { + JSDOC.PluginManager.run("onDocTagSynonym", this); + } + + src = this.nibbleType(src); + + // only some tags are allowed to have names. + if (this.title == "param" || this.title == "property" || this.title == "config") { // @config is deprecated + src = this.nibbleName(src); + } + } + catch(e) { + if (LOG) LOG.warn(e); + else throw e; + } + this.desc = src; // whatever is left + + // example tags need to have whitespace preserved + if (this.title != "example") this.desc = this.desc.trim(); + + if (JSDOC.PluginManager) { + JSDOC.PluginManager.run("onDocTag", this); + } +} + +/** + Automatically called when this is stringified. + */ +JSDOC.DocTag.prototype.toString = function() { + return this.desc; +} + +/*t: + plan(1, "testing JSDOC.DocTag#toString"); + + var tag = new JSDOC.DocTag("param {object} date A valid date."); + is(""+tag, "A valid date.", "stringifying a tag returns the desc."); + */ + +/** + Find and shift off the title of a tag. + @param {string} src + @return src + */ +JSDOC.DocTag.prototype.nibbleTitle = function(src) { + if (typeof src != "string") throw "src must be a string not "+(typeof src); + + var parts = src.match(/^\s*(\S+)(?:\s([\s\S]*))?$/); + + if (parts && parts[1]) this.title = parts[1]; + if (parts && parts[2]) src = parts[2]; + else src = ""; + + return src; +} + +/*t: + plan(8, "testing JSDOC.DocTag#nibbleTitle"); + + var tag = new JSDOC.DocTag(); + + tag.init().nibbleTitle("aTitleGoesHere"); + is(tag.title, "aTitleGoesHere", "a title can be found in a single-word string."); + + var src = tag.init().nibbleTitle("aTitleGoesHere and the rest"); + is(tag.title, "aTitleGoesHere", "a title can be found in a multi-word string."); + is(src, "and the rest", "the rest is returned when the title is nibbled off."); + + src = tag.init().nibbleTitle(""); + is(tag.title, "", "given an empty string the title is empty."); + is(src, "", "the rest is empty when the tag is empty."); + + var src = tag.init().nibbleTitle(" aTitleGoesHere\n a description"); + is(tag.title, "aTitleGoesHere", "leading and trailing spaces are not part of the title."); + is(src, " a description", "leading spaces (less one) are part of the description."); + + tag.init().nibbleTitle("a.Title::Goes_Here foo"); + is(tag.title, "a.Title::Goes_Here", "titles with punctuation are allowed."); + */ + +/** + Find and shift off the type of a tag. + @requires frame/String.js + @param {string} src + @return src + */ +JSDOC.DocTag.prototype.nibbleType = function(src) { + if (typeof src != "string") throw "src must be a string not "+(typeof src); + + if (src.match(/^\s*\{/)) { + var typeRange = src.balance("{", "}"); + if (typeRange[1] == -1) { + throw "Malformed comment tag ignored. Tag type requires an opening { and a closing }: "+src; + } + this.type = src.substring(typeRange[0]+1, typeRange[1]).trim(); + this.type = this.type.replace(/\s*,\s*/g, "|"); // multiples can be separated by , or | + src = src.substring(typeRange[1]+1); + } + + return src; +} + +/*t: + plan(5, "testing JSDOC.DocTag.parser.nibbleType"); + requires("../frame/String.js"); + + var tag = new JSDOC.DocTag(); + + tag.init().nibbleType("{String[]} aliases"); + is(tag.type, "String[]", "type can have non-alpha characters."); + + tag.init().nibbleType("{ aTypeGoesHere } etc etc"); + is(tag.type, "aTypeGoesHere", "type is trimmed."); + + tag.init().nibbleType("{ oneType, twoType ,\n threeType } etc etc"); + is(tag.type, "oneType|twoType|threeType", "multiple types can be separated by commas."); + + var error; + try { tag.init().nibbleType("{widget foo"); } + catch(e) { error = e; } + is(typeof error, "string", "malformed tag type throws error."); + isnt(error.indexOf("Malformed"), -1, "error message tells tag is malformed."); + */ + +/** + Find and shift off the name of a tag. + @requires frame/String.js + @param {string} src + @return src + */ +JSDOC.DocTag.prototype.nibbleName = function(src) { + if (typeof src != "string") throw "src must be a string not "+(typeof src); + + src = src.trim(); + + // is optional? + if (src.charAt(0) == "[") { + var nameRange = src.balance("[", "]"); + if (nameRange[1] == -1) { + throw "Malformed comment tag ignored. Tag optional name requires an opening [ and a closing ]: "+src; + } + this.name = src.substring(nameRange[0]+1, nameRange[1]).trim(); + this.isOptional = true; + + src = src.substring(nameRange[1]+1); + + // has default value? + var nameAndValue = this.name.split("="); + if (nameAndValue.length) { + this.name = nameAndValue.shift().trim(); + this.defaultValue = nameAndValue.join("="); + } + } + else { + var parts = src.match(/^(\S+)(?:\s([\s\S]*))?$/); + if (parts) { + if (parts[1]) this.name = parts[1]; + if (parts[2]) src = parts[2].trim(); + else src = ""; + } + } + + return src; +} + +/*t: + requires("../frame/String.js"); + plan(9, "testing JSDOC.DocTag.parser.nibbleName"); + + var tag = new JSDOC.DocTag(); + + tag.init().nibbleName("[foo] This is a description."); + is(tag.isOptional, true, "isOptional syntax is detected."); + is(tag.name, "foo", "optional param name is found."); + + tag.init().nibbleName("[foo] This is a description."); + is(tag.isOptional, true, "isOptional syntax is detected when no type."); + is(tag.name, "foo", "optional param name is found when no type."); + + tag.init().nibbleName("[foo=7] This is a description."); + is(tag.name, "foo", "optional param name is found when default value."); + is(tag.defaultValue, 7, "optional param default value is found when default value."); + + //tag.init().nibbleName("[foo= a value] This is a description."); + //is(tag.defaultValue, " a value", "optional param default value is found when default value has spaces (issue #112)."); + + tag.init().nibbleName("[foo=[]] This is a description."); + is(tag.defaultValue, "[]", "optional param default value is found when default value is [] (issue #95)."); + + tag.init().nibbleName("[foo=a=b] This is a description."); + is(tag.name, "foo", "optional param name is found when default value is a=b."); + is(tag.defaultValue, "a=b", "optional param default value is found when default value is a=b.") + */ + +/*t: + plan(32, "Testing JSDOC.DocTag.parser."); + requires("../frame/String.js"); + + var tag = new JSDOC.DocTag(); + + is(typeof tag, "object", "JSDOC.DocTag.parser with an empty string returns an object."); + is(typeof tag.title, "string", "returned object has a string property 'title'."); + is(typeof tag.type, "string", "returned object has a string property 'type'."); + is(typeof tag.name, "string", "returned object has a string property 'name'."); + is(typeof tag.defaultValue, "string", "returned object has a string property 'defaultValue'."); + is(typeof tag.isOptional, "boolean", "returned object has a boolean property 'isOptional'."); + is(typeof tag.desc, "string", "returned object has a string property 'desc'."); + + tag = new JSDOC.DocTag("param {widget} foo"); + is(tag.title, "param", "param title is found."); + is(tag.name, "foo", "param name is found when desc is missing."); + is(tag.desc, "", "param desc is empty when missing."); + + tag = new JSDOC.DocTag("param {object} date A valid date."); + is(tag.name, "date", "param name is found with a type."); + is(tag.type, "object", "param type is found."); + is(tag.desc, "A valid date.", "param desc is found with a type."); + + tag = new JSDOC.DocTag("param aName a description goes\n here."); + is(tag.name, "aName", "param name is found without a type."); + is(tag.desc, "a description goes\n here.", "param desc is found without a type."); + + tag = new JSDOC.DocTag("param {widget}"); + is(tag.name, "", "param name is empty when it is not given."); + + tag = new JSDOC.DocTag("param {widget} [foo] This is a description."); + is(tag.name, "foo", "optional param name is found."); + + tag = new JSDOC.DocTag("return {aType} This is a description."); + is(tag.type, "aType", "when return tag has no name, type is found."); + is(tag.desc, "This is a description.", "when return tag has no name, desc is found."); + + tag = new JSDOC.DocTag("author Joe Coder "); + is(tag.title, "author", "author tag has a title."); + is(tag.type, "", "the author tag has no type."); + is(tag.name, "", "the author tag has no name."); + is(tag.desc, "Joe Coder ", "author tag has desc."); + + tag = new JSDOC.DocTag("private \t\n "); + is(tag.title, "private", "private tag has a title."); + is(tag.type, "", "the private tag has no type."); + is(tag.name, "", "the private tag has no name."); + is(tag.desc, "", "private tag has no desc."); + + tag = new JSDOC.DocTag("example\n example(code);\n more();"); + is(tag.desc, " example(code);\n more();", "leading whitespace (less one) in examples code is preserved."); + + tag = new JSDOC.DocTag("param theName \n"); + is(tag.name, "theName", "name only is found."); + + tag = new JSDOC.DocTag("type theDesc \n"); + is(tag.desc, "theDesc", "desc only is found."); + + tag = new JSDOC.DocTag("type {theType} \n"); + is(tag.type, "theType", "type only is found."); + + tag = new JSDOC.DocTag(""); + is(tag.title, "", "title is empty when tag is empty."); + */ \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/JsDoc.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/JsDoc.js new file mode 100644 index 00000000..2c4bfb82 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/JsDoc.js @@ -0,0 +1,126 @@ +/** + @constructor + @param [opt] Used to override the commandline options. Useful for testing. + @version $Id: JsDoc.js 773 2009-01-24 09:42:04Z micmath $ +*/ +JSDOC.JsDoc = function(/**object*/ opt) { + if (opt) { + JSDOC.opt = opt; + } + + if (JSDOC.opt.h) { + JSDOC.usage(); + quit(); + } + + // defend against options that are not sane + if (JSDOC.opt._.length == 0) { + LOG.warn("No source files to work on. Nothing to do."); + quit(); + } + if (JSDOC.opt.t === true || JSDOC.opt.d === true) { + JSDOC.usage(); + } + + if (typeof JSDOC.opt.d == "string") { + if (!JSDOC.opt.d.charAt(JSDOC.opt.d.length-1).match(/[\\\/]/)) { + JSDOC.opt.d = JSDOC.opt.d+"/"; + } + LOG.inform("Output directory set to '"+JSDOC.opt.d+"'."); + IO.mkPath(JSDOC.opt.d); + } + if (JSDOC.opt.e) IO.setEncoding(JSDOC.opt.e); + + // the -r option: scan source directories recursively + if (typeof JSDOC.opt.r == "boolean") JSDOC.opt.r = 10; + else if (!isNaN(parseInt(JSDOC.opt.r))) JSDOC.opt.r = parseInt(JSDOC.opt.r); + else JSDOC.opt.r = 1; + + // the -D option: define user variables + var D = {}; + if (JSDOC.opt.D) { + for (var i = 0; i < JSDOC.opt.D.length; i++) { + var defineParts = JSDOC.opt.D[i].split(":", 2); + if (defineParts) D[defineParts[0]] = defineParts[1]; + } + } + JSDOC.opt.D = D; + // combine any conf file D options with the commandline D options + if (defined(JSDOC.conf)) for (var c in JSDOC.conf.D) { + if (!defined(JSDOC.opt.D[c])) { + JSDOC.opt.D[c] = JSDOC.conf.D[c]; + } + } + + // Give plugins a chance to initialize + if (defined(JSDOC.PluginManager)) { + JSDOC.PluginManager.run("onInit", JSDOC.opt); + } + + JSDOC.opt.srcFiles = JSDOC.JsDoc._getSrcFiles(); + JSDOC.JsDoc._parseSrcFiles(); + JSDOC.JsDoc.symbolSet = JSDOC.Parser.symbols; +} + +/** + Retrieve source file list. + @returns {String[]} The pathnames of the files to be parsed. + */ +JSDOC.JsDoc._getSrcFiles = function() { + JSDOC.JsDoc.srcFiles = []; + + var ext = ["js"]; + if (JSDOC.opt.x) { + ext = JSDOC.opt.x.split(",").map(function($) {return $.toLowerCase()}); + } + + for (var i = 0; i < JSDOC.opt._.length; i++) { + JSDOC.JsDoc.srcFiles = JSDOC.JsDoc.srcFiles.concat( + IO.ls(JSDOC.opt._[i], JSDOC.opt.r).filter( + function($) { + var thisExt = $.split(".").pop().toLowerCase(); + + if (JSDOC.opt.E) { + for(var n = 0; n < JSDOC.opt.E.length; n++) { + if ($.match(new RegExp(JSDOC.opt.E[n]))) { + LOG.inform("Excluding " + $); + return false; // if the file matches the regex then it's excluded. + } + } + } + + return (ext.indexOf(thisExt) > -1); // we're only interested in files with certain extensions + } + ) + ); + } + + return JSDOC.JsDoc.srcFiles; +} + +JSDOC.JsDoc._parseSrcFiles = function() { + JSDOC.Parser.init(); + for (var i = 0, l = JSDOC.JsDoc.srcFiles.length; i < l; i++) { + var srcFile = JSDOC.JsDoc.srcFiles[i]; + + if (JSDOC.opt.v) LOG.inform("Parsing file: " + srcFile); + + try { + var src = IO.readFile(srcFile); + } + catch(e) { + LOG.warn("Can't read source file '"+srcFile+"': "+e.message); + } + + var tr = new JSDOC.TokenReader(); + var ts = new JSDOC.TokenStream(tr.tokenize(new JSDOC.TextStream(src))); + + JSDOC.Parser.parse(ts, srcFile); + + } + JSDOC.Parser.finish(); + + if (JSDOC.PluginManager) { + JSDOC.PluginManager.run("onFinishedParsing", JSDOC.Parser.symbols); + } +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/JsPlate.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/JsPlate.js new file mode 100644 index 00000000..bcaebc9c --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/JsPlate.js @@ -0,0 +1,109 @@ +/** + @constructor +*/ +JSDOC.JsPlate = function(templateFile) { + if (templateFile) this.template = IO.readFile(templateFile); + + this.templateFile = templateFile; + this.code = ""; + this.parse(); +} + +JSDOC.JsPlate.prototype.parse = function() { + this.template = this.template.replace(/\{#[\s\S]+?#\}/gi, ""); + this.code = "var output=\u001e"+this.template; + + this.code = this.code.replace( + //gi, + function (match, eachName, inName) { + return "\u001e;\rvar $"+eachName+"_keys = keys("+inName+");\rfor(var $"+eachName+"_i = 0; $"+eachName+"_i < $"+eachName+"_keys.length; $"+eachName+"_i++) {\rvar $"+eachName+"_last = ($"+eachName+"_i == $"+eachName+"_keys.length-1);\rvar $"+eachName+"_key = $"+eachName+"_keys[$"+eachName+"_i];\rvar "+eachName+" = "+inName+"[$"+eachName+"_key];\routput+=\u001e"; + } + ); + this.code = this.code.replace(//g, "\u001e;\rif ($1) { output+=\u001e"); + this.code = this.code.replace(//g, "\u001e;}\relse if ($1) { output+=\u001e"); + this.code = this.code.replace(//g, "\u001e;}\relse { output+=\u001e"); + this.code = this.code.replace(/<\/(if|for)>/g, "\u001e;\r};\routput+=\u001e"); + this.code = this.code.replace( + /\{\+\s*([\s\S]+?)\s*\+\}/gi, + function (match, code) { + code = code.replace(/"/g, "\u001e"); // prevent qoute-escaping of inline code + code = code.replace(/(\r?\n)/g, " "); + return "\u001e+ ("+code+") +\u001e"; + } + ); + this.code = this.code.replace( + /\{!\s*([\s\S]+?)\s*!\}/gi, + function (match, code) { + code = code.replace(/"/g, "\u001e"); // prevent qoute-escaping of inline code + code = code.replace(/(\n)/g, " "); + return "\u001e; "+code+";\routput+=\u001e"; + } + ); + this.code = this.code+"\u001e;"; + + this.code = this.code.replace(/(\r?\n)/g, "\\n"); + this.code = this.code.replace(/"/g, "\\\""); + this.code = this.code.replace(/\u001e/g, "\""); +} + +JSDOC.JsPlate.prototype.toCode = function() { + return this.code; +} + +JSDOC.JsPlate.keys = function(obj) { + var keys = []; + if (obj.constructor.toString().indexOf("Array") > -1) { + for (var i = 0; i < obj.length; i++) { + keys.push(i); + } + } + else { + for (var i in obj) { + keys.push(i); + } + } + return keys; +}; + +JSDOC.JsPlate.values = function(obj) { + var values = []; + if (obj.constructor.toString().indexOf("Array") > -1) { + for (var i = 0; i < obj.length; i++) { + values.push(obj[i]); + } + } + else { + for (var i in obj) { + values.push(obj[i]); + } + } + return values; +}; + +JSDOC.JsPlate.prototype.process = function(data, compact) { + var keys = JSDOC.JsPlate.keys; + var values = JSDOC.JsPlate.values; + + try { + eval(this.code); + } + catch (e) { + print(">> There was an error evaluating the compiled code from template: "+this.templateFile); + print(" The error was on line "+e.lineNumber+" "+e.name+": "+e.message); + var lines = this.code.split("\r"); + if (e.lineNumber-2 >= 0) print("line "+(e.lineNumber-1)+": "+lines[e.lineNumber-2]); + print("line "+e.lineNumber+": "+lines[e.lineNumber-1]); + print(""); + } + + if (compact) { // patch by mcbain.asm + // Remove lines that contain only space-characters, usually left by lines in the template + // which originally only contained JSPlate tags or code. This makes it easier to write + // non-tricky templates which still put out nice code (not bloated with extra lines). + // Lines purposely left blank (just a line ending) are left alone. + output = output.replace(/\s+?(\r?)\n/g, "$1\n"); + } + + /*debug*///print(this.code); + return output; +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Lang.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Lang.js new file mode 100644 index 00000000..62919d7d --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Lang.js @@ -0,0 +1,144 @@ +/** + @namespace +*/ +JSDOC.Lang = { +} + +JSDOC.Lang.isBuiltin = function(name) { + return (JSDOC.Lang.isBuiltin.coreObjects.indexOf(name) > -1); +} +JSDOC.Lang.isBuiltin.coreObjects = ['_global_', 'Array', 'Boolean', 'Date', 'Error', 'Function', 'Math', 'Number', 'Object', 'RegExp', 'String']; + +JSDOC.Lang.whitespace = function(ch) { + return JSDOC.Lang.whitespace.names[ch]; +} +JSDOC.Lang.whitespace.names = { + " ": "SPACE", + "\f": "FORMFEED", + "\t": "TAB", + "\u0009": "UNICODE_TAB", + "\u000A": "UNICODE_NBR", + "\u0008": "VERTICAL_TAB" +}; + +JSDOC.Lang.newline = function(ch) { + return JSDOC.Lang.newline.names[ch]; +} +JSDOC.Lang.newline.names = { + "\n": "NEWLINE", + "\r": "RETURN", + "\u000A": "UNICODE_LF", + "\u000D": "UNICODE_CR", + "\u2029": "UNICODE_PS", + "\u2028": "UNICODE_LS" +}; + +JSDOC.Lang.keyword = function(word) { + return JSDOC.Lang.keyword.names["="+word]; +} +JSDOC.Lang.keyword.names = { + "=break": "BREAK", + "=case": "CASE", + "=catch": "CATCH", + "=const": "VAR", + "=continue": "CONTINUE", + "=default": "DEFAULT", + "=delete": "DELETE", + "=do": "DO", + "=else": "ELSE", + "=false": "FALSE", + "=finally": "FINALLY", + "=for": "FOR", + "=function": "FUNCTION", + "=if": "IF", + "=in": "IN", + "=instanceof": "INSTANCEOF", + "=new": "NEW", + "=null": "NULL", + "=return": "RETURN", + "=switch": "SWITCH", + "=this": "THIS", + "=throw": "THROW", + "=true": "TRUE", + "=try": "TRY", + "=typeof": "TYPEOF", + "=void": "VOID", + "=while": "WHILE", + "=with": "WITH", + "=var": "VAR" +}; + +JSDOC.Lang.punc = function(ch) { + return JSDOC.Lang.punc.names[ch]; +} +JSDOC.Lang.punc.names = { + ";": "SEMICOLON", + ",": "COMMA", + "?": "HOOK", + ":": "COLON", + "||": "OR", + "&&": "AND", + "|": "BITWISE_OR", + "^": "BITWISE_XOR", + "&": "BITWISE_AND", + "===": "STRICT_EQ", + "==": "EQ", + "=": "ASSIGN", + "!==": "STRICT_NE", + "!=": "NE", + "<<": "LSH", + "<=": "LE", + "<": "LT", + ">>>": "URSH", + ">>": "RSH", + ">=": "GE", + ">": "GT", + "++": "INCREMENT", + "--": "DECREMENT", + "+": "PLUS", + "-": "MINUS", + "*": "MUL", + "/": "DIV", + "%": "MOD", + "!": "NOT", + "~": "BITWISE_NOT", + ".": "DOT", + "[": "LEFT_BRACKET", + "]": "RIGHT_BRACKET", + "{": "LEFT_CURLY", + "}": "RIGHT_CURLY", + "(": "LEFT_PAREN", + ")": "RIGHT_PAREN" +}; + +JSDOC.Lang.matching = function(name) { + return JSDOC.Lang.matching.names[name]; +} +JSDOC.Lang.matching.names = { + "LEFT_PAREN": "RIGHT_PAREN", + "RIGHT_PAREN": "LEFT_PAREN", + "LEFT_CURLY": "RIGHT_CURLY", + "RIGHT_CURLY": "LEFT_CURLY", + "LEFT_BRACE": "RIGHT_BRACE", + "RIGHT_BRACE": "LEFT_BRACE" +} + +JSDOC.Lang.isNumber = function(str) { + return /^(\.[0-9]|[0-9]+\.|[0-9])[0-9]*([eE][+-][0-9]+)?$/i.test(str); +} + +JSDOC.Lang.isHexDec = function(str) { + return /^0x[0-9A-F]+$/i.test(str); +} + +JSDOC.Lang.isWordChar = function(str) { + return /^[a-zA-Z0-9$_.]+$/.test(str); +} + +JSDOC.Lang.isSpace = function(str) { + return (typeof JSDOC.Lang.whitespace(str) != "undefined"); +} + +JSDOC.Lang.isNewline = function(str) { + return (typeof JSDOC.Lang.newline(str) != "undefined"); +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Parser.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Parser.js new file mode 100644 index 00000000..b8e05ef0 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Parser.js @@ -0,0 +1,144 @@ +if (typeof JSDOC == "undefined") JSDOC = {}; + +/** + @namespace + @requires JSDOC.Walker + @requires JSDOC.Symbol + @requires JSDOC.DocComment +*/ +JSDOC.Parser = { + conf: { + ignoreCode: JSDOC.opt.n, + ignoreAnonymous: true, // factory: true + treatUnderscoredAsPrivate: true, // factory: true + explain: false // factory: false + }, + + addSymbol: function(symbol) { + + if (JSDOC.Parser.rename) { + for (var n in JSDOC.Parser.rename) { + if (symbol.alias.indexOf(n) == 0) { + if (symbol.name == symbol.alias) { + symbol.name = symbol.name.replace(n, JSDOC.Parser.rename[n]); + } + symbol.alias = symbol.alias.replace(n, JSDOC.Parser.rename[n]); + } + } + } + + if (JSDOC.opt.S) { + if (typeof JSDOC.Parser.secureModules == "undefined") JSDOC.Parser.secureModules = {}; + if (/^exports\./.test(symbol.alias)) { + symbol.srcFile.match(/(^|[\\\/])([^\\\/]+)\.js/i); + var fileNS = RegExp.$2; + + // need to create the namespace associated with this file first + if (!JSDOC.Parser.secureModules[fileNS]) { + JSDOC.Parser.secureModules[fileNS] = 1; + var nsSymbol = new JSDOC.Symbol(fileNS, [], "GLOBAL", new JSDOC.DocComment("")); + nsSymbol.isNamespace = true; + nsSymbol.srcFile = ""; + nsSymbol.isPrivate = false; + nsSymbol.srcFile = symbol.srcFile; + nsSymbol.desc = (JSDOC.Parser.symbols.getSymbol(symbol.srcFile) || {desc: ""}).desc; + JSDOC.Parser.addSymbol(nsSymbol); + } + + symbol.alias = symbol.alias.replace(/^exports\./, fileNS + '.'); + symbol.name = symbol.name.replace(/^exports\./, ''); + symbol.memberOf = fileNS; + symbol.isStatic = true; + } + } + + // if a symbol alias is documented more than once the last one with the user docs wins + if (JSDOC.Parser.symbols.hasSymbol(symbol.alias)) { + var oldSymbol = JSDOC.Parser.symbols.getSymbol(symbol.alias); + if (oldSymbol.comment.isUserComment) { + if (symbol.comment.isUserComment) { // old and new are both documented + LOG.warn("The symbol '"+symbol.alias+"' is documented more than once."); + } + else { // old is documented but new isn't + return; + } + } + } + + // we don't document anonymous things + if (JSDOC.Parser.conf.ignoreAnonymous && symbol.name.match(/\$anonymous\b/)) return; + + // uderscored things may be treated as if they were marked private, this cascades + if (JSDOC.Parser.conf.treatUnderscoredAsPrivate && symbol.name.match(/[.#-]_[^.#-]+$/)) { + if (!symbol.comment.getTag("public").length > 0) symbol.isPrivate = true; + } + + // -p flag is required to document private things + if (!JSDOC.opt.p && symbol.isPrivate) return; // issue #161 fixed by mcbain.asm + + // ignored things are not documented, this doesn't cascade + if (symbol.isIgnored) return; + JSDOC.Parser.symbols.addSymbol(symbol); + }, + + addBuiltin: function(name) { + var builtin = new JSDOC.Symbol(name, [], "CONSTRUCTOR", new JSDOC.DocComment("")); + builtin.isNamespace = true; + builtin.srcFile = ""; + builtin.isPrivate = false; + JSDOC.Parser.addSymbol(builtin); + return builtin; + }, + + init: function() { + JSDOC.Parser.symbols = new JSDOC.SymbolSet(); + JSDOC.Parser.walker = new JSDOC.Walker(); + }, + + finish: function() { + JSDOC.Parser.symbols.relate(); + + // make a litle report about what was found + if (JSDOC.Parser.conf.explain) { + var symbols = JSDOC.Parser.symbols.toArray(); + var srcFile = ""; + for (var i = 0, l = symbols.length; i < l; i++) { + var symbol = symbols[i]; + if (srcFile != symbol.srcFile) { + srcFile = symbol.srcFile; + print("\n"+srcFile+"\n-------------------"); + } + print(i+":\n alias => "+symbol.alias + "\n name => "+symbol.name+ "\n isa => "+symbol.isa + "\n memberOf => " + symbol.memberOf + "\n isStatic => " + symbol.isStatic + ", isInner => " + symbol.isInner+ ", isPrivate => " + symbol.isPrivate); + } + print("-------------------\n"); + } + } +} + +JSDOC.Parser.parse = function(/**JSDOC.TokenStream*/ts, /**String*/srcFile) { + JSDOC.Symbol.srcFile = (srcFile || ""); + JSDOC.DocComment.shared = ""; // shared comments don't cross file boundaries + + if (!JSDOC.Parser.walker) JSDOC.Parser.init(); + JSDOC.Parser.walker.walk(ts); // adds to our symbols + + // filter symbols by option + for (var p = JSDOC.Parser.symbols._index.first(); p; p = JSDOC.Parser.symbols._index.next()) { + var symbol = p.value; + + if (!symbol) continue; + + if (symbol.is("FILE") || symbol.is("GLOBAL")) { + continue; + } + else if (!JSDOC.opt.a && !symbol.comment.isUserComment) { + JSDOC.Parser.symbols.deleteSymbol(symbol.alias); + } + + if (/#$/.test(symbol.alias)) { // we don't document prototypes + JSDOC.Parser.symbols.deleteSymbol(symbol.alias); + } + } + + return JSDOC.Parser.symbols.toArray(); +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/PluginManager.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/PluginManager.js new file mode 100644 index 00000000..9c911931 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/PluginManager.js @@ -0,0 +1,33 @@ +/** + @namespace Holds functionality related to running plugins. +*/ +JSDOC.PluginManager = { +} + +/** + @param name A unique name that identifies that plugin. + @param handlers A collection of named functions. The names correspond to hooks in the core code. +*/ +JSDOC.PluginManager.registerPlugin = function(/**String*/name, /**Object*/handlers) { + if (!defined(JSDOC.PluginManager.plugins)) + /** The collection of all plugins. Requires a unique name for each. + */ + JSDOC.PluginManager.plugins = {}; + + + JSDOC.PluginManager.plugins[name] = handlers; +} + +/** + @param hook The name of the hook that is being caught. + @param target Any object. This will be passed as the only argument to the handler whose + name matches the hook name. Handlers cannot return a value, so must modify the target + object to have an effect. +*/ +JSDOC.PluginManager.run = function(/**String*/hook, /**Mixed*/target) { + for (var name in JSDOC.PluginManager.plugins) { + if (defined(JSDOC.PluginManager.plugins[name][hook])) { + JSDOC.PluginManager.plugins[name][hook](target); + } + } +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Symbol.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Symbol.js new file mode 100644 index 00000000..1aa44da8 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Symbol.js @@ -0,0 +1,644 @@ +if (typeof JSDOC == "undefined") JSDOC = {}; + +/** + Create a new Symbol. + @class Represents a symbol in the source code. + */ +JSDOC.Symbol = function() { + this.init(); + if (arguments.length) this.populate.apply(this, arguments); +} + +JSDOC.Symbol.count = 0; + +JSDOC.Symbol.prototype.init = function() { + this._name = ""; + this._params = []; + this.$args = []; + this.addOn = ""; + this.alias = ""; + this.augments = []; + this.author = ""; + this.classDesc = ""; + this.comment = {}; + this.defaultValue = undefined; + this.deprecated = ""; + this.desc = ""; + this.example = []; + this.exceptions = []; + this.fires = []; + this.id = JSDOC.Symbol.count++; + this.inherits = []; + this.inheritsFrom = []; + this.isa = "OBJECT"; + this.isConstant = false; + this.isEvent = false; + this.isIgnored = false; + this.isInner = false; + this.isNamespace = false; + this.isPrivate = false; + this.isStatic = false; + this.memberOf = ""; + this.methods = []; + this.properties = []; + this.requires = []; + this.returns = []; + this.see = []; + this.since = ""; + this.srcFile = {}; + this.type = ""; + this.version = ""; +} + +JSDOC.Symbol.prototype.serialize = function() { + var keys = []; + for (var p in this) { + keys.push (p); + } + keys = keys.sort(); + + var out = ""; + for (var i in keys) { + if (typeof this[keys[i]] == "function") continue; + out += keys[i]+" => "+Dumper.dump(this[keys[i]])+",\n"; + } + return "\n{\n" + out + "}\n"; +} + +JSDOC.Symbol.prototype.clone = function() { + var clone = new JSDOC.Symbol(); + clone.populate.apply(clone, this.$args); // repopulate using the original arguments + clone.srcFile = this.srcFile; // not the current srcFile, the one when the original was made + return clone; +} + +JSDOC.Symbol.prototype.__defineSetter__("name", + function(n) { n = n.replace(/^_global_[.#-]/, ""); n = n.replace(/\.prototype\.?/g, '#'); this._name = n; } +); +JSDOC.Symbol.prototype.__defineGetter__("name", + function() { return this._name; } +); +JSDOC.Symbol.prototype.__defineSetter__("params", + function(v) { + for (var i = 0, l = v.length; i < l; i++) { + if (v[i].constructor != JSDOC.DocTag) { // may be a generic object parsed from signature, like {type:..., name:...} + this._params[i] = new JSDOC.DocTag("param"+((v[i].type)?" {"+v[i].type+"}":"")+" "+v[i].name); + } + else { + this._params[i] = v[i]; + } + } + } +); +JSDOC.Symbol.prototype.__defineGetter__("params", + function() { return this._params; } +); + +JSDOC.Symbol.prototype.getEvents = function() { + var events = []; + for (var i = 0, l = this.methods.length; i < l; i++) { + if (this.methods[i].isEvent) { + this.methods[i].name = this.methods[i].name.replace("event:", ""); + events.push(this.methods[i]); + } + } + return events; +} + +JSDOC.Symbol.prototype.getMethods = function() { + var nonEvents = []; + for (var i = 0, l = this.methods.length; i < l; i++) { + if (!this.methods[i].isEvent) { + nonEvents.push(this.methods[i]); + } + } + return nonEvents; +} + + +JSDOC.Symbol.prototype.populate = function( + /** String */ name, + /** Object[] */ params, + /** String */ isa, + /** JSDOC.DocComment */ comment +) { + this.$args = arguments; + + this.name = name; + this.alias = this.name; + + this.params = params; + this.isa = (isa == "VIRTUAL")? "OBJECT":isa; + this.comment = comment || new JSDOC.DocComment(""); + this.srcFile = JSDOC.Symbol.srcFile; + + if (this.is("FILE") && !this.alias) this.alias = this.srcFile; + + this.setTags(); + + if (typeof JSDOC.PluginManager != "undefined") { + JSDOC.PluginManager.run("onSymbol", this); + } +} + +JSDOC.Symbol.prototype.setTags = function() { + // @author + var authors = this.comment.getTag("author"); + if (authors.length) { + this.author = authors.map(function($){return $.desc;}).join(", "); + } + + /*t: + plan(34, "testing JSDOC.Symbol"); + + requires("../lib/JSDOC/DocComment.js"); + requires("../frame/String.js"); + requires("../lib/JSDOC/DocTag.js"); + + var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@author Joe Smith*"+"/")); + is(sym.author, "Joe Smith", "@author tag, author is found."); + */ + + // @desc + var descs = this.comment.getTag("desc"); + if (descs.length) { + this.desc = descs.map(function($){return $.desc;}).join("\n"); // multiple descriptions are concatenated into one + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@desc This is a description.*"+"/")); + is(sym.desc, "This is a description.", "@desc tag, description is found."); + */ + + // @overview + if (this.is("FILE")) { + if (!this.alias) this.alias = this.srcFile; + + var overviews = this.comment.getTag("overview"); + if (overviews.length) { + this.desc = [this.desc].concat(overviews.map(function($){return $.desc;})).join("\n"); + } + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@overview This is an overview.*"+"/")); + is(sym.desc, "\nThis is an overview.", "@overview tag, description is found."); + */ + + // @since + var sinces = this.comment.getTag("since"); + if (sinces.length) { + this.since = sinces.map(function($){return $.desc;}).join(", "); + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@since 1.01*"+"/")); + is(sym.since, "1.01", "@since tag, description is found."); + */ + + // @constant + if (this.comment.getTag("constant").length) { + this.isConstant = true; + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@constant*"+"/")); + is(sym.isConstant, true, "@constant tag, isConstant set."); + */ + + // @version + var versions = this.comment.getTag("version"); + if (versions.length) { + this.version = versions.map(function($){return $.desc;}).join(", "); + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@version 2.0x*"+"/")); + is(sym.version, "2.0x", "@version tag, version is found."); + */ + + // @deprecated + var deprecateds = this.comment.getTag("deprecated"); + if (deprecateds.length) { + this.deprecated = deprecateds.map(function($){return $.desc;}).join("\n"); + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@deprecated Use other method.*"+"/")); + is(sym.deprecated, "Use other method.", "@deprecated tag, desc is found."); + */ + + // @example + var examples = this.comment.getTag("example"); + if (examples.length) { + this.example = examples.map( + // trim trailing whitespace + function($) { + $.desc = $.desc.replace(/\s+$/, ""); + return $; + } + ); + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@example This\n is an example. \n*"+"/")); + isnt(typeof sym.example[0], "undefined", "@example tag, creates sym.example array."); + is(sym.example[0], "This\n is an example.", "@example tag, desc is found."); + */ + + // @see + var sees = this.comment.getTag("see"); + if (sees.length) { + var thisSee = this.see; + sees.map(function($){thisSee.push($.desc);}); + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "FILE", new JSDOC.DocComment("/**@see The other thing.*"+"/")); + is(sym.see, "The other thing.", "@see tag, desc is found."); + */ + + // @class + var classes = this.comment.getTag("class"); + if (classes.length) { + this.isa = "CONSTRUCTOR"; + this.classDesc = classes[0].desc; // desc can't apply to the constructor as there is none. + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@class This describes the class.*"+"/")); + is(sym.isa, "CONSTRUCTOR", "@class tag, makes symbol a constructor."); + is(sym.classDesc, "This describes the class.", "@class tag, class description is found."); + */ + + // @namespace + var namespaces = this.comment.getTag("namespace"); + if (namespaces.length) { + this.classDesc = namespaces[0].desc; + this.isNamespace = true; + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@namespace This describes the namespace.*"+"/")); + is(sym.classDesc, "This describes the namespace.", "@namespace tag, class description is found."); + */ + + // @param + var params = this.comment.getTag("param"); + if (params.length) { + // user-defined params overwrite those with same name defined by the parser + var thisParams = this.params; + + if (thisParams.length == 0) { // none exist yet, so just bung all these user-defined params straight in + this.params = params; + } + else { // need to overlay these user-defined params on to existing parser-defined params + for (var i = 0, l = params.length; i < l; i++) { + if (thisParams[i]) { + if (params[i].type) thisParams[i].type = params[i].type; + thisParams[i].name = params[i].name; + thisParams[i].desc = params[i].desc; + thisParams[i].isOptional = params[i].isOptional; + thisParams[i].defaultValue = params[i].defaultValue; + } + else thisParams[i] = params[i]; + } + } + } + + /*t: + var sym = new JSDOC.Symbol("foo", [{type: "array", name: "pages"}], "FUNCTION", new JSDOC.DocComment("/**Description.*"+"/")); + is(sym.params.length, 1, "parser defined param is found."); + + sym = new JSDOC.Symbol("foo", [], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {array} pages*"+"/")); + is(sym.params.length, 1, "user defined param is found."); + is(sym.params[0].type, "array", "user defined param type is found."); + is(sym.params[0].name, "pages", "user defined param name is found."); + + sym = new JSDOC.Symbol("foo", [{type: "array", name: "pages"}], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {string} uid*"+"/")); + is(sym.params.length, 1, "user defined param overwrites parser defined param."); + is(sym.params[0].type, "string", "user defined param type overwrites parser defined param type."); + is(sym.params[0].name, "uid", "user defined param name overwrites parser defined param name."); + + sym = new JSDOC.Symbol("foo", [{type: "array", name: "pages"}, {type: "number", name: "count"}], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {string} uid*"+"/")); + is(sym.params.length, 2, "user defined params overlay parser defined params."); + is(sym.params[1].type, "number", "user defined param type overlays parser defined param type."); + is(sym.params[1].name, "count", "user defined param name overlays parser defined param name."); + + sym = new JSDOC.Symbol("foo", [], "FUNCTION", new JSDOC.DocComment("/**Description.\n@param {array} pages The pages description.*"+"/")); + is(sym.params.length, 1, "user defined param with description is found."); + is(sym.params[0].desc, "The pages description.", "user defined param description is found."); + */ + + // @constructor + if (this.comment.getTag("constructor").length) { + this.isa = "CONSTRUCTOR"; + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@constructor*"+"/")); + is(sym.isa, "CONSTRUCTOR", "@constructor tag, makes symbol a constructor."); + */ + + // @static + if (this.comment.getTag("static").length) { + this.isStatic = true; + if (this.isa == "CONSTRUCTOR") { + this.isNamespace = true; + } + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@static\n@constructor*"+"/")); + is(sym.isStatic, true, "@static tag, makes isStatic true."); + is(sym.isNamespace, true, "@static and @constructor tag, makes isNamespace true."); + */ + + // @inner + if (this.comment.getTag("inner").length) { + this.isInner = true; + this.isStatic = false; + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@inner*"+"/")); + is(sym.isStatic, false, "@inner tag, makes isStatic false."); + is(sym.isInner, true, "@inner makes isInner true."); + */ + + // @name + var names = this.comment.getTag("name"); + if (names.length) { + this.name = names[0].desc; + } + + /*t: + // todo + */ + + // @field + if (this.comment.getTag("field").length) { + this.isa = "OBJECT"; + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "FUNCTION", new JSDOC.DocComment("/**@field*"+"/")); + is(sym.isa, "OBJECT", "@field tag, makes symbol an object."); + */ + + // @function + if (this.comment.getTag("function").length) { + this.isa = "FUNCTION"; + if (/event:/.test(this.alias)) this.isEvent = true; + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@function*"+"/")); + is(sym.isa, "FUNCTION", "@function tag, makes symbol a function."); + */ + + // @event + var events = this.comment.getTag("event"); + if (events.length) { + this.isa = "FUNCTION"; + this.isEvent = true; + if (!/event:/.test(this.alias)) + this.alias = this.alias.replace(/^(.*[.#-])([^.#-]+)$/, "$1event:$2"); + } + + /*t: + var sym = new JSDOC.Symbol("foo", [], "OBJECT", new JSDOC.DocComment("/**@event*"+"/")); + is(sym.isa, "FUNCTION", "@event tag, makes symbol a function."); + is(sym.isEvent, true, "@event makes isEvent true."); + */ + + // @fires + var fires = this.comment.getTag("fires"); + if (fires.length) { + for (var i = 0; i < fires.length; i++) { + this.fires.push(fires[i].desc); + } + } + + /*t: + // todo + */ + + // @property + var properties = this.comment.getTag("property"); + if (properties.length) { + thisProperties = this.properties; + for (var i = 0; i < properties.length; i++) { + var property = new JSDOC.Symbol(this.alias+"#"+properties[i].name, [], "OBJECT", new JSDOC.DocComment("/**"+properties[i].desc+"*/")); + // TODO: shouldn't the following happen in the addProperty method of Symbol? + if (properties[i].type) property.type = properties[i].type; + if (properties[i].defaultValue) property.defaultValue = properties[i].defaultValue; + this.addProperty(property); + if (!JSDOC.Parser.symbols.getSymbolByName(property.name)) + JSDOC.Parser.addSymbol(property); + } + } + + /*t: + // todo + */ + + // @return + var returns = this.comment.getTag("return"); + if (returns.length) { // there can be many return tags in a single doclet + this.returns = returns; + this.type = returns.map(function($){return $.type}).join(", "); + } + + /*t: + // todo + */ + + // @exception + this.exceptions = this.comment.getTag("throws"); + + /*t: + // todo + */ + + // @requires + var requires = this.comment.getTag("requires"); + if (requires.length) { + this.requires = requires.map(function($){return $.desc}); + } + + /*t: + // todo + */ + + // @type + var types = this.comment.getTag("type"); + if (types.length) { + this.type = types[0].desc; //multiple type tags are ignored + } + + /*t: + // todo + */ + + // @private + if (this.comment.getTag("private").length || this.isInner) { + this.isPrivate = true; + } + + // @ignore + if (this.comment.getTag("ignore").length) { + this.isIgnored = true; + } + + /*t: + // todo + */ + + // @inherits ... as ... + var inherits = this.comment.getTag("inherits"); + if (inherits.length) { + for (var i = 0; i < inherits.length; i++) { + if (/^\s*([a-z$0-9_.#:-]+)(?:\s+as\s+([a-z$0-9_.#:-]+))?/i.test(inherits[i].desc)) { + var inAlias = RegExp.$1; + var inAs = RegExp.$2 || inAlias; + + if (inAlias) inAlias = inAlias.replace(/\.prototype\.?/g, "#"); + + if (inAs) { + inAs = inAs.replace(/\.prototype\.?/g, "#"); + inAs = inAs.replace(/^this\.?/, "#"); + } + + if (inAs.indexOf(inAlias) != 0) { //not a full namepath + var joiner = "."; + if (this.alias.charAt(this.alias.length-1) == "#" || inAs.charAt(0) == "#") { + joiner = ""; + } + inAs = this.alias + joiner + inAs; + } + } + this.inherits.push({alias: inAlias, as: inAs}); + } + } + + /*t: + // todo + */ + + // @augments + this.augments = this.comment.getTag("augments"); + + // @default + var defaults = this.comment.getTag("default"); + if (defaults.length) { + if (this.is("OBJECT")) { + this.defaultValue = defaults[0].desc; + } + } + + /*t: + // todo + */ + + // @memberOf + var memberOfs = this.comment.getTag("memberOf"); + if (memberOfs.length) { + this.memberOf = memberOfs[0].desc; + this.memberOf = this.memberOf.replace(/\.prototype\.?/g, "#"); + } + + /*t: + // todo + */ + + // @public + if (this.comment.getTag("public").length) { + this.isPrivate = false; + } + + /*t: + // todo + */ + + if (JSDOC.PluginManager) { + JSDOC.PluginManager.run("onSetTags", this); + } +} + +JSDOC.Symbol.prototype.is = function(what) { + return this.isa === what; +} + +JSDOC.Symbol.prototype.isBuiltin = function() { + return JSDOC.Lang.isBuiltin(this.alias); +} + +JSDOC.Symbol.prototype.setType = function(/**String*/comment, /**Boolean*/overwrite) { + if (!overwrite && this.type) return; + var typeComment = JSDOC.DocComment.unwrapComment(comment); + this.type = typeComment; +} + +JSDOC.Symbol.prototype.inherit = function(symbol) { + if (!this.hasMember(symbol.name) && !symbol.isInner) { + if (symbol.is("FUNCTION")) + this.methods.push(symbol); + else if (symbol.is("OBJECT")) + this.properties.push(symbol); + } +} + +JSDOC.Symbol.prototype.hasMember = function(name) { + return (this.hasMethod(name) || this.hasProperty(name)); +} + +JSDOC.Symbol.prototype.addMember = function(symbol) { + if (symbol.is("FUNCTION")) { this.addMethod(symbol); } + else if (symbol.is("OBJECT")) { this.addProperty(symbol); } +} + +JSDOC.Symbol.prototype.hasMethod = function(name) { + var thisMethods = this.methods; + for (var i = 0, l = thisMethods.length; i < l; i++) { + if (thisMethods[i].name == name) return true; + if (thisMethods[i].alias == name) return true; + } + return false; +} + +JSDOC.Symbol.prototype.addMethod = function(symbol) { + var methodAlias = symbol.alias; + var thisMethods = this.methods; + for (var i = 0, l = thisMethods.length; i < l; i++) { + if (thisMethods[i].alias == methodAlias) { + thisMethods[i] = symbol; // overwriting previous method + return; + } + } + thisMethods.push(symbol); // new method with this alias +} + +JSDOC.Symbol.prototype.hasProperty = function(name) { + var thisProperties = this.properties; + for (var i = 0, l = thisProperties.length; i < l; i++) { + if (thisProperties[i].name == name) return true; + if (thisProperties[i].alias == name) return true; + } + return false; +} + +JSDOC.Symbol.prototype.addProperty = function(symbol) { + var propertyAlias = symbol.alias; + var thisProperties = this.properties; + for (var i = 0, l = thisProperties.length; i < l; i++) { + if (thisProperties[i].alias == propertyAlias) { + thisProperties[i] = symbol; // overwriting previous property + return; + } + } + + thisProperties.push(symbol); // new property with this alias +} + +JSDOC.Symbol.srcFile = ""; //running reference to the current file being parsed diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/SymbolSet.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/SymbolSet.js new file mode 100644 index 00000000..e24d50af --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/SymbolSet.js @@ -0,0 +1,242 @@ +/** @constructor */ +JSDOC.SymbolSet = function() { + this.init(); +} + +JSDOC.SymbolSet.prototype.init = function() { + this._index = new Hash(); +} + +JSDOC.SymbolSet.prototype.keys = function() { + return this._index.keys(); +} + +JSDOC.SymbolSet.prototype.hasSymbol = function(alias) { + return this._index.hasKey(alias); +} + +JSDOC.SymbolSet.prototype.addSymbol = function(symbol) { + if (this.hasSymbol(symbol.alias)) { + LOG.warn("Overwriting symbol documentation for: "+symbol.alias + "."); + } + this._index.set(symbol.alias, symbol); +} + +JSDOC.SymbolSet.prototype.getSymbol = function(alias) { + if (this.hasSymbol(alias)) return this._index.get(alias); +} + +JSDOC.SymbolSet.prototype.getSymbolByName = function(name) { + for (var p = this._index.first(); p; p = this._index.next()) { + var symbol = p.value; + if (symbol.name == name) return symbol; + } +} + +JSDOC.SymbolSet.prototype.toArray = function() { + return this._index.values(); +} + +JSDOC.SymbolSet.prototype.deleteSymbol = function(alias) { + if (!this.hasSymbol(alias)) return; + this._index.drop(alias); +} + +JSDOC.SymbolSet.prototype.renameSymbol = function(oldName, newName) { + // todo: should check if oldname or newname already exist + this._index.replace(oldName, newName); + this._index.get(newName).alias = newName; + return newName; +} + +JSDOC.SymbolSet.prototype.relate = function() { + this.resolveBorrows(); + this.resolveMemberOf(); + this.resolveAugments(); +} + +JSDOC.SymbolSet.prototype.resolveBorrows = function() { + for (var p = this._index.first(); p; p = this._index.next()) { + var symbol = p.value; + if (symbol.is("FILE") || symbol.is("GLOBAL")) continue; + + var borrows = symbol.inherits; + for (var i = 0; i < borrows.length; i++) { + +if (/#$/.test(borrows[i].alias)) { + LOG.warn("Attempted to borrow entire instance of "+borrows[i].alias+" but that feature is not yet implemented."); + return; +} + var borrowed = this.getSymbol(borrows[i].alias); + + if (!borrowed) { + LOG.warn("Can't borrow undocumented "+borrows[i].alias+"."); + continue; + } + + if (borrows[i].as == borrowed.alias) { + var assumedName = borrowed.name.split(/([#.-])/).pop(); + borrows[i].as = symbol.name+RegExp.$1+assumedName; + LOG.inform("Assuming borrowed as name is "+borrows[i].as+" but that feature is experimental."); + } + + var borrowAsName = borrows[i].as; + var borrowAsAlias = borrowAsName; + if (!borrowAsName) { + LOG.warn("Malformed @borrow, 'as' is required."); + continue; + } + + if (borrowAsName.length > symbol.alias.length && borrowAsName.indexOf(symbol.alias) == 0) { + borrowAsName = borrowAsName.replace(borrowed.alias, "") + } + else { + var joiner = ""; + if (borrowAsName.charAt(0) != "#") joiner = "."; + borrowAsAlias = borrowed.alias + joiner + borrowAsName; + } + + borrowAsName = borrowAsName.replace(/^[#.]/, ""); + + if (this.hasSymbol(borrowAsAlias)) continue; + + var clone = borrowed.clone(); + clone.name = borrowAsName; + clone.alias = borrowAsAlias; + this.addSymbol(clone); + } + } +} + +JSDOC.SymbolSet.prototype.resolveMemberOf = function() { + for (var p = this._index.first(); p; p = this._index.next()) { + var symbol = p.value; + + if (symbol.is("FILE") || symbol.is("GLOBAL")) continue; + + // the memberOf value was provided in the @memberOf tag + else if (symbol.memberOf) { + // like foo.bar is a memberOf foo + if (symbol.alias.indexOf(symbol.memberOf) == 0) { + var memberMatch = new RegExp("^("+symbol.memberOf+")[.#-]?(.+)$"); + var aliasParts = symbol.alias.match(memberMatch); + + if (aliasParts) { + symbol.memberOf = aliasParts[1]; + symbol.name = aliasParts[2]; + } + + var nameParts = symbol.name.match(memberMatch); + + if (nameParts) { + symbol.name = nameParts[2]; + } + } + // like bar is a memberOf foo + else { + var joiner = symbol.memberOf.charAt(symbol.memberOf.length-1); + if (!/[.#-]/.test(joiner)) symbol.memberOf += "."; + this.renameSymbol(symbol.alias, symbol.memberOf + symbol.name); + } + } + // the memberOf must be calculated + else { + var parts = symbol.alias.match(/^(.*[.#-])([^.#-]+)$/); + + if (parts) { + symbol.memberOf = parts[1]; + symbol.name = parts[2]; + } + } + + // set isStatic, isInner + if (symbol.memberOf) { + switch (symbol.memberOf.charAt(symbol.memberOf.length-1)) { + case '#' : + symbol.isStatic = false; + symbol.isInner = false; + break; + case '.' : + symbol.isStatic = true; + symbol.isInner = false; + break; + case '-' : + symbol.isStatic = false; + symbol.isInner = true; + break; + default: // memberOf ends in none of the above + symbol.isStatic = true; + break; + } + } + + // unowned methods and fields belong to the global object + if (!symbol.is("CONSTRUCTOR") && !symbol.isNamespace && symbol.memberOf == "") { + symbol.memberOf = "_global_"; + } + + // clean up + if (symbol.memberOf.match(/[.#-]$/)) { + symbol.memberOf = symbol.memberOf.substr(0, symbol.memberOf.length-1); + } + // add to parent's methods or properties list + if (symbol.memberOf) { + + var container = this.getSymbol(symbol.memberOf); + if (!container) { + if (JSDOC.Lang.isBuiltin(symbol.memberOf)) container = JSDOC.Parser.addBuiltin(symbol.memberOf); + else { + LOG.warn("Trying to document "+symbol.name +" as a member of undocumented symbol "+symbol.memberOf+"."); + } + } + + if (container) container.addMember(symbol); + } + } +} + +JSDOC.SymbolSet.prototype.resolveAugments = function() { + for (var p = this._index.first(); p; p = this._index.next()) { + var symbol = p.value; + + if (symbol.alias == "_global_" || symbol.is("FILE")) continue; + JSDOC.SymbolSet.prototype.walk.apply(this, [symbol]); + } +} + +JSDOC.SymbolSet.prototype.walk = function(symbol) { + var augments = symbol.augments; + for(var i = 0; i < augments.length; i++) { + var contributer = this.getSymbol(augments[i]); + if (!contributer && JSDOC.Lang.isBuiltin(''+augments[i])) { + contributer = new JSDOC.Symbol("_global_."+augments[i], [], augments[i], new JSDOC.DocComment("Built in.")); + contributer.isNamespace = true; + contributer.srcFile = ""; + contributer.isPrivate = false; + JSDOC.Parser.addSymbol(contributer); + } + + if (contributer) { + if (contributer.augments.length) { + JSDOC.SymbolSet.prototype.walk.apply(this, [contributer]); + } + + symbol.inheritsFrom.push(contributer.alias); + //if (!isUnique(symbol.inheritsFrom)) { + // LOG.warn("Can't resolve augments: Circular reference: "+symbol.alias+" inherits from "+contributer.alias+" more than once."); + //} + //else { + var cmethods = contributer.methods; + var cproperties = contributer.properties; + + for (var ci = 0, cl = cmethods.length; ci < cl; ci++) { + if (!cmethods[ci].isStatic) symbol.inherit(cmethods[ci]); + } + for (var ci = 0, cl = cproperties.length; ci < cl; ci++) { + if (!cproperties[ci].isStatic) symbol.inherit(cproperties[ci]); + } + //} + } + else LOG.warn("Can't augment contributer: "+augments[i]+", not found."); + } +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/TextStream.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/TextStream.js new file mode 100644 index 00000000..ccc48a87 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/TextStream.js @@ -0,0 +1,41 @@ + +/** + @constructor +*/ +JSDOC.TextStream = function(text) { + if (typeof(text) == "undefined") text = ""; + text = ""+text; + this.text = text; + this.cursor = 0; +} + +JSDOC.TextStream.prototype.look = function(n) { + if (typeof n == "undefined") n = 0; + + if (this.cursor+n < 0 || this.cursor+n >= this.text.length) { + var result = new String(""); + result.eof = true; + return result; + } + return this.text.charAt(this.cursor+n); +} + +JSDOC.TextStream.prototype.next = function(n) { + if (typeof n == "undefined") n = 1; + if (n < 1) return null; + + var pulled = ""; + for (var i = 0; i < n; i++) { + if (this.cursor+i < this.text.length) { + pulled += this.text.charAt(this.cursor+i); + } + else { + var result = new String(""); + result.eof = true; + return result; + } + } + + this.cursor += n; + return pulled; +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Token.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Token.js new file mode 100644 index 00000000..fb7f9d94 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Token.js @@ -0,0 +1,18 @@ +if (typeof JSDOC == "undefined") JSDOC = {}; + +/** + @constructor +*/ +JSDOC.Token = function(data, type, name) { + this.data = data; + this.type = type; + this.name = name; +} + +JSDOC.Token.prototype.toString = function() { + return "<"+this.type+" name=\""+this.name+"\">"+this.data+""; +} + +JSDOC.Token.prototype.is = function(what) { + return this.name === what || this.type === what; +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/TokenReader.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/TokenReader.js new file mode 100644 index 00000000..9f658fb9 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/TokenReader.js @@ -0,0 +1,332 @@ +if (typeof JSDOC == "undefined") JSDOC = {}; + +/** + @class Search a {@link JSDOC.TextStream} for language tokens. +*/ +JSDOC.TokenReader = function() { + this.keepDocs = true; + this.keepWhite = false; + this.keepComments = false; +} + +/** + @type {JSDOC.Token[]} + */ +JSDOC.TokenReader.prototype.tokenize = function(/**JSDOC.TextStream*/stream) { + var tokens = []; + /**@ignore*/ tokens.last = function() { return tokens[tokens.length-1]; } + /**@ignore*/ tokens.lastSym = function() { + for (var i = tokens.length-1; i >= 0; i--) { + if (!(tokens[i].is("WHIT") || tokens[i].is("COMM"))) return tokens[i]; + } + } + + while (!stream.look().eof) { + if (this.read_mlcomment(stream, tokens)) continue; + if (this.read_slcomment(stream, tokens)) continue; + if (this.read_dbquote(stream, tokens)) continue; + if (this.read_snquote(stream, tokens)) continue; + if (this.read_regx(stream, tokens)) continue; + if (this.read_numb(stream, tokens)) continue; + if (this.read_punc(stream, tokens)) continue; + if (this.read_newline(stream, tokens)) continue; + if (this.read_space(stream, tokens)) continue; + if (this.read_word(stream, tokens)) continue; + + // if execution reaches here then an error has happened + tokens.push(new JSDOC.Token(stream.next(), "TOKN", "UNKNOWN_TOKEN")); + } + return tokens; +} + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_word = function(/**JSDOC.TokenStream*/stream, tokens) { + var found = ""; + while (!stream.look().eof && JSDOC.Lang.isWordChar(stream.look())) { + found += stream.next(); + } + + if (found === "") { + return false; + } + else { + var name; + if ((name = JSDOC.Lang.keyword(found))) tokens.push(new JSDOC.Token(found, "KEYW", name)); + else tokens.push(new JSDOC.Token(found, "NAME", "NAME")); + return true; + } +} + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_punc = function(/**JSDOC.TokenStream*/stream, tokens) { + var found = ""; + var name; + while (!stream.look().eof && JSDOC.Lang.punc(found+stream.look())) { + found += stream.next(); + } + + if (found === "") { + return false; + } + else { + tokens.push(new JSDOC.Token(found, "PUNC", JSDOC.Lang.punc(found))); + return true; + } +} + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_space = function(/**JSDOC.TokenStream*/stream, tokens) { + var found = ""; + + while (!stream.look().eof && JSDOC.Lang.isSpace(stream.look())) { + found += stream.next(); + } + + if (found === "") { + return false; + } + else { + if (this.collapseWhite) found = " "; + if (this.keepWhite) tokens.push(new JSDOC.Token(found, "WHIT", "SPACE")); + return true; + } +} + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_newline = function(/**JSDOC.TokenStream*/stream, tokens) { + var found = ""; + + while (!stream.look().eof && JSDOC.Lang.isNewline(stream.look())) { + found += stream.next(); + } + + if (found === "") { + return false; + } + else { + if (this.collapseWhite) found = "\n"; + if (this.keepWhite) tokens.push(new JSDOC.Token(found, "WHIT", "NEWLINE")); + return true; + } +} + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_mlcomment = function(/**JSDOC.TokenStream*/stream, tokens) { + if (stream.look() == "/" && stream.look(1) == "*") { + var found = stream.next(2); + + while (!stream.look().eof && !(stream.look(-1) == "/" && stream.look(-2) == "*")) { + found += stream.next(); + } + + // to start doclet we allow /** or /*** but not /**/ or /**** + if (/^\/\*\*([^\/]|\*[^*])/.test(found) && this.keepDocs) tokens.push(new JSDOC.Token(found, "COMM", "JSDOC")); + else if (this.keepComments) tokens.push(new JSDOC.Token(found, "COMM", "MULTI_LINE_COMM")); + return true; + } + return false; +} + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_slcomment = function(/**JSDOC.TokenStream*/stream, tokens) { + var found; + if ( + (stream.look() == "/" && stream.look(1) == "/" && (found=stream.next(2))) + || + (stream.look() == "<" && stream.look(1) == "!" && stream.look(2) == "-" && stream.look(3) == "-" && (found=stream.next(4))) + ) { + + while (!stream.look().eof && !JSDOC.Lang.isNewline(stream.look())) { + found += stream.next(); + } + + if (this.keepComments) { + tokens.push(new JSDOC.Token(found, "COMM", "SINGLE_LINE_COMM")); + } + return true; + } + return false; +} + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_dbquote = function(/**JSDOC.TokenStream*/stream, tokens) { + if (stream.look() == "\"") { + // find terminator + var string = stream.next(); + + while (!stream.look().eof) { + if (stream.look() == "\\") { + if (JSDOC.Lang.isNewline(stream.look(1))) { + do { + stream.next(); + } while (!stream.look().eof && JSDOC.Lang.isNewline(stream.look())); + string += "\\\n"; + } + else { + string += stream.next(2); + } + } + else if (stream.look() == "\"") { + string += stream.next(); + tokens.push(new JSDOC.Token(string, "STRN", "DOUBLE_QUOTE")); + return true; + } + else { + string += stream.next(); + } + } + } + return false; // error! unterminated string +} + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_snquote = function(/**JSDOC.TokenStream*/stream, tokens) { + if (stream.look() == "'") { + // find terminator + var string = stream.next(); + + while (!stream.look().eof) { + if (stream.look() == "\\") { // escape sequence + string += stream.next(2); + } + else if (stream.look() == "'") { + string += stream.next(); + tokens.push(new JSDOC.Token(string, "STRN", "SINGLE_QUOTE")); + return true; + } + else { + string += stream.next(); + } + } + } + return false; // error! unterminated string +} + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_numb = function(/**JSDOC.TokenStream*/stream, tokens) { + if (stream.look() === "0" && stream.look(1) == "x") { + return this.read_hex(stream, tokens); + } + + var found = ""; + + while (!stream.look().eof && JSDOC.Lang.isNumber(found+stream.look())){ + found += stream.next(); + } + + if (found === "") { + return false; + } + else { + if (/^0[0-7]/.test(found)) tokens.push(new JSDOC.Token(found, "NUMB", "OCTAL")); + else tokens.push(new JSDOC.Token(found, "NUMB", "DECIMAL")); + return true; + } +} +/*t: + requires("../lib/JSDOC/TextStream.js"); + requires("../lib/JSDOC/Token.js"); + requires("../lib/JSDOC/Lang.js"); + + plan(3, "testing JSDOC.TokenReader.prototype.read_numb"); + + //// setup + var src = "function foo(num){while (num+8.0 >= 0x20 && num < 0777){}}"; + var tr = new JSDOC.TokenReader(); + var tokens = tr.tokenize(new JSDOC.TextStream(src)); + + var hexToken, octToken, decToken; + for (var i = 0; i < tokens.length; i++) { + if (tokens[i].name == "HEX_DEC") hexToken = tokens[i]; + if (tokens[i].name == "OCTAL") octToken = tokens[i]; + if (tokens[i].name == "DECIMAL") decToken = tokens[i]; + } + //// + + is(decToken.data, "8.0", "decimal number is found in source."); + is(hexToken.data, "0x20", "hexdec number is found in source (issue #99)."); + is(octToken.data, "0777", "octal number is found in source."); +*/ + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_hex = function(/**JSDOC.TokenStream*/stream, tokens) { + var found = stream.next(2); + + while (!stream.look().eof) { + if (JSDOC.Lang.isHexDec(found) && !JSDOC.Lang.isHexDec(found+stream.look())) { // done + tokens.push(new JSDOC.Token(found, "NUMB", "HEX_DEC")); + return true; + } + else { + found += stream.next(); + } + } + return false; +} + +/** + @returns {Boolean} Was the token found? + */ +JSDOC.TokenReader.prototype.read_regx = function(/**JSDOC.TokenStream*/stream, tokens) { + var last; + if ( + stream.look() == "/" + && + ( + + ( + !(last = tokens.lastSym()) // there is no last, the regex is the first symbol + || + ( + !last.is("NUMB") + && !last.is("NAME") + && !last.is("RIGHT_PAREN") + && !last.is("RIGHT_BRACKET") + ) + ) + ) + ) { + var regex = stream.next(); + + while (!stream.look().eof) { + if (stream.look() == "\\") { // escape sequence + regex += stream.next(2); + } + else if (stream.look() == "/") { + regex += stream.next(); + + while (/[gmi]/.test(stream.look())) { + regex += stream.next(); + } + + tokens.push(new JSDOC.Token(regex, "REGX", "REGX")); + return true; + } + else { + regex += stream.next(); + } + } + // error: unterminated regex + } + return false; +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/TokenStream.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/TokenStream.js new file mode 100644 index 00000000..1eeb44cb --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/TokenStream.js @@ -0,0 +1,133 @@ +if (typeof JSDOC == "undefined") JSDOC = {}; + +/** + @constructor +*/ +JSDOC.TokenStream = function(tokens) { + this.tokens = (tokens || []); + this.rewind(); +} + +/** + @constructor + @private +*/ +function VoidToken(/**String*/type) { + this.toString = function() {return ""}; + this.is = function(){return false;} +} + +JSDOC.TokenStream.prototype.rewind = function() { + this.cursor = -1; +} + +/** + @type JSDOC.Token +*/ +JSDOC.TokenStream.prototype.look = function(/**Number*/n, /**Boolean*/considerWhitespace) { + if (typeof n == "undefined") n = 0; + + if (considerWhitespace == true) { + if (this.cursor+n < 0 || this.cursor+n > this.tokens.length) return {}; + return this.tokens[this.cursor+n]; + } + else { + var count = 0; + var i = this.cursor; + + while (true) { + if (i < 0) return new JSDOC.Token("", "VOID", "START_OF_STREAM"); + else if (i > this.tokens.length) return new JSDOC.Token("", "VOID", "END_OF_STREAM"); + + if (i != this.cursor && (this.tokens[i] === undefined || this.tokens[i].is("WHIT"))) { + if (n < 0) i--; else i++; + continue; + } + + if (count == Math.abs(n)) { + return this.tokens[i]; + } + count++; + (n < 0)? i-- : i++; + } + + return new JSDOC.Token("", "VOID", "STREAM_ERROR"); // because null isn't an object and caller always expects an object + } +} + +/** + @type JSDOC.Token|JSDOC.Token[] +*/ +JSDOC.TokenStream.prototype.next = function(/**Number*/howMany) { + if (typeof howMany == "undefined") howMany = 1; + if (howMany < 1) return null; + var got = []; + + for (var i = 1; i <= howMany; i++) { + if (this.cursor+i >= this.tokens.length) { + return null; + } + got.push(this.tokens[this.cursor+i]); + } + this.cursor += howMany; + + if (howMany == 1) { + return got[0]; + } + else return got; +} + +/** + @type JSDOC.Token[] +*/ +JSDOC.TokenStream.prototype.balance = function(/**String*/start, /**String*/stop) { + if (!stop) stop = JSDOC.Lang.matching(start); + + var depth = 0; + var got = []; + var started = false; + + while ((token = this.look())) { + if (token.is(start)) { + depth++; + started = true; + } + + if (started) { + got.push(token); + } + + if (token.is(stop)) { + depth--; + if (depth == 0) return got; + } + if (!this.next()) break; + } +} + +JSDOC.TokenStream.prototype.getMatchingToken = function(/**String*/start, /**String*/stop) { + var depth = 0; + var cursor = this.cursor; + + if (!start) { + start = JSDOC.Lang.matching(stop); + depth = 1; + } + if (!stop) stop = JSDOC.Lang.matching(start); + + while ((token = this.tokens[cursor])) { + if (token.is(start)) { + depth++; + } + + if (token.is(stop) && cursor) { + depth--; + if (depth == 0) return this.tokens[cursor]; + } + cursor++; + } +} + +JSDOC.TokenStream.prototype.insertAhead = function(/**JSDOC.Token*/token) { + this.tokens.splice(this.cursor+1, 0, token); +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Util.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Util.js new file mode 100644 index 00000000..6d7edb36 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Util.js @@ -0,0 +1,32 @@ +/** + * @namespace + * @deprecated Use {@link FilePath} instead. + */ +JSDOC.Util = { +} + +/** + * @deprecated Use {@link FilePath.fileName} instead. + */ +JSDOC.Util.fileName = function(path) { + LOG.warn("JSDOC.Util.fileName is deprecated. Use FilePath.fileName instead."); + var nameStart = Math.max(path.lastIndexOf("/")+1, path.lastIndexOf("\\")+1, 0); + return path.substring(nameStart); +} + +/** + * @deprecated Use {@link FilePath.fileExtension} instead. + */ +JSDOC.Util.fileExtension = function(filename) { + LOG.warn("JSDOC.Util.fileExtension is deprecated. Use FilePath.fileExtension instead."); + return filename.split(".").pop().toLowerCase(); +}; + +/** + * @deprecated Use {@link FilePath.dir} instead. + */ +JSDOC.Util.dir = function(path) { + LOG.warn("JSDOC.Util.dir is deprecated. Use FilePath.dir instead."); + var nameStart = Math.max(path.lastIndexOf("/")+1, path.lastIndexOf("\\")+1, 0); + return path.substring(0, nameStart-1); +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Walker.js b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Walker.js new file mode 100644 index 00000000..6ecaea88 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/lib/JSDOC/Walker.js @@ -0,0 +1,507 @@ +if (typeof JSDOC == "undefined") JSDOC = {}; + +/** @constructor */ +JSDOC.Walker = function(/**JSDOC.TokenStream*/ts) { + this.init(); + if (typeof ts != "undefined") { + this.walk(ts); + } +} + +JSDOC.Walker.prototype.init = function() { + this.ts = null; + + var globalSymbol = new JSDOC.Symbol("_global_", [], "GLOBAL", new JSDOC.DocComment("")); + globalSymbol.isNamespace = true; + globalSymbol.srcFile = ""; + globalSymbol.isPrivate = false; + JSDOC.Parser.addSymbol(globalSymbol); + this.lastDoc = null; + this.token = null; + + /** + The chain of symbols under which we are currently nested. + @type Array + */ + this.namescope = [globalSymbol]; + this.namescope.last = function(n){ if (!n) n = 0; return this[this.length-(1+n)] || "" }; +} + +JSDOC.Walker.prototype.walk = function(/**JSDOC.TokenStream*/ts) { + this.ts = ts; + while (this.token = this.ts.look()) { + if (this.token.popNamescope) { + + var symbol = this.namescope.pop(); + if (symbol.is("FUNCTION")) { + if (this.ts.look(1).is("LEFT_PAREN") && symbol.comment.getTag("function").length == 0) { + symbol.isa = "OBJECT"; + } + } + } + this.step(); + if (!this.ts.next()) break; + } +} + +JSDOC.Walker.prototype.step = function() { + if (this.token.is("JSDOC")) { // it's a doc comment + + var doc = new JSDOC.DocComment(this.token.data); + + + if (doc.getTag("exports").length > 0) { + var exports = doc.getTag("exports")[0]; + + exports.desc.match(/(\S+) as (\S+)/i); + var n1 = RegExp.$1; + var n2 = RegExp.$2; + + if (!n1 && n2) throw "@exports tag requires a value like: 'name as ns.name'"; + + JSDOC.Parser.rename = (JSDOC.Parser.rename || {}); + JSDOC.Parser.rename[n1] = n2 + } + + if (doc.getTag("lends").length > 0) { + var lends = doc.getTag("lends")[0]; + + var name = lends.desc + if (!name) throw "@lends tag requires a value."; + + var symbol = new JSDOC.Symbol(name, [], "OBJECT", doc); + + this.namescope.push(symbol); + + var matching = this.ts.getMatchingToken("LEFT_CURLY"); + if (matching) matching.popNamescope = name; + else LOG.warn("Mismatched } character. Can't parse code in file " + symbol.srcFile + "."); + + this.lastDoc = null; + return true; + } + else if (doc.getTag("name").length > 0 && doc.getTag("overview").length == 0) { // it's a virtual symbol + var virtualName = doc.getTag("name")[0].desc; + if (!virtualName) throw "@name tag requires a value."; + + if (doc.getTag("memberOf").length > 0) { + virtualName = (doc.getTag("memberOf")[0] + "." + virtualName) + .replace(/([#.])\./, "$1"); + doc.deleteTag("memberOf"); + } + + var symbol = new JSDOC.Symbol(virtualName, [], "VIRTUAL", doc); + + JSDOC.Parser.addSymbol(symbol); + + this.lastDoc = null; + return true; + } + else if (doc.meta) { // it's a meta doclet + if (doc.meta == "@+") JSDOC.DocComment.shared = doc.src; + else if (doc.meta == "@-") JSDOC.DocComment.shared = ""; + else if (doc.meta == "nocode+") JSDOC.Parser.conf.ignoreCode = true; + else if (doc.meta == "nocode-") JSDOC.Parser.conf.ignoreCode = JSDOC.opt.n; + else throw "Unrecognized meta comment: "+doc.meta; + + this.lastDoc = null; + return true; + } + else if (doc.getTag("overview").length > 0) { // it's a file overview + symbol = new JSDOC.Symbol("", [], "FILE", doc); + + JSDOC.Parser.addSymbol(symbol); + + this.lastDoc = null; + return true; + } + else { + this.lastDoc = doc; + return false; + } + } + else if (!JSDOC.Parser.conf.ignoreCode) { // it's code + if (this.token.is("NAME")) { // it's the name of something + var symbol; + var name = this.token.data; + var doc = null; if (this.lastDoc) doc = this.lastDoc; + var params = []; + + // it's inside an anonymous object + if (this.ts.look(1).is("COLON") && this.ts.look(-1).is("LEFT_CURLY") && !(this.ts.look(-2).is("JSDOC") || this.namescope.last().comment.getTag("lends").length || this.ts.look(-2).is("ASSIGN") || this.ts.look(-2).is("COLON"))) { + name = "$anonymous"; + name = this.namescope.last().alias+"-"+name + + params = []; + + symbol = new JSDOC.Symbol(name, params, "OBJECT", doc); + + JSDOC.Parser.addSymbol(symbol); + + this.namescope.push(symbol); + + var matching = this.ts.getMatchingToken(null, "RIGHT_CURLY"); + if (matching) matching.popNamescope = name; + else LOG.warn("Mismatched } character. Can't parse code in file " + symbol.srcFile + "."); + } + // function foo() {} + else if (this.ts.look(-1).is("FUNCTION") && this.ts.look(1).is("LEFT_PAREN")) { + var isInner; + + if (this.lastDoc) doc = this.lastDoc; + + if (doc && doc.getTag("memberOf").length > 0) { + name = (doc.getTag("memberOf")[0]+"."+name).replace("#.", "#"); + doc.deleteTag("memberOf"); + } + else { + name = this.namescope.last().alias+"-"+name; + if (!this.namescope.last().is("GLOBAL")) isInner = true; + } + + if (!this.namescope.last().is("GLOBAL")) isInner = true; + + params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN")); + + symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc); + if (isInner) symbol.isInner = true; + + if (this.ts.look(1).is("JSDOC")) { + var inlineReturn = ""+this.ts.look(1).data; + inlineReturn = inlineReturn.replace(/(^\/\*\* *| *\*\/$)/g, ""); + symbol.type = inlineReturn; + } + + JSDOC.Parser.addSymbol(symbol); + + this.namescope.push(symbol); + + var matching = this.ts.getMatchingToken("LEFT_CURLY"); + if (matching) matching.popNamescope = name; + else LOG.warn("Mismatched } character. Can't parse code in file " + symbol.srcFile + "."); + } + // foo = function() {} + else if (this.ts.look(1).is("ASSIGN") && this.ts.look(2).is("FUNCTION")) { + var constructs; + var isConstructor = false; + if (doc && (constructs = doc.getTag("constructs")) && constructs.length) { + if (constructs[0].desc) { + name = constructs[0].desc; + isConstructor = true; + } + } + + var isInner; + if (this.ts.look(-1).is("VAR") || this.isInner) { + if (doc && doc.getTag("memberOf").length > 0) { + name = (doc.getTag("memberOf")[0]+"."+name).replace("#.", "#"); + doc.deleteTag("memberOf"); + } + else { + name = this.namescope.last().alias+"-"+name; + if (!this.namescope.last().is("GLOBAL")) isInner = true; + } + if (!this.namescope.last().is("GLOBAL")) isInner = true; + } + else if (name.indexOf("this.") == 0) { + name = this.resolveThis(name); + } + + if (this.lastDoc) doc = this.lastDoc; + params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN")); + + symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc); + + if (isInner) symbol.isInner = true; + if (isConstructor) symbol.isa = "CONSTRUCTOR"; + + if (this.ts.look(1).is("JSDOC")) { + var inlineReturn = ""+this.ts.look(1).data; + inlineReturn = inlineReturn.replace(/(^\/\*\* *| *\*\/$)/g, ""); + symbol.type = inlineReturn; + } + + JSDOC.Parser.addSymbol(symbol); + + this.namescope.push(symbol); + + var matching = this.ts.getMatchingToken("LEFT_CURLY"); + if (matching) matching.popNamescope = name; + else LOG.warn("Mismatched } character. Can't parse code in file " + symbol.srcFile + "."); + } + // foo = new function() {} or foo = (function() {} + else if (this.ts.look(1).is("ASSIGN") && (this.ts.look(2).is("NEW") || this.ts.look(2).is("LEFT_PAREN")) && this.ts.look(3).is("FUNCTION")) { + var isInner; + if (this.ts.look(-1).is("VAR") || this.isInner) { + name = this.namescope.last().alias+"-"+name + if (!this.namescope.last().is("GLOBAL")) isInner = true; + } + else if (name.indexOf("this.") == 0) { + name = this.resolveThis(name); + } + + this.ts.next(3); // advance past the "new" or "(" + + if (this.lastDoc) doc = this.lastDoc; + params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN")); + + symbol = new JSDOC.Symbol(name, params, "OBJECT", doc); + if (isInner) symbol.isInner = true; + + if (this.ts.look(1).is("JSDOC")) { + var inlineReturn = ""+this.ts.look(1).data; + inlineReturn = inlineReturn.replace(/(^\/\*\* *| *\*\/$)/g, ""); + symbol.type = inlineReturn; + } + + JSDOC.Parser.addSymbol(symbol); + + symbol.scopeType = "INSTANCE"; + this.namescope.push(symbol); + + var matching = this.ts.getMatchingToken("LEFT_CURLY"); + if (matching) matching.popNamescope = name; + else LOG.warn("Mismatched } character. Can't parse code in file " + symbol.srcFile + "."); + } + // foo: function() {} + else if (this.ts.look(1).is("COLON") && this.ts.look(2).is("FUNCTION")) { + name = (this.namescope.last().alias+"."+name).replace("#.", "#"); + + if (this.lastDoc) doc = this.lastDoc; + params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN")); + + if (doc && doc.getTag("constructs").length) { + name = name.replace(/\.prototype(\.|$)/, "#"); + + if (name.indexOf("#") > -1) name = name.match(/(^[^#]+)/)[0]; + else name = this.namescope.last().alias; + + symbol = new JSDOC.Symbol(name, params, "CONSTRUCTOR", doc); + } + else { + symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc); + } + + if (this.ts.look(1).is("JSDOC")) { + var inlineReturn = ""+this.ts.look(1).data; + inlineReturn = inlineReturn.replace(/(^\/\*\* *| *\*\/$)/g, ""); + symbol.type = inlineReturn; + } + + JSDOC.Parser.addSymbol(symbol); + + this.namescope.push(symbol); + + var matching = this.ts.getMatchingToken("LEFT_CURLY"); + if (matching) matching.popNamescope = name; + else LOG.warn("Mismatched } character. Can't parse code in file " + symbol.srcFile + "."); + } + // foo = {} + else if (this.ts.look(1).is("ASSIGN") && this.ts.look(2).is("LEFT_CURLY")) { + var isInner; + if (this.ts.look(-1).is("VAR") || this.isInner) { + name = this.namescope.last().alias+"-"+name + if (!this.namescope.last().is("GLOBAL")) isInner = true; + } + else if (name.indexOf("this.") == 0) { + name = this.resolveThis(name); + } + + if (this.lastDoc) doc = this.lastDoc; + + symbol = new JSDOC.Symbol(name, params, "OBJECT", doc); + if (isInner) symbol.isInner = true; + + + if (doc) JSDOC.Parser.addSymbol(symbol); + + this.namescope.push(symbol); + + var matching = this.ts.getMatchingToken("LEFT_CURLY"); + if (matching) matching.popNamescope = name; + else LOG.warn("Mismatched } character. Can't parse code in file " + symbol.srcFile + "."); + } + // var foo; + else if (this.ts.look(1).is("SEMICOLON")) { + var isInner; + + if (this.ts.look(-1).is("VAR") || this.isInner) { + name = this.namescope.last().alias+"-"+name + if (!this.namescope.last().is("GLOBAL")) isInner = true; + + if (this.lastDoc) doc = this.lastDoc; + + symbol = new JSDOC.Symbol(name, params, "OBJECT", doc); + if (isInner) symbol.isInner = true; + + + if (doc) JSDOC.Parser.addSymbol(symbol); + } + } + // foo = x + else if (this.ts.look(1).is("ASSIGN")) { + var isInner; + if (this.ts.look(-1).is("VAR") || this.isInner) { + name = this.namescope.last().alias+"-"+name + if (!this.namescope.last().is("GLOBAL")) isInner = true; + } + else if (name.indexOf("this.") == 0) { + name = this.resolveThis(name); + } + + if (this.lastDoc) doc = this.lastDoc; + + symbol = new JSDOC.Symbol(name, params, "OBJECT", doc); + if (isInner) symbol.isInner = true; + + + if (doc) JSDOC.Parser.addSymbol(symbol); + } + // foo: {} + else if (this.ts.look(1).is("COLON") && this.ts.look(2).is("LEFT_CURLY")) { + name = (this.namescope.last().alias+"."+name).replace("#.", "#"); + + if (this.lastDoc) doc = this.lastDoc; + + symbol = new JSDOC.Symbol(name, params, "OBJECT", doc); + + + if (doc) JSDOC.Parser.addSymbol(symbol); + + this.namescope.push(symbol); + + var matching = this.ts.getMatchingToken("LEFT_CURLY"); + if (matching) matching.popNamescope = name; + else LOG.warn("Mismatched } character. Can't parse code in file " + symbol.srcFile + "."); + } + // foo: x + else if (this.ts.look(1).is("COLON")) { + name = (this.namescope.last().alias+"."+name).replace("#.", "#");; + + if (this.lastDoc) doc = this.lastDoc; + + symbol = new JSDOC.Symbol(name, params, "OBJECT", doc); + + + if (doc) JSDOC.Parser.addSymbol(symbol); + } + // foo(...) + else if (this.ts.look(1).is("LEFT_PAREN")) { + if (typeof JSDOC.PluginManager != "undefined") { + var functionCall = {name: name}; + + var cursor = this.ts.cursor; + params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN")); + this.ts.cursor = cursor; + + for (var i = 0; i < params.length; i++) + functionCall["arg" + (i + 1)] = params[i].name; + + JSDOC.PluginManager.run("onFunctionCall", functionCall); + if (functionCall.doc) { + this.ts.insertAhead(new JSDOC.Token(functionCall.doc, "COMM", "JSDOC")); + } + } + } + this.lastDoc = null; + } + else if (this.token.is("FUNCTION")) { // it's an anonymous function + if ( + (!this.ts.look(-1).is("COLON") || !this.ts.look(-1).is("ASSIGN")) + && !this.ts.look(1).is("NAME") + ) { + if (this.lastDoc) doc = this.lastDoc; + + name = "$anonymous"; + name = this.namescope.last().alias+"-"+name + + params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN")); + + symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc); + + JSDOC.Parser.addSymbol(symbol); + + this.namescope.push(symbol); + + var matching = this.ts.getMatchingToken("LEFT_CURLY"); + if (matching) matching.popNamescope = name; + else LOG.warn("Mismatched } character. Can't parse code in file " + symbol.srcFile + "."); + } + } + } + return true; +} + +/** + Resolves what "this." means when it appears in a name. + @param name The name that starts with "this.". + @returns The name with "this." resolved. + */ +JSDOC.Walker.prototype.resolveThis = function(name) { + name.match(/^this\.(.+)$/) + var nameFragment = RegExp.$1; + if (!nameFragment) return name; + + var symbol = this.namescope.last(); + var scopeType = symbol.scopeType || symbol.isa; + + // if we are in a constructor function, `this` means the instance + if (scopeType == "CONSTRUCTOR") { + name = symbol.alias+"#"+nameFragment; + } + + // if we are in an anonymous constructor function, `this` means the instance + else if (scopeType == "INSTANCE") { + name = symbol.alias+"."+nameFragment; + } + + // if we are in a function, `this` means the container (possibly the global) + else if (scopeType == "FUNCTION") { + // in a method of a prototype, so `this` means the constructor + if (symbol.alias.match(/(^.*)[#.-][^#.-]+/)) { + var parentName = RegExp.$1; + var parent = JSDOC.Parser.symbols.getSymbol(parentName); + + if (!parent) { + if (JSDOC.Lang.isBuiltin(parentName)) parent = JSDOC.Parser.addBuiltin(parentName); + else { + if (symbol.alias.indexOf("$anonymous") < 0) // these will be ignored eventually + LOG.warn("Trying to document "+symbol.alias+" without first documenting "+parentName+"."); + } + } + if (parent) name = parentName+(parent.is("CONSTRUCTOR")?"#":".")+nameFragment; + } + else { + parent = this.namescope.last(1); + name = parent.alias+(parent.is("CONSTRUCTOR")?"#":".")+nameFragment; + } + } + // otherwise it means the global + else { + name = nameFragment; + } + + return name; +} + +JSDOC.Walker.onParamList = function(/**Array*/paramTokens) { + if (!paramTokens) { + LOG.warn("Malformed parameter list. Can't parse code."); + return []; + } + var params = []; + for (var i = 0, l = paramTokens.length; i < l; i++) { + if (paramTokens[i].is("JSDOC")) { + var paramType = paramTokens[i].data.replace(/(^\/\*\* *| *\*\/$)/g, ""); + + if (paramTokens[i+1] && paramTokens[i+1].is("NAME")) { + i++; + params.push({type: paramType, name: paramTokens[i].data}); + } + } + else if (paramTokens[i].is("NAME")) { + params.push({name: paramTokens[i].data}); + } + } + return params; +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/main.js b/jsdoc_toolkit-2.3.3-beta/app/main.js new file mode 100644 index 00000000..f9008c87 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/main.js @@ -0,0 +1,111 @@ +/** + * @version $Id: main.js 818 2009-11-08 14:51:41Z micmath $ + */ + +function main() { + IO.include("lib/JSDOC.js"); + IO.includeDir("plugins/"); + + // process the options + + // the -c option: options are defined in a configuration file + if (JSDOC.opt.c) { + eval("JSDOC.conf = " + IO.readFile(JSDOC.opt.c)); + + LOG.inform("Using configuration file at '"+JSDOC.opt.c+"'."); + + for (var c in JSDOC.conf) { + if (c !== "D" && !defined(JSDOC.opt[c])) { // commandline overrules config file + JSDOC.opt[c] = JSDOC.conf[c]; + } + } + + if (typeof JSDOC.conf["_"] != "undefined") { + JSDOC.opt["_"] = JSDOC.opt["_"].concat(JSDOC.conf["_"]); + } + + LOG.inform("With configuration: "); + for (var o in JSDOC.opt) { + LOG.inform(" "+o+": "+JSDOC.opt[o]); + } + } + + // be verbose + if (JSDOC.opt.v) LOG.verbose = true; + + // send log messages to a file + if (JSDOC.opt.o) LOG.out = IO.open(JSDOC.opt.o); + + // run the unit tests + if (JSDOC.opt.T) { + LOG.inform("JsDoc Toolkit running in test mode at "+new Date()+"."); + IO.include("frame/Testrun.js"); + IO.include("test.js"); + } + else { + // a template must be defined and must be a directory path + if (!JSDOC.opt.t && System.getProperty("jsdoc.template.dir")) { + JSDOC.opt.t = System.getProperty("jsdoc.template.dir"); + } + if (JSDOC.opt.t && SYS.slash != JSDOC.opt.t.slice(-1)) { + JSDOC.opt.t += SYS.slash; + } + + // verbose messages about the options we were given + LOG.inform("JsDoc Toolkit main() running at "+new Date()+"."); + LOG.inform("With options: "); + for (var o in JSDOC.opt) { + LOG.inform(" "+o+": "+JSDOC.opt[o]); + } + + // initialize and build a symbolSet from your code + JSDOC.JsDoc(); + + // debugger's option: dump the entire symbolSet produced from your code + if (JSDOC.opt.Z) { + LOG.warn("So you want to see the data structure, eh? This might hang if you have circular refs..."); + IO.include("frame/Dumper.js"); + var symbols = JSDOC.JsDoc.symbolSet.toArray(); + for (var i = 0, l = symbols.length; i < l; i++) { + var symbol = symbols[i]; + print("// symbol: " + symbol.alias); + print(symbol.serialize()); + } + } + else { + if (typeof JSDOC.opt.t != "undefined") { + try { + // a file named "publish.js" must exist in the template directory + load(JSDOC.opt.t+"publish.js"); + + // and must define a function named "publish" + if (!publish) { + LOG.warn("No publish() function is defined in that template so nothing to do."); + } + else { + // which will be called with the symbolSet produced from your code + publish(JSDOC.JsDoc.symbolSet); + } + } + catch(e) { + LOG.warn("Sorry, that doesn't seem to be a valid template: "+JSDOC.opt.t+"publish.js : "+e); + } + } + else { + LOG.warn("No template given. Might as well read the usage notes."); + JSDOC.usage(); + } + } + } + + // notify of any warnings + if (!JSDOC.opt.q && LOG.warnings.length) { + print(LOG.warnings.length+" warning"+(LOG.warnings.length != 1? "s":"")+"."); + } + + // stop sending log messages to a file + if (LOG.out) { + LOG.out.flush(); + LOG.out.close(); + } +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/plugins/commentSrcJson.js b/jsdoc_toolkit-2.3.3-beta/app/plugins/commentSrcJson.js new file mode 100644 index 00000000..e826b572 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/plugins/commentSrcJson.js @@ -0,0 +1,20 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.commentSrcJson", + { + onDocCommentSrc: function(comment) { + var json; + if (/^\s*@json\b/.test(comment)) { + comment.src = new String(comment.src).replace("@json", ""); + + eval("json = "+comment.src); + var tagged = ""; + for (var i in json) { + var tag = json[i]; + // todo handle cases where tag is an object + tagged += "@"+i+" "+tag+"\n"; + } + comment.src = tagged; + } + } + } +); \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/plugins/frameworkPrototype.js b/jsdoc_toolkit-2.3.3-beta/app/plugins/frameworkPrototype.js new file mode 100644 index 00000000..9c417518 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/plugins/frameworkPrototype.js @@ -0,0 +1,16 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.frameworkPrototype", + { + onPrototypeClassCreate: function(classCreator) { + var desc = ""; + if (classCreator.comment) { + desc = classCreator.comment; + } + var insert = desc+"/** @name "+classCreator.name+"\n@constructor\n@scope "+classCreator.name+".prototype */" + + insert = insert.replace(/\*\/\/\*\*/g, "\n"); + /*DEBUG*///print("insert is "+insert); + classCreator.addComment.data = insert; + } + } +); diff --git a/jsdoc_toolkit-2.3.3-beta/app/plugins/functionCall.js b/jsdoc_toolkit-2.3.3-beta/app/plugins/functionCall.js new file mode 100644 index 00000000..6f87705e --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/plugins/functionCall.js @@ -0,0 +1,10 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.functionCall", + { + onFunctionCall: function(functionCall) { + if (functionCall.name == "dojo.define" && functionCall.arg1) { + functionCall.doc = "/** @lends "+eval(functionCall.arg1)+".prototype */"; + } + } + } +); \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/plugins/publishSrcHilite.js b/jsdoc_toolkit-2.3.3-beta/app/plugins/publishSrcHilite.js new file mode 100644 index 00000000..65514f2c --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/plugins/publishSrcHilite.js @@ -0,0 +1,62 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.publishSrcHilite", + { + onPublishSrc: function(src) { + if (src.path in JsHilite.cache) { + return; // already generated src code + } + else JsHilite.cache[src.path] = true; + + try { + var sourceCode = IO.readFile(src.path); + } + catch(e) { + print(e.message); + quit(); + } + + var hiliter = new JsHilite(sourceCode, src.charset); + src.hilited = hiliter.hilite(); + } + } +); + +function JsHilite(src, charset) { + + var tr = new JSDOC.TokenReader(); + + tr.keepComments = true; + tr.keepDocs = true; + tr.keepWhite = true; + + this.tokens = tr.tokenize(new JSDOC.TextStream(src)); + + // TODO is redefining toString() the best way? + JSDOC.Token.prototype.toString = function() { + return ""+this.data.replace(/"; + } + + if (!charset) charset = "utf-8"; + + this.header = ' '+ + "

";
+	this.footer = "
"; + this.showLinenumbers = true; +} + +JsHilite.cache = {}; + +JsHilite.prototype.hilite = function() { + var hilited = this.tokens.join(""); + var line = 1; + if (this.showLinenumbers) hilited = hilited.replace(/(^|\n)/g, function(m){return m+""+((line<10)? " ":"")+((line<100)? " ":"")+(line++)+" "}); + + return this.header+hilited+this.footer; +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/plugins/symbolLink.js b/jsdoc_toolkit-2.3.3-beta/app/plugins/symbolLink.js new file mode 100644 index 00000000..c87f1ca7 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/plugins/symbolLink.js @@ -0,0 +1,10 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.symbolLink", + { + onSymbolLink: function(link) { + // modify link.linkPath (the href part of the link) + // or link.linkText (the text displayed) + // or link.linkInner (the #name part of the link) + } + } +); \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/plugins/tagParamConfig.js b/jsdoc_toolkit-2.3.3-beta/app/plugins/tagParamConfig.js new file mode 100644 index 00000000..3ea8a1be --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/plugins/tagParamConfig.js @@ -0,0 +1,31 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.tagParamConfig", + { + onDocCommentTags: function(comment) { + var currentParam = null; + var tags = comment.tags; + for (var i = 0, l = tags.length; i < l; i++) { + + if (tags[i].title == "param") { + if (tags[i].name.indexOf(".") == -1) { + currentParam = i; + } + } + else if (tags[i].title == "config") { + tags[i].title = "param"; + if (currentParam == null) { + tags[i].name = "arguments"+"."+tags[i].name; + } + else if (tags[i].name.indexOf(tags[currentParam].name+".") != 0) { + tags[i].name = tags[currentParam].name+"."+tags[i].name; + } + currentParam != null + //tags[currentParam].properties.push(tags[i]); + } + else { + currentParam = null; + } + } + } + } +); diff --git a/jsdoc_toolkit-2.3.3-beta/app/plugins/tagSynonyms.js b/jsdoc_toolkit-2.3.3-beta/app/plugins/tagSynonyms.js new file mode 100644 index 00000000..49a874f1 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/plugins/tagSynonyms.js @@ -0,0 +1,43 @@ +JSDOC.PluginManager.registerPlugin( + "JSDOC.tagSynonyms", + { + onDocCommentSrc: function(comment) { + comment.src = comment.src.replace(/@methodOf\b/i, "@function\n@memberOf"); + comment.src = comment.src.replace(/@fieldOf\b/i, "@field\n@memberOf"); + }, + + onDocCommentTags: function(comment) { + for (var i = 0, l = comment.tags.length; i < l; i++) { + var title = comment.tags[i].title.toLowerCase(); + var syn; + if ((syn = JSDOC.tagSynonyms.synonyms["="+title])) { + comment.tags[i].title = syn; + } + } + } + } +); + +new Namespace( + "JSDOC.tagSynonyms", + function() { + JSDOC.tagSynonyms.synonyms = { + "=member": "memberOf", + "=memberof": "memberOf", + "=description": "desc", + "=exception": "throws", + "=argument": "param", + "=returns": "return", + "=classdescription": "class", + "=fileoverview": "overview", + "=extends": "augments", + "=base": "augments", + "=projectdescription": "overview", + "=classdescription": "class", + "=link": "see", + "=borrows": "inherits", + "=scope": "lends", + "=construct": "constructor" + } + } +); \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/run.js b/jsdoc_toolkit-2.3.3-beta/app/run.js new file mode 100644 index 00000000..1f875cdb --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/run.js @@ -0,0 +1,348 @@ +/** + * @fileOverview + * A bootstrap script that creates some basic required objects + * for loading other scripts. + * @author Michael Mathews, micmath@gmail.com + * @version $Id: run.js 756 2009-01-07 21:32:58Z micmath $ + */ + +/** + * @namespace Keep track of any messages from the running script. + */ +LOG = { + warn: function(msg, e) { + if (JSDOC.opt.q) return; + if (e) msg = e.fileName+", line "+e.lineNumber+": "+msg; + + msg = ">> WARNING: "+msg; + LOG.warnings.push(msg); + if (LOG.out) LOG.out.write(msg+"\n"); + else print(msg); + }, + + inform: function(msg) { + if (JSDOC.opt.q) return; + msg = " > "+msg; + if (LOG.out) LOG.out.write(msg+"\n"); + else if (typeof LOG.verbose != "undefined" && LOG.verbose) print(msg); + } +}; +LOG.warnings = []; +LOG.verbose = false +LOG.out = undefined; + +/** + * @class Manipulate a filepath. + */ +function FilePath(absPath, separator) { + this.slash = separator || "/"; + this.root = this.slash; + this.path = []; + this.file = ""; + + var parts = absPath.split(/[\\\/]/); + if (parts) { + if (parts.length) this.root = parts.shift() + this.slash; + if (parts.length) this.file = parts.pop() + if (parts.length) this.path = parts; + } + + this.path = this.resolvePath(); +} + +/** Collapse any dot-dot or dot items in a filepath. */ +FilePath.prototype.resolvePath = function() { + var resolvedPath = []; + for (var i = 0; i < this.path.length; i++) { + if (this.path[i] == "..") resolvedPath.pop(); + else if (this.path[i] != ".") resolvedPath.push(this.path[i]); + } + return resolvedPath; +} + +/** Trim off the filename. */ +FilePath.prototype.toDir = function() { + if (this.file) this.file = ""; + return this; +} + +/** Go up a directory. */ +FilePath.prototype.upDir = function() { + this.toDir(); + if (this.path.length) this.path.pop(); + return this; +} + +FilePath.prototype.toString = function() { + return this.root + + this.path.join(this.slash) + + ((this.path.length > 0)? this.slash : "") + + this.file; +} + +/** + * Turn a path into just the name of the file. + */ +FilePath.fileName = function(path) { + var nameStart = Math.max(path.lastIndexOf("/")+1, path.lastIndexOf("\\")+1, 0); + return path.substring(nameStart); +} + +/** + * Get the extension of a filename + */ +FilePath.fileExtension = function(filename) { + return filename.split(".").pop().toLowerCase(); +}; + +/** + * Turn a path into just the directory part. + */ +FilePath.dir = function(path) { + var nameStart = Math.max(path.lastIndexOf("/")+1, path.lastIndexOf("\\")+1, 0); + return path.substring(0, nameStart-1); +} + + +importClass(java.lang.System); + +/** + * @namespace A collection of information about your system. + */ +SYS = { + /** + * Information about your operating system: arch, name, version. + * @type string + */ + os: [ + new String(System.getProperty("os.arch")), + new String(System.getProperty("os.name")), + new String(System.getProperty("os.version")) + ].join(", "), + + /** + * Which way does your slash lean. + * @type string + */ + slash: System.getProperty("file.separator")||"/", + + /** + * The path to the working directory where you ran java. + * @type string + */ + userDir: new String(System.getProperty("user.dir")), + + /** + * Where is Java's home folder. + * @type string + */ + javaHome: new String(System.getProperty("java.home")), + + /** + * The absolute path to the directory containing this script. + * @type string + */ + pwd: undefined +}; + +// jsrun appends an argument, with the path to here. +if (arguments[arguments.length-1].match(/^-j=(.+)/)) { + if (RegExp.$1.charAt(0) == SYS.slash || RegExp.$1.charAt(1) == ":") { // absolute path to here + SYS.pwd = new FilePath(RegExp.$1).toDir().toString(); + } + else { // relative path to here + SYS.pwd = new FilePath(SYS.userDir + SYS.slash + RegExp.$1).toDir().toString(); + } + arguments.pop(); +} +else { + print("The run.js script requires you use jsrun.jar."); + quit(); +} + +// shortcut +var File = Packages.java.io.File; + +/** + * @namespace A collection of functions that deal with reading a writing to disk. + */ +IO = { + + /** + * Create a new file in the given directory, with the given name and contents. + */ + saveFile: function(/**string*/ outDir, /**string*/ fileName, /**string*/ content) { + var out = new Packages.java.io.PrintWriter( + new Packages.java.io.OutputStreamWriter( + new Packages.java.io.FileOutputStream(outDir+SYS.slash+fileName), + IO.encoding + ) + ); + out.write(content); + out.flush(); + out.close(); + }, + + /** + * @type string + */ + readFile: function(/**string*/ path) { + if (!IO.exists(path)) { + throw "File doesn't exist there: "+path; + } + return readFile(path, IO.encoding); + }, + + /** + * @param inFile + * @param outDir + * @param [fileName=The original filename] + */ + copyFile: function(/**string*/ inFile, /**string*/ outDir, /**string*/ fileName) { + if (fileName == null) fileName = FilePath.fileName(inFile); + + var inFile = new File(inFile); + var outFile = new File(outDir+SYS.slash+fileName); + + var bis = new Packages.java.io.BufferedInputStream(new Packages.java.io.FileInputStream(inFile), 4096); + var bos = new Packages.java.io.BufferedOutputStream(new Packages.java.io.FileOutputStream(outFile), 4096); + var theChar; + while ((theChar = bis.read()) != -1) { + bos.write(theChar); + } + bos.close(); + bis.close(); + }, + + /** + * Creates a series of nested directories. + */ + mkPath: function(/**Array*/ path) { + if (path.constructor != Array) path = path.split(/[\\\/]/); + var make = ""; + for (var i = 0, l = path.length; i < l; i++) { + make += path[i] + SYS.slash; + if (! IO.exists(make)) { + IO.makeDir(make); + } + } + }, + + /** + * Creates a directory at the given path. + */ + makeDir: function(/**string*/ path) { + (new File(path)).mkdir(); + }, + + /** + * @type string[] + * @param dir The starting directory to look in. + * @param [recurse=1] How many levels deep to scan. + * @returns An array of all the paths to files in the given dir. + */ + ls: function(/**string*/ dir, /**number*/ recurse, _allFiles, _path) { + if (_path === undefined) { // initially + var _allFiles = []; + var _path = [dir]; + } + if (_path.length == 0) return _allFiles; + if (recurse === undefined) recurse = 1; + + dir = new File(dir); + if (!dir.directory) return [String(dir)]; + var files = dir.list(); + + for (var f = 0; f < files.length; f++) { + var file = String(files[f]); + if (file.match(/^\.[^\.\/\\]/)) continue; // skip dot files + + if ((new File(_path.join(SYS.slash)+SYS.slash+file)).list()) { // it's a directory + _path.push(file); + if (_path.length-1 < recurse) IO.ls(_path.join(SYS.slash), recurse, _allFiles, _path); + _path.pop(); + } + else { + _allFiles.push((_path.join(SYS.slash)+SYS.slash+file).replace(SYS.slash+SYS.slash, SYS.slash)); + } + } + + return _allFiles; + }, + + /** + * @type boolean + */ + exists: function(/**string*/ path) { + file = new File(path); + + if (file.isDirectory()){ + return true; + } + if (!file.exists()){ + return false; + } + if (!file.canRead()){ + return false; + } + return true; + }, + + /** + * + */ + open: function(/**string*/ path, /**string*/ append) { + var append = true; + var outFile = new File(path); + var out = new Packages.java.io.PrintWriter( + new Packages.java.io.OutputStreamWriter( + new Packages.java.io.FileOutputStream(outFile, append), + IO.encoding + ) + ); + return out; + }, + + /** + * Sets {@link IO.encoding}. + * Encoding is used when reading and writing text to files, + * and in the meta tags of HTML output. + */ + setEncoding: function(/**string*/ encoding) { + if (/ISO-8859-([0-9]+)/i.test(encoding)) { + IO.encoding = "ISO8859_"+RegExp.$1; + } + else { + IO.encoding = encoding; + } + }, + + /** + * @default "utf-8" + * @private + */ + encoding: "utf-8", + + /** + * Load the given script. + */ + include: function(relativePath) { + load(SYS.pwd+relativePath); + }, + + /** + * Loads all scripts from the given directory path. + */ + includeDir: function(path) { + if (!path) return; + + for (var lib = IO.ls(SYS.pwd+path), i = 0; i < lib.length; i++) + if (/\.js$/i.test(lib[i])) load(lib[i]); + } +} + +// now run the application +IO.include("frame.js"); +IO.include("main.js"); + +main(); diff --git a/jsdoc_toolkit-2.3.3-beta/app/t/TestDoc.js b/jsdoc_toolkit-2.3.3-beta/app/t/TestDoc.js new file mode 100644 index 00000000..c0768b71 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/t/TestDoc.js @@ -0,0 +1,144 @@ +var TestDoc = { + fails: 0, + plans: 0, + passes: 0, + results: [] +}; + +TestDoc.record = function(result) { + TestDoc.results.push(result); + if (typeof result.verdict == "boolean") { + if (result.verdict === false) TestDoc.fails++; + if (result.verdict === true) TestDoc.passes++; + } +} + +TestDoc.prove = function(filePath) { + if (typeof document != "undefined" && typeof document.write != "undefined") { + if (TestDoc.console) print = function(s) { TestDoc.console.appendChild(document.createTextNode(s+"\n")); } + else print = function(s) { document.write(s+"
"); } + } + TestDoc.run(TestDoc.readFile(filePath)); +} + +TestDoc.run = function(src) { + try { eval(src); } catch(e) { print("# ERROR! "+e); } + + var chunks = src.split(/\/\*t:/); + + var run = function(chunk) { + // local shortcuts + var is = TestDoc.assertEquals; + var isnt = TestDoc.assertNotEquals; + var plan = TestDoc.plan; + var requires = TestDoc.requires; + + try { eval(chunk); } catch(e) { print("# ERROR! "+e); } + } + for (var start = -1, end = 0; (start = src.indexOf("/*t:", end)) > end; start = end) { + run( + src.substring( + start+4, + (end = src.indexOf("*/", start)) + ) + ); + } +} + +TestDoc.Result = function(verdict, message) { + this.verdict = verdict; + this.message = message; +} + +TestDoc.Result.prototype.toString = function() { + if (typeof this.verdict == "boolean") { + return (this.verdict? "ok" : "not ok") + " " + (++TestDoc.report.counter) + " - " + this.message; + } + + return "# " + this.message; +} + +TestDoc.requires = function(file) { + if (!TestDoc.requires.loaded[file]) { + load(file); + TestDoc.requires.loaded[file] = true; + } +} +TestDoc.requires.loaded = {}; + +TestDoc.report = function() { + TestDoc.report.counter = 0; + print("1.."+TestDoc.plans); + for (var i = 0; i < TestDoc.results.length; i++) { + print(TestDoc.results[i]); + } + print("----------------------------------------"); + if (TestDoc.fails == 0 && TestDoc.passes == TestDoc.plans) { + print("All tests successful."); + } + else { + print("Failed " + TestDoc.fails + "/" + TestDoc.plans + " tests, "+((TestDoc.plans == 0)? 0 : Math.round(TestDoc.passes/(TestDoc.passes+TestDoc.fails)*10000)/100)+"% okay. Planned to run "+TestDoc.plans+", did run "+(TestDoc.passes+TestDoc.fails)+".") + } +} + +TestDoc.plan = function(n, message) { + TestDoc.plans += n; + TestDoc.record(new TestDoc.Result(null, message+" ("+n+" tests)")); +} + +TestDoc.assertEquals = function(a, b, message) { + var result = (a == b); + if (!result) message += "\n#\n# " + a + " does not equal " + b + "\n#"; + TestDoc.record(new TestDoc.Result(result, message)); +} + +TestDoc.assertNotEquals = function(a, b, message) { + var result = (a != b); + if (!result) message += "\n#\n# " + a + " equals " + b + "\n#"; + TestDoc.record(new TestDoc.Result(result, message)); +} + +TestDoc.readFile = (function(){ + // rhino + if (typeof readFile == "function") { + return function(url) { + var text = readFile(url); + return text || ""; + } + } + + // a web browser + else { + return function(url) { + var httpRequest; + + if (window.XMLHttpRequest) { // Mozilla, Safari, etc + httpRequest = new XMLHttpRequest(); + } + else if (window.ActiveXObject) { // IE + try { + httpRequest = new ActiveXObject("Msxml2.XMLHTTP"); + } + catch (e) { + try { + httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); + } + catch (e) { + } + } + } + + if (!httpRequest) { throw "Cannot create HTTP Request."; } + + httpRequest.open('GET', url, false); + httpRequest.send(''); + if (httpRequest.readyState == 4) { + if (httpRequest.status >= 400) { + throw "The HTTP Request returned an error code: "+httpRequest.status; + } + } + + return httpRequest.responseText || ""; + } + } +})(); diff --git a/jsdoc_toolkit-2.3.3-beta/app/t/runner.js b/jsdoc_toolkit-2.3.3-beta/app/t/runner.js new file mode 100644 index 00000000..3f9fb4c9 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/t/runner.js @@ -0,0 +1,13 @@ +// try: java -jar ../../jsrun.jar runner.js + +load("TestDoc.js"); + +TestDoc.prove("../frame/Opt.js"); +TestDoc.prove("../lib/JSDOC.js"); +TestDoc.prove("../frame/String.js"); +TestDoc.prove("../lib/JSDOC/DocTag.js"); +TestDoc.prove("../lib/JSDOC/DocComment.js"); +TestDoc.prove("../lib/JSDOC/TokenReader.js"); +TestDoc.prove("../lib/JSDOC/Symbol.js"); + +TestDoc.report(); diff --git a/jsdoc_toolkit-2.3.3-beta/app/test.js b/jsdoc_toolkit-2.3.3-beta/app/test.js new file mode 100644 index 00000000..8b2dc8b1 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test.js @@ -0,0 +1,342 @@ +load("app/frame/Dumper.js"); +function symbolize(opt) { + symbols = null; + JSDOC.JsDoc(opt); + symbols = JSDOC.JsDoc.symbolSet; +} + +var testCases = [ + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/overview.js"]}); + //print(Dumper.dump(symbols)); + is('symbols.getSymbolByName("My Cool Library").name', 'My Cool Library', 'File overview can be found by alias.'); + } + , + function() { + symbolize({_: [SYS.pwd+"test/name.js"]}); + + is('symbols.getSymbol("Response").name', "Response", 'Virtual class name is found.'); + is('symbols.getSymbol("Response#text").alias', "Response#text", 'Virtual method name is found.'); + is('symbols.getSymbol("Response#text").memberOf', "Response", 'Virtual method parent name is found.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/prototype.js"]}); + + is('symbols.getSymbol("Article").name', "Article", 'Function set to constructor prototype with inner constructor name is found.'); + is('symbols.getSymbol("Article").hasMethod("init")', true, 'The initializer method name of prototype function is correct.'); + is('symbols.getSymbol("Article").hasMember("counter")', true, 'A static property set in the prototype definition is found.'); + is('symbols.getSymbol("Article").hasMember("title")', true, 'An instance property set in the prototype is found.'); + is('symbols.getSymbol("Article#title").isStatic', false, 'An instance property has isStatic set to false.'); + is('symbols.getSymbol("Article.counter").name', "counter", 'A static property set in the initializer has the name set correctly.'); + is('symbols.getSymbol("Article.counter").memberOf', "Article", 'A static property set in the initializer has the memberOf set correctly.'); + is('symbols.getSymbol("Article.counter").isStatic', true, 'A static property set in the initializer has isStatic set to true.'); + } + , + function() { + symbolize({a:true, _: [SYS.pwd+"test/prototype_oblit.js"]}); + + is('symbols.getSymbol("Article").name', "Article", 'Oblit set to constructor prototype name is found.'); + is('typeof symbols.getSymbol("Article.prototype")', "undefined", 'The prototype oblit is not a symbol.'); + is('symbols.getSymbol("Article#getTitle").name', "getTitle", 'The nonstatic method name of prototype oblit is correct.'); + is('symbols.getSymbol("Article#getTitle").alias', "Article#getTitle", 'The alias of non-static method of prototype oblit is correct.'); + is('symbols.getSymbol("Article#getTitle").isStatic', false, 'The isStatic of a nonstatic method of prototype oblit is correct.'); + is('symbols.getSymbol("Article.getTitle").name', "getTitle", 'The static method name of prototype oblit is correct.'); + is('symbols.getSymbol("Article.getTitle").isStatic', true, 'The isStatic of a static method of prototype oblit is correct.'); + is('symbols.getSymbol("Article#getTitle").isa', "FUNCTION", 'The isa of non-static method of prototype oblit is correct.'); + is('symbols.getSymbol("Article.getTitle").alias', "Article.getTitle", 'The alias of a static method of prototype oblit is correct.'); + is('symbols.getSymbol("Article.getTitle").isa', "FUNCTION", 'The isa of static method of prototype oblit is correct.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/prototype_oblit_constructor.js"]}); + + is('symbols.getSymbol("Article").name', "Article", 'Oblit set to constructor prototype with inner constructor name is found.'); + is('symbols.getSymbol("Article#init").name', "init", 'The initializer method name of prototype oblit is correct.'); + is('symbols.getSymbol("Article").hasMember("pages")', true, 'Property set by initializer method "this" is on the outer constructor.'); + is('symbols.getSymbol("Article#Title").name', "Title", 'Name of the inner constructor name is found.'); + is('symbols.getSymbol("Article#Title").memberOf', "Article", 'The memberOf of the inner constructor name is found.'); + is('symbols.getSymbol("Article#Title").isa', "CONSTRUCTOR", 'The isa of the inner constructor name is constructor.'); + is('symbols.getSymbol("Article#Title").hasMember("title")', true, 'A property set on the inner constructor "this" is on the inner constructor.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/inner.js"]}); + + is('symbols.getSymbol("Outer").name', "Outer", 'Outer constructor prototype name is found.'); + is('symbols.getSymbol("Outer").methods.length', 1, 'Inner function doesnt appear as a method of the outer.'); + is('symbols.getSymbol("Outer").hasMethod("open")', true, 'Outer constructors methods arent affected by inner function.'); + is('symbols.getSymbol("Outer-Inner").alias', "Outer-Inner", 'Alias of inner function is found.'); + is('symbols.getSymbol("Outer-Inner").isa', "CONSTRUCTOR", 'isa of inner function constructor is found.'); + is('symbols.getSymbol("Outer-Inner").memberOf', "Outer", 'The memberOf of inner function is found.'); + is('symbols.getSymbol("Outer-Inner").name', "Inner", 'The name of inner function is found.'); + is('symbols.getSymbol("Outer-Inner#name").name', "name", 'A member of the inner function constructor, attached to "this" is found on inner.'); + is('symbols.getSymbol("Outer-Inner#name").memberOf', "Outer-Inner", 'The memberOf of an inner function member is found.'); + } + , + function() { + symbolize({a:true, _: [SYS.pwd+"test/prototype_nested.js"]}); + + is('symbols.getSymbol("Word").name', "Word", 'Base constructor name is found.'); + is('symbols.getSymbol("Word").hasMethod("reverse")', true, 'Base constructor method is found.'); + is('symbols.getSymbol("Word").methods.length', 1, 'Base constructor has only one method.'); + is('symbols.getSymbol("Word").memberOf', "", 'Base constructor memberOf is empty.'); + is('symbols.getSymbol("Word#reverse").name', "reverse", 'Member of constructor prototype name is found.'); + is('symbols.getSymbol("Word#reverse").memberOf', "Word", 'Member of constructor prototype memberOf is found.'); + is('symbols.getSymbol("Word#reverse.utf8").name', "utf8", 'Member of constructor prototype method name is found.'); + is('symbols.getSymbol("Word#reverse.utf8").memberOf', "Word#reverse", 'Static nested member memberOf is found.'); + } + , + function() { + symbolize({a:true, _: [SYS.pwd+"test/namespace_nested.js"]}); + + is('symbols.getSymbol("ns1").name', "ns1", 'Base namespace name is found.'); + is('symbols.getSymbol("ns1").memberOf', "", 'Base namespace memberOf is empty (its a constructor).'); + is('symbols.getSymbol("ns1.ns2").name', "ns2", 'Nested namespace name is found.'); + is('symbols.getSymbol("ns1.ns2").alias', "ns1.ns2", 'Nested namespace alias is found.'); + is('symbols.getSymbol("ns1.ns2").memberOf', "ns1", 'Nested namespace memberOf is found.'); + is('symbols.getSymbol("ns1.ns2.Function1").name', "Function1", 'Method of nested namespace name is found.'); + is('symbols.getSymbol("ns1.ns2.Function1").memberOf', "ns1.ns2", 'Constructor of nested namespace memberOf is found.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/functions_nested.js"]}); + + is('symbols.getSymbol("Zop").name', "Zop", 'Any constructor name is found.'); + is('symbols.getSymbol("Zop").isa', "CONSTRUCTOR", 'It isa constructor.'); + is('symbols.getSymbol("Zop").hasMethod("zap")', true, 'Its method name, set later, is in methods array.'); + is('symbols.getSymbol("Foo").name', "Foo", 'The containing constructor name is found.'); + is('symbols.getSymbol("Foo").hasMethod("methodOne")', true, 'Its method name is found.'); + is('symbols.getSymbol("Foo").hasMethod("methodTwo")', true, 'Its second method name is found.'); + is('symbols.getSymbol("Foo#methodOne").alias', "Foo#methodOne", 'A methods alias is found.'); + is('symbols.getSymbol("Foo#methodOne").isStatic', false, 'A methods is not static.'); + is('symbols.getSymbol("Bar").name', "Bar", 'A global function declared inside another function is found.'); + is('symbols.getSymbol("Bar").isa', "FUNCTION", 'It isa function.'); + is('symbols.getSymbol("Bar").memberOf', "_global_", 'It is global.'); + is('symbols.getSymbol("Foo-inner").name', "inner", 'An inner functions name is found.'); + is('symbols.getSymbol("Foo-inner").memberOf', "Foo", 'It is member of the outer function.'); + is('symbols.getSymbol("Foo-inner").isInner', true, 'It is an inner function.'); + } + , + function() { + symbolize({a:true, _: [SYS.pwd+"test/memberof_constructor.js"]}); + + is('symbols.getSymbol("Circle#Tangent").name', "Tangent", 'Constructor set on prototype using @member has correct name.'); + is('symbols.getSymbol("Circle#Tangent").memberOf', "Circle", 'Constructor set on prototype using @member has correct memberOf.'); + is('symbols.getSymbol("Circle#Tangent").alias', "Circle#Tangent", 'Constructor set on prototype using @member has correct alias.'); + is('symbols.getSymbol("Circle#Tangent").isa', "CONSTRUCTOR", 'Constructor set on prototype using @member has correct isa.'); + is('symbols.getSymbol("Circle#Tangent").isStatic', false, 'Constructor set on prototype using @member is not static.'); + is('symbols.getSymbol("Circle#Tangent#getDiameter").name', "getDiameter", 'Method set on prototype using @member has correct name.'); + is('symbols.getSymbol("Circle#Tangent#getDiameter").memberOf', "Circle#Tangent", 'Method set on prototype using @member has correct memberOf.'); + is('symbols.getSymbol("Circle#Tangent#getDiameter").alias', "Circle#Tangent#getDiameter", 'Method set on prototype using @member has correct alias.'); + is('symbols.getSymbol("Circle#Tangent#getDiameter").isa', "FUNCTION", 'Method set on prototype using @member has correct isa.'); + is('symbols.getSymbol("Circle#Tangent#getDiameter").isStatic', false, 'Method set on prototype using @member is not static.'); + } + , + function() { + symbolize({a:true, p: true, _: [SYS.pwd+"test/memberof.js"]}); + + is('symbols.getSymbol("pack.install").alias', "pack.install", 'Using @memberOf sets alias, when parent name is in memberOf tag.'); + is('symbols.getSymbol("pack.install.overwrite").name', "install.overwrite", 'Using @memberOf sets name, even if the name is dotted.'); + is('symbols.getSymbol("pack.install.overwrite").memberOf', "pack", 'Using @memberOf sets memberOf.'); + is('symbols.getSymbol("pack.install.overwrite").isStatic', true, 'Using @memberOf with value not ending in octothorp sets isStatic to true.'); + } + , + function() { + symbolize({a:true, p: true, _: [SYS.pwd+"test/memberof2.js"]}); + + is('symbols.getSymbol("Foo#bar").alias', "Foo#bar", 'An inner function can be documented as an instance method.'); + is('symbols.getSymbol("Foo.zip").alias', "Foo.zip", 'An inner function can be documented as a static method.'); + is('symbols.getSymbol("Foo.Fiz").alias', "Foo.Fiz", 'An inner function can be documented as a static constructor.'); + is('symbols.getSymbol("Foo.Fiz#fipple").alias', "Foo.Fiz#fipple", 'An inner function can be documented as a static constructor with a method.'); + is('symbols.getSymbol("Foo#blat").alias', "Foo#blat", 'An global function can be documented as an instance method.'); + } + , + function() { + symbolize({a:true, p: true, _: [SYS.pwd+"test/memberof3.js"]}); + + is('symbols.getSymbol("Foo#bar").alias', "Foo#bar", 'A virtual field can be documented as an instance method.'); + is('symbols.getSymbol("Foo2#bar").alias', "Foo2#bar", 'A virtual field with the same name can be documented as an instance method.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/borrows.js"]}); + + is('symbols.getSymbol("Layout").name', "Layout", 'Constructor can be found.'); + is('symbols.getSymbol("Layout").hasMethod("init")', true, 'Constructor method name can be found.'); + is('symbols.getSymbol("Layout").hasMember("orientation")', true, 'Constructor property name can be found.'); + + is('symbols.getSymbol("Page").hasMethod("reset")', true, 'Second constructor method name can be found.'); + is('symbols.getSymbol("Page").hasMember("orientation")', true, 'Second constructor borrowed property name can be found in properties.'); + is('symbols.getSymbol("Page#orientation").memberOf', "Page", 'Second constructor borrowed property memberOf can be found.'); + is('symbols.getSymbol("Page-getInnerElements").alias', "Page-getInnerElements", 'Can borrow an inner function and it is still inner.'); + is('symbols.getSymbol("Page.units").alias', "Page.units", 'Can borrow a static function and it is still static.'); + + is('symbols.getSymbol("ThreeColumnPage#init").alias', "ThreeColumnPage#init", 'Third constructor method can be found even though method with same name is borrowed.'); + is('symbols.getSymbol("ThreeColumnPage#reset").alias', "ThreeColumnPage#reset", 'Borrowed method can be found.'); + is('symbols.getSymbol("ThreeColumnPage#orientation").alias', "ThreeColumnPage#orientation", 'Twice borrowed method can be found.'); + + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/borrows2.js"]}); + + is('symbols.getSymbol("Foo").hasMethod("my_zop")', true, 'Borrowed method can be found.'); + is('symbols.getSymbol("Bar").hasMethod("my_zip")', true, 'Second borrowed method can be found.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/constructs.js"]}); + + is('symbols.getSymbol("Person").hasMethod("say")', true, 'The constructs tag creates a class that lends can add a method to.'); + } + , + function() { + symbolize({a: true, _: [SYS.pwd+"test/augments.js", SYS.pwd+"test/augments2.js"]}); + + is('symbols.getSymbol("Page").augments[0]', "Layout", 'An augmented class can be found.'); + is('symbols.getSymbol("Page#reset").alias', "Page#reset", 'Method of augmenter can be found.'); + is('symbols.getSymbol("Page").hasMethod("Layout#init")', true, 'Method from augmented can be found.'); + is('symbols.getSymbol("Page").hasMember("Layout#orientation")', true, 'Property from augmented can be found.'); + is('symbols.getSymbol("Page").methods.length', 3, 'Methods of augmented class are included in methods array.'); + + is('symbols.getSymbol("ThreeColumnPage").augments[0]', "Page", 'The extends tag is a synonym for augments.'); + is('symbols.getSymbol("ThreeColumnPage").hasMethod("ThreeColumnPage#init")', true, 'Local method overrides augmented method of same name.'); + is('symbols.getSymbol("ThreeColumnPage").methods.length', 3, 'Local method count is right.'); + + is('symbols.getSymbol("NewsletterPage").augments[0]', "ThreeColumnPage", 'Can augment across file boundaries.'); + is('symbols.getSymbol("NewsletterPage").augments.length', 2, 'Multiple augments are supported.'); + is('symbols.getSymbol("NewsletterPage").inherits[0].alias', "Junkmail#annoy", 'Inherited method with augments.'); + is('symbols.getSymbol("NewsletterPage").methods.length', 6, 'Methods of augmented class are included in methods array across files.'); + is('symbols.getSymbol("NewsletterPage").properties.length', 1, 'Properties of augmented class are included in properties array across files.'); + } + , + function() { + symbolize({a:true, _: [SYS.pwd+"test/static_this.js"]}); + + is('symbols.getSymbol("box.holder").name', "holder", 'Static namespace name can be found.'); + is('symbols.getSymbol("box.holder.foo").name', "foo", 'Static namespace method name can be found.'); + is('symbols.getSymbol("box.holder").isStatic', true, 'Static namespace method is static.'); + + is('symbols.getSymbol("box.holder.counter").name', "counter", 'Instance namespace property name set on "this" can be found.'); + is('symbols.getSymbol("box.holder.counter").alias', "box.holder.counter", 'Instance namespace property alias set on "this" can be found.'); + is('symbols.getSymbol("box.holder.counter").memberOf', "box.holder", 'Static namespace property memberOf set on "this" can be found.'); + } + , + function() { + symbolize({a:true, p: true, _: [SYS.pwd+"test/lend.js"]}); + + is('symbols.getSymbol("Person").name', "Person", 'Class defined in lend comment is found.'); + is('symbols.getSymbol("Person").hasMethod("initialize")', true, 'Lent instance method name can be found.'); + is('symbols.getSymbol("Person").hasMethod("say")', true, 'Second instance method can be found.'); + is('symbols.getSymbol("Person#sing").isStatic', false, 'Instance method is known to be not static.'); + + is('symbols.getSymbol("Person.getCount").name', "getCount", 'Static method name from second lend comment can be found.'); + is('symbols.getSymbol("Person.getCount").isStatic', true, 'Static method from second lend comment is known to be static.'); + + is('LOG.warnings.filter(function($){if($.indexOf("notok") > -1) return $}).length', 1, 'A warning is emitted when lending to an undocumented parent.'); + } + , + function() { + symbolize({a:true, _: [SYS.pwd+"test/param_inline.js"]}); + + is('symbols.getSymbol("Layout").params[0].type', "int", 'Inline param name is set.'); + is('symbols.getSymbol("Layout").params[0].desc', "The number of columns.", 'Inline param desc is set from comment.'); + is('symbols.getSymbol("Layout#getElement").params[0].name', "id", 'User defined param documentation takes precedence over parser defined.'); + is('symbols.getSymbol("Layout#getElement").params[0].isOptional', true, 'Default for param is to not be optional.'); + is('symbols.getSymbol("Layout#getElement").params[1].isOptional', false, 'Can mark a param as being optional.'); + is('symbols.getSymbol("Layout#getElement").params[1].type', "number|string", 'Type of inline param doc can have multiple values.'); + is('symbols.getSymbol("Layout#Canvas").params[0].type', "", 'Type can be not defined for some params.'); + is('symbols.getSymbol("Layout#Canvas").params[2].type', "int", 'Type can be defined inline for only some params.'); + is('symbols.getSymbol("Layout#rotate").params.length', 0, 'Docomments inside function sig is ignored without a param.'); + is('symbols.getSymbol("Layout#init").params[2].type', "zoppler", 'Doc comment type overrides inline type for param with same name.'); + } + , + function() { + symbolize({a: true, _: [SYS.pwd+"test/shared.js", SYS.pwd+"test/shared2.js"]}); + + is('symbols.getSymbol("Array#some").name', 'some', 'The name of a symbol in a shared section is found.'); + is('symbols.getSymbol("Array#some").alias', 'Array#some', 'The alias of a symbol in a shared section is found.'); + is('symbols.getSymbol("Array#some").desc', "Extension to builtin array.", 'A description can be shared.'); + is('symbols.getSymbol("Array#filter").desc', "Extension to builtin array.\nChange every element of an array.", 'A shared description is appended.'); + is('symbols.getSymbol("Queue").desc', "A first in, first out data structure.", 'A description is not shared when outside a shared section.'); + is('symbols.getSymbol("Queue.rewind").alias', "Queue.rewind", 'Second shared tag can be started.'); + is('symbols.getSymbol("startOver").alias', "startOver", 'Shared tag doesnt cross over files.'); + } + , + function() { + symbolize({a: true, _: [SYS.pwd+"test/config.js"]}); + is('symbols.getSymbol("Contact").params[0].name', 'person', 'The name of a param is found.'); + is('symbols.getSymbol("Contact").params[1].name', 'person.name', 'The name of a param set with a dot name is found.'); + is('symbols.getSymbol("Contact").params[2].name', 'person.age', 'The name of a second param set with a dot name is found.'); + is('symbols.getSymbol("Contact").params[4].name', 'connection', 'The name of a param after config is found.'); + + is('symbols.getSymbol("Family").params[0].name', 'persons', 'Another name of a param is found.'); + is('symbols.getSymbol("Family").params[1].name', 'persons.Father', 'The name of a param+config is found.'); + is('symbols.getSymbol("Family").params[2].name', 'persons.Mother', 'The name of a second param+config is found.'); + is('symbols.getSymbol("Family").params[3].name', 'persons.Children', 'The name of a third param+config is found.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/ignore.js"]}); + is('LOG.warnings.filter(function($){if($.indexOf("undocumented symbol Ignored") > -1) return $}).length', 1, 'A warning is emitted when documenting members of an ignored parent.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/functions_anon.js"]}); + is('symbols.getSymbol("a.b").alias', 'a.b', 'In anonymous constructor this is found to be the container object.'); + is('symbols.getSymbol("a.f").alias', 'a.f', 'In anonymous constructor this can have a method.'); + is('symbols.getSymbol("a.c").alias', 'a.c', 'In anonymous constructor method this is found to be the container object.'); + is('symbols.getSymbol("g").alias', 'g', 'In anonymous function executed inline this is the global.'); + is('symbols.getSymbol("bar2.p").alias', 'bar2.p', 'In named constructor executed inline this is the container object.'); + is('symbols.getSymbol("module.pub").alias', 'module.pub', 'In parenthesized anonymous function executed inline function scoped variables arent documented.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/oblit_anon.js"]}); + is('symbols.getSymbol("opt").name', 'opt', 'Anonymous object properties are created.'); + is('symbols.getSymbol("opt.conf.keep").alias', 'opt.conf.keep', 'Anonymous object first property is assigned to $anonymous.'); + is('symbols.getSymbol("opt.conf.base").alias', 'opt.conf.base', 'Anonymous object second property is assigned to $anonymous.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/params_optional.js"]}); + is('symbols.getSymbol("Document").params.length', 3, 'Correct number of params are found when optional param syntax is used.'); + is('symbols.getSymbol("Document").params[1].name', "id", 'Name of optional param is found.'); + is('symbols.getSymbol("Document").params[1].isOptional', true, 'Optional param is marked isOptional.'); + is('symbols.getSymbol("Document").params[2].name', "title", 'Name of optional param with default value is found.'); + is('symbols.getSymbol("Document").params[2].isOptional', true, 'Optional param with default value is marked isOptional.'); + is('symbols.getSymbol("Document").params[2].defaultValue', " This is untitled.", 'Optional param default value is found.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/synonyms.js"]}); + is('symbols.getSymbol("myObject.myFunc").type', 'function', 'Type can be set to function.'); + } + , + function() { + symbolize({a:true, p:true, _: [SYS.pwd+"test/event.js"]}); + is('symbols.getSymbol("Kitchen#event:cakeEaten").isEvent', true, 'Function with event prefix is an event.'); + is('symbols.getSymbol("Kitchen#cakeEaten").isa', "FUNCTION", 'Function with same name as event isa function.'); + } + , + function() { + symbolize({x:"js", a:true, _: [SYS.pwd+"test/scripts/"]}); + is('JSDOC.JsDoc.srcFiles.length', 1, 'Only js files are scanned when -x=js.'); + } + , + function() { + symbolize({x:"js", a:true, _: [SYS.pwd+"test/exports.js"]}); + is('symbols.getSymbol("mxn.Map#doThings").name', 'doThings', 'Exports creates a documentation alias that can have methods.'); + } + , + function() { + symbolize({p:true, a:true, _: [SYS.pwd+"test/module.js"]}); + is('symbols.getSymbol("myProject.myModule.myPublicMethod").name', 'myPublicMethod', 'A function wrapped in parens can be recognized.'); + is('symbols.getSymbol("myProject.myModule-myPrivateMethod").name', 'myPrivateMethod', 'A private method in the scope of a function wrapped in parens can be recognized.'); + is('symbols.getSymbol("myProject.myModule-myPrivateVar").name', 'myPrivateVar', 'A private member in the scope of a function wrapped in parens can be recognized.'); + } +]; + +//// run and print results +print(testrun(testCases)); diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/addon.js b/jsdoc_toolkit-2.3.3-beta/app/test/addon.js new file mode 100644 index 00000000..88862053 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/addon.js @@ -0,0 +1,24 @@ +String.prototype.reverse = function() { +} + +String.prototype.reverse.utf8 = function() { +} + +Function.count = function() { +} + +/** @memberOf Function */ +Function.count.reset = function() { +} + +/** @memberOf Function */ +count.getValue = function() { +} + +/** @memberOf Function.prototype */ +getSig = function() { +} + +/** @memberOf Function.prototype */ +Function.prototype.getProps = function() { +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/anon_inner.js b/jsdoc_toolkit-2.3.3-beta/app/test/anon_inner.js new file mode 100644 index 00000000..227eeee5 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/anon_inner.js @@ -0,0 +1,14 @@ +/** + * @name bar + * @namespace + */ + +new function() { + /** + * @name bar-foo + * @function + * @param {number} x + */ + function foo(x) { + } +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/augments.js b/jsdoc_toolkit-2.3.3-beta/app/test/augments.js new file mode 100644 index 00000000..12e706eb --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/augments.js @@ -0,0 +1,31 @@ +/** +@constructor +*/ +function Layout(p) { + this.init = function(p) { + } + + this.getId = function() { + } + + /** @type Page */ + this.orientation = "landscape"; +} + +/** +@constructor +@augments Layout +*/ +function Page() { + this.reset = function(b) { + } +} + +/** +@extends Page +@constructor +*/ +function ThreeColumnPage() { + this.init = function(resetCode) { + } +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/augments2.js b/jsdoc_toolkit-2.3.3-beta/app/test/augments2.js new file mode 100644 index 00000000..e8388f0f --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/augments2.js @@ -0,0 +1,26 @@ +/** +@constructor +*/ +function LibraryItem() { + this.reserve = function() { + } +} + +/** +@constructor +*/ +function Junkmail() { + this.annoy = function() { + } +} + +/** +@inherits Junkmail.prototype.annoy as pester +@augments ThreeColumnPage +@augments LibraryItem +@constructor +*/ +function NewsletterPage() { + this.getHeadline = function() { + } +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/borrows.js b/jsdoc_toolkit-2.3.3-beta/app/test/borrows.js new file mode 100644 index 00000000..a5d8ea4a --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/borrows.js @@ -0,0 +1,46 @@ +/** +@constructor +*/ +function Layout(p) { + /** initilize 1 */ + this.init = function(p) { + } + + /** get the id */ + this.getId = function() { + } + + /** @type string */ + this.orientation = "landscape"; + + function getInnerElements(elementSecretId){ + } +} + +/** A static method. */ +Layout.units = function() { +} + +/** +@constructor +@borrows Layout#orientation +@borrows Layout-getInnerElements +@borrows Layout.units +*/ +function Page() { + /** reset the page */ + this.reset = function(b) { + } +} + +/** +@constructor +@borrows Layout.prototype.orientation as this.orientation +@borrows Layout.prototype.init as #init +@inherits Page.prototype.reset as #reset +*/ +function ThreeColumnPage() { + /** initilize 2 */ + this.init = function(p) { + } +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/borrows2.js b/jsdoc_toolkit-2.3.3-beta/app/test/borrows2.js new file mode 100644 index 00000000..c0d5ea21 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/borrows2.js @@ -0,0 +1,23 @@ +// testing circular borrows + +/** + @class + @borrows Bar#zop as this.my_zop +*/ +function Foo() { + /** this is a zip. */ + this.zip = function() {} + + this.my_zop = new Bar().zop; +} + +/** + @class + @borrows Foo#zip as this.my_zip +*/ +function Bar() { + /** this is a zop. */ + this.zop = function() {} + + this.my_zip = new Foo().zip; +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/config.js b/jsdoc_toolkit-2.3.3-beta/app/test/config.js new file mode 100644 index 00000000..0748a210 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/config.js @@ -0,0 +1,22 @@ +/** + * @constructor + * @param person The person. + * @param {string} person.name The person's name. + * @config {integer} age The person's age. + * @config [id=1] Optional id number to use. + * @param connection + */ +function Contact(person, connection) { + +} + +/** + * @constructor + * @param persons + * @config {string} Father The paternal person. + * @config {string} Mother The maternal person. + * @config {string[]} Children And the rest. + */ +function Family(/**Object*/persons) { + +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/constructs.js b/jsdoc_toolkit-2.3.3-beta/app/test/constructs.js new file mode 100644 index 00000000..cca5dbd3 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/constructs.js @@ -0,0 +1,18 @@ +var Person = makeClass( + /** + @scope Person + */ + { + /** + This is just another way to define a constructor. + @constructs + @param {string} name The name of the person. + */ + initialize: function(name) { + this.name = name; + }, + say: function(message) { + return this.name + " says: " + message; + } + } +); \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/encoding.js b/jsdoc_toolkit-2.3.3-beta/app/test/encoding.js new file mode 100644 index 00000000..ba642193 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/encoding.js @@ -0,0 +1,10 @@ + +/** + * @Constructor + * @desc é…置文件 + * @class 什么也ä¸è¿”回 + */ +function Test(conf) { + // do something; +} + diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/encoding_other.js b/jsdoc_toolkit-2.3.3-beta/app/test/encoding_other.js new file mode 100644 index 00000000..b144da4c --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/encoding_other.js @@ -0,0 +1,12 @@ + +/** + * @Constructor + * @desc ðïîÛ + * @class ßàáâãäåæçèçìëêíîï °±²³´µ¡¶·¸¹ + */ +function Test(conf) { + // do something; +} + +// run with commanline option -e=iso-8859-5 + diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/event.js b/jsdoc_toolkit-2.3.3-beta/app/test/event.js new file mode 100644 index 00000000..7e41d6f6 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/event.js @@ -0,0 +1,54 @@ +/** + * @name Kitchen + * @constructor + * @fires Bakery#event:donutOrdered + */ + +/** + * Fired when some cake is eaten. + * @name Kitchen#event:cakeEaten + * @function + * @param {Number} pieces The number of pieces eaten. + */ + +/** + * Find out if cake was eaten. + * @name Kitchen#cakeEaten + * @function + * @param {Boolean} wasEaten + */ + +/** + * @name getDesert + * @function + * @fires Kitchen#event:cakeEaten + */ + +/** + * @name Bakery + * @constructor + * @extends Kitchen + */ + +/** + * Fired when a donut order is made. + * @name Bakery#event:donutOrdered + * @event + * @param {Event} e The event object. + * @param {String} [e.topping] Optional sprinkles. + */ + +/** + * @constructor + * @borrows Bakery#event:donutOrdered as this.event:cakeOrdered + */ +function CakeShop() { +} + +/** @event */ +CakeShop.prototype.icingReady = function(isPink) { +} + +/** @event */ +function amHungry(/**Boolean*/enoughToEatAHorse) { +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/exports.js b/jsdoc_toolkit-2.3.3-beta/app/test/exports.js new file mode 100644 index 00000000..63a87cb4 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/exports.js @@ -0,0 +1,14 @@ +/** @namespace */ +var mxn = {}; + +(function(){ + /** @exports Map as mxn.Map */ + var Map = + /** @constructor */ + mxn.Map = function() { + }; + + /** A method. */ + Map.prototype.doThings = function() { + }; +})(); \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/functions_anon.js b/jsdoc_toolkit-2.3.3-beta/app/test/functions_anon.js new file mode 100644 index 00000000..e9dd6c1b --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/functions_anon.js @@ -0,0 +1,39 @@ +/** an anonymous constructor executed inline */ +a = new function() { + /** a.b*/ + this.b = 1; + /** a.f */ + this.f = function() { + /** a.c */ + this.c = 2; + } +} + + +/** + named function executed inline +*/ +bar1 = function Zoola1() { + /** property of global */ + this.g = 1; +}(); + +/** + named constructor executed inline +*/ +bar2 = new function Zoola2() { + /** property of bar */ + this.p = 1; +}; + +/** module pattern */ +module = (function () { + /** won't appear in documentation */ + var priv = 1; + + /** @scope module */ + return { + /** will appear as a property of module */ + pub: 1 + } +})(); diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/functions_nested.js b/jsdoc_toolkit-2.3.3-beta/app/test/functions_nested.js new file mode 100644 index 00000000..f044fafe --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/functions_nested.js @@ -0,0 +1,33 @@ +/** @constructor */ +function Zop() { +} + +/** + @class +*/ +Foo = function(id) { + // this is a bit twisted, but if you call Foo() you will then + // modify Foo(). This is kinda, sorta non-insane, because you + // would have to call Foo() 100% of the time to use Foo's methods + Foo.prototype.methodOne = function(bar) { + alert(bar); + }; + + // same again + Foo.prototype.methodTwo = function(bar2) { + alert(bar2); + }; + + // and these are only executed if the enclosing function is actually called + // and who knows if that will ever happen? + Bar = function(pez) { + alert(pez); + }; + Zop.prototype.zap = function(p){ + alert(p); + }; + + // but this is only visible inside Foo + function inner() { + } +}; diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/global.js b/jsdoc_toolkit-2.3.3-beta/app/test/global.js new file mode 100644 index 00000000..5ea48949 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/global.js @@ -0,0 +1,13 @@ +/** ecks */ +var x = [1, 2, 4]; + +var y = { + foo: function(){ + } +} + +bar = function() { +} + +function zop() { +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/globals.js b/jsdoc_toolkit-2.3.3-beta/app/test/globals.js new file mode 100644 index 00000000..3f83fb1f --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/globals.js @@ -0,0 +1,25 @@ +function example(/**Circle*/a, b) { + /** a global defined in function */ + var number = a; + + var hideNumber = function(){ + } + + setNumber = function(){ + } + alert('You have chosen: ' + b); +} + +function initPage() { + var supported = document.createElement && document.getElementsByTagName; + if (!supported) return; + // start of DOM script + var x = document.getElementById('writeroot'); + // etc. +} + +/** an example var */ +var document = new Document(x, y); + +var getNumber = function(){ +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/ignore.js b/jsdoc_toolkit-2.3.3-beta/app/test/ignore.js new file mode 100644 index 00000000..d3fac9ed --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/ignore.js @@ -0,0 +1,10 @@ +/** + * A test constructor. + * @constructor + * @ignore + */ +function Ignored() { + /** a method */ + this.bar = function() { + } +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/inner.js b/jsdoc_toolkit-2.3.3-beta/app/test/inner.js new file mode 100644 index 00000000..37cfa9dc --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/inner.js @@ -0,0 +1,16 @@ +/** + * @constructor + */ +function Outer() { + /** + * @constructor + */ + function Inner(name) { + /** The name of this. */ + this.name = name; + } + + this.open = function(name) { + return (new Inner(name)); + } +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/jsdoc_test.js b/jsdoc_toolkit-2.3.3-beta/app/test/jsdoc_test.js new file mode 100644 index 00000000..08177128 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/jsdoc_test.js @@ -0,0 +1,477 @@ +/** + * @fileoverview This file is to be used for testing the JSDoc parser + * It is not intended to be an example of good JavaScript OO-programming, + * nor is it intended to fulfill any specific purpose apart from + * demonstrating the functionality of the + * JSDoc parser + * + * @author Gabriel Reid gab_reid@users.sourceforge.net + * @version 0.1 + */ + + +/** + * Construct a new Shape object. + * @class This is the basic Shape class. + * It can be considered an abstract class, even though no such thing + * really existing in JavaScript + * @constructor + * @throws MemoryException if there is no more memory + * @throws GeneralShapeException rarely (if ever) + * @return {Shape|Coordinate} A new shape. + */ +function Shape(){ + + /** + * This is an example of a function that is not given as a property + * of a prototype, but instead it is assigned within a constructor. + * For inner functions like this to be picked up by the parser, the + * function that acts as a constructor must be denoted with + * the @constructor tag in its comment. + * @type String + */ + this.getClassName = function(){ + return "Shape"; + } + + /** + * This is an inner method, just used here as an example + * @since version 0.5 + * @author Sue Smart + */ + function addReference(){ + // Do nothing... + } + +} + +/** + * Create a new Hexagon instance. + * @extends Shape + * @class Hexagon is a class that is a logical sublcass of + * {@link Shape} (thanks to the @extends tag), but in + * reality it is completely unrelated to Shape. + * @param {int} sideLength The length of one side for the new Hexagon + * @example + * var h = new Hexagon(2); + * @example + * if (hasHex) { + * hex = new Hexagon(5); + * color = hex.getColor(); + * } + */ +function Hexagon(sideLength) { +} + + +/** + * This is an unattached (static) function that adds two integers together. + * @param {int} One The first number to add + * @param {int} Two The second number to add + * @author Gabriel Reid + * @deprecated So you shouldn't use it anymore! Use {@link Shape#getClassName} instead. + */ +function Add(One, Two){ + return One + Two; +} + + +/** + * The color of this shape + * @type Color + */ +Shape.prototype.color = null; + +/** + * The border of this shape. + * @field + * @type int + */ +Shape.prototype.border = function(){return border;}; + +/* + * These are all the instance method implementations for Shape + */ + +/** + * Get the coordinates of this shape. It is assumed that we're always talking + * about shapes in a 2D location here. + * @requires The {@link Shape} class + * @returns A Coordinate object representing the location of this Shape + * @type Coordinate[] + */ +Shape.prototype.getCoords = function(){ + return this.coords; +} + +/** + * Get the color of this shape. + * @see #setColor + * @see The Color library. + * @link Shape + * @type Color + */ +Shape.prototype.getColor = function(){ + return this.color; +} + +/** + * Set the coordinates for this Shape + * @param {Coordinate} coordinates The coordinates to set for this Shape + */ +Shape.prototype.setCoords = function(coordinates){ + this.coords = coordinates; +} + +/** + * Set the color for this Shape + * @param {Color} color The color to set for this Shape + * @param other There is no other param, but it can still be documented if + * optional parameters are used + * @throws NonExistantColorException (no, not really!) + * @see #getColor + */ +Shape.prototype.setColor = function(color){ + this.color = color; +} + +/** + * Clone this shape + * @returns A copy of this shape + * @type Shape + * @author Gabriel Reid + */ +Shape.prototype.clone = function(){ + return new Shape(); +} + +/** + * Create a new Rectangle instance. + * @class A basic rectangle class, inherits from Shape. + * This class could be considered a concrete implementation class + * @constructor + * @param {int} width The optional width for this Rectangle + * @param {int} height Thie optional height for this Rectangle + * @author Gabriel Reid + * @see Shape is the base class for this + * @augments Shape + * @hilited + */ +function Rectangle(width, // This is the width + height // This is the height + ){ + if (width){ + this.width = width; + if (height){ + this.height = height; + } + } +} + + +/* Inherit from Shape */ +Rectangle.prototype = new Shape(); + +/** + * Value to represent the width of the Rectangle. + *
Text in bold and italic and a + * link to SourceForge + * @private + * @type int + */ +Rectangle.prototype.width = 0; + +/** + * Value to represent the height of the Rectangle + * @private + * @type int + */ +Rectangle.prototype.height = 0; + +/** + * Get the type of this object. + * @type String + */ +Rectangle.prototype.getClassName= function(){ + return "Rectangle"; +} + +/** + * Get the value of the width for the Rectangle + * @type int + * @see Rectangle#setWidth + */ +Rectangle.prototype.getWidth = function(){ + return this.width; +} + +/** + * Get the value of the height for the Rectangle. + * Another getter is the {@link Shape#getColor} method in the + * {@link Shape} base class. + * @return The height of this Rectangle + * @type int + * @see Rectangle#setHeight + */ +Rectangle.prototype.getHeight = function(){ + return this.height; +} + +/** + * Set the width value for this Rectangle. + * @param {int} width The width value to be set + * @see #setWidth + */ +Rectangle.prototype.setWidth = function(width){ + this.width = width; +} + +/** + * Set the height value for this Rectangle. + * @param {int} height The height value to be set + * @see #getHeight + */ +Rectangle.prototype.setHeight = function(height){ + this.height = height; +} + +/** + * Get the value for the total area of this Rectangle + * @return total area of this Rectangle + * @type int + */ +Rectangle.prototype.getArea = function(){ + return width * height; +} + + +/** + * Create a new Square instance. + * @class A Square is a subclass of {@link Rectangle} + * @param {int} width The optional width for this Rectangle + * @param {int} height The optional height for this Rectangle + * @augments Rectangle + */ +function Square(width, height){ + if (width){ + this.width = width; + if (height){ + this.height = height; + } + } + +} + +/* Square is a subclass of Rectangle */ +Square.prototype = new Rectangle(); + +/** + * Set the width value for this Shape. + * @param {int} width The width value to be set + * @see #getWidth + */ +Square.prototype.setWidth = function(width){ + this.width = this.height = width; +} + +/** + * Set the height value for this Shape + * Sets the {@link Rectangle#height} attribute in the Rectangle. + * @param {int} height The height value to be set + */ +Square.prototype.setHeight = function(height){ + this.height = this.width = height; +} + + +/** + * Create a new Circle instance based on a radius. + * @class Circle class is another subclass of Shape + * @extends Shape + * @param {int} radius The optional radius of this {@link Circle } + * @mixin Square.prototype.setWidth as this.setDiameter + */ +function Circle(radius){ + if (radius) { + /** The radius of the this Circle. */ + this.radius = radius; + } +} + +/* Circle inherits from {@link Shape} */ +Circle.prototype = new Shape(); + +/** + * The radius value for this Circle + * @private + * @type int + */ +Circle.prototype.radius = 0; + +/** + * A very simple class (static) field that is also a constant + * @final + * @type float + */ +Circle.PI = 3.14; + +/** + * Get the radius value for this Circle + * @type int + * @see #setRadius + */ +Circle.prototype.getRadius = function(){ + return this.radius; +} + +/** + * Set the radius value for this Circle + * @param {int} radius The {@link Circle#radius} value to set + * @see #getRadius + */ +Circle.prototype.setRadius = function(radius){ + this.radius = radius; +} + +/** + * An example of a class (static) method that acts as a factory for Circle + * objects. Given a radius value, this method creates a new Circle. + * @param {int} radius The radius value to use for the new Circle. + * @type Circle + */ +Circle.createCircle = function(radius){ + return new Circle(radius); +} + + +/** + * Create a new Coordinate instance based on x and y grid data. + * @class Coordinate is a class that can encapsulate location information. + * @param {int} [x=0] The optional x portion of the Coordinate + * @param {int} [y=0] The optinal y portion of the Coordinate + */ +function Coordinate(x, y){ + if (x){ + this.x = x; + if (y){ + this.y = y; + } + } +} + +/** + * The x portion of the Coordinate + * @type int + * @see #getX + * @see #setX + */ +Coordinate.prototype.x = 0; + +/** + * The y portion of the Coordinate + * @type int + * @see #getY + * @see #setY + */ +Coordinate.prototype.y = 0; + +/** + * Gets the x portion of the Coordinate. + * @type int + * @see #setX + */ +Coordinate.prototype.getX = function(){ + return this.x; +} + +/** + * Get the y portion of the Coordinate. + * @type int + * @see #setY + */ +Coordinate.prototype.getY = function(){ + return this.y; +} + +/** + * Sets the x portion of the Coordinate. + * @param {int} x The x value to set + * @see #getX + */ +Coordinate.prototype.setX = function(x){ + this.x = x; +} + +/** + * Sets the y portion of the Coordinate. + * @param {int} y The y value to set + * @see #getY + */ +Coordinate.prototype.setY = function(y){ + this.y = y; +} + +/** + * @class This class exists to demonstrate the assignment of a class prototype + * as an anonymous block. + */ +function ShapeFactory(){ +} + +ShapeFactory.prototype = { + /** + * Creates a new {@link Shape} instance. + * @return A new {@link Shape} + * @type Shape + */ + createShape: function(){ + return new Shape(); + } +} + +/** + * An example of a singleton class + * @param ... Arguments represent {@link coordinate}s in the shape. + * @constructor + */ +MySingletonShapeFactory = function(){ + + /** + * Get the next {@link Shape} + * @type Shape + * @return A new {@link Shape} + */ + this.getShape = function(){ + return null; + } + +} + + +/** + * Create a new Foo instance. + * @class This is the Foo class. It exists to demonstrate 'nested' classes. + * @constructor + * @see Foo.Bar + */ +function Foo(){} + +/** + * Creates a new instance of Bar. + * @class This class exists to demonstrate 'nested' classes. + * @constructor + * @see Foo.Bar + */ +function Bar(){} + +/** + * Nested class + * @constructor + */ +Foo.Bar = function(){ + /** The x. */ this.x = 2; +} + +Foo.Bar.prototype = new Bar(); +/** The y. */ +Foo.Bar.prototype.y = '3'; diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/lend.js b/jsdoc_toolkit-2.3.3-beta/app/test/lend.js new file mode 100644 index 00000000..92b15d5a --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/lend.js @@ -0,0 +1,33 @@ + /** @class */ +var Person = Class.create( + /** + @lends Person.prototype + */ + { + initialize: function(name) { + this.name = name; + }, + say: function(message) { + return this.name + ': ' + message; + } + } + ); + +/** @lends Person.prototype */ +{ + /** like say but more musical */ + sing: function(song) { + } +} + +/** @lends Person */ +{ + getCount: function() { + } +} + +/** @lends Unknown.prototype */ +{ + notok: function() { + } +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/memberof.js b/jsdoc_toolkit-2.3.3-beta/app/test/memberof.js new file mode 100644 index 00000000..883bbdeb --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/memberof.js @@ -0,0 +1,19 @@ +/** @constructor */ +pack = function() { + this.init = function(){} + function config(){} +} + + pack.build = function(task) {}; + +/** @memberOf pack */ +pack.install = function() {} + +/** @memberOf pack */ +pack.install.overwrite = function() {} + +/** @memberOf pack */ +clean = function() {} + +/** @memberOf pack-config */ +install = function() {}; diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/memberof2.js b/jsdoc_toolkit-2.3.3-beta/app/test/memberof2.js new file mode 100644 index 00000000..bc3d9d8f --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/memberof2.js @@ -0,0 +1,38 @@ +/** + * @constructor + */ +function Foo() { + /** + @memberOf Foo.prototype + */ + function bar(a, b) { + } + + /** + @memberOf Foo + */ + var zip = function(p, q) { + } + + /** + @memberOf Foo + */ + function zop( x,y ) { + } + + /** + @memberOf Foo + @constructor + */ + function Fiz() { + /** A method of Foo#Fiz. */ + this.fipple = function(fop){} + } +} + +/** + @memberOf Foo# + */ +var blat = function() { + +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/memberof3.js b/jsdoc_toolkit-2.3.3-beta/app/test/memberof3.js new file mode 100644 index 00000000..8c259425 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/memberof3.js @@ -0,0 +1,33 @@ +/** +* @name Foo +* @class +*/ + +/**#@+ +* @memberOf Foo# +* @field +*/ + +/** +* @name bar +* @type Object[] +*/ + +/**#@-*/ + +/** +* @name Foo2 +* @class +*/ + +/**#@+ +* @memberOf Foo2# +* @field +*/ + +/** +* @name bar +* @type Object[] +*/ + +/**#@-*/ \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/memberof_constructor.js b/jsdoc_toolkit-2.3.3-beta/app/test/memberof_constructor.js new file mode 100644 index 00000000..80fde735 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/memberof_constructor.js @@ -0,0 +1,17 @@ +/** @constructor */ +function Circle(){} + +/** + @constructor + @memberOf Circle# + */ +Circle.prototype.Tangent = function(){}; + +// renaming Circle#Tangent to Circle#Circle#Tangent + +/** + @memberOf Circle#Tangent# + */ +Circle.prototype.Tangent.prototype.getDiameter = function(){}; + + diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/module.js b/jsdoc_toolkit-2.3.3-beta/app/test/module.js new file mode 100644 index 00000000..5b3fe42c --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/module.js @@ -0,0 +1,17 @@ +/** @namespace */ +myProject = myProject || {}; + +/** @namespace */ +myProject.myModule = (function () { + /** describe myPrivateVar here */ + var myPrivateVar = ""; + + var myPrivateMethod = function () { + } + + /** @scope myProject.myModule */ + return { + myPublicMethod: function () { + } + }; +})(); \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/multi_methods.js b/jsdoc_toolkit-2.3.3-beta/app/test/multi_methods.js new file mode 100644 index 00000000..bab35c76 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/multi_methods.js @@ -0,0 +1,25 @@ + +/** + Get the entire flavor. + @name flavor^3 + @function + @returns {Object} The entire flavor hash. +*/ +/** + Get a named flavor. + @name flavor^2 + @function + @param {String} name The name of the flavor to get. + @returns {String} The value of that flavor. +*/ +/** + Set the flavor. + @param {String} name The name of the flavor to set. + @param {String} value The value of the flavor. + @returns {String} The value of that flavor. +*/ +function flavor(name, value) { + if (arguments.length > 1) flavor[name] = value; + else if (arguments.length == 1) return flavor[name]; + else return flavor; +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/name.js b/jsdoc_toolkit-2.3.3-beta/app/test/name.js new file mode 100644 index 00000000..e88a51a7 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/name.js @@ -0,0 +1,19 @@ +/** + @name Response + @class +*/ + +Response.prototype = { + /** + @name Response#text + @function + @description + Gets the body of the response as plain text + @returns {String} + Response as text + */ + + text: function() { + return this.nativeResponse.responseText; + } +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/namespace_nested.js b/jsdoc_toolkit-2.3.3-beta/app/test/namespace_nested.js new file mode 100644 index 00000000..46cafa2f --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/namespace_nested.js @@ -0,0 +1,23 @@ +/** + @namespace This is the first namespace. +*/ +ns1 = {}; + +/** + This is the second namespace. + @namespace +*/ +ns1.ns2 = {}; + +/** + This part of ns1.ns2 + @constructor +*/ +ns1.ns2.Function1 = function() { +}; + +ns1.staticFunction = function() { +}; + +/** A static field in a namespace. */ +ns1.ns2.staticField = 1; diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/nocode.js b/jsdoc_toolkit-2.3.3-beta/app/test/nocode.js new file mode 100644 index 00000000..1cf99bc9 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/nocode.js @@ -0,0 +1,13 @@ +/**#nocode+*/ + /** + @name star + @function + */ + function blahblah() { + + } +/**#nocode-*/ + +function yaddayadda() { + +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/oblit_anon.js b/jsdoc_toolkit-2.3.3-beta/app/test/oblit_anon.js new file mode 100644 index 00000000..8d9e9413 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/oblit_anon.js @@ -0,0 +1,20 @@ +/** the options */ +opt = Opt.get( + arguments, + { + d: "directory", + c: "conf", + "D[]": "define" + } +); + +/** configuration */ +opt.conf = { + /** keep */ + keep: true, + /** base */ + base: getBase(this, {p: properties}) +} + + + diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/overview.js b/jsdoc_toolkit-2.3.3-beta/app/test/overview.js new file mode 100644 index 00000000..1dfc09b1 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/overview.js @@ -0,0 +1,20 @@ +/** + * @overview This "library" contains a + * lot of classes and functions. + * @example +
+	var x (x < 1);
+	alert("This 'is' \"code\"");
+ 
+ * @name My Cool Library + * @author Joe Smith jsmith@company.com + * @version 0.1 + */ + +/** + * Gets the current foo + * @param {String} fooId The unique identifier for the foo. + * @return {Object} Returns the current foo. + */ +function getFoo(fooID){ +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/param_inline.js b/jsdoc_toolkit-2.3.3-beta/app/test/param_inline.js new file mode 100644 index 00000000..09845b28 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/param_inline.js @@ -0,0 +1,37 @@ +/** + @constructor + @param columns The number of columns. +*/ +function Layout(/**int*/columns){ + /** + @param [id] The id of the element. + @param elName The name of the element. + */ + this.getElement = function( + /** string */ elName, + /** number|string */ id + ) { + }; + + /** + @constructor + */ + this.Canvas = function(top, left, /**int*/width, height) { + /** Is it initiated yet? */ + this.initiated = true; + } + + this.rotate = function(/**nothing*/) { + } + + /** + @param x + @param y + @param {zoppler} z*/ + this.init = function(x, y, /**abbler*/z) { + /** The xyz. */ + this.xyz = x+y+z; + this.getXyz = function() { + } + } +} diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/params_optional.js b/jsdoc_toolkit-2.3.3-beta/app/test/params_optional.js new file mode 100644 index 00000000..18bf5982 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/params_optional.js @@ -0,0 +1,8 @@ + +/** + * @param {Page[]} pages + * @param {number} [id] Specifies the id, if applicable. + * @param {String} [title = This is untitled.] Specifies the title. + */ +function Document(pages, id, title){ +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/prototype.js b/jsdoc_toolkit-2.3.3-beta/app/test/prototype.js new file mode 100644 index 00000000..11470083 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/prototype.js @@ -0,0 +1,17 @@ +/** @constructor */ +function Article() { +} + +Article.prototype.init = function(title) { + /** the instance title */ + this.title = title; + + /** the static counter */ + Article.counter = 1; +} + +a = new Article(); +a.Init("my title"); + +print(a.title); +print(Article.counter); \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/prototype_nested.js b/jsdoc_toolkit-2.3.3-beta/app/test/prototype_nested.js new file mode 100644 index 00000000..e8ca1ced --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/prototype_nested.js @@ -0,0 +1,9 @@ +/** @constructor */ +function Word() { +} + +Word.prototype.reverse = function() { +} + +Word.prototype.reverse.utf8 = function() { +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/prototype_oblit.js b/jsdoc_toolkit-2.3.3-beta/app/test/prototype_oblit.js new file mode 100644 index 00000000..6cfc39ca --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/prototype_oblit.js @@ -0,0 +1,13 @@ +/** @constructor */ +function Article() { +} + +Article.prototype = { + /** instance get title */ + getTitle: function(){ + } +} + +/** static get title */ +Article.getTitle = function(){ +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/prototype_oblit_constructor.js b/jsdoc_toolkit-2.3.3-beta/app/test/prototype_oblit_constructor.js new file mode 100644 index 00000000..92482486 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/prototype_oblit_constructor.js @@ -0,0 +1,24 @@ +/** @constructor */ +function Article() { +} + +Article.prototype = { + /** @constructor */ + Title: function(title) { + /** the value of the Title instance */ + this.title = title; + }, + + init: function(pages) { + /** the value of the pages of the Article instance */ + this.pages = pages; + } +} + +f = new Article(); +f.init("one two three"); + +t = new f.Title("my title"); + +print(f.pages); +print(t.title); \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/public.js b/jsdoc_toolkit-2.3.3-beta/app/test/public.js new file mode 100644 index 00000000..35d34f6f --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/public.js @@ -0,0 +1,10 @@ +/**@constructor*/ +function Foo() { + /** + @public + @static + @field + */ + var bar = function(x) { + } +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/scripts/code.js b/jsdoc_toolkit-2.3.3-beta/app/test/scripts/code.js new file mode 100644 index 00000000..e9d7ed2e --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/scripts/code.js @@ -0,0 +1,5 @@ +/** + @class + */ +function thisiscode() { +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/scripts/notcode.txt b/jsdoc_toolkit-2.3.3-beta/app/test/scripts/notcode.txt new file mode 100644 index 00000000..fcd737e7 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/scripts/notcode.txt @@ -0,0 +1,5 @@ +(This is not code) +function foo(){{{{ +( +! +@ \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/shared.js b/jsdoc_toolkit-2.3.3-beta/app/test/shared.js new file mode 100644 index 00000000..e1c277a6 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/shared.js @@ -0,0 +1,42 @@ + +/** + * Builtin object. + * @class + * @name Array + */ + +/**#@+ + * Extension to builtin array. + * @memberOf Array + * @method + */ + +/** + * @returns Boolen if some array members... + */ +Array.prototype.some = function(){}; + +/** + * Change every element of an array. + * @returns Filtered array copy. + */ +Array.prototype.filter = function(){}; + +/**#@-*/ + + +/** + * A first in, first out data structure. + * @constructor + */ +Queue = function(){}; + +/**#@+ + * Extension to Queue. + * @memberOf Queue + */ + +rewind = function(){ +} + +// should close automatically here. \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/shared2.js b/jsdoc_toolkit-2.3.3-beta/app/test/shared2.js new file mode 100644 index 00000000..3f7736a7 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/shared2.js @@ -0,0 +1,2 @@ +startOver = function(){ +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/shortcuts.js b/jsdoc_toolkit-2.3.3-beta/app/test/shortcuts.js new file mode 100644 index 00000000..f738f1e1 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/shortcuts.js @@ -0,0 +1,22 @@ +// /**#=+ +// * { +// * 'D': 'Date.prototype', +// * '$N': 'Number' +// * } +// */ +// var D = Date.prototype, +// $N = Number; +// +// D.locale = function(){ +// }; +// +// /** +// @return {string} The cardinal number string. +// */ +// $N.nth = function(n){ +// }; +// +// LOAD.file = function(){ +// } +// +// /**#=-*/ \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/static_this.js b/jsdoc_toolkit-2.3.3-beta/app/test/static_this.js new file mode 100644 index 00000000..9407b20f --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/static_this.js @@ -0,0 +1,13 @@ +/** the parent */ +var box = {}; + +/** @namespace */ +box.holder = {} + +box.holder.foo = function() { + /** the counter */ + this.counter = 1; +} + +box.holder.foo(); +print(box.holder.counter); diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/synonyms.js b/jsdoc_toolkit-2.3.3-beta/app/test/synonyms.js new file mode 100644 index 00000000..09066b98 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/synonyms.js @@ -0,0 +1,31 @@ +/** + @class + @inherits Bar#zop as #my_zop +*/ +function Foo() { + /** this is a zip. */ + this.zip = function() {} + + /** from Bar */ + this.my_zop = new Bar().zop; +} + +/** + @class + @borrows Foo#zip as this.my_zip +*/ +function Bar() { + /** this is a zop. */ + this.zop = function() {} + + /** from Foo */ + this.my_zip = new Foo().zip; +} + +/** @namespace */ +var myObject = { + /** + @type function + */ + myFunc: getFunction() +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/tosource.js b/jsdoc_toolkit-2.3.3-beta/app/test/tosource.js new file mode 100644 index 00000000..706d4765 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/tosource.js @@ -0,0 +1,23 @@ +/** + * @param {Object} object + * @return {string} + */ +function valueOf(object) {} + +/** + * @param {Object} object + * @return {string} + */ +function toString(object) {} + +/** + * @param {Object} object + * @return {string} + */ +function toSource(object) {} + +/** + * @param {Object} object + * @return {string} + */ +function constructor(object) {} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/app/test/variable_redefine.js b/jsdoc_toolkit-2.3.3-beta/app/test/variable_redefine.js new file mode 100644 index 00000000..2c07da09 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/app/test/variable_redefine.js @@ -0,0 +1,14 @@ +/** @constructor */ +function Foo() { + var bar = 1; + bar = 2; // redefining a private + + this.baz = 1; + baz = 2; // global + + /** a private */ + var blap = { + /** in here */ + tada: 1 + } +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/changes.txt b/jsdoc_toolkit-2.3.3-beta/changes.txt new file mode 100644 index 00000000..2d352aeb --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/changes.txt @@ -0,0 +1,117 @@ +== 2.3.3 == + + * Fixed bug that made all fields declared with the @property tag static. ( issue #262 ) + * Minor fix to better handle trailing slash on path to template (from jwmetrocat). (issue #237 ) + * Fix for @memberOf when applied to inner members. ( issue #264 ) + * Fix for @memberOf when applied to symbols documented with @name. ( issue #260 ) + * Applied patch from kunhualqk, fix for bug where @link to borrowed member did not resolve to parent class. ( issue #218 ) + * Fix for @requires not linking back to the required class + * Added experimental support for @constructs to have an argument, the class name, when applied to a function assignment. + +== 2.3.2 == + + * Minor update to the usage notes and corrected the version number displayed in the output. + +== 2.3.1 == + + * Fixed HTML typo in allfiles template. ( issue #228 ) + * Modified template to display version information for classes. + * Modified template to better support multiple methods with the same name. + * Fixed bug that caused template to error when backtick characters appeared around class names. + +== 2.3.0 == + + * Added option -u, --unique to avoid bug that causes multiple symbols with names that differ only by case to overwrite each others output on case-insensitive filesystems. ( issue #162 ) + * Fixed bug where {@links} in @deprecated tags did not resolve. ( issue #220 ) + * Fixed bug that caused parens around a function to make it to be unrecognized. ( issue #213 ) + * Fixed bug prevented explicit links to named anchors from working (thanks katgao.pku). ( issue #215 ) + * Fixed bug that prevented full description from appearing in file overview. ( issue #224 ) + +== 2.2.1 == + + * Fixed bug with class template, where sorting of methods was accidentally removed (thanks dezfowler). + * Added missing test files for the @exports unit tests. + +== 2.2.0 == + + * Fixed bug that caused exception when given a folder containing non-js files, even with the x commandline option set to "js". ( issue #193 ) + * Fixed typo in index template [patch submitted by olle]. ( issue #198 ) + * Modified @borrows tag experimentally to allow for missing "as ..." clause. + * Added support for the @exports tag, to allow one symbol to be documented as another. + * Added support for the -S option to document code following the Secure Modules pattern. + +== 2.1.0 == + + * Added support for the @event tag. + * Fixed bug that prevented the : character from appearing in symbol names. + * Fixed bug that prevented underscored symbols marked with @public being tagged as private. (issue #184 ) + * Fixed bug that randomly affected the @memberOf tag when the name of the symbol did not include the parent name. + * Fixed bug that prevented templates that were not in the jsdoc-toolkit folder from being found. ( issue #176 ) + * Added ability to check for trailing slash on template path. ( issue #177 ) + * Modified classDesc so that it no longer is appended with the constructor desc. + * Fixed call to plugin onDocCommentSrc. + * Added missing support for inline doc comments for function return types. ( issue #189 ) + * Added command line option -q, --quiet. + * Added command line option -E, --exclude. ( issue #143 ) + * Added 2 more hooks for plugins. ( issue #163 ) + * Added support for extending built-ins. ( issue #160 ) + * Added "compact" option to JSDOC.JsPlate.prototype.process. ( issue #159 ) + * @augments no longer documents static members as inherited. ( issue #138 ) + * @link to a class now goes to the page for that class, not the constructor. ( issue #178 ) + * Warnings of mismatched curly brace now include filename. ( issue #166 ) + * Fixed bug affecting template paths loaded via a configuration file when the trailing slash is missing. ( issue #191 ) + * Minor optimizations. + +== 2.0.2 == + + * Fixed bug that sometimes caused an example of division in the source code to be interpretted as a regex by the JsDoc Toolkit analyzer. ( issue #158 ) + * Fixed a bug that prevented private variables marked as @public from appearing in the documentation. ( issue #161 ) + * Fixed bug that prevented variable names with underscored properties from appearing in summaries. ( issue #173 ) + +== 2.0.1 == + + * Fixed bug that prevented @fileOverview tag from being recognized. + * Added support for @fieldOf as a synonym for @field plus @memberOf. + * Added support for @name tag in a @fileOverview comment to control the displayed name of the file. + * Added support for multiple @example tags. ( issue #152 ) + * Modified style sheet of jsdoc template to make more readable. ( issue #151 ) + * Fixed bug that prevented @since documentation from displaying correctly when it appeared in a class. ( issue #150 ) + * Fixed bug that caused inhertited properties to sometimes not resolve correctly. ( issue #144 ) + * Modified so that trailing whitespace in @example is always trimmed. ( issue #153 ) + * Added support for elseif to JsPlate. (hat tip to fredck) + * Added support for @location urls in the @overview comment to the jsdoc template. + +== Changes From Versions 1.4.0 to 2.0.0 == + + * Upgraded included version of Rhino from 1.6 to 1.7R1. + * Removed circular references in parsed documentation objects. + * Improved inheritance handling, now properties and events can be inherited same as methods. + * Improved handling of cross-file relationships, now having two related objects in separate files is not a problem. + * Improved ability to recognize membership of previously defined objects. + * Added ability to redefine parsing behavior with plugins. + * @methodOf is a synonym for @function and @memberOf. + * Added @default to document default values of members that are objects. + * Added ability to parse and refer to inner functions. + * Fixed bug that appeared when calling a method to set properties of the instance referred to by "this". + * Added ability to automatically create links to other symbols. + * New "jsdoc" template now produces fully W3C valid XHTML. + * Inline parameter type hint comments are now documented. + * Fixed error: Locally scoped variables (declared with var) no longer appear as global. + * It is now possible to run JsDoc Toolkit from any directory. + * Added support for inline {@link ...} tags. + * Added support for the -H command-line option to allow for custom content handlers. + * Tag names @inherits and @scope changed to @borrows and @lends. + ? Combining @constructor in a doclet with @lends now supported. + * Multiple @lend tags now supported. + * Added support for the @constructs tag, used inside a @lends block. + * Added support for the @constant tag. + * Fixed bug that prevented the use of [] as a default value. + * Added support for the @field tag. + * Added support for the @public tag (applied to inner functions). + * @namespace tag can now be applied to functions, not just object literals. + * Added support for the -s command line option to suppress source code output. + * Added new unit test framework. + * Underscored symbols are now treated as if they have a @private tag by default. + * Improved support for anonymous constructors. + * Added support for the nocode meta tag. + \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/conf/sample.conf b/jsdoc_toolkit-2.3.3-beta/conf/sample.conf new file mode 100644 index 00000000..ad0f08e6 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/conf/sample.conf @@ -0,0 +1,31 @@ +/* + This is an example of one way you could set up a configuration file to more + conveniently define some commandline options. You might like to do this if + you frequently reuse the same options. Note that you don't need to define + every option in this file, you can combine a configuration file with + additional options on the commandline if your wish. + + You would include this configuration file by running JsDoc Toolkit like so: + java -jar jsrun.jar app/run.js -c=conf/sample.conf + +*/ + +{ + // source files to use + _: ['app/test/jsdoc_test.js'], + + // document all functions, even uncommented ones + a: true, + + // including those marked @private + p: true, + + // some extra variables I want to include + D: {generatedBy: "Michael Mathews", copyright: "2008"}, + + // use this directory as the output directory + d: "docs", + + // use this template + t: "templates/jsdoc" +} \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/java/build.xml b/jsdoc_toolkit-2.3.3-beta/java/build.xml new file mode 100644 index 00000000..bb845ce3 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/java/build.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc_toolkit-2.3.3-beta/java/build_1.4.xml b/jsdoc_toolkit-2.3.3-beta/java/build_1.4.xml new file mode 100644 index 00000000..ab408a4c --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/java/build_1.4.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jsdoc_toolkit-2.3.3-beta/java/classes/js.jar b/jsdoc_toolkit-2.3.3-beta/java/classes/js.jar new file mode 100644 index 00000000..0352cb18 Binary files /dev/null and b/jsdoc_toolkit-2.3.3-beta/java/classes/js.jar differ diff --git a/jsdoc_toolkit-2.3.3-beta/java/src/JsDebugRun.java b/jsdoc_toolkit-2.3.3-beta/java/src/JsDebugRun.java new file mode 100755 index 00000000..319a5c67 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/java/src/JsDebugRun.java @@ -0,0 +1,21 @@ +/** + * A trivial bootstrap class that simply adds the path to the + * .js file as an argument to the Rhino call. This little hack + * allows the code in the .js file to have access to it's own + * path via the Rhino arguments object. This is necessary to + * allow the .js code to find resource files in a location + * relative to itself. + * + * USAGE: java -jar jsdebug.jar path/to/file.js + */ +public class JsDebugRun { + public static void main(String[] args) { + String[] jsargs = {"-j="+args[0]}; + + String[] allArgs = new String[jsargs.length + args.length]; + System.arraycopy(args, 0, allArgs, 0, args.length); + System.arraycopy(jsargs, 0, allArgs, args.length ,jsargs.length); + + org.mozilla.javascript.tools.debugger.Main.main(allArgs); + } +} diff --git a/jsdoc_toolkit-2.3.3-beta/java/src/JsRun.java b/jsdoc_toolkit-2.3.3-beta/java/src/JsRun.java new file mode 100644 index 00000000..25f519a9 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/java/src/JsRun.java @@ -0,0 +1,21 @@ +/** + * A trivial bootstrap class that simply adds the path to the + * .js file as an argument to the Rhino call. This little hack + * allows the code in the .js file to have access to it's own + * path via the Rhino arguments object. This is necessary to + * allow the .js code to find resource files in a location + * relative to itself. + * + * USAGE: java -jar jsrun.jar path/to/file.js + */ +public class JsRun { + public static void main(String[] args) { + String[] jsargs = {"-j="+args[0]}; + + String[] allArgs = new String[jsargs.length + args.length]; + System.arraycopy(args, 0, allArgs, 0, args.length); + System.arraycopy(jsargs, 0, allArgs, args.length ,jsargs.length); + + org.mozilla.javascript.tools.shell.Main.main(allArgs); + } +} diff --git a/jsdoc_toolkit-2.3.3-beta/jsdebug.jar b/jsdoc_toolkit-2.3.3-beta/jsdebug.jar new file mode 100644 index 00000000..a0ac7daa Binary files /dev/null and b/jsdoc_toolkit-2.3.3-beta/jsdebug.jar differ diff --git a/jsdoc_toolkit-2.3.3-beta/jsrun.jar b/jsdoc_toolkit-2.3.3-beta/jsrun.jar new file mode 100644 index 00000000..49c03f4c Binary files /dev/null and b/jsdoc_toolkit-2.3.3-beta/jsrun.jar differ diff --git a/jsdoc_toolkit-2.3.3-beta/jsrun.sh b/jsdoc_toolkit-2.3.3-beta/jsrun.sh new file mode 100755 index 00000000..74ca79c0 --- /dev/null +++ b/jsdoc_toolkit-2.3.3-beta/jsrun.sh @@ -0,0 +1,52 @@ +#!/bin/ksh + +# launcher script for jsdoc +# Author: Avi Deitcher +# +# This program is released under the MIT License as follows: + +# Copyright (c) 2008-2009 Atomic Inc +# +#Permission is hereby granted, free of charge, to any person +#obtaining a copy of this software and associated documentation +#files (the "Software"), to deal in the Software without +#restriction, including without limitation the rights to use, +#copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the +#Software is furnished to do so, subject to the following +#conditions: +## +#The above copyright notice and this permission notice shall be +#included in all copies or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +#EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +#OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +#NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +#WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +#FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +#OTHER DEALINGS IN THE SOFTWARE. +# + + +if [[ -n "$JSDOCDIR" ]]; then + _DOCDIR="-Djsdoc.dir=$JSDOCDIR" + _APPDIR="$JSDOCDIR/app" + _BASEDIR="$JSDOCDIR" +else + _DOCDIR="" + _APPDIR="./app" + _BASEDIR="." +fi + +if [[ -n "$JSDOCTEMPLATEDIR" ]]; then + _TDIR="-Djsdoc.template.dir=$JSDOCTEMPLATEDIR" +else + _TDIR="" +fi + +CMD="java $_DOCDIR $_TDIR -jar $_BASEDIR/jsrun.jar $_APPDIR/run.js $@" +echo $CMD +$CMD + diff --git a/lint/coding_guidelines.pl b/lint/coding_guidelines.pl new file mode 100755 index 00000000..78c4b0cd --- /dev/null +++ b/lint/coding_guidelines.pl @@ -0,0 +1,73 @@ +#!/usr/bin/env perl + +# SJCL coding guidelines: +# +# No tabs in any Javascript +# +# Indentation is two spaces. Alignment of stuff in multi-line statements is +# encouraged. +# +# Braces everywhere, following jslint. I'm pretty sure the Closure compressor +# removes them. Semicolons at the end of every statement. +# +# ++ and -- are allowed. +# +# Varibles are in camelCase. I prefer underscore_separated, but JavaScript uses +# camelCase everywhere. +# +# Private members and methods are prefixed by underscores. +# +# Constants (and only constants) are UNDERSCORE_SEPARATED_UPPER_CASE. The +# compression scripts rely on this. +# +# Classes begin with a capital letter (not yet implemented. Namespaces too?) +# +# Block comments are not on the same line as code. + +my $in_comment = 0; +my $file = ''; + +# for some reason, $. doesn't work. +my $line = 0; + +while (<>) { + if ($ARGV ne $file) { + # opening a new file + if ($in_comment) { + print STDERR "Opening file $ARGV: comment from $file wasn't closed.\n"; + } + $file = $ARGV; + $in_comment = $line = 0; + } + $line ++; + + if (/\/\*(?:[^\*]|\*[^\/])*(\*\/\s*)?/) { + # block comment on this line + $in_comment = 1 unless defined $1; + $ba = "$`$'"; + + # shouldn't have code before or after it + print STDERR "$file line $line: block comment and code together.\n" if $ba =~ /\S/; + + next; + } elsif ($in_comment and /\*\//) { + + # leaving block comment + $in_comment = 0; + print STDERR "$file line $line: block comment and code together.\n" if $' =~ /\S/; + } + + # don't enforce code style in a comment. + next if $in_comment; + + reset; + while (?[\.\s+\*/<>=,;:-]([a-zA-Z0-9_]+_[a-zA-Z0-9_]*)?) { + # find variable names with underscores + my $varname = $1; + print STDERR "$file line $line: Variable name $varname contains an underscore\n" + if $varname =~ /[a-z]/; + } + + reset; + print STDERR "$file line $line contains a tab\n" if /\t/; +} diff --git a/lint/jslint_rhino.js b/lint/jslint_rhino.js new file mode 100644 index 00000000..63961da3 --- /dev/null +++ b/lint/jslint_rhino.js @@ -0,0 +1,5658 @@ +// jslint.js +// 2010-04-06 + +/* +Copyright (c) 2002 Douglas Crockford (www.JSLint.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* + JSLINT is a global function. It takes two parameters. + + var myResult = JSLINT(source, option); + + The first parameter is either a string or an array of strings. If it is a + string, it will be split on '\n' or '\r'. If it is an array of strings, it + is assumed that each string represents one line. The source can be a + JavaScript text, or HTML text, or a Konfabulator text. + + The second parameter is an optional object of options which control the + operation of JSLINT. Most of the options are booleans: They are all are + optional and have a default value of false. + + If it checks out, JSLINT returns true. Otherwise, it returns false. + + If false, you can inspect JSLINT.errors to find out the problems. + JSLINT.errors is an array of objects containing these members: + + { + line : The line (relative to 0) at which the lint was found + character : The character (relative to 0) at which the lint was found + reason : The problem + evidence : The text line in which the problem occurred + raw : The raw message before the details were inserted + a : The first detail + b : The second detail + c : The third detail + d : The fourth detail + } + + If a fatal error was found, a null will be the last element of the + JSLINT.errors array. + + You can request a Function Report, which shows all of the functions + and the parameters and vars that they use. This can be used to find + implied global variables and other problems. The report is in HTML and + can be inserted in an HTML . + + var myReport = JSLINT.report(limited); + + If limited is true, then the report will be limited to only errors. + + You can request a data structure which contains JSLint's results. + + var myData = JSLINT.data(); + + It returns a structure with this form: + + { + errors: [ + { + line: NUMBER, + character: NUMBER, + reason: STRING, + evidence: STRING + } + ], + functions: [ + name: STRING, + line: NUMBER, + last: NUMBER, + param: [ + STRING + ], + closure: [ + STRING + ], + var: [ + STRING + ], + exception: [ + STRING + ], + outer: [ + STRING + ], + unused: [ + STRING + ], + global: [ + STRING + ], + label: [ + STRING + ] + ], + globals: [ + STRING + ], + member: { + STRING: NUMBER + }, + unuseds: [ + { + name: STRING, + line: NUMBER + } + ], + implieds: [ + { + name: STRING, + line: NUMBER + } + ], + urls: [ + STRING + ], + json: BOOLEAN + } + + Empty arrays will not be included. + +*/ + +/*jslint + evil: true, nomen: false, onevar: false, regexp: false, strict: true +*/ + +/*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", + "(begin)", "(breakage)", "(context)", "(error)", "(global)", + "(identifier)", "(last)", "(line)", "(loopage)", "(name)", "(onevar)", + "(params)", "(scope)", "(verb)", "*", "+", "++", "-", "--", "\/", + "<", "<=", "==", "===", ">", ">=", ADSAFE, ActiveXObject, + Array, Boolean, COM, CScript, Canvas, CustomAnimation, Date, Debug, E, + Enumerator, Error, EvalError, FadeAnimation, Flash, FormField, Frame, + Function, HotKey, Image, JSON, LN10, LN2, LOG10E, LOG2E, MAX_VALUE, + MIN_VALUE, Math, MenuItem, MoveAnimation, NEGATIVE_INFINITY, Number, + Object, Option, PI, POSITIVE_INFINITY, Point, RangeError, Rectangle, + ReferenceError, RegExp, ResizeAnimation, RotateAnimation, SQRT1_2, + SQRT2, ScrollBar, String, Style, SyntaxError, System, Text, TextArea, + Timer, TypeError, URIError, URL, VBArray, WScript, Web, Window, XMLDOM, + XMLHttpRequest, "\\", a, abbr, acronym, addEventListener, address, + adsafe, alert, aliceblue, animator, antiquewhite, appleScript, applet, + apply, approved, aqua, aquamarine, area, arguments, arity, article, + aside, audio, autocomplete, azure, b, background, + "background-attachment", "background-color", "background-image", + "background-position", "background-repeat", base, bdo, beep, beige, big, + bisque, bitwise, black, blanchedalmond, block, blockquote, blue, + blueviolet, blur, body, border, "border-bottom", "border-bottom-color", + "border-bottom-style", "border-bottom-width", "border-collapse", + "border-color", "border-left", "border-left-color", "border-left-style", + "border-left-width", "border-right", "border-right-color", + "border-right-style", "border-right-width", "border-spacing", + "border-style", "border-top", "border-top-color", "border-top-style", + "border-top-width", "border-width", bottom, br, brown, browser, + burlywood, button, bytesToUIString, c, cadetblue, call, callee, caller, + canvas, cap, caption, "caption-side", cases, center, charAt, charCodeAt, + character, chartreuse, chocolate, chooseColor, chooseFile, chooseFolder, + cite, clear, clearInterval, clearTimeout, clip, close, closeWidget, + closed, closure, cm, code, col, colgroup, color, command, comment, + condition, confirm, console, constructor, content, convertPathToHFS, + convertPathToPlatform, coral, cornflowerblue, cornsilk, + "counter-increment", "counter-reset", create, crimson, css, cursor, + cyan, d, darkblue, darkcyan, darkgoldenrod, darkgray, darkgreen, + darkkhaki, darkmagenta, darkolivegreen, darkorange, darkorchid, darkred, + darksalmon, darkseagreen, darkslateblue, darkslategray, darkturquoise, + darkviolet, data, datalist, dd, debug, decodeURI, decodeURIComponent, + deeppink, deepskyblue, defaultStatus, defineClass, del, deserialize, + details, devel, dfn, dialog, dimension, dimgray, dir, direction, + display, div, dl, document, dodgerblue, dt, edition, else, em, embed, + empty, "empty-cells", encodeURI, encodeURIComponent, entityify, eqeqeq, + errors, es5, escape, eval, event, evidence, evil, ex, exception, exec, exps, + fieldset, figure, filesystem, firebrick, first, float, floor, + floralwhite, focus, focusWidget, font, "font-face", "font-family", + "font-size", "font-size-adjust", "font-stretch", "font-style", + "font-variant", "font-weight", footer, forestgreen, forin, form, + fragment, frame, frames, frameset, from, fromCharCode, fuchsia, fud, + funct, function, functions, g, gainsboro, gc, getComputedStyle, + ghostwhite, global, globals, gold, goldenrod, gray, green, greenyellow, + h1, h2, h3, h4, h5, h6, hasOwnProperty, head, header, height, help, + hgroup, history, honeydew, hotpink, hr, html, i, iTunes, id, identifier, + iframe, img, immed, implieds, in, include, indent, indexOf, indianred, + indigo, init, input, ins, isAlpha, isApplicationRunning, isDigit, + isFinite, isNaN, ivory, join, jslint, json, kbd, keygen, khaki, + konfabulatorVersion, label, labelled, lang, last, lavender, + lavenderblush, lawngreen, laxbreak, lbp, led, left, legend, + lemonchiffon, length, "letter-spacing", li, lib, lightblue, lightcoral, + lightcyan, lightgoldenrodyellow, lightgreen, lightpink, lightsalmon, + lightseagreen, lightskyblue, lightslategray, lightsteelblue, + lightyellow, lime, limegreen, line, "line-height", linen, link, + "list-style", "list-style-image", "list-style-position", + "list-style-type", load, loadClass, location, log, m, magenta, map, + margin, "margin-bottom", "margin-left", "margin-right", "margin-top", + mark, "marker-offset", maroon, match, "max-height", "max-width", maxerr, + maxlen, md5, media, mediumaquamarine, mediumblue, mediumorchid, + mediumpurple, mediumseagreen, mediumslateblue, mediumspringgreen, + mediumturquoise, mediumvioletred, member, menu, message, meta, meter, + midnightblue, "min-height", "min-width", mintcream, mistyrose, mm, + moccasin, moveBy, moveTo, name, nav, navajowhite, navigator, navy, new, + newcap, noframes, nomen, noscript, nud, object, ol, oldlace, olive, + olivedrab, on, onbeforeunload, onblur, onerror, onevar, onfocus, onload, + onresize, onunload, opacity, open, openURL, opener, opera, optgroup, + option, orange, orangered, orchid, outer, outline, "outline-color", + "outline-style", "outline-width", output, overflow, "overflow-x", + "overflow-y", p, padding, "padding-bottom", "padding-left", + "padding-right", "padding-top", page, "page-break-after", + "page-break-before", palegoldenrod, palegreen, paleturquoise, + palevioletred, papayawhip, param, parent, parseFloat, parseInt, + passfail, pc, peachpuff, peru, pink, play, plum, plusplus, pop, + popupMenu, position, powderblue, pre, predef, preferenceGroups, + preferences, print, progress, prompt, prototype, pt, purple, push, px, + q, quit, quotes, random, range, raw, reach, readFile, readUrl, reason, + red, regexp, reloadWidget, removeEventListener, replace, report, + reserved, resizeBy, resizeTo, resolvePath, resumeUpdates, rhino, right, + rosybrown, royalblue, rp, rt, ruby, runCommand, runCommandInBg, + saddlebrown, safe, salmon, samp, sandybrown, saveAs, savePreferences, + screen, script, scroll, scrollBy, scrollTo, seagreen, seal, search, + seashell, section, select, serialize, setInterval, setTimeout, shift, + showWidgetPreferences, sienna, silver, skyblue, slateblue, slategray, + sleep, slice, small, snow, sort, source, span, spawn, speak, split, + springgreen, src, stack, status, steelblue, strict, strong, style, + styleproperty, sub, substr, sup, supplant, suppressUpdates, sync, + system, table, "table-layout", tan, tbody, td, teal, tellWidget, test, + "text-align", "text-decoration", "text-indent", "text-shadow", + "text-transform", textarea, tfoot, th, thead, thistle, time, title, + toLowerCase, toString, toUpperCase, toint32, token, tomato, top, tr, tt, + turquoise, type, u, ul, undef, unescape, "unicode-bidi", unused, + unwatch, updateNow, urls, value, valueOf, var, version, + "vertical-align", video, violet, visibility, watch, wheat, white, + "white-space", whitesmoke, widget, width, windows, "word-spacing", + "word-wrap", yahooCheckLogin, yahooLogin, yahooLogout, yellow, + yellowgreen, "z-index" +*/ + +// We build the application inside a function so that we produce only a single +// global variable. The function will be invoked, its return value is the JSLINT +// application itself. + +"use strict"; + +var JSLINT = (function () { + var adsafe_id, // The widget's ADsafe id. + adsafe_may, // The widget may load approved scripts. + adsafe_went, // ADSAFE.go has been called. + anonname, // The guessed name for anonymous functions. + approved, // ADsafe approved urls. + + atrule = { + media : true, + 'font-face': true, + page : true + }, + +// These are operators that should not be used with the ! operator. + + bang = { + '<': true, + '<=': true, + '==': true, + '===': true, + '!==': true, + '!=': true, + '>': true, + '>=': true, + '+': true, + '-': true, + '*': true, + '/': true, + '%': true + }, + +// These are members that should not be permitted in the safe subset. + + banned = { // the member names that ADsafe prohibits. + 'arguments' : true, + callee : true, + caller : true, + constructor : true, + 'eval' : true, + prototype : true, + stack : true, + unwatch : true, + valueOf : true, + watch : true + }, + + +// These are the JSLint boolean options. + + boolOptions = { + adsafe : true, // if ADsafe should be enforced + bitwise : true, // if bitwise operators should not be allowed + browser : true, // if the standard browser globals should be predefined + cap : true, // if upper case HTML should be allowed + css : true, // if CSS workarounds should be tolerated + debug : true, // if debugger statements should be allowed + devel : true, // if logging should be allowed (console, alert, etc.) + eqeqeq : true, // if === should be required + es5 : true, // if ES5 syntax should be allowed + evil : true, // if eval should be allowed + forin : true, // if for in statements must filter + fragment : true, // if HTML fragments should be allowed + immed : true, // if immediate invocations must be wrapped in parens + laxbreak : true, // if line breaks should not be checked + newcap : true, // if constructor names must be capitalized + nomen : true, // if names should be checked + on : true, // if HTML event handlers should be allowed + onevar : true, // if only one var statement per function should be allowed + passfail : true, // if the scan should stop on first error + plusplus : true, // if increment/decrement should not be allowed + regexp : true, // if the . should not be allowed in regexp literals + rhino : true, // if the Rhino environment globals should be predefined + undef : true, // if variables should be declared before used + safe : true, // if use of some browser features should be restricted + windows : true, // if MS Windows-specigic globals should be predefined + strict : true, // require the "use strict"; pragma + sub : true, // if all forms of subscript notation are tolerated + white : true, // if strict whitespace rules apply + widget : true // if the Yahoo Widgets globals should be predefined + }, + +// browser contains a set of global names which are commonly provided by a +// web browser environment. + + browser = { + addEventListener: false, + blur : false, + clearInterval : false, + clearTimeout : false, + close : false, + closed : false, + defaultStatus : false, + document : false, + event : false, + focus : false, + frames : false, + getComputedStyle: false, + history : false, + Image : false, + length : false, + location : false, + moveBy : false, + moveTo : false, + name : false, + navigator : false, + onbeforeunload : true, + onblur : true, + onerror : true, + onfocus : true, + onload : true, + onresize : true, + onunload : true, + open : false, + opener : false, + Option : false, + parent : false, + print : false, + removeEventListener: false, + resizeBy : false, + resizeTo : false, + screen : false, + scroll : false, + scrollBy : false, + scrollTo : false, + setInterval : false, + setTimeout : false, + status : false, + top : false, + XMLHttpRequest : false + }, + + cssAttributeData, + cssAny, + + cssColorData = { + "aliceblue" : true, + "antiquewhite" : true, + "aqua" : true, + "aquamarine" : true, + "azure" : true, + "beige" : true, + "bisque" : true, + "black" : true, + "blanchedalmond" : true, + "blue" : true, + "blueviolet" : true, + "brown" : true, + "burlywood" : true, + "cadetblue" : true, + "chartreuse" : true, + "chocolate" : true, + "coral" : true, + "cornflowerblue" : true, + "cornsilk" : true, + "crimson" : true, + "cyan" : true, + "darkblue" : true, + "darkcyan" : true, + "darkgoldenrod" : true, + "darkgray" : true, + "darkgreen" : true, + "darkkhaki" : true, + "darkmagenta" : true, + "darkolivegreen" : true, + "darkorange" : true, + "darkorchid" : true, + "darkred" : true, + "darksalmon" : true, + "darkseagreen" : true, + "darkslateblue" : true, + "darkslategray" : true, + "darkturquoise" : true, + "darkviolet" : true, + "deeppink" : true, + "deepskyblue" : true, + "dimgray" : true, + "dodgerblue" : true, + "firebrick" : true, + "floralwhite" : true, + "forestgreen" : true, + "fuchsia" : true, + "gainsboro" : true, + "ghostwhite" : true, + "gold" : true, + "goldenrod" : true, + "gray" : true, + "green" : true, + "greenyellow" : true, + "honeydew" : true, + "hotpink" : true, + "indianred" : true, + "indigo" : true, + "ivory" : true, + "khaki" : true, + "lavender" : true, + "lavenderblush" : true, + "lawngreen" : true, + "lemonchiffon" : true, + "lightblue" : true, + "lightcoral" : true, + "lightcyan" : true, + "lightgoldenrodyellow" : true, + "lightgreen" : true, + "lightpink" : true, + "lightsalmon" : true, + "lightseagreen" : true, + "lightskyblue" : true, + "lightslategray" : true, + "lightsteelblue" : true, + "lightyellow" : true, + "lime" : true, + "limegreen" : true, + "linen" : true, + "magenta" : true, + "maroon" : true, + "mediumaquamarine" : true, + "mediumblue" : true, + "mediumorchid" : true, + "mediumpurple" : true, + "mediumseagreen" : true, + "mediumslateblue" : true, + "mediumspringgreen" : true, + "mediumturquoise" : true, + "mediumvioletred" : true, + "midnightblue" : true, + "mintcream" : true, + "mistyrose" : true, + "moccasin" : true, + "navajowhite" : true, + "navy" : true, + "oldlace" : true, + "olive" : true, + "olivedrab" : true, + "orange" : true, + "orangered" : true, + "orchid" : true, + "palegoldenrod" : true, + "palegreen" : true, + "paleturquoise" : true, + "palevioletred" : true, + "papayawhip" : true, + "peachpuff" : true, + "peru" : true, + "pink" : true, + "plum" : true, + "powderblue" : true, + "purple" : true, + "red" : true, + "rosybrown" : true, + "royalblue" : true, + "saddlebrown" : true, + "salmon" : true, + "sandybrown" : true, + "seagreen" : true, + "seashell" : true, + "sienna" : true, + "silver" : true, + "skyblue" : true, + "slateblue" : true, + "slategray" : true, + "snow" : true, + "springgreen" : true, + "steelblue" : true, + "tan" : true, + "teal" : true, + "thistle" : true, + "tomato" : true, + "turquoise" : true, + "violet" : true, + "wheat" : true, + "white" : true, + "whitesmoke" : true, + "yellow" : true, + "yellowgreen" : true + }, + + cssBorderStyle, + cssBreak, + + cssLengthData = { + '%': true, + 'cm': true, + 'em': true, + 'ex': true, + 'in': true, + 'mm': true, + 'pc': true, + 'pt': true, + 'px': true + }, + + cssOverflow, + + devel = { + alert : false, + confirm : false, + console : false, + Debug : false, + opera : false, + prompt : false + }, + + escapes = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '/' : '\\/', + '\\': '\\\\' + }, + + funct, // The current function + + functionicity = [ + 'closure', 'exception', 'global', 'label', + 'outer', 'unused', 'var' + ], + + functions, // All of the functions + + global, // The global scope + htmltag = { + a: {}, + abbr: {}, + acronym: {}, + address: {}, + applet: {}, + area: {empty: true, parent: ' map '}, + article: {}, + aside: {}, + audio: {}, + b: {}, + base: {empty: true, parent: ' head '}, + bdo: {}, + big: {}, + blockquote: {}, + body: {parent: ' html noframes '}, + br: {empty: true}, + button: {}, + canvas: {parent: ' body p div th td '}, + caption: {parent: ' table '}, + center: {}, + cite: {}, + code: {}, + col: {empty: true, parent: ' table colgroup '}, + colgroup: {parent: ' table '}, + command: {parent: ' menu '}, + datalist: {}, + dd: {parent: ' dl '}, + del: {}, + details: {}, + dialog: {}, + dfn: {}, + dir: {}, + div: {}, + dl: {}, + dt: {parent: ' dl '}, + em: {}, + embed: {}, + fieldset: {}, + figure: {}, + font: {}, + footer: {}, + form: {}, + frame: {empty: true, parent: ' frameset '}, + frameset: {parent: ' html frameset '}, + h1: {}, + h2: {}, + h3: {}, + h4: {}, + h5: {}, + h6: {}, + head: {parent: ' html '}, + header: {}, + hgroup: {}, + html: {parent: '*'}, + hr: {empty: true}, + i: {}, + iframe: {}, + img: {empty: true}, + input: {empty: true}, + ins: {}, + kbd: {}, + keygen: {}, + label: {}, + legend: {parent: ' details fieldset figure '}, + li: {parent: ' dir menu ol ul '}, + link: {empty: true, parent: ' head '}, + map: {}, + mark: {}, + menu: {}, + meta: {empty: true, parent: ' head noframes noscript '}, + meter: {}, + nav: {}, + noframes: {parent: ' html body '}, + noscript: {parent: ' body head noframes '}, + object: {}, + ol: {}, + optgroup: {parent: ' select '}, + option: {parent: ' optgroup select '}, + output: {}, + p: {}, + param: {empty: true, parent: ' applet object '}, + pre: {}, + progress: {}, + q: {}, + rp: {}, + rt: {}, + ruby: {}, + samp: {}, + script: {empty: true, parent: ' body div frame head iframe p pre span '}, + section: {}, + select: {}, + small: {}, + span: {}, + source: {}, + strong: {}, + style: {parent: ' head ', empty: true}, + sub: {}, + sup: {}, + table: {}, + tbody: {parent: ' table '}, + td: {parent: ' tr '}, + textarea: {}, + tfoot: {parent: ' table '}, + th: {parent: ' tr '}, + thead: {parent: ' table '}, + time: {}, + title: {parent: ' head '}, + tr: {parent: ' table tbody thead tfoot '}, + tt: {}, + u: {}, + ul: {}, + 'var': {}, + video: {} + }, + + ids, // HTML ids + implied, // Implied globals + inblock, + indent, + jsonmode, + lines, + lookahead, + member, + membersOnly, + nexttoken, + noreach, + option, + predefined, // Global variables defined by option + prereg, + prevtoken, + + rhino = { + defineClass : false, + deserialize : false, + gc : false, + help : false, + load : false, + loadClass : false, + print : false, + quit : false, + readFile : false, + readUrl : false, + runCommand : false, + seal : false, + serialize : false, + spawn : false, + sync : false, + toint32 : false, + version : false + }, + + scope, // The current scope + + windows = { + ActiveXObject: false, + CScript : false, + Debug : false, + Enumerator : false, + System : false, + VBArray : false, + WScript : false + }, + + src, + stack, + +// standard contains the global names that are provided by the +// ECMAScript standard. + + standard = { + Array : false, + Boolean : false, + Date : false, + decodeURI : false, + decodeURIComponent : false, + encodeURI : false, + encodeURIComponent : false, + Error : false, + 'eval' : false, + EvalError : false, + Function : false, + hasOwnProperty : false, + isFinite : false, + isNaN : false, + JSON : false, + Math : false, + Number : false, + Object : false, + parseInt : false, + parseFloat : false, + RangeError : false, + ReferenceError : false, + RegExp : false, + String : false, + SyntaxError : false, + TypeError : false, + URIError : false + }, + + standard_member = { + E : true, + LN2 : true, + LN10 : true, + LOG2E : true, + LOG10E : true, + PI : true, + SQRT1_2 : true, + SQRT2 : true, + MAX_VALUE : true, + MIN_VALUE : true, + NEGATIVE_INFINITY : true, + POSITIVE_INFINITY : true + }, + + strict_mode, + syntax = {}, + tab, + token, + urls, + warnings, + +// widget contains the global names which are provided to a Yahoo +// (fna Konfabulator) widget. + + widget = { + alert : true, + animator : true, + appleScript : true, + beep : true, + bytesToUIString : true, + Canvas : true, + chooseColor : true, + chooseFile : true, + chooseFolder : true, + closeWidget : true, + COM : true, + convertPathToHFS : true, + convertPathToPlatform : true, + CustomAnimation : true, + escape : true, + FadeAnimation : true, + filesystem : true, + Flash : true, + focusWidget : true, + form : true, + FormField : true, + Frame : true, + HotKey : true, + Image : true, + include : true, + isApplicationRunning : true, + iTunes : true, + konfabulatorVersion : true, + log : true, + md5 : true, + MenuItem : true, + MoveAnimation : true, + openURL : true, + play : true, + Point : true, + popupMenu : true, + preferenceGroups : true, + preferences : true, + print : true, + prompt : true, + random : true, + Rectangle : true, + reloadWidget : true, + ResizeAnimation : true, + resolvePath : true, + resumeUpdates : true, + RotateAnimation : true, + runCommand : true, + runCommandInBg : true, + saveAs : true, + savePreferences : true, + screen : true, + ScrollBar : true, + showWidgetPreferences : true, + sleep : true, + speak : true, + Style : true, + suppressUpdates : true, + system : true, + tellWidget : true, + Text : true, + TextArea : true, + Timer : true, + unescape : true, + updateNow : true, + URL : true, + Web : true, + widget : true, + Window : true, + XMLDOM : true, + XMLHttpRequest : true, + yahooCheckLogin : true, + yahooLogin : true, + yahooLogout : true + }, + +// xmode is used to adapt to the exceptions in html parsing. +// It can have these states: +// false .js script file +// html +// outer +// script +// style +// scriptstring +// styleproperty + + xmode, + xquote, + +// unsafe comment or string + ax = /@cc|<\/?|script|\]*s\]|<\s*!|</i, +// unsafe characters that are silently deleted by one or more browsers + cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, +// token + tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jslint|members?|global)?|=|\/)?|\*[\/=]?|\+[+=]?|-[\-=]?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/, +// html token +//////// hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-]*|[0-9]+|--|.)/, + hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-]*|[0-9]+|--)/, +// characters in strings that need escapement + nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, + nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, +// outer html token + ox = /[>&]|<[\/!]?|--/, +// star slash + lx = /\*\/|\/\*/, +// identifier + ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/, +// javascript url + jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i, +// url badness + ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto/i, +// style + sx = /^\s*([{:#%.=,>+\[\]@()"';]|\*=?|\$=|\|=|\^=|~=|[a-zA-Z_][a-zA-Z0-9_\-]*|[0-9]+|<\/|\/\*)/, + ssx = /^\s*([@#!"'};:\-%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\/\*?|\d+(?:\.\d+)?|<\/)/, +// attributes characters + qx = /[^a-zA-Z0-9+\-_\/ ]/, +// query characters for ids + dx = /[\[\]\/\\"'*<>.&:(){}+=#]/, + + rx = { + outer: hx, + html: hx, + style: sx, + styleproperty: ssx + }; + + function F() {} + + if (typeof Object.create !== 'function') { + Object.create = function (o) { + F.prototype = o; + return new F(); + }; + } + + + function is_own(object, name) { + return Object.prototype.hasOwnProperty.call(object, name); + } + + + function combine(t, o) { + var n; + for (n in o) { + if (is_own(o, n)) { + t[n] = o[n]; + } + } + } + + String.prototype.entityify = function () { + return this. + replace(/&/g, '&'). + replace(//g, '>'); + }; + + String.prototype.isAlpha = function () { + return (this >= 'a' && this <= 'z\uffff') || + (this >= 'A' && this <= 'Z\uffff'); + }; + + + String.prototype.isDigit = function () { + return (this >= '0' && this <= '9'); + }; + + + String.prototype.supplant = function (o) { + return this.replace(/\{([^{}]*)\}/g, function (a, b) { + var r = o[b]; + return typeof r === 'string' || typeof r === 'number' ? r : a; + }); + }; + + String.prototype.name = function () { + +// If the string looks like an identifier, then we can return it as is. +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can simply slap some quotes around it. +// Otherwise we must also replace the offending characters with safe +// sequences. + + if (ix.test(this)) { + return this; + } + if (nx.test(this)) { + return '"' + this.replace(nxg, function (a) { + var c = escapes[a]; + if (c) { + return c; + } + return '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4); + }) + '"'; + } + return '"' + this + '"'; + }; + + + function assume() { + if (!option.safe) { + if (option.rhino) { + combine(predefined, rhino); + } + if (option.devel) { + combine(predefined, devel); + } + if (option.browser) { + combine(predefined, browser); + } + if (option.windows) { + combine(predefined, windows); + } + if (option.widget) { + combine(predefined, widget); + } + } + } + + +// Produce an error warning. + + function quit(m, l, ch) { + throw { + name: 'JSLintError', + line: l, + character: ch, + message: m + " (" + Math.floor((l / lines.length) * 100) + + "% scanned)." + }; + } + + function warning(m, t, a, b, c, d) { + var ch, l, w; + t = t || nexttoken; + if (t.id === '(end)') { // `~ + t = token; + } + l = t.line || 0; + ch = t.from || 0; + w = { + id: '(error)', + raw: m, + evidence: lines[l - 1] || '', + line: l, + character: ch, + a: a, + b: b, + c: c, + d: d + }; + w.reason = m.supplant(w); + JSLINT.errors.push(w); + if (option.passfail) { + quit('Stopping. ', l, ch); + } + warnings += 1; + if (warnings >= option.maxerr) { + quit("Too many errors.", l, ch); + } + return w; + } + + function warningAt(m, l, ch, a, b, c, d) { + return warning(m, { + line: l, + from: ch + }, a, b, c, d); + } + + function error(m, t, a, b, c, d) { + var w = warning(m, t, a, b, c, d); + quit("Stopping, unable to continue.", w.line, w.character); + } + + function errorAt(m, l, ch, a, b, c, d) { + return error(m, { + line: l, + from: ch + }, a, b, c, d); + } + + + +// lexical analysis + + var lex = (function lex() { + var character, from, line, s; + +// Private lex methods + + function nextLine() { + var at; + if (line >= lines.length) { + return false; + } + character = 1; + s = lines[line]; + line += 1; + at = s.search(/ \t/); + if (at >= 0) { + warningAt("Mixed spaces and tabs.", line, at + 1); + } + s = s.replace(/\t/g, tab); + at = s.search(cx); + if (at >= 0) { + warningAt("Unsafe character.", line, at); + } + if (option.maxlen && option.maxlen < s.length) { + warningAt("Line too long.", line, s.length); + } + return true; + } + +// Produce a token object. The token inherits from a syntax symbol. + + function it(type, value) { + var i, t; + if (type === '(color)') { + t = {type: type}; + } else if (type === '(punctuator)' || + (type === '(identifier)' && is_own(syntax, value))) { + t = syntax[value] || syntax['(error)']; + } else { + t = syntax[type]; + } + t = Object.create(t); + if (type === '(string)' || type === '(range)') { + if (jx.test(value)) { + warningAt("Script URL.", line, from); + } + } + if (type === '(identifier)') { + t.identifier = true; + if (value === '__iterator__' || value === '__proto__') { + errorAt("Reserved name '{a}'.", + line, from, value); + } else if (option.nomen && + (value.charAt(0) === '_' || + value.charAt(value.length - 1) === '_')) { + warningAt("Unexpected {a} in '{b}'.", line, from, + "dangling '_'", value); + } + } + t.value = value; + t.line = line; + t.character = character; + t.from = from; + i = t.id; + if (i !== '(endline)') { + prereg = i && + (('(,=:[!&|?{};'.indexOf(i.charAt(i.length - 1)) >= 0) || + i === 'return'); + } + return t; + } + +// Public lex methods + + return { + init: function (source) { + if (typeof source === 'string') { + lines = source. + replace(/\r\n/g, '\n'). + replace(/\r/g, '\n'). + split('\n'); + } else { + lines = source; + } + line = 0; + nextLine(); + from = 1; + }, + + range: function (begin, end) { + var c, value = ''; + from = character; + if (s.charAt(0) !== begin) { + errorAt("Expected '{a}' and instead saw '{b}'.", + line, character, begin, s.charAt(0)); + } + for (;;) { + s = s.slice(1); + character += 1; + c = s.charAt(0); + switch (c) { + case '': + errorAt("Missing '{a}'.", line, character, c); + break; + case end: + s = s.slice(1); + character += 1; + return it('(range)', value); + case xquote: + case '\\': + warningAt("Unexpected '{a}'.", line, character, c); + } + value += c; + } + + }, + +// token -- this is called by advance to get the next token. + + token: function () { + var b, c, captures, d, depth, high, i, l, low, q, t; + + function match(x) { + var r = x.exec(s), r1; + if (r) { + l = r[0].length; + r1 = r[1]; + c = r1.charAt(0); + s = s.substr(l); + from = character + l - r1.length; + character += l; + return r1; + } + } + + function string(x) { + var c, j, r = ''; + + if (jsonmode && x !== '"') { + warningAt("Strings must use doublequote.", + line, character); + } + + if (xquote === x || (xmode === 'scriptstring' && !xquote)) { + return it('(punctuator)', x); + } + + function esc(n) { + var i = parseInt(s.substr(j + 1, n), 16); + j += n; + if (i >= 32 && i <= 126 && + i !== 34 && i !== 92 && i !== 39) { + warningAt("Unnecessary escapement.", line, character); + } + character += n; + c = String.fromCharCode(i); + } + j = 0; + for (;;) { + while (j >= s.length) { + j = 0; + if (xmode !== 'html' || !nextLine()) { + errorAt("Unclosed string.", line, from); + } + } + c = s.charAt(j); + if (c === x) { + character += 1; + s = s.substr(j + 1); + return it('(string)', r, x); + } + if (c < ' ') { + if (c === '\n' || c === '\r') { + break; + } + warningAt("Control character in string: {a}.", + line, character + j, s.slice(0, j)); + } else if (c === xquote) { + warningAt("Bad HTML string", line, character + j); + } else if (c === '<') { + if (option.safe && xmode === 'html') { + warningAt("ADsafe string violation.", + line, character + j); + } else if (s.charAt(j + 1) === '/' && (xmode || option.safe)) { + warningAt("Expected '<\\/' and instead saw ' 0) { + character += 1; + s = s.slice(i); + break; + } else { + if (!nextLine()) { + return it('(end)', ''); + } + } + } +// t = match(rx[xmode] || tx); +// if (!t) { +// if (xmode === 'html') { +// return it('(error)', s.charAt(0)); +// } else { +// t = ''; +// c = ''; +// while (s && s < '!') { +// s = s.substr(1); +// } +// if (s) { +// errorAt("Unexpected '{a}'.", +// line, character, s.substr(0, 1)); +// } +// } + t = match(rx[xmode] || tx); + if (!t) { + t = ''; + c = ''; + while (s && s < '!') { + s = s.substr(1); + } + if (s) { + if (xmode === 'html') { + return it('(error)', s.charAt(0)); + } else { + errorAt("Unexpected '{a}'.", + line, character, s.substr(0, 1)); + } + } + } else { + + // identifier + + if (c.isAlpha() || c === '_' || c === '$') { + return it('(identifier)', t); + } + + // number + + if (c.isDigit()) { + if (xmode !== 'style' && !isFinite(Number(t))) { + warningAt("Bad number '{a}'.", + line, character, t); + } + if (xmode !== 'style' && + xmode !== 'styleproperty' && + s.substr(0, 1).isAlpha()) { + warningAt("Missing space after '{a}'.", + line, character, t); + } + if (c === '0') { + d = t.substr(1, 1); + if (d.isDigit()) { + if (token.id !== '.' && xmode !== 'styleproperty') { + warningAt("Don't use extra leading zeros '{a}'.", + line, character, t); + } + } else if (jsonmode && (d === 'x' || d === 'X')) { + warningAt("Avoid 0x-. '{a}'.", + line, character, t); + } + } + if (t.substr(t.length - 1) === '.') { + warningAt( + "A trailing decimal point can be confused with a dot '{a}'.", + line, character, t); + } + return it('(number)', t); + } + switch (t) { + + // string + + case '"': + case "'": + return string(t); + + // // comment + + case '//': + if (src || (xmode && xmode !== 'script')) { + warningAt("Unexpected comment.", line, character); + } else if (xmode === 'script' && /<\s*\//i.test(s)) { + warningAt("Unexpected <\/ in comment.", line, character); + } else if ((option.safe || xmode === 'script') && ax.test(s)) { + warningAt("Dangerous comment.", line, character); + } + s = ''; + token.comment = true; + break; + + // /* comment + + case '/*': + if (src || (xmode && xmode !== 'script' && xmode !== 'style' && xmode !== 'styleproperty')) { + warningAt("Unexpected comment.", line, character); + } + if (option.safe && ax.test(s)) { + warningAt("ADsafe comment violation.", line, character); + } + for (;;) { + i = s.search(lx); + if (i >= 0) { + break; + } + if (!nextLine()) { + errorAt("Unclosed comment.", line, character); + } else { + if (option.safe && ax.test(s)) { + warningAt("ADsafe comment violation.", + line, character); + } + } + } + character += i + 2; + if (s.substr(i, 1) === '/') { + errorAt("Nested comment.", line, character); + } + s = s.substr(i + 2); + token.comment = true; + break; + + // /*members /*jslint /*global + + case '/*members': + case '/*member': + case '/*jslint': + case '/*global': + case '*/': + return { + value: t, + type: 'special', + line: line, + character: character, + from: from + }; + + case '': + break; + // / + case '/': + if (token.id === '/=') { + errorAt( +"A regular expression literal can be confused with '/='.", line, from); + } + if (prereg) { + depth = 0; + captures = 0; + l = 0; + for (;;) { + b = true; + c = s.charAt(l); + l += 1; + switch (c) { + case '': + errorAt("Unclosed regular expression.", + line, from); + return; + case '/': + if (depth > 0) { + warningAt("Unescaped '{a}'.", + line, from + l, '/'); + } + c = s.substr(0, l - 1); + q = { + g: true, + i: true, + m: true + }; + while (q[s.charAt(l)] === true) { + q[s.charAt(l)] = false; + l += 1; + } + character += l; + s = s.substr(l); + q = s.charAt(0); + if (q === '/' || q === '*') { + errorAt("Confusing regular expression.", + line, from); + } + return it('(regexp)', c); + case '\\': + c = s.charAt(l); + if (c < ' ') { + warningAt( +"Unexpected control character in regular expression.", line, from + l); + } else if (c === '<') { + warningAt( +"Unexpected escaped character '{a}' in regular expression.", line, from + l, c); + } + l += 1; + break; + case '(': + depth += 1; + b = false; + if (s.charAt(l) === '?') { + l += 1; + switch (s.charAt(l)) { + case ':': + case '=': + case '!': + l += 1; + break; + default: + warningAt( +"Expected '{a}' and instead saw '{b}'.", line, from + l, ':', s.charAt(l)); + } + } else { + captures += 1; + } + break; + case '|': + b = false; + break; + case ')': + if (depth === 0) { + warningAt("Unescaped '{a}'.", + line, from + l, ')'); + } else { + depth -= 1; + } + break; + case ' ': + q = 1; + while (s.charAt(l) === ' ') { + l += 1; + q += 1; + } + if (q > 1) { + warningAt( +"Spaces are hard to count. Use {{a}}.", line, from + l, q); + } + break; + case '[': + c = s.charAt(l); + if (c === '^') { + l += 1; + if (option.regexp) { + warningAt("Insecure '{a}'.", + line, from + l, c); + } + } + q = false; + if (c === ']') { + warningAt("Empty class.", line, + from + l - 1); + q = true; + } +klass: do { + c = s.charAt(l); + l += 1; + switch (c) { + case '[': + case '^': + warningAt("Unescaped '{a}'.", + line, from + l, c); + q = true; + break; + case '-': + if (q) { + q = false; + } else { + warningAt("Unescaped '{a}'.", + line, from + l, '-'); + q = true; + } + break; + case ']': + if (!q) { + warningAt("Unescaped '{a}'.", + line, from + l - 1, '-'); + } + break klass; + case '\\': + c = s.charAt(l); + if (c < ' ') { + warningAt( +"Unexpected control character in regular expression.", line, from + l); + } else if (c === '<') { + warningAt( +"Unexpected escaped character '{a}' in regular expression.", line, from + l, c); + } + l += 1; + q = true; + break; + case '/': + warningAt("Unescaped '{a}'.", + line, from + l - 1, '/'); + q = true; + break; + case '<': + if (xmode === 'script') { + c = s.charAt(l); + if (c === '!' || c === '/') { + warningAt( +"HTML confusion in regular expression '<{a}'.", line, from + l, c); + } + } + q = true; + break; + default: + q = true; + } + } while (c); + break; + case '.': + if (option.regexp) { + warningAt("Insecure '{a}'.", line, + from + l, c); + } + break; + case ']': + case '?': + case '{': + case '}': + case '+': + case '*': + warningAt("Unescaped '{a}'.", line, + from + l, c); + break; + case '<': + if (xmode === 'script') { + c = s.charAt(l); + if (c === '!' || c === '/') { + warningAt( +"HTML confusion in regular expression '<{a}'.", line, from + l, c); + } + } + } + if (b) { + switch (s.charAt(l)) { + case '?': + case '+': + case '*': + l += 1; + if (s.charAt(l) === '?') { + l += 1; + } + break; + case '{': + l += 1; + c = s.charAt(l); + if (c < '0' || c > '9') { + warningAt( +"Expected a number and instead saw '{a}'.", line, from + l, c); + } + l += 1; + low = +c; + for (;;) { + c = s.charAt(l); + if (c < '0' || c > '9') { + break; + } + l += 1; + low = +c + (low * 10); + } + high = low; + if (c === ',') { + l += 1; + high = Infinity; + c = s.charAt(l); + if (c >= '0' && c <= '9') { + l += 1; + high = +c; + for (;;) { + c = s.charAt(l); + if (c < '0' || c > '9') { + break; + } + l += 1; + high = +c + (high * 10); + } + } + } + if (s.charAt(l) !== '}') { + warningAt( +"Expected '{a}' and instead saw '{b}'.", line, from + l, '}', c); + } else { + l += 1; + } + if (s.charAt(l) === '?') { + l += 1; + } + if (low > high) { + warningAt( +"'{a}' should not be greater than '{b}'.", line, from + l, low, high); + } + } + } + } + c = s.substr(0, l - 1); + character += l; + s = s.substr(l); + return it('(regexp)', c); + } + return it('(punctuator)', t); + + // punctuator + + case '.", line, character); + } + character += 3; + s = s.slice(i + 3); + break; + case '#': + if (xmode === 'html' || xmode === 'styleproperty') { + for (;;) { + c = s.charAt(0); + if ((c < '0' || c > '9') && + (c < 'a' || c > 'f') && + (c < 'A' || c > 'F')) { + break; + } + character += 1; + s = s.substr(1); + t += c; + } + if (t.length !== 4 && t.length !== 7) { + warningAt("Bad hex color '{a}'.", line, + from + l, t); + } + return it('(color)', t); + } + return it('(punctuator)', t); + default: + if (xmode === 'outer' && c === '&') { + character += 1; + s = s.substr(1); + for (;;) { + c = s.charAt(0); + character += 1; + s = s.substr(1); + if (c === ';') { + break; + } + if (!((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + c === '#')) { + errorAt("Bad entity", line, from + l, + character); + } + } + break; + } + return it('(punctuator)', t); + } + } + } + } + }; + }()); + + + function addlabel(t, type) { + + if (option.safe && funct['(global)'] && + typeof predefined[t] !== 'boolean') { + warning('ADsafe global: ' + t + '.', token); + } else if (t === 'hasOwnProperty') { + warning("'hasOwnProperty' is a really bad name."); + } + +// Define t in the current function in the current scope. + + if (is_own(funct, t) && !funct['(global)']) { + warning(funct[t] === true ? + "'{a}' was used before it was defined." : + "'{a}' is already defined.", + nexttoken, t); + } + funct[t] = type; + if (funct['(global)']) { + global[t] = funct; + if (is_own(implied, t)) { + warning("'{a}' was used before it was defined.", nexttoken, t); + delete implied[t]; + } + } else { + scope[t] = funct; + } + } + + + function doOption() { + var b, obj, filter, o = nexttoken.value, t, v; + switch (o) { + case '*/': + error("Unbegun comment."); + break; + case '/*members': + case '/*member': + o = '/*members'; + if (!membersOnly) { + membersOnly = {}; + } + obj = membersOnly; + break; + case '/*jslint': + if (option.safe) { + warning("ADsafe restriction."); + } + obj = option; + filter = boolOptions; + break; + case '/*global': + if (option.safe) { + warning("ADsafe restriction."); + } + obj = predefined; + break; + default: + } + t = lex.token(); +loop: for (;;) { + for (;;) { + if (t.type === 'special' && t.value === '*/') { + break loop; + } + if (t.id !== '(endline)' && t.id !== ',') { + break; + } + t = lex.token(); + } + if (t.type !== '(string)' && t.type !== '(identifier)' && + o !== '/*members') { + error("Bad option.", t); + } + v = lex.token(); + if (v.id === ':') { + v = lex.token(); + if (obj === membersOnly) { + error("Expected '{a}' and instead saw '{b}'.", + t, '*/', ':'); + } + if (t.value === 'indent' && o === '/*jslint') { + b = +v.value; + if (typeof b !== 'number' || !isFinite(b) || b <= 0 || + Math.floor(b) !== b) { + error("Expected a small integer and instead saw '{a}'.", + v, v.value); + } + obj.white = true; + obj.indent = b; + } else if (t.value === 'maxerr' && o === '/*jslint') { + b = +v.value; + if (typeof b !== 'number' || !isFinite(b) || b <= 0 || + Math.floor(b) !== b) { + error("Expected a small integer and instead saw '{a}'.", + v, v.value); + } + obj.maxerr = b; + } else if (t.value === 'maxlen' && o === '/*jslint') { + b = +v.value; + if (typeof b !== 'number' || !isFinite(b) || b <= 0 || + Math.floor(b) !== b) { + error("Expected a small integer and instead saw '{a}'.", + v, v.value); + } + obj.maxlen = b; + } else if (v.value === 'true') { + obj[t.value] = true; + } else if (v.value === 'false') { + obj[t.value] = false; + } else { + error("Bad option value.", v); + } + t = lex.token(); + } else { + if (o === '/*jslint') { + error("Missing option value.", t); + } + obj[t.value] = false; + t = v; + } + } + if (filter) { + assume(); + } + } + + +// We need a peek function. If it has an argument, it peeks that much farther +// ahead. It is used to distinguish +// for ( var i in ... +// from +// for ( var i = ... + + function peek(p) { + var i = p || 0, j = 0, t; + + while (j <= i) { + t = lookahead[j]; + if (!t) { + t = lookahead[j] = lex.token(); + } + j += 1; + } + return t; + } + + + +// Produce the next token. It looks for programming errors. + + function advance(id, t) { + switch (token.id) { + case '(number)': + if (nexttoken.id === '.') { + warning( +"A dot following a number can be confused with a decimal point.", token); + } + break; + case '-': + if (nexttoken.id === '-' || nexttoken.id === '--') { + warning("Confusing minusses."); + } + break; + case '+': + if (nexttoken.id === '+' || nexttoken.id === '++') { + warning("Confusing plusses."); + } + break; + } + if (token.type === '(string)' || token.identifier) { + anonname = token.value; + } + + if (id && nexttoken.id !== id) { + if (t) { + if (nexttoken.id === '(end)') { + warning("Unmatched '{a}'.", t, t.id); + } else { + warning( +"Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", + nexttoken, id, t.id, t.line, nexttoken.value); + } + } else if (nexttoken.type !== '(identifier)' || + nexttoken.value !== id) { + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, id, nexttoken.value); + } + } + prevtoken = token; + token = nexttoken; + for (;;) { + nexttoken = lookahead.shift() || lex.token(); + if (nexttoken.id === '(end)' || nexttoken.id === '(error)') { + return; + } + if (nexttoken.type === 'special') { + doOption(); + } else { + if (nexttoken.id !== '(endline)') { + break; + } + } + } + } + + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add to Pratt's model .fud, which is +// like nud except that it is only used on the first token of a statement. +// Having .fud makes it much easier to define JavaScript. I retained Pratt's +// nomenclature. + +// .nud Null denotation +// .fud First null denotation +// .led Left denotation +// lbp Left binding power +// rbp Right binding power + +// They are key to the parsing method called Top Down Operator Precedence. + + function parse(rbp, initial) { + var left; + if (nexttoken.id === '(end)') { + error("Unexpected early end of program.", token); + } + advance(); + if (option.safe && typeof predefined[token.value] === 'boolean' && + (nexttoken.id !== '(' && nexttoken.id !== '.')) { + warning('ADsafe violation.', token); + } + if (initial) { + anonname = 'anonymous'; + funct['(verb)'] = token.value; + } + if (initial === true && token.fud) { + left = token.fud(); + } else { + if (token.nud) { + left = token.nud(); + } else { + if (nexttoken.type === '(number)' && token.id === '.') { + warning( +"A leading decimal point can be confused with a dot: '.{a}'.", + token, nexttoken.value); + advance(); + return token; + } else { + error("Expected an identifier and instead saw '{a}'.", + token, token.id); + } + } + while (rbp < nexttoken.lbp) { + advance(); + if (token.led) { + left = token.led(left); + } else { + error("Expected an operator and instead saw '{a}'.", + token, token.id); + } + } + } + return left; + } + + +// Functions for conformance of style. + + function adjacent(left, right) { + left = left || token; + right = right || nexttoken; + if (option.white || xmode === 'styleproperty' || xmode === 'style') { + if (left.character !== right.from && left.line === right.line) { + warning("Unexpected space after '{a}'.", right, left.value); + } + } + } + + function nospace(left, right) { + left = left || token; + right = right || nexttoken; + if (option.white && !left.comment) { + if (left.line === right.line) { + adjacent(left, right); + } + } + } + + + function nonadjacent(left, right) { + if (option.white) { + left = left || token; + right = right || nexttoken; + if (left.line === right.line && left.character === right.from) { + warning("Missing space after '{a}'.", + nexttoken, left.value); + } + } + } + + function nobreaknonadjacent(left, right) { + left = left || token; + right = right || nexttoken; + if (!option.laxbreak && left.line !== right.line) { + warning("Bad line breaking before '{a}'.", right, right.id); + } else if (option.white) { + left = left || token; + right = right || nexttoken; + if (left.character === right.from) { + warning("Missing space after '{a}'.", + nexttoken, left.value); + } + } + } + + function indentation(bias) { + var i; + if (option.white && nexttoken.id !== '(end)') { + i = indent + (bias || 0); + if (nexttoken.from !== i) { + warning( +"Expected '{a}' to have an indentation at {b} instead at {c}.", + nexttoken, nexttoken.value, i, nexttoken.from); + } + } + } + + function nolinebreak(t) { + t = t || token; + if (t.line !== nexttoken.line) { + warning("Line breaking error '{a}'.", t, t.value); + } + } + + + function comma() { + if (token.line !== nexttoken.line) { + if (!option.laxbreak) { + warning("Bad line breaking before '{a}'.", token, nexttoken.id); + } + } else if (token.character !== nexttoken.from && option.white) { + warning("Unexpected space after '{a}'.", nexttoken, token.value); + } + advance(','); + nonadjacent(token, nexttoken); + } + + +// Functional constructors for making the symbols that will be inherited by +// tokens. + + function symbol(s, p) { + var x = syntax[s]; + if (!x || typeof x !== 'object') { + syntax[s] = x = { + id: s, + lbp: p, + value: s + }; + } + return x; + } + + + function delim(s) { + return symbol(s, 0); + } + + + function stmt(s, f) { + var x = delim(s); + x.identifier = x.reserved = true; + x.fud = f; + return x; + } + + + function blockstmt(s, f) { + var x = stmt(s, f); + x.block = true; + return x; + } + + + function reserveName(x) { + var c = x.id.charAt(0); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + x.identifier = x.reserved = true; + } + return x; + } + + + function prefix(s, f) { + var x = symbol(s, 150); + reserveName(x); + x.nud = (typeof f === 'function') ? f : function () { + this.right = parse(150); + this.arity = 'unary'; + if (this.id === '++' || this.id === '--') { + if (option.plusplus) { + warning("Unexpected use of '{a}'.", this, this.id); + } else if ((!this.right.identifier || this.right.reserved) && + this.right.id !== '.' && this.right.id !== '[') { + warning("Bad operand.", this); + } + } + return this; + }; + return x; + } + + + function type(s, f) { + var x = delim(s); + x.type = s; + x.nud = f; + return x; + } + + + function reserve(s, f) { + var x = type(s, f); + x.identifier = x.reserved = true; + return x; + } + + + function reservevar(s, v) { + return reserve(s, function () { + if (this.id === 'this' || this.id === 'arguments' || + this.id === 'eval') { + if (strict_mode && funct['(global)']) { + warning("Strict violation.", this); + } else if (option.safe) { + warning("ADsafe violation.", this); + } + } + return this; + }); + } + + + function infix(s, f, p, w) { + var x = symbol(s, p); + reserveName(x); + x.led = function (left) { + if (!w) { + nobreaknonadjacent(prevtoken, token); + nonadjacent(token, nexttoken); + } + if (typeof f === 'function') { + return f(left, this); + } else { + this.left = left; + this.right = parse(p); + return this; + } + }; + return x; + } + + + function relation(s, f) { + var x = symbol(s, 100); + x.led = function (left) { + nobreaknonadjacent(prevtoken, token); + nonadjacent(token, nexttoken); + var right = parse(100); + if ((left && left.id === 'NaN') || (right && right.id === 'NaN')) { + warning("Use the isNaN function to compare with NaN.", this); + } else if (f) { + f.apply(this, [left, right]); + } + if (left.id === '!') { + warning("Confusing use of '{a}'.", left, '!'); + } + if (right.id === '!') { + warning("Confusing use of '{a}'.", left, '!'); + } + this.left = left; + this.right = right; + return this; + }; + return x; + } + + + function isPoorRelation(node) { + return node && + ((node.type === '(number)' && +node.value === 0) || + (node.type === '(string)' && node.value === ' ') || + node.type === 'true' || + node.type === 'false' || + node.type === 'undefined' || + node.type === 'null'); + } + + + function assignop(s, f) { + symbol(s, 20).exps = true; + return infix(s, function (left, that) { + var l; + that.left = left; + if (predefined[left.value] === false && + scope[left.value]['(global)'] === true) { + warning('Read only.', left); + } + if (option.safe) { + l = left; + do { + if (typeof predefined[l.value] === 'boolean') { + warning('ADsafe violation.', l); + } + l = l.left; + } while (l); + } + if (left) { + if (left.id === '.' || left.id === '[') { + if (!left.left || left.left.value === 'arguments') { + warning('Bad assignment.', that); + } + that.right = parse(19); + return that; + } else if (left.identifier && !left.reserved) { + if (funct[left.value] === 'exception') { + warning("Do not assign to the exception parameter.", left); + } + that.right = parse(19); + return that; + } + if (left === syntax['function']) { + warning( +"Expected an identifier in an assignment and instead saw a function invocation.", + token); + } + } + error("Bad assignment.", that); + }, 20); + } + + function bitwise(s, f, p) { + var x = symbol(s, p); + reserveName(x); + x.led = (typeof f === 'function') ? f : function (left) { + if (option.bitwise) { + warning("Unexpected use of '{a}'.", this, this.id); + } + this.left = left; + this.right = parse(p); + return this; + }; + return x; + } + + function bitwiseassignop(s) { + symbol(s, 20).exps = true; + return infix(s, function (left, that) { + if (option.bitwise) { + warning("Unexpected use of '{a}'.", that, that.id); + } + nonadjacent(prevtoken, token); + nonadjacent(token, nexttoken); + if (left) { + if (left.id === '.' || left.id === '[' || + (left.identifier && !left.reserved)) { + parse(19); + return that; + } + if (left === syntax['function']) { + warning( +"Expected an identifier in an assignment, and instead saw a function invocation.", + token); + } + return that; + } + error("Bad assignment.", that); + }, 20); + } + + + function suffix(s, f) { + var x = symbol(s, 150); + x.led = function (left) { + if (option.plusplus) { + warning("Unexpected use of '{a}'.", this, this.id); + } else if ((!left.identifier || left.reserved) && + left.id !== '.' && left.id !== '[') { + warning("Bad operand.", this); + } + this.left = left; + return this; + }; + return x; + } + + + function optionalidentifier() { + if (nexttoken.identifier) { + advance(); + if (option.safe && banned[token.value]) { + warning("ADsafe violation: '{a}'.", token, token.value); + } else if (token.reserved && !option.es5) { + warning("Expected an identifier and instead saw '{a}' (a reserved word).", + token, token.id); + } + return token.value; + } + } + + + function identifier() { + var i = optionalidentifier(); + if (i) { + return i; + } + if (token.id === 'function' && nexttoken.id === '(') { + warning("Missing name in function statement."); + } else { + error("Expected an identifier and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + } + + function reachable(s) { + var i = 0, t; + if (nexttoken.id !== ';' || noreach) { + return; + } + for (;;) { + t = peek(i); + if (t.reach) { + return; + } + if (t.id !== '(endline)') { + if (t.id === 'function') { + warning( +"Inner functions should be listed at the top of the outer function.", t); + break; + } + warning("Unreachable '{a}' after '{b}'.", t, t.value, s); + break; + } + i += 1; + } + } + + + function statement(noindent) { + var i = indent, r, s = scope, t = nexttoken; + +// We don't like the empty statement. + + if (t.id === ';') { + warning("Unnecessary semicolon.", t); + advance(';'); + return; + } + +// Is this a labelled statement? + + if (t.identifier && !t.reserved && peek().id === ':') { + advance(); + advance(':'); + scope = Object.create(s); + addlabel(t.value, 'label'); + if (!nexttoken.labelled) { + warning("Label '{a}' on {b} statement.", + nexttoken, t.value, nexttoken.value); + } + if (jx.test(t.value + ':')) { + warning("Label '{a}' looks like a javascript url.", + t, t.value); + } + nexttoken.label = t.value; + t = nexttoken; + } + +// Parse the statement. + + if (!noindent) { + indentation(); + } + r = parse(0, true); + +// Look for the final semicolon. + + if (!t.block) { + if (!r || !r.exps) { + warning( +"Expected an assignment or function call and instead saw an expression.", + token); + } else if (r.id === '(' && r.left.id === 'new') { + warning("Do not use 'new' for side effects."); + } + if (nexttoken.id !== ';') { + warningAt("Missing semicolon.", token.line, + token.from + token.value.length); + } else { + adjacent(token, nexttoken); + advance(';'); + nonadjacent(token, nexttoken); + } + } + +// Restore the indentation. + + indent = i; + scope = s; + return r; + } + + + function use_strict() { + if (nexttoken.value === 'use strict') { + advance(); + advance(';'); + strict_mode = true; + return true; + } else { + return false; + } + } + + + function statements(begin) { + var a = [], f, p; + if (begin && !use_strict() && option.strict) { + warning('Missing "use strict" statement.', nexttoken); + } + if (option.adsafe) { + switch (begin) { + case 'script': + if (!adsafe_may) { + if (nexttoken.value !== 'ADSAFE' || + peek(0).id !== '.' || + (peek(1).value !== 'id' && + peek(1).value !== 'go')) { + error('ADsafe violation: Missing ADSAFE.id or ADSAFE.go.', + nexttoken); + } + } + if (nexttoken.value === 'ADSAFE' && + peek(0).id === '.' && + peek(1).value === 'id') { + if (adsafe_may) { + error('ADsafe violation.', nexttoken); + } + advance('ADSAFE'); + advance('.'); + advance('id'); + advance('('); + if (nexttoken.value !== adsafe_id) { + error('ADsafe violation: id does not match.', nexttoken); + } + advance('(string)'); + advance(')'); + advance(';'); + adsafe_may = true; + } + break; + case 'lib': + if (nexttoken.value === 'ADSAFE') { + advance('ADSAFE'); + advance('.'); + advance('lib'); + advance('('); + advance('(string)'); + comma(); + f = parse(0); + if (f.id !== 'function') { + error('The second argument to lib must be a function.', f); + } + p = f.funct['(params)']; + p = p && p.join(', '); + if (p && p !== 'lib') { + error("Expected '{a}' and instead saw '{b}'.", + f, '(lib)', '(' + p + ')'); + } + advance(')'); + advance(';'); + return a; + } else { + error("ADsafe lib violation."); + } + } + } + while (!nexttoken.reach && nexttoken.id !== '(end)') { + if (nexttoken.id === ';') { + warning("Unnecessary semicolon."); + advance(';'); + } else { + a.push(statement()); + } + } + return a; + } + + + function block(f) { + var a, b = inblock, old_indent = indent, s = scope, t; + inblock = f; + scope = Object.create(scope); + nonadjacent(token, nexttoken); + t = nexttoken; + if (nexttoken.id === '{') { + advance('{'); + if (nexttoken.id !== '}' || token.line !== nexttoken.line) { + indent += option.indent; + while (!f && nexttoken.from > indent) { + indent += option.indent; + } + if (!f) { + use_strict(); + } + a = statements(); + indent -= option.indent; + indentation(); + } + advance('}', t); + indent = old_indent; + } else { + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, '{', nexttoken.value); + noreach = true; + a = [statement()]; + noreach = false; + } + funct['(verb)'] = null; + scope = s; + inblock = b; + return a; + } + + +// An identity function, used by string and number tokens. + + function idValue() { + return this; + } + + + function countMember(m) { + if (membersOnly && typeof membersOnly[m] !== 'boolean') { + warning("Unexpected /*member '{a}'.", token, m); + } + if (typeof member[m] === 'number') { + member[m] += 1; + } else { + member[m] = 1; + } + } + + + function note_implied(token) { + var name = token.value, line = token.line, a = implied[name]; + if (typeof a === 'function') { + a = false; + } + if (!a) { + a = [line]; + implied[name] = a; + } else if (a[a.length - 1] !== line) { + a.push(line); + } + } + +// CSS parsing. + + + function cssName() { + if (nexttoken.identifier) { + advance(); + return true; + } + } + + function cssNumber() { + if (nexttoken.id === '-') { + advance('-'); + adjacent(); + nolinebreak(); + } + if (nexttoken.type === '(number)') { + advance('(number)'); + return true; + } + } + + function cssString() { + if (nexttoken.type === '(string)') { + advance(); + return true; + } + } + + function cssColor() { + var i, number, value; + if (nexttoken.identifier) { + value = nexttoken.value; + if (value === 'rgb' || value === 'rgba') { + advance(); + advance('('); + for (i = 0; i < 3; i += 1) { + if (i) { + advance(','); + } + number = nexttoken.value; + if (nexttoken.type !== '(number)' || number < 0) { + warning("Expected a positive number and instead saw '{a}'", + nexttoken, number); + advance(); + } else { + advance(); + if (nexttoken.id === '%') { + advance('%'); + if (number > 100) { + warning("Expected a percentage and instead saw '{a}'", + token, number); + } + } else { + if (number > 255) { + warning("Expected a small number and instead saw '{a}'", + token, number); + } + } + } + } + if (value === 'rgba') { + advance(','); + number = +nexttoken.value; + if (nexttoken.type !== '(number)' || number < 0 || number > 1) { + warning("Expected a number between 0 and 1 and instead saw '{a}'", + nexttoken, number); + } + advance(); + if (nexttoken.id === '%') { + warning("Unexpected '%'."); + advance('%'); + } + } + advance(')'); + return true; + } else if (cssColorData[nexttoken.value] === true) { + advance(); + return true; + } + } else if (nexttoken.type === '(color)') { + advance(); + return true; + } + return false; + } + + function cssLength() { + if (nexttoken.id === '-') { + advance('-'); + adjacent(); + nolinebreak(); + } + if (nexttoken.type === '(number)') { + advance(); + if (nexttoken.type !== '(string)' && + cssLengthData[nexttoken.value] === true) { + adjacent(); + advance(); + } else if (+token.value !== 0) { + warning("Expected a linear unit and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + return true; + } + return false; + } + + function cssLineHeight() { + if (nexttoken.id === '-') { + advance('-'); + adjacent(); + } + if (nexttoken.type === '(number)') { + advance(); + if (nexttoken.type !== '(string)' && + cssLengthData[nexttoken.value] === true) { + adjacent(); + advance(); + } + return true; + } + return false; + } + + function cssWidth() { + if (nexttoken.identifier) { + switch (nexttoken.value) { + case 'thin': + case 'medium': + case 'thick': + advance(); + return true; + } + } else { + return cssLength(); + } + } + + function cssMargin() { + if (nexttoken.identifier) { + if (nexttoken.value === 'auto') { + advance(); + return true; + } + } else { + return cssLength(); + } + } + + function cssAttr() { + if (nexttoken.identifier && nexttoken.value === 'attr') { + advance(); + advance('('); + if (!nexttoken.identifier) { + warning("Expected a name and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + advance(')'); + return true; + } + return false; + } + + function cssCommaList() { + while (nexttoken.id !== ';') { + if (!cssName() && !cssString()) { + warning("Expected a name and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + if (nexttoken.id !== ',') { + return true; + } + comma(); + } + } + + function cssCounter() { + if (nexttoken.identifier && nexttoken.value === 'counter') { + advance(); + advance('('); + if (!nexttoken.identifier) { + } + advance(); + if (nexttoken.id === ',') { + comma(); + if (nexttoken.type !== '(string)') { + warning("Expected a string and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + } + advance(')'); + return true; + } + if (nexttoken.identifier && nexttoken.value === 'counters') { + advance(); + advance('('); + if (!nexttoken.identifier) { + warning("Expected a name and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + if (nexttoken.id === ',') { + comma(); + if (nexttoken.type !== '(string)') { + warning("Expected a string and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + } + if (nexttoken.id === ',') { + comma(); + if (nexttoken.type !== '(string)') { + warning("Expected a string and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + } + advance(')'); + return true; + } + return false; + } + + + function cssShape() { + var i; + if (nexttoken.identifier && nexttoken.value === 'rect') { + advance(); + advance('('); + for (i = 0; i < 4; i += 1) { + if (!cssLength()) { + warning("Expected a number and instead saw '{a}'.", + nexttoken, nexttoken.value); + break; + } + } + advance(')'); + return true; + } + return false; + } + + function cssUrl() { + var c, url; + if (nexttoken.identifier && nexttoken.value === 'url') { + nexttoken = lex.range('(', ')'); + url = nexttoken.value; + c = url.charAt(0); + if (c === '"' || c === '\'') { + if (url.slice(-1) !== c) { + warning("Bad url string."); + } else { + url = url.slice(1, -1); + if (url.indexOf(c) >= 0) { + warning("Bad url string."); + } + } + } + if (!url) { + warning("Missing url."); + } + advance(); + if (option.safe && ux.test(url)) { + error("ADsafe URL violation."); + } + urls.push(url); + return true; + } + return false; + } + + cssAny = [cssUrl, function () { + for (;;) { + if (nexttoken.identifier) { + switch (nexttoken.value.toLowerCase()) { + case 'url': + cssUrl(); + break; + case 'expression': + warning("Unexpected expression '{a}'.", + nexttoken, nexttoken.value); + advance(); + break; + default: + advance(); + } + } else { + if (nexttoken.id === ';' || nexttoken.id === '!' || + nexttoken.id === '(end)' || nexttoken.id === '}') { + return true; + } + advance(); + } + } + }]; + + cssBorderStyle = [ + 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'ridge', + 'inset', 'outset' + ]; + + cssBreak = [ + 'auto', 'always', 'avoid', 'left', 'right' + ]; + + cssOverflow = [ + 'auto', 'hidden', 'scroll', 'visible' + ]; + + cssAttributeData = { + background: [ + true, 'background-attachment', 'background-color', + 'background-image', 'background-position', 'background-repeat' + ], + 'background-attachment': ['scroll', 'fixed'], + 'background-color': ['transparent', cssColor], + 'background-image': ['none', cssUrl], + 'background-position': [ + 2, [cssLength, 'top', 'bottom', 'left', 'right', 'center'] + ], + 'background-repeat': [ + 'repeat', 'repeat-x', 'repeat-y', 'no-repeat' + ], + 'border': [true, 'border-color', 'border-style', 'border-width'], + 'border-bottom': [ + true, 'border-bottom-color', 'border-bottom-style', + 'border-bottom-width' + ], + 'border-bottom-color': cssColor, + 'border-bottom-style': cssBorderStyle, + 'border-bottom-width': cssWidth, + 'border-collapse': ['collapse', 'separate'], + 'border-color': ['transparent', 4, cssColor], + 'border-left': [ + true, 'border-left-color', 'border-left-style', 'border-left-width' + ], + 'border-left-color': cssColor, + 'border-left-style': cssBorderStyle, + 'border-left-width': cssWidth, + 'border-right': [ + true, 'border-right-color', 'border-right-style', + 'border-right-width' + ], + 'border-right-color': cssColor, + 'border-right-style': cssBorderStyle, + 'border-right-width': cssWidth, + 'border-spacing': [2, cssLength], + 'border-style': [4, cssBorderStyle], + 'border-top': [ + true, 'border-top-color', 'border-top-style', 'border-top-width' + ], + 'border-top-color': cssColor, + 'border-top-style': cssBorderStyle, + 'border-top-width': cssWidth, + 'border-width': [4, cssWidth], + bottom: [cssLength, 'auto'], + 'caption-side' : ['bottom', 'left', 'right', 'top'], + clear: ['both', 'left', 'none', 'right'], + clip: [cssShape, 'auto'], + color: cssColor, + content: [ + 'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote', + cssString, cssUrl, cssCounter, cssAttr + ], + 'counter-increment': [ + cssName, 'none' + ], + 'counter-reset': [ + cssName, 'none' + ], + cursor: [ + cssUrl, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move', + 'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize', + 'se-resize', 'sw-resize', 'w-resize', 'text', 'wait' + ], + direction: ['ltr', 'rtl'], + display: [ + 'block', 'compact', 'inline', 'inline-block', 'inline-table', + 'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption', + 'table-cell', 'table-column', 'table-column-group', + 'table-footer-group', 'table-header-group', 'table-row', + 'table-row-group' + ], + 'empty-cells': ['show', 'hide'], + 'float': ['left', 'none', 'right'], + font: [ + 'caption', 'icon', 'menu', 'message-box', 'small-caption', + 'status-bar', true, 'font-size', 'font-style', 'font-weight', + 'font-family' + ], + 'font-family': cssCommaList, + 'font-size': [ + 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', + 'xx-large', 'larger', 'smaller', cssLength + ], + 'font-size-adjust': ['none', cssNumber], + 'font-stretch': [ + 'normal', 'wider', 'narrower', 'ultra-condensed', + 'extra-condensed', 'condensed', 'semi-condensed', + 'semi-expanded', 'expanded', 'extra-expanded' + ], + 'font-style': [ + 'normal', 'italic', 'oblique' + ], + 'font-variant': [ + 'normal', 'small-caps' + ], + 'font-weight': [ + 'normal', 'bold', 'bolder', 'lighter', cssNumber + ], + height: [cssLength, 'auto'], + left: [cssLength, 'auto'], + 'letter-spacing': ['normal', cssLength], + 'line-height': ['normal', cssLineHeight], + 'list-style': [ + true, 'list-style-image', 'list-style-position', 'list-style-type' + ], + 'list-style-image': ['none', cssUrl], + 'list-style-position': ['inside', 'outside'], + 'list-style-type': [ + 'circle', 'disc', 'square', 'decimal', 'decimal-leading-zero', + 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha', + 'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana', + 'hiragana-iroha', 'katakana-oroha', 'none' + ], + margin: [4, cssMargin], + 'margin-bottom': cssMargin, + 'margin-left': cssMargin, + 'margin-right': cssMargin, + 'margin-top': cssMargin, + 'marker-offset': [cssLength, 'auto'], + 'max-height': [cssLength, 'none'], + 'max-width': [cssLength, 'none'], + 'min-height': cssLength, + 'min-width': cssLength, + opacity: cssNumber, + outline: [true, 'outline-color', 'outline-style', 'outline-width'], + 'outline-color': ['invert', cssColor], + 'outline-style': [ + 'dashed', 'dotted', 'double', 'groove', 'inset', 'none', + 'outset', 'ridge', 'solid' + ], + 'outline-width': cssWidth, + overflow: cssOverflow, + 'overflow-x': cssOverflow, + 'overflow-y': cssOverflow, + padding: [4, cssLength], + 'padding-bottom': cssLength, + 'padding-left': cssLength, + 'padding-right': cssLength, + 'padding-top': cssLength, + 'page-break-after': cssBreak, + 'page-break-before': cssBreak, + position: ['absolute', 'fixed', 'relative', 'static'], + quotes: [8, cssString], + right: [cssLength, 'auto'], + 'table-layout': ['auto', 'fixed'], + 'text-align': ['center', 'justify', 'left', 'right'], + 'text-decoration': [ + 'none', 'underline', 'overline', 'line-through', 'blink' + ], + 'text-indent': cssLength, + 'text-shadow': ['none', 4, [cssColor, cssLength]], + 'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'], + top: [cssLength, 'auto'], + 'unicode-bidi': ['normal', 'embed', 'bidi-override'], + 'vertical-align': [ + 'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle', + 'text-bottom', cssLength + ], + visibility: ['visible', 'hidden', 'collapse'], + 'white-space': [ + 'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit' + ], + width: [cssLength, 'auto'], + 'word-spacing': ['normal', cssLength], + 'word-wrap': ['break-word', 'normal'], + 'z-index': ['auto', cssNumber] + }; + + function styleAttribute() { + var v; + while (nexttoken.id === '*' || nexttoken.id === '#' || + nexttoken.value === '_') { + if (!option.css) { + warning("Unexpected '{a}'.", nexttoken, nexttoken.value); + } + advance(); + } + if (nexttoken.id === '-') { + if (!option.css) { + warning("Unexpected '{a}'.", nexttoken, nexttoken.value); + } + advance('-'); + if (!nexttoken.identifier) { + warning( +"Expected a non-standard style attribute and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + return cssAny; + } else { + if (!nexttoken.identifier) { + warning("Excepted a style attribute, and instead saw '{a}'.", + nexttoken, nexttoken.value); + } else { + if (is_own(cssAttributeData, nexttoken.value)) { + v = cssAttributeData[nexttoken.value]; + } else { + v = cssAny; + if (!option.css) { + warning("Unrecognized style attribute '{a}'.", + nexttoken, nexttoken.value); + } + } + } + advance(); + return v; + } + } + + function styleValue(v) { + var i = 0, + n, + once, + match, + round, + start = 0, + vi; + switch (typeof v) { + case 'function': + return v(); + case 'string': + if (nexttoken.identifier && nexttoken.value === v) { + advance(); + return true; + } + return false; + } + for (;;) { + if (i >= v.length) { + return false; + } + vi = v[i]; + i += 1; + if (vi === true) { + break; + } else if (typeof vi === 'number') { + n = vi; + vi = v[i]; + i += 1; + } else { + n = 1; + } + match = false; + while (n > 0) { + if (styleValue(vi)) { + match = true; + n -= 1; + } else { + break; + } + } + if (match) { + return true; + } + } + start = i; + once = []; + for (;;) { + round = false; + for (i = start; i < v.length; i += 1) { + if (!once[i]) { + if (styleValue(cssAttributeData[v[i]])) { + match = true; + round = true; + once[i] = true; + break; + } + } + } + if (!round) { + return match; + } + } + } + + function styleChild() { + if (nexttoken.id === '(number)') { + advance(); + if (nexttoken.value === 'n' && nexttoken.identifier) { + adjacent(); + advance(); + if (nexttoken.id === '+') { + adjacent(); + advance('+'); + adjacent(); + advance('(number)'); + } + } + return; + } else { + switch (nexttoken.value) { + case 'odd': + case 'even': + if (nexttoken.identifier) { + advance(); + return; + } + } + } + warning("Unexpected token '{a}'.", nexttoken, nexttoken.value); + } + + function substyle() { + var v; + for (;;) { + if (nexttoken.id === '}' || nexttoken.id === '(end)' || + xquote && nexttoken.id === xquote) { + return; + } + while (nexttoken.id === ';') { + warning("Misplaced ';'."); + advance(';'); + } + v = styleAttribute(); + advance(':'); + if (nexttoken.identifier && nexttoken.value === 'inherit') { + advance(); + } else { + if (!styleValue(v)) { + warning("Unexpected token '{a}'.", nexttoken, + nexttoken.value); + advance(); + } + } + if (nexttoken.id === '!') { + advance('!'); + adjacent(); + if (nexttoken.identifier && nexttoken.value === 'important') { + advance(); + } else { + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, 'important', nexttoken.value); + } + } + if (nexttoken.id === '}' || nexttoken.id === xquote) { + warning("Missing '{a}'.", nexttoken, ';'); + } else { + advance(';'); + } + } + } + + function styleSelector() { + if (nexttoken.identifier) { + if (!is_own(htmltag, nexttoken.value)) { + warning("Expected a tagName, and instead saw {a}.", + nexttoken, nexttoken.value); + } + advance(); + } else { + switch (nexttoken.id) { + case '>': + case '+': + advance(); + styleSelector(); + break; + case ':': + advance(':'); + switch (nexttoken.value) { + case 'active': + case 'after': + case 'before': + case 'checked': + case 'disabled': + case 'empty': + case 'enabled': + case 'first-child': + case 'first-letter': + case 'first-line': + case 'first-of-type': + case 'focus': + case 'hover': + case 'last-of-type': + case 'link': + case 'only-of-type': + case 'root': + case 'target': + case 'visited': + advance(); + break; + case 'lang': + advance(); + advance('('); + if (!nexttoken.identifier) { + warning("Expected a lang code, and instead saw :{a}.", + nexttoken, nexttoken.value); + } + advance(')'); + break; + case 'nth-child': + case 'nth-last-child': + case 'nth-last-of-type': + case 'nth-of-type': + advance(); + advance('('); + styleChild(); + advance(')'); + break; + case 'not': + advance(); + advance('('); + if (nexttoken.id === ':' && peek(0).value === 'not') { + warning("Nested not."); + } + styleSelector(); + advance(')'); + break; + default: + warning("Expected a pseudo, and instead saw :{a}.", + nexttoken, nexttoken.value); + } + break; + case '#': + advance('#'); + if (!nexttoken.identifier) { + warning("Expected an id, and instead saw #{a}.", + nexttoken, nexttoken.value); + } + advance(); + break; + case '*': + advance('*'); + break; + case '.': + advance('.'); + if (!nexttoken.identifier) { + warning("Expected a class, and instead saw #.{a}.", + nexttoken, nexttoken.value); + } + advance(); + break; + case '[': + advance('['); + if (!nexttoken.identifier) { + warning("Expected an attribute, and instead saw [{a}].", + nexttoken, nexttoken.value); + } + advance(); + if (nexttoken.id === '=' || nexttoken.value === '~=' || + nexttoken.value === '$=' || + nexttoken.value === '|=' || + nexttoken.id === '*=' || + nexttoken.id === '^=') { + advance(); + if (nexttoken.type !== '(string)') { + warning("Expected a string, and instead saw {a}.", + nexttoken, nexttoken.value); + } + advance(); + } + advance(']'); + break; + default: + error("Expected a CSS selector, and instead saw {a}.", + nexttoken, nexttoken.value); + } + } + } + + function stylePattern() { + var name; + if (nexttoken.id === '{') { + warning("Expected a style pattern, and instead saw '{a}'.", nexttoken, + nexttoken.id); + } else if (nexttoken.id === '@') { + advance('@'); + name = nexttoken.value; + if (nexttoken.identifier && atrule[name] === true) { + advance(); + return name; + } + warning("Expected an at-rule, and instead saw @{a}.", nexttoken, name); + } + for (;;) { + styleSelector(); + if (nexttoken.id === ' fragments and .js files.", token); + } + if (option.fragment) { + if (n !== 'div') { + error("ADsafe violation: Wrap the widget in a div.", token); + } + } else { + error("Use the fragment option.", token); + } + } + option.browser = true; + assume(); + } + + function doAttribute(n, a, v) { + var u, x; + if (a === 'id') { + u = typeof v === 'string' ? v.toUpperCase() : ''; + if (ids[u] === true) { + warning("Duplicate id='{a}'.", nexttoken, v); + } + if (!/^[A-Za-z][A-Za-z0-9._:\-]*$/.test(v)) { + warning("Bad id: '{a}'.", nexttoken, v); + } else if (option.adsafe) { + if (adsafe_id) { + if (v.slice(0, adsafe_id.length) !== adsafe_id) { + warning("ADsafe violation: An id must have a '{a}' prefix", + nexttoken, adsafe_id); + } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { + warning("ADSAFE violation: bad id."); + } + } else { + adsafe_id = v; + if (!/^[A-Z]+_$/.test(v)) { + warning("ADSAFE violation: bad id."); + } + } + } + x = v.search(dx); + if (x >= 0) { + warning("Unexpected character '{a}' in {b}.", token, v.charAt(x), a); + } + ids[u] = true; + } else if (a === 'class' || a === 'type' || a === 'name') { + x = v.search(qx); + if (x >= 0) { + warning("Unexpected character '{a}' in {b}.", token, v.charAt(x), a); + } + ids[u] = true; + } else if (a === 'href' || a === 'background' || + a === 'content' || a === 'data' || + a.indexOf('src') >= 0 || a.indexOf('url') >= 0) { + if (option.safe && ux.test(v)) { + error("ADsafe URL violation."); + } + urls.push(v); + } else if (a === 'for') { + if (option.adsafe) { + if (adsafe_id) { + if (v.slice(0, adsafe_id.length) !== adsafe_id) { + warning("ADsafe violation: An id must have a '{a}' prefix", + nexttoken, adsafe_id); + } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { + warning("ADSAFE violation: bad id."); + } + } else { + warning("ADSAFE violation: bad id."); + } + } + } else if (a === 'name') { + if (option.adsafe && v.indexOf('_') >= 0) { + warning("ADsafe name violation."); + } + } + } + + function doTag(n, a) { + var i, t = htmltag[n], x; + src = false; + if (!t) { + error("Unrecognized tag '<{a}>'.", + nexttoken, + n === n.toLowerCase() ? n : + n + ' (capitalization error)'); + } + if (stack.length > 0) { + if (n === 'html') { + error("Too many tags.", token); + } + x = t.parent; + if (x) { + if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) { + error("A '<{a}>' must be within '<{b}>'.", + token, n, x); + } + } else if (!option.adsafe && !option.fragment) { + i = stack.length; + do { + if (i <= 0) { + error("A '<{a}>' must be within '<{b}>'.", + token, n, 'body'); + } + i -= 1; + } while (stack[i].name !== 'body'); + } + } + switch (n) { + case 'div': + if (option.adsafe && stack.length === 1 && !adsafe_id) { + warning("ADSAFE violation: missing ID_."); + } + break; + case 'script': + xmode = 'script'; + advance('>'); + indent = nexttoken.from; + if (a.lang) { + warning("lang is deprecated.", token); + } + if (option.adsafe && stack.length !== 1) { + warning("ADsafe script placement violation.", token); + } + if (a.src) { + if (option.adsafe && (!adsafe_may || !approved[a.src])) { + warning("ADsafe unapproved script source.", token); + } + if (a.type) { + warning("type is unnecessary.", token); + } + } else { + if (adsafe_went) { + error("ADsafe script violation.", token); + } + statements('script'); + } + xmode = 'html'; + advance(''); + styles(); + xmode = 'html'; + advance(''; + } + + function html() { + var a, attributes, e, n, q, t, v, w = option.white, wmode; + xmode = 'html'; + xquote = ''; + stack = null; + for (;;) { + switch (nexttoken.value) { + case '<': + xmode = 'html'; + advance('<'); + attributes = {}; + t = nexttoken; + if (!t.identifier) { + warning("Bad identifier {a}.", t, t.value); + } + n = t.value; + if (option.cap) { + n = n.toLowerCase(); + } + t.name = n; + advance(); + if (!stack) { + stack = []; + doBegin(n); + } + v = htmltag[n]; + if (typeof v !== 'object') { + error("Unrecognized tag '<{a}>'.", t, n); + } + e = v.empty; + t.type = n; + for (;;) { + if (nexttoken.id === '/') { + advance('/'); + if (nexttoken.id !== '>') { + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, '>', nexttoken.value); + } + break; + } + if (nexttoken.id && nexttoken.id.substr(0, 1) === '>') { + break; + } + if (!nexttoken.identifier) { + if (nexttoken.id === '(end)' || nexttoken.id === '(error)') { + error("Missing '>'.", nexttoken); + } + warning("Bad identifier."); + } + option.white = true; + nonadjacent(token, nexttoken); + a = nexttoken.value; + option.white = w; + advance(); + if (!option.cap && a !== a.toLowerCase()) { + warning("Attribute '{a}' not all lower case.", nexttoken, a); + } + a = a.toLowerCase(); + xquote = ''; + if (is_own(attributes, a)) { + warning("Attribute '{a}' repeated.", nexttoken, a); + } + if (a.slice(0, 2) === 'on') { + if (!option.on) { + warning("Avoid HTML event handlers."); + } + xmode = 'scriptstring'; + advance('='); + q = nexttoken.id; + if (q !== '"' && q !== "'") { + error("Missing quote."); + } + xquote = q; + wmode = option.white; + option.white = false; + advance(q); + statements('on'); + option.white = wmode; + if (nexttoken.id !== q) { + error("Missing close quote on script attribute."); + } + xmode = 'html'; + xquote = ''; + advance(q); + v = false; + } else if (a === 'style') { + xmode = 'scriptstring'; + advance('='); + q = nexttoken.id; + if (q !== '"' && q !== "'") { + error("Missing quote."); + } + xmode = 'styleproperty'; + xquote = q; + advance(q); + substyle(); + xmode = 'html'; + xquote = ''; + advance(q); + v = false; + } else { + if (nexttoken.id === '=') { + advance('='); + v = nexttoken.value; + if (!nexttoken.identifier && + nexttoken.id !== '"' && + nexttoken.id !== '\'' && + nexttoken.type !== '(string)' && + nexttoken.type !== '(number)' && + nexttoken.type !== '(color)') { + warning("Expected an attribute value and instead saw '{a}'.", token, a); + } + advance(); + } else { + v = true; + } + } + attributes[a] = v; + doAttribute(n, a, v); + } + doTag(n, attributes); + if (!e) { + stack.push(t); + } + xmode = 'outer'; + advance('>'); + break; + case '') { + error("Missing '{a}'.", nexttoken, '>'); + } + xmode = 'outer'; + advance('>'); + break; + case '' || nexttoken.id === '(end)') { + break; + } + if (nexttoken.value.indexOf('--') >= 0) { + warning("Unexpected --."); + } + if (nexttoken.value.indexOf('<') >= 0) { + warning("Unexpected <."); + } + if (nexttoken.value.indexOf('>') >= 0) { + warning("Unexpected >."); + } + } + xmode = 'outer'; + advance('>'); + break; + case '(end)': + return; + default: + if (nexttoken.id === '(end)') { + error("Missing '{a}'.", nexttoken, + ''); + } else { + advance(); + } + } + if (stack && stack.length === 0 && (option.adsafe || + !option.fragment || nexttoken.id === '(end)')) { + break; + } + } + if (nexttoken.id !== '(end)') { + error("Unexpected material after the end."); + } + } + + +// Build the syntax table by declaring the syntactic elements of the language. + + type('(number)', idValue); + type('(string)', idValue); + + syntax['(identifier)'] = { + type: '(identifier)', + lbp: 0, + identifier: true, + nud: function () { + var v = this.value, + s = scope[v], + f; + if (typeof s === 'function') { + s = undefined; + } else if (typeof s === 'boolean') { + f = funct; + funct = functions[0]; + addlabel(v, 'var'); + s = funct; + funct = f; + } + +// The name is in scope and defined in the current function. + + if (funct === s) { + +// Change 'unused' to 'var', and reject labels. + + switch (funct[v]) { + case 'unused': + funct[v] = 'var'; + break; + case 'label': + warning("'{a}' is a statement label.", token, v); + break; + } + +// The name is not defined in the function. If we are in the global scope, +// then we have an undefined variable. + + } else if (funct['(global)']) { + if (option.undef && predefined[v] !== 'boolean') { + warning("'{a}' is not defined.", token, v); + } + note_implied(token); + +// If the name is already defined in the current +// function, but not as outer, then there is a scope error. + + } else { + switch (funct[v]) { + case 'closure': + case 'function': + case 'var': + case 'unused': + warning("'{a}' used out of scope.", token, v); + break; + case 'label': + warning("'{a}' is a statement label.", token, v); + break; + case 'outer': + case 'global': + break; + default: + +// If the name is defined in an outer function, make an outer entry, and if +// it was unused, make it var. + + if (s === true) { + funct[v] = true; + } else if (s === null) { + warning("'{a}' is not allowed.", token, v); + note_implied(token); + } else if (typeof s !== 'object') { + if (option.undef) { + warning("'{a}' is not defined.", token, v); + } else { + funct[v] = true; + } + note_implied(token); + } else { + switch (s[v]) { + case 'function': + case 'var': + case 'unused': + s[v] = 'closure'; + funct[v] = s['(global)'] ? 'global' : 'outer'; + break; + case 'closure': + case 'parameter': + funct[v] = s['(global)'] ? 'global' : 'outer'; + break; + case 'label': + warning("'{a}' is a statement label.", token, v); + } + } + } + } + return this; + }, + led: function () { + error("Expected an operator and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + }; + + type('(regexp)', function () { + return this; + }); + + +// ECMAScript parser + + delim('(endline)'); + delim('(begin)'); + delim('(end)').reach = true; + delim(''); + delim('(error)').reach = true; + delim('}').reach = true; + delim(')'); + delim(']'); + delim('"').reach = true; + delim("'").reach = true; + delim(';'); + delim(':').reach = true; + delim(','); + delim('#'); + delim('@'); + reserve('else'); + reserve('case').reach = true; + reserve('catch'); + reserve('default').reach = true; + reserve('finally'); + reservevar('arguments'); + reservevar('eval'); + reservevar('false'); + reservevar('Infinity'); + reservevar('NaN'); + reservevar('null'); + reservevar('this'); + reservevar('true'); + reservevar('undefined'); + assignop('=', 'assign', 20); + assignop('+=', 'assignadd', 20); + assignop('-=', 'assignsub', 20); + assignop('*=', 'assignmult', 20); + assignop('/=', 'assigndiv', 20).nud = function () { + error("A regular expression literal can be confused with '/='."); + }; + assignop('%=', 'assignmod', 20); + bitwiseassignop('&=', 'assignbitand', 20); + bitwiseassignop('|=', 'assignbitor', 20); + bitwiseassignop('^=', 'assignbitxor', 20); + bitwiseassignop('<<=', 'assignshiftleft', 20); + bitwiseassignop('>>=', 'assignshiftright', 20); + bitwiseassignop('>>>=', 'assignshiftrightunsigned', 20); + infix('?', function (left, that) { + that.left = left; + that.right = parse(10); + advance(':'); + that['else'] = parse(10); + return that; + }, 30); + + infix('||', 'or', 40); + infix('&&', 'and', 50); + bitwise('|', 'bitor', 70); + bitwise('^', 'bitxor', 80); + bitwise('&', 'bitand', 90); + relation('==', function (left, right) { + if (option.eqeqeq) { + warning("Expected '{a}' and instead saw '{b}'.", + this, '===', '=='); + } else if (isPoorRelation(left)) { + warning("Use '{a}' to compare with '{b}'.", + this, '===', left.value); + } else if (isPoorRelation(right)) { + warning("Use '{a}' to compare with '{b}'.", + this, '===', right.value); + } + return this; + }); + relation('==='); + relation('!=', function (left, right) { + if (option.eqeqeq) { + warning("Expected '{a}' and instead saw '{b}'.", + this, '!==', '!='); + } else if (isPoorRelation(left)) { + warning("Use '{a}' to compare with '{b}'.", + this, '!==', left.value); + } else if (isPoorRelation(right)) { + warning("Use '{a}' to compare with '{b}'.", + this, '!==', right.value); + } + return this; + }); + relation('!=='); + relation('<'); + relation('>'); + relation('<='); + relation('>='); + bitwise('<<', 'shiftleft', 120); + bitwise('>>', 'shiftright', 120); + bitwise('>>>', 'shiftrightunsigned', 120); + infix('in', 'in', 120); + infix('instanceof', 'instanceof', 120); + infix('+', function (left, that) { + var right = parse(130); + if (left && right && left.id === '(string)' && right.id === '(string)') { + left.value += right.value; + left.character = right.character; + if (jx.test(left.value)) { + warning("JavaScript URL.", left); + } + return left; + } + that.left = left; + that.right = right; + return that; + }, 130); + prefix('+', 'num'); + infix('-', 'sub', 130); + prefix('-', 'neg'); + infix('*', 'mult', 140); + infix('/', 'div', 140); + infix('%', 'mod', 140); + + suffix('++', 'postinc'); + prefix('++', 'preinc'); + syntax['++'].exps = true; + + suffix('--', 'postdec'); + prefix('--', 'predec'); + syntax['--'].exps = true; + prefix('delete', function () { + var p = parse(0); + if (!p || (p.id !== '.' && p.id !== '[')) { + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, '.', nexttoken.value); + } + this.first = p; + return this; + }).exps = true; + + + prefix('~', function () { + if (option.bitwise) { + warning("Unexpected '{a}'.", this, '~'); + } + parse(150); + return this; + }); + prefix('!', function () { + this.right = parse(150); + this.arity = 'unary'; + if (bang[this.right.id] === true) { + warning("Confusing use of '{a}'.", this, '!'); + } + return this; + }); + prefix('typeof', 'typeof'); + prefix('new', function () { + var c = parse(155), i; + if (c && c.id !== 'function') { + if (c.identifier) { + c['new'] = true; + switch (c.value) { + case 'Object': + warning("Use the object literal notation {}.", token); + break; + case 'Array': + if (nexttoken.id !== '(') { + warning("Use the array literal notation [].", token); + } else { + advance('('); + if (nexttoken.id === ')') { + warning("Use the array literal notation [].", token); + } else { + i = parse(0); + c.dimension = i; + if ((i.id === '(number)' && /[.+\-Ee]/.test(i.value)) || + (i.id === '-' && !i.right) || + i.id === '(string)' || i.id === '[' || + i.id === '{' || i.id === 'true' || + i.id === 'false' || + i.id === 'null' || i.id === 'undefined' || + i.id === 'Infinity') { + warning("Use the array literal notation [].", token); + } + if (nexttoken.id !== ')') { + error("Use the array literal notation [].", token); + } + } + advance(')'); + } + this.first = c; + return this; + case 'Number': + case 'String': + case 'Boolean': + case 'Math': + case 'JSON': + warning("Do not use {a} as a constructor.", token, c.value); + break; + case 'Function': + if (!option.evil) { + warning("The Function constructor is eval."); + } + break; + case 'Date': + case 'RegExp': + break; + default: + if (c.id !== 'function') { + i = c.value.substr(0, 1); + if (option.newcap && (i < 'A' || i > 'Z')) { + warning( + "A constructor name should start with an uppercase letter.", + token); + } + } + } + } else { + if (c.id !== '.' && c.id !== '[' && c.id !== '(') { + warning("Bad constructor.", token); + } + } + } else { + warning("Weird construction. Delete 'new'.", this); + } + adjacent(token, nexttoken); + if (nexttoken.id !== '(') { + warning("Missing '()' invoking a constructor."); + } + this.first = c; + return this; + }); + syntax['new'].exps = true; + + infix('.', function (left, that) { + adjacent(prevtoken, token); + var m = identifier(); + if (typeof m === 'string') { + countMember(m); + } + that.left = left; + that.right = m; + if (!option.evil && left && left.value === 'document' && + (m === 'write' || m === 'writeln')) { + warning("document.write can be a form of eval.", left); + } else if (option.adsafe) { + if (left && left.value === 'ADSAFE') { + if (m === 'id' || m === 'lib') { + warning("ADsafe violation.", that); + } else if (m === 'go') { + if (xmode !== 'script') { + warning("ADsafe violation.", that); + } else if (adsafe_went || nexttoken.id !== '(' || + peek(0).id !== '(string)' || + peek(0).value !== adsafe_id || + peek(1).id !== ',') { + error("ADsafe violation: go.", that); + } + adsafe_went = true; + adsafe_may = false; + } + } + } + if (!option.evil && (m === 'eval' || m === 'execScript')) { + warning('eval is evil.'); + } else if (option.safe) { + for (;;) { + if (banned[m] === true) { + warning("ADsafe restricted word '{a}'.", token, m); + } + if (typeof predefined[left.value] !== 'boolean' || + nexttoken.id === '(') { + break; + } + if (standard_member[m] === true) { + if (nexttoken.id === '.') { + warning("ADsafe violation.", that); + } + break; + } + if (nexttoken.id !== '.') { + warning("ADsafe violation.", that); + break; + } + advance('.'); + token.left = that; + token.right = m; + that = token; + m = identifier(); + if (typeof m === 'string') { + countMember(m); + } + } + } + return that; + }, 160, true); + + infix('(', function (left, that) { + adjacent(prevtoken, token); + nospace(); + var n = 0, + p = []; + if (left) { + if (left.type === '(identifier)') { + if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { + if (left.value !== 'Number' && left.value !== 'String' && + left.value !== 'Boolean' && + left.value !== 'Date') { + if (left.value === 'Math') { + warning("Math is not a function.", left); + } else if (option.newcap) { + warning( +"Missing 'new' prefix when invoking a constructor.", left); + } + } + } + } else if (left.id === '.') { + if (option.safe && left.left.value === 'Math' && + left.right === 'random') { + warning("ADsafe violation.", left); + } + } + } + if (nexttoken.id !== ')') { + for (;;) { + p[p.length] = parse(10); + n += 1; + if (nexttoken.id !== ',') { + break; + } + comma(); + } + } + advance(')'); + if (option.immed && left.id === 'function' && nexttoken.id !== ')') { + warning("Wrap the entire immediate function invocation in parens.", + that); + } + nospace(prevtoken, token); + if (typeof left === 'object') { + if (left.value === 'parseInt' && n === 1) { + warning("Missing radix parameter.", left); + } + if (!option.evil) { + if (left.value === 'eval' || left.value === 'Function' || + left.value === 'execScript') { + warning("eval is evil.", left); + } else if (p[0] && p[0].id === '(string)' && + (left.value === 'setTimeout' || + left.value === 'setInterval')) { + warning( + "Implied eval is evil. Pass a function instead of a string.", left); + } + } + if (!left.identifier && left.id !== '.' && left.id !== '[' && + left.id !== '(' && left.id !== '&&' && left.id !== '||' && + left.id !== '?') { + warning("Bad invocation.", left); + } + } + that.left = left; + return that; + }, 155, true).exps = true; + + prefix('(', function () { + nospace(); + var v = parse(0); + advance(')', this); + nospace(prevtoken, token); + if (option.immed && v.id === 'function') { + if (nexttoken.id === '(') { + warning( +"Move the invocation into the parens that contain the function.", nexttoken); + } else { + warning( +"Do not wrap function literals in parens unless they are to be immediately invoked.", + this); + } + } + return v; + }); + + infix('[', function (left, that) { + nospace(); + var e = parse(0), s; + if (e && e.type === '(string)') { + if (option.safe && banned[e.value] === true) { + warning("ADsafe restricted word '{a}'.", that, e.value); + } else if (!option.evil && + (e.value === 'eval' || e.value === 'execScript')) { + warning("eval is evil.", that); + } else if (option.safe && + (e.value.charAt(0) === '_' || e.value.charAt(0) === '-')) { + warning("ADsafe restricted subscript '{a}'.", that, e.value); + } + countMember(e.value); + if (!option.sub && ix.test(e.value)) { + s = syntax[e.value]; + if (!s || !s.reserved) { + warning("['{a}'] is better written in dot notation.", + e, e.value); + } + } + } else if (!e || e.type !== '(number)' || e.value < 0) { + if (option.safe) { + warning('ADsafe subscripting.'); + } + } + advance(']', that); + nospace(prevtoken, token); + that.left = left; + that.right = e; + return that; + }, 160, true); + + prefix('[', function () { + var b = token.line !== nexttoken.line; + this.first = []; + if (b) { + indent += option.indent; + if (nexttoken.from === indent + option.indent) { + indent += option.indent; + } + } + while (nexttoken.id !== '(end)') { + while (nexttoken.id === ',') { + warning("Extra comma."); + advance(','); + } + if (nexttoken.id === ']') { + break; + } + if (b && token.line !== nexttoken.line) { + indentation(); + } + this.first.push(parse(10)); + if (nexttoken.id === ',') { + comma(); + if (nexttoken.id === ']' && !option.es5) { + warning("Extra comma.", token); + break; + } + } else { + break; + } + } + if (b) { + indent -= option.indent; + indentation(); + } + advance(']', this); + return this; + }, 160); + + + function property_name() { + var i = optionalidentifier(true); + if (!i) { + if (nexttoken.id === '(string)') { + i = nexttoken.value; + advance(); + } else if (nexttoken.id === '(number)') { + i = nexttoken.value.toString(); + advance(); + } + } + return i; + } + + + function functionparams() { + var i, t = nexttoken, p = []; + advance('('); + nospace(); + if (nexttoken.id === ')') { + advance(')'); + nospace(prevtoken, token); + return; + } + for (;;) { + i = identifier(); + p.push(i); + addlabel(i, 'parameter'); + if (nexttoken.id === ',') { + comma(); + } else { + advance(')', t); + nospace(prevtoken, token); + return p; + } + } + } + + + function doFunction(i) { + var f, s = scope; + scope = Object.create(s); + funct = { + '(name)' : i || '"' + anonname + '"', + '(line)' : nexttoken.line, + '(context)' : funct, + '(breakage)': 0, + '(loopage)' : 0, + '(scope)' : scope + }; + f = funct; + token.funct = funct; + functions.push(funct); + if (i) { + addlabel(i, 'function'); + } + funct['(params)'] = functionparams(); + + block(false); + scope = s; + funct['(last)'] = token.line; + funct = funct['(context)']; + return f; + } + + + (function (x) { + x.nud = function () { + var b, f, i, j, p, seen = {}, t; + b = token.line !== nexttoken.line; + if (b) { + indent += option.indent; + if (nexttoken.from === indent + option.indent) { + indent += option.indent; + } + } + for (;;) { + if (nexttoken.id === '}') { + break; + } + if (b) { + indentation(); + } + if (nexttoken.value === 'get' && peek().id !== ':') { + advance('get'); + if (!option.es5) { + error("get/set are ES5 features."); + } + i = property_name(); + if (!i) { + error("Missing property name."); + } + t = nexttoken; + adjacent(token, nexttoken); + f = doFunction(i); + if (funct['(loopage)']) { + warning("Don't make functions within a loop.", t); + } + p = f['(params)']; + if (p) { + warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i); + } + adjacent(token, nexttoken); + advance(','); + indentation(); + advance('set'); + j = property_name(); + if (i !== j) { + error("Expected {a} and instead saw {b}.", token, i, j); + } + t = nexttoken; + adjacent(token, nexttoken); + f = doFunction(i); + p = f['(params)']; + if (!p || p.length !== 1 || p[0] !== 'value') { + warning("Expected (value) in set {a} function.", t, i); + } + } else { + i = property_name(); + if (typeof i !== 'string') { + break; + } + advance(':'); + nonadjacent(token, nexttoken); + parse(10); + } + if (seen[i] === true) { + warning("Duplicate member '{a}'.", nexttoken, i); + } + seen[i] = true; + countMember(i); + if (nexttoken.id === ',') { + comma(); + if (nexttoken.id === ',') { + warning("Extra comma.", token); + } else if (nexttoken.id === '}' && !option.es5) { + warning("Extra comma.", token); + } + } else { + break; + } + } + if (b) { + indent -= option.indent; + indentation(); + } + advance('}', this); + return this; + }; + x.fud = function () { + error("Expected to see a statement and instead saw a block.", token); + }; + }(delim('{'))); + + + function varstatement(prefix) { + +// JavaScript does not have block scope. It only has function scope. So, +// declaring a variable in a block can have unexpected consequences. + + var id, name, value; + + if (funct['(onevar)'] && option.onevar) { + warning("Too many var statements."); + } else if (!funct['(global)']) { + funct['(onevar)'] = true; + } + this.first = []; + for (;;) { + nonadjacent(token, nexttoken); + id = identifier(); + if (funct['(global)'] && predefined[id] === false) { + warning("Redefinition of '{a}'.", token, id); + } + addlabel(id, 'unused'); + if (prefix) { + break; + } + name = token; + this.first.push(token); + if (nexttoken.id === '=') { + nonadjacent(token, nexttoken); + advance('='); + nonadjacent(token, nexttoken); + if (nexttoken.id === 'undefined') { + warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id); + } + if (peek(0).id === '=' && nexttoken.identifier) { + error("Variable {a} was not declared correctly.", + nexttoken, nexttoken.value); + } + value = parse(0); + name.first = value; + } + if (nexttoken.id !== ',') { + break; + } + comma(); + } + return this; + } + + + stmt('var', varstatement).exps = true; + + + blockstmt('function', function () { + if (inblock) { + warning( +"Function statements cannot be placed in blocks. Use a function expression or move the statement to the top of the outer function.", token); + + } + var i = identifier(); + adjacent(token, nexttoken); + addlabel(i, 'unused'); + doFunction(i); + if (nexttoken.id === '(' && nexttoken.line === token.line) { + error( +"Function statements are not invocable. Wrap the whole function invocation in parens."); + } + return this; + }); + + prefix('function', function () { + var i = optionalidentifier(); + if (i) { + adjacent(token, nexttoken); + } else { + nonadjacent(token, nexttoken); + } + doFunction(i); + if (funct['(loopage)']) { + warning("Don't make functions within a loop."); + } + return this; + }); + + blockstmt('if', function () { + var t = nexttoken; + advance('('); + nonadjacent(this, t); + nospace(); + parse(20); + if (nexttoken.id === '=') { + warning("Expected a conditional expression and instead saw an assignment."); + advance('='); + parse(20); + } + advance(')', t); + nospace(prevtoken, token); + block(true); + if (nexttoken.id === 'else') { + nonadjacent(token, nexttoken); + advance('else'); + if (nexttoken.id === 'if' || nexttoken.id === 'switch') { + statement(true); + } else { + block(true); + } + } + return this; + }); + + blockstmt('try', function () { + var b, e, s; + if (option.adsafe) { + warning("ADsafe try violation.", this); + } + block(false); + if (nexttoken.id === 'catch') { + advance('catch'); + nonadjacent(token, nexttoken); + advance('('); + s = scope; + scope = Object.create(s); + e = nexttoken.value; + if (nexttoken.type !== '(identifier)') { + warning("Expected an identifier and instead saw '{a}'.", + nexttoken, e); + } else { + addlabel(e, 'exception'); + } + advance(); + advance(')'); + block(false); + b = true; + scope = s; + } + if (nexttoken.id === 'finally') { + advance('finally'); + block(false); + return; + } else if (!b) { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, 'catch', nexttoken.value); + } + return this; + }); + + blockstmt('while', function () { + var t = nexttoken; + funct['(breakage)'] += 1; + funct['(loopage)'] += 1; + advance('('); + nonadjacent(this, t); + nospace(); + parse(20); + if (nexttoken.id === '=') { + warning("Expected a conditional expression and instead saw an assignment."); + advance('='); + parse(20); + } + advance(')', t); + nospace(prevtoken, token); + block(true); + funct['(breakage)'] -= 1; + funct['(loopage)'] -= 1; + return this; + }).labelled = true; + + reserve('with'); + + blockstmt('switch', function () { + var t = nexttoken, + g = false; + funct['(breakage)'] += 1; + advance('('); + nonadjacent(this, t); + nospace(); + this.condition = parse(20); + advance(')', t); + nospace(prevtoken, token); + nonadjacent(token, nexttoken); + t = nexttoken; + advance('{'); + nonadjacent(token, nexttoken); + indent += option.indent; + this.cases = []; + for (;;) { + switch (nexttoken.id) { + case 'case': + switch (funct['(verb)']) { + case 'break': + case 'case': + case 'continue': + case 'return': + case 'switch': + case 'throw': + break; + default: + warning( + "Expected a 'break' statement before 'case'.", + token); + } + indentation(-option.indent); + advance('case'); + this.cases.push(parse(20)); + g = true; + advance(':'); + funct['(verb)'] = 'case'; + break; + case 'default': + switch (funct['(verb)']) { + case 'break': + case 'continue': + case 'return': + case 'throw': + break; + default: + warning( + "Expected a 'break' statement before 'default'.", + token); + } + indentation(-option.indent); + advance('default'); + g = true; + advance(':'); + break; + case '}': + indent -= option.indent; + indentation(); + advance('}', t); + if (this.cases.length === 1 || this.condition.id === 'true' || + this.condition.id === 'false') { + warning("This 'switch' should be an 'if'.", this); + } + funct['(breakage)'] -= 1; + funct['(verb)'] = undefined; + return; + case '(end)': + error("Missing '{a}'.", nexttoken, '}'); + return; + default: + if (g) { + switch (token.id) { + case ',': + error("Each value should have its own case label."); + return; + case ':': + statements(); + break; + default: + error("Missing ':' on a case clause.", token); + } + } else { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, 'case', nexttoken.value); + } + } + } + }).labelled = true; + + stmt('debugger', function () { + if (!option.debug) { + warning("All 'debugger' statements should be removed."); + } + return this; + }).exps = true; + + (function () { + var x = stmt('do', function () { + funct['(breakage)'] += 1; + funct['(loopage)'] += 1; + this.first = block(true); + advance('while'); + var t = nexttoken; + nonadjacent(token, t); + advance('('); + nospace(); + parse(20); + if (nexttoken.id === '=') { + warning("Expected a conditional expression and instead saw an assignment."); + advance('='); + parse(20); + } + advance(')', t); + nospace(prevtoken, token); + funct['(breakage)'] -= 1; + funct['(loopage)'] -= 1; + return this; + }); + x.labelled = true; + x.exps = true; + }()); + + blockstmt('for', function () { + var f = option.forin, s, t = nexttoken; + funct['(breakage)'] += 1; + funct['(loopage)'] += 1; + advance('('); + nonadjacent(this, t); + nospace(); + if (peek(nexttoken.id === 'var' ? 1 : 0).id === 'in') { + if (nexttoken.id === 'var') { + advance('var'); + varstatement(true); + } else { + switch (funct[nexttoken.value]) { + case 'unused': + funct[nexttoken.value] = 'var'; + break; + case 'var': + break; + default: + warning("Bad for in variable '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + } + advance('in'); + parse(20); + advance(')', t); + s = block(true); + if (!f && (s.length > 1 || typeof s[0] !== 'object' || + s[0].value !== 'if')) { + warning("The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.", this); + } + funct['(breakage)'] -= 1; + funct['(loopage)'] -= 1; + return this; + } else { + if (nexttoken.id !== ';') { + if (nexttoken.id === 'var') { + advance('var'); + varstatement(); + } else { + for (;;) { + parse(0, 'for'); + if (nexttoken.id !== ',') { + break; + } + comma(); + } + } + } + nolinebreak(token); + advance(';'); + if (nexttoken.id !== ';') { + parse(20); + if (nexttoken.id === '=') { + warning("Expected a conditional expression and instead saw an assignment."); + advance('='); + parse(20); + } + } + nolinebreak(token); + advance(';'); + if (nexttoken.id === ';') { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, ')', ';'); + } + if (nexttoken.id !== ')') { + for (;;) { + parse(0, 'for'); + if (nexttoken.id !== ',') { + break; + } + comma(); + } + } + advance(')', t); + nospace(prevtoken, token); + block(true); + funct['(breakage)'] -= 1; + funct['(loopage)'] -= 1; + return this; + } + }).labelled = true; + + + stmt('break', function () { + var v = nexttoken.value; + if (funct['(breakage)'] === 0) { + warning("Unexpected '{a}'.", nexttoken, this.value); + } + nolinebreak(this); + if (nexttoken.id !== ';') { + if (token.line === nexttoken.line) { + if (funct[v] !== 'label') { + warning("'{a}' is not a statement label.", nexttoken, v); + } else if (scope[v] !== funct) { + warning("'{a}' is out of scope.", nexttoken, v); + } + this.first = nexttoken; + advance(); + } + } + reachable('break'); + return this; + }).exps = true; + + + stmt('continue', function () { + var v = nexttoken.value; + if (funct['(breakage)'] === 0) { + warning("Unexpected '{a}'.", nexttoken, this.value); + } + nolinebreak(this); + if (nexttoken.id !== ';') { + if (token.line === nexttoken.line) { + if (funct[v] !== 'label') { + warning("'{a}' is not a statement label.", nexttoken, v); + } else if (scope[v] !== funct) { + warning("'{a}' is out of scope.", nexttoken, v); + } + this.first = nexttoken; + advance(); + } + } else if (!funct['(loopage)']) { + warning("Unexpected '{a}'.", nexttoken, this.value); + } + reachable('continue'); + return this; + }).exps = true; + + + stmt('return', function () { + nolinebreak(this); + if (nexttoken.id === '(regexp)') { + warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator."); + } + if (nexttoken.id !== ';' && !nexttoken.reach) { + nonadjacent(token, nexttoken); + this.first = parse(20); + } + reachable('return'); + return this; + }).exps = true; + + + stmt('throw', function () { + nolinebreak(this); + nonadjacent(token, nexttoken); + this.first = parse(20); + reachable('throw'); + return this; + }).exps = true; + + reserve('void'); + +// Superfluous reserved words + + reserve('class'); + reserve('const'); + reserve('enum'); + reserve('export'); + reserve('extends'); + reserve('import'); + reserve('super'); + + reserve('let'); + reserve('yield'); + reserve('implements'); + reserve('interface'); + reserve('package'); + reserve('private'); + reserve('protected'); + reserve('public'); + reserve('static'); + + +// Parse JSON + + function jsonValue() { + + function jsonObject() { + var o = {}, t = nexttoken; + advance('{'); + if (nexttoken.id !== '}') { + for (;;) { + if (nexttoken.id === '(end)') { + error("Missing '}' to match '{' from line {a}.", + nexttoken, t.line); + } else if (nexttoken.id === '}') { + warning("Unexpected comma.", token); + break; + } else if (nexttoken.id === ',') { + error("Unexpected comma.", nexttoken); + } else if (nexttoken.id !== '(string)') { + warning("Expected a string and instead saw {a}.", + nexttoken, nexttoken.value); + } + if (o[nexttoken.value] === true) { + warning("Duplicate key '{a}'.", + nexttoken, nexttoken.value); + } else if (nexttoken.value === '__proto__') { + warning("Stupid key '{a}'.", + nexttoken, nexttoken.value); + } else { + o[nexttoken.value] = true; + } + advance(); + advance(':'); + jsonValue(); + if (nexttoken.id !== ',') { + break; + } + advance(','); + } + } + advance('}'); + } + + function jsonArray() { + var t = nexttoken; + advance('['); + if (nexttoken.id !== ']') { + for (;;) { + if (nexttoken.id === '(end)') { + error("Missing ']' to match '[' from line {a}.", + nexttoken, t.line); + } else if (nexttoken.id === ']') { + warning("Unexpected comma.", token); + break; + } else if (nexttoken.id === ',') { + error("Unexpected comma.", nexttoken); + } + jsonValue(); + if (nexttoken.id !== ',') { + break; + } + advance(','); + } + } + advance(']'); + } + + switch (nexttoken.id) { + case '{': + jsonObject(); + break; + case '[': + jsonArray(); + break; + case 'true': + case 'false': + case 'null': + case '(number)': + case '(string)': + advance(); + break; + case '-': + advance('-'); + if (token.character !== nexttoken.from) { + warning("Unexpected space after '-'.", token); + } + adjacent(token, nexttoken); + advance('(number)'); + break; + default: + error("Expected a JSON value.", nexttoken); + } + } + + +// The actual JSLINT function itself. + + var itself = function (s, o) { + var a, i; + JSLINT.errors = []; + predefined = Object.create(standard); + if (o) { + a = o.predef; + if (a instanceof Array) { + for (i = 0; i < a.length; i += 1) { + predefined[a[i]] = true; + } + } + if (o.adsafe) { + o.safe = true; + } + if (o.safe) { + o.browser = false; + o.css = false; + o.debug = false; + o.devel = false; + o.eqeqeq = true; + o.evil = false; + o.forin = false; + o.nomen = true; + o.on = false; + o.rhino = false; + o.safe = true; + o.windows = false; + o.strict = true; + o.sub = false; + o.undef = true; + o.widget = false; + predefined.Date = null; + predefined['eval'] = null; + predefined.Function = null; + predefined.Object = null; + predefined.ADSAFE = false; + predefined.lib = false; + } + option = o; + } else { + option = {}; + } + option.indent = option.indent || 4; + option.maxerr = option.maxerr || 50; + adsafe_id = ''; + adsafe_may = false; + adsafe_went = false; + approved = {}; + if (option.approved) { + for (i = 0; i < option.approved.length; i += 1) { + approved[option.approved[i]] = option.approved[i]; + } + } else { + approved.test = 'test'; + } + tab = ''; + for (i = 0; i < option.indent; i += 1) { + tab += ' '; + } + indent = 1; + global = Object.create(predefined); + scope = global; + funct = { + '(global)': true, + '(name)': '(global)', + '(scope)': scope, + '(breakage)': 0, + '(loopage)': 0 + }; + functions = [funct]; + ids = {}; + urls = []; + src = false; + xmode = false; + stack = null; + member = {}; + membersOnly = null; + implied = {}; + inblock = false; + lookahead = []; + jsonmode = false; + warnings = 0; + lex.init(s); + prereg = true; + strict_mode = false; + + prevtoken = token = nexttoken = syntax['(begin)']; + assume(); + + try { + advance(); + if (nexttoken.value.charAt(0) === '<') { + html(); + if (option.adsafe && !adsafe_went) { + warning("ADsafe violation: Missing ADSAFE.go.", this); + } + } else { + switch (nexttoken.id) { + case '{': + case '[': + option.laxbreak = true; + jsonmode = true; + jsonValue(); + break; + case '@': + case '*': + case '#': + case '.': + case ':': + xmode = 'style'; + advance(); + if (token.id !== '@' || !nexttoken.identifier || + nexttoken.value !== 'charset' || token.line !== 1 || + token.from !== 1) { + error('A css file should begin with @charset "UTF-8";'); + } + advance(); + if (nexttoken.type !== '(string)' && + nexttoken.value !== 'UTF-8') { + error('A css file should begin with @charset "UTF-8";'); + } + advance(); + advance(';'); + styles(); + break; + + default: + if (option.adsafe && option.fragment) { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, '
', nexttoken.value); + } + statements('lib'); + } + } + advance('(end)'); + } catch (e) { + if (e) { + JSLINT.errors.push({ + reason : e.message, + line : e.line || nexttoken.line, + character : e.character || nexttoken.from + }, null); + } + } + return JSLINT.errors.length === 0; + }; + + function is_array(o) { + return Object.prototype.toString.apply(o) === '[object Array]'; + } + + function to_array(o) { + var a = [], k; + for (k in o) { + if (is_own(o, k)) { + a.push(k); + } + } + return a; + } + + +// Data summary. + + itself.data = function () { + + var data = {functions: []}, fu, globals, implieds = [], f, i, j, + members = [], n, unused = [], v; + if (itself.errors.length) { + data.errors = itself.errors; + } + + if (jsonmode) { + data.json = true; + } + + for (n in implied) { + if (is_own(implied, n)) { + implieds.push({ + name: n, + line: implied[n] + }); + } + } + if (implieds.length > 0) { + data.implieds = implieds; + } + + if (urls.length > 0) { + data.urls = urls; + } + + globals = to_array(scope); + if (globals.length > 0) { + data.globals = globals; + } + + for (i = 1; i < functions.length; i += 1) { + f = functions[i]; + fu = {}; + for (j = 0; j < functionicity.length; j += 1) { + fu[functionicity[j]] = []; + } + for (n in f) { + if (is_own(f, n) && n.charAt(0) !== '(') { + v = f[n]; + if (is_array(fu[v])) { + fu[v].push(n); + if (v === 'unused') { + unused.push({ + name: n, + line: f['(line)'], + 'function': f['(name)'] + }); + } + } + } + } + for (j = 0; j < functionicity.length; j += 1) { + if (fu[functionicity[j]].length === 0) { + delete fu[functionicity[j]]; + } + } + fu.name = f['(name)']; + fu.param = f['(params)']; + fu.line = f['(line)']; + fu.last = f['(last)']; + data.functions.push(fu); + } + + if (unused.length > 0) { + data.unused = unused; + } + + members = []; + for (n in member) { + if (typeof member[n] === 'number') { + data.member = member; + break; + } + } + + return data; + }; + + itself.report = function (option) { + var data = itself.data(); + + var a = [], c, e, err, f, i, k, l, m = '', n, o = [], s; + + function detail(h, array) { + var b, i, singularity; + if (array) { + o.push('
' + h + ' '); + array = array.sort(); + for (i = 0; i < array.length; i += 1) { + if (array[i] !== singularity) { + singularity = array[i]; + o.push((b ? ', ' : '') + singularity); + b = true; + } + } + o.push('
'); + } + } + + + if (data.errors || data.implieds || data.unused) { + err = true; + o.push('
Error:'); + if (data.errors) { + for (i = 0; i < data.errors.length; i += 1) { + c = data.errors[i]; + if (c) { + e = c.evidence || ''; + o.push('

Problem' + (isFinite(c.line) ? ' at line ' + + c.line + ' character ' + c.character : '') + + ': ' + c.reason.entityify() + + '

' + + (e && (e.length > 80 ? e.slice(0, 77) + '...' : + e).entityify()) + '

'); + } + } + } + + if (data.implieds) { + s = []; + for (i = 0; i < data.implieds.length; i += 1) { + s[i] = '' + data.implieds[i].name + ' ' + + data.implieds[i].line + ''; + } + o.push('

Implied global: ' + s.join(', ') + '

'); + } + + if (data.unused) { + s = []; + for (i = 0; i < data.unused.length; i += 1) { + s[i] = '' + data.unused[i].name + ' ' + + data.unused[i].line + ' ' + + data.unused[i]['function'] + ''; + } + o.push('

Unused variable: ' + s.join(', ') + '

'); + } + if (data.json) { + o.push('

JSON: bad.

'); + } + o.push('
'); + } + + if (!option) { + + o.push('
'); + + if (data.urls) { + detail("URLs
", data.urls, '
'); + } + + if (xmode === 'style') { + o.push('

CSS.

'); + } else if (data.json && !err) { + o.push('

JSON: good.

'); + } else if (data.globals) { + o.push('
Global ' + + data.globals.sort().join(', ') + '
'); + } else { + o.push('
No new global variables introduced.
'); + } + + for (i = 0; i < data.functions.length; i += 1) { + f = data.functions[i]; + + o.push('
' + f.line + '-' + + f.last + ' ' + (f.name || '') + '(' + + (f.param ? f.param.join(', ') : '') + ')
'); + detail('Unused', f.unused); + detail('Closure', f.closure); + detail('Variable', f['var']); + detail('Exception', f.exception); + detail('Outer', f.outer); + detail('Global', f.global); + detail('Label', f.label); + } + + if (data.member) { + a = to_array(data.member); + if (a.length) { + a = a.sort(); + m = '
/*members ';
+                    l = 10;
+                    for (i = 0; i < a.length; i += 1) {
+                        k = a[i];
+                        n = k.name();
+                        if (l + n.length > 72) {
+                            o.push(m + '
'); + m = ' '; + l = 1; + } + l += n.length + 2; + if (data.member[k] === 1) { + n = '' + n + ''; + } + if (i < a.length - 1) { + n += ', '; + } + m += n; + } + o.push(m + '
*/
'); + } + o.push('
'); + } + } + return o.join(''); + }; + itself.jslint = itself; + + itself.edition = '2010-04-06'; + + return itself; + +}()); +// rhino.js +// 2009-09-11 +/* +Copyright (c) 2002 Douglas Crockford (www.JSLint.com) Rhino Edition +*/ + +// This is the Rhino companion to fulljslint.js. + +/*global JSLINT */ +/*jslint rhino: true, strict: false */ + +(function (a) { + var e, i, input; + if (!a[0]) { + print("Usage: jslint.js file.js"); + quit(1); + } + input = readFile(a[0]); + if (!input) { + print("jslint: Couldn't open file '" + a[0] + "'."); + quit(1); + } + if (!JSLINT(input, {bitwise: true, eqeqeq: true, immed: true, + newcap: true, nomen: true, onevar: true, plusplus: true, + regexp: true, rhino: true, undef: true, white: true})) { + for (i = 0; i < JSLINT.errors.length; i += 1) { + e = JSLINT.errors[i]; + if (e) { + print('Lint at line ' + e.line + ' character ' + + e.character + ': ' + e.reason); + print((e.evidence || ''). + replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1")); + print(''); + } + } + quit(2); + } else { + print("jslint: No problems found in " + a[0]); + quit(); + } +}(arguments)); diff --git a/sjcl.js b/sjcl.js new file mode 100644 index 00000000..c887e5d3 --- /dev/null +++ b/sjcl.js @@ -0,0 +1,39 @@ +"use strict";var sjcl={cipher:{},hash:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a}}}; +sjcl.cipher.aes=function(a){this.h[0][0][0]||this.w();var b,c,d,e,f=this.h[0][4],g=this.h[1];b=a.length;var h=1;if(b!==4&&b!==6&&b!==8)throw new sjcl.exception.invalid("invalid aes key size");this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(a%b===0||b===8&&a%b===4){c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255];if(a%b===0){c=c<<8^c>>>24^h<<24;h=h<<1^(h>>7)*283}}d[a]=d[a-b]^c}for(b=0;a;b++,a--){c=d[b&3?a:a-4];e[b]=a<=4||b<4?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^ +g[3][f[c&255]]}}; +sjcl.cipher.aes.prototype={encrypt:function(a){return this.H(a,0)},decrypt:function(a){return this.H(a,1)},h:[[[],[],[],[],[]],[[],[],[],[],[]]],w:function(){var a=this.h[0],b=this.h[1],c=a[4],d=b[4],e,f,g,h=[],i=[],k,j,l,m;for(e=0;e<0x100;e++)i[(h[e]=e<<1^(e>>7)*283)^e]=e;for(f=g=0;!c[f];f^=k||1,g=i[g]||1){l=g^g<<1^g<<2^g<<3^g<<4;l=l>>8^l&255^99;c[f]=l;d[l]=f;j=h[e=h[k=h[f]]];m=j*0x1010101^e*0x10001^k*0x101^f*0x1010100;j=h[l]*0x101^l*0x1010100;for(e=0;e<4;e++){a[e][f]=j=j<<24^j>>>8;b[e][l]=m=m<<24^m>>>8}}for(e= +0;e<5;e++){a[e]=a[e].slice(0);b[e]=b[e].slice(0)}},H:function(a,b){if(a.length!==4)throw new sjcl.exception.invalid("invalid aes block size");var c=this.a[b],d=a[0]^c[0],e=a[b?3:1]^c[1],f=a[2]^c[2];a=a[b?1:3]^c[3];var g,h,i,k=c.length/4-2,j,l=4,m=[0,0,0,0];g=this.h[b];var n=g[0],o=g[1],p=g[2],q=g[3],r=g[4];for(j=0;j>>24]^o[e>>16&255]^p[f>>8&255]^q[a&255]^c[l];h=n[e>>>24]^o[f>>16&255]^p[a>>8&255]^q[d&255]^c[l+1];i=n[f>>>24]^o[a>>16&255]^p[d>>8&255]^q[e&255]^c[l+2];a=n[a>>>24]^o[d>>16& +255]^p[e>>8&255]^q[f&255]^c[l+3];l+=4;d=g;e=h;f=i}for(j=0;j<4;j++){m[b?3&-j:j]=r[d>>>24]<<24^r[e>>16&255]<<16^r[f>>8&255]<<8^r[a&255]^c[l++];g=d;d=e;e=f;f=a;a=g}return m}}; +sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===undefined?a:sjcl.bitArray.clamp(a,c-b)},concat:function(a,b){if(a.length===0||b.length===0)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return d===32?a.concat(b):sjcl.bitArray.P(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;if(b===0)return 0;return(b-1)*32+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(a.length*320&&b)a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1);return a},partial:function(a,b,c){if(a===32)return b;return(c?b|0:b<<32-a)+a*0xffffffff},getPartial:function(a){return Math.round(a/0xffffffff)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return false;var c=0,d;for(d=0;d=32;b-=32){d.push(c);c=0}if(b===0)return d.concat(a); +for(e=0;e>>b);c=a[e]<<32-b}e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,b+a>32?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}}; +sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d>>24);e<<=8}return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c>>e)>>>26);if(e<6){g=a[d]<<6-e;e+=26;d++}else{g<<=6;e-=6}}for(;c.length&3&&!b;)c+="=";return c},toBits:function(a){a=a.replace(/\s|=/g,"");var b=[],c,d=0,e=sjcl.codec.base64.D,f=0,g;for(c=0;c26){d-=26;b.push(f^g>>>d);f=g<<32-d}else{d+=6;f^=g<<32-d}}d&56&&b.push(sjcl.bitArray.partial(d&56,f,1));return b}};sjcl.hash.sha256=function(a){this.a[0]||this.w();if(a){this.n=a.n.slice(0);this.i=a.i.slice(0);this.e=a.e}else this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()}; +sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.n=this.N.slice(0);this.i=[];this.e=0;return this},update:function(a){if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);var b,c=this.i=sjcl.bitArray.concat(this.i,a);b=this.e;a=this.e=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)this.C(c.splice(0,16));return this},finalize:function(){var a,b=this.i,c=this.n;b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.e/ +4294967296));for(b.push(this.e|0);b.length;)this.C(b.splice(0,16));this.reset();return c},N:[],a:[],w:function(){function a(e){return(e-Math.floor(e))*0xffffffff|0}var b=0,c=2,d;a:for(;b<64;c++){for(d=2;d*d<=c;d++)if(c%d===0)continue a;if(b<8)this.N[b]=a(Math.pow(c,0.5));this.a[b]=a(Math.pow(c,1/3));b++}},C:function(a){var b,c,d=a.slice(0),e=this.n,f=this.a,g=e[0],h=e[1],i=e[2],k=e[3],j=e[4],l=e[5],m=e[6],n=e[7];for(a=0;a<64;a++){if(a<16)b=d[a];else{b=d[a+1&15];c=d[a+14&15];b=d[a&15]=(b>>>7^b>>>18^ +b>>>3^b<<25^b<<14)+(c>>>17^c>>>19^c>>>10^c<<15^c<<13)+d[a&15]+d[a+9&15]|0}b=b+n+(j>>>6^j>>>11^j>>>25^j<<26^j<<21^j<<7)+(m^j&(l^m))+f[a];n=m;m=l;l=j;j=k+b|0;k=i;i=h;h=g;g=b+(h&i^k&(h^i))+(h>>>2^h>>>13^h>>>22^h<<30^h<<19^h<<10)|0}e[0]=e[0]+g|0;e[1]=e[1]+h|0;e[2]=e[2]+i|0;e[3]=e[3]+k|0;e[4]=e[4]+j|0;e[5]=e[5]+l|0;e[6]=e[6]+m|0;e[7]=e[7]+n|0}}; +sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,i=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];if(i<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;f<4&&k>>>8*f;f++);if(f<15-i)f=15-i;c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.G(a,b,c,d,e,f);g=sjcl.mode.ccm.I(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),i=f.clamp(b,h-e),k=f.bitSlice(b, +h-e);h=(h-e)/8;if(g<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;b<4&&h>>>8*b;b++);if(b<15-g)b=15-g;c=f.clamp(c,8*(15-b));i=sjcl.mode.ccm.I(a,i,c,k,e,b);a=sjcl.mode.ccm.G(a,i.data,c,d,e,b);if(!f.equal(i.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match");return i.data},G:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,i=h.k;e/=8;if(e%2||e<4||e>16)throw new sjcl.exception.invalid("ccm: invalid tag length");if(d.length>0xffffffff||b.length>0xffffffff)throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"); +f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;if(c<=65279)g=[h.partial(16,c)];else if(c<=0xffffffff)g=h.concat([h.partial(16,65534)],[c]);g=h.concat(g,d);for(d=0;d>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^(a[0]>>>31)*135]}};sjcl.misc.hmac=function(a,b){this.M=b=b||sjcl.hash.sha256;var c=[[],[]],d=b.prototype.blockSize/32;this.l=[new b,new b];if(a.length>d)a=b.hash(a);for(b=0;b0;){b++;e>>>=1}this.b[g].update([d,this.J++,2,b,f,a.length].concat(a));break;case "string":if(b===undefined)b=a.length;this.b[g].update([d,this.J++,3,b,f,a.length]);this.b[g].update(a);break;default:throw new sjcl.exception.bug("random: addEntropy only supports number, array or string");}this.j[g]+=b;this.f+=b;if(h===0){this.isReady()!==0&&this.K("seeded",Math.max(this.g, +this.f));this.K("progress",this.getProgress())}},isReady:function(a){a=this.B[a!==undefined?a:this.t];return this.g&&this.g>=a?this.j[0]>80&&(new Date).valueOf()>this.O?3:1:this.f>=a?2:0},getProgress:function(a){a=this.B[a?a:this.t];return this.g>=a?1["0"]:this.f>a?1["0"]:this.f/a},startCollectors:function(){if(!this.m){if(window.addEventListener){window.addEventListener("load",this.o,false);window.addEventListener("mousemove",this.p,false)}else if(document.attachEvent){document.attachEvent("onload", +this.o);document.attachEvent("onmousemove",this.p)}else throw new sjcl.exception.bug("can't attach event");this.m=true}},stopCollectors:function(){if(this.m){if(window.removeEventListener){window.removeEventListener("load",this.o);window.removeEventListener("mousemove",this.p)}else if(window.detachEvent){window.detachEvent("onload",this.o);window.detachEvent("onmousemove",this.p)}this.m=false}},addEventListener:function(a,b){this.r[a][this.Q++]=b},removeEventListener:function(a,b){var c;a=this.r[a]; +var d=[];for(c in a)a.hasOwnProperty[c]&&a[c]===b&&d.push(c);for(b=0;b=1<this.g)this.g=c;this.z++;this.T(b)},p:function(a){sjcl.random.addEntropy([a.x|| +a.clientX||a.offsetX,a.y||a.clientY||a.offsetY],2,"mouse")},o:function(){sjcl.random.addEntropy(new Date,2,"loadtime")},K:function(a,b){var c;a=sjcl.random.r[a];var d=[];for(c in a)a.hasOwnProperty(c)&&d.push(a[c]);for(c=0;c +4)throw new sjcl.exception.invalid("json encrypt: invalid parameters");if(typeof a==="string"){c=sjcl.misc.cachedPbkdf2(a,f);a=c.key.slice(0,f.ks/32);f.salt=c.salt}if(typeof b==="string")b=sjcl.codec.utf8String.toBits(b);c=new sjcl.cipher[f.cipher](a);e.c(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(c,b,f.iv,f.adata,f.tag);return e.encode(e.V(f,e.defaults))},decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.c(e.c(e.c({},e.defaults),e.decode(b)),c,true);if(typeof b.salt==="string")b.salt= +sjcl.codec.base64.toBits(b.salt);if(typeof b.iv==="string")b.iv=sjcl.codec.base64.toBits(b.iv);if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||typeof a==="string"&&b.iter<=100||b.ts!==64&&b.ts!==96&&b.ts!==128||b.ks!==128&&b.ks!==192&&b.ks!==0x100||!b.iv||b.iv.length<2||b.iv.length>4)throw new sjcl.exception.invalid("json decrypt: invalid parameters");if(typeof a==="string"){c=sjcl.misc.cachedPbkdf2(a,b);a=c.key.slice(0,b.ks/32);b.salt=c.salt}c=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(c, +b.ct,b.iv,b.adata,b.tag);e.c(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+b+":";d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],1)+'"';break;default:throw new sjcl.exception.bug("json encode: unsupported type"); +}}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");if(!a.match(/^\{.*\}$/))throw new sjcl.exception.invalid("json decode: this isn't json!");a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c