JAL-1759 merge from develop
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 8 Jul 2015 14:53:51 +0000 (15:53 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 8 Jul 2015 14:53:51 +0000 (15:53 +0100)
179 files changed:
THIRDPARTYLIBS
build.xml
examples/biojson-doc/.gitignore [new file with mode: 0755]
examples/biojson-doc/LICENSE [new file with mode: 0755]
examples/biojson-doc/README.md [new file with mode: 0755]
examples/biojson-doc/biojsonschema.json [new file with mode: 0644]
examples/biojson-doc/bower.json [new file with mode: 0755]
examples/biojson-doc/css/docson.css [new file with mode: 0755]
examples/biojson-doc/docson-swagger.js [new file with mode: 0755]
examples/biojson-doc/docson.js [new file with mode: 0755]
examples/biojson-doc/index.html [new file with mode: 0755]
examples/biojson-doc/lib/handlebars.js [new file with mode: 0755]
examples/biojson-doc/lib/highlight.js [new file with mode: 0755]
examples/biojson-doc/lib/jquery.js [new file with mode: 0755]
examples/biojson-doc/lib/jsonpointer.js [new file with mode: 0755]
examples/biojson-doc/lib/marked.js [new file with mode: 0755]
examples/biojson-doc/lib/require.js [new file with mode: 0755]
examples/biojson-doc/lib/traverse.js [new file with mode: 0755]
examples/biojson-doc/templates/box.html [new file with mode: 0755]
examples/biojson-doc/templates/signature.html [new file with mode: 0755]
examples/biojson-doc/tests/additionalItems.json [new file with mode: 0755]
examples/biojson-doc/tests/additionalProperties.json [new file with mode: 0755]
examples/biojson-doc/tests/address.json [new file with mode: 0755]
examples/biojson-doc/tests/allOf.json [new file with mode: 0755]
examples/biojson-doc/tests/anyOf.json [new file with mode: 0755]
examples/biojson-doc/tests/basic.json [new file with mode: 0755]
examples/biojson-doc/tests/definitions.json [new file with mode: 0755]
examples/biojson-doc/tests/dependencies.json [new file with mode: 0755]
examples/biojson-doc/tests/enum.json [new file with mode: 0755]
examples/biojson-doc/tests/example2.json [new file with mode: 0755]
examples/biojson-doc/tests/fstab.json [new file with mode: 0755]
examples/biojson-doc/tests/geo.json [new file with mode: 0755]
examples/biojson-doc/tests/invoice.json [new file with mode: 0755]
examples/biojson-doc/tests/items.json [new file with mode: 0755]
examples/biojson-doc/tests/maxItems.json [new file with mode: 0755]
examples/biojson-doc/tests/maxLength.json [new file with mode: 0755]
examples/biojson-doc/tests/maxProperties.json [new file with mode: 0755]
examples/biojson-doc/tests/maximum.json [new file with mode: 0755]
examples/biojson-doc/tests/minItems.json [new file with mode: 0755]
examples/biojson-doc/tests/minLength.json [new file with mode: 0755]
examples/biojson-doc/tests/minProperties.json [new file with mode: 0755]
examples/biojson-doc/tests/minimum.json [new file with mode: 0755]
examples/biojson-doc/tests/multipleOf.json [new file with mode: 0755]
examples/biojson-doc/tests/not.json [new file with mode: 0755]
examples/biojson-doc/tests/oneOf.json [new file with mode: 0755]
examples/biojson-doc/tests/pattern.json [new file with mode: 0755]
examples/biojson-doc/tests/patternProperties.json [new file with mode: 0755]
examples/biojson-doc/tests/properties.json [new file with mode: 0755]
examples/biojson-doc/tests/ref.json [new file with mode: 0755]
examples/biojson-doc/tests/refRemote.json [new file with mode: 0755]
examples/biojson-doc/tests/required.json [new file with mode: 0755]
examples/biojson-doc/tests/schema.json [new file with mode: 0755]
examples/biojson-doc/tests/test.html [new file with mode: 0755]
examples/biojson-doc/tests/type.json [new file with mode: 0755]
examples/biojson-doc/tests/uniqueItems.json [new file with mode: 0755]
examples/biojson-doc/widget.js [new file with mode: 0755]
examples/biojsonschema.json [new file with mode: 0644]
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
src/jalview/analysis/AAFrequency.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/bin/Jalview.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/xdb/embl/EmblEntry.java
src/jalview/ext/varna/RnaModel.java
src/jalview/gui/AlignExportSettings.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/AppVarna.java
src/jalview/gui/AppVarnaBinding.java
src/jalview/gui/CutAndPasteTransfer.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/ProgressBar.java
src/jalview/io/BioJsHTMLOutput.java
src/jalview/io/FileLoader.java
src/jalview/io/IdentifyFile.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/PDBDocFieldPreferences.java
src/jalview/json/binding/v1/BioJSRepositoryPojo.java
src/jalview/renderer/AnnotationRenderer.java
src/jalview/util/ImageMaker.java
src/jalview/util/StringUtils.java
src/jalview/ws/dbsources/PDBRestClient.java
test/MCview/AtomTest.java
test/MCview/BondTest.java
test/MCview/PDBChainTest.java
test/MCview/PDBfileTest.java
test/MCview/ResidueTest.java
test/com/stevesoft/pat/RegexWriterTest.java
test/jalview/analysis/AAFrequencyTest.java
test/jalview/analysis/AlignSeqTest.java
test/jalview/analysis/AlignmentAnnotationUtilsTest.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/analysis/AnnotationSorterTest.java
test/jalview/analysis/CodingUtilsTest.java
test/jalview/analysis/CrossRefTest.java
test/jalview/analysis/DnaTest.java
test/jalview/analysis/GroupingTest.java
test/jalview/analysis/ParsePropertiesTest.java
test/jalview/analysis/RnaTest.java [new file with mode: 0644]
test/jalview/analysis/TestAlignSeq.java
test/jalview/analysis/scoremodels/FeatureScoreModelTest.java
test/jalview/bin/CommandLineOperations.java
test/jalview/commands/EditCommandTest.java
test/jalview/datamodel/AlignedCodonFrameTest.java
test/jalview/datamodel/AlignedCodonIteratorTest.java
test/jalview/datamodel/AlignedCodonTest.java
test/jalview/datamodel/AlignmentAnnotationTests.java
test/jalview/datamodel/AlignmentTest.java
test/jalview/datamodel/ColumnSelectionTest.java
test/jalview/datamodel/DBRefEntryTest.java
test/jalview/datamodel/MappingTest.java
test/jalview/datamodel/PDBEntryTest.java
test/jalview/datamodel/SearchResultsTest.java
test/jalview/datamodel/SeqCigarTest.java
test/jalview/datamodel/SequenceDummyTest.java
test/jalview/datamodel/SequenceTest.java
test/jalview/datamodel/xdb/embl/EmblFileTest.java
test/jalview/ext/jmol/PDBFileWithJmolTest.java
test/jalview/ext/paradise/TestAnnotate3D.java
test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java
test/jalview/ext/rbvi/chimera/ChimeraConnect.java
test/jalview/ext/rbvi/chimera/JalviewChimeraView.java
test/jalview/gui/AlignViewportTest.java
test/jalview/gui/AnnotationChooserTest.java
test/jalview/gui/AppVarnaTest.java [new file with mode: 0644]
test/jalview/gui/FontChooserTest.java
test/jalview/gui/HelpTest.java
test/jalview/gui/JAL1353bugdemo.java
test/jalview/gui/JvSwingUtilsTest.java
test/jalview/gui/PDBSearchPanelTest.java
test/jalview/gui/PaintRefresherTest.java
test/jalview/gui/PopupMenuTest.java
test/jalview/gui/ProgressBarTest.java
test/jalview/gui/SequenceRendererTest.java
test/jalview/gui/StructureChooserTest.java
test/jalview/io/AnnotatedPDBFileInputTest.java
test/jalview/io/AnnotationFileIOTest.java
test/jalview/io/BioJsHTMLOutputTest.java
test/jalview/io/FileIOTester.java
test/jalview/io/Gff3tests.java
test/jalview/io/HtmlFileTest.java
test/jalview/io/JSONFileTest.java
test/jalview/io/Jalview2xmlTests.java
test/jalview/io/NewickFileTests.java
test/jalview/io/PhylipFileTests.java
test/jalview/io/RNAMLfileTest.java
test/jalview/io/StockholmFileTest.java
test/jalview/io/TCoffeeScoreFileTest.java
test/jalview/schemes/DnaCodonTests.java
test/jalview/schemes/ResiduePropertiesTest.java
test/jalview/schemes/ScoreMatrixPrinter.java
test/jalview/structure/Mapping.java
test/jalview/structure/StructureSelectionManagerTest.java
test/jalview/structures/models/AAStructureBindingModelTest.java
test/jalview/util/ColorUtilsTest.java
test/jalview/util/ComparisonTest.java
test/jalview/util/DBRefUtilsTest.java
test/jalview/util/MapListTest.java
test/jalview/util/MappingUtilsTest.java
test/jalview/util/QuickSortTest.java
test/jalview/util/ShiftListTest.java
test/jalview/util/StringUtilsTest.java
test/jalview/viewmodel/styles/ViewStyleTest.java
test/jalview/ws/PDBSequenceFetcherTest.java
test/jalview/ws/dbsources/PDBRestClientTest.java
test/jalview/ws/dbsources/UniprotTest.java
test/jalview/ws/gui/Jws2ParamView.java
test/jalview/ws/jabaws/DisorderAnnotExportImport.java
test/jalview/ws/jabaws/JalviewJabawsTestUtils.java
test/jalview/ws/jabaws/JpredJabaStructExportImport.java
test/jalview/ws/jabaws/MinJabawsClientTests.java
test/jalview/ws/jabaws/RNAStructExportImport.java
test/jalview/ws/jws2/ParameterUtilsTest.java
test/jalview/ws/rest/RestClientTest.java
test/jalview/ws/rest/ShmmrRSBSService.java
test/jalview/ws/seqfetcher/DasSequenceFetcher.java
test/jalview/ws/seqfetcher/DbRefFetcherTest.java
utils/InstallAnywhere/Jalview.iap_xml

index e3237f2..3094939 100644 (file)
@@ -11,7 +11,7 @@ Licencing information for each library is given below:
 
 JGoogleAnalytics_0.3.jar       APL 2.0 License - http://code.google.com/p/jgoogleanalytics/
 Jmol-14.2.14_2015.06.11.jar    GPL/LGPLv2 http://sourceforge.net/projects/jmol/files/
-VARNAv3-91.jar GPL licenced software by K�vin Darty, Alain Denise and Yann Ponty. http://varna.lri.fr
+VARNAv3-93.jar GPL licenced software by K�vin Darty, Alain Denise and Yann Ponty. http://varna.lri.fr
 activation.jar 
 apache-mime4j-0.6.jar
 axis.jar
index f5f89b6..44021ba 100755 (executable)
--- a/build.xml
+++ b/build.xml
     <property name="outputJar" value="jalview.jar" />
     <!-- Jalview Applet JMol Jar Dependency -->
     <property name="jmolJar" value="JmolApplet-14.2.14_2015.06.01.jar" />
-    <property name="varnaJar" value="VARNAv3-91.jar" />
+    <property name="varnaJar" value="VARNAv3-93.jar" />
     <property name="jsoup" value="jsoup-1.8.1.jar" />
     <property name="jsonSimple" value="json_simple-1.1.jar" />
     <property name="javaJson" value="java-json.jar" />
diff --git a/examples/biojson-doc/.gitignore b/examples/biojson-doc/.gitignore
new file mode 100755 (executable)
index 0000000..0736b15
--- /dev/null
@@ -0,0 +1,3 @@
+.idea
+docson.iml
+/nbproject
diff --git a/examples/biojson-doc/LICENSE b/examples/biojson-doc/LICENSE
new file mode 100755 (executable)
index 0000000..e06d208
--- /dev/null
@@ -0,0 +1,202 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/examples/biojson-doc/README.md b/examples/biojson-doc/README.md
new file mode 100755 (executable)
index 0000000..2b1de69
--- /dev/null
@@ -0,0 +1,114 @@
+<p align='right'>A <a href="http://www.swisspush.org">swisspush</a> project <a href="http://www.swisspush.org" border=0><img align="top"  src='https://1.gravatar.com/avatar/cf7292487846085732baf808def5685a?s=32'></a></p>
+Docson
+======
+
+Documentation for your JSON types.
+
+Give Docson a JSON schema and it will generate a [beautiful documentation](http://lbovet.github.io/docson/index.html#/docson/examples/example.json).
+
+## Features
+* [JSON schema](http://json-schema.org/) v4 keywords.
+* Runs entirely in the browser.
+* Render schema descriptions with markdown
+
+## Installation
+
+* Place the Docson distribution on the web server serving the schemas (to avoid cross-origin issues).
+
+## Usage
+
+* Open [index.html](http://lbovet.github.io/docson/index.html) and enter the schema path in the form field.
+* Or give the schema path directly as hash parameter: [index.html#/docson/examples/example.json](http://lbovet.github.io/docson/index.html#/docson/examples/example.json)
+
+Note that you can refer to a sub-schema by adding a json-pointer path as 'dollar-parameter': [index.html#/docson/examples/example.json$items](http://lbovet.github.io/docson/index.html#/docson/examples/example.json$items)
+
+## Typson
+
+You can directly reference your JSON types defined as TypeScript interfaces. If the path ends with `.ts`, Docson will use [Typson](https://github.com/lbovet/typson) to convert the Type Scripts to schema in order to generate the documentation.
+
+For example, [index.html#/typson/example/invoice/line.ts$InvoiceLine](http://lbovet.github.io/docson/index.html#/typson/example/invoice/line.ts$InvoiceLine) is the documentation of [line.ts](https://github.com/lbovet/typson/blob/master/example/invoice/line.ts).
+
+You need to install [Typson](https://github.com/lbovet/typson) by yourself on your server. It must be in a directory named `typson` located at the same level as the `docson` directory.
+
+## Widget
+
+To include a Docson schema documentations on any page (wiki, ...) without worrying about messing up with javascript libraries and cross-origin issues:
+
+* Install Docson somewhere as described above.
+* Place the following `script` tags in the including page, nothing else is needed:
+
+```
+<script src="http://somewhere/path-to-docson/widget.js" data-schema="/path-to-schema">
+</script>
+```
+
+See the [widget example](http://jsfiddle.net/3kXu2/3/) on jsfiddle.
+
+## Swagger
+
+You can adapt [Swagger UI](https://github.com/wordnik/swagger-ui) to display Docson-generated model documentation instead of the builtin signatures.
+
+See how it looks like in the [Swagger Docson example](http://lbovet.github.io/swagger-ui/dist/index.html)
+
+In Swagger UI's `index.html`, include the [Swagger integration script after other script tags](https://github.com/lbovet/swagger-ui/blob/3f37722b03db6c48cc2a8460df26dda5f4d6f8e4/src/main/html/index.html#L19):
+```
+  <script src='/path-to-docson/docson-swagger.js' type='text/javascript'></script>
+```
+
+Also, you will need a patched version of [Swagger Client](https://github.com/lbovet/swagger-js/blob/models-exposed/lib/swagger.js) so that the raw json-schema model is visible from Docson. Either replace the `swagger.js` file in your Swagger UI disctribution or take it directly from github by replacing
+
+```
+   <script src='/lib/swagger.js' type='text/javascript'></script>
+```
+
+with 
+
+```
+  <script src='https://raw2.github.com/lbovet/swagger-js/models-exposed/lib/swagger.js' type='text/javascript'></script>
+```
+
+For a better layout of parameter models, you may [want to change the width of some elements](https://github.com/lbovet/swagger-ui/blob/3f37722b03db6c48cc2a8460df26dda5f4d6f8e4/src/main/html/index.html#L20-L27):
+
+```
+  <style>
+      .swagger-ui-wrap {
+          max-width: 1200px;
+      }
+      .swagger-ui-wrap .body-textarea {
+          width: 200px;
+      }
+  </style>
+```
+
+## Integration
+
+You can also integrate Docson in your application and use its javascript API:
+
+```javascript
+docson.doc(element, schema, ref)
+```
+
+* `element` is the element which will host the documentation. Either a DOM element (id or object) or jQuery element.
+* `schema` is the URI or path to the schema or a string containing the schema source itself.
+* `ref` is an optional json-pointer path to a sub-schema.
+
+Examples:
+* [Simple integration example](http://lbovet.github.io/docson/examples/example.html)
+* [See it in action](http://lbovet.github.io/typson-demo/) with its buddy [typson](https://github.com/lbovet/typson).
+
+## Limitations
+
+* Mixing unrelated keywords can lead to unexpected results.
+
+Not implemented:
+* Non-primitive values in enums and default values
+* dependencies, additionalItems, patternProperties
+
+## Development
+
+* [All tests](http://lbovet.github.io/docson/tests/test.html)
+
+Please pull-request your failing schemas in the `tests/` folder and open an issue describing the expected result.
+
+[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lbovet/docson/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
+
diff --git a/examples/biojson-doc/biojsonschema.json b/examples/biojson-doc/biojsonschema.json
new file mode 100644 (file)
index 0000000..e6670d2
--- /dev/null
@@ -0,0 +1 @@
+{"$schema":"http://json-schema.org/draft-04/schema#","id":"http://jsonschema.net","type":"object","properties":{"seqs":{"id":"http://jsonschema.net/seqs","type":"array","items":{"id":"http://jsonschema.net/seqs/0","type":"object","properties":{"name":{"id":"http://jsonschema.net/seqs/0/name","type":"string","description":"Sequence name"},"start":{"id":"http://jsonschema.net/seqs/0/start","type":"integer","description":"Start residue position"},"svid":{"id":"http://jsonschema.net/seqs/0/svid","type":"string","description":"Serial version id for sequence object"},"end":{"id":"http://jsonschema.net/seqs/0/end","type":"integer","description":"End residue position"},"id":{"id":"http://jsonschema.net/seqs/0/id","type":"string","description":"Sequence unique identifier"},"seq":{"id":"http://jsonschema.net/seqs/0/seq","type":"string","description":"Sequence Residues"},"order":{"id":"http://jsonschema.net/seqs/0/order","type":"integer","description":"Sequence order in an alignment space"}},"required":["name","start","svid","end","id","seq"]},"required":["0"],"description":"Sequences in the Alignemnt","minItems":"1","maxItems":"*"},"appSettings":{"id":"http://jsonschema.net/appSettings","type":"object","properties":{"globalColorScheme":{"id":"http://jsonschema.net/appSettings/globalColorScheme","type":"string","description":"Global colour schem for the alignment"},"webStartUrl":{"id":"http://jsonschema.net/appSettings/webStartUrl","type":"string","description":"Jalview specific setting which points to a url for launching Jalview"},"application":{"id":"http://jsonschema.net/appSettings/application","type":"string","description":"Application which generated the Json"},"showSeqFeatures":{"id":"http://jsonschema.net/appSettings/showSeqFeatures","type":"string","description":"Determines if sequence features are visible or not"},"version":{"id":"http://jsonschema.net/appSettings/version","type":"string","description":"Verion of the application which generated the JSON"},"hiddenCols":{"id":"http://jsonschema.net/appSettings/hiddenCols","type":"string","description":"Delimited lists of hidden colums ranges i.e [2-3,5-5,11-23]"}},"description":"Application specific settings"},"seqGroups":{"id":"http://jsonschema.net/seqGroups","type":"array","items":[{"id":"http://jsonschema.net/seqGroups/0","type":"object","properties":{"displayText":{"id":"http://jsonschema.net/seqGroups/0/displayText","type":"boolean","description":"Determines if the texts of the group is displayed or not"},"startRes":{"id":"http://jsonschema.net/seqGroups/0/startRes","type":"integer","description":"Start residue position for a given group"},"groupName":{"id":"http://jsonschema.net/seqGroups/0/groupName","type":"string","description":"Group name"},"endRes":{"id":"http://jsonschema.net/seqGroups/0/endRes","type":"integer","description":"End residue position for a given group"},"colourText":{"id":"http://jsonschema.net/seqGroups/0/colourText","type":"boolean","description":"Determines if the Residues text for the group is coloured"},"seqsHash":{"id":"http://jsonschema.net/seqGroups/0/seqsHash","type":"array","items":[],"minItems":"0","maxItems":"*","description":"The id's of the sequences which belongs to the group"},"svid":{"id":"http://jsonschema.net/seqGroups/0/svid","type":"string","description":"Serial version id for a given group"},"showNonconserved":{"id":"http://jsonschema.net/seqGroups/0/showNonconserved","type":"boolean","description":"Determines if non conserved regions of a group is shown or not"},"colourScheme":{"id":"http://jsonschema.net/seqGroups/0/colourScheme","type":"string","description":"Colour Scheme for the sequence group"},"displayBoxes":{"id":"http://jsonschema.net/seqGroups/0/displayBoxes","type":"boolean","description":"Determines if the group border should be visible or not"}}}],"description":"Sequence groups in the Alignment","minItems":"0","maxItems":"*"},"alignAnnotation":{"id":"http://jsonschema.net/alignAnnotation","type":"array","items":{"id":"http://jsonschema.net/alignAnnotation/0","type":"object","properties":{"svid":{"id":"http://jsonschema.net/alignAnnotation/0/svid","type":"string","description":"Serial version id for the annotation object"},"annotations":{"id":"http://jsonschema.net/alignAnnotation/0/annotations","type":"array","items":[{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0","type":"object","properties":{"displayCharacter":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0/displayCharacter","type":"string","description":"Display character to denote the given annotation"},"value":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0/value","type":"integer","description":"Value of the annotation"},"secondaryStructure":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0/secondaryStructure","type":"string","description":"Secondary structure symbol for the given annotation"}}},{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1","type":"object","properties":{"displayCharacter":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1/displayCharacter","type":"string"},"value":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1/value","type":"integer"},"secondaryStructure":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1/secondaryStructure","type":"string"}}},{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2","type":"object","properties":{"displayCharacter":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2/displayCharacter","type":"string"},"value":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2/value","type":"integer"},"secondaryStructure":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2/secondaryStructure","type":"string"}}}]},"description":{"id":"http://jsonschema.net/alignAnnotation/0/description","type":"string","description":"Description of the Alignment Annotation"},"label":{"id":"http://jsonschema.net/alignAnnotation/0/label","type":"string","description":"Label for the Annotation"}}},"description":"Alignment Annotations","minItems":"0","maxItems":"*"},"svid":{"id":"http://jsonschema.net/svid","type":"string","description":"Serial version id"},"seqFeatures":{"id":"http://jsonschema.net/seqFeatures","type":"array","items":{"id":"http://jsonschema.net/seqFeatures/0","type":"object","properties":{"fillColor":{"id":"http://jsonschema.net/seqFeatures/0/fillColor","type":"string","description":"Fill colour"},"score":{"id":"http://jsonschema.net/seqFeatures/0/score","type":"integer","description":"Score"},"sequenceRef":{"id":"http://jsonschema.net/seqFeatures/0/sequenceRef","type":"string","description":"Reference to the Sequence in the alignement (More like a foreign key)"},"featureGroup":{"id":"http://jsonschema.net/seqFeatures/0/featureGroup","type":"string","description":"Feature Group"},"svid":{"id":"http://jsonschema.net/seqFeatures/0/svid","type":"string","description":"Serial version id for the SeqFeature object"},"description":{"id":"http://jsonschema.net/seqFeatures/0/description","type":"string","description":"Description of Feature"},"xStart":{"id":"http://jsonschema.net/seqFeatures/0/xStart","type":"integer","description":"Start residue position for the sequence feature"},"xEnd":{"id":"http://jsonschema.net/seqFeatures/0/xEnd","type":"integer","description":"End residue position for the sequence feature"},"type":{"id":"http://jsonschema.net/seqFeatures/0/type","type":"string","description":"The name of the SequenceFeature"}}},"minItems":"0","maxItems":"*","description":"Sequence Features within the alignment"}},"required":["seqs","appSettings","seqGroups","alignAnnotation","svid","seqFeatures"]}
\ No newline at end of file
diff --git a/examples/biojson-doc/bower.json b/examples/biojson-doc/bower.json
new file mode 100755 (executable)
index 0000000..847e758
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "name": "docson",
+  "main": "docson.js",
+  "version": "0.1.0",
+  "homepage": "https://github.com/lbovet/docson",
+  "authors": [
+    "Laurent Bovet <laurent.bovet@windmaster.ch>"
+  ],
+  "description": "Documentation for your JSON types",
+  "moduleType": [
+    "amd"
+  ],
+  "keywords": [
+    "docson",
+    "json"
+  ],
+  "license": "http://www.apache.org/licenses/LICENSE-2.0",
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "test",
+    "tests"
+  ]
+}
diff --git a/examples/biojson-doc/css/docson.css b/examples/biojson-doc/css/docson.css
new file mode 100755 (executable)
index 0000000..1627e74
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2013 Laurent Bovet <laurent.bovet@windmaster.ch>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.docson {
+    font-family: 'Droid Sans',verdana,helvetica;
+    font-size: 13px;
+    padding: 6px 6px 6px 6px;
+}
+
+.docson .box {
+    position: relative;
+    float: left;
+    background-color:rgba(255, 255, 255, 0.2);
+    border: 1px solid lightgrey;
+    border-radius: 4px;
+    -moz-border-radius: 4px;
+    -webkit-border-radius: 4px;
+    padding-bottom: 10px;
+    -moz-box-shadow:    1px 1px 1px darkgray;
+    -webkit-box-shadow: 1px 1px 1px darkgray;
+    box-shadow:         1px 1px 1px darkgray;
+}
+
+.docson .expand-button {
+    position: absolute;
+    margin-top: 15px;
+    padding: 2px;
+    right: 2px;
+    float: right;
+    width: 18px;
+    text-align: center;
+    cursor: pointer;
+    font-weight: bold;
+    font-family: "Lucida Console", Monaco, monospace;
+    font-size: 15px;
+    color: darkgray;
+    display: none;
+}
+
+.docson .source-button {
+    position: absolute;
+    padding: 2px;
+    margin-top: 3px;
+    right: 2px;
+    width: 18px;
+    text-align: center;
+    cursor: pointer;
+    font-weight: bold;
+    font-family: "Lucida Console", Monaco, monospace;
+    font-size: 10px;
+    color: darkgray;
+    display: none;
+}
+
+.docson .box-header {
+    padding: 6px;
+    border-top-left-radius: 4px;
+    border-top-right-radius: 4px;
+    border-bottom: 1px solid lightgrey;
+    background: whitesmoke;
+   /* background: -webkit-linear-gradient(ghostwhite, whitesmoke, whitesmoke, gainsboro);
+    background: -moz-linear-gradient(ghostwhite, whitesmoke, whitesmoke, gainsboro);*/
+}
+
+.docson .box-name {
+    color: darkblue;
+    padding: 5px 40px 2px 3px;
+    float: left;
+}
+
+.docson .title {
+    margin-top: -3px;
+    font-size: 120%;
+}
+
+.docson .link {
+    text-decoration: underline;
+}
+
+.docson .box-description {
+    color: dimgray;
+    float: left;
+    max-width: 600px;
+}
+
+.docson .end {
+    clear: both;
+}
+
+.docson .error {
+    color: red;
+}
+
+.docson .box-body {
+    padding-left: 10px;
+    padding-right: 28px;
+    clear: both;
+}
+
+.docson .box-1 {
+    background: whitesmoke;/*
+    background: -webkit-linear-gradient(ghostwhite, whitesmoke, whitesmoke, gainsboro);
+    background: -moz-linear-gradient(ghostwhite, whitesmoke, whitesmoke, gainsboro);*/
+}
+
+.docson .box-2 {
+    background: beige;
+    background: -webkit-linear-gradient(white, beige, beige, gainsboro);
+    background: -moz-linear-gradient(white, beige, beige, gainsboro);
+}
+
+.docson .box-3 {
+    background: azure;
+    background: -webkit-linear-gradient(white, azure, azure, gainsboro);
+    background: -moz-linear-gradient(white, azure, azure, gainsboro);
+}
+
+.docson .box-4 {
+    background: mistyrose;
+    background: -webkit-linear-gradient(white, mistyrose, mistyrose, gainsboro);
+    background: -moz-linear-gradient(white, mistyrose, mistyrose, gainsboro);
+}
+
+.docson .box-5 {
+    background: #ddffee;
+    background: -webkit-linear-gradient(white, #ddffee, #ddffee, gainsboro);
+    background: -moz-linear-gradient(white, #ddffee, #ddffee, gainsboro);
+}
+
+.docson .box-6 {
+    background: ghostwhite;
+    background: -webkit-linear-gradient(white, ghostwhite, ghostwhite, gainsboro);
+    background: -moz-linear-gradient(white, ghostwhite, ghostwhite, gainsboro);
+}
+
+.docson .box-7 {
+    background: lavenderblush;
+    background: -webkit-linear-gradient(white, lavenderblush, lavenderblush, gainsboro);
+    background: -moz-linear-gradient(white, lavenderblush, lavenderblush, gainsboro);
+}
+
+.docson .box-8 {
+    background: aliceblue;
+    background: -webkit-linear-gradient(white, aliceblue, aliceblue, gainsboro);
+    background: -moz-linear-gradient(white, aliceblue, aliceblue, gainsboro);
+}
+
+.docson .signature {
+    padding-top: 5px;
+    clear: left;
+}
+
+.docson .signature-header {
+    padding-top: 3px;
+    height: 20px;
+    line-height: 24px;
+}
+
+.docson .property-name {
+    float: left;
+    font-family: "Lucida Console", Monaco, monospace;
+    min-width: 130px;
+}
+
+.docson .required {
+    font-weight: bold;
+}
+
+.docson .type-keyword {
+    font-size: 80%;
+    color: dimgray;
+}
+
+.docson .type-pattern {
+    font-size: 80%;
+    color: darkolivegreen;
+}
+
+.docson .type-enum {
+    color: purple;
+    font-family: "Lucida Console", Monaco, monospace;
+    font-size: 90%;
+    padding-left: 2px;
+}
+
+.docson .type-default {
+    color: darkslategrey;
+    font-family: "Lucida Console", Monaco, monospace;
+}
+
+.docson .signature-type {
+    padding-left: 6px;
+    float: left;
+    min-width: 160px;
+    max-width: 320px;
+}
+
+.docson .signature-type-any {
+    font-style: italic;
+}
+
+.docson .signature-type-string {
+    color: green;
+}
+
+.docson .signature-type-number {
+    color: blue;
+}
+
+.docson .signature-type-integer {
+    color: blueviolet;
+}
+
+.docson .signature-type-boolean {
+    color: orangered;
+}
+
+.docson .signature-type-null {
+    font-family: "Lucida Console", Monaco, monospace;
+    color: red;
+}
+
+.docson .signature-type-date-time {
+    color: #224466;
+}
+
+.docson .signature-type-email {
+    color: cornflowerblue;
+}
+
+.docson .signature-type-hostname {
+    color: darkolivegreen;
+}
+
+.docson .signature-type-ipv6 {
+    color: darkslateblue;
+}
+
+.docson .signature-type-ipv4 {
+    color: indigo;
+}
+
+.docson .signature-type-uri {
+    color: saddlebrown;
+}
+
+.docson .signature-button {
+    cursor: pointer;
+    padding: 4px;
+    border-radius: 4px;
+    border: 1px solid lightgrey;
+    -moz-border-radius: 4px;
+}
+
+.docson .signature-type-ref {
+    color: darkblue;
+}
+
+.docson .signature-type-expanded {
+    padding: 5px 3px 3px 5px;
+    vertical-align: -1px;
+    opacity: 0.6;
+    -moz-box-shadow:    inset 1px 1px 1px darkgray;
+    -webkit-box-shadow: inset 1px 1px 1px darkgray;
+    box-shadow:         inset 1px 1px 1px darkgray;
+}
+
+.docson .signature-description {
+    padding-left: 6px;
+    padding-bottom: 3px;
+    color: dimgray;
+    float: left;
+    max-width: 600px;
+}
+
+.docson .signature-box-container {
+    clear: both;
+}
+
+.docson .box-container {
+    float: left;
+    padding-top: 4px;
+    padding-left: 8px;
+    display: none;
+}
+
+.docson .button {
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.docson .button:hover {
+    color: darkred;
+}
+
+@media all and (min-width: 20px) and (max-width: 500px) {
+    .docson .property-name {
+        min-width: 0px;
+    }
+    .docson .signature-type {
+        min-width: 0px;
+    }
+    .docson .source {
+        width: 100%;
+    }
+    .docson .box-container, .box {
+        width: 100%;
+    }
+}
+
+.docson .desc {
+    padding-left: 6px;
+    line-height: 17px;
+}
+
+.docson .desc p {
+    margin: 0;
+    margin-top: 3px;
+}
+
+.docson .desc code {
+    color: purple;
+    font-size: 90%;
+}
+
+
+.docson .desc table {
+    margin-top: 4px;
+    margin-bottom: 4px;
+    border-collapse:collapse;
+}
+
+.docson .desc th {
+    padding: 0px;
+    font-size: 80%;
+    font-weight: normal;
+    background: rgba(231, 231, 231, 0.5);
+}
+
+.docson .desc table,
+.docson .desc th,
+.docson .desc td {
+    border: 1px solid lightgrey;
+}
+
+.docson .desc tr > td {
+    padding: 2px 6px 2px 6px;
+}
+
+.docson .desc h1 {
+    font-size: 120%;
+    margin-top: 0px;
+    margin-bottom: 3px;
+}
+
+.docson .desc h2 {
+    font-size: 115%;
+    margin-top: 0px;
+    margin-bottom: 3px;
+}
+
+.docson .desc h3 {
+    font-size: 110%;
+    margin-top: 0px;
+    margin-bottom: 3px;
+}
+
+.docson .desc h4 {
+    font-size: 100%;
+    margin-top: 0px;
+    margin-bottom: 3px;
+}
+
+.docson .desc pre {
+    border: 1px solid lightgray;
+    border-radius: 4px;
+    -webkit-border-radius: 4px;
+    -moz-border-radius: 4px;
+    padding: 4px;
+    margin: 3px 0px;
+}
+
+.docson .desc ul {
+    padding-left: 16px;
+    margin: 3px 0px;
+}
+
+.docson .desc ol {
+    padding-left: 16px;
+    margin: 3px 0px;
+}
+
+.docson .desc blockquote {
+    margin: 3px 0px 3px 10px;
+    padding-left: 6px;
+    border-left: 1px solid lightgray;
+}
+
+.docson .desc img {
+    vertical-align: middle;
+}
+
+.docson .desc hr {
+    border: 0;
+    height: 1px;
+    background: lightgrey;
+    color: lightgrey;
+    width: 50%;
+}
+
+.docson .source {
+    display: none;
+    padding-left: 16px;
+    padding-right: 30px;
+}
+
+.docson .json, .json-schema {
+    font-family: "Lucida Console", Monaco, monospace;
+}
+
+.docson .json-punctuation {
+    font-weight: bold;
+}
+
+.docson .json-null, .json-true, .json-false {
+    font-weight: bold;
+}
+
+.docson .json-true {
+    color: #080;
+}
+
+.docson .json-false {
+    color: #800;
+}
+
+.docson .json-object-key {
+    color: #246;
+}
+
+.docson .json-keyword {
+    font-weight: bold;
+    color: #036;
+}
+
+.docson .json-number {
+    font-weight: bold;
+    color: #660;
+}
+
+.docson .json-string {
+    color: #800;
+}
+
+.docson .json-schema-map > .json-object-key {
+    color: #080;
+    font-style: italic;
+}
+
diff --git a/examples/biojson-doc/docson-swagger.js b/examples/biojson-doc/docson-swagger.js
new file mode 100755 (executable)
index 0000000..80ffaaf
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 Laurent Bovet <laurent.bovet@windmaster.ch>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var allDefinitions;
+var counter = 0;
+
+function createDoc(definitions, type) {
+    counter++;
+    var docson = "/docson/index.html";
+    if(!allDefinitions) {
+        allDefinitions = {};
+        function receiveMessage(event) {
+            if (event.data.id && event.data.id == "docson") {
+                var frame = $("#docson-"+event.data.url.split("$")[2]);
+                if (event.data.action == "resized") {
+                    frame.get(0).width = event.data.width + 18;
+                    frame.get(0).height = event.data.height + 36;
+                    frame.parents("td").width(event.data.width + 24)
+                }
+                if (event.data.action == "ready") {
+                    frame.get(0).contentWindow.postMessage({ id: "docson", action: "load", definitions: allDefinitions, type: event.data.url.split("$")[1]}, "*");
+                }
+            }
+        }
+        window.addEventListener("message", receiveMessage, false);
+    }
+    $.extend(allDefinitions, definitions);
+    return "<iframe class='docson-frame' id='docson-" + counter + "' style='padding: 0; border: 0; width:100%; background: transparent' src='" + docson + "#$" + type + "$"+counter+"'></iframe>"
+}
+
+SwaggerOperation.prototype.getSignature = function(type, models) {
+    var collectionType, isPrimitive;
+    if(this.isCollectionType) {
+        collectionType = this.isCollectionType(type);
+    } else {
+        collectionType = this.isListType(type);
+    }
+    isPrimitive = ((collectionType != null) && models[collectionType]) || (models[type] != null) ? false : true;
+    if (isPrimitive) {
+        return type;
+    } else {
+        if (collectionType != null) {
+            return "<p class='stronger'>" + type + "</p>" + createDoc(this.resource.rawModels, collectionType);
+        } else {
+            return createDoc(this.resource.rawModels, type);
+        }
+    }
+};
+
+$(document).on("click", ".toggleOperation", function() {
+    $(this).parents(".operations").find(".docson-frame").each(function(k, frame) {
+        frame.contentWindow.postMessage({id: "docson", action: "resize"}, "*");
+    });
+});
diff --git a/examples/biojson-doc/docson.js b/examples/biojson-doc/docson.js
new file mode 100755 (executable)
index 0000000..93d7d09
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * Copyright 2013 Laurent Bovet <laurent.bovet@windmaster.ch>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var docson = docson || {};
+
+docson.templateBaseUrl="templates";
+
+define(["lib/jquery", "lib/handlebars", "lib/highlight", "lib/jsonpointer", "lib/marked", "lib/traverse"], function(jquery, handlebars, highlight, jsonpointer, marked) {
+
+    var ready = $.Deferred();
+    var boxTemplate;
+    var signatureTemplate;
+    var source;
+    var stack = [];
+    var boxes=[];
+
+    Handlebars.registerHelper('scope', function(schema, options) {
+        var result;
+        boxes.push([]);
+        if(schema && (schema.id || schema.root)) {
+            stack.push( schema );
+            result = options.fn(this);
+            stack.pop();
+        } else {
+            result = options.fn(this);
+        }
+        boxes.pop();
+        return result;
+    });
+
+    Handlebars.registerHelper('source', function(schema) {
+        delete schema.root;
+        delete schema.__boxId;
+        delete schema.__name;
+        delete schema.__ref;
+        return JSON.stringify(schema, null, 2);
+    });
+
+    Handlebars.registerHelper('desc', function(schema) {
+        var description = schema.description;
+
+        if( !description ) return "";
+        var text = description;
+        if(marked) {
+            marked.setOptions({gfm: true, breaks: true})
+            return new Handlebars.SafeString(marked(text));
+        } else {
+            return text;
+        }
+    });
+
+    Handlebars.registerHelper('equals', function(lvalue, rvalue, options) {
+        if (arguments.length < 3)
+            throw new Error("Handlebars Helper equals needs 2 parameters");
+        if( lvalue!=rvalue ) {
+            return options.inverse(this);
+        } else {
+            return options.fn(this);
+        }
+    });
+
+    Handlebars.registerHelper('contains', function(arr, item, options) {;
+        if(arr && arr instanceof Array && arr.indexOf(item) != -1) {
+            return options.fn(this);
+        }
+    });
+
+    Handlebars.registerHelper('primitive', function(schema, options) {
+        if(schema.type && schema.type != "object" && schema.type != "array" || schema.enum) {
+            return withType(this, options, true)
+        }
+    });
+
+    Handlebars.registerHelper('exists', function(value, options) {
+        if(value !== undefined) {
+            value = value === null ? "null": value;
+            value = value === true ? "true": value;
+            value = value === false ? "false": value;
+            value = typeof value === "object" ? JSON.stringify(value): value;
+            this.__default = value;
+            var result = options.fn(this);
+            delete this.__default;
+            return result;
+        }
+    });
+
+    Handlebars.registerHelper('range', function(from, to, replFrom, replTo, exclFrom, exclTo, sep) {
+        var result = "";
+        if(from !== undefined || to !== undefined) {
+            result += exclFrom ? "]" : "[";
+            result += from !== undefined ? from : replFrom;
+            if( (from || replFrom) !== (to || replTo)) {
+                result += (from !== undefined || replFrom !== null) && (to !== undefined || replTo !== null) ? sep : "";
+                result += to !== undefined ? to : replTo;
+            }
+            result += exclTo ? "[" : "]";
+            return result;
+        }
+    });
+
+    var sub = function(schema) {
+        return schema.type == "array" || schema.allOf || schema.anyOf || schema.oneOf || schema.not;
+    }
+
+    Handlebars.registerHelper('sub', function(schema, options) {
+        if(sub(schema) || (schema.type && schema.type != "object" && schema.type != "array") || schema.enum) {
+            return options.fn(this);
+        }
+    });
+
+    Handlebars.registerHelper('main', function(schema, options) {
+        if(!sub(schema)) {
+            return options.fn(this);
+        }
+    });
+
+    var simpleSchema = function(schema) {
+        var result = schema.description===undefined && schema.title===undefined && schema.id===undefined;
+        result &= schema.properties===undefined;
+        return result;
+    };
+
+    Handlebars.registerHelper('simple', function(schema, options) {
+        if(simpleSchema(schema) && !schema.$ref) {
+            return withType(schema, options, true);
+        }
+    });
+
+    var withType = function(schema, options, hideAny) {
+        schema.__type = schema.type;
+        if(!schema.type && !hideAny) {
+            schema.__type="any";
+        }
+        if(schema.format) {
+            schema.__type=schema.format;
+        }
+        if( (schema.__type == "any" || schema.__type == "object") && schema.title) {
+            schema.__type = schema.title;
+        }
+        var result = options.fn(schema);
+        delete schema.__type;
+        return result;
+    }
+
+    Handlebars.registerHelper('complex', function(schema, options) {
+        if(!simpleSchema(schema) && !schema.$ref || schema.properties) {
+            return withType(schema, options);
+        }
+    });
+
+    Handlebars.registerHelper('enum', function(schema) {
+        if(schema.enum) {
+            return (schema.enum.length > 1) ? "enum": "constant";
+        }
+    });
+
+    Handlebars.registerHelper('obj', function(schema, options) {
+        if(schema.properties || schema.type == "object") {
+            return withType(schema, options);
+        }
+    });
+
+    var pushBox = function(schema) {
+        boxes[boxes.length-1].push(schema);
+    }
+
+    Handlebars.registerHelper('box', function(schema, options) {
+        if(schema) {
+            pushBox(schema);
+            return options.fn(schema);
+        }
+    });
+
+    Handlebars.registerHelper('boxId', function() {
+        return boxes[boxes.length-1].length
+    });
+
+    Handlebars.registerHelper('boxes', function(options) {
+        var result="";
+        $.each(boxes[boxes.length-1], function(k, box) {
+            box.__boxId = k+1;
+            result=result+options.fn(box);
+        });
+        boxes[boxes.length-1] = []
+        return result;
+    });
+
+    var resolveIdRef = function(ref) {
+        if(stack) {
+            var i;
+            for(i=stack.length-1; i>=0; i--) {
+                if(stack[i][ref]) {
+                    return stack[i][ref];
+                }
+            }
+        }
+        return null;
+    }
+
+    var resolvePointerRef = function(ref) {
+        var root = stack[1];
+        if(ref=="#") {
+            return root;
+        }
+        try {
+            return jsonpointer.get(stack[1], ref);
+        } catch(e) {
+            console.log(e);
+            return null;
+        }
+    }
+
+    var resolveRef = function(ref) {
+        if(ref.indexOf("#") == 0) {
+            return resolvePointerRef(ref);
+        } else {
+            return resolveIdRef(ref);
+        }
+    }
+
+    var getName = function(schema) {
+        if(!schema) {
+            return "<error>";
+        }
+        var name = schema.title;
+        name = !name && schema.id ? schema.id: name;
+        name = !name ? schema.__name: name;
+        return name;
+    }
+
+    Handlebars.registerHelper('name', function(schema, options) {
+        schema.__name = getName(schema);
+        if(schema.__name) {
+            return options.fn(schema);
+        }
+    });
+
+    var refName = function(ref) {
+        var name = getName(resolveRef(ref));
+        if(!name) {
+            if(ref == "#") {
+                name = "<root>";
+            } else {
+                name = ref.replace("#", "/")
+            }
+        }
+        var segments = name.split("/");
+        name = segments[segments.length-1];
+        return name;
+    }
+
+    function renderSchema(schema) {
+        if(stack.indexOf(schema) == -1) { // avoid recursion
+            stack.push(schema);
+            var ret = new Handlebars.SafeString(boxTemplate(schema));
+            stack.pop();
+            return ret;
+        } else {
+            return new Handlebars.SafeString(boxTemplate({"description": "_circular reference_"}));
+        }
+    }
+
+    Handlebars.registerHelper('ref', function(schema, options) {
+        if(schema.$ref) {
+            var target = resolveRef(schema.$ref);
+            if(target) {
+                target.__name = refName(schema.$ref);
+                target.__ref = schema.$ref.replace("#", "");
+            }
+            var result;
+            if(target) {
+                result = options.fn(target);
+            } else {
+                result = new Handlebars.SafeString("<span class='signature-type-ref'>"+schema.$ref+"</span>");
+            }
+            if(target) {
+                delete target.__ref;
+            }
+            return result;
+        }
+    });
+
+    Handlebars.registerHelper('schema', function(schema) {
+        return renderSchema(schema);
+    });
+
+    Handlebars.registerHelper('signature', function(schema, keyword, schemas) {
+        if(!schemas) {
+            schemas = []
+        }
+        schemas = schemas instanceof Array ? schemas : [schemas];
+        return new Handlebars.SafeString(signatureTemplate({ schema: schema, keyword: keyword, schemas: schemas}));
+    });
+
+    Handlebars.registerHelper('l', function(context) {
+        console.log(context);
+    });
+
+    function init() {
+        $.when( $.get(docson.templateBaseUrl+"/box.html").done(function(content) {
+            source = content
+            boxTemplate = Handlebars.compile(source);
+        }), $.get(docson.templateBaseUrl+"/signature.html").done(function(content) {
+            source = content
+            signatureTemplate = Handlebars.compile(source);
+        })).always(function() {
+            ready.resolve();
+        });
+    };
+
+    docson.doc = function(element, schema, ref, baseUrl) {
+        var d = $.Deferred();
+        if(baseUrl === undefined) baseUrl='';
+        init();
+        ready.done(function() {
+            if(typeof element == "string") {
+                element = $("#"+element);
+            }
+            if(typeof schema == "string") {
+                schema = JSON.parse(schema);
+            }
+
+            var refsPromise = $.Deferred().resolve().promise();
+            var refs = {};
+
+
+            var renderBox = function() {
+                stack.push(refs);
+                var target = schema;
+                if(ref) {
+                    ref = ref[0] !== '/' ? '/'+ref : ref;
+                    target = jsonpointer.get(schema, ref);
+                    stack.push( schema );
+                }
+                target.root = true;
+                target.__ref = "<root>";
+                var html = boxTemplate(target);
+
+                if(ref) {
+                    stack.pop();
+                }
+                stack.pop();
+
+                element.addClass("docson").html(html);
+
+                var resizeHandler = element.get(0).onresize;
+                function resized() {
+                    if(resizeHandler) {
+                        var box = element.find(".box").first();
+                        element.get(0).onresize(box.outerWidth(), box.outerHeight());
+                    }
+                }
+                element.get(0).resized = resized;
+                resized();
+
+                if(highlight) {
+                    element.find(".json-schema").each(function(k, schemaElement) {
+                        highlight.highlightSchema(schemaElement);
+                    });
+                }
+                element.find(".box-title").each(function() {
+                   var ref = $(this).attr("ref");
+                   if(ref) {
+                       if(window.location.href.indexOf("docson/index.html") > -1) {
+                           $(this).find(".box-name").css("cursor", "pointer").attr("title", "Open in new window")
+                           .hover(
+                               function(){ $(this).addClass('link') },
+                               function(){ $(this).removeClass('link') })
+                           .click(function() {
+                                var url = window.location.href+"$$expand";
+                                if(ref !=="<root>") {
+                                   url = url.replace(/(docson\/index.html#[^\$]*).*/, "$1$"+ref+"$$expand");
+                                }
+                                var w;
+                                function receiveMessage(event) {
+                                   if (event.data.id && event.data.id == "docson" && event.data.action == "ready") {
+                                       w.postMessage({ id: "docson", action: "load", definitions: schema, type: event.data.url.split("$")[1], expand: true}, "*");
+                                   }
+                                }
+                                window.addEventListener("message", receiveMessage, false);
+                                w = window.open(url, "_blank");
+                           });
+                       }
+                   }
+                });
+                element.find(".box").mouseenter(function() {
+                    $(this).children(".source-button").fadeIn(300);
+                    $(this).children(".box-body").children(".expand-button").fadeIn(300);
+                });
+                element.find(".box").mouseleave(function() {
+                    $(this).children(".source-button").fadeOut(300);
+                    $(this).children(".box-body").children(".expand-button").fadeOut(300);
+                });
+                element.find(".signature-type-expandable").click(function() {
+                    var boxId = $(this).attr("boxid");
+                    $(this).toggleClass("signature-type-expanded");
+                    $(this).parent().parent().parent().children(".signature-box-container").
+                        children("[boxid='"+boxId+"']").toggle(resizeHandler ? 0 : 300);
+                    resized();
+                });
+                element.find(".expand-button").click(function() {
+                    if($(this).attr("expanded")) {
+                        $(this).parent().parent().find(".expand-button").html(" + ").attr("title", "Expand all");
+                        $(this).parent().parent().find(".signature-type-expandable").removeClass("signature-type-expanded");
+                        $(this).parent().parent().find(".box-container").hide( resizeHandler ? 0 : 300);
+                        $(this).parent().parent().find(".expand-button").removeAttr("expanded");
+                        resized();
+                    } else {
+                        $(this).parent().parent().find(".expand-button").html(" - ").attr("title", "Collapse all");
+                        $(this).parent().parent().find(".signature-type-expandable").addClass("signature-type-expanded");
+                        $(this).parent().parent().find(".box-container").show(resizeHandler ? 0 : 300);
+                        $(this).parent().parent().find(".expand-button").attr("expanded", true);
+                        resized();
+                    }
+                });
+                element.find(".source-button").click(function() {
+                    $(this).parent().children(".box-body").toggle();
+                    $(this).parent().children(".source").toggle();
+                    resized();
+                });
+            };
+
+            var resolveRefsReentrant = function(schema){
+                traverse(schema).forEach(function(item) {
+                    // Fix Swagger weird generation for array.
+                    if(item && item.$ref == "array") {
+                        delete item.$ref;
+                        item.type ="array";
+                    }
+
+                    // Fetch external schema
+                    if(this.key === "$ref") {
+                        var external = false;
+                        //Local meaning local to this server, but not in this file.
+                        var local = false;
+                        if((/^https?:\/\//).test(item)) {
+                            external = true;
+                        }
+                        else if((/^[^#]/).test(item)) {
+                            local = true;
+                        } else if(item.indexOf('#') > 0) {
+                            //Internal reference
+                            //Turning relative refs to absolute ones
+                            external = true;
+                            item = baseUrl + item;
+                            this.update(item);
+                        }
+                        if(external){
+                            //External reference, fetch it.
+                            var segments = item.split("#");
+                            refs[item] = null;
+                            var p = $.get(segments[0]).then(function(content) {
+                                if(typeof content != "object") {
+                                    try {
+                                        content = JSON.parse(content);
+                                    } catch(e) {
+                                        console.error("Unable to parse "+segments[0], e);
+                                    }
+                                }
+                                if(content) {
+                                    refs[item] = content;
+                                    renderBox();
+                                    resolveRefsReentrant(content); 
+                                }
+                            });
+                        }
+                        else if(local) {
+                            //Local to this server, fetch relative
+                            var segments = item.split("#");
+                            refs[item] = null;
+                            var p = $.get(baseUrl + segments[0]).then(function(content) {
+                                if(typeof content != "object") {
+                                    try {
+                                        content = JSON.parse(content);
+                                    } catch(e) {
+                                        console.error("Unable to parse "+segments[0], e);
+                                    }
+                                }
+                                if(content) {
+                                    refs[item] = content;
+                                    renderBox();
+                                    resolveRefsReentrant(content);
+                                }
+                            });
+                        }
+                    }
+                });
+            };
+            
+            resolveRefsReentrant(schema);
+            renderBox();
+            
+            d.resolve();
+        })
+        return d.promise();
+    }
+
+    return docson;
+});
diff --git a/examples/biojson-doc/index.html b/examples/biojson-doc/index.html
new file mode 100755 (executable)
index 0000000..7c82aaf
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Docson Example</title>
+    <meta charset="utf-8">
+    <link rel="stylesheet" href="css/docson.css">
+    <script src="lib/require.js"></script></head>
+<body>
+<div id="doc"></div>
+<script charset="utf-8">
+    require.config({ baseUrl: ""});
+    require(["docson", "lib/jquery"], function(docson) {
+        $(function() {
+            var schema =
+            {"$schema":"http://json-schema.org/draft-04/schema#","id":"http://jsonschema.net","type":"object","properties":{"seqs":{"id":"http://jsonschema.net/seqs","type":"array","items":{"id":"http://jsonschema.net/seqs/0","type":"object","properties":{"name":{"id":"http://jsonschema.net/seqs/0/name","type":"string","description":"Sequence name"},"start":{"id":"http://jsonschema.net/seqs/0/start","type":"integer","description":"Start residue position"},"svid":{"id":"http://jsonschema.net/seqs/0/svid","type":"string","description":"Serial version id for sequence object"},"end":{"id":"http://jsonschema.net/seqs/0/end","type":"integer","description":"End residue position"},"id":{"id":"http://jsonschema.net/seqs/0/id","type":"string","description":"Sequence unique identifier"},"seq":{"id":"http://jsonschema.net/seqs/0/seq","type":"string","description":"Sequence Residues"},"order":{"id":"http://jsonschema.net/seqs/0/order","type":"integer","description":"Sequence order in an alignment space"}},"required":["name","start","svid","end","id","seq"]},"required":["0"],"description":"Sequences in the Alignemnt","minItems":"1","maxItems":"*"},"appSettings":{"id":"http://jsonschema.net/appSettings","type":"object","properties":{"globalColorScheme":{"id":"http://jsonschema.net/appSettings/globalColorScheme","type":"string","description":"Global colour schem for the alignment"},"webStartUrl":{"id":"http://jsonschema.net/appSettings/webStartUrl","type":"string","description":"Jalview specific setting which points to a url for launching Jalview"},"application":{"id":"http://jsonschema.net/appSettings/application","type":"string","description":"Application which generated the Json"},"showSeqFeatures":{"id":"http://jsonschema.net/appSettings/showSeqFeatures","type":"string","description":"Determines if sequence features are visible or not"},"version":{"id":"http://jsonschema.net/appSettings/version","type":"string","description":"Verion of the application which generated the JSON"},"hiddenCols":{"id":"http://jsonschema.net/appSettings/hiddenCols","type":"string","description":"Delimited lists of hidden colums ranges i.e [2-3,5-5,11-23]"}},"description":"Application specific settings"},"seqGroups":{"id":"http://jsonschema.net/seqGroups","type":"array","items":[{"id":"http://jsonschema.net/seqGroups/0","type":"object","properties":{"displayText":{"id":"http://jsonschema.net/seqGroups/0/displayText","type":"boolean","description":"Determines if the texts of the group is displayed or not"},"startRes":{"id":"http://jsonschema.net/seqGroups/0/startRes","type":"integer","description":"Start residue position for a given group"},"groupName":{"id":"http://jsonschema.net/seqGroups/0/groupName","type":"string","description":"Group name"},"endRes":{"id":"http://jsonschema.net/seqGroups/0/endRes","type":"integer","description":"End residue position for a given group"},"colourText":{"id":"http://jsonschema.net/seqGroups/0/colourText","type":"boolean","description":"Determines if the Residues text for the group is coloured"},"seqsHash":{"id":"http://jsonschema.net/seqGroups/0/seqsHash","type":"array","items":[],"minItems":"0","maxItems":"*","description":"The id's of the sequences which belongs to the group"},"svid":{"id":"http://jsonschema.net/seqGroups/0/svid","type":"string","description":"Serial version id for a given group"},"showNonconserved":{"id":"http://jsonschema.net/seqGroups/0/showNonconserved","type":"boolean","description":"Determines if non conserved regions of a group is shown or not"},"colourScheme":{"id":"http://jsonschema.net/seqGroups/0/colourScheme","type":"string","description":"Colour Scheme for the sequence group"},"displayBoxes":{"id":"http://jsonschema.net/seqGroups/0/displayBoxes","type":"boolean","description":"Determines if the group border should be visible or not"}}}],"description":"Sequence groups in the Alignment","minItems":"0","maxItems":"*"},"alignAnnotation":{"id":"http://jsonschema.net/alignAnnotation","type":"array","items":{"id":"http://jsonschema.net/alignAnnotation/0","type":"object","properties":{"svid":{"id":"http://jsonschema.net/alignAnnotation/0/svid","type":"string","description":"Serial version id for the annotation object"},"annotations":{"id":"http://jsonschema.net/alignAnnotation/0/annotations","type":"array","items":[{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0","type":"object","properties":{"displayCharacter":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0/displayCharacter","type":"string","description":"Display character to denote the given annotation"},"value":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0/value","type":"integer","description":"Value of the annotation"},"secondaryStructure":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0/secondaryStructure","type":"string","description":"Secondary structure symbol for the given annotation"}}},{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1","type":"object","properties":{"displayCharacter":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1/displayCharacter","type":"string"},"value":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1/value","type":"integer"},"secondaryStructure":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1/secondaryStructure","type":"string"}}},{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2","type":"object","properties":{"displayCharacter":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2/displayCharacter","type":"string"},"value":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2/value","type":"integer"},"secondaryStructure":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2/secondaryStructure","type":"string"}}}]},"description":{"id":"http://jsonschema.net/alignAnnotation/0/description","type":"string","description":"Description of the Alignment Annotation"},"label":{"id":"http://jsonschema.net/alignAnnotation/0/label","type":"string","description":"Label for the Annotation"}}},"description":"Alignment Annotations","minItems":"0","maxItems":"*"},"svid":{"id":"http://jsonschema.net/svid","type":"string","description":"Serial version id"},"seqFeatures":{"id":"http://jsonschema.net/seqFeatures","type":"array","items":{"id":"http://jsonschema.net/seqFeatures/0","type":"object","properties":{"fillColor":{"id":"http://jsonschema.net/seqFeatures/0/fillColor","type":"string","description":"Fill colour"},"score":{"id":"http://jsonschema.net/seqFeatures/0/score","type":"integer","description":"Score"},"sequenceRef":{"id":"http://jsonschema.net/seqFeatures/0/sequenceRef","type":"string","description":"Reference to the Sequence in the alignement (More like a foreign key)"},"featureGroup":{"id":"http://jsonschema.net/seqFeatures/0/featureGroup","type":"string","description":"Feature Group"},"svid":{"id":"http://jsonschema.net/seqFeatures/0/svid","type":"string","description":"Serial version id for the SeqFeature object"},"description":{"id":"http://jsonschema.net/seqFeatures/0/description","type":"string","description":"Description of Feature"},"xStart":{"id":"http://jsonschema.net/seqFeatures/0/xStart","type":"integer","description":"Start residue position for the sequence feature"},"xEnd":{"id":"http://jsonschema.net/seqFeatures/0/xEnd","type":"integer","description":"End residue position for the sequence feature"},"type":{"id":"http://jsonschema.net/seqFeatures/0/type","type":"string","description":"The name of the SequenceFeature"}}},"minItems":"0","maxItems":"*","description":"Sequence Features within the alignment"}},"required":["seqs","appSettings","seqGroups","alignAnnotation","svid","seqFeatures"]}
+            docson.templateBaseUrl="templates";
+            docson.doc("doc", schema);
+        });
+    });
+</script>
+</body>
+</html>
diff --git a/examples/biojson-doc/lib/handlebars.js b/examples/biojson-doc/lib/handlebars.js
new file mode 100755 (executable)
index 0000000..ba71792
--- /dev/null
@@ -0,0 +1,2595 @@
+/*!
+
+ handlebars v1.1.2
+
+Copyright (C) 2011 by Yehuda Katz
+
+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.
+
+@license
+*/
+var Handlebars = (function() {
+// handlebars/safe-string.js
+var __module4__ = (function() {
+  "use strict";
+  var __exports__;
+  // Build out our basic SafeString type
+  function SafeString(string) {
+    this.string = string;
+  }
+
+  SafeString.prototype.toString = function() {
+    return "" + this.string;
+  };
+
+  __exports__ = SafeString;
+  return __exports__;
+})();
+
+// handlebars/utils.js
+var __module3__ = (function(__dependency1__) {
+  "use strict";
+  var __exports__ = {};
+  var SafeString = __dependency1__;
+
+  var escape = {
+    "&": "&amp;",
+    "<": "&lt;",
+    ">": "&gt;",
+    '"': "&quot;",
+    "'": "&#x27;",
+    "`": "&#x60;"
+  };
+
+  var badChars = /[&<>"'`]/g;
+  var possible = /[&<>"'`]/;
+
+  function escapeChar(chr) {
+    return escape[chr] || "&amp;";
+  }
+
+  function extend(obj, value) {
+    for(var key in value) {
+      if(value.hasOwnProperty(key)) {
+        obj[key] = value[key];
+      }
+    }
+  }
+
+  __exports__.extend = extend;var toString = Object.prototype.toString;
+  __exports__.toString = toString;
+  // Sourced from lodash
+  // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
+  var isFunction = function(value) {
+    return typeof value === 'function';
+  };
+  // fallback for older versions of Chrome and Safari
+  if (isFunction(/x/)) {
+    isFunction = function(value) {
+      return typeof value === 'function' && toString.call(value) === '[object Function]';
+    };
+  }
+  var isFunction;
+  __exports__.isFunction = isFunction;
+  var isArray = Array.isArray || function(value) {
+    return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
+  };
+  __exports__.isArray = isArray;
+
+  function escapeExpression(string) {
+    // don't escape SafeStrings, since they're already safe
+    if (string instanceof SafeString) {
+      return string.toString();
+    } else if (!string && string !== 0) {
+      return "";
+    }
+
+    // Force a string conversion as this will be done by the append regardless and
+    // the regex test will do this transparently behind the scenes, causing issues if
+    // an object's to string has escaped characters in it.
+    string = "" + string;
+
+    if(!possible.test(string)) { return string; }
+    return string.replace(badChars, escapeChar);
+  }
+
+  __exports__.escapeExpression = escapeExpression;function isEmpty(value) {
+    if (!value && value !== 0) {
+      return true;
+    } else if (isArray(value) && value.length === 0) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  __exports__.isEmpty = isEmpty;
+  return __exports__;
+})(__module4__);
+
+// handlebars/exception.js
+var __module5__ = (function() {
+  "use strict";
+  var __exports__;
+
+  var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
+
+  function Exception(/* message */) {
+    var tmp = Error.prototype.constructor.apply(this, arguments);
+
+    // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
+    for (var idx = 0; idx < errorProps.length; idx++) {
+      this[errorProps[idx]] = tmp[errorProps[idx]];
+    }
+  }
+
+  Exception.prototype = new Error();
+
+  __exports__ = Exception;
+  return __exports__;
+})();
+
+// handlebars/base.js
+var __module2__ = (function(__dependency1__, __dependency2__) {
+  "use strict";
+  var __exports__ = {};
+  /*globals Exception, Utils */
+  var Utils = __dependency1__;
+  var Exception = __dependency2__;
+
+  var VERSION = "1.1.2";
+  __exports__.VERSION = VERSION;var COMPILER_REVISION = 4;
+  __exports__.COMPILER_REVISION = COMPILER_REVISION;
+  var REVISION_CHANGES = {
+    1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
+    2: '== 1.0.0-rc.3',
+    3: '== 1.0.0-rc.4',
+    4: '>= 1.0.0'
+  };
+  __exports__.REVISION_CHANGES = REVISION_CHANGES;
+  var isArray = Utils.isArray,
+      isFunction = Utils.isFunction,
+      toString = Utils.toString,
+      objectType = '[object Object]';
+
+  function HandlebarsEnvironment(helpers, partials) {
+    this.helpers = helpers || {};
+    this.partials = partials || {};
+
+    registerDefaultHelpers(this);
+  }
+
+  __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = {
+    constructor: HandlebarsEnvironment,
+
+    logger: logger,
+    log: log,
+
+    registerHelper: function(name, fn, inverse) {
+      if (toString.call(name) === objectType) {
+        if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); }
+        Utils.extend(this.helpers, name);
+      } else {
+        if (inverse) { fn.not = inverse; }
+        this.helpers[name] = fn;
+      }
+    },
+
+    registerPartial: function(name, str) {
+      if (toString.call(name) === objectType) {
+        Utils.extend(this.partials,  name);
+      } else {
+        this.partials[name] = str;
+      }
+    }
+  };
+
+  function registerDefaultHelpers(instance) {
+    instance.registerHelper('helperMissing', function(arg) {
+      if(arguments.length === 2) {
+        return undefined;
+      } else {
+        throw new Error("Missing helper: '" + arg + "'");
+      }
+    });
+
+    instance.registerHelper('blockHelperMissing', function(context, options) {
+      var inverse = options.inverse || function() {}, fn = options.fn;
+
+      if (isFunction(context)) { context = context.call(this); }
+
+      if(context === true) {
+        return fn(this);
+      } else if(context === false || context == null) {
+        return inverse(this);
+      } else if (isArray(context)) {
+        if(context.length > 0) {
+          return instance.helpers.each(context, options);
+        } else {
+          return inverse(this);
+        }
+      } else {
+        return fn(context);
+      }
+    });
+
+    instance.registerHelper('each', function(context, options) {
+      var fn = options.fn, inverse = options.inverse;
+      var i = 0, ret = "", data;
+
+      if (isFunction(context)) { context = context.call(this); }
+
+      if (options.data) {
+        data = createFrame(options.data);
+      }
+
+      if(context && typeof context === 'object') {
+        if (isArray(context)) {
+          for(var j = context.length; i<j; i++) {
+            if (data) {
+              data.index = i;
+              data.first = (i === 0)
+              data.last  = (i === (context.length-1));
+            }
+            ret = ret + fn(context[i], { data: data });
+          }
+        } else {
+          for(var key in context) {
+            if(context.hasOwnProperty(key)) {
+              if(data) { data.key = key; }
+              ret = ret + fn(context[key], {data: data});
+              i++;
+            }
+          }
+        }
+      }
+
+      if(i === 0){
+        ret = inverse(this);
+      }
+
+      return ret;
+    });
+
+    instance.registerHelper('if', function(conditional, options) {
+      if (isFunction(conditional)) { conditional = conditional.call(this); }
+
+      // Default behavior is to render the positive path if the value is truthy and not empty.
+      // The `includeZero` option may be set to treat the condtional as purely not empty based on the
+      // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative.
+      if ((!options.hash.includeZero && !conditional) || Utils.isEmpty(conditional)) {
+        return options.inverse(this);
+      } else {
+        return options.fn(this);
+      }
+    });
+
+    instance.registerHelper('unless', function(conditional, options) {
+      return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash});
+    });
+
+    instance.registerHelper('with', function(context, options) {
+      if (isFunction(context)) { context = context.call(this); }
+
+      if (!Utils.isEmpty(context)) return options.fn(context);
+    });
+
+    instance.registerHelper('log', function(context, options) {
+      var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
+      instance.log(level, context);
+    });
+  }
+
+  var logger = {
+    methodMap: { 0: 'debug', 1: 'info', 2: 'warn', 3: 'error' },
+
+    // State enum
+    DEBUG: 0,
+    INFO: 1,
+    WARN: 2,
+    ERROR: 3,
+    level: 3,
+
+    // can be overridden in the host environment
+    log: function(level, obj) {
+      if (logger.level <= level) {
+        var method = logger.methodMap[level];
+        if (typeof console !== 'undefined' && console[method]) {
+          console[method].call(console, obj);
+        }
+      }
+    }
+  };
+  __exports__.logger = logger;
+  function log(level, obj) { logger.log(level, obj); }
+
+  __exports__.log = log;var createFrame = function(object) {
+    var obj = {};
+    Utils.extend(obj, object);
+    return obj;
+  };
+  __exports__.createFrame = createFrame;
+  return __exports__;
+})(__module3__, __module5__);
+
+// handlebars/runtime.js
+var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) {
+  "use strict";
+  var __exports__ = {};
+  /*global Utils */
+  var Utils = __dependency1__;
+  var Exception = __dependency2__;
+  var COMPILER_REVISION = __dependency3__.COMPILER_REVISION;
+  var REVISION_CHANGES = __dependency3__.REVISION_CHANGES;
+
+  function checkRevision(compilerInfo) {
+    var compilerRevision = compilerInfo && compilerInfo[0] || 1,
+        currentRevision = COMPILER_REVISION;
+
+    if (compilerRevision !== currentRevision) {
+      if (compilerRevision < currentRevision) {
+        var runtimeVersions = REVISION_CHANGES[currentRevision],
+            compilerVersions = REVISION_CHANGES[compilerRevision];
+        throw new Error("Template was precompiled with an older version of Handlebars than the current runtime. "+
+              "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").");
+      } else {
+        // Use the embedded version info since the runtime doesn't know about this revision yet
+        throw new Error("Template was precompiled with a newer version of Handlebars than the current runtime. "+
+              "Please update your runtime to a newer version ("+compilerInfo[1]+").");
+      }
+    }
+  }
+
+  // TODO: Remove this line and break up compilePartial
+
+  function template(templateSpec, env) {
+    if (!env) {
+      throw new Error("No environment passed to template");
+    }
+
+    var invokePartialWrapper;
+    if (env.compile) {
+      invokePartialWrapper = function(partial, name, context, helpers, partials, data) {
+        // TODO : Check this for all inputs and the options handling (partial flag, etc). This feels
+        // like there should be a common exec path
+        var result = invokePartial.apply(this, arguments);
+        if (result) { return result; }
+
+        var options = { helpers: helpers, partials: partials, data: data };
+        partials[name] = env.compile(partial, { data: data !== undefined }, env);
+        return partials[name](context, options);
+      };
+    } else {
+      invokePartialWrapper = function(partial, name /* , context, helpers, partials, data */) {
+        var result = invokePartial.apply(this, arguments);
+        if (result) { return result; }
+        throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
+      };
+    }
+
+    // Just add water
+    var container = {
+      escapeExpression: Utils.escapeExpression,
+      invokePartial: invokePartialWrapper,
+      programs: [],
+      program: function(i, fn, data) {
+        var programWrapper = this.programs[i];
+        if(data) {
+          programWrapper = program(i, fn, data);
+        } else if (!programWrapper) {
+          programWrapper = this.programs[i] = program(i, fn);
+        }
+        return programWrapper;
+      },
+      merge: function(param, common) {
+        var ret = param || common;
+
+        if (param && common && (param !== common)) {
+          ret = {};
+          Utils.extend(ret, common);
+          Utils.extend(ret, param);
+        }
+        return ret;
+      },
+      programWithDepth: programWithDepth,
+      noop: noop,
+      compilerInfo: null
+    };
+
+    return function(context, options) {
+      options = options || {};
+      var namespace = options.partial ? options : env,
+          helpers,
+          partials;
+
+      if (!options.partial) {
+        helpers = options.helpers;
+        partials = options.partials;
+      }
+      var result = templateSpec.call(
+            container,
+            namespace, context,
+            helpers,
+            partials,
+            options.data);
+
+      if (!options.partial) {
+        checkRevision(container.compilerInfo);
+      }
+
+      return result;
+    };
+  }
+
+  __exports__.template = template;function programWithDepth(i, fn, data /*, $depth */) {
+    var args = Array.prototype.slice.call(arguments, 3);
+
+    var prog = function(context, options) {
+      options = options || {};
+
+      return fn.apply(this, [context, options.data || data].concat(args));
+    };
+    prog.program = i;
+    prog.depth = args.length;
+    return prog;
+  }
+
+  __exports__.programWithDepth = programWithDepth;function program(i, fn, data) {
+    var prog = function(context, options) {
+      options = options || {};
+
+      return fn(context, options.data || data);
+    };
+    prog.program = i;
+    prog.depth = 0;
+    return prog;
+  }
+
+  __exports__.program = program;function invokePartial(partial, name, context, helpers, partials, data) {
+    var options = { partial: true, helpers: helpers, partials: partials, data: data };
+
+    if(partial === undefined) {
+      throw new Exception("The partial " + name + " could not be found");
+    } else if(partial instanceof Function) {
+      return partial(context, options);
+    }
+  }
+
+  __exports__.invokePartial = invokePartial;function noop() { return ""; }
+
+  __exports__.noop = noop;
+  return __exports__;
+})(__module3__, __module5__, __module2__);
+
+// handlebars.runtime.js
+var __module1__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) {
+  "use strict";
+  var __exports__;
+  var base = __dependency1__;
+
+  // Each of these augment the Handlebars object. No need to setup here.
+  // (This is done to easily share code between commonjs and browse envs)
+  var SafeString = __dependency2__;
+  var Exception = __dependency3__;
+  var Utils = __dependency4__;
+  var runtime = __dependency5__;
+
+  // For compatibility and usage outside of module systems, make the Handlebars object a namespace
+  var create = function() {
+    var hb = new base.HandlebarsEnvironment();
+
+    Utils.extend(hb, base);
+    hb.SafeString = SafeString;
+    hb.Exception = Exception;
+    hb.Utils = Utils;
+
+    hb.VM = runtime;
+    hb.template = function(spec) {
+      return runtime.template(spec, hb);
+    };
+
+    return hb;
+  };
+
+  var Handlebars = create();
+  Handlebars.create = create;
+
+  __exports__ = Handlebars;
+  return __exports__;
+})(__module2__, __module4__, __module5__, __module3__, __module6__);
+
+// handlebars/compiler/ast.js
+var __module7__ = (function(__dependency1__) {
+  "use strict";
+  var __exports__ = {};
+  var Exception = __dependency1__;
+
+  function ProgramNode(statements, inverseStrip, inverse) {
+    this.type = "program";
+    this.statements = statements;
+    this.strip = {};
+
+    if(inverse) {
+      this.inverse = new ProgramNode(inverse, inverseStrip);
+      this.strip.right = inverseStrip.left;
+    } else if (inverseStrip) {
+      this.strip.left = inverseStrip.right;
+    }
+  }
+
+  __exports__.ProgramNode = ProgramNode;function MustacheNode(rawParams, hash, open, strip) {
+    this.type = "mustache";
+    this.hash = hash;
+    this.strip = strip;
+
+    var escapeFlag = open[3] || open[2];
+    this.escaped = escapeFlag !== '{' && escapeFlag !== '&';
+
+    var id = this.id = rawParams[0];
+    var params = this.params = rawParams.slice(1);
+
+    // a mustache is an eligible helper if:
+    // * its id is simple (a single part, not `this` or `..`)
+    var eligibleHelper = this.eligibleHelper = id.isSimple;
+
+    // a mustache is definitely a helper if:
+    // * it is an eligible helper, and
+    // * it has at least one parameter or hash segment
+    this.isHelper = eligibleHelper && (params.length || hash);
+
+    // if a mustache is an eligible helper but not a definite
+    // helper, it is ambiguous, and will be resolved in a later
+    // pass or at runtime.
+  }
+
+  __exports__.MustacheNode = MustacheNode;function PartialNode(partialName, context, strip) {
+    this.type         = "partial";
+    this.partialName  = partialName;
+    this.context      = context;
+    this.strip = strip;
+  }
+
+  __exports__.PartialNode = PartialNode;function BlockNode(mustache, program, inverse, close) {
+    if(mustache.id.original !== close.path.original) {
+      throw new Exception(mustache.id.original + " doesn't match " + close.path.original);
+    }
+
+    this.type = "block";
+    this.mustache = mustache;
+    this.program  = program;
+    this.inverse  = inverse;
+
+    this.strip = {
+      left: mustache.strip.left,
+      right: close.strip.right
+    };
+
+    (program || inverse).strip.left = mustache.strip.right;
+    (inverse || program).strip.right = close.strip.left;
+
+    if (inverse && !program) {
+      this.isInverse = true;
+    }
+  }
+
+  __exports__.BlockNode = BlockNode;function ContentNode(string) {
+    this.type = "content";
+    this.string = string;
+  }
+
+  __exports__.ContentNode = ContentNode;function HashNode(pairs) {
+    this.type = "hash";
+    this.pairs = pairs;
+  }
+
+  __exports__.HashNode = HashNode;function IdNode(parts) {
+    this.type = "ID";
+
+    var original = "",
+        dig = [],
+        depth = 0;
+
+    for(var i=0,l=parts.length; i<l; i++) {
+      var part = parts[i].part;
+      original += (parts[i].separator || '') + part;
+
+      if (part === ".." || part === "." || part === "this") {
+        if (dig.length > 0) { throw new Exception("Invalid path: " + original); }
+        else if (part === "..") { depth++; }
+        else { this.isScoped = true; }
+      }
+      else { dig.push(part); }
+    }
+
+    this.original = original;
+    this.parts    = dig;
+    this.string   = dig.join('.');
+    this.depth    = depth;
+
+    // an ID is simple if it only has one part, and that part is not
+    // `..` or `this`.
+    this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
+
+    this.stringModeValue = this.string;
+  }
+
+  __exports__.IdNode = IdNode;function PartialNameNode(name) {
+    this.type = "PARTIAL_NAME";
+    this.name = name.original;
+  }
+
+  __exports__.PartialNameNode = PartialNameNode;function DataNode(id) {
+    this.type = "DATA";
+    this.id = id;
+  }
+
+  __exports__.DataNode = DataNode;function StringNode(string) {
+    this.type = "STRING";
+    this.original =
+      this.string =
+      this.stringModeValue = string;
+  }
+
+  __exports__.StringNode = StringNode;function IntegerNode(integer) {
+    this.type = "INTEGER";
+    this.original =
+      this.integer = integer;
+    this.stringModeValue = Number(integer);
+  }
+
+  __exports__.IntegerNode = IntegerNode;function BooleanNode(bool) {
+    this.type = "BOOLEAN";
+    this.bool = bool;
+    this.stringModeValue = bool === "true";
+  }
+
+  __exports__.BooleanNode = BooleanNode;function CommentNode(comment) {
+    this.type = "comment";
+    this.comment = comment;
+  }
+
+  __exports__.CommentNode = CommentNode;
+  return __exports__;
+})(__module5__);
+
+// handlebars/compiler/parser.js
+var __module9__ = (function() {
+  "use strict";
+  var __exports__;
+  /* Jison generated parser */
+  var handlebars = (function(){
+  var parser = {trace: function trace() { },
+  yy: {},
+  symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"inMustache_repetition0":28,"inMustache_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"hash":35,"hash_repetition_plus0":36,"hashSegment":37,"ID":38,"EQUALS":39,"DATA":40,"pathSegments":41,"SEP":42,"$accept":0,"$end":1},
+  terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",38:"ID",39:"EQUALS",40:"DATA",42:"SEP"},
+  productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[35,1],[37,3],[26,1],[26,1],[26,1],[30,2],[21,1],[41,3],[41,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[36,1],[36,2]],
+  performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
+
+  var $0 = $$.length - 1;
+  switch (yystate) {
+  case 1: return new yy.ProgramNode($$[$0-1]); 
+  break;
+  case 2: return new yy.ProgramNode([]); 
+  break;
+  case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0]);
+  break;
+  case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0]);
+  break;
+  case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], []);
+  break;
+  case 6:this.$ = new yy.ProgramNode($$[$0]);
+  break;
+  case 7:this.$ = new yy.ProgramNode([]);
+  break;
+  case 8:this.$ = new yy.ProgramNode([]);
+  break;
+  case 9:this.$ = [$$[$0]];
+  break;
+  case 10: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; 
+  break;
+  case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
+  break;
+  case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
+  break;
+  case 13:this.$ = $$[$0];
+  break;
+  case 14:this.$ = $$[$0];
+  break;
+  case 15:this.$ = new yy.ContentNode($$[$0]);
+  break;
+  case 16:this.$ = new yy.CommentNode($$[$0]);
+  break;
+  case 17:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
+  break;
+  case 18:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
+  break;
+  case 19:this.$ = {path: $$[$0-1], strip: stripFlags($$[$0-2], $$[$0])};
+  break;
+  case 20:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
+  break;
+  case 21:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
+  break;
+  case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0]));
+  break;
+  case 23:this.$ = stripFlags($$[$0-1], $$[$0]);
+  break;
+  case 24:this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
+  break;
+  case 25:this.$ = [[$$[$0]], null];
+  break;
+  case 26:this.$ = $$[$0];
+  break;
+  case 27:this.$ = new yy.StringNode($$[$0]);
+  break;
+  case 28:this.$ = new yy.IntegerNode($$[$0]);
+  break;
+  case 29:this.$ = new yy.BooleanNode($$[$0]);
+  break;
+  case 30:this.$ = $$[$0];
+  break;
+  case 31:this.$ = new yy.HashNode($$[$0]);
+  break;
+  case 32:this.$ = [$$[$0-2], $$[$0]];
+  break;
+  case 33:this.$ = new yy.PartialNameNode($$[$0]);
+  break;
+  case 34:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0]));
+  break;
+  case 35:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0]));
+  break;
+  case 36:this.$ = new yy.DataNode($$[$0]);
+  break;
+  case 37:this.$ = new yy.IdNode($$[$0]);
+  break;
+  case 38: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2]; 
+  break;
+  case 39:this.$ = [{part: $$[$0]}];
+  break;
+  case 42:this.$ = [];
+  break;
+  case 43:$$[$0-1].push($$[$0]);
+  break;
+  case 46:this.$ = [$$[$0]];
+  break;
+  case 47:$$[$0-1].push($$[$0]);
+  break;
+  }
+  },
+  table: [{3:1,4:2,5:[1,3],8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[3]},{5:[1,16],8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:29,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:30,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:31,21:24,30:25,38:[1,28],40:[1,27],41:26},{21:33,26:32,32:[1,34],33:[1,35],38:[1,28],41:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,38:[1,28],40:[1,27],41:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,42],24:[2,42],28:43,32:[2,42],33:[2,42],34:[2,42],38:[2,42],40:[2,42]},{18:[2,25],24:[2,25]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],38:[2,37],40:[2,37],42:[1,44]},{21:45,38:[1,28],41:26},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],40:[2,39],42:[2,39]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,40],21:50,27:49,38:[1,28],41:26},{18:[2,33],38:[2,33]},{18:[2,34],38:[2,34]},{18:[2,35],38:[2,35]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,38:[1,28],41:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,44],21:56,24:[2,44],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:55,36:61,37:62,38:[1,63],40:[1,27],41:26},{38:[1,64]},{18:[2,36],24:[2,36],32:[2,36],33:[2,36],34:[2,36],38:[2,36],40:[2,36]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,65]},{18:[2,41]},{18:[1,66]},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],25:[1,15]},{18:[2,24],24:[2,24]},{18:[2,43],24:[2,43],32:[2,43],33:[2,43],34:[2,43],38:[2,43],40:[2,43]},{18:[2,45],24:[2,45]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],38:[2,26],40:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],38:[2,27],40:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],38:[2,28],40:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],38:[2,29],40:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],38:[2,30],40:[2,30]},{18:[2,31],24:[2,31],37:67,38:[1,68]},{18:[2,46],24:[2,46],38:[2,46]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],39:[1,69],40:[2,39],42:[2,39]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],38:[2,38],40:[2,38],42:[2,38]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{18:[2,47],24:[2,47],38:[2,47]},{39:[1,69]},{21:56,30:60,31:70,32:[1,57],33:[1,58],34:[1,59],38:[1,28],40:[1,27],41:26},{18:[2,32],24:[2,32],38:[2,32]}],
+  defaultActions: {3:[2,2],16:[2,1],50:[2,41]},
+  parseError: function parseError(str, hash) {
+      throw new Error(str);
+  },
+  parse: function parse(input) {
+      var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+      this.lexer.setInput(input);
+      this.lexer.yy = this.yy;
+      this.yy.lexer = this.lexer;
+      this.yy.parser = this;
+      if (typeof this.lexer.yylloc == "undefined")
+          this.lexer.yylloc = {};
+      var yyloc = this.lexer.yylloc;
+      lstack.push(yyloc);
+      var ranges = this.lexer.options && this.lexer.options.ranges;
+      if (typeof this.yy.parseError === "function")
+          this.parseError = this.yy.parseError;
+      function popStack(n) {
+          stack.length = stack.length - 2 * n;
+          vstack.length = vstack.length - n;
+          lstack.length = lstack.length - n;
+      }
+      function lex() {
+          var token;
+          token = self.lexer.lex() || 1;
+          if (typeof token !== "number") {
+              token = self.symbols_[token] || token;
+          }
+          return token;
+      }
+      var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+      while (true) {
+          state = stack[stack.length - 1];
+          if (this.defaultActions[state]) {
+              action = this.defaultActions[state];
+          } else {
+              if (symbol === null || typeof symbol == "undefined") {
+                  symbol = lex();
+              }
+              action = table[state] && table[state][symbol];
+          }
+          if (typeof action === "undefined" || !action.length || !action[0]) {
+              var errStr = "";
+              if (!recovering) {
+                  expected = [];
+                  for (p in table[state])
+                      if (this.terminals_[p] && p > 2) {
+                          expected.push("'" + this.terminals_[p] + "'");
+                      }
+                  if (this.lexer.showPosition) {
+                      errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
+                  } else {
+                      errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
+                  }
+                  this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
+              }
+          }
+          if (action[0] instanceof Array && action.length > 1) {
+              throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
+          }
+          switch (action[0]) {
+          case 1:
+              stack.push(symbol);
+              vstack.push(this.lexer.yytext);
+              lstack.push(this.lexer.yylloc);
+              stack.push(action[1]);
+              symbol = null;
+              if (!preErrorSymbol) {
+                  yyleng = this.lexer.yyleng;
+                  yytext = this.lexer.yytext;
+                  yylineno = this.lexer.yylineno;
+                  yyloc = this.lexer.yylloc;
+                  if (recovering > 0)
+                      recovering--;
+              } else {
+                  symbol = preErrorSymbol;
+                  preErrorSymbol = null;
+              }
+              break;
+          case 2:
+              len = this.productions_[action[1]][1];
+              yyval.$ = vstack[vstack.length - len];
+              yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
+              if (ranges) {
+                  yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
+              }
+              r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
+              if (typeof r !== "undefined") {
+                  return r;
+              }
+              if (len) {
+                  stack = stack.slice(0, -1 * len * 2);
+                  vstack = vstack.slice(0, -1 * len);
+                  lstack = lstack.slice(0, -1 * len);
+              }
+              stack.push(this.productions_[action[1]][0]);
+              vstack.push(yyval.$);
+              lstack.push(yyval._$);
+              newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+              stack.push(newState);
+              break;
+          case 3:
+              return true;
+          }
+      }
+      return true;
+  }
+  };
+
+
+  function stripFlags(open, close) {
+    return {
+      left: open[2] === '~',
+      right: close[0] === '~' || close[1] === '~'
+    };
+  }
+
+  /* Jison generated lexer */
+  var lexer = (function(){
+  var lexer = ({EOF:1,
+  parseError:function parseError(str, hash) {
+          if (this.yy.parser) {
+              this.yy.parser.parseError(str, hash);
+          } else {
+              throw new Error(str);
+          }
+      },
+  setInput:function (input) {
+          this._input = input;
+          this._more = this._less = this.done = false;
+          this.yylineno = this.yyleng = 0;
+          this.yytext = this.matched = this.match = '';
+          this.conditionStack = ['INITIAL'];
+          this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
+          if (this.options.ranges) this.yylloc.range = [0,0];
+          this.offset = 0;
+          return this;
+      },
+  input:function () {
+          var ch = this._input[0];
+          this.yytext += ch;
+          this.yyleng++;
+          this.offset++;
+          this.match += ch;
+          this.matched += ch;
+          var lines = ch.match(/(?:\r\n?|\n).*/g);
+          if (lines) {
+              this.yylineno++;
+              this.yylloc.last_line++;
+          } else {
+              this.yylloc.last_column++;
+          }
+          if (this.options.ranges) this.yylloc.range[1]++;
+
+          this._input = this._input.slice(1);
+          return ch;
+      },
+  unput:function (ch) {
+          var len = ch.length;
+          var lines = ch.split(/(?:\r\n?|\n)/g);
+
+          this._input = ch + this._input;
+          this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
+          //this.yyleng -= len;
+          this.offset -= len;
+          var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+          this.match = this.match.substr(0, this.match.length-1);
+          this.matched = this.matched.substr(0, this.matched.length-1);
+
+          if (lines.length-1) this.yylineno -= lines.length-1;
+          var r = this.yylloc.range;
+
+          this.yylloc = {first_line: this.yylloc.first_line,
+            last_line: this.yylineno+1,
+            first_column: this.yylloc.first_column,
+            last_column: lines ?
+                (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
+                this.yylloc.first_column - len
+            };
+
+          if (this.options.ranges) {
+              this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+          }
+          return this;
+      },
+  more:function () {
+          this._more = true;
+          return this;
+      },
+  less:function (n) {
+          this.unput(this.match.slice(n));
+      },
+  pastInput:function () {
+          var past = this.matched.substr(0, this.matched.length - this.match.length);
+          return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+      },
+  upcomingInput:function () {
+          var next = this.match;
+          if (next.length < 20) {
+              next += this._input.substr(0, 20-next.length);
+          }
+          return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
+      },
+  showPosition:function () {
+          var pre = this.pastInput();
+          var c = new Array(pre.length + 1).join("-");
+          return pre + this.upcomingInput() + "\n" + c+"^";
+      },
+  next:function () {
+          if (this.done) {
+              return this.EOF;
+          }
+          if (!this._input) this.done = true;
+
+          var token,
+              match,
+              tempMatch,
+              index,
+              col,
+              lines;
+          if (!this._more) {
+              this.yytext = '';
+              this.match = '';
+          }
+          var rules = this._currentRules();
+          for (var i=0;i < rules.length; i++) {
+              tempMatch = this._input.match(this.rules[rules[i]]);
+              if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+                  match = tempMatch;
+                  index = i;
+                  if (!this.options.flex) break;
+              }
+          }
+          if (match) {
+              lines = match[0].match(/(?:\r\n?|\n).*/g);
+              if (lines) this.yylineno += lines.length;
+              this.yylloc = {first_line: this.yylloc.last_line,
+                             last_line: this.yylineno+1,
+                             first_column: this.yylloc.last_column,
+                             last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
+              this.yytext += match[0];
+              this.match += match[0];
+              this.matches = match;
+              this.yyleng = this.yytext.length;
+              if (this.options.ranges) {
+                  this.yylloc.range = [this.offset, this.offset += this.yyleng];
+              }
+              this._more = false;
+              this._input = this._input.slice(match[0].length);
+              this.matched += match[0];
+              token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
+              if (this.done && this._input) this.done = false;
+              if (token) return token;
+              else return;
+          }
+          if (this._input === "") {
+              return this.EOF;
+          } else {
+              return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
+                      {text: "", token: null, line: this.yylineno});
+          }
+      },
+  lex:function lex() {
+          var r = this.next();
+          if (typeof r !== 'undefined') {
+              return r;
+          } else {
+              return this.lex();
+          }
+      },
+  begin:function begin(condition) {
+          this.conditionStack.push(condition);
+      },
+  popState:function popState() {
+          return this.conditionStack.pop();
+      },
+  _currentRules:function _currentRules() {
+          return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
+      },
+  topState:function () {
+          return this.conditionStack[this.conditionStack.length-2];
+      },
+  pushState:function begin(condition) {
+          this.begin(condition);
+      }});
+  lexer.options = {};
+  lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+
+
+  function strip(start, end) {
+    return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end);
+  }
+
+
+  var YYSTATE=YY_START
+  switch($avoiding_name_collisions) {
+  case 0:
+                                     if(yy_.yytext.slice(-2) === "\\\\") {
+                                       strip(0,1);
+                                       this.begin("mu");
+                                     } else if(yy_.yytext.slice(-1) === "\\") {
+                                       strip(0,1);
+                                       this.begin("emu");
+                                     } else {
+                                       this.begin("mu");
+                                     }
+                                     if(yy_.yytext) return 14;
+                                   
+  break;
+  case 1:return 14;
+  break;
+  case 2:
+                                     if(yy_.yytext.slice(-1) !== "\\") this.popState();
+                                     if(yy_.yytext.slice(-1) === "\\") strip(0,1);
+                                     return 14;
+                                   
+  break;
+  case 3:strip(0,4); this.popState(); return 15;
+  break;
+  case 4:return 25;
+  break;
+  case 5:return 16;
+  break;
+  case 6:return 20;
+  break;
+  case 7:return 19;
+  break;
+  case 8:return 19;
+  break;
+  case 9:return 23;
+  break;
+  case 10:return 22;
+  break;
+  case 11:this.popState(); this.begin('com');
+  break;
+  case 12:strip(3,5); this.popState(); return 15;
+  break;
+  case 13:return 22;
+  break;
+  case 14:return 39;
+  break;
+  case 15:return 38;
+  break;
+  case 16:return 38;
+  break;
+  case 17:return 42;
+  break;
+  case 18:/*ignore whitespace*/
+  break;
+  case 19:this.popState(); return 24;
+  break;
+  case 20:this.popState(); return 18;
+  break;
+  case 21:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32;
+  break;
+  case 22:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32;
+  break;
+  case 23:return 40;
+  break;
+  case 24:return 34;
+  break;
+  case 25:return 34;
+  break;
+  case 26:return 33;
+  break;
+  case 27:return 38;
+  break;
+  case 28:yy_.yytext = strip(1,2); return 38;
+  break;
+  case 29:return 'INVALID';
+  break;
+  case 30:return 5;
+  break;
+  }
+  };
+  lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s])))/,/^(?:false(?=([~}\s])))/,/^(?:-?[0-9]+(?=([~}\s])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
+  lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,30],"inclusive":true}};
+  return lexer;})()
+  parser.lexer = lexer;
+  function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
+  return new Parser;
+  })();__exports__ = handlebars;
+  return __exports__;
+})();
+
+// handlebars/compiler/base.js
+var __module8__ = (function(__dependency1__, __dependency2__) {
+  "use strict";
+  var __exports__ = {};
+  var parser = __dependency1__;
+  var AST = __dependency2__;
+
+  __exports__.parser = parser;
+
+  function parse(input) {
+    // Just return if an already-compile AST was passed in.
+    if(input.constructor === AST.ProgramNode) { return input; }
+
+    parser.yy = AST;
+    return parser.parse(input);
+  }
+
+  __exports__.parse = parse;
+  return __exports__;
+})(__module9__, __module7__);
+
+// handlebars/compiler/javascript-compiler.js
+var __module11__ = (function(__dependency1__) {
+  "use strict";
+  var __exports__;
+  var COMPILER_REVISION = __dependency1__.COMPILER_REVISION;
+  var REVISION_CHANGES = __dependency1__.REVISION_CHANGES;
+  var log = __dependency1__.log;
+
+  function Literal(value) {
+    this.value = value;
+  }
+
+  function JavaScriptCompiler() {}
+
+  JavaScriptCompiler.prototype = {
+    // PUBLIC API: You can override these methods in a subclass to provide
+    // alternative compiled forms for name lookup and buffering semantics
+    nameLookup: function(parent, name /* , type*/) {
+      var wrap,
+          ret;
+      if (parent.indexOf('depth') === 0) {
+        wrap = true;
+      }
+
+      if (/^[0-9]+$/.test(name)) {
+        ret = parent + "[" + name + "]";
+      } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
+        ret = parent + "." + name;
+      }
+      else {
+        ret = parent + "['" + name + "']";
+      }
+
+      if (wrap) {
+        return '(' + parent + ' && ' + ret + ')';
+      } else {
+        return ret;
+      }
+    },
+
+    appendToBuffer: function(string) {
+      if (this.environment.isSimple) {
+        return "return " + string + ";";
+      } else {
+        return {
+          appendToBuffer: true,
+          content: string,
+          toString: function() { return "buffer += " + string + ";"; }
+        };
+      }
+    },
+
+    initializeBuffer: function() {
+      return this.quotedString("");
+    },
+
+    namespace: "Handlebars",
+    // END PUBLIC API
+
+    compile: function(environment, options, context, asObject) {
+      this.environment = environment;
+      this.options = options || {};
+
+      log('debug', this.environment.disassemble() + "\n\n");
+
+      this.name = this.environment.name;
+      this.isChild = !!context;
+      this.context = context || {
+        programs: [],
+        environments: [],
+        aliases: { }
+      };
+
+      this.preamble();
+
+      this.stackSlot = 0;
+      this.stackVars = [];
+      this.registers = { list: [] };
+      this.compileStack = [];
+      this.inlineStack = [];
+
+      this.compileChildren(environment, options);
+
+      var opcodes = environment.opcodes, opcode;
+
+      this.i = 0;
+
+      for(var l=opcodes.length; this.i<l; this.i++) {
+        opcode = opcodes[this.i];
+
+        if(opcode.opcode === 'DECLARE') {
+          this[opcode.name] = opcode.value;
+        } else {
+          this[opcode.opcode].apply(this, opcode.args);
+        }
+
+        // Reset the stripNext flag if it was not set by this operation.
+        if (opcode.opcode !== this.stripNext) {
+          this.stripNext = false;
+        }
+      }
+
+      // Flush any trailing content that might be pending.
+      this.pushSource('');
+
+      return this.createFunctionContext(asObject);
+    },
+
+    preamble: function() {
+      var out = [];
+
+      if (!this.isChild) {
+        var namespace = this.namespace;
+
+        var copies = "helpers = this.merge(helpers, " + namespace + ".helpers);";
+        if (this.environment.usePartial) { copies = copies + " partials = this.merge(partials, " + namespace + ".partials);"; }
+        if (this.options.data) { copies = copies + " data = data || {};"; }
+        out.push(copies);
+      } else {
+        out.push('');
+      }
+
+      if (!this.environment.isSimple) {
+        out.push(", buffer = " + this.initializeBuffer());
+      } else {
+        out.push("");
+      }
+
+      // track the last context pushed into place to allow skipping the
+      // getContext opcode when it would be a noop
+      this.lastContext = 0;
+      this.source = out;
+    },
+
+    createFunctionContext: function(asObject) {
+      var locals = this.stackVars.concat(this.registers.list);
+
+      if(locals.length > 0) {
+        this.source[1] = this.source[1] + ", " + locals.join(", ");
+      }
+
+      // Generate minimizer alias mappings
+      if (!this.isChild) {
+        for (var alias in this.context.aliases) {
+          if (this.context.aliases.hasOwnProperty(alias)) {
+            this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
+          }
+        }
+      }
+
+      if (this.source[1]) {
+        this.source[1] = "var " + this.source[1].substring(2) + ";";
+      }
+
+      // Merge children
+      if (!this.isChild) {
+        this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
+      }
+
+      if (!this.environment.isSimple) {
+        this.pushSource("return buffer;");
+      }
+
+      var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
+
+      for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
+        params.push("depth" + this.environment.depths.list[i]);
+      }
+
+      // Perform a second pass over the output to merge content when possible
+      var source = this.mergeSource();
+
+      if (!this.isChild) {
+        var revision = COMPILER_REVISION,
+            versions = REVISION_CHANGES[revision];
+        source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source;
+      }
+
+      if (asObject) {
+        params.push(source);
+
+        return Function.apply(this, params);
+      } else {
+        var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n  ' + source + '}';
+        log('debug', functionSource + "\n\n");
+        return functionSource;
+      }
+    },
+    mergeSource: function() {
+      // WARN: We are not handling the case where buffer is still populated as the source should
+      // not have buffer append operations as their final action.
+      var source = '',
+          buffer;
+      for (var i = 0, len = this.source.length; i < len; i++) {
+        var line = this.source[i];
+        if (line.appendToBuffer) {
+          if (buffer) {
+            buffer = buffer + '\n    + ' + line.content;
+          } else {
+            buffer = line.content;
+          }
+        } else {
+          if (buffer) {
+            source += 'buffer += ' + buffer + ';\n  ';
+            buffer = undefined;
+          }
+          source += line + '\n  ';
+        }
+      }
+      return source;
+    },
+
+    // [blockValue]
+    //
+    // On stack, before: hash, inverse, program, value
+    // On stack, after: return value of blockHelperMissing
+    //
+    // The purpose of this opcode is to take a block of the form
+    // `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
+    // replace it on the stack with the result of properly
+    // invoking blockHelperMissing.
+    blockValue: function() {
+      this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
+
+      var params = ["depth0"];
+      this.setupParams(0, params);
+
+      this.replaceStack(function(current) {
+        params.splice(1, 0, current);
+        return "blockHelperMissing.call(" + params.join(", ") + ")";
+      });
+    },
+
+    // [ambiguousBlockValue]
+    //
+    // On stack, before: hash, inverse, program, value
+    // Compiler value, before: lastHelper=value of last found helper, if any
+    // On stack, after, if no lastHelper: same as [blockValue]
+    // On stack, after, if lastHelper: value
+    ambiguousBlockValue: function() {
+      this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
+
+      var params = ["depth0"];
+      this.setupParams(0, params);
+
+      var current = this.topStack();
+      params.splice(1, 0, current);
+
+      // Use the options value generated from the invocation
+      params[params.length-1] = 'options';
+
+      this.pushSource("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
+    },
+
+    // [appendContent]
+    //
+    // On stack, before: ...
+    // On stack, after: ...
+    //
+    // Appends the string value of `content` to the current buffer
+    appendContent: function(content) {
+      if (this.pendingContent) {
+        content = this.pendingContent + content;
+      }
+      if (this.stripNext) {
+        content = content.replace(/^\s+/, '');
+      }
+
+      this.pendingContent = content;
+    },
+
+    // [strip]
+    //
+    // On stack, before: ...
+    // On stack, after: ...
+    //
+    // Removes any trailing whitespace from the prior content node and flags
+    // the next operation for stripping if it is a content node.
+    strip: function() {
+      if (this.pendingContent) {
+        this.pendingContent = this.pendingContent.replace(/\s+$/, '');
+      }
+      this.stripNext = 'strip';
+    },
+
+    // [append]
+    //
+    // On stack, before: value, ...
+    // On stack, after: ...
+    //
+    // Coerces `value` to a String and appends it to the current buffer.
+    //
+    // If `value` is truthy, or 0, it is coerced into a string and appended
+    // Otherwise, the empty string is appended
+    append: function() {
+      // Force anything that is inlined onto the stack so we don't have duplication
+      // when we examine local
+      this.flushInline();
+      var local = this.popStack();
+      this.pushSource("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
+      if (this.environment.isSimple) {
+        this.pushSource("else { " + this.appendToBuffer("''") + " }");
+      }
+    },
+
+    // [appendEscaped]
+    //
+    // On stack, before: value, ...
+    // On stack, after: ...
+    //
+    // Escape `value` and append it to the buffer
+    appendEscaped: function() {
+      this.context.aliases.escapeExpression = 'this.escapeExpression';
+
+      this.pushSource(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
+    },
+
+    // [getContext]
+    //
+    // On stack, before: ...
+    // On stack, after: ...
+    // Compiler value, after: lastContext=depth
+    //
+    // Set the value of the `lastContext` compiler value to the depth
+    getContext: function(depth) {
+      if(this.lastContext !== depth) {
+        this.lastContext = depth;
+      }
+    },
+
+    // [lookupOnContext]
+    //
+    // On stack, before: ...
+    // On stack, after: currentContext[name], ...
+    //
+    // Looks up the value of `name` on the current context and pushes
+    // it onto the stack.
+    lookupOnContext: function(name) {
+      this.push(this.nameLookup('depth' + this.lastContext, name, 'context'));
+    },
+
+    // [pushContext]
+    //
+    // On stack, before: ...
+    // On stack, after: currentContext, ...
+    //
+    // Pushes the value of the current context onto the stack.
+    pushContext: function() {
+      this.pushStackLiteral('depth' + this.lastContext);
+    },
+
+    // [resolvePossibleLambda]
+    //
+    // On stack, before: value, ...
+    // On stack, after: resolved value, ...
+    //
+    // If the `value` is a lambda, replace it on the stack by
+    // the return value of the lambda
+    resolvePossibleLambda: function() {
+      this.context.aliases.functionType = '"function"';
+
+      this.replaceStack(function(current) {
+        return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current;
+      });
+    },
+
+    // [lookup]
+    //
+    // On stack, before: value, ...
+    // On stack, after: value[name], ...
+    //
+    // Replace the value on the stack with the result of looking
+    // up `name` on `value`
+    lookup: function(name) {
+      this.replaceStack(function(current) {
+        return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
+      });
+    },
+
+    // [lookupData]
+    //
+    // On stack, before: ...
+    // On stack, after: data, ...
+    //
+    // Push the data lookup operator
+    lookupData: function() {
+      this.push('data');
+    },
+
+    // [pushStringParam]
+    //
+    // On stack, before: ...
+    // On stack, after: string, currentContext, ...
+    //
+    // This opcode is designed for use in string mode, which
+    // provides the string value of a parameter along with its
+    // depth rather than resolving it immediately.
+    pushStringParam: function(string, type) {
+      this.pushStackLiteral('depth' + this.lastContext);
+
+      this.pushString(type);
+
+      if (typeof string === 'string') {
+        this.pushString(string);
+      } else {
+        this.pushStackLiteral(string);
+      }
+    },
+
+    emptyHash: function() {
+      this.pushStackLiteral('{}');
+
+      if (this.options.stringParams) {
+        this.register('hashTypes', '{}');
+        this.register('hashContexts', '{}');
+      }
+    },
+    pushHash: function() {
+      this.hash = {values: [], types: [], contexts: []};
+    },
+    popHash: function() {
+      var hash = this.hash;
+      this.hash = undefined;
+
+      if (this.options.stringParams) {
+        this.register('hashContexts', '{' + hash.contexts.join(',') + '}');
+        this.register('hashTypes', '{' + hash.types.join(',') + '}');
+      }
+      this.push('{\n    ' + hash.values.join(',\n    ') + '\n  }');
+    },
+
+    // [pushString]
+    //
+    // On stack, before: ...
+    // On stack, after: quotedString(string), ...
+    //
+    // Push a quoted version of `string` onto the stack
+    pushString: function(string) {
+      this.pushStackLiteral(this.quotedString(string));
+    },
+
+    // [push]
+    //
+    // On stack, before: ...
+    // On stack, after: expr, ...
+    //
+    // Push an expression onto the stack
+    push: function(expr) {
+      this.inlineStack.push(expr);
+      return expr;
+    },
+
+    // [pushLiteral]
+    //
+    // On stack, before: ...
+    // On stack, after: value, ...
+    //
+    // Pushes a value onto the stack. This operation prevents
+    // the compiler from creating a temporary variable to hold
+    // it.
+    pushLiteral: function(value) {
+      this.pushStackLiteral(value);
+    },
+
+    // [pushProgram]
+    //
+    // On stack, before: ...
+    // On stack, after: program(guid), ...
+    //
+    // Push a program expression onto the stack. This takes
+    // a compile-time guid and converts it into a runtime-accessible
+    // expression.
+    pushProgram: function(guid) {
+      if (guid != null) {
+        this.pushStackLiteral(this.programExpression(guid));
+      } else {
+        this.pushStackLiteral(null);
+      }
+    },
+
+    // [invokeHelper]
+    //
+    // On stack, before: hash, inverse, program, params..., ...
+    // On stack, after: result of helper invocation
+    //
+    // Pops off the helper's parameters, invokes the helper,
+    // and pushes the helper's return value onto the stack.
+    //
+    // If the helper is not found, `helperMissing` is called.
+    invokeHelper: function(paramSize, name) {
+      this.context.aliases.helperMissing = 'helpers.helperMissing';
+
+      var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
+      var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
+
+      this.push(helper.name + ' || ' + nonHelper);
+      this.replaceStack(function(name) {
+        return name + ' ? ' + name + '.call(' +
+            helper.callParams + ") " + ": helperMissing.call(" +
+            helper.helperMissingParams + ")";
+      });
+    },
+
+    // [invokeKnownHelper]
+    //
+    // On stack, before: hash, inverse, program, params..., ...
+    // On stack, after: result of helper invocation
+    //
+    // This operation is used when the helper is known to exist,
+    // so a `helperMissing` fallback is not required.
+    invokeKnownHelper: function(paramSize, name) {
+      var helper = this.setupHelper(paramSize, name);
+      this.push(helper.name + ".call(" + helper.callParams + ")");
+    },
+
+    // [invokeAmbiguous]
+    //
+    // On stack, before: hash, inverse, program, params..., ...
+    // On stack, after: result of disambiguation
+    //
+    // This operation is used when an expression like `{{foo}}`
+    // is provided, but we don't know at compile-time whether it
+    // is a helper or a path.
+    //
+    // This operation emits more code than the other options,
+    // and can be avoided by passing the `knownHelpers` and
+    // `knownHelpersOnly` flags at compile-time.
+    invokeAmbiguous: function(name, helperCall) {
+      this.context.aliases.functionType = '"function"';
+
+      this.pushStackLiteral('{}');    // Hash value
+      var helper = this.setupHelper(0, name, helperCall);
+
+      var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
+
+      var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
+      var nextStack = this.nextStack();
+
+      this.pushSource('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }');
+      this.pushSource('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.call(' + helper.callParams + ') : ' + nextStack + '; }');
+    },
+
+    // [invokePartial]
+    //
+    // On stack, before: context, ...
+    // On stack after: result of partial invocation
+    //
+    // This operation pops off a context, invokes a partial with that context,
+    // and pushes the result of the invocation back.
+    invokePartial: function(name) {
+      var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
+
+      if (this.options.data) {
+        params.push("data");
+      }
+
+      this.context.aliases.self = "this";
+      this.push("self.invokePartial(" + params.join(", ") + ")");
+    },
+
+    // [assignToHash]
+    //
+    // On stack, before: value, hash, ...
+    // On stack, after: hash, ...
+    //
+    // Pops a value and hash off the stack, assigns `hash[key] = value`
+    // and pushes the hash back onto the stack.
+    assignToHash: function(key) {
+      var value = this.popStack(),
+          context,
+          type;
+
+      if (this.options.stringParams) {
+        type = this.popStack();
+        context = this.popStack();
+      }
+
+      var hash = this.hash;
+      if (context) {
+        hash.contexts.push("'" + key + "': " + context);
+      }
+      if (type) {
+        hash.types.push("'" + key + "': " + type);
+      }
+      hash.values.push("'" + key + "': (" + value + ")");
+    },
+
+    // HELPERS
+
+    compiler: JavaScriptCompiler,
+
+    compileChildren: function(environment, options) {
+      var children = environment.children, child, compiler;
+
+      for(var i=0, l=children.length; i<l; i++) {
+        child = children[i];
+        compiler = new this.compiler();
+
+        var index = this.matchExistingProgram(child);
+
+        if (index == null) {
+          this.context.programs.push('');     // Placeholder to prevent name conflicts for nested children
+          index = this.context.programs.length;
+          child.index = index;
+          child.name = 'program' + index;
+          this.context.programs[index] = compiler.compile(child, options, this.context);
+          this.context.environments[index] = child;
+        } else {
+          child.index = index;
+          child.name = 'program' + index;
+        }
+      }
+    },
+    matchExistingProgram: function(child) {
+      for (var i = 0, len = this.context.environments.length; i < len; i++) {
+        var environment = this.context.environments[i];
+        if (environment && environment.equals(child)) {
+          return i;
+        }
+      }
+    },
+
+    programExpression: function(guid) {
+      this.context.aliases.self = "this";
+
+      if(guid == null) {
+        return "self.noop";
+      }
+
+      var child = this.environment.children[guid],
+          depths = child.depths.list, depth;
+
+      var programParams = [child.index, child.name, "data"];
+
+      for(var i=0, l = depths.length; i<l; i++) {
+        depth = depths[i];
+
+        if(depth === 1) { programParams.push("depth0"); }
+        else { programParams.push("depth" + (depth - 1)); }
+      }
+
+      return (depths.length === 0 ? "self.program(" : "self.programWithDepth(") + programParams.join(", ") + ")";
+    },
+
+    register: function(name, val) {
+      this.useRegister(name);
+      this.pushSource(name + " = " + val + ";");
+    },
+
+    useRegister: function(name) {
+      if(!this.registers[name]) {
+        this.registers[name] = true;
+        this.registers.list.push(name);
+      }
+    },
+
+    pushStackLiteral: function(item) {
+      return this.push(new Literal(item));
+    },
+
+    pushSource: function(source) {
+      if (this.pendingContent) {
+        this.source.push(this.appendToBuffer(this.quotedString(this.pendingContent)));
+        this.pendingContent = undefined;
+      }
+
+      if (source) {
+        this.source.push(source);
+      }
+    },
+
+    pushStack: function(item) {
+      this.flushInline();
+
+      var stack = this.incrStack();
+      if (item) {
+        this.pushSource(stack + " = " + item + ";");
+      }
+      this.compileStack.push(stack);
+      return stack;
+    },
+
+    replaceStack: function(callback) {
+      var prefix = '',
+          inline = this.isInline(),
+          stack;
+
+      // If we are currently inline then we want to merge the inline statement into the
+      // replacement statement via ','
+      if (inline) {
+        var top = this.popStack(true);
+
+        if (top instanceof Literal) {
+          // Literals do not need to be inlined
+          stack = top.value;
+        } else {
+          // Get or create the current stack name for use by the inline
+          var name = this.stackSlot ? this.topStackName() : this.incrStack();
+
+          prefix = '(' + this.push(name) + ' = ' + top + '),';
+          stack = this.topStack();
+        }
+      } else {
+        stack = this.topStack();
+      }
+
+      var item = callback.call(this, stack);
+
+      if (inline) {
+        if (this.inlineStack.length || this.compileStack.length) {
+          this.popStack();
+        }
+        this.push('(' + prefix + item + ')');
+      } else {
+        // Prevent modification of the context depth variable. Through replaceStack
+        if (!/^stack/.test(stack)) {
+          stack = this.nextStack();
+        }
+
+        this.pushSource(stack + " = (" + prefix + item + ");");
+      }
+      return stack;
+    },
+
+    nextStack: function() {
+      return this.pushStack();
+    },
+
+    incrStack: function() {
+      this.stackSlot++;
+      if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
+      return this.topStackName();
+    },
+    topStackName: function() {
+      return "stack" + this.stackSlot;
+    },
+    flushInline: function() {
+      var inlineStack = this.inlineStack;
+      if (inlineStack.length) {
+        this.inlineStack = [];
+        for (var i = 0, len = inlineStack.length; i < len; i++) {
+          var entry = inlineStack[i];
+          if (entry instanceof Literal) {
+            this.compileStack.push(entry);
+          } else {
+            this.pushStack(entry);
+          }
+        }
+      }
+    },
+    isInline: function() {
+      return this.inlineStack.length;
+    },
+
+    popStack: function(wrapped) {
+      var inline = this.isInline(),
+          item = (inline ? this.inlineStack : this.compileStack).pop();
+
+      if (!wrapped && (item instanceof Literal)) {
+        return item.value;
+      } else {
+        if (!inline) {
+          this.stackSlot--;
+        }
+        return item;
+      }
+    },
+
+    topStack: function(wrapped) {
+      var stack = (this.isInline() ? this.inlineStack : this.compileStack),
+          item = stack[stack.length - 1];
+
+      if (!wrapped && (item instanceof Literal)) {
+        return item.value;
+      } else {
+        return item;
+      }
+    },
+
+    quotedString: function(str) {
+      return '"' + str
+        .replace(/\\/g, '\\\\')
+        .replace(/"/g, '\\"')
+        .replace(/\n/g, '\\n')
+        .replace(/\r/g, '\\r')
+        .replace(/\u2028/g, '\\u2028')   // Per Ecma-262 7.3 + 7.8.4
+        .replace(/\u2029/g, '\\u2029') + '"';
+    },
+
+    setupHelper: function(paramSize, name, missingParams) {
+      var params = [];
+      this.setupParams(paramSize, params, missingParams);
+      var foundHelper = this.nameLookup('helpers', name, 'helper');
+
+      return {
+        params: params,
+        name: foundHelper,
+        callParams: ["depth0"].concat(params).join(", "),
+        helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
+      };
+    },
+
+    // the params and contexts arguments are passed in arrays
+    // to fill in
+    setupParams: function(paramSize, params, useRegister) {
+      var options = [], contexts = [], types = [], param, inverse, program;
+
+      options.push("hash:" + this.popStack());
+
+      inverse = this.popStack();
+      program = this.popStack();
+
+      // Avoid setting fn and inverse if neither are set. This allows
+      // helpers to do a check for `if (options.fn)`
+      if (program || inverse) {
+        if (!program) {
+          this.context.aliases.self = "this";
+          program = "self.noop";
+        }
+
+        if (!inverse) {
+         this.context.aliases.self = "this";
+          inverse = "self.noop";
+        }
+
+        options.push("inverse:" + inverse);
+        options.push("fn:" + program);
+      }
+
+      for(var i=0; i<paramSize; i++) {
+        param = this.popStack();
+        params.push(param);
+
+        if(this.options.stringParams) {
+          types.push(this.popStack());
+          contexts.push(this.popStack());
+        }
+      }
+
+      if (this.options.stringParams) {
+        options.push("contexts:[" + contexts.join(",") + "]");
+        options.push("types:[" + types.join(",") + "]");
+        options.push("hashContexts:hashContexts");
+        options.push("hashTypes:hashTypes");
+      }
+
+      if(this.options.data) {
+        options.push("data:data");
+      }
+
+      options = "{" + options.join(",") + "}";
+      if (useRegister) {
+        this.register('options', options);
+        params.push('options');
+      } else {
+        params.push(options);
+      }
+      return params.join(", ");
+    }
+  };
+
+  var reservedWords = (
+    "break else new var" +
+    " case finally return void" +
+    " catch for switch while" +
+    " continue function this with" +
+    " default if throw" +
+    " delete in try" +
+    " do instanceof typeof" +
+    " abstract enum int short" +
+    " boolean export interface static" +
+    " byte extends long super" +
+    " char final native synchronized" +
+    " class float package throws" +
+    " const goto private transient" +
+    " debugger implements protected volatile" +
+    " double import public let yield"
+  ).split(" ");
+
+  var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
+
+  for(var i=0, l=reservedWords.length; i<l; i++) {
+    compilerWords[reservedWords[i]] = true;
+  }
+
+  JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
+    if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
+      return true;
+    }
+    return false;
+  };
+
+  __exports__ = JavaScriptCompiler;
+  return __exports__;
+})(__module2__);
+
+// handlebars/compiler/compiler.js
+var __module10__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__) {
+  "use strict";
+  var __exports__ = {};
+  var Exception = __dependency1__;
+  var parse = __dependency2__.parse;
+  var JavaScriptCompiler = __dependency3__;
+  var AST = __dependency4__;
+
+  function Compiler() {}
+
+  __exports__.Compiler = Compiler;// the foundHelper register will disambiguate helper lookup from finding a
+  // function in a context. This is necessary for mustache compatibility, which
+  // requires that context functions in blocks are evaluated by blockHelperMissing,
+  // and then proceed as if the resulting value was provided to blockHelperMissing.
+
+  Compiler.prototype = {
+    compiler: Compiler,
+
+    disassemble: function() {
+      var opcodes = this.opcodes, opcode, out = [], params, param;
+
+      for (var i=0, l=opcodes.length; i<l; i++) {
+        opcode = opcodes[i];
+
+        if (opcode.opcode === 'DECLARE') {
+          out.push("DECLARE " + opcode.name + "=" + opcode.value);
+        } else {
+          params = [];
+          for (var j=0; j<opcode.args.length; j++) {
+            param = opcode.args[j];
+            if (typeof param === "string") {
+              param = "\"" + param.replace("\n", "\\n") + "\"";
+            }
+            params.push(param);
+          }
+          out.push(opcode.opcode + " " + params.join(" "));
+        }
+      }
+
+      return out.join("\n");
+    },
+
+    equals: function(other) {
+      var len = this.opcodes.length;
+      if (other.opcodes.length !== len) {
+        return false;
+      }
+
+      for (var i = 0; i < len; i++) {
+        var opcode = this.opcodes[i],
+            otherOpcode = other.opcodes[i];
+        if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) {
+          return false;
+        }
+        for (var j = 0; j < opcode.args.length; j++) {
+          if (opcode.args[j] !== otherOpcode.args[j]) {
+            return false;
+          }
+        }
+      }
+
+      len = this.children.length;
+      if (other.children.length !== len) {
+        return false;
+      }
+      for (i = 0; i < len; i++) {
+        if (!this.children[i].equals(other.children[i])) {
+          return false;
+        }
+      }
+
+      return true;
+    },
+
+    guid: 0,
+
+    compile: function(program, options) {
+      this.opcodes = [];
+      this.children = [];
+      this.depths = {list: []};
+      this.options = options;
+
+      // These changes will propagate to the other compiler components
+      var knownHelpers = this.options.knownHelpers;
+      this.options.knownHelpers = {
+        'helperMissing': true,
+        'blockHelperMissing': true,
+        'each': true,
+        'if': true,
+        'unless': true,
+        'with': true,
+        'log': true
+      };
+      if (knownHelpers) {
+        for (var name in knownHelpers) {
+          this.options.knownHelpers[name] = knownHelpers[name];
+        }
+      }
+
+      return this.accept(program);
+    },
+
+    accept: function(node) {
+      var strip = node.strip || {},
+          ret;
+      if (strip.left) {
+        this.opcode('strip');
+      }
+
+      ret = this[node.type](node);
+
+      if (strip.right) {
+        this.opcode('strip');
+      }
+
+      return ret;
+    },
+
+    program: function(program) {
+      var statements = program.statements;
+
+      for(var i=0, l=statements.length; i<l; i++) {
+        this.accept(statements[i]);
+      }
+      this.isSimple = l === 1;
+
+      this.depths.list = this.depths.list.sort(function(a, b) {
+        return a - b;
+      });
+
+      return this;
+    },
+
+    compileProgram: function(program) {
+      var result = new this.compiler().compile(program, this.options);
+      var guid = this.guid++, depth;
+
+      this.usePartial = this.usePartial || result.usePartial;
+
+      this.children[guid] = result;
+
+      for(var i=0, l=result.depths.list.length; i<l; i++) {
+        depth = result.depths.list[i];
+
+        if(depth < 2) { continue; }
+        else { this.addDepth(depth - 1); }
+      }
+
+      return guid;
+    },
+
+    block: function(block) {
+      var mustache = block.mustache,
+          program = block.program,
+          inverse = block.inverse;
+
+      if (program) {
+        program = this.compileProgram(program);
+      }
+
+      if (inverse) {
+        inverse = this.compileProgram(inverse);
+      }
+
+      var type = this.classifyMustache(mustache);
+
+      if (type === "helper") {
+        this.helperMustache(mustache, program, inverse);
+      } else if (type === "simple") {
+        this.simpleMustache(mustache);
+
+        // now that the simple mustache is resolved, we need to
+        // evaluate it by executing `blockHelperMissing`
+        this.opcode('pushProgram', program);
+        this.opcode('pushProgram', inverse);
+        this.opcode('emptyHash');
+        this.opcode('blockValue');
+      } else {
+        this.ambiguousMustache(mustache, program, inverse);
+
+        // now that the simple mustache is resolved, we need to
+        // evaluate it by executing `blockHelperMissing`
+        this.opcode('pushProgram', program);
+        this.opcode('pushProgram', inverse);
+        this.opcode('emptyHash');
+        this.opcode('ambiguousBlockValue');
+      }
+
+      this.opcode('append');
+    },
+
+    hash: function(hash) {
+      var pairs = hash.pairs, pair, val;
+
+      this.opcode('pushHash');
+
+      for(var i=0, l=pairs.length; i<l; i++) {
+        pair = pairs[i];
+        val  = pair[1];
+
+        if (this.options.stringParams) {
+          if(val.depth) {
+            this.addDepth(val.depth);
+          }
+          this.opcode('getContext', val.depth || 0);
+          this.opcode('pushStringParam', val.stringModeValue, val.type);
+        } else {
+          this.accept(val);
+        }
+
+        this.opcode('assignToHash', pair[0]);
+      }
+      this.opcode('popHash');
+    },
+
+    partial: function(partial) {
+      var partialName = partial.partialName;
+      this.usePartial = true;
+
+      if(partial.context) {
+        this.ID(partial.context);
+      } else {
+        this.opcode('push', 'depth0');
+      }
+
+      this.opcode('invokePartial', partialName.name);
+      this.opcode('append');
+    },
+
+    content: function(content) {
+      this.opcode('appendContent', content.string);
+    },
+
+    mustache: function(mustache) {
+      var options = this.options;
+      var type = this.classifyMustache(mustache);
+
+      if (type === "simple") {
+        this.simpleMustache(mustache);
+      } else if (type === "helper") {
+        this.helperMustache(mustache);
+      } else {
+        this.ambiguousMustache(mustache);
+      }
+
+      if(mustache.escaped && !options.noEscape) {
+        this.opcode('appendEscaped');
+      } else {
+        this.opcode('append');
+      }
+    },
+
+    ambiguousMustache: function(mustache, program, inverse) {
+      var id = mustache.id,
+          name = id.parts[0],
+          isBlock = program != null || inverse != null;
+
+      this.opcode('getContext', id.depth);
+
+      this.opcode('pushProgram', program);
+      this.opcode('pushProgram', inverse);
+
+      this.opcode('invokeAmbiguous', name, isBlock);
+    },
+
+    simpleMustache: function(mustache) {
+      var id = mustache.id;
+
+      if (id.type === 'DATA') {
+        this.DATA(id);
+      } else if (id.parts.length) {
+        this.ID(id);
+      } else {
+        // Simplified ID for `this`
+        this.addDepth(id.depth);
+        this.opcode('getContext', id.depth);
+        this.opcode('pushContext');
+      }
+
+      this.opcode('resolvePossibleLambda');
+    },
+
+    helperMustache: function(mustache, program, inverse) {
+      var params = this.setupFullMustacheParams(mustache, program, inverse),
+          name = mustache.id.parts[0];
+
+      if (this.options.knownHelpers[name]) {
+        this.opcode('invokeKnownHelper', params.length, name);
+      } else if (this.options.knownHelpersOnly) {
+        throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
+      } else {
+        this.opcode('invokeHelper', params.length, name);
+      }
+    },
+
+    ID: function(id) {
+      this.addDepth(id.depth);
+      this.opcode('getContext', id.depth);
+
+      var name = id.parts[0];
+      if (!name) {
+        this.opcode('pushContext');
+      } else {
+        this.opcode('lookupOnContext', id.parts[0]);
+      }
+
+      for(var i=1, l=id.parts.length; i<l; i++) {
+        this.opcode('lookup', id.parts[i]);
+      }
+    },
+
+    DATA: function(data) {
+      this.options.data = true;
+      if (data.id.isScoped || data.id.depth) {
+        throw new Exception('Scoped data references are not supported: ' + data.original);
+      }
+
+      this.opcode('lookupData');
+      var parts = data.id.parts;
+      for(var i=0, l=parts.length; i<l; i++) {
+        this.opcode('lookup', parts[i]);
+      }
+    },
+
+    STRING: function(string) {
+      this.opcode('pushString', string.string);
+    },
+
+    INTEGER: function(integer) {
+      this.opcode('pushLiteral', integer.integer);
+    },
+
+    BOOLEAN: function(bool) {
+      this.opcode('pushLiteral', bool.bool);
+    },
+
+    comment: function() {},
+
+    // HELPERS
+    opcode: function(name) {
+      this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) });
+    },
+
+    declare: function(name, value) {
+      this.opcodes.push({ opcode: 'DECLARE', name: name, value: value });
+    },
+
+    addDepth: function(depth) {
+      if(isNaN(depth)) { throw new Error("EWOT"); }
+      if(depth === 0) { return; }
+
+      if(!this.depths[depth]) {
+        this.depths[depth] = true;
+        this.depths.list.push(depth);
+      }
+    },
+
+    classifyMustache: function(mustache) {
+      var isHelper   = mustache.isHelper;
+      var isEligible = mustache.eligibleHelper;
+      var options    = this.options;
+
+      // if ambiguous, we can possibly resolve the ambiguity now
+      if (isEligible && !isHelper) {
+        var name = mustache.id.parts[0];
+
+        if (options.knownHelpers[name]) {
+          isHelper = true;
+        } else if (options.knownHelpersOnly) {
+          isEligible = false;
+        }
+      }
+
+      if (isHelper) { return "helper"; }
+      else if (isEligible) { return "ambiguous"; }
+      else { return "simple"; }
+    },
+
+    pushParams: function(params) {
+      var i = params.length, param;
+
+      while(i--) {
+        param = params[i];
+
+        if(this.options.stringParams) {
+          if(param.depth) {
+            this.addDepth(param.depth);
+          }
+
+          this.opcode('getContext', param.depth || 0);
+          this.opcode('pushStringParam', param.stringModeValue, param.type);
+        } else {
+          this[param.type](param);
+        }
+      }
+    },
+
+    setupMustacheParams: function(mustache) {
+      var params = mustache.params;
+      this.pushParams(params);
+
+      if(mustache.hash) {
+        this.hash(mustache.hash);
+      } else {
+        this.opcode('emptyHash');
+      }
+
+      return params;
+    },
+
+    // this will replace setupMustacheParams when we're done
+    setupFullMustacheParams: function(mustache, program, inverse) {
+      var params = mustache.params;
+      this.pushParams(params);
+
+      this.opcode('pushProgram', program);
+      this.opcode('pushProgram', inverse);
+
+      if(mustache.hash) {
+        this.hash(mustache.hash);
+      } else {
+        this.opcode('emptyHash');
+      }
+
+      return params;
+    }
+  };
+
+  function precompile(input, options) {
+    if (input == null || (typeof input !== 'string' && input.constructor !== AST.ProgramNode)) {
+      throw new Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);
+    }
+
+    options = options || {};
+    if (!('data' in options)) {
+      options.data = true;
+    }
+
+    var ast = parse(input);
+    var environment = new Compiler().compile(ast, options);
+    return new JavaScriptCompiler().compile(environment, options);
+  }
+
+  __exports__.precompile = precompile;function compile(input, options, env) {
+    if (input == null || (typeof input !== 'string' && input.constructor !== AST.ProgramNode)) {
+      throw new Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
+    }
+
+    options = options || {};
+
+    if (!('data' in options)) {
+      options.data = true;
+    }
+
+    var compiled;
+
+    function compileInput() {
+      var ast = parse(input);
+      var environment = new Compiler().compile(ast, options);
+      var templateSpec = new JavaScriptCompiler().compile(environment, options, undefined, true);
+      return env.template(templateSpec);
+    }
+
+    // Template is only compiled on first use and cached after that point.
+    return function(context, options) {
+      if (!compiled) {
+        compiled = compileInput();
+      }
+      return compiled.call(this, context, options);
+    };
+  }
+
+  __exports__.compile = compile;
+  return __exports__;
+})(__module5__, __module8__, __module11__, __module7__);
+
+// handlebars.js
+var __module0__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) {
+  "use strict";
+  var __exports__;
+  var Handlebars = __dependency1__;
+
+  // Compiler imports
+  var AST = __dependency2__;
+  var Parser = __dependency3__.parser;
+  var parse = __dependency3__.parse;
+  var Compiler = __dependency4__.Compiler;
+  var compile = __dependency4__.compile;
+  var precompile = __dependency4__.precompile;
+  var JavaScriptCompiler = __dependency5__;
+
+  var _create = Handlebars.create;
+  var create = function() {
+    var hb = _create();
+
+    hb.compile = function(input, options) {
+      return compile(input, options, hb);
+    };
+    hb.precompile = precompile;
+
+    hb.AST = AST;
+    hb.Compiler = Compiler;
+    hb.JavaScriptCompiler = JavaScriptCompiler;
+    hb.Parser = Parser;
+    hb.parse = parse;
+
+    return hb;
+  };
+
+  Handlebars = create();
+  Handlebars.create = create;
+
+  __exports__ = Handlebars;
+  return __exports__;
+})(__module1__, __module7__, __module8__, __module10__, __module11__);
+
+  return __module0__;
+})();
diff --git a/examples/biojson-doc/lib/highlight.js b/examples/biojson-doc/lib/highlight.js
new file mode 100755 (executable)
index 0000000..6a266ad
--- /dev/null
@@ -0,0 +1,302 @@
+
+/*
+ * Copyright 2013 Geraint Luff <http://geraintluff.github.io/tv4/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+define(function() {
+
+    var highlight = {};
+
+    var REGEX = {
+        whitespace: /^([ \r\n\t]|&nbsp;)*/,
+        number: /^-?[0-9]+(\.[0-9]+)?([eE][+\-]?[0-9]+)?/
+    };
+
+    function Highlighter(stringData) {
+        this.remaining = stringData;
+        this.html = "";
+    }
+    Highlighter.prototype = {
+        unshift: function (next) {
+            this.remaining = next + this.remaining;
+        },
+        next: function () {
+            this.whitespace();
+            return this.nextCharacter();
+        },
+        nextCharacter: function () {
+            if (this.remaining.length == 0) {
+                throw new Error("Unexpected end of input");
+            }
+            if (this.remaining[0] == "&") {
+                var endIndex = this.remaining.indexOf(";") + 1;
+                if (endIndex == -1) {
+                    endIndex = 1;
+                }
+                var result = this.remaining.substring(0, endIndex);
+                this.remaining = this.remaining.substring(endIndex);
+                return result;
+            }
+            var result = this.remaining[0];
+            this.remaining = this.remaining.substring(1);
+            return result;
+        },
+        whitespace: function () {
+            var ws = this.remaining.match(REGEX.whitespace)[0];
+            this.html += ws;
+            this.remaining = this.remaining.substring(ws.length);
+        },
+        highlightJson: function (keywords) {
+            if (keywords != undefined) {
+                this.html += keywords.wrapper[0];
+            }
+            this.whitespace();
+            var next = this.next();
+            if (next == "{") {
+                this.highlightObject(keywords);
+            } else if (next == '[') {
+                this.highlightArray(keywords);
+            } else if (next == '"' || next == "&quot;") {
+                this.highlightString();
+            } else if ((next + this.remaining).match(REGEX.number)) {
+                var numberString = (next + this.remaining).match(REGEX.number)[0];
+                this.html += '<span class="json-number">' + numberString + '</span>';
+                this.remaining = this.remaining.substring(numberString.length - 1);
+            } else if (next == "n" && this.remaining.substring(0, 3) == "ull") {
+                this.remaining = this.remaining.substring(3);
+                this.html += '<span class="json-null">null</span>';
+            } else if (next == "t" && this.remaining.substring(0, 3) == "rue") {
+                this.remaining = this.remaining.substring(3);
+                this.html += '<span class="json-true">true</span>';
+            } else if (next == "f" && this.remaining.substring(0, 4) == "alse") {
+                this.remaining = this.remaining.substring(4);
+                this.html += '<span class="json-false">false</span>';
+            } else {
+                this.html += next;
+                this.highlightJson(keywords);
+            }
+            if (keywords != undefined) {
+                this.html += keywords.wrapper[1];
+            }
+        },
+        highlightObject: function (keywords) {
+            this.html += '<span class="json-punctuation">{</span>';
+            var next = this.next();
+            while (next != "}") {
+                if (next == '"' || next == "&quot;") {
+                    var keyHtml = "";
+                    next = this.next();
+                    while (next != '"' && next != '&quot') {
+                        if (next == "\\") {
+                            keyHtml += next;
+                            next = this.nextCharacter();
+                        }
+                        keyHtml += next;
+                        next = this.next();
+                    }
+                    if (keywords != undefined && keywords.isKeyword(keyHtml)) {
+                        this.html += '<span class="json-keyword">&quot;'
+                            + keyHtml
+                            + '&quot;</span>';
+                    } else {
+                        this.html += '<span class="json-object-key">&quot;'
+                            + keyHtml
+                            + '&quot;</span>';
+                    }
+                    next = this.next();
+                    while (next != ":") {
+                        this.html += next;
+                        next = this.next();
+                    }
+                    this.html += '<span class="json-punctuation">:</span>';
+                    var nextKeywords = null;
+                    if (keywords != undefined) {
+                        nextKeywords = keywords.forKey(keyHtml);
+                    }
+                    this.highlightJson(nextKeywords);
+                    next = this.next();
+                    if (next == ",") {
+                        this.html += '<span class="json-punctuation">,</span>';
+                        next = this.next();
+                        continue;
+                    } else while (next != "}") {
+                        this.html += next;
+                        next = this.next();
+                    }
+                } else {
+                    this.html += next;
+                    next = this.next();
+                }
+            }
+            this.html += '<span class="json-punctuation">}</span>';
+        },
+        highlightArray: function (keywords) {
+            this.html += '<span class="json-punctuation">[</span>';
+            var next = this.next();
+            var i = 0;
+            while (next != "]") {
+                this.unshift(next);
+                this.highlightJson(keywords != undefined ? keywords.forItem(i) : null);
+                next = this.next();
+                if (next == ",") {
+                    this.html += '<span class="json-punctuation">,</span>';
+                    next = this.next();
+                    i++;
+                    continue;
+                } else while (next != "]") {
+                    this.html += next;
+                    next = this.next();
+                }
+            }
+            this.html += '<span class="json-punctuation">]</span>';
+        },
+        highlightString: function () {
+            this.html += '<span class="json-punctuation">&quot;</span><span class="json-string">';
+            next = this.next();
+            while (next != '"' && next != '&quot') {
+                if (next == "\\") {
+                    this.html += next;
+                    next = this.nextCharacter();
+                }
+                this.html += next;
+                next = this.next();
+            }
+            this.html += '</span><span class="json-punctuation">&quot;</span>';
+        }
+    };
+
+    function KeywordMap() {
+    }
+    KeywordMap.prototype = {
+        wrapper: ["<span>", "</span>"],
+        keywords: {},
+        isKeyword: function (keyHtml) {
+            return this.keywords[keyHtml] !== undefined;
+        },
+        forKey: function (keyHtml) {
+            return this.keywords[keyHtml];
+        },
+        forItem: function (keyHtml) {
+            return null;
+        }
+    };
+    var schema = new KeywordMap();
+    var schemaMedia = new KeywordMap();
+    var mapToSchemas = new KeywordMap();
+    var links = new KeywordMap();
+    schema.keywords = {
+        // from v3
+        type: null,
+        properties: mapToSchemas,
+        patternProperties: mapToSchemas,
+        additionalProperties: schema,
+        items: schema,
+        additionalItems: schema,
+        required: null,
+        dependencies: mapToSchemas,
+        minimum: null,
+        maximum: null,
+        exclusiveMinimum: null,
+        exclusiveMaximum: null,
+        minItems: null,
+        maxItems: null,
+        uniqueItems: null,
+        pattern: null,
+        minLength: null,
+        maxLength: null,
+        "enum": null,
+        "default": null,
+        title: null,
+        description: null,
+        format: null,
+        divisibleBy: null,
+        disallow: schema,
+        "extends": schema,
+        "id": null,
+        "$ref": null,
+        "$schema": null,
+        // from v4 core
+        multipleOf: null,
+        maxProperties: null,
+        minProperties: null,
+        allOf: schema,
+        anyOf: schema,
+        oneOf: schema,
+        not: schema,
+        definitions: mapToSchemas,
+        // from v4 hyper-schema
+        media: schemaMedia,
+        links: links,
+        pathStart: null,
+        fragmentResolution: null
+    };
+    schema.forItem = function () {
+        return schema;
+    };
+    schemaMedia.keywords = {
+        binaryEncoding: null,
+        type: null
+    };
+    mapToSchemas.wrapper = ['<span class="json-schema-map">', '</span>'];
+    mapToSchemas.forKey = function () {
+        return schema;
+    };
+    links.keywords = {
+        rel: null,
+        href:null,
+        method: null,
+        encType: null,
+        pathStart: null,
+        schema: schema,
+        targetSchema: schema
+    };
+    links.forItem = function () {
+        return links;
+    };
+
+    function highlightElement(element, keywords) {
+        var highlighter = new Highlighter(element.innerHTML);
+        try {
+            highlighter.highlightJson(keywords);
+        } catch (e) {
+            throw e;
+        }
+        element.innerHTML = highlighter.html + highlighter.remaining;
+    }
+
+    if (document.getElementsByClassName == undefined) {
+        document.getElementsByClassName = function(className)
+        {
+            var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)");
+            var allElements = document.getElementsByTagName("*");
+            var results = [];
+
+            var element;
+            for (var i = 0; (element = allElements[i]) != null; i++) {
+                var elementClass = element.className;
+                if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass))
+                    results.push(element);
+            }
+
+            return results;
+        }
+    }
+
+    highlight.highlightSchema = function(element) {
+        highlightElement(element, schema);
+    }
+
+    return highlight;
+});
diff --git a/examples/biojson-doc/lib/jquery.js b/examples/biojson-doc/lib/jquery.js
new file mode 100755 (executable)
index 0000000..da41706
--- /dev/null
@@ -0,0 +1,6 @@
+/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license
+//@ sourceMappingURL=jquery-1.10.2.min.map
+*/
+(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav></:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="<div></div>",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t
+}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/<tbody/i,wt=/<|&#?\w+;/,Tt=/<(?:script|style|link)/i,Ct=/^(?:checkbox|radio)$/i,Nt=/checked\s*(?:[^=]|=\s*.checked.)/i,kt=/^$|\/(?:java|ecma)script/i,Et=/^true\/(.*)/,St=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,At={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1></$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1></$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?"<table>"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle);
+u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=un(e,t),Pt.detach()),Gt[e]=n),n}function un(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[0],"display");return n.remove(),r}x.each(["height","width"],function(e,n){x.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(x.css(e,"display"))?x.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,i),i):0)}}}),x.support.opacity||(x.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=x.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===x.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),x(function(){x.support.reliableMarginRight||(x.cssHooks.marginRight={get:function(e,n){return n?x.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!x.support.pixelPosition&&x.fn.position&&x.each(["top","left"],function(e,n){x.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?x(e).position()[n]+"px":r):t}}})}),x.expr&&x.expr.filters&&(x.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!x.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||x.css(e,"display"))},x.expr.filters.visible=function(e){return!x.expr.filters.hidden(e)}),x.each({margin:"",padding:"",border:"Width"},function(e,t){x.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(x.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Ct.test(e))}).map(function(e,t){var n=x(this).val();return null==n?null:x.isArray(n)?x.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),x.param=function(e,n){var r,i=[],o=function(e,t){t=x.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=x.ajaxSettings&&x.ajaxSettings.traditional),x.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==x.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}x.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){x.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),x.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var mn,yn,vn=x.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Cn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Nn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=x.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=o.href}catch(Ln){yn=a.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(T)||[];if(x.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(l){var u;return o[l]=!0,x.each(e[l]||[],function(e,l){var c=l(n,r,i);return"string"!=typeof c||a||o[c]?a?!(u=c):t:(n.dataTypes.unshift(c),s(c),!1)}),u}return s(n.dataTypes[0])||!o["*"]&&s("*")}function _n(e,n){var r,i,o=x.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&x.extend(!0,e,r),e}x.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,l=e.indexOf(" ");return l>=0&&(i=e.slice(l,e.length),e=e.slice(0,l)),x.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&x.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?x("<div>").append(x.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Cn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?_n(_n(e,x.ajaxSettings),t):_n(x.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,l,u,c,p=x.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),g=x.Callbacks("once memory"),m=p.statusCode||{},y={},v={},b=0,w="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return b||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)m[t]=[m[t],e[t]];else C.always(e[C.status]);return this},abort:function(e){var t=e||w;return u&&u.abort(t),k(0,t),this}};if(h.promise(C).complete=g.add,C.success=C.done,C.error=C.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=x.trim(p.dataType||"*").toLowerCase().match(T)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(mn[3]||("http:"===mn[1]?"80":"443")))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=x.param(p.data,p.traditional)),qn(An,p,n,C),2===b)return C;l=p.global,l&&0===x.active++&&x.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Nn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(x.lastModified[o]&&C.setRequestHeader("If-Modified-Since",x.lastModified[o]),x.etag[o]&&C.setRequestHeader("If-None-Match",x.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&C.setRequestHeader("Content-Type",p.contentType),C.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)C.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,C,p)===!1||2===b))return C.abort();w="abort";for(i in{success:1,error:1,complete:1})C[i](p[i]);if(u=qn(jn,p,n,C)){C.readyState=1,l&&d.trigger("ajaxSend",[C,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){C.abort("timeout")},p.timeout));try{b=1,u.send(y,k)}catch(N){if(!(2>b))throw N;k(-1,N)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,N=n;2!==b&&(b=2,s&&clearTimeout(s),u=t,a=i||"",C.readyState=e>0?4:0,c=e>=200&&300>e||304===e,r&&(w=Mn(p,C,r)),w=On(p,w,C,c),c?(p.ifModified&&(T=C.getResponseHeader("Last-Modified"),T&&(x.lastModified[o]=T),T=C.getResponseHeader("etag"),T&&(x.etag[o]=T)),204===e||"HEAD"===p.type?N="nocontent":304===e?N="notmodified":(N=w.state,y=w.data,v=w.error,c=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),C.status=e,C.statusText=(n||N)+"",c?h.resolveWith(f,[y,N,C]):h.rejectWith(f,[C,N,v]),C.statusCode(m),m=t,l&&d.trigger(c?"ajaxSuccess":"ajaxError",[C,p,c?y:v]),g.fireWith(f,[C,N]),l&&(d.trigger("ajaxComplete",[C,p]),--x.active||x.event.trigger("ajaxStop")))}return C},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,n){return x.get(e,t,n,"script")}}),x.each(["get","post"],function(e,n){x[n]=function(e,r,i,o){return x.isFunction(r)&&(o=o||i,i=r,r=t),x.ajax({url:e,type:n,dataType:o,data:r,success:i})}});function Mn(e,n,r){var i,o,a,s,l=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in l)if(l[s]&&l[s].test(o)){u.unshift(s);break}if(u[0]in r)a=u[0];else{for(s in r){if(!u[0]||e.converters[s+" "+u[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==u[0]&&u.unshift(a),r[a]):t}function On(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(p){return{state:"parsererror",error:a?p:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),x.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=a.head||x("head")[0]||a.documentElement;return{send:function(t,i){n=a.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Fn=[],Bn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Fn.pop()||x.expando+"_"+vn++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,l=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return l||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=x.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,l?n[l]=n[l].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||x.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Fn.push(o)),s&&x.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}x.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=x.ajaxSettings.xhr(),x.support.cors=!!Rn&&"withCredentials"in Rn,Rn=x.support.ajax=!!Rn,Rn&&x.ajaxTransport(function(n){if(!n.crossDomain||x.support.cors){var r;return{send:function(i,o){var a,s,l=n.xhr();if(n.username?l.open(n.type,n.url,n.async,n.username,n.password):l.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)l[s]=n.xhrFields[s];n.mimeType&&l.overrideMimeType&&l.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)l.setRequestHeader(s,i[s])}catch(u){}l.send(n.hasContent&&n.data||null),r=function(e,i){var s,u,c,p;try{if(r&&(i||4===l.readyState))if(r=t,a&&(l.onreadystatechange=x.noop,$n&&delete Pn[a]),i)4!==l.readyState&&l.abort();else{p={},s=l.status,u=l.getAllResponseHeaders(),"string"==typeof l.responseText&&(p.text=l.responseText);try{c=l.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,u)},n.async?4===l.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},x(e).unload($n)),Pn[a]=r),l.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+w+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Yn.exec(t),o=i&&i[3]||(x.cssNumber[e]?"":"px"),a=(x.cssNumber[e]||"px"!==o&&+r)&&Yn.exec(x.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,x.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=x.now()}function Zn(e,t,n){var r,i=(Qn[t]||[]).concat(Qn["*"]),o=0,a=i.length;for(;a>o;o++)if(r=i[o].call(n,t,e))return r}function er(e,t,n){var r,i,o=0,a=Gn.length,s=x.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;for(;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(tr(c,u.opts.specialEasing);a>o;o++)if(r=Gn[o].call(u,e,c,u.opts))return r;return x.map(c,Zn,u),x.isFunction(u.opts.start)&&u.opts.start.call(e,u),x.fx.timer(x.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function tr(e,t){var n,r,i,o,a;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=x.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(er,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,l,u=this,c={},p=e.style,f=e.nodeType&&nn(e),d=x._data(e,"fxshow");n.queue||(s=x._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,u.always(function(){u.always(function(){s.unqueued--,x.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(x.support.inlineBlockNeedsLayout&&"inline"!==ln(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",x.support.shrinkWrapBlocks||u.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Vn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(f?"hide":"show"))continue;c[r]=d&&d[r]||x.style(e,r)}if(!x.isEmptyObject(c)){d?"hidden"in d&&(f=d.hidden):d=x._data(e,"fxshow",{}),o&&(d.hidden=!f),f?x(e).show():u.done(function(){x(e).hide()}),u.done(function(){var t;x._removeData(e,"fxshow");for(t in c)x.style(e,t,c[t])});for(r in c)a=Zn(f?d[r]:0,r,u),r in d||(d[r]=a.start,f&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}x.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),a=function(){var t=er(this,x.extend({},e),o);(i||x._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=x.timers,a=x._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=x._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,a=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=rr.prototype.init,x.fx.tick=function(){var e,n=x.timers,r=0;for(Xn=x.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||x.fx.stop(),Xn=t},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){Un||(Un=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(Un),Un=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){x.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,x.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},x.offset={setOffset:function(e,t,n){var r=x.css(e,"position");"static"===r&&(e.style.position="relative");var i=x(e),o=i.offset(),a=x.css(e,"top"),s=x.css(e,"left"),l=("absolute"===r||"fixed"===r)&&x.inArray("auto",[a,s])>-1,u={},c={},p,f;l?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),x.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(u.top=t.top-o.top+p),null!=t.left&&(u.left=t.left-o.left+f),"using"in t?t.using.call(e,u):i.css(u)}},x.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===x.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(n=e.offset()),n.top+=x.css(e[0],"borderTopWidth",!0),n.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-x.css(r,"marginTop",!0),left:t.left-n.left-x.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);x.fn[e]=function(i){return x.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?x(a).scrollLeft():o,r?o:x(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return x.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}x.each({Height:"height",Width:"width"},function(e,n){x.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){x.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return x.access(this,function(n,r,i){var o;return x.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?x.css(n,r,s):x.style(n,r,i,s)},n,a?i:t,a,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=x:(e.jQuery=e.$=x,"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}))})(window);
diff --git a/examples/biojson-doc/lib/jsonpointer.js b/examples/biojson-doc/lib/jsonpointer.js
new file mode 100755 (executable)
index 0000000..3a662e7
--- /dev/null
@@ -0,0 +1,343 @@
+/**
+ * @author Alexey Kuzmin <alex.s.kuzmin@gmail.com>
+ * @fileoverview JavaScript implementation of JSON Pointer.
+ * @see http://tools.ietf.org/html/rfc6901
+ */
+
+
+
+;(function() {
+  'use strict';
+
+  /**
+   * List of special characters and their escape sequences.
+   * Special characters will be unescaped in order they are listed.
+   * Section 3 of spec.
+   * @type {Array.<Array.<string>>}
+   * @const
+   */
+  var SPECIAL_CHARACTERS = [
+    ['/', '~1'],
+    ['~', '~0']
+  ];
+
+
+  /**
+   * Tokens' separator in JSON pointer string.
+   * Section 3 of spec.
+   * @type {string}
+   * @const
+   */
+  var TOKENS_SEPARATOR = '/';
+
+
+  /**
+   * Prefix for error messages.
+   * @type {string}
+   * @const
+   */
+  var ERROR_MESSAGE_PREFIX = 'JSON Pointer: ';
+
+
+  /**
+   * Validates non-empty pointer string.
+   * @type {RegExp}
+   * @const
+   */
+  var NON_EMPTY_POINTER_REGEXP = /(\/[^\/]*)+/;
+
+
+  /**
+   * List of error messages.
+   * Please keep it in alphabetical order.
+   * @enum {string}
+   */
+  var ErrorMessage = {
+    HYPHEN_IS_NOT_SUPPORTED_IN_ARRAY_CONTEXT:
+        'Implementation does not support "-" token for arrays.',
+    INVALID_DOCUMENT: 'JSON document is not valid.',
+    INVALID_DOCUMENT_TYPE: 'JSON document must be a string or object.',
+    INVALID_POINTER: 'Pointer is not valid.',
+    NON_NUMBER_TOKEN_IN_ARRAY_CONTEXT:
+        'Non-number tokens cannot be used in array context.',
+    TOKEN_WITH_LEADING_ZERO_IN_ARRAY_CONTEXT:
+        'Token with leading zero cannot be used in array context.'
+  };
+
+
+  /**
+   * Returns |target| object's value pointed by |opt_pointer|, returns undefined
+   * if |opt_pointer| points to non-existing value.
+   * If pointer is not provided, validates first argument and returns
+   * evaluator function that takes pointer as argument.
+   * @param {(string|Object|Array)} target Evaluation target.
+   * @param {string=} opt_pointer JSON Pointer string.
+   * @returns {*} Some value.
+   */
+  function getPointedValue(target, opt_pointer) {
+    // .get() method implementation.
+
+    // First argument must be either string or object.
+    if (isString(target)) {
+
+      // If string it must be valid JSON document.
+      try {
+        // Let's try to parse it as JSON.
+        target = JSON.parse(target);
+      }
+      catch (e) {
+        // If parsing failed, an exception will be thrown.
+        throw getError(ErrorMessage.INVALID_DOCUMENT);
+      }
+    }
+    else if (!isObject(target)) {
+      // If not object or string, an exception will be thrown.
+      throw getError(ErrorMessage.INVALID_DOCUMENT_TYPE);
+    }
+
+    // |target| is already parsed, let's create evaluator function for it.
+    var evaluator = createPointerEvaluator(target);
+
+    if (isUndefined(opt_pointer)) {
+      // If pointer was not provided, return evaluator function.
+      return evaluator;
+    }
+    else {
+      // If pointer is provided, return evaluation result.
+      return evaluator(opt_pointer);
+    }
+  }
+
+
+  /**
+   * Returns function that takes JSON Pointer as single argument
+   * and evaluates it in given |target| context.
+   * Returned function throws an exception if pointer is not valid
+   * or any error occurs during evaluation.
+   * @param {*} target Evaluation target.
+   * @returns {Function}
+   */
+  function createPointerEvaluator(target) {
+
+    // Use cache to store already received values.
+    var cache = {};
+
+    return function(pointer) {
+
+      if (!isValidJSONPointer(pointer)) {
+        // If it's not, an exception will be thrown.
+        throw getError(ErrorMessage.INVALID_POINTER);
+      }
+
+      // First, look up in the cache.
+      if (cache.hasOwnProperty(pointer)) {
+        // If cache entry exists, return it's value.
+        return cache[pointer];
+      }
+
+      // Now, when all arguments are valid, we can start evaluation.
+      // First of all, let's convert JSON pointer string to tokens list.
+      var tokensList = parsePointer(pointer);
+      var token;
+      var value = target;
+
+      // Evaluation will be continued till tokens list is not empty
+      // and returned value is not an undefined.
+      while (!isUndefined(value) && !isUndefined(token = tokensList.pop())) {
+        // Let's evaluate token in current context.
+        // `getValue()` might throw an exception, but we won't handle it.
+        value = getValue(value, token);
+      }
+
+      // Pointer evaluation is done, save value in the cache and return it.
+      cache[pointer] = value;
+      return value;
+    };
+  }
+
+
+  /**
+   * Returns true if given |pointer| is valid, returns false otherwise.
+   * @param {!string} pointer
+   * @returns {boolean} Whether pointer is valid.
+   */
+  function isValidJSONPointer(pointer) {
+    // Validates JSON pointer string.
+
+    if (!isString(pointer)) {
+      // If it's not a string, it obviously is not valid.
+      return false;
+    }
+
+    if ('' === pointer) {
+      // If it is string and is an empty string, it's valid.
+      return true;
+    }
+
+    // If it is non-empty string, it must match spec defined format.
+    // Check Section 3 of specification for concrete syntax.
+    return NON_EMPTY_POINTER_REGEXP.test(pointer);
+  }
+
+
+  /**
+   * Returns tokens list for given |pointer|. List is reversed, e.g.
+   *     '/simple/path' -> ['path', 'simple']
+   * @param {!string} pointer JSON pointer string.
+   * @returns {Array} List of tokens.
+   */
+  function parsePointer(pointer) {
+    // Converts JSON pointer string into tokens list.
+
+    // Let's split pointer string by tokens' separator character.
+    // Also we will reverse resulting array to simplify it's further usage.
+    var tokens = pointer.split(TOKENS_SEPARATOR).reverse();
+
+    // Last item in resulting array is always an empty string,
+    // we don't need it, let's remove it.
+    tokens.pop();
+
+    // Now tokens' array is ready to use, let's return it.
+    return tokens;
+  }
+
+
+  /**
+   * Decodes all escape sequences in given |rawReferenceToken|.
+   * @param {!string} rawReferenceToken
+   * @returns {string} Unescaped reference token.
+   */
+  function unescapeReferenceToken(rawReferenceToken) {
+    // Unescapes reference token. See Section 3 of specification.
+
+    var referenceToken = rawReferenceToken;
+    var character;
+    var escapeSequence;
+    var replaceRegExp;
+
+    // Order of unescaping does matter.
+    // That's why an array is used here and not hash.
+    SPECIAL_CHARACTERS.forEach(function(pair) {
+      character = pair[0];
+      escapeSequence = pair[1];
+      replaceRegExp = new RegExp(escapeSequence, 'g');
+      referenceToken = referenceToken.replace(replaceRegExp, character);
+    });
+
+    return referenceToken;
+  }
+
+
+  /**
+   * Returns value pointed by |token| in evaluation |context|.
+   * Throws an exception if any error occurs.
+   * @param {*} context Current evaluation context.
+   * @param {!string} token Unescaped reference token.
+   * @returns {*} Some value or undefined if value if not found.
+   */
+  function getValue(context, token) {
+    // Reference token evaluation. See Section 4 of spec.
+
+    // First of all we should unescape all special characters in token.
+    token = unescapeReferenceToken(token);
+
+    // Further actions depend of context of evaluation.
+
+    if (isArray(context)) {
+      // In array context there are more strict requirements
+      // for token value.
+
+      if ('-' === token) {
+        // Token cannot be a "-" character,
+        // it has no sense in current implementation.
+        throw getError(ErrorMessage.HYPHEN_IS_NOT_SUPPORTED_IN_ARRAY_CONTEXT);
+      }
+      if (!isNumber(token)) {
+        // Token cannot be non-number.
+        throw getError(ErrorMessage.NON_NUMBER_TOKEN_IN_ARRAY_CONTEXT);
+      }
+      if (token.length > 1 && '0' === token[0]) {
+        // Token cannot be non-zero number with leading zero.
+        throw getError(ErrorMessage.TOKEN_WITH_LEADING_ZERO_IN_ARRAY_CONTEXT);
+      }
+      // If all conditions are met, simply return element
+      // with token's value index.
+      // It might be undefined, but it's ok.
+      return context[token];
+    }
+
+    if (isObject(context)) {
+      // In object context we can simply return element w/ key equal to token.
+      // It might be undefined, but it's ok.
+      return context[token];
+    }
+
+    // If context is not an array or an object,
+    // token evaluation is not possible.
+    // This is the expected situation and so we won't throw an error,
+    // undefined value is perfectly suitable here.
+    return;
+  }
+
+
+  /**
+   * Returns Error instance for throwing.
+   * @param {string} message Error message.
+   * @returns {Error}
+   */
+  function getError(message) {
+    return new Error(ERROR_MESSAGE_PREFIX + message);
+  }
+
+
+  function isObject(o) {
+    return 'object' === typeof o && null !== o;
+  }
+
+
+  function isArray(a) {
+    return Array.isArray(a);
+  }
+
+
+  function isNumber(n) {
+    return !isNaN(Number(n));
+  }
+
+
+  function isString(s) {
+    return 'string' === typeof s || s instanceof String;
+  }
+
+
+  function isUndefined(v) {
+    return 'undefined' === typeof v;
+  }
+
+
+  // Let's expose API to the world.
+
+  var jsonpointer = {
+    get: getPointedValue
+  };
+
+  if ('object' === typeof exports) {
+    // If `exports` is an object, we are in Node.js context.
+    // We are supposed to act as Node.js package.
+    module.exports = jsonpointer;
+  } else if ('function' === typeof define && define.amd) {
+    // If there is global function `define()` and `define.amd` is defined,
+    // we are supposed to act as AMD module.
+    define(function() {
+      return jsonpointer;
+    });
+  } else {
+    // Last resort.
+    // Let's create global `jsonpointer` object.
+    this.jsonpointer = jsonpointer;
+  }
+
+}).call((function() {
+  'use strict';
+  return (typeof window !== 'undefined' ? window : global);
+})());
diff --git a/examples/biojson-doc/lib/marked.js b/examples/biojson-doc/lib/marked.js
new file mode 100755 (executable)
index 0000000..da025bd
--- /dev/null
@@ -0,0 +1,1194 @@
+/*
+ * Copyright 2013 Laurent Bovet <laurent.bovet@windmaster.ch>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+;(function() {
+
+    /**
+     * Block-Level Grammar
+     */
+
+    var block = {
+        newline: /^\n+/,
+        code: /^( {4}[^\n]+\n*)+/,
+        fences: noop,
+        hr: /^( *[-*_]){3,} *(?:\n+|$)/,
+        heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
+        nptable: noop,
+        lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
+        blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
+        list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
+        html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
+        def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
+        table: noop,
+        paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
+        text: /^[^\n]+/
+    };
+
+    block.bullet = /(?:[*+-]|\d+\.)/;
+    block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
+    block.item = replace(block.item, 'gm')
+        (/bull/g, block.bullet)
+        ();
+
+    block.list = replace(block.list)
+        (/bull/g, block.bullet)
+        ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
+        ();
+
+    block._tag = '(?!(?:'
+        + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
+        + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
+        + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
+
+    block.html = replace(block.html)
+        ('comment', /<!--[\s\S]*?-->/)
+        ('closed', /<(tag)[\s\S]+?<\/\1>/)
+        ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
+        (/tag/g, block._tag)
+        ();
+
+    block.paragraph = replace(block.paragraph)
+        ('hr', block.hr)
+        ('heading', block.heading)
+        ('lheading', block.lheading)
+        ('blockquote', block.blockquote)
+        ('tag', '<' + block._tag)
+        ('def', block.def)
+        ();
+
+    /**
+     * Normal Block Grammar
+     */
+
+    block.normal = merge({}, block);
+
+    /**
+     * GFM Block Grammar
+     */
+
+    block.gfm = merge({}, block.normal, {
+        fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
+        paragraph: /^/
+    });
+
+    block.gfm.paragraph = replace(block.paragraph)
+        ('(?!', '(?!'
+            + block.gfm.fences.source.replace('\\1', '\\2') + '|'
+            + block.list.source.replace('\\1', '\\3') + '|')
+        ();
+
+    /**
+     * GFM + Tables Block Grammar
+     */
+
+    block.tables = merge({}, block.gfm, {
+        nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
+        table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
+    });
+
+    /**
+     * Block Lexer
+     */
+
+    function Lexer(options) {
+        this.tokens = [];
+        this.tokens.links = {};
+        this.options = options || marked.defaults;
+        this.rules = block.normal;
+
+        if (this.options.gfm) {
+            if (this.options.tables) {
+                this.rules = block.tables;
+            } else {
+                this.rules = block.gfm;
+            }
+        }
+    }
+
+    /**
+     * Expose Block Rules
+     */
+
+    Lexer.rules = block;
+
+    /**
+     * Static Lex Method
+     */
+
+    Lexer.lex = function(src, options) {
+        var lexer = new Lexer(options);
+        return lexer.lex(src);
+    };
+
+    /**
+     * Preprocessing
+     */
+
+    Lexer.prototype.lex = function(src) {
+        src = src
+            .replace(/\r\n|\r/g, '\n')
+            .replace(/\t/g, '    ')
+            .replace(/\u00a0/g, ' ')
+            .replace(/\u2424/g, '\n');
+
+        return this.token(src, true);
+    };
+
+    /**
+     * Lexing
+     */
+
+    Lexer.prototype.token = function(src, top) {
+        var src = src.replace(/^ +$/gm, '')
+            , next
+            , loose
+            , cap
+            , bull
+            , b
+            , item
+            , space
+            , i
+            , l;
+
+        while (src) {
+            // newline
+            if (cap = this.rules.newline.exec(src)) {
+                src = src.substring(cap[0].length);
+                if (cap[0].length > 1) {
+                    this.tokens.push({
+                        type: 'space'
+                    });
+                }
+            }
+
+            // code
+            if (cap = this.rules.code.exec(src)) {
+                src = src.substring(cap[0].length);
+                cap = cap[0].replace(/^ {4}/gm, '');
+                this.tokens.push({
+                    type: 'code',
+                    text: !this.options.pedantic
+                        ? cap.replace(/\n+$/, '')
+                        : cap
+                });
+                continue;
+            }
+
+            // fences (gfm)
+            if (cap = this.rules.fences.exec(src)) {
+                src = src.substring(cap[0].length);
+                this.tokens.push({
+                    type: 'code',
+                    lang: cap[2],
+                    text: cap[3]
+                });
+                continue;
+            }
+
+            // heading
+            if (cap = this.rules.heading.exec(src)) {
+                src = src.substring(cap[0].length);
+                this.tokens.push({
+                    type: 'heading',
+                    depth: cap[1].length,
+                    text: cap[2]
+                });
+                continue;
+            }
+
+            // table no leading pipe (gfm)
+            if (top && (cap = this.rules.nptable.exec(src))) {
+                src = src.substring(cap[0].length);
+
+                item = {
+                    type: 'table',
+                    header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+                    align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+                    cells: cap[3].replace(/\n$/, '').split('\n')
+                };
+
+                for (i = 0; i < item.align.length; i++) {
+                    if (/^ *-+: *$/.test(item.align[i])) {
+                        item.align[i] = 'right';
+                    } else if (/^ *:-+: *$/.test(item.align[i])) {
+                        item.align[i] = 'center';
+                    } else if (/^ *:-+ *$/.test(item.align[i])) {
+                        item.align[i] = 'left';
+                    } else {
+                        item.align[i] = null;
+                    }
+                }
+
+                for (i = 0; i < item.cells.length; i++) {
+                    item.cells[i] = item.cells[i].split(/ *\| */);
+                }
+
+                this.tokens.push(item);
+
+                continue;
+            }
+
+            // lheading
+            if (cap = this.rules.lheading.exec(src)) {
+                src = src.substring(cap[0].length);
+                this.tokens.push({
+                    type: 'heading',
+                    depth: cap[2] === '=' ? 1 : 2,
+                    text: cap[1]
+                });
+                continue;
+            }
+
+            // hr
+            if (cap = this.rules.hr.exec(src)) {
+                src = src.substring(cap[0].length);
+                this.tokens.push({
+                    type: 'hr'
+                });
+                continue;
+            }
+
+            // blockquote
+            if (cap = this.rules.blockquote.exec(src)) {
+                src = src.substring(cap[0].length);
+
+                this.tokens.push({
+                    type: 'blockquote_start'
+                });
+
+                cap = cap[0].replace(/^ *> ?/gm, '');
+
+                // Pass `top` to keep the current
+                // "toplevel" state. This is exactly
+                // how markdown.pl works.
+                this.token(cap, top);
+
+                this.tokens.push({
+                    type: 'blockquote_end'
+                });
+
+                continue;
+            }
+
+            // list
+            if (cap = this.rules.list.exec(src)) {
+                src = src.substring(cap[0].length);
+                bull = cap[2];
+
+                this.tokens.push({
+                    type: 'list_start',
+                    ordered: bull.length > 1
+                });
+
+                // Get each top-level item.
+                cap = cap[0].match(this.rules.item);
+
+                next = false;
+                l = cap.length;
+                i = 0;
+
+                for (; i < l; i++) {
+                    item = cap[i];
+
+                    // Remove the list item's bullet
+                    // so it is seen as the next token.
+                    space = item.length;
+                    item = item.replace(/^ *([*+-]|\d+\.) +/, '');
+
+                    // Outdent whatever the
+                    // list item contains. Hacky.
+                    if (~item.indexOf('\n ')) {
+                        space -= item.length;
+                        item = !this.options.pedantic
+                            ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
+                            : item.replace(/^ {1,4}/gm, '');
+                    }
+
+                    // Determine whether the next list item belongs here.
+                    // Backpedal if it does not belong in this list.
+                    if (this.options.smartLists && i !== l - 1) {
+                        b = block.bullet.exec(cap[i + 1])[0];
+                        if (bull !== b && !(bull.length > 1 && b.length > 1)) {
+                            src = cap.slice(i + 1).join('\n') + src;
+                            i = l - 1;
+                        }
+                    }
+
+                    // Determine whether item is loose or not.
+                    // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
+                    // for discount behavior.
+                    loose = next || /\n\n(?!\s*$)/.test(item);
+                    if (i !== l - 1) {
+                        next = item.charAt(item.length - 1) === '\n';
+                        if (!loose) loose = next;
+                    }
+
+                    this.tokens.push({
+                        type: loose
+                            ? 'loose_item_start'
+                            : 'list_item_start'
+                    });
+
+                    // Recurse.
+                    this.token(item, false);
+
+                    this.tokens.push({
+                        type: 'list_item_end'
+                    });
+                }
+
+                this.tokens.push({
+                    type: 'list_end'
+                });
+
+                continue;
+            }
+
+            // html
+            if (cap = this.rules.html.exec(src)) {
+                src = src.substring(cap[0].length);
+                this.tokens.push({
+                    type: this.options.sanitize
+                        ? 'paragraph'
+                        : 'html',
+                    pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
+                    text: cap[0]
+                });
+                continue;
+            }
+
+            // def
+            if (top && (cap = this.rules.def.exec(src))) {
+                src = src.substring(cap[0].length);
+                this.tokens.links[cap[1].toLowerCase()] = {
+                    href: cap[2],
+                    title: cap[3]
+                };
+                continue;
+            }
+
+            // table (gfm)
+            if (top && (cap = this.rules.table.exec(src))) {
+                src = src.substring(cap[0].length);
+
+                item = {
+                    type: 'table',
+                    header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+                    align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+                    cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
+                };
+
+                for (i = 0; i < item.align.length; i++) {
+                    if (/^ *-+: *$/.test(item.align[i])) {
+                        item.align[i] = 'right';
+                    } else if (/^ *:-+: *$/.test(item.align[i])) {
+                        item.align[i] = 'center';
+                    } else if (/^ *:-+ *$/.test(item.align[i])) {
+                        item.align[i] = 'left';
+                    } else {
+                        item.align[i] = null;
+                    }
+                }
+
+                for (i = 0; i < item.cells.length; i++) {
+                    item.cells[i] = item.cells[i]
+                        .replace(/^ *\| *| *\| *$/g, '')
+                        .split(/ *\| */);
+                }
+
+                this.tokens.push(item);
+
+                continue;
+            }
+
+            // top-level paragraph
+            if (top && (cap = this.rules.paragraph.exec(src))) {
+                src = src.substring(cap[0].length);
+                this.tokens.push({
+                    type: 'paragraph',
+                    text: cap[1].charAt(cap[1].length - 1) === '\n'
+                        ? cap[1].slice(0, -1)
+                        : cap[1]
+                });
+                continue;
+            }
+
+            // text
+            if (cap = this.rules.text.exec(src)) {
+                // Top-level should never reach here.
+                src = src.substring(cap[0].length);
+                this.tokens.push({
+                    type: 'text',
+                    text: cap[0]
+                });
+                continue;
+            }
+
+            if (src) {
+                throw new
+                    Error('Infinite loop on byte: ' + src.charCodeAt(0));
+            }
+        }
+
+        return this.tokens;
+    };
+
+    /**
+     * Inline-Level Grammar
+     */
+
+    var inline = {
+        escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
+        autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
+        url: noop,
+        tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
+        link: /^!?\[(inside)\]\(href\)/,
+        reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
+        nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
+        strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
+        em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
+        code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
+        br: /^ {2,}\n(?!\s*$)/,
+        del: noop,
+        text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
+    };
+
+    inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
+    inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
+
+    inline.link = replace(inline.link)
+        ('inside', inline._inside)
+        ('href', inline._href)
+        ();
+
+    inline.reflink = replace(inline.reflink)
+        ('inside', inline._inside)
+        ();
+
+    /**
+     * Normal Inline Grammar
+     */
+
+    inline.normal = merge({}, inline);
+
+    /**
+     * Pedantic Inline Grammar
+     */
+
+    inline.pedantic = merge({}, inline.normal, {
+        strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
+        em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
+    });
+
+    /**
+     * GFM Inline Grammar
+     */
+
+    inline.gfm = merge({}, inline.normal, {
+        escape: replace(inline.escape)('])', '~|])')(),
+        url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
+        del: /^~~(?=\S)([\s\S]*?\S)~~/,
+        text: replace(inline.text)
+            (']|', '~]|')
+            ('|', '|https?://|')
+            ()
+    });
+
+    /**
+     * GFM + Line Breaks Inline Grammar
+     */
+
+    inline.breaks = merge({}, inline.gfm, {
+        br: replace(inline.br)('{2,}', '*')(),
+        text: replace(inline.gfm.text)('{2,}', '*')()
+    });
+
+    /**
+     * Inline Lexer & Compiler
+     */
+
+    function InlineLexer(links, options) {
+        this.options = options || marked.defaults;
+        this.links = links;
+        this.rules = inline.normal;
+        this.renderer = this.options.renderer || new Renderer;
+
+        if (!this.links) {
+            throw new
+                Error('Tokens array requires a `links` property.');
+        }
+
+        if (this.options.gfm) {
+            if (this.options.breaks) {
+                this.rules = inline.breaks;
+            } else {
+                this.rules = inline.gfm;
+            }
+        } else if (this.options.pedantic) {
+            this.rules = inline.pedantic;
+        }
+    }
+
+    /**
+     * Expose Inline Rules
+     */
+
+    InlineLexer.rules = inline;
+
+    /**
+     * Static Lexing/Compiling Method
+     */
+
+    InlineLexer.output = function(src, links, options) {
+        var inline = new InlineLexer(links, options);
+        return inline.output(src);
+    };
+
+    /**
+     * Lexing/Compiling
+     */
+
+    InlineLexer.prototype.output = function(src) {
+        var out = ''
+            , link
+            , text
+            , href
+            , cap;
+
+        while (src) {
+            // escape
+            if (cap = this.rules.escape.exec(src)) {
+                src = src.substring(cap[0].length);
+                out += cap[1];
+                continue;
+            }
+
+            // autolink
+            if (cap = this.rules.autolink.exec(src)) {
+                src = src.substring(cap[0].length);
+                if (cap[2] === '@') {
+                    text = cap[1].charAt(6) === ':'
+                        ? this.mangle(cap[1].substring(7))
+                        : this.mangle(cap[1]);
+                    href = this.mangle('mailto:') + text;
+                } else {
+                    text = escape(cap[1]);
+                    href = text;
+                }
+                out += this.renderer.link(href, null, text);
+                continue;
+            }
+
+            // url (gfm)
+            if (cap = this.rules.url.exec(src)) {
+                src = src.substring(cap[0].length);
+                text = escape(cap[1]);
+                href = text;
+                out += this.renderer.link(href, null, text);
+                continue;
+            }
+
+            // tag
+            if (cap = this.rules.tag.exec(src)) {
+                src = src.substring(cap[0].length);
+                out += this.options.sanitize
+                    ? escape(cap[0])
+                    : cap[0];
+                continue;
+            }
+
+            // link
+            if (cap = this.rules.link.exec(src)) {
+                src = src.substring(cap[0].length);
+                out += this.outputLink(cap, {
+                    href: cap[2],
+                    title: cap[3]
+                });
+                continue;
+            }
+
+            // reflink, nolink
+            if ((cap = this.rules.reflink.exec(src))
+                || (cap = this.rules.nolink.exec(src))) {
+                src = src.substring(cap[0].length);
+                link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
+                link = this.links[link.toLowerCase()];
+                if (!link || !link.href) {
+                    out += cap[0].charAt(0);
+                    src = cap[0].substring(1) + src;
+                    continue;
+                }
+                out += this.outputLink(cap, link);
+                continue;
+            }
+
+            // strong
+            if (cap = this.rules.strong.exec(src)) {
+                src = src.substring(cap[0].length);
+                out += this.renderer.strong(this.output(cap[2] || cap[1]));
+                continue;
+            }
+
+            // em
+            if (cap = this.rules.em.exec(src)) {
+                src = src.substring(cap[0].length);
+                out += this.renderer.em(this.output(cap[2] || cap[1]));
+                continue;
+            }
+
+            // code
+            if (cap = this.rules.code.exec(src)) {
+                src = src.substring(cap[0].length);
+                out += this.renderer.codespan(escape(cap[2], true));
+                continue;
+            }
+
+            // br
+            if (cap = this.rules.br.exec(src)) {
+                src = src.substring(cap[0].length);
+                out += this.renderer.br();
+                continue;
+            }
+
+            // del (gfm)
+            if (cap = this.rules.del.exec(src)) {
+                src = src.substring(cap[0].length);
+                out += this.renderer.del(this.output(cap[1]));
+                continue;
+            }
+
+            // text
+            if (cap = this.rules.text.exec(src)) {
+                src = src.substring(cap[0].length);
+                out += escape(this.smartypants(cap[0]));
+                continue;
+            }
+
+            if (src) {
+                throw new
+                    Error('Infinite loop on byte: ' + src.charCodeAt(0));
+            }
+        }
+
+        return out;
+    };
+
+    /**
+     * Compile Link
+     */
+
+    InlineLexer.prototype.outputLink = function(cap, link) {
+        var href = escape(link.href)
+            , title = link.title ? escape(link.title) : null;
+
+        if (cap[0].charAt(0) !== '!') {
+            return this.renderer.link(href, title, this.output(cap[1]));
+        } else {
+            return this.renderer.image(href, title, escape(cap[1]));
+        }
+    };
+
+    /**
+     * Smartypants Transformations
+     */
+
+    InlineLexer.prototype.smartypants = function(text) {
+        if (!this.options.smartypants) return text;
+        return text
+            // em-dashes
+            .replace(/--/g, '\u2014')
+            // opening singles
+            .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
+            // closing singles & apostrophes
+            .replace(/'/g, '\u2019')
+            // opening doubles
+            .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
+            // closing doubles
+            .replace(/"/g, '\u201d')
+            // ellipses
+            .replace(/\.{3}/g, '\u2026');
+    };
+
+    /**
+     * Mangle Links
+     */
+
+    InlineLexer.prototype.mangle = function(text) {
+        var out = ''
+            , l = text.length
+            , i = 0
+            , ch;
+
+        for (; i < l; i++) {
+            ch = text.charCodeAt(i);
+            if (Math.random() > 0.5) {
+                ch = 'x' + ch.toString(16);
+            }
+            out += '&#' + ch + ';';
+        }
+
+        return out;
+    };
+
+    /**
+     * Renderer
+     */
+
+    function Renderer() {}
+
+    Renderer.prototype.code = function(code, lang) {
+        if (!lang) {
+            return '<pre><code>'
+                + escape(code, true)
+                + '\n</code></pre>';
+        }
+
+        return '<pre><code class="'
+            + 'lang-'
+            + lang
+            + '">'
+            + escape(code)
+            + '\n</code></pre>\n';
+    };
+
+    Renderer.prototype.blockquote = function(quote) {
+        return '<blockquote>\n' + quote + '</blockquote>\n';
+    };
+
+    Renderer.prototype.html = function(html) {
+        return html;
+    };
+
+    Renderer.prototype.heading = function(text, level, raw, options) {
+        return '<h'
+            + level
+            + '>'
+            + text
+            + '</h'
+            + level
+            + '>\n';
+    };
+
+    Renderer.prototype.hr = function() {
+        return '<hr>\n';
+    };
+
+    Renderer.prototype.list = function(body, ordered) {
+        var type = ordered ? 'ol' : 'ul';
+        return '<' + type + '>\n' + body + '</' + type + '>\n';
+    };
+
+    Renderer.prototype.listitem = function(text) {
+        return '<li>' + text + '</li>\n';
+    };
+
+    Renderer.prototype.paragraph = function(text) {
+        return '<p>' + text + '</p>\n';
+    };
+
+    Renderer.prototype.table = function(header, body) {
+        return '<table>\n'
+            + '<thead>\n'
+            + header
+            + '</thead>\n'
+            + '<tbody>\n'
+            + body
+            + '</tbody>\n'
+            + '</table>\n';
+    };
+
+    Renderer.prototype.tablerow = function(content) {
+        return '<tr>\n' + content + '</tr>\n';
+    };
+
+    Renderer.prototype.tablecell = function(content, flags) {
+        var type = flags.header ? 'th' : 'td';
+        var tag = flags.align
+            ? '<' + type + ' style="text-align:' + flags.align + '">'
+            : '<' + type + '>';
+        return tag + content + '</' + type + '>\n';
+    };
+
+// span level renderer
+    Renderer.prototype.strong = function(text) {
+        return '<strong>' + text + '</strong>';
+    };
+
+    Renderer.prototype.em = function(text) {
+        return '<em>' + text + '</em>';
+    };
+
+    Renderer.prototype.codespan = function(text) {
+        return '<code>' + text + '</code>';
+    };
+
+    Renderer.prototype.br = function() {
+        return '<br>';
+    };
+
+    Renderer.prototype.del = function(text) {
+        return '<del>' + text + '</del>';
+    };
+
+    Renderer.prototype.link = function(href, title, text) {
+        var out = '<a href="' + href + '"';
+        if (title) {
+            out += ' title="' + title + '"';
+        }
+        out += '>' + text + '</a>';
+        return out;
+    };
+
+    Renderer.prototype.image = function(href, title, text) {
+        var out = '<img src="' + href + '" alt="' + text + '"';
+        if (title) {
+            out += ' title="' + title + '"';
+        }
+        out += '>';
+        return out;
+    };
+
+    /**
+     * Parsing & Compiling
+     */
+
+    function Parser(options) {
+        this.tokens = [];
+        this.token = null;
+        this.options = options || marked.defaults;
+        this.options.renderer = this.options.renderer || new Renderer;
+        this.renderer = this.options.renderer;
+    }
+
+    /**
+     * Static Parse Method
+     */
+
+    Parser.parse = function(src, options, renderer) {
+        var parser = new Parser(options, renderer);
+        return parser.parse(src);
+    };
+
+    /**
+     * Parse Loop
+     */
+
+    Parser.prototype.parse = function(src) {
+        this.inline = new InlineLexer(src.links, this.options, this.renderer);
+        this.tokens = src.reverse();
+
+        var out = '';
+        while (this.next()) {
+            out += this.tok();
+        }
+
+        return out;
+    };
+
+    /**
+     * Next Token
+     */
+
+    Parser.prototype.next = function() {
+        return this.token = this.tokens.pop();
+    };
+
+    /**
+     * Preview Next Token
+     */
+
+    Parser.prototype.peek = function() {
+        return this.tokens[this.tokens.length - 1] || 0;
+    };
+
+    /**
+     * Parse Text Tokens
+     */
+
+    Parser.prototype.parseText = function() {
+        var body = this.token.text;
+
+        while (this.peek().type === 'text') {
+            body += '\n' + this.next().text;
+        }
+
+        return this.inline.output(body);
+    };
+
+    /**
+     * Parse Current Token
+     */
+
+    Parser.prototype.tok = function() {
+        switch (this.token.type) {
+            case 'space': {
+                return '';
+            }
+            case 'hr': {
+                return this.renderer.hr();
+            }
+            case 'heading': {
+                return this.renderer.heading(
+                    this.inline.output(this.token.text),
+                    this.token.depth
+                );
+            }
+            case 'code': {
+                return this.renderer.code(this.token.text, this.token.lang);
+            }
+            case 'table': {
+                var header = ''
+                    , body = ''
+                    , i
+                    , row
+                    , cell
+                    , flags
+                    , j;
+
+                // header
+                cell = '';
+                for (i = 0; i < this.token.header.length; i++) {
+                    flags = { header: true, align: this.token.align[i] };
+                    cell += this.renderer.tablecell(
+                        this.inline.output(this.token.header[i]),
+                        { header: true, align: this.token.align[i] }
+                    );
+                }
+                header += this.renderer.tablerow(cell);
+
+                for (i = 0; i < this.token.cells.length; i++) {
+                    row = this.token.cells[i];
+
+                    cell = '';
+                    for (j = 0; j < row.length; j++) {
+                        cell += this.renderer.tablecell(
+                            this.inline.output(row[j]),
+                            { header: false, align: this.token.align[j] }
+                        );
+                    }
+
+                    body += this.renderer.tablerow(cell);
+                }
+                return this.renderer.table(header, body);
+            }
+            case 'blockquote_start': {
+                var body = '';
+
+                while (this.next().type !== 'blockquote_end') {
+                    body += this.tok();
+                }
+
+                return this.renderer.blockquote(body);
+            }
+            case 'list_start': {
+                var body = ''
+                    , ordered = this.token.ordered;
+
+                while (this.next().type !== 'list_end') {
+                    body += this.tok();
+                }
+
+                return this.renderer.list(body, ordered);
+            }
+            case 'list_item_start': {
+                var body = '';
+
+                while (this.next().type !== 'list_item_end') {
+                    body += this.token.type === 'text'
+                        ? this.parseText()
+                        : this.tok();
+                }
+
+                return this.renderer.listitem(body);
+            }
+            case 'loose_item_start': {
+                var body = '';
+
+                while (this.next().type !== 'list_item_end') {
+                    body += this.tok();
+                }
+
+                return this.renderer.listitem(body);
+            }
+            case 'html': {
+                var html = !this.token.pre && !this.options.pedantic
+                    ? this.inline.output(this.token.text)
+                    : this.token.text;
+                return this.renderer.html(html);
+            }
+            case 'paragraph': {
+                return this.renderer.paragraph(this.inline.output(this.token.text));
+            }
+            case 'text': {
+                return this.renderer.paragraph(this.parseText());
+            }
+        }
+    };
+
+    /**
+     * Helpers
+     */
+
+    function escape(html, encode) {
+        return html
+            .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
+            .replace(/</g, '&lt;')
+            .replace(/>/g, '&gt;')
+            .replace(/"/g, '&quot;')
+            .replace(/'/g, '&#39;');
+    }
+
+    function replace(regex, opt) {
+        regex = regex.source;
+        opt = opt || '';
+        return function self(name, val) {
+            if (!name) return new RegExp(regex, opt);
+            val = val.source || val;
+            val = val.replace(/(^|[^\[])\^/g, '$1');
+            regex = regex.replace(name, val);
+            return self;
+        };
+    }
+
+    function noop() {}
+    noop.exec = noop;
+
+    function merge(obj) {
+        var i = 1
+            , target
+            , key;
+
+        for (; i < arguments.length; i++) {
+            target = arguments[i];
+            for (key in target) {
+                if (Object.prototype.hasOwnProperty.call(target, key)) {
+                    obj[key] = target[key];
+                }
+            }
+        }
+
+        return obj;
+    }
+
+
+    /**
+     * Marked
+     */
+
+    function marked(src, opt, callback) {
+        if (callback || typeof opt === 'function') {
+            if (!callback) {
+                callback = opt;
+                opt = null;
+            }
+
+            opt = merge({}, marked.defaults, opt || {});
+
+            var highlight = opt.highlight
+                , tokens
+                , pending
+                , i = 0;
+
+            try {
+                tokens = Lexer.lex(src, opt)
+            } catch (e) {
+                return callback(e);
+            }
+
+            pending = tokens.length;
+
+            var done = function() {
+                var out, err;
+
+                try {
+                    out = Parser.parse(tokens, opt);
+                } catch (e) {
+                    err = e;
+                }
+
+                opt.highlight = highlight;
+
+                return err
+                    ? callback(err)
+                    : callback(null, out);
+            };
+
+            return done();
+        }
+        try {
+            if (opt) opt = merge({}, marked.defaults, opt);
+            return Parser.parse(Lexer.lex(src, opt), opt);
+        } catch (e) {
+            e.message += '\nPlease report this to https://github.com/chjj/marked.';
+            if ((opt || marked.defaults).silent) {
+                return '<p>An error occured:</p><pre>'
+                    + escape(e.message + '', true)
+                    + '</pre>';
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * Options
+     */
+
+    marked.options =
+        marked.setOptions = function(opt) {
+            merge(marked.defaults, opt);
+            return marked;
+        };
+
+    marked.defaults = {
+        gfm: true,
+        tables: true,
+        breaks: false,
+        pedantic: false,
+        sanitize: false,
+        smartLists: false,
+        silent: false,
+        smartypants: false,
+        renderer: new Renderer
+    };
+
+    /**
+     * Expose
+     */
+
+    marked.Parser = Parser;
+    marked.parser = Parser.parse;
+
+    marked.Renderer = Renderer;
+
+    marked.Lexer = Lexer;
+    marked.lexer = Lexer.lex;
+
+    marked.InlineLexer = InlineLexer;
+    marked.inlineLexer = InlineLexer.output;
+
+    marked.parse = marked;
+
+    if (typeof exports === 'object') {
+        module.exports = marked;
+    } else if (typeof define === 'function' && define.amd) {
+        define(function() { return marked; });
+    } else {
+        this.marked = marked;
+    }
+
+}).call(function() {
+        return this || (typeof window !== 'undefined' ? window : global);
+    }());
diff --git a/examples/biojson-doc/lib/require.js b/examples/biojson-doc/lib/require.js
new file mode 100755 (executable)
index 0000000..ee9999f
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ RequireJS 2.1.9 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
+ Available via the MIT or new BSD license.
+ see: http://github.com/jrburke/requirejs for details
+*/
+var requirejs,require,define;
+(function(Z){function H(b){return"[object Function]"===L.call(b)}function I(b){return"[object Array]"===L.call(b)}function y(b,c){if(b){var e;for(e=0;e<b.length&&(!b[e]||!c(b[e],e,b));e+=1);}}function M(b,c){if(b){var e;for(e=b.length-1;-1<e&&(!b[e]||!c(b[e],e,b));e-=1);}}function t(b,c){return ga.call(b,c)}function l(b,c){return t(b,c)&&b[c]}function F(b,c){for(var e in b)if(t(b,e)&&c(b[e],e))break}function Q(b,c,e,h){c&&F(c,function(c,j){if(e||!t(b,j))h&&"string"!==typeof c?(b[j]||(b[j]={}),Q(b[j],
+c,e,h)):b[j]=c});return b}function u(b,c){return function(){return c.apply(b,arguments)}}function aa(b){throw b;}function ba(b){if(!b)return b;var c=Z;y(b.split("."),function(b){c=c[b]});return c}function A(b,c,e,h){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=h;e&&(c.originalError=e);return c}function ha(b){function c(a,f,b){var d,m,c,g,e,h,j,i=f&&f.split("/");d=i;var n=k.map,p=n&&n["*"];if(a&&"."===a.charAt(0))if(f){d=l(k.pkgs,f)?i=[f]:i.slice(0,i.length-
+1);f=a=d.concat(a.split("/"));for(d=0;f[d];d+=1)if(m=f[d],"."===m)f.splice(d,1),d-=1;else if(".."===m)if(1===d&&(".."===f[2]||".."===f[0]))break;else 0<d&&(f.splice(d-1,2),d-=2);d=l(k.pkgs,f=a[0]);a=a.join("/");d&&a===f+"/"+d.main&&(a=f)}else 0===a.indexOf("./")&&(a=a.substring(2));if(b&&n&&(i||p)){f=a.split("/");for(d=f.length;0<d;d-=1){c=f.slice(0,d).join("/");if(i)for(m=i.length;0<m;m-=1)if(b=l(n,i.slice(0,m).join("/")))if(b=l(b,c)){g=b;e=d;break}if(g)break;!h&&(p&&l(p,c))&&(h=l(p,c),j=d)}!g&&
+h&&(g=h,e=j);g&&(f.splice(0,e,g),a=f.join("/"))}return a}function e(a){z&&y(document.getElementsByTagName("script"),function(f){if(f.getAttribute("data-requiremodule")===a&&f.getAttribute("data-requirecontext")===i.contextName)return f.parentNode.removeChild(f),!0})}function h(a){var f=l(k.paths,a);if(f&&I(f)&&1<f.length)return f.shift(),i.require.undef(a),i.require([a]),!0}function $(a){var f,b=a?a.indexOf("!"):-1;-1<b&&(f=a.substring(0,b),a=a.substring(b+1,a.length));return[f,a]}function n(a,f,
+b,d){var m,B,g=null,e=f?f.name:null,h=a,j=!0,k="";a||(j=!1,a="_@r"+(L+=1));a=$(a);g=a[0];a=a[1];g&&(g=c(g,e,d),B=l(r,g));a&&(g?k=B&&B.normalize?B.normalize(a,function(a){return c(a,e,d)}):c(a,e,d):(k=c(a,e,d),a=$(k),g=a[0],k=a[1],b=!0,m=i.nameToUrl(k)));b=g&&!B&&!b?"_unnormalized"+(M+=1):"";return{prefix:g,name:k,parentMap:f,unnormalized:!!b,url:m,originalName:h,isDefine:j,id:(g?g+"!"+k:k)+b}}function q(a){var f=a.id,b=l(p,f);b||(b=p[f]=new i.Module(a));return b}function s(a,f,b){var d=a.id,m=l(p,
+d);if(t(r,d)&&(!m||m.defineEmitComplete))"defined"===f&&b(r[d]);else if(m=q(a),m.error&&"error"===f)b(m.error);else m.on(f,b)}function v(a,f){var b=a.requireModules,d=!1;if(f)f(a);else if(y(b,function(f){if(f=l(p,f))f.error=a,f.events.error&&(d=!0,f.emit("error",a))}),!d)j.onError(a)}function w(){R.length&&(ia.apply(G,[G.length-1,0].concat(R)),R=[])}function x(a){delete p[a];delete T[a]}function E(a,f,b){var d=a.map.id;a.error?a.emit("error",a.error):(f[d]=!0,y(a.depMaps,function(d,c){var g=d.id,
+e=l(p,g);e&&(!a.depMatched[c]&&!b[g])&&(l(f,g)?(a.defineDep(c,r[g]),a.check()):E(e,f,b))}),b[d]=!0)}function C(){var a,f,b,d,m=(b=1E3*k.waitSeconds)&&i.startTime+b<(new Date).getTime(),c=[],g=[],j=!1,l=!0;if(!U){U=!0;F(T,function(b){a=b.map;f=a.id;if(b.enabled&&(a.isDefine||g.push(b),!b.error))if(!b.inited&&m)h(f)?j=d=!0:(c.push(f),e(f));else if(!b.inited&&(b.fetched&&a.isDefine)&&(j=!0,!a.prefix))return l=!1});if(m&&c.length)return b=A("timeout","Load timeout for modules: "+c,null,c),b.contextName=
+i.contextName,v(b);l&&y(g,function(a){E(a,{},{})});if((!m||d)&&j)if((z||da)&&!V)V=setTimeout(function(){V=0;C()},50);U=!1}}function D(a){t(r,a[0])||q(n(a[0],null,!0)).init(a[1],a[2])}function J(a){var a=a.currentTarget||a.srcElement,b=i.onScriptLoad;a.detachEvent&&!W?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=i.onScriptError;(!a.detachEvent||W)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function K(){var a;for(w();G.length;){a=
+G.shift();if(null===a[0])return v(A("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));D(a)}}var U,X,i,N,V,k={waitSeconds:7,baseUrl:"./",paths:{},pkgs:{},shim:{},config:{}},p={},T={},Y={},G=[],r={},S={},L=1,M=1;N={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?a.exports:a.exports=r[a.map.id]={}},module:function(a){return a.module?a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){var b=
+l(k.pkgs,a.map.id);return(b?l(k.config,a.map.id+"/"+b.main):l(k.config,a.map.id))||{}},exports:r[a.map.id]}}};X=function(a){this.events=l(Y,a.id)||{};this.map=a;this.shim=l(k.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};X.prototype={init:function(a,b,c,d){d=d||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=u(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=c;this.inited=!0;
+this.ignore=d.ignore;d.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],u(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
+this.map.url;S[a]||(S[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var d=this.exports,m=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(H(m)){if(this.events.error&&this.map.isDefine||j.onError!==aa)try{d=i.execCb(c,m,b,d)}catch(e){a=e}else d=i.execCb(c,m,b,d);this.map.isDefine&&((b=this.module)&&void 0!==b.exports&&b.exports!==
+this.exports?d=b.exports:void 0===d&&this.usingExports&&(d=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",v(this.error=a)}else d=m;this.exports=d;if(this.map.isDefine&&!this.ignore&&(r[c]=d,j.onResourceLoad))j.onResourceLoad(i,this.map,this.depMaps);x(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=
+!0)}}else this.fetch()}},callPlugin:function(){var a=this.map,b=a.id,e=n(a.prefix);this.depMaps.push(e);s(e,"defined",u(this,function(d){var m,e;e=this.map.name;var g=this.map.parentMap?this.map.parentMap.name:null,h=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(d.normalize&&(e=d.normalize(e,function(a){return c(a,g,!0)})||""),d=n(a.prefix+"!"+e,this.map.parentMap),s(d,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),
+e=l(p,d.id)){this.depMaps.push(d);if(this.events.error)e.on("error",u(this,function(a){this.emit("error",a)}));e.enable()}}else m=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),m.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];F(p,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&x(a.map.id)});v(a)}),m.fromText=u(this,function(d,c){var e=a.name,g=n(e),B=O;c&&(d=c);B&&(O=!1);q(g);t(k.config,b)&&(k.config[e]=k.config[b]);try{j.exec(d)}catch(ca){return v(A("fromtexteval",
+"fromText eval for "+b+" failed: "+ca,ca,[b]))}B&&(O=!0);this.depMaps.push(g);i.completeLoad(e);h([e],m)}),d.load(a.name,h,m,k)}));i.enable(e,this);this.pluginMaps[e.id]=e},enable:function(){T[this.map.id]=this;this.enabling=this.enabled=!0;y(this.depMaps,u(this,function(a,b){var c,d;if("string"===typeof a){a=n(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=l(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;s(a,"defined",u(this,function(a){this.defineDep(b,
+a);this.check()}));this.errback&&s(a,"error",u(this,this.errback))}c=a.id;d=p[c];!t(N,c)&&(d&&!d.enabled)&&i.enable(a,this)}));F(this.pluginMaps,u(this,function(a){var b=l(p,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){y(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:k,contextName:b,registry:p,defined:r,urlFetched:S,defQueue:G,Module:X,makeModuleMap:n,
+nextTick:j.nextTick,onError:v,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=k.pkgs,c=k.shim,d={paths:!0,config:!0,map:!0};F(a,function(a,b){d[b]?"map"===b?(k.map||(k.map={}),Q(k[b],a,!0,!0)):Q(k[b],a,!0):k[b]=a});a.shim&&(F(a.shim,function(a,b){I(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);c[b]=a}),k.shim=c);a.packages&&(y(a.packages,function(a){a="string"===typeof a?{name:a}:a;b[a.name]={name:a.name,
+location:a.location||a.name,main:(a.main||"main").replace(ja,"").replace(ea,"")}}),k.pkgs=b);F(p,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=n(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(Z,arguments));return b||a.exports&&ba(a.exports)}},makeRequire:function(a,f){function h(d,c,e){var g,k;f.enableBuildCallback&&(c&&H(c))&&(c.__requireJsBuild=!0);if("string"===typeof d){if(H(c))return v(A("requireargs",
+"Invalid require call"),e);if(a&&t(N,d))return N[d](p[a.id]);if(j.get)return j.get(i,d,a,h);g=n(d,a,!1,!0);g=g.id;return!t(r,g)?v(A("notloaded",'Module name "'+g+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[g]}K();i.nextTick(function(){K();k=q(n(null,a));k.skipMap=f.skipMap;k.init(d,c,e,{enabled:!0});C()});return h}f=f||{};Q(h,{isBrowser:z,toUrl:function(b){var f,e=b.lastIndexOf("."),g=b.split("/")[0];if(-1!==e&&(!("."===g||".."===g)||1<e))f=b.substring(e,b.length),b=
+b.substring(0,e);return i.nameToUrl(c(b,a&&a.id,!0),f,!0)},defined:function(b){return t(r,n(b,a,!1,!0).id)},specified:function(b){b=n(b,a,!1,!0).id;return t(r,b)||t(p,b)}});a||(h.undef=function(b){w();var c=n(b,a,!0),f=l(p,b);e(b);delete r[b];delete S[c.url];delete Y[b];f&&(f.events.defined&&(Y[b]=f.events),x(b))});return h},enable:function(a){l(p,a.id)&&q(a).enable()},completeLoad:function(a){var b,c,d=l(k.shim,a)||{},e=d.exports;for(w();G.length;){c=G.shift();if(null===c[0]){c[0]=a;if(b)break;b=
+!0}else c[0]===a&&(b=!0);D(c)}c=l(p,a);if(!b&&!t(r,a)&&c&&!c.inited){if(k.enforceDefine&&(!e||!ba(e)))return h(a)?void 0:v(A("nodefine","No define call for "+a,null,[a]));D([a,d.deps||[],d.exportsFn])}C()},nameToUrl:function(a,b,c){var d,e,h,g,i,n;if(j.jsExtRegExp.test(a))g=a+(b||"");else{d=k.paths;e=k.pkgs;g=a.split("/");for(i=g.length;0<i;i-=1)if(n=g.slice(0,i).join("/"),h=l(e,n),n=l(d,n)){I(n)&&(n=n[0]);g.splice(0,i,n);break}else if(h){a=a===h.name?h.location+"/"+h.main:h.location;g.splice(0,i,
+a);break}g=g.join("/");g+=b||(/^data\:|\?/.test(g)||c?"":".js");g=("/"===g.charAt(0)||g.match(/^[\w\+\.\-]+:/)?"":k.baseUrl)+g}return k.urlArgs?g+((-1===g.indexOf("?")?"?":"&")+k.urlArgs):g},load:function(a,b){j.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ka.test((a.currentTarget||a.srcElement).readyState))P=null,a=J(a),i.completeLoad(a.id)},onScriptError:function(a){var b=J(a);if(!h(b.id))return v(A("scripterror","Script error for: "+b.id,
+a,[b.id]))}};i.require=i.makeRequire();return i}var j,w,x,C,J,D,P,K,q,fa,la=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ma=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,ea=/\.js$/,ja=/^\.\//;w=Object.prototype;var L=w.toString,ga=w.hasOwnProperty,ia=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),da=!z&&"undefined"!==typeof importScripts,ka=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,W="undefined"!==typeof opera&&
+"[object Opera]"===opera.toString(),E={},s={},R=[],O=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(H(requirejs))return;s=requirejs;requirejs=void 0}"undefined"!==typeof require&&!H(require)&&(s=require,require=void 0);j=requirejs=function(b,c,e,h){var q,n="_";!I(b)&&"string"!==typeof b&&(q=b,I(c)?(b=c,c=e,e=h):b=[]);q&&q.context&&(n=q.context);(h=l(E,n))||(h=E[n]=j.s.newContext(n));q&&h.configure(q);return h.require(b,c,e)};j.config=function(b){return j(b)};j.nextTick="undefined"!==
+typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=j);j.version="2.1.9";j.jsExtRegExp=/^\/|:|\?|\.js$/;j.isBrowser=z;w=j.s={contexts:E,newContext:ha};j({});y(["toUrl","undef","defined","specified"],function(b){j[b]=function(){var c=E._;return c.require[b].apply(c,arguments)}});if(z&&(x=w.head=document.getElementsByTagName("head")[0],C=document.getElementsByTagName("base")[0]))x=w.head=C.parentNode;j.onError=aa;j.createNode=function(b){var c=b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml",
+"html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};j.load=function(b,c,e){var h=b&&b.config||{};if(z)return h=j.createNode(h,c,e),h.setAttribute("data-requirecontext",b.contextName),h.setAttribute("data-requiremodule",c),h.attachEvent&&!(h.attachEvent.toString&&0>h.attachEvent.toString().indexOf("[native code"))&&!W?(O=!0,h.attachEvent("onreadystatechange",b.onScriptLoad)):(h.addEventListener("load",b.onScriptLoad,!1),h.addEventListener("error",
+b.onScriptError,!1)),h.src=e,K=h,C?x.insertBefore(h,C):x.appendChild(h),K=null,h;if(da)try{importScripts(e),b.completeLoad(c)}catch(l){b.onError(A("importscripts","importScripts failed for "+c+" at "+e,l,[c]))}};z&&!s.skipDataMain&&M(document.getElementsByTagName("script"),function(b){x||(x=b.parentNode);if(J=b.getAttribute("data-main"))return q=J,s.baseUrl||(D=q.split("/"),q=D.pop(),fa=D.length?D.join("/")+"/":"./",s.baseUrl=fa),q=q.replace(ea,""),j.jsExtRegExp.test(q)&&(q=J),s.deps=s.deps?s.deps.concat(q):
+[q],!0});define=function(b,c,e){var h,j;"string"!==typeof b&&(e=c,c=b,b=null);I(c)||(e=c,c=null);!c&&H(e)&&(c=[],e.length&&(e.toString().replace(la,"").replace(ma,function(b,e){c.push(e)}),c=(1===e.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(h=K))P&&"interactive"===P.readyState||M(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),h=P;h&&(b||(b=h.getAttribute("data-requiremodule")),j=E[h.getAttribute("data-requirecontext")])}(j?
+j.defQueue:R).push([b,c,e])};define.amd={jQuery:!0};j.exec=function(b){return eval(b)};j(s)}})(this);
diff --git a/examples/biojson-doc/lib/traverse.js b/examples/biojson-doc/lib/traverse.js
new file mode 100755 (executable)
index 0000000..bd2f937
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ Copyright 2010 James Halliday (mail@substack.net)
+
+ This project is free software released under the MIT/X11 license:
+ http://www.opensource.org/licenses/mit-license.php
+
+ Copyright 2010 James Halliday (mail@substack.net)
+
+ 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.
+ */
+
+var traverse;
+
+(function(){
+    traverse = function (obj) {
+        return new Traverse(obj);
+    };
+
+    function Traverse (obj) {
+        this.value = obj;
+    }
+
+    Traverse.prototype.get = function (ps) {
+        var node = this.value;
+        for (var i = 0; i < ps.length; i ++) {
+            var key = ps[i];
+            if (!node || !hasOwnProperty.call(node, key)) {
+                node = undefined;
+                break;
+            }
+            node = node[key];
+        }
+        return node;
+    };
+
+    Traverse.prototype.has = function (ps) {
+        var node = this.value;
+        for (var i = 0; i < ps.length; i ++) {
+            var key = ps[i];
+            if (!node || !hasOwnProperty.call(node, key)) {
+                return false;
+            }
+            node = node[key];
+        }
+        return true;
+    };
+
+    Traverse.prototype.set = function (ps, value) {
+        var node = this.value;
+        for (var i = 0; i < ps.length - 1; i ++) {
+            var key = ps[i];
+            if (!hasOwnProperty.call(node, key)) node[key] = {};
+            node = node[key];
+        }
+        node[ps[i]] = value;
+        return value;
+    };
+
+    Traverse.prototype.map = function (cb) {
+        return walk(this.value, cb, true);
+    };
+
+    Traverse.prototype.forEach = function (cb) {
+        this.value = walk(this.value, cb, false);
+        return this.value;
+    };
+
+    Traverse.prototype.reduce = function (cb, init) {
+        var skip = arguments.length === 1;
+        var acc = skip ? this.value : init;
+        this.forEach(function (x) {
+            if (!this.isRoot || !skip) {
+                acc = cb.call(this, acc, x);
+            }
+        });
+        return acc;
+    };
+
+    Traverse.prototype.paths = function () {
+        var acc = [];
+        this.forEach(function (x) {
+            acc.push(this.path);
+        });
+        return acc;
+    };
+
+    Traverse.prototype.nodes = function () {
+        var acc = [];
+        this.forEach(function (x) {
+            acc.push(this.node);
+        });
+        return acc;
+    };
+
+    Traverse.prototype.clone = function () {
+        var parents = [], nodes = [];
+
+        return (function clone (src) {
+            for (var i = 0; i < parents.length; i++) {
+                if (parents[i] === src) {
+                    return nodes[i];
+                }
+            }
+
+            if (typeof src === 'object' && src !== null) {
+                var dst = copy(src);
+
+                parents.push(src);
+                nodes.push(dst);
+
+                forEach(objectKeys(src), function (key) {
+                    dst[key] = clone(src[key]);
+                });
+
+                parents.pop();
+                nodes.pop();
+                return dst;
+            }
+            else {
+                return src;
+            }
+        })(this.value);
+    };
+
+    function walk (root, cb, immutable) {
+        var path = [];
+        var parents = [];
+        var alive = true;
+
+        return (function walker (node_) {
+            var node = immutable ? copy(node_) : node_;
+            var modifiers = {};
+
+            var keepGoing = true;
+
+            var state = {
+                node : node,
+                node_ : node_,
+                path : [].concat(path),
+                parent : parents[parents.length - 1],
+                parents : parents,
+                key : path.slice(-1)[0],
+                isRoot : path.length === 0,
+                level : path.length,
+                circular : null,
+                update : function (x, stopHere) {
+                    if (!state.isRoot) {
+                        state.parent.node[state.key] = x;
+                    }
+                    state.node = x;
+                    if (stopHere) keepGoing = false;
+                },
+                'delete' : function (stopHere) {
+                    delete state.parent.node[state.key];
+                    if (stopHere) keepGoing = false;
+                },
+                remove : function (stopHere) {
+                    if (isArray(state.parent.node)) {
+                        state.parent.node.splice(state.key, 1);
+                    }
+                    else {
+                        delete state.parent.node[state.key];
+                    }
+                    if (stopHere) keepGoing = false;
+                },
+                keys : null,
+                before : function (f) { modifiers.before = f },
+                after : function (f) { modifiers.after = f },
+                pre : function (f) { modifiers.pre = f },
+                post : function (f) { modifiers.post = f },
+                stop : function () { alive = false },
+                block : function () { keepGoing = false }
+            };
+
+            if (!alive) return state;
+
+            function updateState() {
+                if (typeof state.node === 'object' && state.node !== null) {
+                    if (!state.keys || state.node_ !== state.node) {
+                        state.keys = objectKeys(state.node)
+                    }
+
+                    state.isLeaf = state.keys.length == 0;
+
+                    for (var i = 0; i < parents.length; i++) {
+                        if (parents[i].node_ === node_) {
+                            state.circular = parents[i];
+                            break;
+                        }
+                    }
+                }
+                else {
+                    state.isLeaf = true;
+                    state.keys = null;
+                }
+
+                state.notLeaf = !state.isLeaf;
+                state.notRoot = !state.isRoot;
+            }
+
+            updateState();
+
+            // use return values to update if defined
+            var ret = cb.call(state, state.node);
+            if (ret !== undefined && state.update) state.update(ret);
+
+            if (modifiers.before) modifiers.before.call(state, state.node);
+
+            if (!keepGoing) return state;
+
+            if (typeof state.node == 'object'
+                && state.node !== null && !state.circular) {
+                parents.push(state);
+
+                updateState();
+
+                forEach(state.keys, function (key, i) {
+                    path.push(key);
+
+                    if (modifiers.pre) modifiers.pre.call(state, state.node[key], key);
+
+                    var child = walker(state.node[key]);
+                    if (immutable && hasOwnProperty.call(state.node, key)) {
+                        state.node[key] = child.node;
+                    }
+
+                    child.isLast = i == state.keys.length - 1;
+                    child.isFirst = i == 0;
+
+                    if (modifiers.post) modifiers.post.call(state, child);
+
+                    path.pop();
+                });
+                parents.pop();
+            }
+
+            if (modifiers.after) modifiers.after.call(state, state.node);
+
+            return state;
+        })(root).node;
+    }
+
+    function copy (src) {
+        if (typeof src === 'object' && src !== null) {
+            var dst;
+
+            if (isArray(src)) {
+                dst = [];
+            }
+            else if (isDate(src)) {
+                dst = new Date(src.getTime ? src.getTime() : src);
+            }
+            else if (isRegExp(src)) {
+                dst = new RegExp(src);
+            }
+            else if (isError(src)) {
+                dst = { message: src.message };
+            }
+            else if (isBoolean(src)) {
+                dst = new Boolean(src);
+            }
+            else if (isNumber(src)) {
+                dst = new Number(src);
+            }
+            else if (isString(src)) {
+                dst = new String(src);
+            }
+            else if (Object.create && Object.getPrototypeOf) {
+                dst = Object.create(Object.getPrototypeOf(src));
+            }
+            else if (src.constructor === Object) {
+                dst = {};
+            }
+            else {
+                var proto =
+                        (src.constructor && src.constructor.prototype)
+                            || src.__proto__
+                            || {}
+                    ;
+                var T = function () {};
+                T.prototype = proto;
+                dst = new T;
+            }
+
+            forEach(objectKeys(src), function (key) {
+                dst[key] = src[key];
+            });
+            return dst;
+        }
+        else return src;
+    }
+
+    var objectKeys = Object.keys || function keys (obj) {
+        var res = [];
+        for (var key in obj) res.push(key)
+        return res;
+    };
+
+    function toS (obj) { return Object.prototype.toString.call(obj) }
+    function isDate (obj) { return toS(obj) === '[object Date]' }
+    function isRegExp (obj) { return toS(obj) === '[object RegExp]' }
+    function isError (obj) { return toS(obj) === '[object Error]' }
+    function isBoolean (obj) { return toS(obj) === '[object Boolean]' }
+    function isNumber (obj) { return toS(obj) === '[object Number]' }
+    function isString (obj) { return toS(obj) === '[object String]' }
+
+    var isArray = Array.isArray || function isArray (xs) {
+        return Object.prototype.toString.call(xs) === '[object Array]';
+    };
+
+    var forEach = function (xs, fn) {
+        if (xs.forEach) return xs.forEach(fn)
+        else for (var i = 0; i < xs.length; i++) {
+            fn(xs[i], i, xs);
+        }
+    };
+
+    forEach(objectKeys(Traverse.prototype), function (key) {
+        traverse[key] = function (obj) {
+            var args = [].slice.call(arguments, 1);
+            var t = new Traverse(obj);
+            return t[key].apply(t, args);
+        };
+    });
+
+    var hasOwnProperty = Object.hasOwnProperty || function (obj, key) {
+        return key in obj;
+    };
+})();
diff --git a/examples/biojson-doc/templates/box.html b/examples/biojson-doc/templates/box.html
new file mode 100755 (executable)
index 0000000..c39369d
--- /dev/null
@@ -0,0 +1,157 @@
+{{!
+ Copyright 2013 Laurent Bovet (laurent.bovet@windmaster.ch)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+}}
+{{#scope this}}
+ <div class="box">
+    <div class="box-header box-{{__boxId}}">
+        <div class="box-title" ref="{{__ref}}">
+            {{#name this}}<div class="box-name {{#if root}}title{{/if}}">{{__name}}</div>{{/name}}
+            <div class="box-description desc">{{desc this}}</div>
+            <div class="end"></div>
+        </div>
+    </div>
+     <div class="source-button button" title="Source">{}</div>
+    <div class="box-body">
+        <div class="expand-button button" title="Expand all">+</div>
+        {{#sub this}}
+        <div class="signature">
+            <div class="signature-header">
+                <div class="signature-type">
+                    {{#primitive this}}
+                        {{signature this null this}}
+                    {{/primitive}}
+                    {{#equals type "array"}}
+                        {{signature this "array" items}}
+                    {{/equals}}
+                    {{#if allOf}}
+                        {{signature this "all of" allOf}}
+                    {{/if}}
+                    {{#if anyOf}}
+                        {{signature this "any of" anyOf}}
+                    {{/if}}
+                    {{#if oneOf}}
+                        {{signature this "one of" oneOf}}
+                    {{/if}}
+                    {{#if not}}
+                        {{signature this "not" not}}
+                    {{/if}}
+                </div>
+            </div>
+            <div class="signature-box-container">
+                {{#boxes}}
+                <div class="box-container" boxid="{{__boxId}}">
+                    {{schema this}}
+                </div>
+                {{/boxes}}
+            </div>
+        </div>
+        {{/sub}}
+        {{#if properties}}
+            {{#each properties}}
+            <div class="signature">
+                <div class="signature-header">
+                    <div class="property-name {{#contains ../required @key}}required{{/contains}}">{{@key}}</div>
+                    <div class="signature-type">
+                        {{#main this}}
+                            {{signature this null this}}
+                        {{/main}}
+                        {{#equals type "array"}}
+                            {{signature this "array" items}}
+                        {{/equals}}
+                        {{#if allOf}}
+                            {{signature this "all of" allOf}}
+                        {{/if}}
+                        {{#if anyOf}}
+                            {{signature this "any of" anyOf}}
+                        {{/if}}
+                        {{#if oneOf}}
+                            {{signature this "one of" oneOf}}
+                        {{/if}}
+                        {{#if additionalProperties}}
+                            {{signature this "map" additionalProperties}}
+                        {{/if}}
+                        {{#if not}}
+                            {{signature this "not" not}}
+                        {{/if}}
+                    </div>
+                    <div class="signature-description desc">{{desc this}}</div>
+                </div>
+                <div class="signature-box-container">
+                {{#boxes}}
+                    <div class="box-container" boxid="{{__boxId}}">
+                        {{schema this}}
+                    </div>
+                {{/boxes}}
+                </div>
+            </div>
+            {{/each}}
+        {{/if}}
+
+        {{#if patternProperties}}
+            {{#each patternProperties}}
+            <div class="signature">
+                <div class="signature-header">
+                    <div class="property-name type-pattern">{{@key}}</div>
+                    <div class="signature-type">
+                        {{#main this}}
+                        {{signature this null this}}
+                        {{/main}}
+                        {{#equals type "array"}}
+                        {{signature this "array" items}}
+                        {{/equals}}
+                        {{#if allOf}}
+                        {{signature this "all of" allOf}}
+                        {{/if}}
+                        {{#if anyOf}}
+                        {{signature this "any of" anyOf}}
+                        {{/if}}
+                        {{#if oneOf}}
+                        {{signature this "one of" oneOf}}
+                        {{/if}}
+                        {{#if not}}
+                        {{signature this "not" not}}
+                        {{/if}}
+                    </div>
+                    <div class="signature-description desc">{{desc this}}</div>
+                </div>
+                <div class="signature-box-container">
+                    {{#boxes}}
+                    <div class="box-container" boxid="{{__boxId}}">
+                        {{schema this}}
+                    </div>
+                    {{/boxes}}
+                </div>
+            </div>
+            {{/each}}
+        {{/if}}
+
+        {{#if additionalProperties}}
+        <div class="signature">
+            <div class="signature-header">
+                <div class="property-name type-pattern">additional</div>
+                <div class="signature-type">
+                    {{schema ../additionalProperties}}
+                </div>
+            </div>
+        </div>
+        {{/if}}
+    </div>
+    <div class="source">
+        <pre class="json-schema">
+{{source this}}</pre>
+    </div>
+</div>
+<div class="end"/>
+{{/scope}}
diff --git a/examples/biojson-doc/templates/signature.html b/examples/biojson-doc/templates/signature.html
new file mode 100755 (executable)
index 0000000..8412dbc
--- /dev/null
@@ -0,0 +1,86 @@
+{{!
+ Copyright 2013 Laurent Bovet (laurent.bovet@windmaster.ch)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+}}
+{{#if keyword}}
+    <span class="type-keyword">{{keyword}}{{range schema.minItems schema.maxItems 0 "" false false ".."}}</span>
+    {{#each schemas}}
+        <span class="type-keyword">{{enum this}}</span>
+        {{#simple this}}
+            <span class="signature-type-{{__type}}">
+                {{__type}}
+            </span>
+            <span class="type-keyword">
+                {{range minLength maxLength "" "" false false ".."}}{{range minimum maximum "-∞" "∞" exclusiveMinimum exclusiveMaximum ";"}}
+            </span>
+        {{/simple}}
+        {{#complex this}}
+            {{#box this}}
+                <span boxid="{{boxId}}" class="box-{{boxId}} signature-type-{{__type}} signature-button signature-type-expandable button">{{__type}}</span>
+            {{/box}}
+        {{/complex}}
+        {{#ref this}}
+            {{#box this}}
+                <span boxid="{{boxId}}" class="box-{{boxId}} signature-type-ref signature-button signature-type-expandable button">{{__name}}</span>
+            {{/box}}
+        {{/ref}}
+        {{#if pattern}}
+            <span class="type-pattern">/{{pattern}}/</span>
+        {{/if}}
+        {{#if enum}}
+            {{#each enum}}
+                <span class="type-enum">{{this}}</span>
+            {{/each}}
+        {{/if}}
+        {{#exists default}}
+            <span class="type-keyword">default</span>
+            <span class="type-default">{{__default}}</span>
+        {{/exists}}
+    {{/each}}
+{{/if}}
+{{#unless keyword}}
+    {{#each schemas}}
+        <span class="type-keyword">{{enum this}}</span>
+        {{#primitive this}}
+           <span class="signature-type-{{__type}}">
+               {{__type}}
+            </span>
+            <span class="type-keyword">
+               {{range minLength maxLength "" "" false false ".."}}{{range minimum maximum "-∞" "∞" exclusiveMinimum exclusiveMaximum ";"}}
+            </span>
+        {{/primitive}}
+        {{#ref this}}
+            {{#box this}}
+                <span boxid="{{boxId}}" class="box-{{boxId}} signature-type-ref signature-button signature-type-expandable button">{{__name}}</span>
+            {{/box}}
+        {{/ref}}
+        {{#obj this}}
+            {{#box this}}
+                <span boxid="{{boxId}}" class="box-{{boxId}} signature-type-{{__type}} signature-button signature-type-expandable button">{{__type}}</span>
+            {{/box}}
+        {{/obj}}
+        {{#if pattern}}
+            <span class="type-pattern">/{{pattern}}/</span>
+        {{/if}}
+        {{#if enum}}
+            {{#each enum}}
+                <span class="type-enum">{{this}}</span>
+            {{/each}}
+        {{/if}}
+        {{#exists default}}
+            <span class="type-keyword">default</span>
+            <span class="type-default">{{__default}}</span>
+        {{/exists}}
+    {{/each}}
+{{/unless}}
diff --git a/examples/biojson-doc/tests/additionalItems.json b/examples/biojson-doc/tests/additionalItems.json
new file mode 100755 (executable)
index 0000000..521745c
--- /dev/null
@@ -0,0 +1,82 @@
+[
+    {
+        "description": "additionalItems as schema",
+        "schema": {
+            "items": [{}],
+            "additionalItems": {"type": "integer"}
+        },
+        "tests": [
+            {
+                "description": "additional items match schema",
+                "data": [ null, 2, 3, 4 ],
+                "valid": true
+            },
+            {
+                "description": "additional items do not match schema",
+                "data": [ null, 2, 3, "foo" ],
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "items is schema, no additionalItems",
+        "schema": {
+            "items": {},
+            "additionalItems": false
+        },
+        "tests": [
+            {
+                "description": "all items match schema",
+                "data": [ 1, 2, 3, 4, 5 ],
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "array of items with no additionalItems",
+        "schema": {
+            "items": [{}, {}, {}],
+            "additionalItems": false
+        },
+        "tests": [
+            {
+                "description": "no additional items present",
+                "data": [ 1, 2, 3 ],
+                "valid": true
+            },
+            {
+                "description": "additional items are not permitted",
+                "data": [ 1, 2, 3, 4 ],
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "additionalItems as false without items",
+        "schema": {"additionalItems": false},
+        "tests": [
+            {
+                "description":
+                    "items defaults to empty schema so everything is valid",
+                "data": [ 1, 2, 3, 4, 5 ],
+                "valid": true
+            },
+            {
+                "description": "ignores non-arrays",
+                "data": {"foo" : "bar"},
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "additionalItems are allowed by default",
+        "schema": {"items": [{"type": "integer"}]},
+        "tests": [
+            {
+                "description": "only the first item is validated",
+                "data": [1, "foo", false],
+                "valid": true
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/additionalProperties.json b/examples/biojson-doc/tests/additionalProperties.json
new file mode 100755 (executable)
index 0000000..eb334c9
--- /dev/null
@@ -0,0 +1,69 @@
+[
+    {
+        "description":
+            "additionalProperties being false does not allow other properties",
+        "schema": {
+            "properties": {"foo": {}, "bar": {}},
+            "patternProperties": { "^v": {} },
+            "additionalProperties": false
+        },
+        "tests": [
+            {
+                "description": "no additional properties is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "an additional property is invalid",
+                "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
+                "valid": false
+            },
+            {
+                "description": "ignores non-objects",
+                "data": [1, 2, 3],
+                "valid": true
+            },
+            {
+                "description": "patternProperties are not additional properties",
+                "data": {"foo":1, "vroom": 2},
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description":
+            "additionalProperties allows a schema which should validate",
+        "schema": {
+            "properties": {"foo": {}, "bar": {}},
+            "additionalProperties": {"type": "boolean"}
+        },
+        "tests": [
+            {
+                "description": "no additional properties is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "an additional valid property is valid",
+                "data": {"foo" : 1, "bar" : 2, "quux" : true},
+                "valid": true
+            },
+            {
+                "description": "an additional invalid property is invalid",
+                "data": {"foo" : 1, "bar" : 2, "quux" : 12},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "additionalProperties are allowed by default",
+        "schema": {"properties": {"foo": {}, "bar": {}}},
+        "tests": [
+            {
+                "description": "additional properties are allowed",
+                "data": {"foo": 1, "bar": 2, "quux": true},
+                "valid": true
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/address.json b/examples/biojson-doc/tests/address.json
new file mode 100755 (executable)
index 0000000..278728a
--- /dev/null
@@ -0,0 +1,33 @@
+[
+    {
+        "schema" : {
+            "$schema": "http://json-schema.org/draft-04/schema#",
+
+            "definitions": {
+                "address": {
+                    "type": "object",
+                    "properties": {
+                        "street_address": { "type": "string" },
+                        "city":           { "type": "string" },
+                        "state":          { "type": "string" }
+                    },
+                    "required": ["street_address", "city", "state"]
+                }
+            },
+
+            "type": "object",
+
+            "properties": {
+                "billing_address": { "$ref": "#/definitions/address" },
+                "shipping_address": {
+                    "allOf": [
+                        { "$ref": "#/definitions/address" },
+                        { "properties":
+                        { "type": { "enum": [ "residential", "business" ] } },
+                            "required": ["type"]
+                        }
+                    ]
+                }
+            }
+        }
+    }]
diff --git a/examples/biojson-doc/tests/allOf.json b/examples/biojson-doc/tests/allOf.json
new file mode 100755 (executable)
index 0000000..bbb5f89
--- /dev/null
@@ -0,0 +1,112 @@
+[
+    {
+        "description": "allOf",
+        "schema": {
+            "allOf": [
+                {
+                    "properties": {
+                        "bar": {"type": "integer"}
+                    },
+                    "required": ["bar"]
+                },
+                {
+                    "properties": {
+                        "foo": {"type": "string"}
+                    },
+                    "required": ["foo"]
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "allOf",
+                "data": {"foo": "baz", "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "mismatch second",
+                "data": {"foo": "baz"},
+                "valid": false
+            },
+            {
+                "description": "mismatch first",
+                "data": {"bar": 2},
+                "valid": false
+            },
+            {
+                "description": "wrong type",
+                "data": {"foo": "baz", "bar": "quux"},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "allOf with base schema",
+        "schema": {
+            "properties": {"bar": {"type": "integer"}},
+            "required": ["bar"],
+            "allOf" : [
+                {
+                    "properties": {
+                        "foo": {"type": "string"}
+                    },
+                    "required": ["foo"]
+                },
+                {
+                    "properties": {
+                        "baz": {"type": "null"}
+                    },
+                    "required": ["baz"]
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "valid",
+                "data": {"foo": "quux", "bar": 2, "baz": null},
+                "valid": true
+            },
+            {
+                "description": "mismatch base schema",
+                "data": {"foo": "quux", "baz": null},
+                "valid": false
+            },
+            {
+                "description": "mismatch first allOf",
+                "data": {"bar": 2, "baz": null},
+                "valid": false
+            },
+            {
+                "description": "mismatch second allOf",
+                "data": {"foo": "quux", "bar": 2},
+                "valid": false
+            },
+            {
+                "description": "mismatch both",
+                "data": {"bar": 2},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "allOf simple types",
+        "schema": {
+            "allOf": [
+                {"maximum": 30},
+                {"minimum": 20}
+            ]
+        },
+        "tests": [
+            {
+                "description": "valid",
+                "data": 25,
+                "valid": true
+            },
+            {
+                "description": "mismatch one",
+                "data": 35,
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/anyOf.json b/examples/biojson-doc/tests/anyOf.json
new file mode 100755 (executable)
index 0000000..a58714a
--- /dev/null
@@ -0,0 +1,68 @@
+[
+    {
+        "description": "anyOf",
+        "schema": {
+            "anyOf": [
+                {
+                    "type": "integer"
+                },
+                {
+                    "minimum": 2
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "first anyOf valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "second anyOf valid",
+                "data": 2.5,
+                "valid": true
+            },
+            {
+                "description": "both anyOf valid",
+                "data": 3,
+                "valid": true
+            },
+            {
+                "description": "neither anyOf valid",
+                "data": 1.5,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "anyOf with base schema",
+        "schema": {
+            "type": "string",
+            "anyOf" : [
+                {
+                    "maxLength": 2
+                },
+                {
+                    "minLength": 4
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "mismatch base schema",
+                "data": 3,
+                "valid": false
+            },
+            {
+                "description": "one anyOf valid",
+                "data": "foobar",
+                "valid": true
+            },
+            {
+                "description": "both anyOf invalid",
+                "data": "foo",
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/basic.json b/examples/biojson-doc/tests/basic.json
new file mode 100755 (executable)
index 0000000..c9569b2
--- /dev/null
@@ -0,0 +1,20 @@
+[{
+    "schema": {
+        "title": "Example Schema",
+        "type": "object",
+        "properties": {
+            "firstName": {
+                "type": "string"
+            },
+            "lastName": {
+                "type": "string"
+            },
+            "age": {
+                "description": "Age in years",
+                "type": "integer",
+                "minimum": 0
+            }
+        },
+        "required": ["firstName", "lastName"]
+    }
+}]
diff --git a/examples/biojson-doc/tests/definitions.json b/examples/biojson-doc/tests/definitions.json
new file mode 100755 (executable)
index 0000000..cf935a3
--- /dev/null
@@ -0,0 +1,32 @@
+[
+    {
+        "description": "valid definition",
+        "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
+        "tests": [
+            {
+                "description": "valid definition schema",
+                "data": {
+                    "definitions": {
+                        "foo": {"type": "integer"}
+                    }
+                },
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "invalid definition",
+        "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
+        "tests": [
+            {
+                "description": "invalid definition schema",
+                "data": {
+                    "definitions": {
+                        "foo": {"type": 1}
+                    }
+                },
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/dependencies.json b/examples/biojson-doc/tests/dependencies.json
new file mode 100755 (executable)
index 0000000..7b9b16a
--- /dev/null
@@ -0,0 +1,113 @@
+[
+    {
+        "description": "dependencies",
+        "schema": {
+            "dependencies": {"bar": ["foo"]}
+        },
+        "tests": [
+            {
+                "description": "neither",
+                "data": {},
+                "valid": true
+            },
+            {
+                "description": "nondependant",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "with dependency",
+                "data": {"foo": 1, "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "missing dependency",
+                "data": {"bar": 2},
+                "valid": false
+            },
+            {
+                "description": "ignores non-objects",
+                "data": "foo",
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "multiple dependencies",
+        "schema": {
+            "dependencies": {"quux": ["foo", "bar"]}
+        },
+        "tests": [
+            {
+                "description": "neither",
+                "data": {},
+                "valid": true
+            },
+            {
+                "description": "nondependants",
+                "data": {"foo": 1, "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "with dependencies",
+                "data": {"foo": 1, "bar": 2, "quux": 3},
+                "valid": true
+            },
+            {
+                "description": "missing dependency",
+                "data": {"foo": 1, "quux": 2},
+                "valid": false
+            },
+            {
+                "description": "missing other dependency",
+                "data": {"bar": 1, "quux": 2},
+                "valid": false
+            },
+            {
+                "description": "missing both dependencies",
+                "data": {"quux": 1},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "multiple dependencies subschema",
+        "schema": {
+            "dependencies": {
+                "bar": {
+                    "properties": {
+                        "foo": {"type": "integer"},
+                        "bar": {"type": "integer"}
+                    }
+                }
+            }
+        },
+        "tests": [
+            {
+                "description": "valid",
+                "data": {"foo": 1, "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "no dependency",
+                "data": {"foo": "quux"},
+                "valid": true
+            },
+            {
+                "description": "wrong type",
+                "data": {"foo": "quux", "bar": 2},
+                "valid": false
+            },
+            {
+                "description": "wrong type other",
+                "data": {"foo": 2, "bar": "quux"},
+                "valid": false
+            },
+            {
+                "description": "wrong type both",
+                "data": {"foo": "quux", "bar": "quux"},
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/enum.json b/examples/biojson-doc/tests/enum.json
new file mode 100755 (executable)
index 0000000..a539edb
--- /dev/null
@@ -0,0 +1,39 @@
+[
+    {
+        "description": "simple enum validation",
+        "schema": {"enum": [1, 2, 3]},
+        "tests": [
+            {
+                "description": "one of the enum is valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "something else is invalid",
+                "data": 4,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "heterogeneous enum validation",
+        "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
+        "tests": [
+            {
+                "description": "one of the enum is valid",
+                "data": [],
+                "valid": true
+            },
+            {
+                "description": "something else is invalid",
+                "data": null,
+                "valid": false
+            },
+            {
+                "description": "objects are deep compared",
+                "data": {"foo": false},
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/example2.json b/examples/biojson-doc/tests/example2.json
new file mode 100755 (executable)
index 0000000..d6d69ad
--- /dev/null
@@ -0,0 +1,49 @@
+[
+    {
+        "schema" : {
+            "$schema": "http://json-schema.org/draft-04/schema#",
+            "title": "Product set",
+            "type": "array",
+            "items": {
+                "title": "Product",
+                "type": "object",
+                "properties": {
+                    "id": {
+                        "description": "The unique identifier for a product",
+                        "type": "number"
+                    },
+                    "name": {
+                        "type": "string"
+                    },
+                    "price": {
+                        "type": "number",
+                        "minimum": 0,
+                        "exclusiveMinimum": true
+                    },
+                    "tags": {
+                        "type": "array",
+                        "items": {
+                            "type": "string"
+                        },
+                        "minItems": 1,
+                        "uniqueItems": true
+                    },
+                    "dimensions": {
+                        "type": "object",
+                        "properties": {
+                            "length": {"type": "number"},
+                            "width": {"type": "number"},
+                            "height": {"type": "number"}
+                        },
+                        "required": ["length", "width", "height"]
+                    },
+                    "warehouseLocation": {
+                        "description": "Coordinates of the warehouse with the product",
+                        "$ref": "http://localhost:8000/docson/tests/geo.json"
+                    }
+                },
+                "required": ["id", "name", "price"]
+            }
+        }
+    }
+]
diff --git a/examples/biojson-doc/tests/fstab.json b/examples/biojson-doc/tests/fstab.json
new file mode 100755 (executable)
index 0000000..bff452d
--- /dev/null
@@ -0,0 +1,87 @@
+[
+    {
+        "schema": {
+            "id": "http://some.site.somewhere/entry-schema#",
+            "$schema": "http://json-schema.org/draft-04/schema#",
+            "description": "schema for an fstab entry",
+            "type": "object",
+            "required": [ "storage" ],
+            "properties": {
+                "storage": {
+                    "type": "object",
+                    "oneOf": [
+                        { "$ref": "#/definitions/diskDevice" },
+                        { "$ref": "#/definitions/diskUUID" },
+                        { "$ref": "#/definitions/nfs" },
+                        { "$ref": "#/definitions/tmpfs" }
+                    ]
+                },
+                "fstype": {
+                    "enum": [ "ext3", "ext4", "btrfs" ],
+                    "default": "ext3"
+                },
+                "options": {
+                    "type": "array",
+                    "minItems": 1,
+                    "items": { "type": "string" },
+                    "uniqueItems": true
+                },
+                "readonly": { "type": "boolean" }
+            },
+            "definitions": {
+                "diskDevice": {
+                    "properties": {
+                        "type": { "enum": [ "disk" ] },
+                        "device": {
+                            "type": "string",
+                            "pattern": "^/dev/[^/]+(/[^/]+)*$"
+                        }
+                    },
+                    "required": [ "type", "device" ],
+                    "additionalProperties": false
+                },
+                "diskUUID": {
+                    "properties": {
+                        "type": { "enum": [ "disk" ] },
+                        "label": {
+                            "type": "string",
+                            "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
+                        }
+                    },
+                    "required": [ "type", "label" ],
+                    "additionalProperties": false
+                },
+                "nfs": {
+                    "properties": {
+                        "type": { "enum": [ "nfs" ] },
+                        "remotePath": {
+                            "type": "string",
+                            "pattern": "^(/[^/]+)+$"
+                        },
+                        "server": {
+                            "type": "string",
+                            "oneOf": [
+                                { "format": "host-name" },
+                                { "format": "ipv4" },
+                                { "format": "ipv6" }
+                            ]
+                        }
+                    },
+                    "required": [ "type", "server", "remotePath" ],
+                    "additionalProperties": false
+                },
+                "tmpfs": {
+                    "properties": {
+                        "type": { "enum": [ "tmpfs" ] },
+                        "sizeInMB": {
+                            "type": "integer",
+                            "minimum": 16,
+                            "maximum": 512
+                        }
+                    },
+                    "required": [ "type", "sizeInMB" ],
+                    "additionalProperties": false
+                }
+            }
+        }
+    }]
diff --git a/examples/biojson-doc/tests/geo.json b/examples/biojson-doc/tests/geo.json
new file mode 100755 (executable)
index 0000000..05345b2
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "description": "A geographical coordinate",
+    "type": "object",
+    "properties": {
+        "latitude": { "type": "number" },
+        "longitude": { "type": "number" }
+    }
+}
diff --git a/examples/biojson-doc/tests/invoice.json b/examples/biojson-doc/tests/invoice.json
new file mode 100755 (executable)
index 0000000..6af4ed4
--- /dev/null
@@ -0,0 +1,98 @@
+[
+    {
+        "schema": {
+            "Invoice": {
+                "id": "Invoice",
+                "description": "Represents the document sent to the customer for payment.",
+                "properties": {
+                    "customer": {
+                        "description": "Who will pay?\nNot me! Ã©Ã Ã¨",
+                        "type": "string"
+                    },
+                    "lines": {
+                        "description": "Invoice content\n",
+                        "minItems": "1",
+                        "maxItems": "50",
+                        "type": "array",
+                        "items": {
+                            "$ref": "InvoiceLine"
+                        }
+                    },
+                    "dimension": {
+                        "description": "Total dimension of the order ",
+                        "$ref": "Dimension"
+                    }
+                }
+            },
+            "InvoiceLine": {
+                "id": "InvoiceLine",
+                "properties": {
+                    "product": {
+                        "$ref": "Product"
+                    },
+                    "quantity": {
+                        "minimum": "0",
+                        "exclusiveMinimum": "true",
+                        "maximum": "10",
+                        "exclusiveMaximum": "false",
+                        "type": "number"
+                    }
+                }
+            },
+            "Dimension": {
+                "id": "Dimension",
+                "properties": {
+                    "width": {
+                        "description": "Width in cm ",
+                        "type": "number"
+                    },
+                    "height": {
+                        "description": "Height in cm ",
+                        "type": "number"
+                    },
+                    "length": {
+                        "description": "Length in cm ",
+                        "type": "number"
+                    }
+                }
+            },
+            "Product": {
+                "id": "Product",
+                "properties": {
+                    "name": {
+                        "description": "Uniquely defines the product ",
+                        "type": "string"
+                    },
+                    "dimension": {
+                        "description": "How big it is ",
+                        "$ref": "Dimension"
+                    },
+                    "category": {
+                        "description": "Classification ",
+                        "$ref": "Category"
+                    }
+                }
+            },
+            "Category": {
+                "id": "Category",
+                "properties": {
+                    "name": {
+                        "description": "Uniquely identifies the category ",
+                        "type": "string"
+                    },
+                    "level": {
+                        "description": "Classification level from 1 to 5 (highest) ",
+                        "type": "number"
+                    }
+                }
+            },
+            "CategoryIndex": {
+                "id": "CategoryIndex",
+                "properties": {
+                    "categories": {},
+                    "products": {},
+                    "sizes": {}
+                }
+            }
+        }
+    }]
diff --git a/examples/biojson-doc/tests/items.json b/examples/biojson-doc/tests/items.json
new file mode 100755 (executable)
index 0000000..f5e18a1
--- /dev/null
@@ -0,0 +1,46 @@
+[
+    {
+        "description": "a schema given for items",
+        "schema": {
+            "items": {"type": "integer"}
+        },
+        "tests": [
+            {
+                "description": "valid items",
+                "data": [ 1, 2, 3 ],
+                "valid": true
+            },
+            {
+                "description": "wrong type of items",
+                "data": [1, "x"],
+                "valid": false
+            },
+            {
+                "description": "ignores non-arrays",
+                "data": {"foo" : "bar"},
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "an array of schemas for items",
+        "schema": {
+            "items": [
+                {"type": "integer"},
+                {"type": "string"}
+            ]
+        },
+        "tests": [
+            {
+                "description": "correct types",
+                "data": [ 1, "foo" ],
+                "valid": true
+            },
+            {
+                "description": "wrong types",
+                "data": [ "foo", 1 ],
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/maxItems.json b/examples/biojson-doc/tests/maxItems.json
new file mode 100755 (executable)
index 0000000..3b53a6b
--- /dev/null
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "maxItems validation",
+        "schema": {"maxItems": 2},
+        "tests": [
+            {
+                "description": "shorter is valid",
+                "data": [1],
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": [1, 2],
+                "valid": true
+            },
+            {
+                "description": "too long is invalid",
+                "data": [1, 2, 3],
+                "valid": false
+            },
+            {
+                "description": "ignores non-arrays",
+                "data": "foobar",
+                "valid": true
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/maxLength.json b/examples/biojson-doc/tests/maxLength.json
new file mode 100755 (executable)
index 0000000..561767b
--- /dev/null
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "maxLength validation",
+        "schema": {"maxLength": 2},
+        "tests": [
+            {
+                "description": "shorter is valid",
+                "data": "f",
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": "fo",
+                "valid": true
+            },
+            {
+                "description": "too long is invalid",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "ignores non-strings",
+                "data": 10,
+                "valid": true
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/maxProperties.json b/examples/biojson-doc/tests/maxProperties.json
new file mode 100755 (executable)
index 0000000..d282446
--- /dev/null
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "maxProperties validation",
+        "schema": {"maxProperties": 2},
+        "tests": [
+            {
+                "description": "shorter is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": {"foo": 1, "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "too long is invalid",
+                "data": {"foo": 1, "bar": 2, "baz": 3},
+                "valid": false
+            },
+            {
+                "description": "ignores non-objects",
+                "data": "foobar",
+                "valid": true
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/maximum.json b/examples/biojson-doc/tests/maximum.json
new file mode 100755 (executable)
index 0000000..86c7b89
--- /dev/null
@@ -0,0 +1,42 @@
+[
+    {
+        "description": "maximum validation",
+        "schema": {"maximum": 3.0},
+        "tests": [
+            {
+                "description": "below the maximum is valid",
+                "data": 2.6,
+                "valid": true
+            },
+            {
+                "description": "above the maximum is invalid",
+                "data": 3.5,
+                "valid": false
+            },
+            {
+                "description": "ignores non-numbers",
+                "data": "x",
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "exclusiveMaximum validation",
+        "schema": {
+            "maximum": 3.0,
+            "exclusiveMaximum": true
+        },
+        "tests": [
+            {
+                "description": "below the maximum is still valid",
+                "data": 2.2,
+                "valid": true
+            },
+            {
+                "description": "boundary point is invalid",
+                "data": 3.0,
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/minItems.json b/examples/biojson-doc/tests/minItems.json
new file mode 100755 (executable)
index 0000000..ed51188
--- /dev/null
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "minItems validation",
+        "schema": {"minItems": 1},
+        "tests": [
+            {
+                "description": "longer is valid",
+                "data": [1, 2],
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": [1],
+                "valid": true
+            },
+            {
+                "description": "too short is invalid",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "ignores non-arrays",
+                "data": "",
+                "valid": true
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/minLength.json b/examples/biojson-doc/tests/minLength.json
new file mode 100755 (executable)
index 0000000..e9c14b1
--- /dev/null
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "minLength validation",
+        "schema": {"minLength": 2},
+        "tests": [
+            {
+                "description": "longer is valid",
+                "data": "foo",
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": "fo",
+                "valid": true
+            },
+            {
+                "description": "too short is invalid",
+                "data": "f",
+                "valid": false
+            },
+            {
+                "description": "ignores non-strings",
+                "data": 1,
+                "valid": true
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/minProperties.json b/examples/biojson-doc/tests/minProperties.json
new file mode 100755 (executable)
index 0000000..a72c7d2
--- /dev/null
@@ -0,0 +1,28 @@
+[
+    {
+        "description": "minProperties validation",
+        "schema": {"minProperties": 1},
+        "tests": [
+            {
+                "description": "longer is valid",
+                "data": {"foo": 1, "bar": 2},
+                "valid": true
+            },
+            {
+                "description": "exact length is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "too short is invalid",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "ignores non-objects",
+                "data": "",
+                "valid": true
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/minimum.json b/examples/biojson-doc/tests/minimum.json
new file mode 100755 (executable)
index 0000000..d5bf000
--- /dev/null
@@ -0,0 +1,42 @@
+[
+    {
+        "description": "minimum validation",
+        "schema": {"minimum": 1.1},
+        "tests": [
+            {
+                "description": "above the minimum is valid",
+                "data": 2.6,
+                "valid": true
+            },
+            {
+                "description": "below the minimum is invalid",
+                "data": 0.6,
+                "valid": false
+            },
+            {
+                "description": "ignores non-numbers",
+                "data": "x",
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "exclusiveMinimum validation",
+        "schema": {
+            "minimum": 1.1,
+            "exclusiveMinimum": true
+        },
+        "tests": [
+            {
+                "description": "above the minimum is still valid",
+                "data": 1.2,
+                "valid": true
+            },
+            {
+                "description": "boundary point is invalid",
+                "data": 1.1,
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/multipleOf.json b/examples/biojson-doc/tests/multipleOf.json
new file mode 100755 (executable)
index 0000000..ca3b761
--- /dev/null
@@ -0,0 +1,60 @@
+[
+    {
+        "description": "by int",
+        "schema": {"multipleOf": 2},
+        "tests": [
+            {
+                "description": "int by int",
+                "data": 10,
+                "valid": true
+            },
+            {
+                "description": "int by int fail",
+                "data": 7,
+                "valid": false
+            },
+            {
+                "description": "ignores non-numbers",
+                "data": "foo",
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "by number",
+        "schema": {"multipleOf": 1.5},
+        "tests": [
+            {
+                "description": "zero is multiple of anything",
+                "data": 0,
+                "valid": true
+            },
+            {
+                "description": "4.5 is multiple of 1.5",
+                "data": 4.5,
+                "valid": true
+            },
+            {
+                "description": "35 is not multiple of 1.5",
+                "data": 35,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "by small number",
+        "schema": {"multipleOf": 0.0001},
+        "tests": [
+            {
+                "description": "0.0075 is multiple of 0.0001",
+                "data": 0.0075,
+                "valid": true
+            },
+            {
+                "description": "0.00751 is not multiple of 0.0001",
+                "data": 0.00751,
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/not.json b/examples/biojson-doc/tests/not.json
new file mode 100755 (executable)
index 0000000..2cdc979
--- /dev/null
@@ -0,0 +1,73 @@
+[
+    {
+        "description": "not",
+        "schema": {
+            "not": {"type": "integer"}
+        },
+        "tests": [
+            {
+                "description": "allowed",
+                "data": "foo",
+                "valid": true
+            },
+            {
+                "description": "disallowed",
+                "data": 1,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "not multiple types",
+        "schema": {
+            "not": {"type": ["integer", "boolean"]}
+        },
+        "tests": [
+            {
+                "description": "valid",
+                "data": "foo",
+                "valid": true
+            },
+            {
+                "description": "mismatch",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "other mismatch",
+                "data": true,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "not more complex schema",
+        "schema": {
+            "not": {
+                "type": "object",
+                "properties": {
+                    "foo": {
+                        "type": "string"
+                    }
+                }
+             }
+        },
+        "tests": [
+            {
+                "description": "match",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "other match",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "mismatch",
+                "data": {"foo": "bar"},
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/oneOf.json b/examples/biojson-doc/tests/oneOf.json
new file mode 100755 (executable)
index 0000000..1eaa4e4
--- /dev/null
@@ -0,0 +1,68 @@
+[
+    {
+        "description": "oneOf",
+        "schema": {
+            "oneOf": [
+                {
+                    "type": "integer"
+                },
+                {
+                    "minimum": 2
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "first oneOf valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "second oneOf valid",
+                "data": 2.5,
+                "valid": true
+            },
+            {
+                "description": "both oneOf valid",
+                "data": 3,
+                "valid": false
+            },
+            {
+                "description": "neither oneOf valid",
+                "data": 1.5,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "oneOf with base schema",
+        "schema": {
+            "type": "string",
+            "oneOf" : [
+                {
+                    "minLength": 2
+                },
+                {
+                    "maxLength": 4
+                }
+            ]
+        },
+        "tests": [
+            {
+                "description": "mismatch base schema",
+                "data": 3,
+                "valid": false
+            },
+            {
+                "description": "one oneOf valid",
+                "data": "foobar",
+                "valid": true
+            },
+            {
+                "description": "both oneOf valid",
+                "data": "foo",
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/pattern.json b/examples/biojson-doc/tests/pattern.json
new file mode 100755 (executable)
index 0000000..befc4b5
--- /dev/null
@@ -0,0 +1,23 @@
+[
+    {
+        "description": "pattern validation",
+        "schema": {"pattern": "^a*$"},
+        "tests": [
+            {
+                "description": "a matching pattern is valid",
+                "data": "aaa",
+                "valid": true
+            },
+            {
+                "description": "a non-matching pattern is invalid",
+                "data": "abc",
+                "valid": false
+            },
+            {
+                "description": "ignores non-strings",
+                "data": true,
+                "valid": true
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/patternProperties.json b/examples/biojson-doc/tests/patternProperties.json
new file mode 100755 (executable)
index 0000000..18586e5
--- /dev/null
@@ -0,0 +1,110 @@
+[
+    {
+        "description":
+            "patternProperties validates properties matching a regex",
+        "schema": {
+            "patternProperties": {
+                "f.*o": {"type": "integer"}
+            }
+        },
+        "tests": [
+            {
+                "description": "a single valid match is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "multiple valid matches is valid",
+                "data": {"foo": 1, "foooooo" : 2},
+                "valid": true
+            },
+            {
+                "description": "a single invalid match is invalid",
+                "data": {"foo": "bar", "fooooo": 2},
+                "valid": false
+            },
+            {
+                "description": "multiple invalid matches is invalid",
+                "data": {"foo": "bar", "foooooo" : "baz"},
+                "valid": false
+            },
+            {
+                "description": "ignores non-objects",
+                "data": 12,
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "multiple simultaneous patternProperties are validated",
+        "schema": {
+            "patternProperties": {
+                "a*": {"type": "integer"},
+                "aaa*": {"maximum": 20}
+            }
+        },
+        "tests": [
+            {
+                "description": "a single valid match is valid",
+                "data": {"a": 21},
+                "valid": true
+            },
+            {
+                "description": "a simultaneous match is valid",
+                "data": {"aaaa": 18},
+                "valid": true
+            },
+            {
+                "description": "multiple matches is valid",
+                "data": {"a": 21, "aaaa": 18},
+                "valid": true
+            },
+            {
+                "description": "an invalid due to one is invalid",
+                "data": {"a": "bar"},
+                "valid": false
+            },
+            {
+                "description": "an invalid due to the other is invalid",
+                "data": {"aaaa": 31},
+                "valid": false
+            },
+            {
+                "description": "an invalid due to both is invalid",
+                "data": {"aaa": "foo", "aaaa": 31},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "regexes are not anchored by default and are case sensitive",
+        "schema": {
+            "patternProperties": {
+                "[0-9]{2,}": { "type": "boolean" },
+                "X_": { "type": "string" }
+            }
+        },
+        "tests": [
+            {
+                "description": "non recognized members are ignored",
+                "data": { "answer 1": "42" },
+                "valid": true
+            },
+            {
+                "description": "recognized members are accounted for",
+                "data": { "a31b": null },
+                "valid": false
+            },
+            {
+                "description": "regexes are case sensitive",
+                "data": { "a_x_3": 3 },
+                "valid": true
+            },
+            {
+                "description": "regexes are case sensitive, 2",
+                "data": { "a_X_3": 3 },
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/properties.json b/examples/biojson-doc/tests/properties.json
new file mode 100755 (executable)
index 0000000..cd1644d
--- /dev/null
@@ -0,0 +1,92 @@
+[
+    {
+        "description": "object properties validation",
+        "schema": {
+            "properties": {
+                "foo": {"type": "integer"},
+                "bar": {"type": "string"}
+            }
+        },
+        "tests": [
+            {
+                "description": "both properties present and valid is valid",
+                "data": {"foo": 1, "bar": "baz"},
+                "valid": true
+            },
+            {
+                "description": "one property invalid is invalid",
+                "data": {"foo": 1, "bar": {}},
+                "valid": false
+            },
+            {
+                "description": "both properties invalid is invalid",
+                "data": {"foo": [], "bar": {}},
+                "valid": false
+            },
+            {
+                "description": "doesn't invalidate other properties",
+                "data": {"quux": []},
+                "valid": true
+            },
+            {
+                "description": "ignores non-objects",
+                "data": [],
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description":
+            "properties, patternProperties, additionalProperties interaction",
+        "schema": {
+            "properties": {
+                "foo": {"type": "array", "maxItems": 3},
+                "bar": {"type": "array"}
+            },
+            "patternProperties": {"f.o": {"minItems": 2}},
+            "additionalProperties": {"type": "integer"}
+        },
+        "tests": [
+            {
+                "description": "property validates property",
+                "data": {"foo": [1, 2]},
+                "valid": true
+            },
+            {
+                "description": "property invalidates property",
+                "data": {"foo": [1, 2, 3, 4]},
+                "valid": false
+            },
+            {
+                "description": "patternProperty invalidates property",
+                "data": {"foo": []},
+                "valid": false
+            },
+            {
+                "description": "patternProperty validates nonproperty",
+                "data": {"fxo": [1, 2]},
+                "valid": true
+            },
+            {
+                "description": "patternProperty invalidates nonproperty",
+                "data": {"fxo": []},
+                "valid": false
+            },
+            {
+                "description": "additionalProperty ignores property",
+                "data": {"bar": []},
+                "valid": true
+            },
+            {
+                "description": "additionalProperty validates others",
+                "data": {"quux": 3},
+                "valid": true
+            },
+            {
+                "description": "additionalProperty invalidates others",
+                "data": {"quux": "foo"},
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/ref.json b/examples/biojson-doc/tests/ref.json
new file mode 100755 (executable)
index 0000000..543af83
--- /dev/null
@@ -0,0 +1,160 @@
+[
+    {
+        "description": "root pointer ref",
+        "schema": {
+            "properties": {
+                "foo": {"$ref": "#"}
+            },
+            "additionalProperties": false
+        },
+        "tests": [
+            {
+                "description": "match",
+                "data": {"foo": false},
+                "valid": true
+            },
+            {
+                "description": "recursive match",
+                "data": {"foo": {"foo": false}},
+                "valid": true
+            },
+            {
+                "description": "mismatch",
+                "data": {"bar": false},
+                "valid": false
+            },
+            {
+                "description": "recursive mismatch",
+                "data": {"foo": {"bar": false}},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "relative pointer ref to object",
+        "schema": {
+            "properties": {
+                "foo": {"type": "integer"},
+                "bar": {"$ref": "#/properties/foo"}
+            }
+        },
+        "tests": [
+            {
+                "description": "match",
+                "data": {"bar": 3},
+                "valid": true
+            },
+            {
+                "description": "mismatch",
+                "data": {"bar": true},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "relative pointer ref to array",
+        "schema": {
+            "items": [
+                {"type": "integer"},
+                {"$ref": "#/items/0"}
+            ]
+        },
+        "tests": [
+            {
+                "description": "match array",
+                "data": [1, 2],
+                "valid": true
+            },
+            {
+                "description": "mismatch array",
+                "data": [1, "foo"],
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "escaped pointer ref",
+        "schema": {
+            "tilda~field": {"type": "integer"},
+            "slash/field": {"type": "integer"},
+            "percent%field": {"type": "integer"},
+            "properties": {
+                "tilda": {"$ref": "#/tilda~0field"},
+                "slash": {"$ref": "#/slash~1field"},
+                "percent": {"$ref": "#/percent%25field"}
+            }
+        },
+        "tests": [
+            {
+                "description": "slash",
+                "data": {"slash": "aoeu"},
+                "valid": false
+            },
+            {
+                "description": "tilda",
+                "data": {"tilda": "aoeu"},
+                "valid": false
+            },
+            {
+                "description": "percent",
+                "data": {"percent": "aoeu"},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "nested refs",
+        "schema": {
+            "definitions": {
+                "a": {"type": "integer"},
+                "b": {"$ref": "#/definitions/a"},
+                "c": {"$ref": "#/definitions/b"}
+            },
+            "$ref": "#/definitions/c"
+        },
+        "tests": [
+            {
+                "description": "nested ref valid",
+                "data": 5,
+                "valid": true
+            },
+            {
+                "description": "nested ref invalid",
+                "data": "a",
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "remote ref, containing refs itself",
+        "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
+        "tests": [
+            {
+                "description": "remote ref valid",
+                "data": {"minLength": 1},
+                "valid": true
+            },
+            {
+                "description": "remote ref invalid",
+                "data": {"minLength": -1},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "circular refs",
+        "schema": {
+            "definitions": {
+                "a": {
+                    "properties": {
+                        "more": {"$ref": "#/definitions/a"}
+                    }
+                }
+            },
+            "properties": {
+                "prop": {"$ref": "#/definitions/a"}
+            }
+        },
+        "tests": []
+    }
+]
diff --git a/examples/biojson-doc/tests/refRemote.json b/examples/biojson-doc/tests/refRemote.json
new file mode 100755 (executable)
index 0000000..4ca8047
--- /dev/null
@@ -0,0 +1,74 @@
+[
+    {
+        "description": "remote ref",
+        "schema": {"$ref": "http://localhost:1234/integer.json"},
+        "tests": [
+            {
+                "description": "remote ref valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "remote ref invalid",
+                "data": "a",
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "fragment within remote ref",
+        "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
+        "tests": [
+            {
+                "description": "remote fragment valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "remote fragment invalid",
+                "data": "a",
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "ref within remote ref",
+        "schema": {
+            "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
+        },
+        "tests": [
+            {
+                "description": "ref within ref valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "ref within ref invalid",
+                "data": "a",
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "change resolution scope",
+        "schema": {
+            "id": "http://localhost:1234/",
+            "items": {
+                "id": "folder/",
+                "items": {"$ref": "folderInteger.json"}
+            }
+        },
+        "tests": [
+            {
+                "description": "changed scope ref valid",
+                "data": [[1]],
+                "valid": true
+            },
+            {
+                "description": "changed scope ref invalid",
+                "data": [["a"]],
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/required.json b/examples/biojson-doc/tests/required.json
new file mode 100755 (executable)
index 0000000..612f73f
--- /dev/null
@@ -0,0 +1,39 @@
+[
+    {
+        "description": "required validation",
+        "schema": {
+            "properties": {
+                "foo": {},
+                "bar": {}
+            },
+            "required": ["foo"]
+        },
+        "tests": [
+            {
+                "description": "present required property is valid",
+                "data": {"foo": 1},
+                "valid": true
+            },
+            {
+                "description": "non-present required property is invalid",
+                "data": {"bar": 1},
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "required default validation",
+        "schema": {
+            "properties": {
+                "foo": {}
+            }
+        },
+        "tests": [
+            {
+                "description": "not required by default",
+                "data": {},
+                "valid": true
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/schema.json b/examples/biojson-doc/tests/schema.json
new file mode 100755 (executable)
index 0000000..7ddd475
--- /dev/null
@@ -0,0 +1,154 @@
+[
+    {
+        "schema": {
+            "id": "http://json-schema.org/draft-04/schema#",
+            "$schema": "http://json-schema.org/draft-04/schema#",
+            "description": "Core schema meta-schema",
+            "definitions": {
+                "schemaArray": {
+                    "type": "array",
+                    "minItems": 1,
+                    "items": { "$ref": "#" }
+                },
+                "positiveInteger": {
+                    "type": "integer",
+                    "minimum": 0
+                },
+                "positiveIntegerDefault0": {
+                    "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
+                },
+                "simpleTypes": {
+                    "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
+                },
+                "stringArray": {
+                    "type": "array",
+                    "items": { "type": "string" },
+                    "minItems": 1,
+                    "uniqueItems": true
+                }
+            },
+            "type": "object",
+            "properties": {
+                "id": {
+                    "type": "string",
+                    "format": "uri"
+                },
+                "$schema": {
+                    "type": "string",
+                    "format": "uri"
+                },
+                "title": {
+                    "type": "string"
+                },
+                "description": {
+                    "type": "string"
+                },
+                "default": {},
+                "multipleOf": {
+                    "type": "number",
+                    "minimum": 0,
+                    "exclusiveMinimum": true
+                },
+                "maximum": {
+                    "type": "number"
+                },
+                "exclusiveMaximum": {
+                    "type": "boolean",
+                    "default": false
+                },
+                "minimum": {
+                    "type": "number"
+                },
+                "exclusiveMinimum": {
+                    "type": "boolean",
+                    "default": false
+                },
+                "maxLength": { "$ref": "#/definitions/positiveInteger" },
+                "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
+                "pattern": {
+                    "type": "string",
+                    "format": "regex"
+                },
+                "additionalItems": {
+                    "anyOf": [
+                        { "type": "boolean" },
+                        { "$ref": "#" }
+                    ],
+                    "default": {}
+                },
+                "items": {
+                    "anyOf": [
+                        { "$ref": "#" },
+                        { "$ref": "#/definitions/schemaArray" }
+                    ],
+                    "default": {}
+                },
+                "maxItems": { "$ref": "#/definitions/positiveInteger" },
+                "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
+                "uniqueItems": {
+                    "type": "boolean",
+                    "default": false
+                },
+                "maxProperties": { "$ref": "#/definitions/positiveInteger" },
+                "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
+                "required": { "$ref": "#/definitions/stringArray" },
+                "additionalProperties": {
+                    "anyOf": [
+                        { "type": "boolean" },
+                        { "$ref": "#" }
+                    ],
+                    "default": {}
+                },
+                "definitions": {
+                    "type": "object",
+                    "additionalProperties": { "$ref": "#" },
+                    "default": {}
+                },
+                "properties": {
+                    "type": "object",
+                    "additionalProperties": { "$ref": "#" },
+                    "default": {}
+                },
+                "patternProperties": {
+                    "type": "object",
+                    "additionalProperties": { "$ref": "#" },
+                    "default": {}
+                },
+                "dependencies": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "anyOf": [
+                            { "$ref": "#" },
+                            { "$ref": "#/definitions/stringArray" }
+                        ]
+                    }
+                },
+                "enum": {
+                    "type": "array",
+                    "minItems": 1,
+                    "uniqueItems": true
+                },
+                "type": {
+                    "anyOf": [
+                        { "$ref": "#/definitions/simpleTypes" },
+                        {
+                            "type": "array",
+                            "items": { "$ref": "#/definitions/simpleTypes" },
+                            "minItems": 1,
+                            "uniqueItems": true
+                        }
+                    ]
+                },
+                "allOf": { "$ref": "#/definitions/schemaArray" },
+                "anyOf": { "$ref": "#/definitions/schemaArray" },
+                "oneOf": { "$ref": "#/definitions/schemaArray" },
+                "not": { "$ref": "#" }
+            },
+            "dependencies": {
+                "exclusiveMaximum": [ "maximum" ],
+                "exclusiveMinimum": [ "minimum" ]
+            },
+            "default": {}
+        }
+
+    }]
diff --git a/examples/biojson-doc/tests/test.html b/examples/biojson-doc/tests/test.html
new file mode 100755 (executable)
index 0000000..2cbbd50
--- /dev/null
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Docson Test</title>
+    <meta charset="utf-8">
+    <link rel="stylesheet" href="../css/docson.css">
+    <script src="../lib/require.js"></script></head>
+    <style>
+        body {
+            font-family: verdana, helvetica;
+        }
+    </style>
+</head>
+<body>
+<script charset="utf-8">
+    var tests = [ "invoice$Invoice", "enum", "schema","additionalProperties", "address", "fstab", "basic", "not", "oneOf", "anyOf", "allOf", "example2", "properties", "ref"]
+    require.config({ baseUrl: ".."});
+    require(["docson", "lib/jquery"], function(docson) {
+        docson.templateBaseUrl="../templates";
+        $(function() {
+            $.each(tests, function(k, test) {
+                var block = $("<div/>");
+                var segments = test.split("$");
+                $("body").append("<h2>"+segments[0]+"</h2>");
+                $("body").append(block);
+                $.get(segments[0]+".json").done(function(items) {
+                    $.each(items, function(k, item) {
+                        var element = $("<div/>");
+                        block.append(element);
+                        if(!item.schema.description) {
+                            item.schema.description = item.description;
+                        }
+                        docson.doc(element, item.schema, segments[1]);
+                    });
+                });
+            });
+        });
+    });
+</script>
+</body>
+</html>
diff --git a/examples/biojson-doc/tests/type.json b/examples/biojson-doc/tests/type.json
new file mode 100755 (executable)
index 0000000..257f051
--- /dev/null
@@ -0,0 +1,330 @@
+[
+    {
+        "description": "integer type matches integers",
+        "schema": {"type": "integer"},
+        "tests": [
+            {
+                "description": "an integer is an integer",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "a float is not an integer",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is not an integer",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is not an integer",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not an integer",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not an integer",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is not an integer",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "number type matches numbers",
+        "schema": {"type": "number"},
+        "tests": [
+            {
+                "description": "an integer is a number",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "a float is a number",
+                "data": 1.1,
+                "valid": true
+            },
+            {
+                "description": "a string is not a number",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is not a number",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not a number",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not a number",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is not a number",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "string type matches strings",
+        "schema": {"type": "string"},
+        "tests": [
+            {
+                "description": "1 is not a string",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "a float is not a string",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is a string",
+                "data": "foo",
+                "valid": true
+            },
+            {
+                "description": "an object is not a string",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not a string",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not a string",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is not a string",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "object type matches objects",
+        "schema": {"type": "object"},
+        "tests": [
+            {
+                "description": "an integer is not an object",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "a float is not an object",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is not an object",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is an object",
+                "data": {},
+                "valid": true
+            },
+            {
+                "description": "an array is not an object",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not an object",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is not an object",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "array type matches arrays",
+        "schema": {"type": "array"},
+        "tests": [
+            {
+                "description": "an integer is not an array",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "a float is not an array",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is not an array",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is not an array",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not an array",
+                "data": [],
+                "valid": true
+            },
+            {
+                "description": "a boolean is not an array",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is not an array",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "boolean type matches booleans",
+        "schema": {"type": "boolean"},
+        "tests": [
+            {
+                "description": "an integer is not a boolean",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "a float is not a boolean",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is not a boolean",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is not a boolean",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not a boolean",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not a boolean",
+                "data": true,
+                "valid": true
+            },
+            {
+                "description": "null is not a boolean",
+                "data": null,
+                "valid": false
+            }
+        ]
+    },
+    {
+        "description": "null type matches only the null object",
+        "schema": {"type": "null"},
+        "tests": [
+            {
+                "description": "an integer is not null",
+                "data": 1,
+                "valid": false
+            },
+            {
+                "description": "a float is not null",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "a string is not null",
+                "data": "foo",
+                "valid": false
+            },
+            {
+                "description": "an object is not null",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is not null",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is not null",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is null",
+                "data": null,
+                "valid": true
+            }
+        ]
+    },
+    {
+        "description": "multiple types can be specified in an array",
+        "schema": {"type": ["integer", "string"]},
+        "tests": [
+            {
+                "description": "an integer is valid",
+                "data": 1,
+                "valid": true
+            },
+            {
+                "description": "a string is valid",
+                "data": "foo",
+                "valid": true
+            },
+            {
+                "description": "a float is invalid",
+                "data": 1.1,
+                "valid": false
+            },
+            {
+                "description": "an object is invalid",
+                "data": {},
+                "valid": false
+            },
+            {
+                "description": "an array is invalid",
+                "data": [],
+                "valid": false
+            },
+            {
+                "description": "a boolean is invalid",
+                "data": true,
+                "valid": false
+            },
+            {
+                "description": "null is invalid",
+                "data": null,
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/tests/uniqueItems.json b/examples/biojson-doc/tests/uniqueItems.json
new file mode 100755 (executable)
index 0000000..c1f4ab9
--- /dev/null
@@ -0,0 +1,79 @@
+[
+    {
+        "description": "uniqueItems validation",
+        "schema": {"uniqueItems": true},
+        "tests": [
+            {
+                "description": "unique array of integers is valid",
+                "data": [1, 2],
+                "valid": true
+            },
+            {
+                "description": "non-unique array of integers is invalid",
+                "data": [1, 1],
+                "valid": false
+            },
+            {
+                "description": "numbers are unique if mathematically unequal",
+                "data": [1.0, 1.00, 1],
+                "valid": false
+            },
+            {
+                "description": "unique array of objects is valid",
+                "data": [{"foo": "bar"}, {"foo": "baz"}],
+                "valid": true
+            },
+            {
+                "description": "non-unique array of objects is invalid",
+                "data": [{"foo": "bar"}, {"foo": "bar"}],
+                "valid": false
+            },
+            {
+                "description": "unique array of nested objects is valid",
+                "data": [
+                    {"foo": {"bar" : {"baz" : true}}},
+                    {"foo": {"bar" : {"baz" : false}}}
+                ],
+                "valid": true
+            },
+            {
+                "description": "non-unique array of nested objects is invalid",
+                "data": [
+                    {"foo": {"bar" : {"baz" : true}}},
+                    {"foo": {"bar" : {"baz" : true}}}
+                ],
+                "valid": false
+            },
+            {
+                "description": "unique array of arrays is valid",
+                "data": [["foo"], ["bar"]],
+                "valid": true
+            },
+            {
+                "description": "non-unique array of arrays is invalid",
+                "data": [["foo"], ["foo"]],
+                "valid": false
+            },
+            {
+                "description": "1 and true are unique",
+                "data": [1, true],
+                "valid": true
+            },
+            {
+                "description": "0 and false are unique",
+                "data": [0, false],
+                "valid": true
+            },
+            {
+                "description": "unique heterogeneous types are valid",
+                "data": [{}, [1], true, null, 1],
+                "valid": true
+            },
+            {
+                "description": "non-unique heterogeneous types are invalid",
+                "data": [{}, [1], true, null, {}, 1],
+                "valid": false
+            }
+        ]
+    }
+]
diff --git a/examples/biojson-doc/widget.js b/examples/biojson-doc/widget.js
new file mode 100755 (executable)
index 0000000..4c5c0a8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 Laurent Bovet <laurent.bovet@windmaster.ch>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var scripts = document.getElementsByTagName('script');
+var script = scripts[ scripts.length - 1 ];
+
+if (script.attributes["data-schema"]) {
+    var docson;
+    if (script.attributes["data-docson"]) {
+        docson = script.attributes["data-docson"].value;
+    } else {
+        docson = script.src.replace("widget.js", "index.html");
+    }
+    document.write("<iframe id='" + script.attributes["data-schema"].value +
+        "' style='padding: 0; border: 0; width:100%; background: transparent' src='" +
+        docson + "#" +
+        script.attributes["data-schema"].value + "'></iframe>");
+    function receiveMessage(event) {
+        console.log("<", event)
+        if (event.data.id && event.data.id == "docson") {
+          var frame = document.getElementById(event.data.url);
+          if(event.data.action == "resized") {
+            frame.height = event.data.height + 18;
+          }
+          if(event.data.action == "ready") {
+            console.log(frame.parentNode)
+            frame.contentWindow.postMessage({ id: "docson", font: window.getComputedStyle(frame.parentNode).fontFamily}, "*");
+          }
+        }
+
+    }
+    window.addEventListener("message", receiveMessage, false);
+} else {
+    alert("<p style='color:red'>Missing data-schema (url to schema)</p>")
+}
diff --git a/examples/biojsonschema.json b/examples/biojsonschema.json
new file mode 100644 (file)
index 0000000..e6670d2
--- /dev/null
@@ -0,0 +1 @@
+{"$schema":"http://json-schema.org/draft-04/schema#","id":"http://jsonschema.net","type":"object","properties":{"seqs":{"id":"http://jsonschema.net/seqs","type":"array","items":{"id":"http://jsonschema.net/seqs/0","type":"object","properties":{"name":{"id":"http://jsonschema.net/seqs/0/name","type":"string","description":"Sequence name"},"start":{"id":"http://jsonschema.net/seqs/0/start","type":"integer","description":"Start residue position"},"svid":{"id":"http://jsonschema.net/seqs/0/svid","type":"string","description":"Serial version id for sequence object"},"end":{"id":"http://jsonschema.net/seqs/0/end","type":"integer","description":"End residue position"},"id":{"id":"http://jsonschema.net/seqs/0/id","type":"string","description":"Sequence unique identifier"},"seq":{"id":"http://jsonschema.net/seqs/0/seq","type":"string","description":"Sequence Residues"},"order":{"id":"http://jsonschema.net/seqs/0/order","type":"integer","description":"Sequence order in an alignment space"}},"required":["name","start","svid","end","id","seq"]},"required":["0"],"description":"Sequences in the Alignemnt","minItems":"1","maxItems":"*"},"appSettings":{"id":"http://jsonschema.net/appSettings","type":"object","properties":{"globalColorScheme":{"id":"http://jsonschema.net/appSettings/globalColorScheme","type":"string","description":"Global colour schem for the alignment"},"webStartUrl":{"id":"http://jsonschema.net/appSettings/webStartUrl","type":"string","description":"Jalview specific setting which points to a url for launching Jalview"},"application":{"id":"http://jsonschema.net/appSettings/application","type":"string","description":"Application which generated the Json"},"showSeqFeatures":{"id":"http://jsonschema.net/appSettings/showSeqFeatures","type":"string","description":"Determines if sequence features are visible or not"},"version":{"id":"http://jsonschema.net/appSettings/version","type":"string","description":"Verion of the application which generated the JSON"},"hiddenCols":{"id":"http://jsonschema.net/appSettings/hiddenCols","type":"string","description":"Delimited lists of hidden colums ranges i.e [2-3,5-5,11-23]"}},"description":"Application specific settings"},"seqGroups":{"id":"http://jsonschema.net/seqGroups","type":"array","items":[{"id":"http://jsonschema.net/seqGroups/0","type":"object","properties":{"displayText":{"id":"http://jsonschema.net/seqGroups/0/displayText","type":"boolean","description":"Determines if the texts of the group is displayed or not"},"startRes":{"id":"http://jsonschema.net/seqGroups/0/startRes","type":"integer","description":"Start residue position for a given group"},"groupName":{"id":"http://jsonschema.net/seqGroups/0/groupName","type":"string","description":"Group name"},"endRes":{"id":"http://jsonschema.net/seqGroups/0/endRes","type":"integer","description":"End residue position for a given group"},"colourText":{"id":"http://jsonschema.net/seqGroups/0/colourText","type":"boolean","description":"Determines if the Residues text for the group is coloured"},"seqsHash":{"id":"http://jsonschema.net/seqGroups/0/seqsHash","type":"array","items":[],"minItems":"0","maxItems":"*","description":"The id's of the sequences which belongs to the group"},"svid":{"id":"http://jsonschema.net/seqGroups/0/svid","type":"string","description":"Serial version id for a given group"},"showNonconserved":{"id":"http://jsonschema.net/seqGroups/0/showNonconserved","type":"boolean","description":"Determines if non conserved regions of a group is shown or not"},"colourScheme":{"id":"http://jsonschema.net/seqGroups/0/colourScheme","type":"string","description":"Colour Scheme for the sequence group"},"displayBoxes":{"id":"http://jsonschema.net/seqGroups/0/displayBoxes","type":"boolean","description":"Determines if the group border should be visible or not"}}}],"description":"Sequence groups in the Alignment","minItems":"0","maxItems":"*"},"alignAnnotation":{"id":"http://jsonschema.net/alignAnnotation","type":"array","items":{"id":"http://jsonschema.net/alignAnnotation/0","type":"object","properties":{"svid":{"id":"http://jsonschema.net/alignAnnotation/0/svid","type":"string","description":"Serial version id for the annotation object"},"annotations":{"id":"http://jsonschema.net/alignAnnotation/0/annotations","type":"array","items":[{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0","type":"object","properties":{"displayCharacter":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0/displayCharacter","type":"string","description":"Display character to denote the given annotation"},"value":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0/value","type":"integer","description":"Value of the annotation"},"secondaryStructure":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/0/secondaryStructure","type":"string","description":"Secondary structure symbol for the given annotation"}}},{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1","type":"object","properties":{"displayCharacter":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1/displayCharacter","type":"string"},"value":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1/value","type":"integer"},"secondaryStructure":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/1/secondaryStructure","type":"string"}}},{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2","type":"object","properties":{"displayCharacter":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2/displayCharacter","type":"string"},"value":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2/value","type":"integer"},"secondaryStructure":{"id":"http://jsonschema.net/alignAnnotation/0/annotations/2/secondaryStructure","type":"string"}}}]},"description":{"id":"http://jsonschema.net/alignAnnotation/0/description","type":"string","description":"Description of the Alignment Annotation"},"label":{"id":"http://jsonschema.net/alignAnnotation/0/label","type":"string","description":"Label for the Annotation"}}},"description":"Alignment Annotations","minItems":"0","maxItems":"*"},"svid":{"id":"http://jsonschema.net/svid","type":"string","description":"Serial version id"},"seqFeatures":{"id":"http://jsonschema.net/seqFeatures","type":"array","items":{"id":"http://jsonschema.net/seqFeatures/0","type":"object","properties":{"fillColor":{"id":"http://jsonschema.net/seqFeatures/0/fillColor","type":"string","description":"Fill colour"},"score":{"id":"http://jsonschema.net/seqFeatures/0/score","type":"integer","description":"Score"},"sequenceRef":{"id":"http://jsonschema.net/seqFeatures/0/sequenceRef","type":"string","description":"Reference to the Sequence in the alignement (More like a foreign key)"},"featureGroup":{"id":"http://jsonschema.net/seqFeatures/0/featureGroup","type":"string","description":"Feature Group"},"svid":{"id":"http://jsonschema.net/seqFeatures/0/svid","type":"string","description":"Serial version id for the SeqFeature object"},"description":{"id":"http://jsonschema.net/seqFeatures/0/description","type":"string","description":"Description of Feature"},"xStart":{"id":"http://jsonschema.net/seqFeatures/0/xStart","type":"integer","description":"Start residue position for the sequence feature"},"xEnd":{"id":"http://jsonschema.net/seqFeatures/0/xEnd","type":"integer","description":"End residue position for the sequence feature"},"type":{"id":"http://jsonschema.net/seqFeatures/0/type","type":"string","description":"The name of the SequenceFeature"}}},"minItems":"0","maxItems":"*","description":"Sequence Features within the alignment"}},"required":["seqs","appSettings","seqGroups","alignAnnotation","svid","seqFeatures"]}
\ No newline at end of file
index b66778c..7cc72cf 100644 (file)
@@ -1,5 +1,7 @@
 package ext.edu.ucsf.rbvi.strucviz2;
 
+import jalview.ws.HttpClientUtils;
+
 import java.awt.Color;
 import java.io.BufferedReader;
 import java.io.File;
@@ -20,8 +22,6 @@ import org.slf4j.LoggerFactory;
 import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
 import ext.edu.ucsf.rbvi.strucviz2.port.ListenerThreads;
 
-import jalview.ws.HttpClientUtils;
-
 /**
  * This object maintains the Chimera communication information.
  */
@@ -554,8 +554,8 @@ public class ChimeraManager
     for (String chimeraPath : chimeraPaths)
     {
       File path = new File(chimeraPath);
-      // enable the next line to simulate Chimera not installed
-      // File path = new File(chimeraPath + "x");
+      // uncomment the next line to simulate Chimera not installed
+      // path = new File(chimeraPath + "x");
       if (!path.canExecute())
       {
         error += "File '" + path + "' does not exist.\n";
@@ -565,6 +565,8 @@ public class ChimeraManager
       {
         List<String> args = new ArrayList<String>();
         args.add(chimeraPath);
+        // shows Chimera output window but suppresses REST responses:
+        // args.add("--debug");
         args.add("--start");
         args.add("RESTServer");
         ProcessBuilder pb = new ProcessBuilder(args);
@@ -589,7 +591,7 @@ public class ChimeraManager
       // TODO: [Optional] Check Chimera version and show a warning if below 1.8
       // Ask Chimera to give us updates
       // startListening(); // later - see ChimeraListener
-      return true;
+      return (chimeraRestPort > 0);
     }
 
     // Tell the user that Chimera could not be started because of an error
@@ -606,20 +608,29 @@ public class ChimeraManager
     InputStream readChan = chimera.getInputStream();
     BufferedReader lineReader = new BufferedReader(new InputStreamReader(
             readChan));
-    String response = null;
+    StringBuilder responses = new StringBuilder();
     try
     {
-      // expect: REST server on host 127.0.0.1 port port_number
-      response = lineReader.readLine();
-      String [] tokens = response.split(" ");
-      if (tokens.length == 7 && "port".equals(tokens[5])) {
-        port = Integer.parseInt(tokens[6]);
-        logger.info("Chimera REST service listening on port "
-                + chimeraRestPort);
+      String response = lineReader.readLine();
+      while (response != null)
+      {
+        responses.append("\n" + response);
+        // expect: REST server on host 127.0.0.1 port port_number
+        if (response.startsWith("REST server"))
+        {
+          String[] tokens = response.split(" ");
+          if (tokens.length == 7 && "port".equals(tokens[5]))
+          {
+            port = Integer.parseInt(tokens[6]);
+            break;
+          }
+        }
+        response = lineReader.readLine();
       }
     } catch (Exception e)
     {
-      logger.error("Failed to get REST port number from " + response + ": "
+      logger.error("Failed to get REST port number from " + responses
+              + ": "
               + e.getMessage());
     } finally
     {
@@ -630,6 +641,13 @@ public class ChimeraManager
       {
       }
     }
+    if (port == 0)
+    {
+      System.err
+              .println("Failed to start Chimera with REST service, response was: "
+                      + responses);
+    }
+    logger.info("Chimera REST service listening on port " + chimeraRestPort);
     return port;
   }
 
index 69c39df..50045dc 100755 (executable)
@@ -596,7 +596,6 @@ public class AAFrequency
       // array holds #seqs, #ungapped, then codon counts indexed by codon
       final int[] codonCounts = (int[]) hci.get(PROFILE);
       int totalCount = 0;
-      StringBuilder mouseOver = new StringBuilder(32);
 
       /*
        * First pass - get total count and find the highest
@@ -636,26 +635,56 @@ public class AAFrequency
        */
 
       /*
-       * Scan sorted array backwards for most frequent values first.
+       * Scan sorted array backwards for most frequent values first. Show
+       * repeated values compactly.
        */
+      StringBuilder mouseOver = new StringBuilder(32);
+      StringBuilder samePercent = new StringBuilder();
+      String percent = null;
+      String lastPercent = null;
+      Format fmt = getPercentageFormat(nseqs);
+
       for (int j = codons.length - 1; j >= 0; j--)
       {
         int codonCount = sortedCodonCounts[j];
         if (codonCount == 0)
         {
+          /*
+           * remaining codons are 0% - ignore, but finish off the last one if
+           * necessary
+           */
+          if (samePercent.length() > 0)
+          {
+            mouseOver.append(samePercent).append(": ").append(percent)
+                    .append("% ");
+          }
           break;
         }
         int codonEncoded = codons[j];
         final int pct = codonCount * 100 / totalCount;
         String codon = String
                 .valueOf(CodingUtils.decodeCodon(codonEncoded));
-        Format fmt = getPercentageFormat(nseqs);
-        String formatted = fmt == null ? Integer.toString(pct) : fmt
+        percent = fmt == null ? Integer.toString(pct) : fmt
                 .form(pct);
         if (showProfileLogo || codonCount == modalCodonCount)
         {
-          mouseOver.append(codon).append(": ").append(formatted)
-                  .append("% ");
+          if (percent.equals(lastPercent) && j > 0)
+          {
+            samePercent.append(samePercent.length() == 0 ? "" : ", ");
+            samePercent.append(codon);
+          }
+          else
+          {
+            if (samePercent.length() > 0)
+            {
+              mouseOver.append(samePercent).append(": ")
+                      .append(lastPercent)
+                      .append("% ");
+            }
+            samePercent.setLength(0);
+            samePercent.append(codon);
+          }
+          lastPercent = percent;
         }
       }
 
index 3cea1a3..0225dc7 100644 (file)
@@ -930,6 +930,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
         aa.visible = visible;
     }
     alignPanel.validateAnnotationDimensions(true);
+    validate();
+    repaint();
   }
 
   private void setAnnotationSortOrder(SequenceAnnotationOrder order)
index 0516533..463c6c3 100644 (file)
  */
 package jalview.appletgui;
 
+import jalview.analysis.AnnotationSorter;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.JalviewLite;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SequenceI;
+import jalview.structure.StructureSelectionManager;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
@@ -34,15 +43,6 @@ import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
 import java.util.List;
 
-import jalview.analysis.AnnotationSorter;
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.bin.JalviewLite;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
-import jalview.datamodel.SequenceI;
-import jalview.structure.StructureSelectionManager;
-
 public class AlignmentPanel extends Panel implements AdjustmentListener,
         AlignmentViewPanel
 {
@@ -552,70 +552,60 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
   }
 
   /**
-   * calculate the annotation dimensions and refresh slider values accordingly.
-   * need to do repaints/notifys afterwards.
+   * Calculate the annotation dimensions and refresh slider values accordingly.
+   * Need to do repaints/notifys afterwards.
    */
   protected void validateAnnotationDimensions(boolean adjustPanelHeight)
   {
-    boolean modified = false;
-    int height = av.calcPanelHeight();
-    int minsize = 0;
-    if (hscroll.isVisible())
-    {
-      height += (minsize = hscroll.getPreferredSize().height);
-    }
-    if (apvscroll.isVisible())
-    {
-      minsize += apvscroll.getPreferredSize().height;
-    }
-    int mheight = height;
-    Dimension d = sequenceHolderPanel.getSize(), e = idPanel.getSize();
-    int seqandannot = d.height - scalePanelHolder.getSize().height;
+    int rowHeight = av.getCharHeight();
+    int alignmentHeight = rowHeight * av.getAlignment().getHeight();
+    int annotationHeight = av.calcPanelHeight();
+
+    int mheight = annotationHeight;
+    Dimension d = sequenceHolderPanel.getSize();
+
+    int availableHeight = d.height - scalePanelHolder.getHeight();
 
     if (adjustPanelHeight)
     {
-      // NOTE: this logic is different in the application. Need a better
-      // algorithm to define behaviour
-      // sets initial preferred height
-      // try and set height according to alignment
-      float sscaling = (float) ((av.getCharHeight() * av.getAlignment()
-              .getHeight()) / (1.0 * mheight));
-      if (sscaling > 0.5)
-      {
-        // if the alignment is too big then
-        // default is 0.5 split
-        height = seqandannot / 2;
-      }
-      else
+      /*
+       * If not enough vertical space, maximize annotation height while keeping
+       * at least two rows of alignment visible
+       */
+      if (annotationHeight + alignmentHeight > availableHeight)
       {
-        // otherwise just set the panel so that one row of sequence is visible
-        height = -av.getCharHeight() * 1
-                + (int) (seqandannot * (1 - sscaling));
+        annotationHeight = Math.min(annotationHeight, availableHeight - 2
+                * rowHeight);
       }
     }
     else
     {
       // maintain same window layout whilst updating sliders
-      height = annotationPanelHolder.getSize().height;
+      annotationHeight = annotationPanelHolder.getSize().height;
     }
 
-    if (seqandannot - height < 5)
+    if (availableHeight - annotationHeight < 5)
     {
-      height = seqandannot;
+      annotationHeight = availableHeight;
     }
-    annotationPanel.setSize(new Dimension(d.width, height));
-    alabels.setSize(new Dimension(e.width, height));
-    annotationSpaceFillerHolder.setSize(new Dimension(e.width, height));
-    annotationPanelHolder.setSize(new Dimension(d.width, height));
+
+    annotationPanel.setSize(new Dimension(d.width, annotationHeight));
+    annotationPanelHolder.setSize(new Dimension(d.width, annotationHeight));
     // seqPanelHolder.setSize(d.width, seqandannot - height);
     seqPanel.seqCanvas
             .setSize(d.width, seqPanel.seqCanvas.getSize().height);
+
+    Dimension e = idPanel.getSize();
+    alabels.setSize(new Dimension(e.width, annotationHeight));
+    annotationSpaceFillerHolder.setSize(new Dimension(e.width,
+            annotationHeight));
+
     int s = apvscroll.getValue();
-    if (s > mheight - height)
+    if (s > mheight - annotationHeight)
     {
       s = 0;
     }
-    apvscroll.setValues(s, height, 0, mheight);
+    apvscroll.setValues(s, annotationHeight, 0, mheight);
     annotationPanel.setScrollOffset(apvscroll.getValue(), false);
     alabels.setScrollOffset(apvscroll.getValue(), false);
   }
index 00a042d..f6d0099 100755 (executable)
@@ -225,10 +225,13 @@ public class Jalview
       desktop.checkForNews();
     }
 
-    BioJsHTMLOutput.updateBioJS();
+    if (!isHeadlessMode())
+    {
+      BioJsHTMLOutput.updateBioJS();
+    }
 
     String file = null, protocol = null, format = null, data = null;
-    jalview.io.FileLoader fileLoader = new jalview.io.FileLoader();
+    jalview.io.FileLoader fileLoader = new jalview.io.FileLoader(!headless);
     Vector getFeatures = null; // vector of das source nicknames to fetch
     // features from
     // loading is done.
@@ -518,8 +521,10 @@ public class Jalview
           }
           else if (format.equalsIgnoreCase("eps"))
           {
-            System.out.println("Creating EPS file: " + file);
-            af.createEPS(new java.io.File(file));
+            File outputFile = new java.io.File(file);
+            System.out.println("Creating EPS file: "
+                    + outputFile.getAbsolutePath());
+            af.createEPS(outputFile);
             continue;
           }
 
@@ -963,6 +968,16 @@ public class Jalview
     }
     return ff;
   }
+
+  public static boolean isHeadlessMode()
+  {
+    String isheadless = System.getProperty("java.awt.headless");
+    if (isheadless != null && isheadless.equalsIgnoreCase("true"))
+    {
+      return true;
+    }
+    return false;
+  }
 }
 
 /**
@@ -1131,4 +1146,4 @@ class FeatureFetcher
     return queued == 0 && running == 0;
   }
 
-};
+}
index 37bb7a9..c78ec22 100755 (executable)
@@ -20,6 +20,9 @@
  */
 package jalview.datamodel;
 
+import jalview.analysis.AlignSeq;
+import jalview.util.StringUtils;
+
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
@@ -27,9 +30,6 @@ import java.util.Vector;
 
 import fr.orsay.lri.varna.models.rna.RNA;
 
-import jalview.analysis.AlignSeq;
-import jalview.util.StringUtils;
-
 /**
  * 
  * Implements the SequenceI interface for a char[] based sequence object.
@@ -768,7 +768,7 @@ public class Sequence extends ASequence implements SequenceI
   public void deleteChars(int i, int j)
   {
     int newstart = start, newend = end;
-    if (i >= sequence.length)
+    if (i >= sequence.length || i < 0)
     {
       return;
     }
index 3f890ba..9c9447e 100644 (file)
  */
 package jalview.datamodel.xdb.embl;
 
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.Vector;
-
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.FeatureProperties;
@@ -33,6 +28,10 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 
+import java.util.Hashtable;
+import java.util.Map.Entry;
+import java.util.Vector;
+
 /**
  * Data model for one entry returned from an EMBL query, as marshalled by a
  * Castor binding file
@@ -397,7 +396,7 @@ public class EmblEntry
           boolean noPeptide, String sourceDb)
   { // TODO: ensure emblEntry.getSequences behaves correctly for returning all
     // cases of noNa and noPeptide
-    Vector seqs = new Vector();
+    Vector<SequenceI> seqs = new Vector<SequenceI>();
     Sequence dna = null;
     if (!noNa)
     {
@@ -415,26 +414,23 @@ public class EmblEntry
       // TODO: transform EMBL Database refs to canonical form
       if (dbRefs != null)
       {
-        for (Iterator i = dbRefs.iterator(); i.hasNext(); dna
-                .addDBRef((DBRefEntry) i.next()))
+        for (DBRefEntry dbref : dbRefs)
         {
-          ;
+          dna.addDBRef(dbref);
         }
       }
     }
     try
     {
-      for (Iterator i = features.iterator(); i.hasNext();)
+      for (EmblFeature feature: features)
       {
-        EmblFeature feature = (EmblFeature) i.next();
         if (!noNa)
         {
-          if (feature.dbRefs != null && feature.dbRefs.size() > 0)
+          if (feature.dbRefs != null)
           {
-            for (Iterator dbr = feature.dbRefs.iterator(); dbr.hasNext(); dna
-                    .addDBRef((DBRefEntry) dbr.next()))
+            for (DBRefEntry dbref : feature.dbRefs)
             {
-              ;
+              dna.addDBRef(dbref);
             }
           }
         }
@@ -445,14 +441,14 @@ public class EmblEntry
         else
         {
           // General feature type.
+          // TODO this is just duplicated code ??
           if (!noNa)
           {
-            if (feature.dbRefs != null && feature.dbRefs.size() > 0)
+            if (feature.dbRefs != null)
             {
-              for (Iterator dbr = feature.dbRefs.iterator(); dbr.hasNext(); dna
-                      .addDBRef((DBRefEntry) dbr.next()))
+              for (DBRefEntry dbref : feature.dbRefs)
               {
-                ;
+                dna.addDBRef(dbref);
               }
             }
           }
@@ -474,7 +470,7 @@ public class EmblEntry
     SequenceI[] sqs = new SequenceI[seqs.size()];
     for (int i = 0, j = seqs.size(); i < j; i++)
     {
-      sqs[i] = (SequenceI) seqs.elementAt(i);
+      sqs[i] = seqs.elementAt(i);
       seqs.set(i, null);
     }
     return sqs;
@@ -496,19 +492,16 @@ public class EmblEntry
    *          flag for generation of Peptide sequence objects
    */
   private void parseCodingFeature(EmblFeature feature, String sourceDb,
-          Vector seqs, Sequence dna, boolean noPeptide)
+          Vector<SequenceI> seqs, Sequence dna, boolean noPeptide)
   {
     boolean isEmblCdna = sourceDb.equals(DBRefSource.EMBLCDS);
     // extract coding region(s)
     jalview.datamodel.Mapping map = null;
     int[] exon = null;
-    if (feature.locations != null && feature.locations.size() > 0)
+    if (feature.locations != null)
     {
-      for (Enumeration locs = feature.locations.elements(); locs
-              .hasMoreElements();)
+      for (EmblFeatureLocations loc : feature.locations)
       {
-        EmblFeatureLocations loc = (EmblFeatureLocations) locs
-                .nextElement();
         int[] se = loc.getElementRanges(accession);
         if (exon == null)
         {
@@ -526,19 +519,17 @@ public class EmblEntry
     String prseq = null;
     String prname = new String();
     String prid = null;
-    Hashtable vals = new Hashtable();
+    Hashtable<String, String> vals = new Hashtable<String, String>();
     int prstart = 1;
     // get qualifiers
-    if (feature.getQualifiers() != null
-            && feature.getQualifiers().size() > 0)
+    if (feature.getQualifiers() != null)
     {
-      for (Iterator quals = feature.getQualifiers().iterator(); quals
-              .hasNext();)
+      for (Qualifier q : feature.getQualifiers())
       {
-        Qualifier q = (Qualifier) quals.next();
-        if (q.getName().equals("translation"))
+        String qname = q.getName();
+        if (qname.equals("translation"))
         {
-          StringBuffer prsq = new StringBuffer(q.getValues()[0]);
+          StringBuilder prsq = new StringBuilder(q.getValues()[0]);
           int p = prsq.indexOf(" ");
           while (p > -1)
           {
@@ -549,15 +540,15 @@ public class EmblEntry
           prsq = null;
 
         }
-        else if (q.getName().equals("protein_id"))
+        else if (qname.equals("protein_id"))
         {
           prid = q.getValues()[0];
         }
-        else if (q.getName().equals("codon_start"))
+        else if (qname.equals("codon_start"))
         {
           prstart = Integer.parseInt(q.getValues()[0]);
         }
-        else if (q.getName().equals("product"))
+        else if (qname.equals("product"))
         {
           prname = q.getValues()[0];
         }
@@ -565,7 +556,7 @@ public class EmblEntry
         {
           // throw anything else into the additional properties hash
           String[] s = q.getValues();
-          StringBuffer sb = new StringBuffer();
+          StringBuilder sb = new StringBuilder();
           if (s != null)
           {
             for (int i = 0; i < s.length; i++)
@@ -574,15 +565,15 @@ public class EmblEntry
               sb.append("\n");
             }
           }
-          vals.put(q.getName(), sb.toString());
+          vals.put(qname, sb.toString());
         }
       }
     }
     Sequence product = null;
     DBRefEntry protEMBLCDS = null;
     exon = adjustForPrStart(prstart, exon);
-    boolean noProteinDbref=true;
-    
+    boolean noProteinDbref = true;
+
     if (prseq != null && prname != null && prid != null)
     {
       // extract proteins.
@@ -663,9 +654,9 @@ public class EmblEntry
             protEMBLCDS = new DBRefEntry(pcdnaref);
             protEMBLCDS.setSource(DBRefSource.EMBLCDSProduct);
             product.addDBRef(protEMBLCDS);
-            
-          }     
-          
+
+          }
+
         }
       }
       // add cds feature to dna seq - this may include the stop codon
@@ -676,31 +667,25 @@ public class EmblEntry
         sf.setEnd(exon[xint + 1]);
         sf.setType(feature.getName());
         sf.setFeatureGroup(sourceDb);
-        sf.setDescription("Exon " + (1 + xint / 2)
-                + " for protein '" + prname + "' EMBLCDS:" + prid);
+        sf.setDescription("Exon " + (1 + xint / 2) + " for protein '"
+                + prname + "' EMBLCDS:" + prid);
         sf.setValue(FeatureProperties.EXONPOS, new Integer(1 + xint));
         sf.setValue(FeatureProperties.EXONPRODUCT, prname);
-        if (vals != null && vals.size() > 0)
+        if (vals != null)
         {
-          Enumeration kv = vals.keys();
-          while (kv.hasMoreElements())
+          for (Entry<String, String> val : vals.entrySet())
           {
-            Object key = kv.nextElement();
-            if (key != null)
-            {
-              sf.setValue(key.toString(), vals.get(key));
-            }
+            sf.setValue(val.getKey(), val.getValue());
           }
         }
         dna.addSequenceFeature(sf);
       }
     }
     // add dbRefs to sequence
-    if (feature.dbRefs != null && feature.dbRefs.size() > 0)
+    if (feature.dbRefs != null)
     {
-      for (Iterator dbr = feature.dbRefs.iterator(); dbr.hasNext();)
+      for (DBRefEntry ref : feature.dbRefs)
       {
-        DBRefEntry ref = (DBRefEntry) dbr.next();
         ref.setSource(jalview.util.DBRefUtils.getCanonicalName(ref
                 .getSource()));
         // Hard code the kind of protein product accessions that EMBL cite
@@ -755,7 +740,7 @@ public class EmblEntry
                   .setMap(new Mapping(product, map.getMap().getInverse()));
         }
         product.addDBRef(protEMBLCDS);
-          
+
         // Add converse mapping reference
         if (map != null)
         {
index 44c6cf6..b39ea94 100644 (file)
@@ -1,10 +1,10 @@
 package jalview.ext.varna;
 
-import fr.orsay.lri.varna.models.rna.RNA;
-
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceI;
 
+import fr.orsay.lri.varna.models.rna.RNA;
+
 /**
  * Data bean wrapping the data items that define one RNA view
  */
@@ -20,18 +20,13 @@ public class RnaModel
 
   public final RNA rna;
 
-  // path to a file holding VARNA session state XML
-  public final String varnaSession;
-
   public RnaModel(String t, AlignmentAnnotation aa, SequenceI s, RNA r,
-          boolean g,
-          String sessionFile)
+          boolean g)
   {
     title = t;
     ann = aa;
     seq = s;
-    gapped = g;
     rna = r;
-    varnaSession = sessionFile;
+    gapped = g;
   }
 }
\ No newline at end of file
index 40128af..49bb64a 100644 (file)
@@ -1,6 +1,7 @@
 package jalview.gui;
 
 import jalview.api.AlignExportSettingI;
+import jalview.bin.Jalview;
 import jalview.jbgui.GAlignExportSettings;
 
 import java.awt.event.ActionEvent;
@@ -23,7 +24,7 @@ public class AlignExportSettings extends GAlignExportSettings implements
           String alignFileFormat)
   {
     super(hasHiddenSeq, hasHiddenCols, alignFileFormat);
-    if (isShowDialog())
+    if (!Jalview.isHeadlessMode() && isShowDialog())
     {
       JOptionPane pane = new JOptionPane(null, JOptionPane.DEFAULT_OPTION,
               JOptionPane.DEFAULT_OPTION, null, new Object[]
index 343d5c7..68d7397 100644 (file)
@@ -37,6 +37,7 @@ import jalview.api.SplitContainerI;
 import jalview.api.ViewStyleI;
 import jalview.api.analysis.ScoreModelI;
 import jalview.bin.Cache;
+import jalview.bin.Jalview;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
@@ -335,7 +336,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    */
   void init()
   {
-    progressBar = new ProgressBar(this.statusPanel, this.statusBar);
+    if (!Jalview.isHeadlessMode())
+    {
+      progressBar = new ProgressBar(this.statusPanel, this.statusBar);
+    }
 
     avc = new jalview.controller.AlignViewController(this, viewport,
             alignPanel);
@@ -1135,8 +1139,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         warningMessage("Cannot save file " + fileName + " using format "
                 + format, "Alignment output format not supported");
-        saveAs_actionPerformed(null);
-        // JBPNote need to have a raise_gui flag here
+        if (!Jalview.isHeadlessMode())
+        {
+          saveAs_actionPerformed(null);
+        }
         return false;
       }
 
@@ -1822,9 +1828,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               viewport.getSelectionGroup().getEndRes() + groupAdjustment);
     }
 
+    /*
+     * just extend the last slide command if compatible; but not if in
+     * SplitFrame mode (to ensure all edits are broadcast - JAL-1802)
+     */
     boolean appendHistoryItem = false;
     Deque<CommandI> historyList = viewport.getHistoryList();
-    if (historyList != null
+    boolean inSplitFrame = getSplitViewContainer() != null;
+    if (!inSplitFrame && historyList != null
             && historyList.size() > 0
             && historyList.peek() instanceof SlideSequencesCommand)
     {
index c952c13..97752f2 100755 (executable)
@@ -139,7 +139,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   public AnnotationPanel(AlignmentPanel ap)
   {
 
-    MAC = new jalview.util.Platform().isAMac();
+    MAC = jalview.util.Platform.isAMac();
 
     ToolTipManager.sharedInstance().registerComponent(this);
     ToolTipManager.sharedInstance().setInitialDelay(0);
index beb4980..99bcff4 100644 (file)
  */
 package jalview.gui;
 
+import jalview.analysis.AlignSeq;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.RnaViewerModel;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.ext.varna.RnaModel;
+import jalview.structure.SecondaryStructureListener;
+import jalview.structure.SelectionListener;
+import jalview.structure.SelectionSource;
+import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasSource;
+import jalview.util.Comparison;
+import jalview.util.MessageManager;
+import jalview.util.ShiftList;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
-import java.lang.reflect.InvocationTargetException;
 import java.util.Collection;
 import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import javax.swing.JInternalFrame;
 import javax.swing.JSplitPane;
@@ -47,28 +60,12 @@ import fr.orsay.lri.varna.models.annotations.HighlightRegionAnnotation;
 import fr.orsay.lri.varna.models.rna.ModeleBase;
 import fr.orsay.lri.varna.models.rna.RNA;
 
-import jalview.analysis.AlignSeq;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.ColumnSelection;
-import jalview.datamodel.RnaViewerModel;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.ext.varna.RnaModel;
-import jalview.structure.SecondaryStructureListener;
-import jalview.structure.SelectionListener;
-import jalview.structure.SelectionSource;
-import jalview.structure.StructureSelectionManager;
-import jalview.structure.VamsasSource;
-import jalview.util.Comparison;
-import jalview.util.MessageManager;
-import jalview.util.ShiftList;
-
 public class AppVarna extends JInternalFrame implements SelectionListener,
         SecondaryStructureListener, InterfaceVARNASelectionListener,
         VamsasSource
 {
-  private static final Pattern PAIRS_PATTERN = Pattern
-          .compile("[^([{<>}])]");
+  private static final byte[] PAIRS = new byte[]
+  { '(', ')', '[', ']', '{', '}', '<', '>' };
 
   private AppVarnaBinding vab;
 
@@ -189,17 +186,16 @@ public class AppVarna extends JInternalFrame implements SelectionListener,
     setTitle(theTitle);
 
     String gappedTitle = sname + " (with gaps)";
-    RnaModel gappedModel = new RnaModel(gappedTitle, aa, seq, null, true,
-            null);
+    RnaModel gappedModel = new RnaModel(gappedTitle, aa, seq, null, true);
     addModel(gappedModel, gappedTitle);
 
     String trimmedTitle = "trimmed " + sname;
-    RnaModel trimmedModel = new RnaModel(trimmedTitle, aa, seq, null,
-            false, null);
+    RnaModel trimmedModel = new RnaModel(trimmedTitle, aa, seq, null, false);
     addModel(trimmedModel, trimmedTitle);
     vab.setSelectedIndex(0);
   }
 
+
   /**
    * Constructor that links the viewer to a parent panel (but has no structures
    * yet - use addModel to add them)
@@ -262,13 +258,6 @@ public class AppVarna extends JInternalFrame implements SelectionListener,
     showPanel(true);
   }
 
-  public String replaceOddGaps(String oldStr)
-  {
-    Matcher matcher = PAIRS_PATTERN.matcher(oldStr);
-    String newStr = matcher.replaceAll(".");
-    return newStr;
-  }
-
   /**
    * Constructs a new RNA model from the given one, without gaps. Also
    * calculates and saves a 'shift list'
@@ -284,7 +273,8 @@ public class AppVarna extends JInternalFrame implements SelectionListener,
     RNA rnaTrim = new RNA(name);
     try
     {
-      rnaTrim.setRNA(rna.getSeq(), replaceOddGaps(rna.getStructDBN()));
+      String structDBN = rna.getStructDBN(true);
+      rnaTrim.setRNA(rna.getSeq(), replaceOddGaps(structDBN));
     } catch (ExceptionUnmatchedClosingParentheses e2)
     {
       e2.printStackTrace();
@@ -295,7 +285,7 @@ public class AppVarna extends JInternalFrame implements SelectionListener,
 
     String seq = rnaTrim.getSeq();
     StringBuilder struc = new StringBuilder(256);
-    struc.append(rnaTrim.getStructDBN());
+    struc.append(rnaTrim.getStructDBN(true));
     int ofstart = -1;
     int sleng = seq.length();
 
@@ -308,7 +298,7 @@ public class AppVarna extends JInternalFrame implements SelectionListener,
           ofstart = i;
         }
         /*
-         * mark base or base pair in the structure with *
+         * mark base or base & pair in the structure with *
          */
         if (!rnaTrim.findPair(i).isEmpty())
         {
@@ -405,14 +395,6 @@ public class AppVarna extends JInternalFrame implements SelectionListener,
     if (rnaModel.seq == sequence)
     {
       int highlightPos = rnaModel.gapped ? index : position - 1;
-      // int highlightPos = index;
-      // ShiftList shift = offsets.get(rna);
-      // if (shift != null)
-      // {
-      // System.err.print("Orig pos:" + index);
-      // highlightPos = shift.shift(index);
-      // System.err.println("\nFinal pos:" + index);
-      // }
       mouseOverHighlighter.highlightRegion(rna, highlightPos, highlightPos);
       vab.updateSelectedRNA(rna);
     }
@@ -600,34 +582,6 @@ public class AppVarna extends JInternalFrame implements SelectionListener,
     }
 
     /*
-     * loaded from project file with Varna session data
-     */
-    if (model.varnaSession != null)
-    {
-      try
-      {
-        FullBackup fromSession = vab.vp.loadSession(model.varnaSession);
-        vab.addStructure(fromSession.rna, fromSession.config);
-        RNA rna = fromSession.rna;
-        // copy the model, but now including the RNA object
-        RnaModel newModel = new RnaModel(model.title, model.ann, model.seq,
-                rna, model.gapped, model.varnaSession);
-        if (!model.gapped)
-        {
-          registerOffset(rna, buildOffset(model.seq));
-        }
-        models.put(rna, newModel);
-        // capture rna selection state when saved
-        selectionHighlighter = new VarnaHighlighter(rna);
-        return fromSession.rna;
-      } catch (ExceptionLoadingFailed e)
-      {
-        e.printStackTrace();
-        return null;
-      }
-    }
-
-    /*
      * opened on request in Jalview session
      */
     RNA rna = new RNA(modelName);
@@ -651,7 +605,7 @@ public class AppVarna extends JInternalFrame implements SelectionListener,
       rna = trimRNA(rna, modelName);
     }
     models.put(rna, new RnaModel(modelName, model.ann, model.seq, rna,
-            model.gapped, null));
+            model.gapped));
     vab.addStructure(rna);
     return rna;
   }
@@ -701,10 +655,102 @@ public class AppVarna extends JInternalFrame implements SelectionListener,
   /**
    * Set the selected index in the model selection list
    * 
-   * @param selectedR
+   * @param selectedIndex
    */
   public void setInitialSelection(final int selectedIndex)
   {
+    /*
+     * empirically it needs a second for Varna/AWT to finish loading/drawing
+     * models for this to work; SwingUtilities.invokeLater _not_ a solution;
+     * explanation and/or better solution welcome!
+     */
+    synchronized (this)
+    {
+      try
+      {
+        wait(1000);
+      } catch (InterruptedException e)
+      {
+        // meh
+      }
+    }
     vab.setSelectedIndex(selectedIndex);
   }
+
+
+  /**
+   * Add a model with associated Varna session file
+   * 
+   * @param rna
+   * @param modelName
+   */
+  public RNA addModelSession(RnaModel model, String modelName,
+          String sessionFile)
+  {
+    if (!model.ann.isValidStruc())
+    {
+      throw new IllegalArgumentException("Invalid RNA structure annotation");
+    }
+
+    try
+    {
+      FullBackup fromSession = vab.vp.loadSession(sessionFile);
+      vab.addStructure(fromSession.rna, fromSession.config);
+      RNA rna = fromSession.rna;
+      // copy the model, but now including the RNA object
+      RnaModel newModel = new RnaModel(model.title, model.ann, model.seq,
+              rna, model.gapped);
+      if (!model.gapped)
+      {
+        registerOffset(rna, buildOffset(model.seq));
+      }
+      models.put(rna, newModel);
+      // capture rna selection state when saved
+      selectionHighlighter = new VarnaHighlighter(rna);
+      return fromSession.rna;
+    } catch (ExceptionLoadingFailed e)
+    {
+      System.err
+              .println("Error restoring Varna session: " + e.getMessage());
+      return null;
+    }
+  }
+
+
+  /**
+   * Replace everything except RNA secondary structure characters with a period
+   * 
+   * @param s
+   * @return
+   */
+  public static String replaceOddGaps(String s)
+  {
+    if (s == null)
+    {
+      return null;
+    }
+
+    // this is measured to be 10 times faster than a regex replace
+    boolean changed = false;
+    byte[] bytes = s.getBytes();
+    for (int i = 0; i < bytes.length; i++)
+    {
+      boolean ok = false;
+      // todo check for ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) if
+      // wanted also
+      for (int j = 0; !ok && (j < PAIRS.length); j++)
+      {
+        if (bytes[i] == PAIRS[j])
+        {
+          ok = true;
+        }
+      }
+      if (!ok)
+      {
+        bytes[i] = '.';
+        changed = true;
+      }
+    }
+    return changed ? new String(bytes) : s;
+  }
 }
index 956c2e1..25af7d1 100644 (file)
  */
 package jalview.gui;
 
+import jalview.datamodel.SequenceI;
+import jalview.ext.varna.JalviewVarnaBinding;
+import jalview.structure.AtomSpec;
+import jalview.util.MessageManager;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -59,38 +64,10 @@ import fr.orsay.lri.varna.models.FullBackup;
 import fr.orsay.lri.varna.models.VARNAConfig;
 import fr.orsay.lri.varna.models.rna.RNA;
 
-import jalview.datamodel.SequenceI;
-import jalview.ext.varna.JalviewVarnaBinding;
-import jalview.structure.AtomSpec;
-import jalview.util.MessageManager;
-
 public class AppVarnaBinding extends JalviewVarnaBinding
 {
   public VARNAPanel vp;
 
-  // remove unused (commented out) fields?
-  // protected JPanel _tools = new JPanel();
-  //
-  // private JPanel _input = new JPanel();
-  //
-  // private JPanel _strPanel = new JPanel();
-  //
-  // private JTextField _str = new JTextField();
-  //
-  // private JTextField _seq = new JTextField();
-  //
-  // private JLabel _strLabel = new JLabel(
-  // MessageManager.getString("label.str"));
-  //
-  // private JButton _updateButton = new JButton(
-  // MessageManager.getString("action.update"));
-  //
-  // private JButton _deleteButton = new JButton(
-  // MessageManager.getString("action.delete"));
-  //
-  // private JButton _duplicateButton = new JButton(
-  // MessageManager.getString("action.snapshot"));
-  //
   protected JPanel _listPanel = new JPanel();
 
   private ReorderableJList _sideList = null;
@@ -165,48 +142,6 @@ public class AppVarnaBinding extends JalviewVarnaBinding
 
     vp.setBackground(_backgroundColor);
 
-    // MC commented out stuff not added to panel - remove?
-    // Font textFieldsFont = Font.decode("MonoSpaced-PLAIN-12");
-    //
-    // _seq.setFont(textFieldsFont);
-    // if (!rnaList.isEmpty())
-    // {
-    // _seq.setText(rnaList.get(0).getSeq());
-    // }
-
-    // _updateButton.addActionListener(new ActionListener()
-    // {
-    // public void actionPerformed(ActionEvent e)
-    // {
-    // FullBackup sel = (FullBackup) _sideList.getSelectedValue();
-    // sel.rna.setSequence("A");
-    // }
-    // });
-    //
-    // _strLabel.setPreferredSize(new Dimension(marginTools, 15));
-    // _strLabel.setHorizontalTextPosition(JLabel.LEFT);
-    // _str.setFont(textFieldsFont);
-    // _strPanel.setLayout(new BorderLayout());
-    // _strPanel.add(_strLabel, BorderLayout.WEST);
-    // _strPanel.add(_str, BorderLayout.CENTER);
-    //
-    // _input.setLayout(new GridLayout(1, 0));
-    // _input.add(_strPanel);
-    //
-    // JPanel goPanel = new JPanel();
-    // goPanel.setLayout(new BorderLayout());
-    //
-    // _tools.setLayout(new BorderLayout());
-    // _tools.add(_input, BorderLayout.CENTER);
-    // _tools.add(goPanel, BorderLayout.EAST);
-    //
-    // goPanel.add(_updateButton, BorderLayout.CENTER);
-    //
-    // JPanel ops = new JPanel();
-    // ops.setLayout(new GridLayout(1, 2));
-    // ops.add(_deleteButton);
-    // ops.add(_duplicateButton);
-
     JLabel j = new JLabel(
             MessageManager.getString("label.structures_manager"),
             JLabel.CENTER);
index e663cf8..979d1aa 100644 (file)
@@ -195,7 +195,13 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
    */
   public void ok_actionPerformed(ActionEvent e)
   {
-    String format = new IdentifyFile().Identify(getText(), "Paste");
+    String text = getText();
+    if (text.trim().length() < 1)
+    {
+      return;
+    }
+
+    String format = new IdentifyFile().Identify(text, "Paste");
     // TODO: identify feature, annotation or tree file and parse appropriately.
     AlignmentI al = null;
 
index 2434b00..be56292 100644 (file)
@@ -3344,9 +3344,8 @@ public class Jalview2XML
           String sessionState = ss.getViewerState();
           String tempStateFile = copyJarEntry(jprovider, sessionState,
                   "varna");
-          RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped,
-                  tempStateFile);
-          appVarna.addModel(rna, rnaTitle);
+          RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped);
+          appVarna.addModelSession(rna, rnaTitle, tempStateFile);
         }
         appVarna.setInitialSelection(viewer.getSelectedRna());
       }
@@ -3807,23 +3806,16 @@ public class Jalview2XML
       newFileLoc.append(";");
     }
 
-    if (newFileLoc.length() == 0)
+    if (newFileLoc.length() > 0)
     {
-      return;
-    }
-    int histbug = newFileLoc.indexOf("history = ");
-    if (histbug > -1)
-    {
-      /*
-       * change "history = [true|false];" to "history = [1|0];"
-       */
+      int histbug = newFileLoc.indexOf("history = ");
       histbug += 10;
       int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";", histbug);
       String val = (diff == -1) ? null : newFileLoc
               .substring(histbug, diff);
       if (val != null && val.length() >= 4)
       {
-        if (val.contains("e")) // eh? what can it be?
+        if (val.contains("e"))
         {
           if (val.trim().equals("true"))
           {
@@ -3836,53 +3828,53 @@ public class Jalview2XML
           newFileLoc.replace(histbug, diff, val);
         }
       }
-    }
 
-    final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
-            .size()]);
-    final String[] id = pdbids.toArray(new String[pdbids.size()]);
-    final SequenceI[][] sq = seqmaps
-            .toArray(new SequenceI[seqmaps.size()][]);
-    final String fileloc = newFileLoc.toString();
-    final String sviewid = viewerData.getKey();
-    final AlignFrame alf = af;
-    final Rectangle rect = new Rectangle(svattrib.getX(), svattrib.getY(),
-            svattrib.getWidth(), svattrib.getHeight());
-    try
-    {
-      javax.swing.SwingUtilities.invokeAndWait(new Runnable()
+      final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
+              .size()]);
+      final String[] id = pdbids.toArray(new String[pdbids.size()]);
+      final SequenceI[][] sq = seqmaps
+              .toArray(new SequenceI[seqmaps.size()][]);
+      final String fileloc = newFileLoc.toString();
+      final String sviewid = viewerData.getKey();
+      final AlignFrame alf = af;
+      final Rectangle rect = new Rectangle(svattrib.getX(),
+              svattrib.getY(), svattrib.getWidth(), svattrib.getHeight());
+      try
       {
-        @Override
-        public void run()
+        javax.swing.SwingUtilities.invokeAndWait(new Runnable()
         {
-          JalviewStructureDisplayI sview = null;
-          try
-          {
-            sview = new StructureViewer(alf.alignPanel
-                    .getStructureSelectionManager()).createView(
-                    StructureViewer.ViewerType.JMOL, pdbf, id, sq,
-                    alf.alignPanel, svattrib, fileloc, rect, sviewid);
-            addNewStructureViewer(sview);
-          } catch (OutOfMemoryError ex)
+          @Override
+          public void run()
           {
-            new OOMWarning("restoring structure view for PDB id " + id,
-                    (OutOfMemoryError) ex.getCause());
-            if (sview != null && sview.isVisible())
+            JalviewStructureDisplayI sview = null;
+            try
+            {
+              sview = new StructureViewer(alf.alignPanel
+                      .getStructureSelectionManager()).createView(
+                      StructureViewer.ViewerType.JMOL, pdbf, id, sq,
+                      alf.alignPanel, svattrib, fileloc, rect, sviewid);
+              addNewStructureViewer(sview);
+            } catch (OutOfMemoryError ex)
             {
-              sview.closeViewer(false);
-              sview.setVisible(false);
-              sview.dispose();
+              new OOMWarning("restoring structure view for PDB id " + id,
+                      (OutOfMemoryError) ex.getCause());
+              if (sview != null && sview.isVisible())
+              {
+                sview.closeViewer(false);
+                sview.setVisible(false);
+                sview.dispose();
+              }
             }
           }
-        }
-      });
-    } catch (InvocationTargetException ex)
-    {
-      warn("Unexpected error when opening Jmol view.", ex);
+        });
+      } catch (InvocationTargetException ex)
+      {
+        warn("Unexpected error when opening Jmol view.", ex);
 
-    } catch (InterruptedException e)
-    {
-      // e.printStackTrace();
+      } catch (InterruptedException e)
+      {
+        // e.printStackTrace();
+      }
     }
   }
 
index 5307f26..c7db535 100644 (file)
@@ -1,5 +1,7 @@
 package jalview.gui;
 
+import jalview.util.MessageManager;
+
 import java.awt.BorderLayout;
 import java.awt.Component;
 import java.awt.GridLayout;
@@ -14,8 +16,6 @@ import javax.swing.JPanel;
 import javax.swing.JProgressBar;
 import javax.swing.SwingUtilities;
 
-import jalview.util.MessageManager;
-
 /**
  * A class to manage multiple progress bars embedded in a JPanel.
  */
index 089f50b..73be79f 100644 (file)
@@ -96,7 +96,6 @@ public class BioJsHTMLOutput
             { "HTML files" }, "HTML files");
     jvFileChooser.setFileView(new JalviewFileView());
 
-    // TODO uncomment when supported by MassageManager
     jvFileChooser.setDialogTitle(MessageManager
             .getString("label.save_as_biojs_html"));
     jvFileChooser.setDialogTitle("save as BioJs HTML");
@@ -201,10 +200,13 @@ public class BioJsHTMLOutput
         try
         {
           String gitRepoPkgJson = getURLContentAsString(BJS_TEMPLATE_GIT_REPO);
-          BioJSRepositoryPojo release = new BioJSRepositoryPojo(
-                  gitRepoPkgJson);
-          syncUpdates(BJS_TEMPLATES_LOCAL_DIRECTORY, release);
-          refreshBioJSVersionsInfo(BJS_TEMPLATES_LOCAL_DIRECTORY);
+          if (gitRepoPkgJson != null)
+          {
+            BioJSRepositoryPojo release = new BioJSRepositoryPojo(
+                    gitRepoPkgJson);
+            syncUpdates(BJS_TEMPLATES_LOCAL_DIRECTORY, release);
+            refreshBioJSVersionsInfo(BJS_TEMPLATES_LOCAL_DIRECTORY);
+          }
         } catch (URISyntaxException e)
         {
           e.printStackTrace();
index 18ab642..3aded05 100755 (executable)
@@ -22,6 +22,7 @@ package jalview.io;
 
 import jalview.api.ComplexAlignFile;
 import jalview.api.FeaturesDisplayedI;
+import jalview.bin.Jalview;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
@@ -257,6 +258,21 @@ public class FileLoader implements Runnable
         {
           format = new IdentifyFile().Identify(file, protocol);
         }
+
+      }
+
+      if (format == null || format.equalsIgnoreCase("EMPTY DATA FILE"))
+      {
+        Desktop.instance.stopLoading();
+        System.err.println("The input file \"" + file
+                + "\" has no data content!");
+        if (!Jalview.isHeadlessMode())
+        {
+          JOptionPane.showMessageDialog(null, "The input file \""
+                  + file + "\" has no data content!", "Empty data file",
+                  JOptionPane.ERROR_MESSAGE);
+        }
+        return;
       }
       // TODO: cache any stream datasources as a temporary file (eg. PDBs
       // retrieved via URL)
index 27f08ae..4c83ac1 100755 (executable)
@@ -98,7 +98,7 @@ public class IdentifyFile
       }
       while ((data = source.nextLine()) != null)
       {
-        length += data.length();
+        length += data.trim().length();
         if (!lineswereskipped)
         {
           for (int i = 0; !isBinary && i < data.length(); i++)
index befa3b1..c6a042b 100755 (executable)
  */
 package jalview.jbgui;
 
+import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
+import jalview.api.SplitContainerI;
+import jalview.bin.Cache;
+import jalview.gui.JvSwingUtils;
+import jalview.gui.Preferences;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.util.MessageManager;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.GridLayout;
@@ -52,14 +60,6 @@ import javax.swing.event.ChangeEvent;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
-import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
-import jalview.api.SplitContainerI;
-import jalview.bin.Cache;
-import jalview.gui.JvSwingUtils;
-import jalview.gui.Preferences;
-import jalview.schemes.ColourSchemeProperty;
-import jalview.util.MessageManager;
-
 public class GAlignFrame extends JInternalFrame
 {
   protected JMenuBar alignFrameMenuBar = new JMenuBar();
@@ -262,7 +262,7 @@ public class GAlignFrame extends JInternalFrame
       System.err.println(e.toString());
     }
 
-    if (!new jalview.util.Platform().isAMac())
+    if (!jalview.util.Platform.isAMac())
     {
       closeMenuItem.setMnemonic('C');
       outputTextboxMenu.setMnemonic('T');
index 96e27e6..4ec649a 100644 (file)
@@ -51,15 +51,15 @@ public class PDBDocFieldPreferences extends JScrollPane
     {
     case SEARCH_SUMMARY:
       columnNames = new String[]
-      { "PDB Feild", "Show in search summary" };
+      { "PDB Field", "Show in search summary" };
       break;
     case STRUCTURE_CHOOSER:
       columnNames = new String[]
-      { "PDB Feild", "Show in structure summary" };
+      { "PDB Field", "Show in structure summary" };
       break;
     case PREFERENCES:
       columnNames = new String[]
-      { "PDB Feild", "Show in search summary", "Show in structure summary" };
+      { "PDB Field", "Show in search summary", "Show in structure summary" };
       break;
     default:
       break;
index 4028a9d..1e6ef7e 100644 (file)
@@ -3,6 +3,7 @@ package jalview.json.binding.v1;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.Objects;
 
 import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
@@ -36,6 +37,8 @@ public class BioJSRepositoryPojo
   @SuppressWarnings("unchecked")
   private void parse(String jsonString) throws ParseException
   {
+    Objects.requireNonNull(jsonString,
+            "Supplied jsonString must not be null");
     JSONParser jsonParser = new JSONParser();
     JSONObject JsonObj = (JSONObject) jsonParser.parse(jsonString);
     this.description = (String) JsonObj.get("description");
index 2ba7aff..3359678 100644 (file)
@@ -132,7 +132,7 @@ public class AnnotationRenderer
 
   private FontMetrics fm;
 
-  private final boolean MAC = new jalview.util.Platform().isAMac();
+  private final boolean MAC = jalview.util.Platform.isAMac();
 
   boolean av_renderHistogram = true, av_renderProfile = true,
           av_normaliseProfile = false;
index 100ecf5..8abe965 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.util;
 
+import jalview.bin.Jalview;
 import jalview.gui.EPSOptions;
 import jalview.gui.SVGOptions;
 import jalview.io.JalviewFileChooser;
@@ -259,6 +260,10 @@ public class ImageMaker
 
   static JalviewFileChooser getPNGChooser()
   {
+    if (Jalview.isHeadlessMode())
+  {
+      return null;
+    }
     return new jalview.io.JalviewFileChooser(
             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
             { "png" }, new String[]
@@ -267,6 +272,10 @@ public class ImageMaker
 
   static JalviewFileChooser getEPSChooser()
   {
+    if (Jalview.isHeadlessMode())
+    {
+      return null;
+    }
     return new jalview.io.JalviewFileChooser(
             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
             { "eps" }, new String[]
@@ -275,6 +284,10 @@ public class ImageMaker
 
   static JalviewFileChooser getSVGChooser()
   {
+    if (Jalview.isHeadlessMode())
+  {
+      return null;
+    }
     return new jalview.io.JalviewFileChooser(
             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
             { "svg" }, new String[]
index 1325ce5..c55d467 100644 (file)
@@ -66,7 +66,7 @@ public class StringUtils
    */
   public static final char[] deleteChars(char[] in, int from, int to)
   {
-    if (from >= in.length)
+    if (from >= in.length || from < 0)
     {
       return in;
     }
index 67ab3f2..09c10ef 100644 (file)
@@ -269,27 +269,27 @@ public class PDBRestClient
    * table. The PDB Id serves as a unique identifier for a given row in the
    * summary table
    * 
-   * @param wantedFeilds
+   * @param wantedFields
    *          the available table columns in no particular order
    * @return the pdb id field column index
    */
   public static int getPDBIdColumIndex(
-          Collection<PDBDocField> wantedFeilds, boolean hasRefSeq)
+          Collection<PDBDocField> wantedFields, boolean hasRefSeq)
   {
 
     // If a reference sequence is attached then start counting from 1 else
     // start from zero
-    int pdbFeildIndexCounter = hasRefSeq ? 1 : 0;
+    int pdbFieldIndexCounter = hasRefSeq ? 1 : 0;
 
-    for (PDBDocField feild : wantedFeilds)
+    for (PDBDocField field : wantedFields)
     {
-      if (feild.equals(PDBDocField.PDB_ID))
+      if (field.equals(PDBDocField.PDB_ID))
       {
         break; // Once PDB Id index is determined exit iteration
       }
-      ++pdbFeildIndexCounter;
+      ++pdbFieldIndexCounter;
     }
-    return pdbFeildIndexCounter;
+    return pdbFieldIndexCounter;
   }
 
   /**
index 186ac03..2e6551c 100644 (file)
@@ -12,7 +12,7 @@ public class AtomTest
    * Test the constructor that parses a PDB file format ATOM line. Fields are in
    * fixed column positions
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testStringConstructor()
   {
     Atom a = new Atom(
@@ -35,7 +35,7 @@ public class AtomTest
    * Test the case where occupancy and temp factor are blank - should default to
    * 1
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testStringConstructor_blankOccupancyTempFactor()
   {
     Atom a = new Atom(
@@ -47,7 +47,7 @@ public class AtomTest
   /**
    * Parsing non-numeric data as Atom throws an exception
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testStringConstructor_malformed()
   {
     try
index 6f701e2..4bdfff7 100644 (file)
@@ -7,7 +7,7 @@ import org.testng.annotations.Test;
 public class BondTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testTranslate()
   {
     Atom a1 = new Atom(1f, 2f, 3f);
index 07f9bd5..da49f22 100644 (file)
@@ -38,7 +38,7 @@ public class PDBChainTest
     c = new PDBChain("1GAQ", "A");
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetNewlineString()
   {
     assertEquals(System.lineSeparator(), c.getNewlineString());
@@ -46,7 +46,7 @@ public class PDBChainTest
     assertEquals("gaga", c.getNewlineString());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testPrint()
   {
     c.offset = 7;
@@ -74,7 +74,7 @@ public class PDBChainTest
    * Test the method that constructs a Bond between two atoms and adds it to the
    * chain's list of bonds
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMakeBond()
   {
     /*
@@ -109,7 +109,7 @@ public class PDBChainTest
     assertEquals(3f, b2.end[2], 0.0001f);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSetChainColours_colour()
   {
     c.makeBond(a1, a2);
@@ -126,7 +126,7 @@ public class PDBChainTest
    * Test setting bond start/end colours based on a colour scheme i.e. colour by
    * residue
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSetChainColours_colourScheme()
   {
     Color alaColour = new Color(204, 255, 0);
@@ -154,7 +154,7 @@ public class PDBChainTest
     assertEquals(Color.gray, b.endCol);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetChargeColour()
   {
     assertEquals(Color.red, PDBChain.getChargeColour("ASP"));
@@ -169,7 +169,7 @@ public class PDBChainTest
   /**
    * Test the method that sets bond start/end colours by residue charge property
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSetChargeColours()
   {
     a1.resName = "ASP"; // red
@@ -198,7 +198,7 @@ public class PDBChainTest
   /**
    * Test the method that converts the raw list of atoms to a list of residues
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMakeResidueList_noAnnotation()
   {
     Vector<Atom> atoms = new Vector<Atom>();
@@ -256,7 +256,7 @@ public class PDBChainTest
    * Test the method that converts the raw list of atoms to a list of residues,
    * including parsing of tempFactor to an alignment annotation
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMakeResidueList_withTempFactor()
   {
     Vector<Atom> atoms = new Vector<Atom>();
@@ -307,7 +307,7 @@ public class PDBChainTest
    * Test the method that constructs bonds between successive residues' CA or P
    * atoms
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMakeCaBondList()
   {
     c.isNa = true;
@@ -338,7 +338,7 @@ public class PDBChainTest
     assertTrue(c.isNa);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMakeCaBondList_nucleotide()
   {
     c.isNa = false;
@@ -370,7 +370,7 @@ public class PDBChainTest
   /**
    * Test the method that updates atoms with their alignment positions
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMakeExactMapping()
   {
     Vector<Atom> atoms = new Vector<Atom>();
index 03b60e6..1dfeba9 100644 (file)
@@ -21,7 +21,7 @@ import org.testng.annotations.Test;
 
 public class PDBfileTest
 {
-  @Test
+  @Test(groups ={ "Functional" })
   public void testIsRna()
   {
     SequenceI seq = new Sequence("Seq1", "CGAU");
@@ -43,7 +43,7 @@ public class PDBfileTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testParse() throws IOException
   {
     /*
@@ -120,7 +120,7 @@ public class PDBfileTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testParse_withAnnotations_noSS() throws IOException
   {
     PDBfile pf = new PDBfile(true, false, false, "examples/3W5V.pdb",
@@ -178,7 +178,7 @@ public class PDBfileTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testParse_withJmol_noAnnotations() throws IOException
   {
     PDBfile pf = new PDBfile(false, true, false, "examples/3W5V.pdb",
@@ -207,7 +207,7 @@ public class PDBfileTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testParse_withJmolAddAlignmentAnnotations()
           throws IOException
   {
@@ -264,7 +264,8 @@ public class PDBfileTest
    * @throws IOException
    */
 
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void testParse_withAnnotate3D() throws IOException
   {
     // TODO requires a mock for Annotate3D processing
index b5ca0ed..bb7f796 100644 (file)
@@ -10,7 +10,7 @@ import org.testng.annotations.Test;
 public class ResidueTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindAtom()
   {
     Atom a1 = new Atom(1f, 2f, 3f);
index ea07aa2..d91119d 100644 (file)
@@ -5,6 +5,7 @@ import static org.testng.AssertJUnit.assertEquals;
 import java.io.IOException;
 import java.io.StringWriter;
 
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 /**
@@ -25,6 +26,9 @@ public class RegexWriterTest
    *          an input string
    * @throws Exception
    */
+
+  @Test(groups =
+  { "Functional" }, dataProvider = "testWriteParam")
   void test(String re, String inp) throws IOException
   {
     StringWriter sw = new StringWriter();
@@ -47,27 +51,50 @@ public class RegexWriterTest
     }
   }
 
-  @Test
-  public void testWrite() throws IOException
+  // @Test(groups ={ "Functional" })
+  // public void testWrite() throws IOException
+  // {
+  // for (int n = 1; n <= 1; n++)
+  // {
+  // test("s/x/y/", "-----x123456789");
+  // test("s/x/y/", "x123456789");
+  // test("s/x/y/", "-----x");
+  // test("s/x.*?x/y/", ".xx..x..x...x...x....x....x");
+  // test("s/x.*x/[$&]/", "--x........x--xx");
+  // test("s/x.*x/[$&]/", "--x........x------");
+  // test("s/.$/a/m", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbbbbbbbbbbbb");
+  // test("s/.$/a/", "123");
+  // test("s/.$/a/", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbb");
+  // test("s/^./a/", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbb");
+  // test("s/$/a/", "bbb");
+  // test("s/^/a/", "bbb");
+  // test("s/^/a/", "");
+  // test("s{.*}{N}", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+  // test("s/.{0,7}/y/", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+  // test("s/x/$&/", "xxx");
+  // }
+  // }
+
+  @DataProvider(name = "testWriteParam")
+  public Object[][] regexTestParams()
   {
-    for (int n = 1; n <= 1; n++)
+    return new Object[][]
     {
-      test("s/x/y/", "-----x123456789");
-      test("s/x/y/", "x123456789");
-      test("s/x/y/", "-----x");
-      test("s/x.*?x/y/", ".xx..x..x...x...x....x....x");
-      test("s/x.*x/[$&]/", "--x........x--xx");
-      test("s/x.*x/[$&]/", "--x........x------");
-      test("s/.$/a/m", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbbbbbbbbbbbb");
-      test("s/.$/a/", "123");
-      test("s/.$/a/", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbb");
-      test("s/^./a/", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbb");
-      test("s/$/a/", "bbb");
-      test("s/^/a/", "bbb");
-      test("s/^/a/", "");
-      test("s{.*}{N}", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
-      test("s/.{0,7}/y/", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
-      test("s/x/$&/", "xxx");
-    }
+    { "s/x/y/", "-----x123456789" },
+    { "s/x/y/", "x123456789" },
+    { "s/x/y/", "-----x" },
+    { "s/x.*?x/y/", ".xx..x..x...x...x....x....x" },
+    { "s/x.*x/[$&]/", "--x........x--xx" },
+    { "s/x.*x/[$&]/", "--x........x------" },
+    { "s/.$/a/m", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbbbbbbbbbbbb" },
+    { "s/.$/a/", "123" },
+    { "s/.$/a/", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbb" },
+    { "s/^./a/", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbb" },
+    { "s/$/a/", "bbb" },
+    { "s/^/a/", "bbb" },
+    { "s/^/a/", "" },
+    { "s{.*}{N}", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" },
+    { "s/.{0,7}/y/", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" },
+    { "s/x/$&/", "xxx" } };
   }
 }
index eb496bb..77d592a 100644 (file)
@@ -22,7 +22,7 @@ public class AAFrequencyTest
 
   private static final String P = AAFrequency.PROFILE;
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCalculate_noProfile()
   {
     SequenceI seq1 = new Sequence("Seq1", "CAGT");
@@ -65,7 +65,7 @@ public class AAFrequencyTest
     assertEquals("T", col.get(R));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCalculate_withProfile()
   {
     SequenceI seq1 = new Sequence("Seq1", "CAGT");
@@ -100,7 +100,7 @@ public class AAFrequencyTest
     assertEquals(4, profile[1][1]);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCalculate_withProfileTiming()
   {
     SequenceI seq1 = new Sequence("Seq1", "CAGT");
@@ -122,7 +122,7 @@ public class AAFrequencyTest
     System.out.println(System.currentTimeMillis() - start);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetPercentageFormat()
   {
     assertNull(AAFrequency.getPercentageFormat(0));
index ed686a7..34ae612 100644 (file)
@@ -3,11 +3,11 @@ package jalview.analysis;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
-import org.junit.Test;
+import org.testng.annotations.Test;
 
 public class AlignSeqTest
 {
-  @Test
+  @Test(groups ={ "Functional" })
   public void testExtractGaps()
   {
     assertNull(AlignSeq.extractGaps(null, null));
index 606a840..7ba2035 100644 (file)
@@ -45,7 +45,7 @@ public class AlignmentAnnotationUtilsTest
   /**
    * Test method that converts a (possibly null) array to a list.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAsList()
   {
     // null array
@@ -143,7 +143,7 @@ public class AlignmentAnnotationUtilsTest
   /**
    * Test a mixture of show/hidden annotations in/outside selection group.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetShownHiddenTypes_forSelectionGroup()
   {
     Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
@@ -222,7 +222,7 @@ public class AlignmentAnnotationUtilsTest
    * Test case where there are 'grouped' annotations, visible and hidden, within
    * and without the selection group.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetShownHiddenTypes_withGraphGroups()
   {
     final int GROUP_3 = 3;
@@ -348,7 +348,7 @@ public class AlignmentAnnotationUtilsTest
   /**
    * Test method that determines visible graph groups.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetVisibleGraphGroups()
   {
     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
@@ -395,7 +395,7 @@ public class AlignmentAnnotationUtilsTest
    * Test for case where no sequence is selected. Shouldn't normally arise but
    * check it handles it gracefully.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetShownHiddenTypes_noSequenceSelected()
   {
     Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
index d44a6bc..61b2487 100644 (file)
@@ -96,7 +96,7 @@ public class AlignmentUtilsTests
   public static Sequence ts = new Sequence("short",
           "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm");
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testExpandContext()
   {
     AlignmentI al = new Alignment(new Sequence[] {});
@@ -150,7 +150,7 @@ public class AlignmentUtilsTests
   /**
    * Test that annotations are correctly adjusted by expandContext
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testExpandContext_annotation()
   {
     AlignmentI al = new Alignment(new Sequence[]
@@ -240,7 +240,7 @@ public class AlignmentUtilsTests
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetSequencesByName() throws IOException
   {
     final String data = ">Seq1Name\nKQYL\n" + ">Seq2Name\nRFPW\n"
@@ -278,7 +278,7 @@ public class AlignmentUtilsTests
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMapProteinToCdna_noXrefs() throws IOException
   {
     List<SequenceI> protseqs = new ArrayList<SequenceI>();
@@ -341,7 +341,7 @@ public class AlignmentUtilsTests
   /**
    * Test for the alignSequenceAs method that takes two sequences and a mapping.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAlignSequenceAs_withMapping_noIntrons()
   {
     MapList map = new MapList(new int[]
@@ -387,7 +387,7 @@ public class AlignmentUtilsTests
   /**
    * Test for the alignSequenceAs method that takes two sequences and a mapping.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAlignSequenceAs_withMapping_withIntrons()
   {
     /*
@@ -437,7 +437,7 @@ public class AlignmentUtilsTests
   /**
    * Test for the case where not all of the protein sequence is mapped to cDNA.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAlignSequenceAs_withMapping_withUnmappedProtein()
   {
     
@@ -487,7 +487,7 @@ public class AlignmentUtilsTests
   /**
    * Test for the alignSequenceAs method where we preserve gaps in introns only.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAlignSequenceAs_keepIntronGapsOnly()
   {
 
@@ -506,7 +506,7 @@ public class AlignmentUtilsTests
    * Test for the method that generates an aligned translated sequence from one
    * mapping.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetAlignedTranslation_dnaLikeProtein()
   {
     // dna alignment will be replaced
@@ -530,7 +530,7 @@ public class AlignmentUtilsTests
   /**
    * Test the method that realigns protein to match mapped codon alignment.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAlignProteinAsDna()
   {
     // seq1 codons are [1,2,3] [4,5,6] [7,8,9] [10,11,12]
@@ -574,7 +574,7 @@ public class AlignmentUtilsTests
    * Test the method that tests whether a CDNA sequence translates to a protein
    * sequence
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testTranslatesAs()
   {
     assertTrue(AlignmentUtils.translatesAs("tttcccaaaggg".toCharArray(), 0,
@@ -613,7 +613,7 @@ public class AlignmentUtilsTests
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMapProteinToCdna_withStartAndStopCodons()
           throws IOException
   {
@@ -706,7 +706,7 @@ public class AlignmentUtilsTests
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMapProteinToCdna_withXrefs() throws IOException
   {
     List<SequenceI> protseqs = new ArrayList<SequenceI>();
@@ -782,7 +782,7 @@ public class AlignmentUtilsTests
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMapProteinToCdna_prioritiseXrefs() throws IOException
   {
     List<SequenceI> protseqs = new ArrayList<SequenceI>();
@@ -832,7 +832,7 @@ public class AlignmentUtilsTests
    * Test the method that shows or hides sequence annotations by type(s) and
    * selection group.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testShowOrHideSequenceAnnotations()
   {
     SequenceI seq1 = new Sequence("Seq1", "AAA");
@@ -940,7 +940,7 @@ public class AlignmentUtilsTests
   /**
    * Tests for the method that checks if one sequence cross-references another
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testHasCrossRef()
   {
     assertFalse(AlignmentUtils.hasCrossRef(null, null));
@@ -969,7 +969,7 @@ public class AlignmentUtilsTests
    * Tests for the method that checks if either sequence cross-references the
    * other
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testHaveCrossRef()
   {
     assertFalse(AlignmentUtils.hasCrossRef(null, null));
@@ -999,7 +999,7 @@ public class AlignmentUtilsTests
   /**
    * Test the method that extracts the exon-only part of a dna alignment.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMakeExonAlignment()
   {
     SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa");
@@ -1094,7 +1094,7 @@ public class AlignmentUtilsTests
    * already has a protein product (Uniprot translation) which in turn has an
    * x-ref to the EMBLCDS record.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMakeExonSequences()
   {
     SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa");
@@ -1136,7 +1136,7 @@ public class AlignmentUtilsTests
    * its product mappings, for the case where there are multiple exon mappings
    * to different protein products.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMakeExonAlignment_multipleProteins()
   {
     SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa");
index 8c3489b..37bfca4 100644 (file)
@@ -84,7 +84,7 @@ public class AnnotationSorterTest
    * sequence ref</li>
    * </ul>
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSortBySequenceAndType_autocalcLast()
   {
     // @formatter:off
@@ -111,7 +111,7 @@ public class AnnotationSorterTest
   /**
    * Variant with autocalculated annotations sorting to front
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSortBySequenceAndType_autocalcFirst()
   {
     // @formatter:off
@@ -147,7 +147,7 @@ public class AnnotationSorterTest
    * sequence ref</li>
    * </ul>
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSortByTypeAndSequence_autocalcLast()
   {
     // @formatter:off
@@ -174,7 +174,7 @@ public class AnnotationSorterTest
   /**
    * Variant of test with autocalculated annotations sorted to front
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSortByTypeAndSequence_autocalcFirst()
   {
     // @formatter:off
@@ -202,7 +202,7 @@ public class AnnotationSorterTest
    * Variant of test with autocalculated annotations sorted to front but
    * otherwise no change.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testNoSort_autocalcFirst()
   {
     // @formatter:off
@@ -226,7 +226,7 @@ public class AnnotationSorterTest
     assertEquals("Structure", anns[6].label);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSort_timingPresorted()
   {
     testTiming_presorted(50, 100);
@@ -242,23 +242,23 @@ public class AnnotationSorterTest
    */
   private void testTiming_presorted(final int numSeqs, final int numAnns)
   {
-    al = buildAlignment(numSeqs);
-    anns = buildAnnotations(numAnns);
+    Alignment alignment = buildAlignment(numSeqs);
+    AlignmentAnnotation[] annotations = buildAnnotations(numAnns);
 
     /*
      * Set the annotations presorted by label
      */
     Random r = new Random();
-    final SequenceI[] sequences = al.getSequencesArray();
-    for (int i = 0; i < anns.length; i++)
+    final SequenceI[] sequences = alignment.getSequencesArray();
+    for (int i = 0; i < annotations.length; i++)
     {
       SequenceI randomSequenceRef = sequences[r.nextInt(sequences.length)];
-      anns[i].sequenceRef = randomSequenceRef;
-      anns[i].label = "label" + i;
+      annotations[i].sequenceRef = randomSequenceRef;
+      annotations[i].label = "label" + i;
     }
     long startTime = System.currentTimeMillis();
-    AnnotationSorter testee = new AnnotationSorter(al, false);
-    testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
+    AnnotationSorter testee = new AnnotationSorter(alignment, false);
+    testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
     long endTime = System.currentTimeMillis();
     final long elapsed = endTime - startTime;
     System.out.println("Timing test for presorted " + numSeqs
@@ -269,7 +269,7 @@ public class AnnotationSorterTest
   /**
    * Timing tests for sorting randomly sorted annotations for various sizes.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSort_timingUnsorted()
   {
     testTiming_unsorted(50, 100);
@@ -286,23 +286,23 @@ public class AnnotationSorterTest
    */
   private void testTiming_unsorted(final int numSeqs, final int numAnns)
   {
-    al = buildAlignment(numSeqs);
-    anns = buildAnnotations(numAnns);
+    Alignment alignment = buildAlignment(numSeqs);
+    AlignmentAnnotation[] annotations = buildAnnotations(numAnns);
 
     /*
      * Set the annotations in random order with respect to the sequences
      */
     Random r = new Random();
-    final SequenceI[] sequences = al.getSequencesArray();
-    for (int i = 0; i < anns.length; i++)
+    final SequenceI[] sequences = alignment.getSequencesArray();
+    for (int i = 0; i < annotations.length; i++)
     {
       SequenceI randomSequenceRef = sequences[r.nextInt(sequences.length)];
-      anns[i].sequenceRef = randomSequenceRef;
-      anns[i].label = "label" + i;
+      annotations[i].sequenceRef = randomSequenceRef;
+      annotations[i].label = "label" + i;
     }
     long startTime = System.currentTimeMillis();
-    AnnotationSorter testee = new AnnotationSorter(al, false);
-    testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL);
+    AnnotationSorter testee = new AnnotationSorter(alignment, false);
+    testee.sort(annotations, SequenceAnnotationOrder.SEQUENCE_AND_LABEL);
     long endTime = System.currentTimeMillis();
     final long elapsed = endTime - startTime;
     System.out.println("Timing test for unsorted " + numSeqs
@@ -313,7 +313,7 @@ public class AnnotationSorterTest
   /**
    * Timing test for sorting annotations with a limited range of types (labels).
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSort_timingSemisorted()
   {
     testTiming_semiSorted(50, 100);
@@ -336,8 +336,8 @@ public class AnnotationSorterTest
    */
   private void testTiming_semiSorted(final int numSeqs, final int numAnns)
   {
-    al = buildAlignment(numSeqs);
-    anns = buildAnnotations(numAnns);
+    Alignment alignment = buildAlignment(numSeqs);
+    AlignmentAnnotation[] annotations = buildAnnotations(numAnns);
 
     String[] labels = new String[]
     { "label1", "label2", "label3", "label4", "label5", "label6" };
@@ -346,16 +346,16 @@ public class AnnotationSorterTest
      * Set the annotations in sequence order with randomly assigned labels.
      */
     Random r = new Random();
-    final SequenceI[] sequences = al.getSequencesArray();
-    for (int i = 0; i < anns.length; i++)
+    final SequenceI[] sequences = alignment.getSequencesArray();
+    for (int i = 0; i < annotations.length; i++)
     {
       SequenceI sequenceRef = sequences[i % sequences.length];
-      anns[i].sequenceRef = sequenceRef;
-      anns[i].label = labels[r.nextInt(labels.length)];
+      annotations[i].sequenceRef = sequenceRef;
+      annotations[i].label = labels[r.nextInt(labels.length)];
     }
     long startTime = System.currentTimeMillis();
-    AnnotationSorter testee = new AnnotationSorter(al, false);
-    testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
+    AnnotationSorter testee = new AnnotationSorter(alignment, false);
+    testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
     long endTime = System.currentTimeMillis();
     long elapsed = endTime - startTime;
     System.out.println("Sort by label for semisorted " + numSeqs
@@ -364,7 +364,7 @@ public class AnnotationSorterTest
 
     // now resort by sequence
     startTime = System.currentTimeMillis();
-    testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL);
+    testee.sort(annotations, SequenceAnnotationOrder.SEQUENCE_AND_LABEL);
     endTime = System.currentTimeMillis();
     elapsed = endTime - startTime;
     System.out.println("Resort by sequence for semisorted " + numSeqs
@@ -373,7 +373,7 @@ public class AnnotationSorterTest
 
     // now resort by label
     startTime = System.currentTimeMillis();
-    testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
+    testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
     endTime = System.currentTimeMillis();
     elapsed = endTime - startTime;
     System.out.println("Resort by label for semisorted " + numSeqs
index e787ac3..429c9ed 100644 (file)
@@ -10,7 +10,7 @@ import org.testng.annotations.Test;
 public class CodingUtilsTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDecodeCodon()
   {
     assertTrue(Arrays.equals(new char[]
@@ -31,7 +31,7 @@ public class CodingUtilsTest
     { 'T', 'T', 'T' }, CodingUtils.decodeCodon(63)));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDecodeNucleotide()
   {
     assertEquals('A', CodingUtils.decodeNucleotide(0));
@@ -41,7 +41,7 @@ public class CodingUtilsTest
     assertEquals('0', CodingUtils.decodeNucleotide(4));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testEncodeCodon()
   {
     assertTrue(CodingUtils.encodeCodon('Z') < 0);
index 095fc05..bd737fa 100644 (file)
@@ -9,7 +9,7 @@ import org.testng.annotations.Test;
 
 public class CrossRefTest
 {
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindXDbRefs()
   {
     DBRefEntry ref1 = new DBRefEntry("UNIPROT", "1", "A123");
index 6371315..55908dd 100644 (file)
@@ -94,7 +94,7 @@ public class DnaTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testTranslateCdna_withUntranslatableCodons()
           throws IOException
   {
@@ -116,7 +116,7 @@ public class DnaTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testTranslateCdna_withUntranslatableCodonsAndHiddenColumns()
           throws IOException
   {
@@ -152,7 +152,7 @@ public class DnaTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testTranslateCdna_simple() throws IOException
   {
     AlignmentI alf = new FormatAdapter().readFile(fasta,
@@ -173,7 +173,7 @@ public class DnaTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testTranslateCdna_hiddenColumns() throws IOException
   {
     AlignmentI alf = new FormatAdapter().readFile(fasta,
@@ -193,7 +193,7 @@ public class DnaTest
   /**
    * Use this test to help debug into any cases of interest.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCompareCodonPos_oneOnly()
   {
     assertFollows("-AA--A", "G--GG"); // 2 shifted seq2, 3 shifted seq1
@@ -202,7 +202,7 @@ public class DnaTest
   /**
    * Tests for method that compares 'alignment' of two codon position triplets.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCompareCodonPos()
   {
     /*
@@ -259,7 +259,7 @@ public class DnaTest
    * reorders the cDNA and retranslates, and verifies that the translations are
    * the same (apart from ordering).
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testTranslateCdna_sequenceOrderIndependent()
   {
     /*
@@ -313,7 +313,7 @@ public class DnaTest
    * Test that all the cases in testCompareCodonPos have a 'symmetric'
    * comparison (without checking the actual comparison result).
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCompareCodonPos_isSymmetric()
   {
     assertSymmetric("AAA", "GGG");
@@ -436,7 +436,7 @@ public class DnaTest
   /**
    * Weirdly, maybe worth a test to prove the helper method of this test class.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConvertCodon()
   {
     assertEquals("[0, 1, 2]", convertCodon("AAA").toString());
index df04243..4698d0d 100644 (file)
@@ -37,7 +37,7 @@ public class GroupingTest
   int[] positions = new int[]
   { 1, 7, 9 };
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMakeGroupsWithBoth()
   {
     ArrayList<String> str = new ArrayList<String>();
index 90faedc..221d47e 100644 (file)
@@ -46,7 +46,7 @@ public class ParsePropertiesTest
    * more 'number characters' (0-9+.), i.e. greedily matches any trailing
    * numeric part of the string
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetScoresFromDescription()
   {
     String regex = ".*([-0-9.+]+)";
@@ -82,7 +82,7 @@ public class ParsePropertiesTest
    * character, followed by at least one 'number character', then any trailing
    * characters.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetScoresFromDescription_twoScores()
   {
     String regex = ".*([-0-9.+]+).+([-0-9.+]+).*";
@@ -144,7 +144,7 @@ public class ParsePropertiesTest
    * 
    * @see AlignFrame.extractScores_actionPerformed
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetScoresFromDescription_wordBoundaries()
   {
     String regex = "\\W*([-+eE0-9.]+)";
diff --git a/test/jalview/analysis/RnaTest.java b/test/jalview/analysis/RnaTest.java
new file mode 100644 (file)
index 0000000..3676e0b
--- /dev/null
@@ -0,0 +1,58 @@
+package jalview.analysis;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.fail;
+
+import jalview.analysis.SecStrConsensus.SimpleBP;
+
+import java.util.Vector;
+
+import org.testng.annotations.Test;
+public class RnaTest
+{
+  @Test(groups ={ "Functional" })
+  public void testGetSimpleBPs() throws WUSSParseException
+  {
+    String rna = "([{})]"; // JAL-1081 example
+    Vector<SimpleBP> bps = Rna.GetSimpleBPs(rna);
+    assertEquals(3, bps.size());
+
+    /*
+     * the base pairs are added in the order in which the matching base is found
+     */
+    assertEquals(2, bps.get(0).bp5); // {
+    assertEquals(3, bps.get(0).bp3); // }
+    assertEquals(0, bps.get(1).bp5); // (
+    assertEquals(4, bps.get(1).bp3); // )
+    assertEquals(1, bps.get(2).bp5); // [
+    assertEquals(5, bps.get(2).bp3); // ]
+  }
+
+  @Test(groups ={ "Functional" })
+  public void testGetSimpleBPs_unmatchedOpener()
+  {
+    String rna = "(([{})]";
+    try
+    {
+      Rna.GetSimpleBPs(rna);
+      fail("expected exception");
+    } catch (WUSSParseException e)
+    {
+      // expected
+    }
+  }
+
+  @Test(groups ={ "Functional" })
+  public void testGetSimpleBPs_unmatchedCloser()
+  {
+    String rna = "([{})]]";
+    try
+    {
+      Rna.GetSimpleBPs(rna);
+      fail("expected exception");
+    } catch (WUSSParseException e)
+    {
+      // expected
+    }
+  }
+}
index 81bcf86..81514a4 100644 (file)
@@ -59,7 +59,7 @@ public class TestAlignSeq
 
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   /**
    * simple test that mapping from alignment corresponds identical positions.
    */
@@ -86,7 +86,7 @@ public class TestAlignSeq
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testExtractGaps()
   {
     assertNull(AlignSeq.extractGaps(null, null));
@@ -96,7 +96,7 @@ public class TestAlignSeq
     assertEquals("ABCD", AlignSeq.extractGaps(" .-", ". -A-B.C D."));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testPrintAlignment()
   {
     AlignSeq as = AlignSeq.doGlobalNWAlignment(s1, s3, AlignSeq.PEP);
index 5ab0bb0..221b230 100644 (file)
@@ -23,7 +23,7 @@ public class FeatureScoreModelTest
   int[] sf3 = new int[]
   { -1, -1, -1, -1, -1, -1, 76, 77 };
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFeatureScoreModel() throws Exception
   {
     AlignFrame alf = new FileLoader(false).LoadFileWaitTillLoaded(alntestFile,
index d9d3bb3..3582dc5 100644 (file)
@@ -26,9 +26,11 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 
 import org.testng.Assert;
-import org.testng.AssertJUnit;
+import org.testng.FileAssert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 public class CommandLineOperations
@@ -81,7 +83,7 @@ public class CommandLineOperations
     String _cmd = "java "
             + (withAwt ? "-Djava.awt.headless=true" : "")
             + " -Djava.ext.dirs=./lib -classpath ./classes jalview.bin.Jalview ";
-    System.out.println("###############Jalview CMD: " + _cmd + cmd);
+    System.out.println("############ Testing Jalview CMD: " + _cmd + cmd);
     Process ls2_proc = Runtime.getRuntime().exec(_cmd + cmd);
     BufferedReader outputReader = new BufferedReader(new InputStreamReader(
             ls2_proc.getInputStream()));
@@ -91,81 +93,126 @@ public class CommandLineOperations
     Worker worker = new Worker(ls2_proc);
     worker.start();
     worker.join(timeout);
-    System.out.println("Output:  ");
-    String ln = null;
-    while ((ln = outputReader.readLine()) != null)
+    // System.out.println("Output:  ");
+    // String ln = null;
+    // while ((ln = outputReader.readLine()) != null)
+    // {
+    // System.out.println(ln);
+    // }
+    //
+    // System.out.println("Error:  ");
+    // while ((ln = errorReader.readLine()) != null)
+    // {
+    // System.out.println(ln);
+    // }
+    return worker;
+  }
+
+  @BeforeTest
+  public void initialize()
+  {
+    new CommandLineOperations();
+  }
+
+  @Test(groups =
+  { "Functional" }, dataProvider = "headlessModeOutputParams")
+  public void testHeadlessModeOutputs(String harg, String type,
+          String fileName, boolean withAWT, int expectedMinFileSize)
+  {
+    String cmd = harg + type + " " + fileName;
+    // System.out.println(">>>>>>>>>>>>>>>> Command : " + cmd);
+    File file = new File(fileName);
+    Worker worker = null;
+    try
     {
-      System.out.println(ln);
+      worker = jalviewDesktopRunner(withAWT, cmd, 9000);
+    } catch (InterruptedException e)
+    {
+      e.printStackTrace();
+    } catch (IOException e)
+    {
+      e.printStackTrace();
     }
 
-    System.out.println("Error:  ");
-    while ((ln = errorReader.readLine()) != null)
+    FileAssert.assertFile(file, "Didn't create an output" + type
+            + " file.[" + harg + "]");
+    FileAssert.assertMinLength(new File(fileName), expectedMinFileSize);
+    if (worker != null && worker.exit == null)
     {
-      System.out.println(ln);
+      worker.interrupt();
+      Thread.currentThread().interrupt();
+      worker.process.destroy();
+      Assert.fail("Jalview did not exit after  "
+              + type
+              + " generation (try running test again to verify - timeout at 9000ms). ["
+              + harg + "]");
     }
-    return worker;
+    new File(fileName).delete();
   }
 
-  @Test
-  public void testHeadlessModeEPS() throws Exception
+  @DataProvider(name = "headlessModeOutputParams")
+  public static Object[][] headlessModeOutput()
   {
-    String[] headlessArgs = new String[]
-    { "nodisplay", "headless", "nogui" };
-    for (String _harg : headlessArgs)
+    return new Object[][]
     {
-      boolean _switch = false, withAwt = false;
-      do
-      {
-        if (_switch)
-        {
-          withAwt = true;
-        }
-        _switch = true;
-        String jalview_input = "examples/uniref50.fa";
-        String jalview_output = "test_uniref50_out.eps";
-        String cmd = "" + _harg + " -open " + jalview_input + " -eps "
-                + jalview_output;
-        String harg = _harg
-                + (withAwt ? "-Djava.awt.headless=true"
-                        : " NO AWT.HEADLESS");
-        System.out.println("Testing with Headless argument: '" + harg
-                + "'\n");
-        Worker worker = jalviewDesktopRunner(withAwt, cmd, 9000);
-        AssertJUnit.assertTrue("Didn't create an output EPS file.[" + harg
-                + "]", new File("test_uniref50_out.eps").exists());
-        AssertJUnit.assertTrue(
-                "Didn't create an EPS file with any content[" + harg + "]",
-                new File("test_uniref50_out.eps").length() > 4096);
-        if (worker.exit == null)
-        {
-          worker.interrupt();
-          Thread.currentThread().interrupt();
-          worker.process.destroy();
-          Assert.fail("Jalview did not exit after EPS generation (try running test again to verify - timeout at 9000ms). ["
-                  + harg + "]");
-        }
-      } while (!withAwt);
-    }
+        { "nodisplay -open examples/uniref50.fa", " -eps",
+            "test_uniref50_out.eps", true, 4096 },
+        { "nodisplay -open examples/uniref50.fa", " -eps",
+            "test_uniref50_out.eps", false, 4096 },
+        { "headless -open examples/uniref50.fa", " -eps",
+            "test_uniref50_out.eps", true, 4096 },
+        { "headless -open examples/uniref50.fa", " -eps",
+            "test_uniref50_out.eps", false, 4096 },
+        { "nogui -open examples/uniref50.fa", " -eps",
+            "test_uniref50_out.eps", true, 4096 },
+        { "nogui -open examples/uniref50.fa", " -eps",
+            "test_uniref50_out.eps", false, 4096 },
+        { "headless -open examples/uniref50.fa", " -svg",
+            "test_uniref50_out.svg", true, 4096 },
+        { "headless -open examples/uniref50.fa", " -svg",
+            "test_uniref50_out.svg", false, 4096 },
+        { "headless -open examples/uniref50.fa", " -png",
+            "test_uniref50_out.png", true, 4096 },
+        { "headless -open examples/uniref50.fa", " -png",
+            "test_uniref50_out.png", false, 4096 },
+        { "headless -open examples/uniref50.fa", " -html",
+            "test_uniref50_out.html", true, 4096 },
+        { "headless -open examples/uniref50.fa", " -html",
+            "test_uniref50_out.html", false, 4096 },
+        { "headless -open examples/uniref50.fa", " -fasta",
+            "test_uniref50_out.mfa", true, 2096 },
+        { "headless -open examples/uniref50.fa", " -fasta",
+            "test_uniref50_out.mfa", false, 2096 },
+        { "headless -open examples/uniref50.fa", " -clustal",
+            "test_uniref50_out.aln", true, 2096 },
+        { "headless -open examples/uniref50.fa", " -clustal",
+            "test_uniref50_out.aln", false, 2096 },
+        { "headless -open examples/uniref50.fa", " -msf",
+            "test_uniref50_out.msf", true, 2096 },
+        { "headless -open examples/uniref50.fa", " -msf",
+            "test_uniref50_out.msf", false, 2096 },
+        { "headless -open examples/uniref50.fa", " -pileup",
+            "test_uniref50_out.aln", true, 2096 },
+        { "headless -open examples/uniref50.fa", " -pileup",
+            "test_uniref50_out.aln", false, 2096 },
+        { "headless -open examples/uniref50.fa", " -pir",
+            "test_uniref50_out.pir", true, 2096 },
+        { "headless -open examples/uniref50.fa", " -pir",
+            "test_uniref50_out.pir", false, 2096 },
+        { "headless -open examples/uniref50.fa", " -pfam",
+            "test_uniref50_out.pfam", true, 2096 },
+        { "headless -open examples/uniref50.fa", " -pfam",
+            "test_uniref50_out.pfam", false, 2096 },
+        { "headless -open examples/uniref50.fa", " -blc",
+            "test_uniref50_out.blc", true, 2096 },
+        { "headless -open examples/uniref50.fa", " -blc",
+            "test_uniref50_out.blc", false, 2096 },
+        { "headless -open examples/uniref50.fa", " -jalview",
+            "test_uniref50_out.jvp", true, 2096 },
+        { "headless -open examples/uniref50.fa", " -jalview",
+            "test_uniref50_out.jvp", false, 2096 },
+    };
   }
-  // @Test
-  // public void testJalview2XMLDataset() throws Exception
-  // {
-  // String jalview_input = "examples/uniref50.fa";
-  // String jalview_output = "test_uniref50_out.eps";
-  // String cmd = ""+" -open "+ jalview_input + " -eps " + jalview_output;
-  // //String harg = _harg+(withAwt ?
-  // "-Djava.awt.headless=true":" NO AWT.HEADLESS");
-  // System.out.println("Testing with Headless argument: '"+harg+"'\n");
-  // Worker worker = jalviewDesktopRunner(withAwt, cmd, 9000);
-  // assertTrue("Didn't create an output EPS file.["+harg+"]", new
-  // File("test_uniref50_out.eps").exists());
-  // assertTrue("Didn't create an EPS file with any content["+harg+"]", new
-  // File("test_uniref50_out.eps").length()>4096);
-  // if (worker.exit == null){
-  // worker.interrupt();
-  // Thread.currentThread().interrupt();
-  // worker.process.destroy();
-  // fail("Jalview did not exit after EPS generation (try running test again to verify - timeout at 9000ms). ["+harg+"]");
-  // }
-  // }
+
+
 }
index 6c570d5..3591648 100644 (file)
@@ -50,7 +50,7 @@ public class EditCommandTest
   /**
    * Test inserting gap characters
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAppendEdit_insertGap()
   {
     // set a non-standard gap character to prove it is actually used
@@ -67,7 +67,7 @@ public class EditCommandTest
    * Test deleting characters from sequences. Note the deleteGap() action does
    * not check that only gap characters are being removed.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAppendEdit_deleteGap()
   {
     testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true);
@@ -81,7 +81,7 @@ public class EditCommandTest
    * Test a cut action. The command should store the cut characters to support
    * undo.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCut()
   {
     Edit ec = testee.new Edit(Action.CUT, seqs, 4, 3, al);
@@ -102,7 +102,8 @@ public class EditCommandTest
   /**
    * Test a Paste action, where this adds sequences to an alignment.
    */
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   // TODO fix so it works
   public void testPaste_addToAlignment()
   {
@@ -122,7 +123,7 @@ public class EditCommandTest
   /**
    * Test insertGap followed by undo command
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testUndo_insertGap()
   {
     // Edit ec = testee.new Edit(Action.INSERT_GAP, seqs, 4, 3, '?');
@@ -140,7 +141,7 @@ public class EditCommandTest
   /**
    * Test deleteGap followed by undo command
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testUndo_deleteGap()
   {
     testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true);
@@ -158,7 +159,7 @@ public class EditCommandTest
   /**
    * Test several commands followed by an undo command
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testUndo_multipleCommands()
   {
     // delete positions 3/4/5 (counting from 1)
@@ -187,7 +188,7 @@ public class EditCommandTest
    * Unit test for JAL-1594 bug: click and drag sequence right to insert gaps -
    * undo did not remove them all.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testUndo_multipleInsertGaps()
   {
     testee.appendEdit(Action.INSERT_GAP, seqs, 4, 1, al, true);
@@ -205,7 +206,7 @@ public class EditCommandTest
   /**
    * Test cut followed by undo command
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testUndo_cut()
   {
     testee.appendEdit(Action.CUT, seqs, 4, 3, al, true);
@@ -222,7 +223,7 @@ public class EditCommandTest
   /**
    * Test the replace command (used to manually edit a sequence)
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testReplace()
   {
     // seem to need a dataset sequence on the edited sequence here
@@ -239,7 +240,7 @@ public class EditCommandTest
    * Test that the addEdit command correctly merges insert gap commands when
    * possible.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAddEdit_multipleInsertGap()
   {
     /*
@@ -289,7 +290,7 @@ public class EditCommandTest
    * Test that the addEdit command correctly merges delete gap commands when
    * possible.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAddEdit_multipleDeleteGap()
   {
     /*
@@ -346,7 +347,7 @@ public class EditCommandTest
    * case when they appear contiguous but are acting on different sequences.
    * They should not be merged.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAddEdit_removeAllGaps()
   {
     seqs[0].setSequence("a???bcdefghjk");
@@ -367,7 +368,7 @@ public class EditCommandTest
    * Test that the addEdit command correctly merges insert gap commands acting
    * on a multi-sequence selection.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAddEdit_groupInsertGaps()
   {
     /*
@@ -403,7 +404,7 @@ public class EditCommandTest
    * <li>last: --A--B-CDEF</li>
    * </ul>
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testPriorState_multipleInserts()
   {
     EditCommand command = new EditCommand();
@@ -431,7 +432,7 @@ public class EditCommandTest
    * <li>End: ABC</li>
    * </ul>
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testPriorState_removeAllGaps()
   {
     EditCommand command = new EditCommand();
@@ -452,7 +453,7 @@ public class EditCommandTest
   /**
    * Test for 'undoing' a single delete edit.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testPriorState_singleDelete()
   {
     EditCommand command = new EditCommand();
@@ -471,7 +472,7 @@ public class EditCommandTest
   /**
    * Test 'undoing' a single gap insertion edit command.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testPriorState_singleInsert()
   {
     EditCommand command = new EditCommand();
@@ -491,7 +492,7 @@ public class EditCommandTest
    * Test that mimics 'remove all gaps' action. This generates delete gap edits
    * for contiguous gaps in each sequence separately.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testPriorState_removeGapsMultipleSeqs()
   {
     EditCommand command = new EditCommand();
@@ -555,7 +556,7 @@ public class EditCommandTest
    * series Delete Gap edits that each act on all sequences that share a gapped
    * column region.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testPriorState_removeGappedCols()
   {
     EditCommand command = new EditCommand();
index 63f6772..35d92d3 100644 (file)
@@ -15,7 +15,7 @@ public class AlignedCodonFrameTest
   /**
    * Test the method that locates the first aligned sequence that has a mapping.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindAlignedSequence()
   {
     AlignmentI cdna = new Alignment(new SequenceI[]
@@ -63,7 +63,7 @@ public class AlignedCodonFrameTest
   /**
    * Test the method that locates the mapped codon for a protein position.
    */
-    @Test
+    @Test(groups ={ "Functional" })
   public void testGetMappedRegion()
   {
     // introns lower case, exons upper case
@@ -111,7 +111,7 @@ public class AlignedCodonFrameTest
     assertNull(acf.getMappedRegion(seq1, aseq2, 1));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetMappedCodon()
   {
     final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T");
index 65fe4bf..d9c7e12 100644 (file)
@@ -21,7 +21,7 @@ public class AlignedCodonIteratorTest
   /**
    * Test normal case for iterating over aligned codons.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testNext()
   {
     SequenceI from = new Sequence("Seq1", "-CgC-C-cCtAG-AtG-Gc");
@@ -49,7 +49,7 @@ public class AlignedCodonIteratorTest
   /**
    * Test weird case where the mapping skips over a peptide.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testNext_unmappedPeptide()
   {
     SequenceI from = new Sequence("Seq1", "-CgC-C-cCtAG-AtG-Gc");
@@ -77,7 +77,7 @@ public class AlignedCodonIteratorTest
   /**
    * Test for exception thrown for an incomplete codon.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testNext_incompleteCodon()
   {
     SequenceI from = new Sequence("Seq1", "-CgC-C-cCgTt");
@@ -106,7 +106,7 @@ public class AlignedCodonIteratorTest
   /**
    * Test normal case for iterating over aligned codons.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAnother()
   {
     SequenceI from = new Sequence("Seq1", "TGCCATTACCAG-");
index afa6169..a6802b1 100644 (file)
@@ -9,7 +9,7 @@ import org.testng.annotations.Test;
 public class AlignedCodonTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testEquals()
   {
     AlignedCodon ac = new AlignedCodon(1, 3, 4);
@@ -20,7 +20,7 @@ public class AlignedCodonTest
     assertTrue(ac.equals(ac));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testToString() {
     AlignedCodon ac = new AlignedCodon(1, 3, 4);
     assertEquals("[1, 3, 4]", ac.toString());
index 4c9eabe..d32105d 100644 (file)
@@ -10,7 +10,7 @@ import org.testng.annotations.Test;
 
 public class AlignmentAnnotationTests
 {
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCopyConstructor()
   {
     SequenceI sq = new Sequence("Foo", "ARAARARARAWEAWEAWRAWEAWE");
@@ -74,7 +74,7 @@ public class AlignmentAnnotationTests
    * different dataset frames (annotation transferred by mapping between
    * sequences)
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testLiftOver()
   {
     SequenceI sqFrom = new Sequence("fromLong", "QQQCDEWGH");
@@ -157,7 +157,7 @@ public class AlignmentAnnotationTests
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAdjustForAlignment()
   {
     SequenceI seq = new Sequence("TestSeq", "ABCDEFG");
index 036f2cf..e885733 100644 (file)
@@ -94,7 +94,7 @@ public class AlignmentTest
   /**
    * Test method that returns annotations that match on calcId.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindAnnotation_byCalcId()
   {
     Iterable<AlignmentAnnotation> anns = al
@@ -106,7 +106,7 @@ public class AlignmentTest
     assertFalse(iter.hasNext());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeleteAllAnnotations_includingAutocalculated()
   {
     AlignmentAnnotation aa = new AlignmentAnnotation("Consensus",
@@ -120,7 +120,7 @@ public class AlignmentTest
     assertEquals("Not all deleted", 0, al.getAlignmentAnnotation().length);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeleteAllAnnotations_excludingAutocalculated()
   {
     AlignmentAnnotation aa = new AlignmentAnnotation("Consensus",
@@ -144,7 +144,7 @@ public class AlignmentTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAlignAs_dnaAsDna() throws IOException
   {
     // aligned cDNA:
@@ -176,7 +176,7 @@ public class AlignmentTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAlignAs_proteinAsCdna() throws IOException
   {
     // see also AlignmentUtilsTests
@@ -200,7 +200,7 @@ public class AlignmentTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAlignAs_cdnaAsProtein() throws IOException
   {
     /*
@@ -232,7 +232,7 @@ public class AlignmentTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAlignAs_dnaAsProtein_withIntrons() throws IOException
   {
     /*
index 2ffebe0..38e9c93 100644 (file)
@@ -9,7 +9,7 @@ import org.testng.annotations.Test;
 public class ColumnSelectionTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAddElement()
   {
     ColumnSelection cs = new ColumnSelection();
@@ -25,7 +25,7 @@ public class ColumnSelectionTest
    * Test the remove method - in particular to verify that remove(int i) removes
    * the element whose value is i, _NOT_ the i'th element.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testRemoveElement()
   {
     ColumnSelection cs = new ColumnSelection();
@@ -50,7 +50,7 @@ public class ColumnSelectionTest
    * Test the method that finds the visible column position of an alignment
    * column, allowing for hidden columns.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindColumnPosition()
   {
     ColumnSelection cs = new ColumnSelection();
index b5549f0..f00cb38 100644 (file)
@@ -13,7 +13,7 @@ public class DBRefEntryTest
   /**
    * Tests for the method that compares equality of reference (but not mapping)
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testEqualRef()
   {
     DBRefEntry ref1 = new DBRefEntry("UNIPROT", "1", "V71633");
index 378d708..cb528aa 100644 (file)
@@ -17,7 +17,7 @@ public class MappingTest
    * trite test of the intersectVisContigs method for a simple DNA -> Protein
    * exon map and a range of visContigs
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testIntersectVisContigs()
   {
     MapList fk = new MapList(new int[]
index 093702f..68c3ddf 100644 (file)
@@ -19,7 +19,7 @@ public class PDBEntryTest
   {
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void test()
   {
     try
index 7f099c8..b576569 100644 (file)
@@ -7,7 +7,7 @@ import org.testng.annotations.Test;
 public class SearchResultsTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testToString()
   {
     SequenceI seq = new Sequence("", "abcdefghijklm");
index b8748b6..3b50ce1 100644 (file)
@@ -15,7 +15,7 @@ public class SeqCigarTest
    * 
    * TODO: split into separate tests
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSomething() throws Exception
   {
     String o_seq = "asdfktryasdtqwrtsaslldddptyipqqwaslchvhttt";
index 2fc1934..4629a47 100644 (file)
@@ -1,15 +1,17 @@
 package jalview.datamodel;
 
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+import org.testng.annotations.Test;
 
-import org.junit.Assert;
-import org.junit.Test;
 
 public class SequenceDummyTest
 {
   /**
    * test for become method
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBecome()
   {
     SequenceI seq = new Sequence("OrigSeq", "ASEQUENCE");
@@ -19,9 +21,9 @@ public class SequenceDummyTest
     SequenceDummy dummySeq = new SequenceDummy("OrigSeq");
     dummySeq.addSequenceFeature(ofeat);
     dummySeq.become(seq);
-    Assert.assertFalse("Dummy sequence did not become a full sequence",
+    assertFalse("Dummy sequence did not become a full sequence",
             dummySeq.isDummy());
-    Assert.assertTrue("Sequence was not updated from template", seq
+    assertTrue("Sequence was not updated from template", seq
             .getSequenceAsString().equals(dummySeq.getSequenceAsString()));
     boolean found = false;
     for (SequenceFeature sf : dummySeq.getSequenceFeatures())
@@ -32,7 +34,7 @@ public class SequenceDummyTest
         break;
       }
     }
-    Assert.assertTrue("Didn't retain original sequence feature", found);
+    assertTrue("Didn't retain original sequence feature", found);
 
     // todo - should test all aspect of copy constructor
   }
index b216b13..78eb66c 100644 (file)
@@ -20,7 +20,7 @@ public class SequenceTest
   {
     seq = new Sequence("FER1", "AKPNGVL");
   }
-  @Test
+  @Test(groups ={ "Functional" })
   public void testInsertGapsAndGapmaps()
   {
     SequenceI aseq = seq.deriveSequence();
@@ -35,7 +35,7 @@ public class SequenceTest
     assertEquals("Gap interval 2 end wrong", 8, gapInt.get(1)[1]);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetAnnotation()
   {
     // initial state returns null not an empty array
@@ -51,7 +51,7 @@ public class SequenceTest
     assertNull(seq.getAnnotation());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetAnnotation_forLabel()
   {
     AlignmentAnnotation ann1 = addAnnotation("label1", "desc1", "calcId1", 1f);
@@ -74,7 +74,7 @@ public class SequenceTest
     return annotation;
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetAlignmentAnnotations_forCalcIdAndLabel()
   {
     AlignmentAnnotation ann1 = addAnnotation("label1", "desc1", "calcId1",
@@ -107,7 +107,7 @@ public class SequenceTest
    * setting the sequenceRef on the annotation. Adding the same annotation twice
    * should be ignored.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAddAlignmentAnnotation()
   {
     assertNull(seq.getAnnotation());
@@ -137,7 +137,7 @@ public class SequenceTest
 
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetStartGetEnd()
   {
     SequenceI seq = new Sequence("test", "ABCDEF");
@@ -157,7 +157,7 @@ public class SequenceTest
    * Tests for the method that returns an alignment column position (base 1) for
    * a given sequence position (base 1).
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindIndex()
   {
     SequenceI seq = new Sequence("test", "ABCDEF");
@@ -185,7 +185,7 @@ public class SequenceTest
    * Tests for the method that returns a dataset sequence position (base 1) for
    * an aligned column position (base 0).
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindPosition()
   {
     SequenceI seq = new Sequence("test", "ABCDEF");
@@ -220,7 +220,7 @@ public class SequenceTest
     assertEquals(7, seq.findPosition(11));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeleteChars()
   {
     SequenceI seq = new Sequence("test", "ABCDEF");
@@ -238,7 +238,7 @@ public class SequenceTest
     assertEquals(6, seq.getEnd());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testInsertCharAt()
   {
     // non-static methods:
@@ -255,7 +255,7 @@ public class SequenceTest
    * Test the method that returns an array of aligned sequence positions where
    * the array index is the data sequence position (both base 0).
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGapMap()
   {
     SequenceI seq = new Sequence("test", "-A--B-CD-E--F-");
@@ -267,7 +267,7 @@ public class SequenceTest
    * Test the method that gets sequence features, either from the sequence or
    * its dataset.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetSequenceFeatures()
   {
     SequenceI seq = new Sequence("test", "GATCAT");
@@ -316,7 +316,7 @@ public class SequenceTest
    * entries are the residue positions at the sequence position (or to the right
    * if a gap)
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindPositionMap()
   {
     /*
@@ -334,7 +334,7 @@ public class SequenceTest
   /**
    * Test for getSubsequence
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetSubsequence()
   {
     SequenceI seq = new Sequence("TestSeq", "ABCDEFG");
@@ -354,7 +354,7 @@ public class SequenceTest
   /**
    * Test for deriveSequence applied to a sequence with a dataset
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeriveSequence_existingDataset()
   {
     SequenceI seq = new Sequence("Seq1", "CD");
@@ -369,7 +369,7 @@ public class SequenceTest
   /**
    * Test for deriveSequence applied to an ungapped sequence with no dataset
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeriveSequence_noDatasetUngapped()
   {
     SequenceI seq = new Sequence("Seq1", "ABCDEF");
@@ -384,7 +384,7 @@ public class SequenceTest
   /**
    * Test for deriveSequence applied to a gapped sequence with no dataset
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeriveSequence_noDatasetGapped()
   {
     SequenceI seq = new Sequence("Seq1", "AB-C.D EF");
index 2d9b713..81aae0b 100644 (file)
@@ -31,7 +31,7 @@ public class EmblFileTest
               + "</locationElement></location></feature>"
           + "<sequence type=\"mRNA\" version=\"2\">GTGACG</sequence></entry></EMBL_Services>";
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetEmblFile()
   {
     Vector<EmblEntry> entries = EmblFile.getEmblFile(
index 0627217..301c70d 100644 (file)
@@ -59,7 +59,7 @@ public class PDBFileWithJmolTest
             Boolean.TRUE.toString());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAlignmentLoader() throws Exception
   {
     for (String f : testFile)
@@ -71,7 +71,7 @@ public class PDBFileWithJmolTest
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFileParser() throws Exception
   {
     for (String pdbStr : testFile)
index d8582af..126c221 100644 (file)
@@ -43,7 +43,8 @@ import compbio.util.FileUtil;
 public class TestAnnotate3D
 {
 
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void test1GIDbyId() throws Exception
   {
     // use same ID as standard tests given at
@@ -53,7 +54,8 @@ public class TestAnnotate3D
     testRNAMLcontent(ids, null);
   }
 
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void testIdVsContent2GIS() throws Exception
   {
     Iterator<Reader> ids = Annotate3D.getRNAMLForPDBId("2GIS");
@@ -97,7 +99,8 @@ public class TestAnnotate3D
    * 
    * @throws Exception
    */
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void testPDBfileVsRNAML() throws Exception
   {
     PDBfile pdbf = new PDBfile(true, false, true, "examples/2GIS.pdb",
@@ -111,7 +114,8 @@ public class TestAnnotate3D
     testRNAMLcontent(readers, pdbf);
   }
 
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   private void testRNAMLcontent(Iterator<Reader> readers, PDBfile pdbf)
           throws Exception
   {
index e8b6b19..a3e13e8 100644 (file)
@@ -13,7 +13,7 @@ import org.testng.annotations.Test;
 
 public class ChimeraCommandsTest
 {
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAddColourRange()
   {
     Map<Color, Map<Integer, Map<String, List<int[]>>>> map = new LinkedHashMap<Color, Map<Integer, Map<String, List<int[]>>>>();
@@ -67,7 +67,7 @@ public class ChimeraCommandsTest
       { 5, 9 }, posList.get(0)));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildColourCommands()
   {
 
index d40a43d..e7f95a0 100644 (file)
@@ -11,7 +11,7 @@ import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
 public class ChimeraConnect
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testLaunchAndExit()
   {
     final StructureManager structureManager = new StructureManager(true);
index 59a0fdb..44b5d58 100644 (file)
@@ -42,7 +42,7 @@ public class JalviewChimeraView
 
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSingleSeqViewJMol()
   {
     String inFile = "examples/1gaq.txt";
@@ -86,7 +86,7 @@ public class JalviewChimeraView
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSingleSeqViewChimera()
   {
     String inFile = "examples/1gaq.txt";
index a6e7ea0..b49352b 100644 (file)
@@ -32,7 +32,7 @@ public class AlignViewportTest
     testee = new AlignViewport(al);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCollateForPdb()
   {
     /*
index 3f56985..a7e84b1 100644 (file)
@@ -107,7 +107,7 @@ public class AnnotationChooserTest
   /**
    * Test creation of panel with OK and Cancel buttons
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildActionButtonsPanel()
   {
     testee = new AnnotationChooser(parentPanel);
@@ -136,7 +136,7 @@ public class AnnotationChooserTest
    * Test 'Apply to' has 3 radio buttons enabled, 'Selected Sequences' selected,
    * when there is a current selection group.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildApplyToOptionsPanel_withSelectionGroup()
   {
     selectSequences(0, 2, 3);
@@ -183,7 +183,7 @@ public class AnnotationChooserTest
    * Test 'Apply to' has 1 radio button enabled, 'All Sequences' selected, when
    * there is no current selection group.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildApplyToOptionsPanel_noSelectionGroup()
   {
     testee = new AnnotationChooser(parentPanel);
@@ -224,7 +224,7 @@ public class AnnotationChooserTest
   /**
    * Test Show and Hide radio buttons created, with Hide initially selected.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildShowHidePanel()
   {
     testee = new AnnotationChooser(parentPanel);
@@ -262,7 +262,7 @@ public class AnnotationChooserTest
   /**
    * Test construction of panel containing two sub-panels
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildShowHideOptionsPanel()
   {
     testee = new AnnotationChooser(parentPanel);
@@ -279,7 +279,7 @@ public class AnnotationChooserTest
    * Test that annotation types are (uniquely) identified.
    * 
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetAnnotationTypes()
   {
     selectSequences(1);
@@ -311,7 +311,7 @@ public class AnnotationChooserTest
    * We expect all annotations of that type to be set hidden. Other annotations
    * should be left visible.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSelectType_hideForAll()
   {
     selectSequences(1, 2);
@@ -347,7 +347,7 @@ public class AnnotationChooserTest
    * We expect the annotations of that type, linked to the sequence group, to be
    * set hidden. Other annotations should be left visible.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSelectType_hideForSelected()
   {
     selectSequences(1, 2);
@@ -384,7 +384,7 @@ public class AnnotationChooserTest
    * We expect all annotations of that type to be set visible. Other annotations
    * should be left unchanged.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeselectType_hideForAll()
   {
     selectSequences(1, 2);
@@ -426,7 +426,7 @@ public class AnnotationChooserTest
    * We expect the annotations of that type, linked to the sequence group, to be
    * set visible. Other annotations should be left unchanged.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeselectType_hideForSelected()
   {
     selectSequences(1, 2);
@@ -461,7 +461,7 @@ public class AnnotationChooserTest
    * We expect all annotations of that type to be set visible. Other annotations
    * should be left unchanged
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSelectType_showForAll()
   {
     selectSequences(1, 2);
@@ -500,7 +500,7 @@ public class AnnotationChooserTest
    * We expect all annotations of that type, linked to the sequence group, to be
    * set visible. Other annotations should be left unchanged
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSelectType_showForSelected()
   {
     // sequences 1 and 2 have annotations IUPred and Jmol
@@ -542,7 +542,7 @@ public class AnnotationChooserTest
    * We expect all annotations of that type to be set hidden. Other annotations
    * should be left unchanged.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeselectType_showForAll()
   {
     selectSequences(1, 2);
@@ -583,7 +583,7 @@ public class AnnotationChooserTest
    * We expect the annotations of that type, linked to the sequence group, to be
    * set hidden. Other annotations should be left unchanged.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeselectType_showForSelected()
   {
     selectSequences(1, 2);
@@ -677,7 +677,7 @@ public class AnnotationChooserTest
    * Test cases include sequences in the selection group, and others not in the
    * group.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testIsInActionScope_selectedScope()
   {
     // sequences 1 and 2 have annotations 4 and 3 respectively
@@ -703,7 +703,7 @@ public class AnnotationChooserTest
    * Test cases include sequences in the selection group, and others not in the
    * group.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testIsInActionScope_unselectedScope()
   {
     // sequences 1 and 2 have annotations 4 and 3 respectively
@@ -727,7 +727,7 @@ public class AnnotationChooserTest
   /**
    * Test that the reset method restores previous visibility flags.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testResetOriginalState()
   {
     testee = new AnnotationChooser(parentPanel);
diff --git a/test/jalview/gui/AppVarnaTest.java b/test/jalview/gui/AppVarnaTest.java
new file mode 100644 (file)
index 0000000..94a3300
--- /dev/null
@@ -0,0 +1,24 @@
+package jalview.gui;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertSame;
+
+import org.testng.annotations.Test;
+
+public class AppVarnaTest
+{
+  @Test
+  public void testReplaceOddGaps()
+  {
+    String struct = "{(<]}>)";
+    String replaced = AppVarna.replaceOddGaps(struct);
+    assertEquals(struct, replaced);
+    assertSame(struct, replaced);
+    assertEquals("..{.([.<.].}.>)",
+            AppVarna.replaceOddGaps("..{ ([*<-]?} >)"));
+    assertEquals("....", AppVarna.replaceOddGaps("cgta"));
+    assertEquals("", AppVarna.replaceOddGaps(""));
+    assertNull(AppVarna.replaceOddGaps(null));
+  }
+}
index 646016c..8da11b8 100644 (file)
@@ -15,7 +15,7 @@ public class FontChooserTest
    * available, plain) fonts and point sizes that would be rejected by Jalview's
    * FontChooser as having an I-width of less than 1.0.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void dumpInvalidFonts()
   {
     String[] fonts = java.awt.GraphicsEnvironment
index f280797..bb538a5 100644 (file)
@@ -14,7 +14,7 @@ import org.testng.annotations.Test;
 
 public class HelpTest
 {
-  @Test
+  @Test(groups ={ "Functional" })
   public void checkHelpTargets() throws HelpSetException
   {
     ClassLoader cl = Desktop.class.getClassLoader();
index 4a17622..868e19e 100644 (file)
@@ -54,7 +54,8 @@ public class JAL1353bugdemo
 
   volatile boolean finish = false;
 
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void test()
   {
     Cache.initLogger();
index 1a16053..8a6f63b 100644 (file)
@@ -9,7 +9,7 @@ import org.testng.annotations.Test;
 public class JvSwingUtilsTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetScrollBarProportion()
   {
     /*
@@ -24,7 +24,7 @@ public class JvSwingUtilsTest
     assertEquals(0.25f, JvSwingUtils.getScrollBarProportion(sb), 0.001f);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetScrollValueForProportion()
   {
     /*
index 3a1c554..514fd7a 100644 (file)
@@ -23,7 +23,7 @@ public class PDBSearchPanelTest
   {
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void populateCmbSearchTargetOptionsTest()
   {
     PDBSearchPanel searchPanel = new PDBSearchPanel(null);
@@ -31,7 +31,7 @@ public class PDBSearchPanelTest
     searchPanel.populateCmbSearchTargetOptions();
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDecodeSearchTerm()
   {
     String expectedString = "1xyz OR text:2xyz OR text:3xyz";
@@ -46,7 +46,7 @@ public class PDBSearchPanelTest
     assertEquals(expectedString, outcome);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testgetPDBIdwithSpecifiedChain()
   {
 
@@ -68,7 +68,8 @@ public class PDBSearchPanelTest
     assertEquals(expectedString, outcome);
   }
 
-  @Test
+  @Test(groups =
+  { "Network" }, timeOut = 5000)
   public void txt_search_ActionPerformedTest()
   {
     PDBSearchPanel searchPanel = new PDBSearchPanel(null);
index adbf385..6916cef 100644 (file)
@@ -34,7 +34,7 @@ public class PaintRefresherTest
     PaintRefresher.components.clear();
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testRegister()
   {
     JPanel jp = new JPanel();
@@ -60,7 +60,7 @@ public class PaintRefresherTest
     assertTrue(registered.get("44").contains(jp4));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testRemoveComponent()
   {
     Map<String, List<Component>> registered = PaintRefresher.components;
@@ -92,7 +92,7 @@ public class PaintRefresherTest
     assertTrue(registered.get("22").contains(jp2));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetAssociatedPanels()
   {
     SequenceI [] seqs = new SequenceI[]{new Sequence("", "ABC")};
index ba1b275..f984dfc 100644 (file)
@@ -60,7 +60,7 @@ public class PopupMenuTest
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConfigureReferenceAnnotationsMenu_noSequenceSelected()
   {
     JMenuItem menu = new JMenuItem();
@@ -78,7 +78,7 @@ public class PopupMenuTest
    * are no reference annotations to add to the alignment. The menu item should
    * be disabled.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConfigureReferenceAnnotationsMenu_noReferenceAnnotations()
   {
     JMenuItem menu = new JMenuItem();
@@ -99,7 +99,7 @@ public class PopupMenuTest
    * reference annotations are already on the alignment. The menu item should be
    * disabled.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConfigureReferenceAnnotationsMenu_alreadyAdded()
   {
     JMenuItem menu = new JMenuItem();
@@ -119,7 +119,7 @@ public class PopupMenuTest
    * The menu item should be enabled, and acquire a tooltip which lists the
    * annotation sources (calcIds) and type (labels).
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConfigureReferenceAnnotationsMenu()
   {
     JMenuItem menu = new JMenuItem();
@@ -140,7 +140,7 @@ public class PopupMenuTest
    * on the alignment. The menu item should be enabled, and acquire a tooltip
    * which lists the annotation sources (calcIds) and type (labels).
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConfigureReferenceAnnotationsMenu_notOnAlignment()
   {
     JMenuItem menu = new JMenuItem();
@@ -219,7 +219,7 @@ public class PopupMenuTest
    * The menu item should be enabled, and acquire a tooltip which lists the
    * annotation sources (calcIds) and type (labels).
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConfigureReferenceAnnotationsMenu_twoViews()
   {
   }
@@ -228,7 +228,7 @@ public class PopupMenuTest
    * Test for building menu options including 'show' and 'hide' annotation
    * types.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildAnnotationTypesMenus()
   {
     JMenu showMenu = new JMenu();
@@ -310,7 +310,7 @@ public class PopupMenuTest
   /**
    * Test for building menu options with only 'hide' annotation types enabled.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildAnnotationTypesMenus_showDisabled()
   {
     JMenu showMenu = new JMenu();
@@ -369,7 +369,7 @@ public class PopupMenuTest
   /**
    * Test for building menu options with only 'show' annotation types enabled.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildAnnotationTypesMenus_hideDisabled()
   {
     JMenu showMenu = new JMenu();
index 281ed2d..e50148c 100644 (file)
@@ -20,7 +20,7 @@ public class ProgressBarTest
 
   private JLabel statusBar;
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConstructor_prematureInstantiation()
   {
     try
@@ -33,7 +33,7 @@ public class ProgressBarTest
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConstructor_wrongLayout()
   {
     statusPanel = new JPanel();
@@ -48,7 +48,7 @@ public class ProgressBarTest
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSetProgressBar()
   {
     statusPanel = new JPanel();
index 64d15b8..0f3d4f6 100644 (file)
@@ -15,7 +15,7 @@ import org.testng.annotations.Test;
 public class SequenceRendererTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetResidueBoxColour_zappo()
   {
     SequenceI seq = new Sequence("name", "MATVLGSPRAPAFF"); // FER1_MAIZE...
index 44b51f2..2baee84 100644 (file)
@@ -48,7 +48,7 @@ public class StructureChooserTest
 
 
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void buildQueryTest()
   {
     String query = StructureChooser.buildQuery(seq);
@@ -58,7 +58,7 @@ public class StructureChooserTest
             query);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void populateFilterComboBoxTest()
   {
     SequenceI[] selectedSeqs = new SequenceI[]
@@ -77,7 +77,7 @@ public class StructureChooserTest
                                  // should be populated
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void fetchStructuresInfoTest()
   {
     SequenceI[] selectedSeqs = new SequenceI[]
index 1c6a489..feba9cb 100644 (file)
@@ -47,7 +47,7 @@ public class AnnotatedPDBFileInputTest
             .get(0).getId();
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void checkNoDuplicates()
   {
     // not strictly a requirement, but strange things may happen if multiple
@@ -67,7 +67,7 @@ public class AnnotatedPDBFileInputTest
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void checkPDBannotationSource()
   {
 
@@ -85,7 +85,7 @@ public class AnnotatedPDBFileInputTest
   /**
    * Check sequence features have been added
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void checkPDBSequenceFeatures()
   {
     StructureSelectionManager ssm = StructureSelectionManager
@@ -124,7 +124,7 @@ public class AnnotatedPDBFileInputTest
     assertEquals("TYR:314 1gaqC", sf[295].getDescription());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void checkAnnotationWiring()
   {
     assertTrue(al.getAlignmentAnnotation() != null);
@@ -173,7 +173,7 @@ public class AnnotatedPDBFileInputTest
 
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testJalviewProjectRelocationAnnotation() throws Exception
   {
 
index d36a82b..a63923a 100644 (file)
@@ -52,7 +52,7 @@ public class AnnotationFileIOTest
       { "Test hiding/showing of insertions on sequence_ref",
           "examples/uniref50.fa", "examples/testdata/uniref50_seqref.jva" } };
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void exampleAnnotationFileIO() throws Exception
   {
     for (String[] testPair : TestFiles)
@@ -100,7 +100,7 @@ public class AnnotationFileIOTest
    *          f
    */
 
-  // @Test
+  // @Test(groups ={ "Functional" })
   public static void testAnnotationFileIO(String testname, File f,
           File annotFile)
   {
index 8440b6d..27c8a0e 100644 (file)
@@ -19,7 +19,7 @@ import org.testng.annotations.Test;
 public class BioJsHTMLOutputTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void getJalviewAlignmentAsJsonString()
   {
     String bjsTemplate = null;
@@ -44,7 +44,8 @@ public class BioJsHTMLOutputTest
     Assert.assertNotNull(bjsTemplate);
   }
 
-  @Test(expectedExceptions = NullPointerException.class)
+  @Test(groups =
+  { "Functional" }, expectedExceptions = NullPointerException.class)
   public void expectedNullPointerException()
   {
     try
@@ -57,7 +58,7 @@ public class BioJsHTMLOutputTest
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void getBioJsMSAVersions()
   {
     TreeMap<String, File> versions = null;
@@ -89,7 +90,8 @@ public class BioJsHTMLOutputTest
 
   }
 
-  @Test
+  @Test(groups =
+  { "Network" })
   public void testBioJsUpdate()
   {
     String url = BioJsHTMLOutput.BJS_TEMPLATE_GIT_REPO;
index 6b7de77..0d66b21 100644 (file)
@@ -73,7 +73,7 @@ public class FileIOTester
             + "' identified as '" + type + "'", type.equalsIgnoreCase(fmt));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testStarsInFasta1() throws IOException
   {
     String uri;
@@ -82,7 +82,7 @@ public class FileIOTester
     assertValidFormat("FASTA", uri, fp);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testStarsInFasta2() throws IOException
   {
     String uri;
@@ -91,7 +91,7 @@ public class FileIOTester
     assertValidFormat("FASTA", uri, fp);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGzipIo() throws IOException
   {
     String uri;
@@ -100,7 +100,7 @@ public class FileIOTester
     assertValidFormat("FASTA", uri, fp);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGziplocalFileIO() throws IOException
   {
     String filepath;
@@ -109,7 +109,7 @@ public class FileIOTester
     assertValidFormat("FASTA", filepath, fp);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testNonGzipURLIO() throws IOException
   {
     String uri;
@@ -118,7 +118,7 @@ public class FileIOTester
     assertValidFormat("FASTA", uri, fp);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testNonGziplocalFileIO() throws IOException
   {
     String filepath;
index 8df44df..c8cf638 100644 (file)
@@ -1,5 +1,11 @@
 package jalview.io;
 
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertTrue;
+
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceDummy;
@@ -8,8 +14,7 @@ import jalview.gui.AlignFrame;
 
 import java.io.IOException;
 
-import org.junit.Assert;
-import org.junit.Test;
+import org.testng.annotations.Test;
 
 public class Gff3tests
 {
@@ -18,7 +23,7 @@ public class Gff3tests
           exonerateOutput = "examples/testdata/exonerateoutput.gff",
           simpleGff3file = "examples/testdata/simpleGff3.gff";
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testExonerateImport()
   {
     // exonerate does not tag sequences after features, so we have a more
@@ -29,26 +34,26 @@ public class Gff3tests
     AlignFrame af = loader.LoadFileWaitTillLoaded(exonerateSeqs,
             FormatAdapter.FILE);
 
-    Assert.assertEquals("Unexpected number of DNA protein associations", 0,
+    assertEquals("Unexpected number of DNA protein associations", 0,
             af.getViewport().getAlignment().getCodonFrames().size());
 
     af.loadJalviewDataFile(exonerateOutput, FormatAdapter.FILE, null, null);
 
-    Assert.assertNotEquals("Expected at least one DNA protein association",
-            0, af.getViewport().getAlignment().getDataset()
-                    .getCodonFrames().size());
+    assertTrue("Expected at least one DNA protein association", af
+            .getViewport().getAlignment().getDataset().getCodonFrames()
+            .size() > 0);
 
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void simpleGff3FileIdentify()
   {
-    Assert.assertEquals("Didn't recognise file correctly.",
+    assertEquals("Didn't recognise file correctly.",
             IdentifyFile.GFF3File,
             new IdentifyFile().Identify(simpleGff3file, FormatAdapter.FILE));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void simpleGff3FileClass() throws IOException
   {
     AlignmentI dataset = new Alignment(new SequenceI[]
@@ -57,22 +62,22 @@ public class Gff3tests
             FormatAdapter.FILE);
 
     boolean parseResult = ffile.parse(dataset, null, null, false, false);
-    Assert.assertTrue("return result should be true", parseResult);
+    assertTrue("return result should be true", parseResult);
     checkDatasetfromSimpleGff3(dataset);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void simpleGff3FileLoader() throws IOException
   {
     AlignFrame af = new FileLoader(false).LoadFileWaitTillLoaded(
             simpleGff3file, FormatAdapter.FILE);
-    Assert.assertTrue(
+    assertTrue(
             "Didn't read the alignment into an alignframe from Gff3 File",
             af != null);
     checkDatasetfromSimpleGff3(af.getViewport().getAlignment().getDataset());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void simpleGff3RelaxedIdMatching() throws IOException
   {
     AlignmentI dataset = new Alignment(new SequenceI[]
@@ -81,12 +86,12 @@ public class Gff3tests
             FormatAdapter.FILE);
 
     boolean parseResult = ffile.parse(dataset, null, null, false, true);
-    Assert.assertTrue("return result (relaxedID matching) should be true",
+    assertTrue("return result (relaxedID matching) should be true",
             parseResult);
     checkDatasetfromSimpleGff3(dataset);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void readGff3File() throws IOException
   {
     Gff3File gff3reader = new Gff3File(simpleGff3file, FormatAdapter.FILE);
@@ -98,47 +103,50 @@ public class Gff3tests
 
   private void checkDatasetfromSimpleGff3(AlignmentI dataset)
   {
-    Assert.assertEquals("no sequences extracted from GFF3 file", 2,
+    assertEquals("no sequences extracted from GFF3 file", 2,
             dataset.getHeight());
 
     SequenceI seq1 = dataset.findName("seq1"), seq2 = dataset
             .findName("seq2");
-    Assert.assertNotNull(seq1);
-    Assert.assertNotNull(seq2);
-    Assert.assertFalse(
+    assertNotNull(seq1);
+    assertNotNull(seq2);
+    assertFalse(
             "Failed to replace dummy seq1 with real sequence",
             seq1 instanceof SequenceDummy
                     && ((SequenceDummy) seq1).isDummy());
-    Assert.assertFalse(
+    assertFalse(
             "Failed to replace dummy seq2 with real sequence",
             seq2 instanceof SequenceDummy
                     && ((SequenceDummy) seq2).isDummy());
     String placeholderseq = new SequenceDummy("foo").getSequenceAsString();
-    Assert.assertFalse("dummy replacement buggy for seq1",
+    assertFalse("dummy replacement buggy for seq1",
             placeholderseq.equals(seq1.getSequenceAsString()));
-    Assert.assertNotEquals("dummy replacement buggy for seq2",
+    assertFalse("dummy replacement buggy for seq2",
             placeholderseq.equals(seq2.getSequenceAsString()));
-    Assert.assertNotNull("No features added to seq1",
+    assertNotNull("No features added to seq1",
             seq1.getSequenceFeatures());// != null);
-    Assert.assertEquals("Wrong number of features", 3,
+    assertEquals("Wrong number of features", 3,
             seq1.getSequenceFeatures().length);
-    Assert.assertNull(seq2.getSequenceFeatures());
-    Assert.assertEquals("Wrong number of features", 0, seq2
+    assertNull(seq2.getSequenceFeatures());
+    assertEquals(
+            "Wrong number of features",
+            0,
+            seq2
             .getSequenceFeatures() == null ? 0
             : seq2.getSequenceFeatures().length);
-    Assert.assertTrue(
+    assertTrue(
             "Expected at least one CDNA/Protein mapping for seq1",
             dataset.getCodonFrame(seq1) != null
                     && dataset.getCodonFrame(seq1).size() > 0);
 
   }
-  // @Test
+  // @Test(groups ={ "Functional" })
   // public final void testPrintGFFFormatSequenceIArrayMapOfStringObject()
   // {
   // fail("Not yet implemented");
   // }
   //
-  // @Test
+  // @Test(groups ={ "Functional" })
   // public final void testAlignFileBooleanStringString()
   // {
   // fail("Not yet implemented");
index eca3984..10d1057 100644 (file)
@@ -7,7 +7,8 @@ import org.testng.annotations.Test;
 public class HtmlFileTest
 {
 
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void test()
   {
     fail("Not yet implemented");
index a695d37..108dd31 100644 (file)
@@ -246,13 +246,13 @@ public class JSONFileTest
     jf = null;
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void roundTripTest()
   {
     assertNotNull("JSON roundtrip test failed!", testJsonFile);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSeqParsed()
   {
     assertNotNull("Couldn't read supplied alignment data.", testAlignment);
@@ -269,7 +269,7 @@ public class JSONFileTest
             TEST_SEQ_HEIGHT, passedCount);
   }
   
-  @Test
+  @Test(groups ={ "Functional" })
   public void hiddenColsTest()
   {
     ColumnSelection cs = testJsonFile.getColumnSelection();
@@ -281,7 +281,7 @@ public class JSONFileTest
             "Mismatched hidden columns!");
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void hiddenSeqsTest()
   {
     Assert.assertNotNull(testJsonFile.getHiddenSequences(),
@@ -290,7 +290,7 @@ public class JSONFileTest
             "Hidden sequece");
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void colorSchemeTest()
   {
     Assert.assertNotNull(testJsonFile.getColourScheme(),
@@ -300,14 +300,14 @@ public class JSONFileTest
             "Zappo colour scheme expected!");
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void isShowSeqFeaturesSet()
   {
     Assert.assertTrue(testJsonFile.isShowSeqFeatures(),
             "Sequence feature isDisplayed setting expected to be true");
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGrpParsed()
   {
     Assert.assertNotNull(testAlignment.getGroups());
@@ -323,7 +323,7 @@ public class JSONFileTest
             TEST_GRP_HEIGHT, passedCount);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAnnotationParsed()
   {
     Assert.assertNotNull(testAlignment.getAlignmentAnnotation());
index 7d24727..c03dd71 100644 (file)
@@ -83,7 +83,7 @@ public class Jalview2xmlTests
     return numdsann;
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testRNAStructureRecovery() throws Exception
   {
     String inFile = "examples/RF00031_folded.stk";
@@ -118,7 +118,7 @@ public class Jalview2xmlTests
             af.getViewport().getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testTCoffeeScores() throws Exception
   {
     String inFile = "examples/uniref50.fa", inAnnot = "examples/uniref50.score_ascii";
@@ -154,7 +154,7 @@ public class Jalview2xmlTests
             .println("T-Coffee score shading successfully recovered from project.");
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testColourByAnnotScores() throws Exception
   {
     String inFile = "examples/uniref50.fa", inAnnot = "examples/testdata/uniref50_iupred.jva";
@@ -240,7 +240,7 @@ public class Jalview2xmlTests
             .println("Per sequence (Group) colourscheme successfully applied and recovered.");
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void gatherViewsHere() throws Exception
   {
     int origCount = Desktop.getAlignFrames() == null ? 0 : Desktop
@@ -253,7 +253,7 @@ public class Jalview2xmlTests
 
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void viewRefPdbAnnotation() throws Exception
   {
     Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
@@ -315,7 +315,7 @@ public class Jalview2xmlTests
     
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCopyViewSettings() throws Exception
   {
     AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
index a016134..a783b1e 100644 (file)
@@ -89,7 +89,7 @@ public class NewickFileTests
   {
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testTreeIO() throws Exception
   {
     String stage = "Init", treename = " '" + name + "' :";
index d335731..9c95486 100644 (file)
@@ -79,7 +79,7 @@ public class PhylipFileTests
    * 
    * @throws Exception
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSequentialDataExtraction() throws Exception
   {
     testDataExtraction(sequentialFile);
@@ -91,7 +91,7 @@ public class PhylipFileTests
    * 
    * @throws Exception
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testInterleavedDataExtraction() throws Exception
   {
     testDataExtraction(interleavedFile);
@@ -124,7 +124,7 @@ public class PhylipFileTests
    * 
    * @throws Exception
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSequentialIO() throws Exception
   {
     testIO(sequentialFile);
@@ -136,7 +136,7 @@ public class PhylipFileTests
    * 
    * @throws Exception
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testInterleavedIO() throws Exception
   {
     testIO(interleavedFile);
index d684512..d9d0b7f 100644 (file)
@@ -39,7 +39,7 @@ public class RNAMLfileTest
   {
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testRnamlToStockholmIO()
   {
     StockholmFileTest.testFileIOwithFormat(new File(
index 45de531..2537002 100644 (file)
@@ -43,13 +43,13 @@ public class StockholmFileTest
   static String PfamFile = "examples/PF00111_seed.stk",
           RfamFile = "examples/RF00031_folded.stk";
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void pfamFileIO() throws Exception
   {
     testFileIOwithFormat(new File(PfamFile), "STH", -1, 0);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void pfamFileDataExtraction() throws Exception
   {
     AppletFormatAdapter af = new AppletFormatAdapter();
@@ -68,7 +68,7 @@ public class StockholmFileTest
             numpdb > 0);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void rfamFileIO() throws Exception
   {
     testFileIOwithFormat(new File(RfamFile), "STH", 2, 1);
index b7da5e4..7911188 100644 (file)
@@ -40,7 +40,7 @@ public class TCoffeeScoreFileTest
   final static File ALIGN_FILE = new File(
           "test/jalview/io/tcoffee.fasta_aln");
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testReadHeader() throws IOException
   {
 
@@ -65,7 +65,7 @@ public class TCoffeeScoreFileTest
     AssertJUnit.assertEquals(90, header.getScoreFor("cons"));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testWrongFile()
   {
     try
@@ -80,7 +80,7 @@ public class TCoffeeScoreFileTest
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testHeightAndWidth() throws IOException
   {
     TCoffeeScoreFile result = new TCoffeeScoreFile(SCORE_FILE.getPath(),
@@ -90,7 +90,7 @@ public class TCoffeeScoreFileTest
     AssertJUnit.assertEquals(83, result.getWidth());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testReadBlock() throws IOException
   {
 
@@ -129,7 +129,7 @@ public class TCoffeeScoreFileTest
             block.getConsensus());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testParse() throws IOException
   {
 
@@ -165,7 +165,7 @@ public class TCoffeeScoreFileTest
             parser.getScoresFor("cons"));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetAsList() throws IOException
   {
 
@@ -203,7 +203,7 @@ public class TCoffeeScoreFileTest
 
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetAsArray() throws IOException
   {
 
@@ -234,7 +234,7 @@ public class TCoffeeScoreFileTest
 
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testHeightAndWidthWithResidueNumbers() throws Exception
   {
     String file = "test/jalview/io/tcoffee.score_ascii_with_residue_numbers";
index 75877ee..3d20190 100644 (file)
@@ -29,13 +29,13 @@ import org.testng.annotations.Test;
 public class DnaCodonTests
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAmbiguityCodeGeneration()
   {
     assertTrue(ResidueProperties.ambiguityCodes.size() > 0);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAmbiguityCodon()
   {
     for (String ac : ResidueProperties.ambiguityCodes.keySet())
@@ -45,7 +45,7 @@ public class DnaCodonTests
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void regenerateCodonTable()
   {
     for (Map.Entry<String, String> codon : ResidueProperties.codonHash2
@@ -56,7 +56,7 @@ public class DnaCodonTests
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void checkOldCodonagainstNewCodonTable()
   {
     // note - this test will be removed once the old codon table (including
index d7e0e0b..eb2ad45 100644 (file)
@@ -14,7 +14,7 @@ public class ResiduePropertiesTest
   /**
    * Test 'standard' codon translations (no ambiguity codes)
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCodonTranslate()
   {
     // standard translation table order column 1/2/3/4
@@ -88,7 +88,7 @@ public class ResiduePropertiesTest
    * Test a sample of codon translations involving ambiguity codes. Should
    * return a protein value where the ambiguity does not affect the translation.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCodonTranslate_ambiguityCodes()
   {
     // Y is C or T
@@ -175,7 +175,7 @@ public class ResiduePropertiesTest
     assertNull(ResidueProperties.codonTranslate("WSK"));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetResidues_nucleotide()
   {
     /*
@@ -194,7 +194,7 @@ public class ResiduePropertiesTest
     assertEquals("[A, C, G, I, N, R, T, U, X, Y]", residues.toString());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetResidues_peptide()
   {
     /*
index 0b15b60..f00ac8d 100644 (file)
@@ -28,7 +28,7 @@ import org.testng.annotations.Test;
 
 public class ScoreMatrixPrinter
 {
-  @Test
+  @Test(groups ={ "Functional" })
   public void printAllMatrices()
   {
     for (Map.Entry<String, ScoreModelI> sm : ResidueProperties.scoreMatrices
@@ -39,7 +39,7 @@ public class ScoreMatrixPrinter
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void printHTMLMatrices()
   {
     for (Map.Entry<String, ScoreModelI> _sm : ResidueProperties.scoreMatrices
index db0ea49..c057980 100644 (file)
@@ -27,7 +27,8 @@ public class Mapping
    * 115 in PDB Res Numbering secondary structure numbers in jmol seem to be in
    * msd numbering, not pdb res numbering.
    */
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void pdbEntryPositionMap() throws Exception
   {
     Assert.fail("This test intentionally left to fail");
@@ -109,7 +110,8 @@ public class Mapping
     }
   }
 
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void testPDBentryMapping() throws Exception
   {
     Assert.fail("This test intentionally left to fail");
@@ -210,7 +212,7 @@ public class Mapping
    * transform
    * 
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void mapFer1From3W5V() throws Exception
   {
     AlignFrame seqf = new FileLoader(false)
@@ -233,7 +235,7 @@ public class Mapping
    * compare reference annotation for imported pdb sequence to identical
    * seuqence with transferred annotation from mapped pdb file
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void compareTransferredToRefPDBAnnot() throws Exception
   {
     AlignFrame ref = new FileLoader(false)
index 589adde..1e4a866 100644 (file)
@@ -21,7 +21,7 @@ public class StructureSelectionManagerTest
     ssm = new StructureSelectionManager();
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAddMapping()
   {
     AlignedCodonFrame acf1 = new AlignedCodonFrame();
@@ -59,7 +59,7 @@ public class StructureSelectionManagerTest
     assertEquals(1, ssm.seqMappingRefCounts.get(acf2).intValue());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testAddMappings()
   {
     AlignedCodonFrame acf1 = new AlignedCodonFrame();
@@ -89,7 +89,7 @@ public class StructureSelectionManagerTest
     assertEquals(1, ssm.seqMappingRefCounts.get(acf3).intValue());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testRemoveMapping()
   {
     AlignedCodonFrame acf1 = new AlignedCodonFrame();
@@ -127,7 +127,7 @@ public class StructureSelectionManagerTest
     assertEquals(0, ssm.seqMappingRefCounts.size());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testRemoveMappings()
   {
     AlignedCodonFrame acf1 = new AlignedCodonFrame();
index 7871e77..c5d1db9 100644 (file)
@@ -119,7 +119,7 @@ public class AAStructureBindingModelTest
    * Verify that the method determines that columns 2, 5 and 6 of the aligment
    * are alignable in structure
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindSuperposableResidues()
   {
     SuperposeData[] structs = new SuperposeData[al.getHeight()];
@@ -150,7 +150,7 @@ public class AAStructureBindingModelTest
     assertTrue(matched[5]);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindSuperposableResidues_hiddenColumn()
   {
     SuperposeData[] structs = new SuperposeData[al.getHeight()];
index ad6e6d0..6d8dded 100644 (file)
@@ -16,7 +16,7 @@ public class ColorUtilsTest
 
   Color darkColour = new Color(11, 30, 50); // dark blue
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDarkerThan()
   {
     assertEquals("Wrong darker shade", new Color(32, 69, 37),
@@ -28,7 +28,7 @@ public class ColorUtilsTest
     assertNull(ColorUtils.darkerThan(null));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBrighterThan()
   {
     assertEquals("Wrong brighter shade", new Color(255, 255, 255), // white
@@ -43,7 +43,7 @@ public class ColorUtilsTest
   /**
    * @see http://www.rtapo.com/notes/named_colors.html
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testToTkCode()
   {
     assertEquals("#fffafa", ColorUtils.toTkCode(new Color(255, 250, 250))); // snow
index 837cbe6..d8efa69 100644 (file)
@@ -12,7 +12,7 @@ import org.testng.annotations.Test;
 public class ComparisonTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testIsGap()
   {
     assertTrue(Comparison.isGap('-'));
@@ -28,7 +28,7 @@ public class ComparisonTest
    * Test for isNucleotide is that sequences in a dataset are more than 85%
    * AGCTU. Test is not case-sensitive and ignores gaps.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testIsNucleotide() {
     SequenceI seq = new Sequence("eightypercent", "agctuAGCPV");
     assertFalse(Comparison.isNucleotide(new SequenceI[]
@@ -94,7 +94,7 @@ public class ComparisonTest
   /**
    * Test percentage identity calculation for two sequences.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testPID_matchGaps()
   {
     String seq1 = "ABCDEF";
index 036ad91..b06132b 100644 (file)
@@ -22,7 +22,7 @@ public class DBRefUtilsTest
    * Test the method that selects DBRefEntry items whose source is in a supplied
    * list
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSelectRefs()
   {
     assertNull(DBRefUtils.selectRefs(null, null));
@@ -66,7 +66,7 @@ public class DBRefUtilsTest
    * Test the method that converts (currently three) database names to a
    * canonical name (not case-sensitive)
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetCanonicalName()
   {
     assertNull(DBRefUtils.getCanonicalName(null));
@@ -83,7 +83,7 @@ public class DBRefUtilsTest
             DBRefUtils.getCanonicalName("UNIPROTKB/SWISS-CHEESE"));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testIsDasCoordinateSystem()
   {
     assertFalse(DBRefUtils.isDasCoordinateSystem(null, null));
@@ -118,7 +118,7 @@ public class DBRefUtilsTest
   /**
    * Test 'parsing' a DBRef - non PDB case
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testParseToDbRef()
   {
     SequenceI seq = new Sequence("Seq1", "ABCD");
@@ -135,7 +135,7 @@ public class DBRefUtilsTest
   /**
    * Test 'parsing' a DBRef - Stockholm PDB format
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testParseToDbRef_PDB()
   {
     SequenceI seq = new Sequence("Seq1", "ABCD");
@@ -160,7 +160,7 @@ public class DBRefUtilsTest
    * Test the method that searches for matches references - case when we are
    * matching a reference with no mappings
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSearchRefs_noMapping()
   {
     DBRefEntry target = new DBRefEntry("EMBL", "2", "A1234");
@@ -189,7 +189,7 @@ public class DBRefUtilsTest
    * Test the method that searches for matches references - case when we are
    * matching a reference with a mapping
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSearchRefs_withMapping()
   {
     DBRefEntry target = new DBRefEntry("EMBL", "2", "A1234");
index 5579efb..6e7e19e 100644 (file)
@@ -14,7 +14,7 @@ import org.testng.annotations.Test;
 public class MapListTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSomething()
   {
     MapList ml = new MapList(new int[]
@@ -219,7 +219,7 @@ public class MapListTest
    * Tests for method that locates ranges in the 'from' map for given range in
    * the 'to' map.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testLocateInFrom_noIntrons()
   {
     /*
@@ -251,7 +251,7 @@ public class MapListTest
    * Tests for method that locates ranges in the 'from' map for given range in
    * the 'to' map.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testLocateInFrom_withIntrons()
   {
     /*
@@ -274,7 +274,7 @@ public class MapListTest
    * Tests for method that locates ranges in the 'to' map for given range in the
    * 'from' map.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testLocateInTo_noIntrons()
   {
     /*
@@ -314,7 +314,7 @@ public class MapListTest
    * Tests for method that locates ranges in the 'to' map for given range in the
    * 'from' map.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testLocateInTo_withIntrons()
   {
     /*
@@ -357,7 +357,7 @@ public class MapListTest
   /**
    * Test equals method.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testEquals()
   {
     int[] codons = new int[]
@@ -406,7 +406,7 @@ public class MapListTest
   /**
    * Test for the method that flattens a list of ranges into a single array.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetRanges()
   {
     List<int[]> ranges = new ArrayList<int[]>();
@@ -420,7 +420,7 @@ public class MapListTest
   /**
    * Check state after construction
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConstructor()
   {
     int[] codons =
@@ -478,7 +478,7 @@ public class MapListTest
   /**
    * Test the method that creates an inverse mapping
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetInverse()
   {
     int[] codons =
@@ -498,7 +498,7 @@ public class MapListTest
             prettyPrint(ml2.getFromRanges()));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testToString()
   {
     MapList ml = new MapList(new int[]
index 9985fb3..2c0045b 100644 (file)
@@ -36,7 +36,7 @@ public class MappingUtilsTest
   /**
    * Simple test of mapping with no intron involved.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildSearchResults()
   {
     final Sequence seq1 = new Sequence("Seq1", "C-G-TA-GC");
@@ -89,7 +89,7 @@ public class MappingUtilsTest
   /**
    * Simple test of mapping with introns involved.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testBuildSearchResults_withIntron()
   {
     final Sequence seq1 = new Sequence("Seq1", "C-G-TAGA-GCAGCTT");
@@ -166,7 +166,7 @@ public class MappingUtilsTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMapSequenceGroup_sequences() throws IOException
   {
     /*
@@ -259,7 +259,7 @@ public class MappingUtilsTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMapColumnSelection_proteinToDna() throws IOException
   {
     setupMappedAlignments();
@@ -357,7 +357,7 @@ public class MappingUtilsTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMapColumnSelection_dnaToProtein() throws IOException
   {
     setupMappedAlignments();
@@ -384,7 +384,7 @@ public class MappingUtilsTest
     assertEquals("[0, 1, 3]", cs.getSelected().toString());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMapColumnSelection_null() throws IOException
   {
     setupMappedAlignments();
@@ -397,7 +397,7 @@ public class MappingUtilsTest
    * Tests for the method that converts a series of [start, end] ranges to
    * single positions
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFlattenRanges()
   {
     assertEquals("[1, 2, 3, 4]",
@@ -423,7 +423,7 @@ public class MappingUtilsTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMapSequenceGroup_columns() throws IOException
   {
     /*
@@ -506,7 +506,7 @@ public class MappingUtilsTest
    * 
    * @throws IOException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testMapSequenceGroup_region() throws IOException
   {
     /*
@@ -600,7 +600,7 @@ public class MappingUtilsTest
     assertEquals(4, mappedGroup.getEndRes());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testFindMappingsForSequence()
   {
     SequenceI seq1 = new Sequence("Seq1", "ABC");
index 7d10758..fac463a 100644 (file)
@@ -30,7 +30,7 @@ public class QuickSortTest
     { c1, c2, c3, c4 };
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSort_byIntValues()
   {
     int[] values = new int[]
@@ -41,7 +41,7 @@ public class QuickSortTest
     assertTrue(Arrays.equals(sortedThings, things));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSort_byFloatValues()
   {
     float[] values = new float[]
@@ -52,7 +52,7 @@ public class QuickSortTest
     assertTrue(Arrays.equals(sortedThings, things));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSort_byDoubleValues()
   {
     double[] values = new double[]
@@ -66,7 +66,7 @@ public class QuickSortTest
   /**
    * Sort by String is descending order, case-sensitive
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSort_byStringValues()
   {
     String[] values = new String[]
@@ -81,7 +81,8 @@ public class QuickSortTest
   /**
    * Test whether sort is stable i.e. equal values retain their mutual ordering.
    */
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void testSort_withDuplicates()
   {
     int[] values = new int[]
@@ -99,7 +100,7 @@ public class QuickSortTest
    * Test that exercises sort with a mostly zero-valued sortby array. May be of
    * interest to check the sort algorithm is efficient.
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSort_MostlyZeroValues()
   {
     char[] residues = new char[64];
index ea9b2e4..eae2811 100644 (file)
@@ -11,7 +11,7 @@ import org.testng.annotations.Test;
 public class ShiftListTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testParseMap()
   {
     assertNull(ShiftList.parseMap(null));
index 7442530..e8de3bd 100644 (file)
@@ -11,7 +11,7 @@ import org.testng.annotations.Test;
 public class StringUtilsTest
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testInsertCharAt()
   {
     char[] c1 = "ABC".toCharArray();
@@ -31,7 +31,7 @@ public class StringUtilsTest
             StringUtils.insertCharAt(c1, 7, 2, 'w')));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDeleteChars()
   {
     char[] c1 = "ABC".toCharArray();
@@ -56,7 +56,7 @@ public class StringUtilsTest
     assertTrue(Arrays.equals(c1, StringUtils.deleteChars(c1, 3, 4)));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetLastToken()
   {
     assertNull(StringUtils.getLastToken(null, null));
@@ -69,7 +69,7 @@ public class StringUtilsTest
             "file://localhost:8080/data/examples/file1.dat", "/"));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testSeparatorListToArray()
   {
     String[] result = StringUtils.separatorListToArray(
@@ -90,7 +90,7 @@ public class StringUtilsTest
             .separatorListToArray("abc='|'d|ef|g", "|")));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testArrayToSeparatorList()
   {
     assertEquals("*", StringUtils.arrayToSeparatorList(null, "*"));
index 64b15a6..9e259a9 100644 (file)
@@ -30,7 +30,7 @@ public class ViewStyleTest
    * @throws IllegalAccessException
    * @throws IllegalArgumentException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCopyConstructor() throws IllegalArgumentException,
           IllegalAccessException
   {
@@ -165,7 +165,7 @@ public class ViewStyleTest
    * @throws IllegalAccessException
    * @throws IllegalArgumentException
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testEquals() throws IllegalArgumentException,
           IllegalAccessException
   {
index ef03fd0..e54d5b9 100644 (file)
@@ -49,7 +49,8 @@ public class PDBSequenceFetcherTest
     sf = new SequenceFetcher(false);
   }
 
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void testRnaSeqRetrieve() throws Exception
   {
     List<DbSourceProxy> sps = sf.getSourceProxy("PDB");
index 3b3ef86..00c1e02 100644 (file)
@@ -44,7 +44,8 @@ public class PDBRestClientTest
   {
   }
 
-  @Test
+  @Test(groups =
+  { "External" })
   public void executeRequestTest()
   {
     List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
@@ -76,7 +77,7 @@ public class PDBRestClientTest
     assertTrue(response.getSearchSummary().size() > 99);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void getPDBDocFieldsAsCommaDelimitedStringTest()
   {
     List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
@@ -93,7 +94,8 @@ public class PDBRestClientTest
     assertEquals("", expectedResult, actualResult);
   }
 
-  @Test
+  @Test(groups =
+  { "External" })
   public void parsePDBJsonExceptionStringTest()
   {
     List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
@@ -131,7 +133,8 @@ public class PDBRestClientTest
     assertEquals(expectedErrorMsg, parsedErrorResponse);
   }
 
-  @Test(expectedExceptions = Exception.class)
+  @Test(groups =
+  { "External" }, expectedExceptions = Exception.class)
   public void testForExpectedRuntimeException() throws Exception
   {
     List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
@@ -144,7 +147,8 @@ public class PDBRestClientTest
     new PDBRestClient().executeRequest(request);
   }
 
-  @Test
+  @Test(groups =
+  { "External" })
   public void parsePDBJsonResponseTest()
   {
     List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
@@ -173,7 +177,7 @@ public class PDBRestClientTest
     assertTrue(response.getSearchSummary().size() == 14);
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void getPDBIdColumIndexTest()
   {
     List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
@@ -186,7 +190,8 @@ public class PDBRestClientTest
     assertEquals(4, PDBRestClient.getPDBIdColumIndex(wantedFields, false));
   }
 
-  @Test
+  @Test(groups =
+  { "External" })
   public void externalServiceIntegrationTest()
   {
     ClientConfig clientConfig = new DefaultClientConfig();
index 9fba1cb..c89324b 100644 (file)
@@ -35,7 +35,7 @@ public class UniprotTest
   /**
    * Test the method that unmarshals XML to a Uniprot model
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetUniprotEntries()
   {
     Uniprot u = new Uniprot();
@@ -108,7 +108,7 @@ public class UniprotTest
   /**
    * Test the method that formats the sequence name in Fasta style
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testConstructSequenceFastaHeader()
   {
     Uniprot u = new Uniprot();
index a3ae56d..b22f0f7 100644 (file)
@@ -74,7 +74,8 @@ public class Jws2ParamView
    * rather than hang
    */
 
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void testJws2Gui()
   {
     Iterator<String> presetEnum = presetTests.iterator();
index 470c39b..03fca21 100644 (file)
@@ -86,7 +86,7 @@ public class DisorderAnnotExportImport
   /**
    * test for patches to JAL-1294
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDisorderAnnotExport()
   {
     disorderClient = new AADisorderClient(iupreds.get(0), af, null, null);
index ec901da..e6b7c58 100644 (file)
@@ -51,7 +51,8 @@ public class JalviewJabawsTestUtils
   { "http://localhost:8080/jabaws",
       "http://www.compbio.dundee.ac.uk/jabaws" };
 
-  @Test(enabled = false)
+  @Test(groups =
+  { "Functional" }, enabled = false)
   public void testAnnotExport()
   {
     fail("Not yet implemented");
index 49a3064..e347e55 100644 (file)
@@ -71,7 +71,6 @@ public class JpredJabaStructExportImport
 
     for (Jws2Instance svc : disc.getServices())
     {
-
       if (svc.getServiceTypeURI().toLowerCase().contains("jpred"))
       {
         jpredws = svc;
@@ -79,18 +78,10 @@ public class JpredJabaStructExportImport
     }
 
     System.out.println("State of jpredws: " + jpredws);
-
-    if (jpredws == null)
-    {
-      Assert.fail("jpredws is null");
-    }
-
+    Assert.assertNotNull(jpredws, "jpredws is null!");
     jalview.io.FileLoader fl = new jalview.io.FileLoader(false);
-
     af = fl.LoadFileWaitTillLoaded(testseqs, jalview.io.FormatAdapter.FILE);
-
     assertNotNull("Couldn't load test data ('" + testseqs + "')", af);
-
   }
 
   @AfterClass
@@ -103,7 +94,7 @@ public class JpredJabaStructExportImport
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testJPredStructOneSeqOnly()
   {
     af.selectAllSequenceMenuItem_actionPerformed(null);
@@ -155,7 +146,7 @@ public class JpredJabaStructExportImport
 
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testJPredStructExport()
   {
 
@@ -226,7 +217,7 @@ public class JpredJabaStructExportImport
             + "\nCouldn't complete Annotation file roundtrip input/output/input test.");
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testJpredwsSettingsRecovery()
   {
     Assert.fail("not implemnented");
index 0a50e47..61f8203 100644 (file)
@@ -23,7 +23,8 @@ public class MinJabawsClientTests
         * @throws Exception
         */
        @SuppressWarnings("rawtypes")
-       @Test
+  @Test(groups =
+  { "Network" })
        public void msaTest() throws Exception {
                String url;
                RegistryWS registry = Jws2Client
index 5d9773a..dc33cbf 100644 (file)
@@ -118,7 +118,7 @@ public class RNAStructExportImport
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testRNAAliFoldValidStructure()
   {
 
@@ -152,7 +152,7 @@ public class RNAStructExportImport
     }
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testRNAStructExport()
   {
 
@@ -223,7 +223,7 @@ public class RNAStructExportImport
             + "\nCouldn't complete Annotation file roundtrip input/output/input test.");
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testRnaalifoldSettingsRecovery()
   {
     List<compbio.metadata.Argument> opts = new ArrayList<compbio.metadata.Argument>();
index 3403446..d76a532 100644 (file)
@@ -59,7 +59,7 @@ public class ParameterUtilsTest
     disc = JalviewJabawsTestUtils.getJabawsDiscoverer();
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testWriteParameterSet() throws WrongParameterException
   {
     for (Jws2Instance service : disc.getServices())
@@ -119,7 +119,7 @@ public class ParameterUtilsTest
             || serviceTests.contains(service.serviceType.toLowerCase());
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCopyOption()
   {
     for (Jws2Instance service : disc.getServices())
@@ -143,7 +143,7 @@ public class ParameterUtilsTest
 
   /**
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testCopyParameter()
   {
     for (Jws2Instance service : disc.getServices())
index 70071a5..0c96c48 100644 (file)
@@ -13,7 +13,7 @@ public class RestClientTest
   /**
    * Refactored 'as is' from main method
    */
-  @Test
+  @Test(groups ={ "Functional" })
   public void testGetRestClient()
   {
     /*
index 99135d9..1f807b3 100644 (file)
@@ -36,7 +36,7 @@ import org.testng.annotations.Test;
 public class ShmmrRSBSService
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testShmmrService()
   {
 
@@ -46,7 +46,7 @@ public class ShmmrRSBSService
                     RestClient.makeShmmrRestClient().service));
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testShmmrServiceDataprep() throws Exception
   {
     RestClient _rc = RestClient.makeShmmrRestClient();
index c830f23..148cb9e 100644 (file)
@@ -6,7 +6,7 @@ import org.testng.annotations.Test;
 public class DasSequenceFetcher
 {
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testDasRegistryContact()
   {
     jalview.bin.Cache.getDasSourceRegistry().refreshSources();
index d6bd4b0..abaa7ec 100644 (file)
@@ -62,7 +62,7 @@ public class DbRefFetcherTest
   {
   }
 
-  @Test
+  @Test(groups ={ "Functional" })
   public void testStandardProtDbs()
   {
     String[] defdb = DBRefSource.PROTEINDBS;
@@ -99,7 +99,8 @@ public class DbRefFetcherTest
             uniprot != null && i < 2);
   }
 
-  @Test
+  @Test(groups =
+  { "External" })
   public void testEmblUniprotProductRecovery() throws Exception
   {
     String retrievalId = "CAA23748"; // "V00488";
@@ -108,7 +109,8 @@ public class DbRefFetcherTest
     verifyProteinNucleotideXref(retrievalId, embl);
   }
 
-  @Test
+  @Test(groups =
+  { "External" })
   public void testEmblCDSUniprotProductRecovery() throws Exception
   {
     String retrievalId = "AAH29712";
index ac30366..14b372f 100755 (executable)
@@ -1840,7 +1840,7 @@ and any path to a file to save to the file]]></string>
                                                                <string><![CDATA[664]]></string>
                                                        </property>
                                                        <property name="sourceName">
-                                                               <string><![CDATA[VARNAv3-91.jar]]></string>
+                                                               <string><![CDATA[VARNAv3-93.jar]]></string>
                                                        </property>
                                                        <property name="overrideUnixPermissions">
                                                                <boolean>false</boolean>
@@ -1858,7 +1858,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>true</boolean>
                                                        </property>
                                                        <property name="destinationName">
-                                                               <string><![CDATA[VARNAv3-91.jar]]></string>
+                                                               <string><![CDATA[VARNAv3-93.jar]]></string>
                                                        </property>
                                                        <property name="fileSize">
                                                                <long>663408</long>