CVE-2018-25087 (Python, published 2023-06-06T03:15Z)
Description: value: A vulnerability classified as problematic was found in Arborator Server. This vulnerability affects the function start of the file project.cgi. The manipulation of the argument project leads to denial of service. Continious delivery with rolling releases is used by this product. Therefore, no version details of affected nor updated releases are available. The patch is identified as cdbdbcbd491db65e9d697ab4365605fdfab1a604. It is recommended to apply a patch to fix this issue. VDB-230662 is the identifier assigned to this vulnerability.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-404 \u2014 Improper Resource Shutdown or Release
The product does not release or incorrectly releases a resource before it is made available for re-use.
Repository: https://github.com/Arborator/arborator-server
Files changed:
HOWTO.txt
LICENSE
README.md
annodoc/annodoc.js
annodoc/annotation_log.js
annodoc/collections.js
annodoc/config.js
annodoc/configuration.js
annodoc/conllu.js
annodoc/dispatcher.js
annodoc/font-awesome.css
annodoc/font-awesome.min.css
annodoc/head.js
annodoc/head.load.min.js
annodoc/hint.css
annodoc/jquery-ui-redmond.css
annodoc/jquery-ui.js
annodoc/jquery-ui.min.js
annodoc/jquery.address.min.js
annodoc/jquery.html
annodoc/jquery.js
annodoc/jquery.min.js
annodoc/jquery.svg.min.js
annodoc/jquery.svgdom.min.js
annodoc/jquery.timeago.js
annodoc/jquery_002.js
annodoc/jquery_003.js
annodoc/jquery_004.js
annodoc/jquery_005.js
annodoc/js.rests.kim.js
annodoc/style-vis.css
annodoc/style.css
annodoc/url_monitor.js
annodoc/util.js
annodoc/visualizer.js
annodoc/waypoints.js
annodoc/waypoints.min.js
annodoc/webfont.js
configCheck.cgi
configedit.cgi
connect.cgi
convert_svg.cgi
corpus/CoNLL.dep.tags
corpus/PTB.tags
css/arborator.css
css/colpick.css
css/jquery-linedtextarea.css
css/jquery-ui-1.8.18.custom.css
css/jquery.fileupload-ui.css
depedit.cgi
depedit.html
editor.cgi
eraseTree.cgi
export.cgi
export/batik-1.7/changes
export/batik-1.7/lib/LICENSE.dom-documentation.txt
export/batik-1.7/lib/LICENSE.dom-software.txt
export/batik-1.7/lib/LICENSE.js.txt
export/batik-1.7/lib/LICENSE.pdf-transcoder.txt
export/batik-1.7/lib/LICENSE.sax.txt
export/batik-1.7/lib/LICENSE.xalan-2.6.0.txt
export/batik-1.7/lib/LICENSE.xerces_2_5_0.txt
export/batik-1.7/lib/README.js.txt
export/batik-1.7/lib/README.pdf-transcoder.txt
export/batik-1.7/lib/README.xalan-2.6.0.txt
export/batik-1.7/lib/README.xerces_2_5_0.txt
export/batik-1.7/lib/README.xml-apis-ext.txt
export/batik-1.7/lib/README.xml-apis.txt
export/batik-1.7/lib/make-squiggle-app.sh
export/batik-1.7/license
export/batik-1.7/notice
export/batik-1.7/readme
getEvaluation.cgi
getTree.cgi
images/arborator.svg
index.cgi
lib/__init__.py
lib/config.py
lib/configobj.py
lib/conll.py
lib/createDB.py
lib/database.py
lib/depedit/__init__.py
lib/depedit/depedit.py
lib/main.py
lib/makeXML.py
lib/newconvert.py
lib/parser.py
lib/rhapsoxml.py
lib/test.conll
lib/treebankfiles.py
lib/trees2train.py
lib/updateTrees.py
login.cgi
logintemplates/admin_config.html
logintemplates/admin_config.txt
logintemplates/admin_eduser.txt
logintemplates/admin_invite.txt
logintemplates/admin_menu.txt
logintemplates/admin_page.html
logintemplates/edacc_page.html
logintemplates/editform_nojs.txt
logintemplates/login_done.html
logintemplates/login_nojs.txt
logintemplates/login_page.html
logintemplates/newlogin_nojs.txt
logintemplates/newlogin_page.html
mate.cgi
modules/__init__.py
modules/cgiutils.py
modules/configobj.py
modules/dataenc.py
modules/dateutils.py
modules/listquote.py
modules/logintools/__init__.py
modules/logintools/admin.py
modules/logintools/login.py
modules/logintools/loginutils.py
modules/logintools/newlogin.py
modules/odict.py
modules/pathutils.py
modules/setup.py
modules/standout.py
modules/urlpath.py
modules/validate.py
parseur/parseur.py
project.cgi
projects/OrfeoGold2016/categories.config
projects/OrfeoGold2016/categories.config.part
projects/OrfeoGold2016/functions.config
projects/OrfeoGold2016/project.cfg
q.cgi
runDepedit.cgi
save.cgi
script/arborator.draw.js
script/arborator.edit.js
script/arborator.view.js
script/arborator.view.min.js
script/colpick.js
script/jHtmlArea-0.7.0.js
script/jquery-linedtextarea.js
script/jquery-ui-1.11.4.custom.min.js
script/jquery-ui-1.8.18.custom.min.js
script/jquery.cookie.js
script/jquery.dimensions.js
script/jquery.fileupload-ui.js
script/jquery.fileupload.js
script/jquery.js
script/jsundoable.js
script/justDrawConll.js
script/q.js
script/raphael-min.js
script/raphael.export.js
script/raphael.js
script/splitter-152.js
script/upload.js
statusChange.cgi
templates/base.html
templates/index.html
templates/login.html
tools/add_cols_from_macro_to_micro.py
tools/addpinyin.py
tools/allsubj.txt
tools/bulkCorrectDatabase.py
tools/checkConll.py
tools/chinparse.py
tools/corpus.py
tools/frmgparsing.py
tools/ftb.platinum.transformConll.py
tools/getEvaluation.offline.py
tools/getTextsForParsing.py
tools/goldPlatinumTransformConll.py
tools/mark.txt
tools/mate.py
tools/mate/confusion.py
tools/parseSentences.py
tools/phrasestruc.py
tools/platinum.pyrite.transformConll.py
tools/platinum.rhodium.transformConll.py
tools/retokenisation.py
tools/retokeniseOnlineTrees.py
tools/searchConll.py
tools/simplification.py
tools/textGridToSentence.py
tools/transformConll.py
uploadFile.cgi
uploader.cgi
users/admin.ini
users/config.ini
users/default.ini
users/guest.ini
writeSvg.cgi
xsitemessage.html
Commit message: Move XSS escaping
  * Previous order could cause a crash if 'project' was None
  * Triggered by actions containing 'action=project_NAME'
  * New order should work in this scenario and for normal 'project=NAME'


Vulnerable code:

Fixed code:
new project:
create folder in projects
run
python createDB.py projectname

                    GNU AFFERO GENERAL PUBLIC LICENSE
                       Version 3, 19 November 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.

  A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate.  Many developers of free software are heartened and
encouraged by the resulting cooperation.  However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.

  The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community.  It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server.  Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.

  An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals.  This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU Affero General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Remote Network Interaction; Use with the GNU General Public License.

  Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software.  This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time.  Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source.  For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code.  There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.

# arborator
The Arborator software is aimed at collaboratively annotating dependency corpora.


![The Arborator](https://github.com/Arborator/arborator-server/blob/stable/images/arborator.png)


Please simply try it out on the [![Quick access to the Arborator](https://github.com/kimgerdes/arborator/blob/master/images/q.png)](http://arborator.ilpga.fr/q.cgi) access to the Arborator (no login is needed)!

Click on the Help link on top to understand the features.

The quick page allows to easily 
* visualize and edit a CoNLL file of dependency annotation
* create a dependency graph for a yet unannotated sentence
* export in graphical or CoNLL formats.

A report on Arborator's options for class-sourcing dependency annotation is here: https://scholar.google.fr/citations?view_op=view_citation&hl=en&citation_for_view=jCFpJfEAAAAJ:k_IJM867U9cC

Please also cite this article if you use Arborator for research.

Check out the instructions on the [Wiki page](https://github.com/Arborator/arborator-server/wiki).
// -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
// vim:set ft=javascript ts=4 sw=4 sts=4 cindent:

/*
Annotation documentation support.

Author: Sampo Pyysalo
License: MIT (http://opensource.org/licenses/MIT)
*/

var Annodoc = (function($, window, undefined) {

    var normalizeSpace = function(s) {
        s = s.replace(/^\s+/, '');
        s = s.replace(/\s+$/, '');
        s = s.replace(/\s\s+/g, ' ');
        return s;
    };

    var normalizeData = function(s) {
        s = s.replace(/^\s+/, '');
        s = s.replace(/\s+$/, '');
        s = s.replace(/[ \t]+/g, ' ');
        return s;
    };

    // return true iff given string only contains characters from a
    // right-to-left Unicode block and is not empty.
    var isRtl = function(s) {
        // range from http://stackoverflow.com/a/14824756
        return !!s.match(/^[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]+$/);
    };

    // return true iff given string only contains characters from the
    // Hebrew Unicode block (U+0590-U+05FF) and is not empty.
    var isHebrew = function(s) {
        return !!s.match(/^[\u0590-\u05FF]+$/);
    };

    // return given token with possible modifications to accommodate
    // issues in brat rendering of right-to-left text
    // (https://github.com/UniversalDependencies/docs/issues/52)
    var rtlFix = function(s) {
        if (isRtl(s)) {
            var wrap = '\u02D1';
            s = wrap + s + wrap;
        }
        return s;
    };

    var compactJSON = function(s) {
        // remove (some) space from JSON string, giving a visually
        // more compact (but equivalent and still pretty-printed)
        // version.

        if (s === undefined) {
            return s;
        }

        // replace any space with ' ' in non-nested curly brackets
        s = s.replace(/(\{[^\{\}\[\]]*\})/g, 
                      function(a, b) { return b.replace(/\s+/g, ' '); });
        // replace any space with ' ' in [] up to nesting depth 1
//        s = s.replace(/(\[(?:[^\[\]\{\}]|\[[^\[\]\{\}]*\])*\])/g, 
//         		      function(a, b) { return b.replace(/\s+/g, ' '); });
        // actually, up to nesting depth 2
        s = s.replace(/(\[(?:[^\[\]\{\}]|\[(?:[^\[\]\{\}]|\[[^\[\]\{\}]*\])*\])*\])/g, 
                      function(a, b) { return b.replace(/\s+/g, ' '); });
        return s;
    };

    var objectToString = function(data) {
        return compactJSON(JSON.stringify(data, undefined, '    '));
    };

    var parseSdToken = function(token) {
        // return [text, POS, features] for token encoded as
        // "text/POS[Feat1=Val1|...]", or [text, POS, ""] if no
        // features are included, or [text, "token", ""] if no POS
        // (/-separated string) is included.
        var text, POS_features;

        m = token.match(/^((?:[^\\]|\\.)+)\/(.+)$/);
        if (!m) {
            text = token, POS_features = 'token';
        } else {
            text = m[1], POS_features = m[2];
        }

        var POS, features;
        m = POS_features.match(/^(.*?)\[(.+)\]$/)
        if (!m) {
            POS = POS_features, features = '';
        } else {
            POS = m[1], features = m[2];
        }

        // apply defined backslash escape sequences
        text = text.replace(/((?:[^\\]+|\\.)*?)\\n/g, '$1\n');

        // unescape backslash escapes in text and tag
        text = text.replace(/([^\\]*)\\(.)/g, '$1$2');
        POS = POS.replace(/([^\\]*)\\(.)/g, '$1$2');
        
        return [text, POS, features];
    };

    // helper for *parse functions
    var makeLogger = function(logElement) {
        if (logElement === undefined) {
            return function(s) {
                console.log(s);
            };
        } else {
            return function(s) {
                logElement.val(logElement.val() + s + '\n');
            };
        }
    };

    // parse .ann format textbound line (helper for parseAnn)
    var parseAnnTextbound = function(line, log) {
        // NOTE: space matched more liberally than in primary format
        m = line.match(/^(\S+)\s+(\S+)\s+(\d+\s+\d+(?:;\d+\s+\d+)*)\s+(.*)$/);

        if (!m) {
            log('failed to parse textbound: "'+line+'"');
            return null;
        }

        var id = m[1],
            type = m[2],
            spanstr = m[3],
            text = m[4];

        // multiple (start, end) spans separated by semicolons
        var spanarr = normalizeSpace(spanstr).split(';'),
            spans = [];
        for (var i=0; i<spanarr.length; i++) {
            m = spanarr[i].match(/^(\d+)\s+(\d+)$/);
            if (!m) {
                log('error parsing offsets: "'+line+'"');
                return null;
            }
            var start = m[1], end = m[2];
            start = parseInt(start, 10);
            end = parseInt(end, 10);
            if (isNaN(start) || isNaN(end) || end < start) {
                log('failed to parse offsets: "'+line+'"');
                return null;
            }
            spans.push([start, end]);
        }

        // TODO verify text

        return [id, type, spans];
    };

    // parse .ann format relation line (helper for parseAnn)
    var parseAnnRelation = function(line, log) {
        // NOTE: space matched more liberally than in primary format
        m = line.match(/^(\S+)\s+(\S+)\s+(\S+):(\S+)\s+(\S+):(\S+)\s*$/);

        if (!m) {
            log('failed to parse relation: "'+line+'"');
            return null;
        }

        var id = m[1],
            type = m[2],
            a1l = m[3],
            a1i = m[4],
            a2l = m[5],
            a2i = m[6];

        return [id, type, [ [a1l, a1i], [a2l, a2i] ] ];
    };

    // parse .ann format equiv line (helper for parseAnn)
    var parseAnnEquiv = function(line, log) {
        // NOTE: space matched more liberally than in primary format
        m = line.match(/^(\S+)\s+(\S+)\s+(.+?)\s*$/);

        if (!m) {
            log('failed to parse equiv: "'+line+'"');
            return null;
        }

        var id = m[1],
            type = m[2],
            argstr = m[3];
    
        var args = normalizeSpace(argstr).split(' ');

        return [id, type].concat(args);
    };

    // parse .ann format event line (helper for parseAnn)
    var parseAnnEvent = function(line, log) {
        // NOTE: space matched more liberally than in primary format
        m = line.match(/^(\S+)\s+(\S+):(\S+)(.*)$/);

        if (!m) {
            log('failed to parse event: "'+line+'"');
            return null;
        }

        var id = m[1],
            type = m[2],
            trigger = m[3],
            argstr = m[4];

        var args = [];
        if (!argstr.match(/^\s*$/)) {
            var argarr = normalizeSpace(argstr).split(' ');
            for (var i=0; i<argarr.length; i++) {
                m = argarr[i].match(/^(\S+):(\S+)$/);
                if (!m) {
                    log('failed to parse event args: "'+line+'"');
                    return null;
                }
                args.push([m[1], m[2]]);
            }
        }

        return [id, trigger, args];
    };

    // parse .ann format modifier line (helper for parseAnn)
    var parseAnnModifier = function(line, log) {
        // NOTE: space matched more liberally than in primary format
        m = line.match(/^(\S+)\s+(\S+)\s+(\S+)\s*$/);

        if (!m) {
            log('failed to parse modifier: "'+line+'"');
            return null;
        }

        var id = m[1],
            type = m[2],
            target = m[3];

        return [id, type, target];
    };

    // parse .ann format attribute line (helper for parseAnn)
    var parseAnnAttribute = function(line, log) {
        // NOTE: space matched more liberally than in primary format
        m = line.match(/^(\S+)\s+(\S+)\s+(\S+)(.*)$/);

        if (!m) {
            log('failed to parse attribute: "'+line+'"');
            return null;
        }

        var id = m[1],
            type = m[2],
            target = m[3],
            value = m[4];

        // support also older ("modifier") format w/o value
        if (value.match(/^\s*$/)) {
            value = true;
        }

        return [id, type, target, value];
    };

    // parse .ann format normalization line (helper for parseAnn)
    var parseAnnNormalization = function(line, log) {
        // TODO: implement parsing for normalization annotations
        log('normalization annotation parsing not implemented: "'+line+'"');
        return null;
    };

    // parse .ann format comment line (helper for parseAnn)
    var parseAnnComment = function(line, log) {
        // TODO: implement parsing for comment annotations
        log('comment annotation parsing not implemented: "'+line+'"');
        return null;
    };

    // parse .ann format, return brat document data format
    var parseAnn = function(ann, log) {
        if (log === undefined) {
            log = makeLogger();
        }
        var lines = ann.split('\n');        

        // first line is assumed to be the document text
        var document_text = lines[0];

        // parse lines after the first as .ann-formatted lines
        // (see e.g. http://brat.nlplab.org/standoff.html)
        var textbounds = [],
            relations = [],
            events = [],
            equivs = [],
            attributes = [],
            normalizations = [],
            comments = [],
            error = false;
        for (var i=1; i<lines.length; i++) {
            var line = lines[i];

            if (line.match(/^\s*$/)) {
                continue; // ignore empties
            }

            // annotation type determined by first character on line
            switch (line[0]) {
                case 'T': 
                    textbounds.push(parseAnnTextbound(line, log));
                    break;
                case 'R': 
                    relations.push(parseAnnRelation(line, log));
                    break;
                case 'E': 
                    events.push(parseAnnEvent(line, log));
                    break;
                case 'M': 
                    attributes.push(parseAnnModifier(line, log));
                    break;
                case 'A': 
                    attributes.push(parseAnnAttribute(line, log));
                    break;
                case 'N': 
                    normalizations.push(parseAnnNormalization(line, log));
                    break;
                case '*': 
                    equivs.push(parseAnnEquiv(line, log));
                    break;
                case '#': 
                    comments.push(parseAnnComment(line, log));
                    break;
                default:
                    log('failed to parse line: "'+line+'"');
                    error = true;
            }
        }

        // filter annotation lists for nulls or empties (marking parse failure),
        // setting error if found.
        var validAnnotation = function(ann) {
            if (ann !== null && ann.length != 0) {
                return true;
            } else {
                error = true; // NOTE: side-effect
                return false;
            }
        };
        textbounds = textbounds.filter(validAnnotation);
        relations = relations.filter(validAnnotation);
        events = events.filter(validAnnotation);
        equivs = equivs.filter(validAnnotation);
        attributes = attributes.filter(validAnnotation);
        normalizations = normalizations.filter(validAnnotation);
        comments = comments.filter(validAnnotation);

        log('.ann parse done, error = '+error);

        // split textbounds into entities and triggers, where any textbound
        // referenced from an event is a trigger, and everything else is an
        // entity.
        var entities = [], 
            triggers = [],
            isTrigger = {};
        for (var i=0; i<events.length; i++) {
            var triggerId = events[i][1];
            isTrigger[triggerId] = true;
        }
        for (var i=0; i<textbounds.length; i++) {
            var textboundId = textbounds[i][0];
            if (isTrigger[textboundId]) {
                 triggers.push(textbounds[i]);
            } else {
                 entities.push(textbounds[i]);
            }
        }

        return {
            'text': document_text,
            'entities': entities,
            'triggers': triggers,
            'relations': relations,
            'events': events,
            'equivs': equivs,
            'attributes': attributes,
            'normalizations': normalizations,
            'comments': comments,
            'error': error
        };
    };

    // parse SD format, return brat document data format
    var parseSd = function(sd, log) {
        if (log === undefined) {
            log = makeLogger();
        }

        sd = normalizeData(sd);

        var lines = sd.split('\n');

        // first line is assumed to be sentence text
        var sentence_text = lines[0];
        sentence_text = normalizeSpace(sentence_text);

        // determine token offsets and construct spans ("entities") and
        // features ("attributes")
        var tokens = sentence_text.split(' '),
            spans = [],
            attributes = [],
            aidseq = 1,
            offset = 0,
            error = false;
        sentence_text = '';
        for (var i=0; i<tokens.length; i++) {
            var text_POS_features = parseSdToken(tokens[i]);
            var text = text_POS_features[0], 
                POS = text_POS_features[1],
                features = text_POS_features[2];
            //; special case hack for RTL languages, see
            //; https://github.com/UniversalDependencies/docs/issues/52
            var fixedText = rtlFix(text);
            var length = fixedText.length;
            if (!text.match(/^\s*$/)) { // skip space-only (e.g. newline)
                spans.push(['T'+(i+1), POS, [[offset, offset+length]]]);
                tokens[i] = text;
                if (features) {
                    features = features.split('|');
                    for (var j=0; j<features.length; j++) {
                        var m = features[j].match(/^(.*)=(.*)$/);
                        if (!m) {
                            // TODO better error reporting
                            console.log('failed to parse feature', features[j]);
                            continue;
                        }
                        attributes.push(['A'+aidseq++, m[1], 'T'+(i+1), m[2]]);
                    }
                }
            }
            offset += length + 1;
            sentence_text += fixedText + ' ';
        }

        var tokenIndex = function(t) {
            // accept two formats: indexed ('dog-1') and simple ('dog')
            // TODO: consider case-insensitive implementation

            // indexed match
            var m = t.match(/^\s*(.*)-(\d+)\s*$/)	      
            if (m) {
                var text = m[1], idx = m[2];
                // confirm match (SD indices are 1-based)
                idx = parseInt(idx, 10) - 1;
                if (tokens[idx] === text) {
                    return idx;
                } else {
                    // TODO consider skipping output, not necessarily an error
                    log('token text mismatch: "'+text+'" vs. "'+
                        tokens[idx]+'"');
                }
                // fall through to allow simple match for e.g. 'il-2'
            }

            // simple match
            var firstIdx = $.inArray(t, tokens);
            if (firstIdx === -1) {
                return null;
            } else {
                if ($.inArray(t, tokens, firstIdx+1) !== -1) {
                    log('warning: multiple occurrences of "'+t+'"');
                }
                return firstIdx;
            }
        };

        // parse lines after the first as dependencies, construct relations
        var relations = [];
        for (var i=1; i<lines.length; i++) {
            var line = lines[i];
            line = normalizeSpace(line);
            
            if (line.match(/^\s*$/)) {
                continue; // ignore empties
            }
            
            var m = line.match(/^(\S+)\s*\(\s*(\S+?)\s*,\s*(\S+)\s*\)$/);
            if (!m) {
                log('failed to parse: "'+line+'"');
                error = true;
                continue;
            }
            var type = m[1], from = m[2], to = m[3];
            
            // ignore root dependency (if any) for visualization
            // TODO: consider showing root when defined?
            if (type == 'root' && from.match(/-0$/)) {
                log('note: skipping "root": "'+line+'"');
                continue;
            }
	    
            // determine which tokens are referred to
            var fromIdx = tokenIndex(from), toIdx = tokenIndex(to);
            if (fromIdx === null || toIdx === null) {
                log('failed to find token: "'+line+'"');
                error = true;
                continue;
            }
	    
            relations.push([ 'R'+i, type, [ [ 'arg1', 'T'+(fromIdx+1) ], 
                                            [ 'arg2', 'T'+(toIdx+1)   ] ] ]);
        }

        log('SD parse done: '+spans.length+' tokens, '+
            relations.length+' dependencies.');
          
        return {
            'text': sentence_text,
            'entities' : spans,
            'relations' : relations,
            'attributes': attributes,
            'error' : error
        };
    };

    var parseConllU = function(conll, log, strict) {
        var doc = new ConllU.Document();
        return doc.parse(conll, log, strict).toBrat();
    }

    // parse CoNLL-X format, return brat document data format
    var parseConllX = function(conll, log) {
        if (log === undefined) {
            log = makeLogger();
        }

        conll = normalizeData(conll);

        var lines = conll.split('\n');

        // each line has the following format
        // (see http://ilk.uvt.nl/conll/#dataformat):
        //
        // 1.  ID: token counter, starting at 1 for each new sentence
        // 2.  FORM: word form or punctuation symbol
        // 3.  LEMMA: lemma or stem of word form
        // 4.  CPOSTAG: coarse-grained part-of-speech tag
        // 5.  POSTAG: fine-grained part-of-speech tag
        // 6.  FEATS: set of syntactic and/or morphological features 
        // 7.  HEAD: head ID of the current token
        // 8.  DEPREL: dependency relation to the HEAD
        // 9.  PHEAD:	projective head ID of current token
        // 10. PDEPREL:	dependency relation to the PHEAD
        // 
        // (here, we will use ID, FORM, CPOSTAG, HEAD and DEPREL)
        // TODO: use also at least PHEAD, PDEPREL
        var ids = [],
            forms = [],
            postags = [],
            heads = [],
            deprels = [],
            error = false;
        for (var i=0; i<lines.length; i++) {
            var line = lines[i];
            line = line.replace(/\s$/, '');
            var fields = line.split(/\s+/);
            // TODO: do we want this flexibility on field number?
            if (fields.length >= 8 && fields.length <= 10) {
                ids.push(fields[0]);
                forms.push(fields[1]);
                postags.push(fields[4]);
                heads.push(fields[6]);
                deprels.push(fields[7]);
            } else {
                // TODO: better error handling
                log('expected 8-10 fields, got '+fields.length+': "'+line+'"');
                error = true;
                continue;
            }
        }

        var spans = [],
            relations = [],
            offset = 0;
        for (i=0; i<ids.length; i++) {
            var length = forms[i].length;
            spans.push(['T'+ids[i], postags[i], [[offset, offset+length]]]);
            offset += length + 1;
            // TODO: handle root
            if (heads[0] === 0) {
                continue;
            }
            relations.push(['R'+ids[i], deprels[i], 
                            [ [ 'arg1', 'T'+heads[i] ],
                              [ 'arg2', 'T'+ids[i] ] ] ]);
        }
        
        var text = forms.join(' ');

        return {
            'text': text,
            'entities' : spans,
            'relations' : relations,
            'error' : error
        };
    };

    // from http://stackoverflow.com/a/2117523
    var random_guid = function() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, 
                                                              function(c) {
            var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
            return v.toString(16);
        });
    };

    // add prefix and suffix to all IDs in data, modifying data. If
    // neither a prefix nor suffix is provided, add a random GUID
    // prefix.
    var reviseIds = function(data, prefix, suffix) {
        if (prefix === undefined) {
            prefix = '';
        }
        if (suffix === undefined) {
            suffix = '';
        }
        if (prefix === '' && suffix == '') {
            // no prefix or suffix defined: generate random GUID prefix
            prefix = random_guid() + '-';
        }

        // entities: entity ID only
        var entities = data['entities'] || [];
        for (var i=0; i<entities.length; i++) {
            entities[i][0] = prefix + entities[i][0] + suffix;
        }

        // triggers: trigger ID only
        var triggers = data['triggers'] || [];
        for (var i=0; i<triggers.length; i++) {
            triggers[i][0] = prefix + triggers[i][0] + suffix;
        }

        // relations: relation ID and argument IDs
        var relations = data['relations'] || [];
        for (var i=0; i<relations.length; i++) {
            relations[i][0] = prefix + relations[i][0] + suffix;
            var args = relations[i][2];
            for (var j=0; j<args.length; j++) {
                args[j][1] = prefix + args[j][1] + suffix;
            }
        }

        // events: event ID, trigger ID, and argument IDs
        var events = data['events'] || [];
        for (var i=0; i<events.length; i++) {
            events[i][0] = prefix + events[i][0] + suffix;
            events[i][1] = prefix + events[i][1] + suffix;
            var args = events[i][2];
            for (var j=0; j<args.length; j++) {
                args[j][1] = prefix + args[j][1] + suffix;
            }
        }

        // equivs: argument IDs only
        var equivs = data['equivs'] || [];
        for (var i=0; i<equivs.length; i++) {
            for (var j=2; j<equivs[i].length; j++) {
                equivs[i][j] = prefix + equivs[i][j] + suffix;
            }
        }

        // attributes: attribute ID and target ID
        var attributes = data['attributes'] || [];
        for (var i=0; i<attributes.length; i++) {
            attributes[i][0] = prefix + attributes[i][0] + suffix;
            attributes[i][2] = prefix + attributes[i][2] + suffix;
        }

        // normalizations: normalization ID and target ID
        var normalizations = data['normalizations'] || [];
        for (var i=0; i<normalizations.length; i++) {
            normalizations[i][0] = prefix + normalizations[i][0] + suffix;
            normalizations[i][2] = prefix + normalizations[i][2] + suffix;
        }

        // comments: target ID only(!)
        var comments = data['comments'] || [];
        for (var i=0; i<comments.length; i++) {
            comments[i][0] = prefix + comments[i][0] + suffix;
        }

        // styles (extension): target ID or IDs
        var styles = data['styles'] || [];
        for (var i=0; i<styles.length; i++) {
            if (styles[i][0] instanceof Array) {
                styles[i][0][0] = prefix + styles[i][0][0] + suffix;
                styles[i][0][1] = prefix + styles[i][0][1] + suffix;
            } else {
                styles[i][0] = prefix + styles[i][0] + suffix;
            }
        }

        return data;
    };

    // START copied from brat visualizer_ui.js
    var adjustToCursor = function(evt, element, offset, top, right) {
        // get the real width, without wrapping
        element.css({ left: 0, top: 0 });
        var screenHeight = $(window).height();
        var screenWidth = $(window).width();
        // FIXME where are these 22s coming from?!?
        var elementHeight = element.height() + 22;
        var elementWidth = element.width() + 22;
        var x, y;
        offset = offset || 0;
        if (top) {
            y = evt.clientY - elementHeight - offset;
            if (y < 0) top = false;
        }
        if (!top) {
            y = evt.clientY + offset;
        }
        if (right) {
            x = evt.clientX + offset;
            if (x >= screenWidth - elementWidth) right = false;
        }
        if (!right) {
            x = evt.clientX - elementWidth - offset;
        }
        if (y < 0) y = 0;
        if (x < 0) x = 0;
        element.css({ top: y, left: x });
    };

    var commentPopup = $('<div id="commentpopup"/>').appendTo('body');

    var commentDisplayed = false;

    var displayCommentTimer = null;
    var displayComment = function(evt, target, comment, commentText) {
        if (commentText) {
            comment += Util.escapeHTMLwithNewlines(commentText);
        }
        commentPopup.html(comment);
        adjustToCursor(evt, commentPopup, 10, true, true);
        clearTimeout(displayCommentTimer);
        /* slight "tooltip" delay to allow highlights to be seen
           before the popup obstructs them. */
        var delay = 250; // ms
        displayCommentTimer = setTimeout(function() {
            commentPopup.stop(true, true).fadeIn();
            commentDisplayed = true;
        }, delay);
    };

    // TODO: these should be visualization-specific. Globals prevent
    // mixing multiple configs in a single page.
    var spanTypes = null;
    var relationTypesHash = null;
    var typesLoaded = function(_spanTypes, _entityAttributeTypes, 
                               _eventAttributeTypes, _relationTypesHash) {
        spanTypes = _spanTypes;
        relationTypesHash = _relationTypesHash;
    };

    // invoked by brat dispatcher to display additional information related 
    // to a particular span ("text-bound") annotation.
    var displaySpanInformation = function(evt, target, spanId, spanType, 
                                          mods, spanText, commentText) {

        // in part following brat/visualizer_ui.js:displaySpanComment()
        var spanLabel = Util.spanDisplayForm(spanTypes, spanType)
        var comment = '';
        comment += '<div>';
        comment += ('<div class="comment_text">"' + 
                    Util.escapeHTML(spanText) + 
                    '"</div>');
        comment += ((spanLabel.match(/^\s*$/) ? '' :
                     '<span class="comment_type_id_wrapper">' +
                     '<span class="comment_type">' +
                     Util.escapeHTML(spanLabel) +
                     '</span></span>'));
        if (mods.length) {
            comment += '<div>' + Util.escapeHTML(mods.join(', ')) + '</div>';
        }
        comment += '</div>';
        displayComment(evt, target, comment, commentText);
    };

    // invoked by brat dispatcher to display additional information related 
    // to a particular arc (relation) annotation.
    var displayArcInformation = function(evt, target, symmetric, arcId,
                                         originSpanId, originSpanType, role, 
                                         targetSpanId, targetSpanType,
                                         commentText) {
        var arcRole = target.attr('data-arc-role');
        // &#8594 == Unicode right arrow
        var arrowStr = '&#8594;';
        var arcDisplayForm = Util.arcDisplayForm(spanTypes, null,
                                                 arcRole, relationTypesHash);
        var comment = "";
        // "VBP -> nsubj -> NNS" - type comment formatting
//         var originType = Util.spanDisplayForm(spanTypes, originSpanType);
//         var targetType = Util.spanDisplayForm(spanTypes, targetSpanType);
//         comment += '<span class="comment_type_id_wrapper">' +
//                    '<span class="comment_type">' +
//                    Util.escapeHTML(originType)     + ' ' + arrowStr + ' ' +
//                    Util.escapeHTML(arcDisplayForm) + ' ' + arrowStr + ' ' +
//                    Util.escapeHTML(targetType) +
//                    '</span></span>';
        comment += '<span class="comment_type_id_wrapper">' +
                   '<span class="comment_type">' +
                   Util.escapeHTML(arcDisplayForm) +
                   '</span></span>';
        displayComment(evt, target, comment, commentText);
    };

    // invoked by brat dispatcher to hide additional information
    // (see displaySpanInformation and displayArcInformation)
    var hideInformation = function() {
        clearTimeout(displayCommentTimer);
        if (commentDisplayed) {
            commentPopup.stop(true, true).fadeOut(function() {
                commentDisplayed = false;
            });
        }
    };

    var onMouseMove = function(evt) {
        if (commentDisplayed) {
            adjustToCursor(evt, commentPopup, 10, true, true);
        }
    };
    // END copied from brat visualizer_ui.js

    // Return callback triggered on changes to visualization input.
    var makeInputHandler = function(eId, defaultLabel, parse, 
                                    dispatcher, visualization, 
                                    inputArea, parsedArea, logArea) {

        var inputHandler = function() {
            var parsed, parse_error = false;

            logArea.val(''); // clear log

            try {
                parsed = parse(inputArea.val(), makeLogger(logArea));
                parsed = reviseIds(parsed, eId + '-');
                parsed = setSentenceLabels(parsed, defaultLabel);
                parse_error = parsed.error;
            } catch (e) {
                console.log(e);
                parse_error = true;
            }

            // console.log(parse_error);

            if (!parse_error) {
                inputArea.css({'border': '2px inset'});
                visualization.removeClass('haserror');
            } else {
                inputArea.css({'border': '2px solid red'});
                visualization.addClass('haserror');
                return;
            }
    
            parsedArea.text(objectToString(parsed));

            try {
                dispatcher.post('requestRenderData', [$.extend({}, parsed)]);
                parsedArea.css({'border': '2px inset'});
                visualization.removeClass('haserror');
            } catch(e) {
                console.log('requestRenderData error:', e);
                logArea.append('requestRenderData error: '+e);
                parsedArea.css({'border': '2px solid red'});
                visualization.addClass('haserror');
            }
        };
        
        return inputHandler;
    };

    // Return embedding options for element.
    var embeddingOptions = function(elem, options) {
        // initialize options, falling back to element attributes as
        // defaults
        if (options === undefined) {
            options = {};
        }
        $.each(elem[0].attributes, function(idx) {
            var name = this.name;
            var value = this.value;
            // ignore standard attributes and options provided by caller
            var standardAttr = ['id', 'class', 'data-embednum'];
            if ($.inArray(name.toLowerCase(), standardAttr) === -1 &&
                options[name] === undefined) {
                options[name] = value;
            }
        });
        return options;
    };

    // Return embedding data, defaulting to element text.
    var embeddingData = function(elem, data) {
        if (data === undefined) {
            data = elem.text();
            // trim one initial and/or terminal newline to accommodate
            // source formatting (e.g. "<div ...>\nDATA\n</div>")
            if (data.length !== 0 && data[0] === '\n') {
                data = data.slice(1);
            }
            if (data.length !== 0 && data.slice(-1) === '\n') {
                data = data.slice(0, -1);
            }
        }
        return data;
    };

    var setEmbeddingStyles = function(elem) {
        // special case for supporting simple "~~~ sdparse [...] ~~~"
        // syntax: if elem has a <pre> parent, add "embedding" class
        // to the <pre>, otherwise add the "embedding" class to the
        // element itself, so we can control margins etc. on the
        // top-level element.
        if (elem.parent('pre').length !== 0) {
            elem.parent('pre').addClass('embedding');
        } else {
            elem.addClass('embedding');
        }

        // add a class to the element itself.
        elem.addClass('embedded-wrapper');
    };

    // Set sentence labels in parsed data
    var setSentenceLabels = function(parsed, defaultLabel) {
        // if there are no labels or the label of the first sentence
        // is empty, use default as label for first sentence.
        if (parsed.sentlabels === undefined || parsed.sentlabels.length === 0) {
            parsed.sentlabels = [defaultLabel];
        } else if (parsed.sentlabels[0] === null) {
            parsed.sentlabels[0] = defaultLabel;
        }
        return parsed;
    };

    // Determine space to allot for sentence labels.
    var sentenceLabelWidth = function(sentenceLabels) {
        var maxWidth = 20; // no less than this

        if (!sentenceLabels || sentenceLabels.length === 0 ||
            sentenceLabels.filter(function(x) { return !!x }).length === 0) {
            return width;
        }
        for (var i=0; i<sentenceLabels.length; i++) {
            var label = sentenceLabels[i];
            if (label !== undefined && label !== null) {
                var width = 10 * label.length; // char width guesstimate
                maxWidth = width > maxWidth ? width : maxWidth;
            }
        }
        return maxWidth;
    };

    // Display sentence labels in parsed data in given visualization.
    var displaySentenceLabels = function(parsed, dispatcher, visualization) {
        if (parsed.sentlabels && parsed.sentlabels.length !== 0 &&
            parsed.sentlabels.filter(function(x) { return !!x }).length !== 0) {
            var sentenceLabels = parsed.sentlabels.slice(0);
            dispatcher.on('doneRendering', function() {
                var elements = visualization.find('.sentnum text');
                for (var i=0; i<elements.length; i++) {
                    if (sentenceLabels[i]) {
                        $(elements[i]).text(sentenceLabels[i]);
                        $(elements[i]).show();
                    }
                }
                // Show separating lines for all numbers (including hidden)
                visualization.find('.sentnum path').show();
            });
        }
    };

    // Return elements for pre-build embedding interface based on
    // element attributes, or null if not available.
    var embeddingElements = function(elem) {
        var visId = elem.attr('data-visid'),
            inputId = elem.attr('data-inputid'),
            parsedId = elem.attr('data-parsedid'),
            logId = elem.attr('data-logid');

        if (visId === undefined || inputId === undefined ||
            parsedId === undefined || logId === undefined) {
            // console.log('embeddingElements: ids not defined');
            return null;
        }
            
        var visualization = $('#'+visId),
            inputArea = $('#'+inputId),
            parsedArea = $('#'+parsedId),
            logArea = $('#'+logId);

        if (visualization === undefined || inputArea === undefined ||
            parsedArea === undefined || logArea === undefined) {
            console.log('embeddingElements: elements not found');
            return null;
        }

        // remove possible initial data on element triggering embedding.
        elem.empty();

        return {
            'visualization': visualization,
            'inputArea': inputArea,
            'parsedArea': parsedArea,
            'logArea': logArea,
        };        
    };

    // Create elements for tab-based embedding interface
    var buildEmbedding = function(elem, eId, options) {
        var inputTabId = eId + '-2',
            parsedTabId = eId + '-3',
            infoTabId = eId + '-4';

        // visualization and related data elements
        var visDiv = $('<div id="'+eId+'-vis"></div>');
        var tabDiv = $('<div id="'+eId+'-tabs"></div>');
        var shDiv  = $('<div id="'+eId+'-sh" class="show-hide-div"></div>');
        var showHideButton = $('<button id="'+eId+'-toggle" '+
                               'class="show-hide-toggle">hide</button>');
        shDiv.append(showHideButton);

        var inputArea = $('<textarea id="'+eId+'-in" ' +
                          'class="embedded-brat-data"></textarea>');
        var parsedArea = $('<textarea id="'+eId+'-brat" disabled="disabled" ' +
                           'class="embedded-brat-data"></textarea>');
        var logArea = $('<textarea id="'+eId+'-log" disabled="disabled" ' +
                        'class="embedded-brat-data"></textarea>');

        // build top-level structure
        // tab headers (li+a)
        tabDiv.append([
                       '<div id="'+eId+'">',
                       '  <ul>',
                       '    <li><a href="#'+inputTabId+'">input</a></li>',
                       '    <li><a href="#'+parsedTabId+'">brat</a></li>',
                       '    <li><a href="#'+infoTabId+'">info</a></li>',
                       '  </ul>',
                       '</div>'
                       ].join('\n'));
        // tab content (divs)
        var inputTab   = $('<div id="'+inputTabId+'"></div>'),
            parsedTab = $('<div id="'+parsedTabId+'"></div>'),
            logTab  = $('<div id="'+infoTabId+'"></div>');
        inputTab.append(inputArea);
        parsedTab.append(parsedArea);
        logTab.append(logArea);

        // add content to top-level element, turn into jQuery-ui tabs
        tabDiv.append(inputTab, parsedTab, logTab);
        tabDiv.tabs();
        var shVisible = 0;
        var setShToggleText = function() {
            $('span', showHideButton).text(shVisible ? 'hide' : 'edit');
        }
        tabDiv.hide(); // TODO make optional (control e.g. using class)

        // wire up button to toggle tab visibility
        showHideButton.button().click(function(ev) {
            tabDiv.toggle('blind');
            shVisible = !shVisible;
            setShToggleText();
        });
        setShToggleText();

        // hide button controlling tab visibility unless set in
        // options
        if (!(options && options.tabs)) {
            showHideButton.hide();
        }
        
        // attach top-level elements
        elem.empty().append(shDiv, tabDiv, visDiv);
        
        return {
            'visualization': visDiv,
            'inputArea': inputArea,
            'parsedArea': parsedArea,
            'logArea': logArea,
            'debugControls': showHideButton,
        };
    };

    // Set up dispatcher bindings for embedAnnotation.
    var setupDispatcher = function(dispatcher, visualization, parsedArea) {
        var renderError = function() {
            parsedArea.css({'border': '2px solid red'});
            visualization.addClass('haserror');
        };
        dispatcher.on('renderError: Fatal', renderError);
        dispatcher.on('displaySpanComment', displaySpanInformation);
        dispatcher.on('displayArcComment', displayArcInformation);
        dispatcher.on('hideComment', hideInformation);
        dispatcher.on('mousemove', onMouseMove);
    };

    var embeddedIdSeq = 1;

    var embedAnnotation = function(elem, parse, cdata, data, options) {
        // argument defaults
        parse = (parse !== undefined) ? parse : parseAnn;
        cdata = (cdata !== undefined) ? cdata : {};
        data = embeddingData(elem, data);
        options = embeddingOptions(elem, options);

        setEmbeddingStyles(elem);

        var myIdSeq = embeddedIdSeq++;
        var eId = 'embedded-' + myIdSeq;

        // store sequence number for reference resolution
        elem.attr('data-embednum', myIdSeq);

        // attempt to find elements for visualization etc. via
        // attributes on elem, falling back to dynamically building a
        // tab-based UI if none are found.
        var eElements = embeddingElements(elem);
        if (eElements === null) {
            var eElements = buildEmbedding(elem, eId, options);
        }
        var visualization = eElements.visualization,
            inputArea = eElements.inputArea,
            parsedArea = eElements.parsedArea,
            logArea = eElements.logArea,
            debugControls = eElements.debugControls;

        // set UI content from data and derivatives
        inputArea.text(data);

        var parsed = parse(data, makeLogger(logArea));
        parsed = reviseIds(parsed, eId + '-');
        parsed = setSentenceLabels(parsed, ''+myIdSeq);

        parsedArea.text(objectToString(parsed));

        if (parsed.error) {
            inputArea.css({'border': '2px solid red'});
            visualization.addClass('haserror');
            // show debug (tab) controls on error to help debugging
            if (debugControls !== undefined) {
                debugControls.show();
            }
        }

        // we need to create a Dispatcher in advance of embed() to bind
        // typesLoaded callback before it is triggered. This in turn
        // is needed to capture the parsed type info for popups.
        // (TODO: do we really need a separate Dispatcher for each
        // embedded visualization?)
        var dispatcher = new Dispatcher();
        dispatcher.on('spanAndAttributeTypesLoaded', typesLoaded);

        // visualization configuration
        var visualizerOptions = {
            'sentNumMargin': sentenceLabelWidth(parsed.sentlabels),
        };

        // initialize brat visualization
        var visId = visualization.attr('id'); 
        Util.embed(visId, $.extend({'collection': null}, cdata),
                   $.extend({}, parsed), webFontURLs, dispatcher,
                   visualizerOptions);

        setupDispatcher(dispatcher, visualization, parsedArea);

        displaySentenceLabels(parsed, dispatcher, visualization);

        var inputHandler = makeInputHandler(eId, ''+myIdSeq, parse,
                                            dispatcher, visualization,
                                            inputArea, parsedArea, logArea);

        inputArea.bind('propertychange keyup input paste', inputHandler);

        // show as late as possible to minimize "jumping"
        // (https://github.com/spyysalo/annodoc/issues/11)
        elem.show();
    };

    var resolveEmbeddedReference = function(elem, data) {
        var refId = elem.attr('href'),
	    refElem = $(refId);
        
        if (refElem === undefined) {
            console.log('Failed to resolve reference to', refId, 'for', elem);
            return;
        }
        
        var refSeq = refElem.attr('data-embednum');
        if (refSeq === undefined) {
            console.log('no data-embednum for', refElem);
            return;
        }
        
        var origText = elem.text();
        var resolvedText = origText.replace(/\#/, refSeq);
        if (origText === resolvedText) {
            console.log('failed replace in text', origText, 'for', elem);
            return;
        }

        elem.text(resolvedText);
    };

    // createDocumentationLink caches
    var collDocByName = {};
    var destByCollDoc = {};

    // createDocumentationLink helper
    var findMatchingTitle = function(s, collection, documents, matchCase) {
        if (documents instanceof Array !== true) {
            return null;
        }
        if (matchCase == undefined) {
            matchCase = true;
        }
        // attempt match against document and collection/document forms
        for (i=0; i<documents.length; i++) {
            var coll_doc = collection+'/'+documents[i].title;
            if (documents[i].title == s || coll_doc == s) {
                return documents[i];
            }
        }
        if (matchCase) {
            return null;
        }
        for (i=0; i<documents.length; i++) {
            var coll_doc = (collection+'/'+documents[i].title).toLowerCase();
            if (documents[i].title.toLowerCase() == s.toLowerCase() ||
                coll_doc == s.toLowerCase()) {
                return documents[i];
            }
        }
        return null;
    };

    var cmp = function(a, b) {
        if (a > b) {
            return 1;
        } else if (a < b) {
            return -1;
        } else {
            return 0;
        }
    }

    // createDocumentationLink helper
    var findBestDocumentMatch = function(s, collections) {
        var currentColl = collections._current;
        var currentDocs = collections[currentColl];
        var d;

        // first try everything with case-sensitive matching, then repeat
        // with case-insensitive matching.
        for (var ci=0; ci<2; ci++) {
            var matchCase = !ci;

            // prioritize match in the current collection
            if ((d = findMatchingTitle(s, currentColl, currentDocs, matchCase))
                !== null) {
                return [currentColl, d];
            }

            // if none, find matches in any collection
            var matches = [];
            $.each(collections, function(coll, docs) {
                if ((d = findMatchingTitle(s, coll, docs, matchCase))
                    !== null) {
                    matches.push([coll, d]);
                }
            });

            // sort multiple matches by length of shared prefix with
            // current collection, so that e.g. "fi-dep" is preferred
            // over "en-pos" if current is "fi-pos".
            var prefixLength = {};
            for (var i=0; i<matches.length; i++) {
                var coll = matches[i][0],
                    maxn = Math.max(currentColl.length, coll.length),
                    n = 0;
                for (; n<maxn && coll[n] == currentColl[n]; n++);
                prefixLength[coll] = n;
            }
            matches.sort(function(a, b) {
                return -cmp(prefixLength[a[0]], prefixLength[b[0]]);
            });
            if (matches.length != 0) {
                return matches[0];
            }
        }
        // TODO: consider attempting also URL matches.

        // failed to match anything
        return null;
    };
    
    var createDocumentationLink = function(e, collections) {
        var s, linkedByText;

        // Determine linked page by element href attribute value by
        // default, falling back on element text if not given.
        if (e.attr('href')) {
            s = e.attr('href');
            linkedByText = false;
        } else {
            s = e.text();
            linkedByText = true;
        }

        if (collDocByName[s] === undefined) {
            collDocByName[s] = findBestDocumentMatch(s, collections);
        }

        if (collDocByName[s] !== null) {
            var coll = collDocByName[s][0],
                doc  = collDocByName[s][1],
                url = doc.url;

            // allow in-document auto-linking destination anchors to
            // pre-empt links to other documents, and use the "al-doc"
            // class to block this functionality.
            var inDocAnchor = destByCollDoc[coll + '/' + doc.title];
            if (inDocAnchor !== undefined && !e.hasClass('al-doc')) {
                url = '#' + inDocAnchor.id;
            }

            e.attr('href', url);
            e.addClass('doclink');
            // add title if not aready present
            if (e.attr('title') === undefined) {
                e.attr('title', coll+' '+doc.title)
            }
            // if the link was set by the element text content, mark
            // with additional class and turn e.g. "entity/person"
            // into "person" in link text
            if (linkedByText) {                
                e.addClass('doclabel');
                if ((m = e.text().match(/^.+\/(.+)$/)) !== null) {
                    e.text(m[1]);
                }
            }
        } else {
            // failed to link, mark. TODO: use class instead of css
            e.css('color', 'red');
            if (e.attr('title') === undefined) {
                e.attr('title', 'failed to link to entry in any collection')
            }
        }
    };

    // <a> href attribute values that trigger auto-linking
    var docLinkRegex = /^((?:[a-zA-Z_-]+\/)*)([a-zA-Z0-9_-]+(?:\:[a-zA-Z0-9_-]+)?)$/;

    var createDocumentationLinks = function(collections) {
        // first, find anchors that are auto-linkable and anchors that
        // are auto-linking destinations within the document

        // auto-linkable anchors either 1) have empty or missing href,
        // id and name attributes, or 2) have a href that matches
        // docLinkRegex.
        var autoLinkable = function(e) {
            // e.href gives page URL even if attribute is empty
            var href = $(e).attr('href');
            if (!href && !e.id && !e.name) {
                return true;
            } else if (typeof href === 'string' && href.match(docLinkRegex)) {
                return true;
            }
            return false;
        };

        // target destination anchors have a specific class and an id.
        var linkDestination = function(e) {
            if (!!e.id && $(e).hasClass('al-dest')) {
                return true;
            } else {
                return false;
            }
        };

        // store linkables and destinations
        var linkables = [],
            destinations = {};

        var linkDestination = function(e) {
            if (e.id && e.id.match(/^al-./) && $(e).hasClass('al-dest')) {
                return true;
            } else {
                return false;
            }
        };

        // store linkables and destinations
        var linkables = [],
            destinations = {};

        $('a').each(function(idx) {
            if (autoLinkable(this)) {
                linkables.push(this);
            } else if(linkDestination(this)) {
                // strip initial "al-" from ID
                var m = this.id.match(/^al-(.*)/);
                if (!m) {
                    console.log('internal error: not al-dest: ' + this.id);
                } else{
                    destinations[m[1]] = this;
                }
            }
        });

        // store in-document destinations for auto-linking, and turn
        // linkable anchors into links.

        destByCollDoc = destinations;

        for (var i=0; i<linkables.length; i++) {
            createDocumentationLink($(linkables[i]), collections);
        }
    };

    // mapping between class attribute values and functions that
    // should be used to parse the content of elements with those
    // classes.
    var parseFunctionMap = {
        // Stanford dependencies
        '.sdparse' : parseSd,
        '.language-sdparse' : parseSd, 
        '.sd-parse' : parseSd, // deprecated, avoid using

        // CoNLL-X
        '.conllx-parse' : parseConllX,
        '.language-conllx' : parseConllX,

        // CoNLL-U
        '.conllu': parseConllU,
        '.conllu-parse' : parseConllU,
        '.language-conllu' : parseConllU,

        // .ann standoff
        '.ann-annotation' : parseAnn,
        '.language-ann' : parseAnn,
    };

    // helper for activate()
    var findParseFunction = function(element) {
        var classes = element.className.split(' '),
            parseFunction;
        for (var i=0; i<classes.length; i++) {
            var dotClass = '.' + classes[i];
            if (parseFunctionMap[dotClass] !== undefined) {
                parseFunction = parseFunctionMap[dotClass];
                break;
            }
        }
        if (parseFunction === undefined) {
            console.log('internal error: missing parse function');
        }        
        return parseFunction;
    };

    // list of [element, parseFunction, collectionData] triplets
    // specifying elements that are to be replaced by embedded
    // visualizations but have not been processed yet.
    var pendingEmbeddings = [];

    // trigger the first of pendingEmbeddings
    var embedFirstPending = function() {
        if (pendingEmbeddings.length === 0) {
            return false;
        }

        var pending = pendingEmbeddings.shift();
        var element = pending[0],
            parseFunction = pending[1],
            collectionData = pending[2];
        embedAnnotation(element, parseFunction, collectionData);
        
        return true;
    };

    // set jQuery waypoint for first of pendingEmbeddings to trigger
    // callback when reached.
    var waypointFirstPending = function(callback, offset) {
        if (callback === undefined) {
            callback = embedFirstPending;
        }
        if (offset === undefined) {
            offset = '100%'; // bottom of screen
        }
        if (pendingEmbeddings.length === 0) {
            return false;
        }
        var pending = pendingEmbeddings[0];
        var element = pending[0];

        var wpOptions = {
            'offset' : offset,
            'triggerOnce': true,
        };

        element.waypoint(callback, wpOptions);
    };

    // embed first of pendingEmbeddings and set waypoint on the next
    // to call this same function back ("recurse") when triggered.
    var embedAndWaypointPending = function() {
        embedFirstPending();

        // delay recursion to allow render
        setTimeout(function() {
            // refresh required due to height changes from embedding
            $.waypoints('refresh');
            waypointFirstPending(embedAndWaypointPending, '200%');
        }, 10);
    };

    // trigger all pendingEmbeddings
    var embedAllPending = function() {
        while (pendingEmbeddings.length !== 0) {
            embedFirstPending();
        }
    };

    // generate embedded visualizations
    var createEmbeddedVisualizations = function(collectionData) {
        // to avoid slowdown with many embedded visualizations, don't
        // trigger full embedding for everything immediately, but
        // only store required data
        var embedClasses = Object.keys(parseFunctionMap);
        $(embedClasses.join(',')).each(function(idx) {
            // (see http://stackoverflow.com/a/122704 for extend())
            var element = $(this),
                parseFunction = findParseFunction(this),
                cdataCopy = jQuery.extend(true, {}, collectionData);
            // hide to minimize "jumping" on draw
            // (https://github.com/spyysalo/annodoc/issues/11)
            element.hide();
            pendingEmbeddings.push([element, parseFunction, cdataCopy]);
        });

        // to simply embed everything immediately instead of
        // attempting to space things out, just uncomment the
        // following line.
//         embedAllPending();

        // embed first of pending embeddings and set waypoint to
        // trigger embedding for the next and recurse.
        embedAndWaypointPending();
    };
    
    // top-level function to call to embed visualizations,
    // create documentation links, etc.
    var activate = function(collectionData, documentCollections) {
        // generate embedded visualization for each element with
        // an appropriate class
        createEmbeddedVisualizations(collectionData);

        // fill in "href" attribute for <a> elements intended to
        // refer to documentation
        createDocumentationLinks(documentCollections);

        // resolve references (e.g. "Figure #" -> "Figure 1"): call
        // resolveEmbeddedReference for elements with "embed-ref" class.
        $('.embed-ref').each(function(idx) {
            resolveEmbeddedReference($(this));
        });
    };

    return {
        activate: activate,
    };
})(jQuery, window);
// -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; -*-
// vim:set ft=javascript ts=2 sw=2 sts=2 cindent:

var AnnotationLog = (function(window, undefined) {
    var AnnotationLog = function(dispatcher) {
      var annotationLoggingOn = false;
      var currentCollection = null;
      var currentDocument = null;
      var currentArguments = null;

      var rememberLoggingState = function(response) {
        annotationLoggingOn = response.annotation_logging;
      };

      var rememberCurrent = function(_collection, _document, _arguments) {
        currentCollection = _collection;
        currentDocument = _document;
        currentArguments = _arguments;
      };

      var logAction = function(_action) {
        if (!annotationLoggingOn) {
          // logging not requested for current collection
          return false;
        } else {
          dispatcher.post('ajax', [ {
            action: 'logAnnotatorAction',
            collection: currentCollection,
            'document': currentDocument,
            log: _action,
          }, null]);
        }
      }

      dispatcher.
          on('collectionLoaded', rememberLoggingState).
          on('current', rememberCurrent).
          on('logAction', logAction);
    }

    return AnnotationLog;
})(window);
var Collections = (function(window, undefined) {
    /*
     * Listing of all the collections and documents. Generated
     * automatically by Jekyll and used e.g for the auto-linking
     * feature. Don't edit this file unless you are sure what you are
     * doing.
     */
    var listing = {
	
        "u-pos": [
            {
                "title": "ADJ",
                "url":   "/u/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/u/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/u/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/u/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/u/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/u/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/u/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/u/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/u/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/u/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/u/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/u/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/u/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/u/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/u/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/u/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/u/pos/X.html",
            },
        ],

        "u-feat": [
            {
                "title": "Animacy",
                "url":   "/u/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/u/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/u/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/u/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/u/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/u/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/u/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/u/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/u/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/u/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/u/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/u/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/u/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/u/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/u/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/u/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/u/feat/Voice.html",
            },
        ],

        "u-dep": [
            {
                "title": "acl",
                "url":   "/u/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/u/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/u/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/u/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/u/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/u/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/u/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/u/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/u/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/u/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/u/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/u/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/u/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/u/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/u/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/u/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/u/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/u/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/u/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/u/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/u/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/u/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/u/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/u/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/u/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/u/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/u/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/u/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/u/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/u/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/u/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/u/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/u/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/u/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/u/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/u/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/u/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/u/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/u/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/u/dep/xcomp.html",
            },
        ],

        "u-overview": [
            {
                "title": "Universal Dependencies",
                "url":   "/u/dep/all.html",
            },
            {
                "title": "Universal Dependencies",
                "url":   "/u/dep/index.html",
            },
            {
                "title": "Universal features",
                "url":   "/u/feat/all.html",
            },
            {
                "title": "Universal features",
                "url":   "/u/feat/index.html",
            },
            {
                "title": "Layered universal features",
                "url":   "/u/overview/feat-layers.html",
            },
            {
                "title": "Morphology",
                "url":   "/u/overview/morphology.html",
            },
            {
                "title": "Universal POS tags",
                "url":   "/u/pos/all.html",
            },
            {
                "title": "Universal POS tags",
                "url":   "/u/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/u/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/u/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/u/overview/tokenization.html",
            },
        ],

        "ext-feat": [
            {
                "title": "Abbr",
                "url":   "/u/ext-feat/Abbr.html",
            },
            {
                "title": "AbsErgDatNumber",
                "url":   "/u/ext-feat/AbsErgDatNumber.html",
            },
            {
                "title": "AbsErgDatPerson",
                "url":   "/u/ext-feat/AbsErgDatPerson.html",
            },
            {
                "title": "AbsErgDatPolite",
                "url":   "/u/ext-feat/AbsErgDatPolite.html",
            },
            {
                "title": "AdpType",
                "url":   "/u/ext-feat/AdpType.html",
            },
            {
                "title": "AdvType",
                "url":   "/u/ext-feat/AdvType.html",
            },
            {
                "title": "ConjType",
                "url":   "/u/ext-feat/ConjType.html",
            },
            {
                "title": "Echo",
                "url":   "/u/ext-feat/Echo.html",
            },
            {
                "title": "ErgDatGender",
                "url":   "/u/ext-feat/ErgDatGender.html",
            },
            {
                "title": "Foreign",
                "url":   "/u/ext-feat/Foreign.html",
            },
            {
                "title": "Hyph",
                "url":   "/u/ext-feat/Hyph.html",
            },
            {
                "title": "NameType",
                "url":   "/u/ext-feat/NameType.html",
            },
            {
                "title": "NounType",
                "url":   "/u/ext-feat/NounType.html",
            },
            {
                "title": "NumForm",
                "url":   "/u/ext-feat/NumForm.html",
            },
            {
                "title": "NumValue",
                "url":   "/u/ext-feat/NumValue.html",
            },
            {
                "title": "PartType",
                "url":   "/u/ext-feat/PartType.html",
            },
            {
                "title": "Polite",
                "url":   "/u/ext-feat/Polite.html",
            },
            {
                "title": "PossGender",
                "url":   "/u/ext-feat/PossGender.html",
            },
            {
                "title": "PossNumber",
                "url":   "/u/ext-feat/PossNumber.html",
            },
            {
                "title": "PossPerson",
                "url":   "/u/ext-feat/PossPerson.html",
            },
            {
                "title": "PossedNumber",
                "url":   "/u/ext-feat/PossedNumber.html",
            },
            {
                "title": "Prefix",
                "url":   "/u/ext-feat/Prefix.html",
            },
            {
                "title": "PrepCase",
                "url":   "/u/ext-feat/PrepCase.html",
            },
            {
                "title": "PunctSide",
                "url":   "/u/ext-feat/PunctSide.html",
            },
            {
                "title": "PunctType",
                "url":   "/u/ext-feat/PunctType.html",
            },
            {
                "title": "Style",
                "url":   "/u/ext-feat/Style.html",
            },
            {
                "title": "Subcat",
                "url":   "/u/ext-feat/Subcat.html",
            },
            {
                "title": "Typo",
                "url":   "/u/ext-feat/Typo.html",
            },
            {
                "title": "VerbType",
                "url":   "/u/ext-feat/VerbType.html",
            },
        ],

        "en-pos": [
            {
                "title": "ADJ",
                "url":   "/en/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/en/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/en/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/en/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/en/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/en/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/en/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/en/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/en/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/en/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/en/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/en/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/en/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/en/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/en/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/en/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/en/pos/X.html",
            },
        ],

        "en-feat": [
            {
                "title": "Animacy",
                "url":   "/en/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/en/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/en/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/en/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/en/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/en/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/en/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/en/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/en/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/en/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/en/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/en/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/en/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/en/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/en/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/en/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/en/feat/Voice.html",
            },
        ],

        "en-dep": [
            {
                "title": "acl:relcl",
                "url":   "/en/dep/acl-relcl.html",
            },
            {
                "title": "acl",
                "url":   "/en/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/en/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/en/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/en/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/en/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/en/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/en/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/en/dep/case.html",
            },
            {
                "title": "cc:preconj",
                "url":   "/en/dep/cc-preconj.html",
            },
            {
                "title": "cc",
                "url":   "/en/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/en/dep/ccomp.html",
            },
            {
                "title": "compound:prt",
                "url":   "/en/dep/compound-prt.html",
            },
            {
                "title": "compound",
                "url":   "/en/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/en/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/en/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/en/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/en/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/en/dep/dep.html",
            },
            {
                "title": "det:predet",
                "url":   "/en/dep/det-predet.html",
            },
            {
                "title": "det",
                "url":   "/en/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/en/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/en/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/en/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/en/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/en/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/en/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/en/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/en/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/en/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/en/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/en/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/en/dep/neg.html",
            },
            {
                "title": "nmod:npmod",
                "url":   "/en/dep/nmod-npmod.html",
            },
            {
                "title": "nmod:poss",
                "url":   "/en/dep/nmod-poss.html",
            },
            {
                "title": "nmod:tmod",
                "url":   "/en/dep/nmod-tmod.html",
            },
            {
                "title": "nmod",
                "url":   "/en/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/en/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/en/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/en/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/en/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/en/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/en/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/en/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/en/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/en/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/en/dep/xcomp.html",
            },
        ],

        "en-overview": [
            {
                "title": "English grammatical relations",
                "url":   "/en/dep/all.html",
            },
            {
                "title": "English grammatical relations",
                "url":   "/en/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/en/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/en/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/en/overview/introduction.html",
            },
            {
                "title": "Migration Guidelines",
                "url":   "/en/overview/migration-guidelines.html",
            },
            {
                "title": "Morphology",
                "url":   "/en/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/en/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/en/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/en/overview/specific-syntax.html",
            },
            {
                "title": "Structure",
                "url":   "/en/overview/syntax.html",
            },
            {
                "title": "Morphology",
                "url":   "/en/overview/tokenization.html",
            },
        ],

        "fi-pos": [
            {
                "title": "ADJ",
                "url":   "/fi/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/fi/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/fi/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/fi/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/fi/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/fi/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/fi/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/fi/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/fi/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/fi/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/fi/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/fi/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/fi/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/fi/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/fi/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/fi/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/fi/pos/X.html",
            },
        ],

        "fi-feat": [
            {
                "title": "Abbr",
                "url":   "/fi/feat/Abbr.html",
            },
            {
                "title": "Case",
                "url":   "/fi/feat/Case.html",
            },
            {
                "title": "Clitic",
                "url":   "/fi/feat/Clitic.html",
            },
            {
                "title": "Connegative",
                "url":   "/fi/feat/Connegative.html",
            },
            {
                "title": "Degree",
                "url":   "/fi/feat/Degree.html",
            },
            {
                "title": "Derivation",
                "url":   "/fi/feat/Derivation.html",
            },
            {
                "title": "Foreign",
                "url":   "/fi/feat/Foreign.html",
            },
            {
                "title": "InfForm",
                "url":   "/fi/feat/InfForm.html",
            },
            {
                "title": "Mood",
                "url":   "/fi/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/fi/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/fi/feat/NumType.html",
            },
            {
                "title": "Number[psor]",
                "url":   "/fi/feat/Number-psor.html",
            },
            {
                "title": "Number",
                "url":   "/fi/feat/Number.html",
            },
            {
                "title": "PartForm",
                "url":   "/fi/feat/PartForm.html",
            },
            {
                "title": "Person[psor]",
                "url":   "/fi/feat/Person-psor.html",
            },
            {
                "title": "Person",
                "url":   "/fi/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/fi/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/fi/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/fi/feat/Reflex.html",
            },
            {
                "title": "Style",
                "url":   "/fi/feat/Style.html",
            },
            {
                "title": "Tense",
                "url":   "/fi/feat/Tense.html",
            },
            {
                "title": "Typo",
                "url":   "/fi/feat/Typo.html",
            },
            {
                "title": "VerbForm",
                "url":   "/fi/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/fi/feat/Voice.html",
            },
        ],

        "fi-dep": [
            {
                "title": "acl:relcl",
                "url":   "/fi/dep/acl-relcl.html",
            },
            {
                "title": "acl",
                "url":   "/fi/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/fi/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/fi/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/fi/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/fi/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/fi/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/fi/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/fi/dep/case.html",
            },
            {
                "title": "cc:preconj",
                "url":   "/fi/dep/cc-preconj.html",
            },
            {
                "title": "cc",
                "url":   "/fi/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/fi/dep/ccomp.html",
            },
            {
                "title": "compound:nn",
                "url":   "/fi/dep/compound-nn.html",
            },
            {
                "title": "compound:prt",
                "url":   "/fi/dep/compound-prt.html",
            },
            {
                "title": "compound",
                "url":   "/fi/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/fi/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/fi/dep/cop.html",
            },
            {
                "title": "csubj:cop",
                "url":   "/fi/dep/csubj-cop.html",
            },
            {
                "title": "csubj",
                "url":   "/fi/dep/csubj.html",
            },
            {
                "title": "dep",
                "url":   "/fi/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/fi/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/fi/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/fi/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/fi/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/fi/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/fi/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/fi/dep/goeswith.html",
            },
            {
                "title": "list",
                "url":   "/fi/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/fi/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/fi/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/fi/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/fi/dep/neg.html",
            },
            {
                "title": "nmod:gobj",
                "url":   "/fi/dep/nmod-gobj.html",
            },
            {
                "title": "nmod:gsubj",
                "url":   "/fi/dep/nmod-gsubj.html",
            },
            {
                "title": "nmod:own",
                "url":   "/fi/dep/nmod-own.html",
            },
            {
                "title": "nmod:poss",
                "url":   "/fi/dep/nmod-poss.html",
            },
            {
                "title": "nmod",
                "url":   "/fi/dep/nmod.html",
            },
            {
                "title": "nsubj:cop",
                "url":   "/fi/dep/nsubj-cop.html",
            },
            {
                "title": "nsubj",
                "url":   "/fi/dep/nsubj.html",
            },
            {
                "title": "nummod",
                "url":   "/fi/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/fi/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/fi/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/fi/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/fi/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/fi/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/fi/dep/vocative.html",
            },
            {
                "title": "xcomp:ds",
                "url":   "/fi/dep/xcomp-ds.html",
            },
            {
                "title": "xcomp",
                "url":   "/fi/dep/xcomp.html",
            },
        ],

        "fi-overview": [
            {
                "title": "FTB Introduction",
                "url":   "/fi-overview/FTB-introduction.html",
            },
            {
                "title": "FTB tokenization",
                "url":   "/fi-overview/FTB-tokenization.html",
            },
            {
                "title": "FTB1 Introduction",
                "url":   "/fi-overview/FTB1introduction.html",
            },
            {
                "title": "FTB1 tokenization",
                "url":   "/fi-overview/FTB1tokenization.html",
            },
            {
                "title": "Finnish grammatical relations",
                "url":   "/fi/dep/all.html",
            },
            {
                "title": "Finnish Relations",
                "url":   "/fi/dep/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/fi/overview/extra.html",
            },
            {
                "title": "Features",
                "url":   "/fi/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/fi/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/fi/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/fi/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/fi/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/fi/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/fi/overview/specific-syntax.html",
            },
            {
                "title": "Structure",
                "url":   "/fi/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/fi/overview/tokenization.html",
            },
        ],

        "sv-pos": [
            {
                "title": "ADJ",
                "url":   "/sv/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/sv/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/sv/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/sv/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/sv/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/sv/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/sv/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/sv/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/sv/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/sv/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/sv/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/sv/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/sv/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/sv/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/sv/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/sv/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/sv/pos/X.html",
            },
        ],

        "sv-feat": [
            {
                "title": "Case",
                "url":   "/sv/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/sv/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/sv/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/sv/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/sv/feat/Mood.html",
            },
            {
                "title": "Number",
                "url":   "/sv/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/sv/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/sv/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/sv/feat/PronType.html",
            },
            {
                "title": "Tense",
                "url":   "/sv/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/sv/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/sv/feat/Voice.html",
            },
        ],

        "sv-dep": [
            {
                "title": "acl:relcl",
                "url":   "/sv/dep/acl-relcl.html",
            },
            {
                "title": "acl",
                "url":   "/sv/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/sv/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/sv/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/sv/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/sv/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/sv/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/sv/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/sv/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/sv/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/sv/dep/ccomp.html",
            },
            {
                "title": "compound:prt",
                "url":   "/sv/dep/compound-prt.html",
            },
            {
                "title": "compound",
                "url":   "/sv/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/sv/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/sv/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/sv/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/sv/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/sv/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/sv/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/sv/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/sv/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/sv/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/sv/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/sv/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/sv/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/sv/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/sv/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/sv/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/sv/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/sv/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/sv/dep/neg.html",
            },
            {
                "title": "nmod:agent",
                "url":   "/sv/dep/nmod-agent.html",
            },
            {
                "title": "nmod:poss",
                "url":   "/sv/dep/nmod-poss.html",
            },
            {
                "title": "nmod",
                "url":   "/sv/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/sv/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/sv/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/sv/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/sv/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/sv/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/sv/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/sv/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/sv/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/sv/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/sv/dep/xcomp.html",
            },
        ],

        "sv-overview": [
            {
                "title": "Dependencies",
                "url":   "/sv/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/sv/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/sv/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/sv/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/sv/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/sv/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/sv/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/sv/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/sv/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/sv/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/sv/overview/tokenization.html",
            },
        ],

        "hu-pos": [
            {
                "title": "ADJ",
                "url":   "/hu/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/hu/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/hu/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/hu/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/hu/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/hu/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/hu/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/hu/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/hu/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/hu/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/hu/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/hu/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/hu/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/hu/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/hu/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/hu/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/hu/pos/X.html",
            },
        ],

        "hu-feat": [
            {
                "title": "Animacy",
                "url":   "/hu/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/hu/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/hu/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/hu/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/hu/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/hu/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/hu/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/hu/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/hu/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/hu/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/hu/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/hu/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/hu/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/hu/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/hu/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/hu/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/hu/feat/Voice.html",
            },
        ],

        "hu-dep": [
            {
                "title": "acl",
                "url":   "/hu/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/hu/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/hu/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/hu/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/hu/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/hu/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/hu/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/hu/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/hu/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/hu/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/hu/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/hu/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/hu/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/hu/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/hu/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/hu/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/hu/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/hu/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/hu/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/hu/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/hu/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/hu/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/hu/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/hu/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/hu/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/hu/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/hu/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/hu/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/hu/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/hu/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/hu/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/hu/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/hu/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/hu/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/hu/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/hu/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/hu/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/hu/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/hu/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/hu/dep/xcomp.html",
            },
        ],

        "hu-overview": [
            {
                "title": "Dependencies",
                "url":   "/hu/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/hu/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/hu/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/hu/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/hu/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/hu/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/hu/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/hu/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/hu/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/hu/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/hu/overview/tokenization.html",
            },
        ],

        "ga-pos": [
            {
                "title": "ADJ",
                "url":   "/ga/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/ga/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/ga/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/ga/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/ga/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/ga/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/ga/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/ga/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/ga/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/ga/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/ga/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/ga/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/ga/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/ga/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/ga/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/ga/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/ga/pos/X.html",
            },
        ],

        "ga-feat": [
            {
                "title": "Animacy",
                "url":   "/ga/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/ga/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/ga/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/ga/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/ga/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/ga/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/ga/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/ga/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/ga/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/ga/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/ga/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/ga/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/ga/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/ga/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/ga/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/ga/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/ga/feat/Voice.html",
            },
        ],

        "ga-dep": [
            {
                "title": "acl:relcl",
                "url":   "/ga/dep/acl-relcl.html",
            },
            {
                "title": "acl",
                "url":   "/ga/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/ga/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/ga/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/ga/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/ga/dep/appos.html",
            },
            {
                "title": "case:voc",
                "url":   "/ga/dep/case-voc.html",
            },
            {
                "title": "case",
                "url":   "/ga/dep/case.html",
            },
            {
                "title": "ccomp",
                "url":   "/ga/dep/ccomp.html",
            },
            {
                "title": "compound:prt",
                "url":   "/ga/dep/compound-prt.html",
            },
            {
                "title": "compound",
                "url":   "/ga/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/ga/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/ga/dep/cop.html",
            },
            {
                "title": "csubj:cleft",
                "url":   "/ga/dep/csubj-cleft.html",
            },
            {
                "title": "csubj:cop",
                "url":   "/ga/dep/csubj-cop.html",
            },
            {
                "title": "csubj",
                "url":   "/ga/dep/csubj.html",
            },
            {
                "title": "dep",
                "url":   "/ga/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/ga/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/ga/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/ga/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/ga/dep/dobj.html",
            },
            {
                "title": "foreign",
                "url":   "/ga/dep/foreign.html",
            },
            {
                "title": "list",
                "url":   "/ga/dep/list.html",
            },
            {
                "title": "mark:prt",
                "url":   "/ga/dep/mark-prt.html",
            },
            {
                "title": "mark",
                "url":   "/ga/dep/mark.html",
            },
            {
                "title": "name",
                "url":   "/ga/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/ga/dep/neg.html",
            },
            {
                "title": "nmod:poss",
                "url":   "/ga/dep/nmod-poss.html",
            },
            {
                "title": "nmod:prep",
                "url":   "/ga/dep/nmod-prep.html",
            },
            {
                "title": "nmod:tmod",
                "url":   "/ga/dep/nmod-tmod.html",
            },
            {
                "title": "nmod",
                "url":   "/ga/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/ga/dep/nsubj.html",
            },
            {
                "title": "nummod",
                "url":   "/ga/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/ga/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/ga/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/ga/dep/remnant.html",
            },
            {
                "title": "root",
                "url":   "/ga/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/ga/dep/vocative.html",
            },
            {
                "title": "xcomp:pred",
                "url":   "/ga/dep/xcomp-pred.html",
            },
            {
                "title": "xcomp",
                "url":   "/ga/dep/xcomp.html",
            },
        ],

        "ga-overview": [
            {
                "title": "Dependencies",
                "url":   "/ga/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/ga/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/ga/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/ga/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/ga/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/ga/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/ga/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/ga/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/ga/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/ga/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/ga/overview/tokenization.html",
            },
        ],

        "bg-pos": [
            {
                "title": "ADJ",
                "url":   "/bg/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/bg/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/bg/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/bg/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/bg/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/bg/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/bg/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/bg/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/bg/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/bg/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/bg/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/bg/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/bg/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/bg/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/bg/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/bg/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/bg/pos/X.html",
            },
        ],

        "bg-feat": [
            {
                "title": "Animacy",
                "url":   "/bg/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/bg/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/bg/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/bg/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/bg/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/bg/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/bg/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/bg/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/bg/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/bg/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/bg/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/bg/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/bg/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/bg/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/bg/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/bg/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/bg/feat/Voice.html",
            },
        ],

        "bg-dep": [
            {
                "title": "acl",
                "url":   "/bg/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/bg/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/bg/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/bg/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/bg/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/bg/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/bg/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/bg/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/bg/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/bg/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/bg/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/bg/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/bg/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/bg/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/bg/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/bg/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/bg/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/bg/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/bg/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/bg/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/bg/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/bg/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/bg/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/bg/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/bg/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/bg/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/bg/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/bg/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/bg/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/bg/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/bg/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/bg/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/bg/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/bg/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/bg/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/bg/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/bg/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/bg/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/bg/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/bg/dep/xcomp.html",
            },
        ],

        "bg-overview": [
            {
                "title": "Dependencies",
                "url":   "/bg/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/bg/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/bg/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/bg/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/bg/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/bg/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/bg/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/bg/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/bg/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/bg/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/bg/overview/tokenization.html",
            },
        ],

        "fa-pos": [
            {
                "title": "ADJ",
                "url":   "/fa/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/fa/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/fa/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/fa/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/fa/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/fa/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/fa/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/fa/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/fa/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/fa/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/fa/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/fa/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/fa/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/fa/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/fa/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/fa/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/fa/pos/X.html",
            },
        ],

        "fa-feat": [
            {
                "title": "Animacy",
                "url":   "/fa/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/fa/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/fa/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/fa/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/fa/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/fa/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/fa/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/fa/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/fa/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/fa/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/fa/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/fa/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/fa/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/fa/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/fa/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/fa/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/fa/feat/Voice.html",
            },
        ],

        "fa-dep": [
            {
                "title": "acl",
                "url":   "/fa/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/fa/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/fa/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/fa/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/fa/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/fa/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/fa/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/fa/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/fa/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/fa/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/fa/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/fa/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/fa/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/fa/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/fa/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/fa/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/fa/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/fa/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/fa/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/fa/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/fa/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/fa/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/fa/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/fa/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/fa/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/fa/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/fa/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/fa/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/fa/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/fa/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/fa/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/fa/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/fa/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/fa/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/fa/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/fa/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/fa/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/fa/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/fa/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/fa/dep/xcomp.html",
            },
        ],

        "fa-overview": [
            {
                "title": "Dependencies",
                "url":   "/fa/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/fa/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/fa/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/fa/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/fa/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/fa/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/fa/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/fa/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/fa/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/fa/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/fa/overview/tokenization.html",
            },
        ],

        "cs-pos": [
            {
                "title": "ADJ",
                "url":   "/cs/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/cs/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/cs/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/cs/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/cs/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/cs/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/cs/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/cs/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/cs/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/cs/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/cs/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/cs/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/cs/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/cs/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/cs/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/cs/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/cs/pos/X.html",
            },
        ],

        "cs-feat": [
            {
                "title": "Abbr",
                "url":   "/cs/feat/Abbr.html",
            },
            {
                "title": "AdpType",
                "url":   "/cs/feat/AdpType.html",
            },
            {
                "title": "Animacy",
                "url":   "/cs/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/cs/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/cs/feat/Case.html",
            },
            {
                "title": "ConjType",
                "url":   "/cs/feat/ConjType.html",
            },
            {
                "title": "Degree",
                "url":   "/cs/feat/Degree.html",
            },
            {
                "title": "Foreign",
                "url":   "/cs/feat/Foreign.html",
            },
            {
                "title": "Gender[psor]",
                "url":   "/cs/feat/Gender-psor.html",
            },
            {
                "title": "Gender",
                "url":   "/cs/feat/Gender.html",
            },
            {
                "title": "Hyph",
                "url":   "/cs/feat/Hyph.html",
            },
            {
                "title": "Mood",
                "url":   "/cs/feat/Mood.html",
            },
            {
                "title": "NameType",
                "url":   "/cs/feat/NameType.html",
            },
            {
                "title": "Negative",
                "url":   "/cs/feat/Negative.html",
            },
            {
                "title": "NumForm",
                "url":   "/cs/feat/NumForm.html",
            },
            {
                "title": "NumType",
                "url":   "/cs/feat/NumType.html",
            },
            {
                "title": "NumValue",
                "url":   "/cs/feat/NumValue.html",
            },
            {
                "title": "Number[psor]",
                "url":   "/cs/feat/Number-psor.html",
            },
            {
                "title": "Number",
                "url":   "/cs/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/cs/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/cs/feat/Poss.html",
            },
            {
                "title": "PrepCase",
                "url":   "/cs/feat/PrepCase.html",
            },
            {
                "title": "PronType",
                "url":   "/cs/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/cs/feat/Reflex.html",
            },
            {
                "title": "Style",
                "url":   "/cs/feat/Style.html",
            },
            {
                "title": "Tense",
                "url":   "/cs/feat/Tense.html",
            },
            {
                "title": "Variant",
                "url":   "/cs/feat/Variant.html",
            },
            {
                "title": "VerbForm",
                "url":   "/cs/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/cs/feat/Voice.html",
            },
        ],

        "cs-dep": [
            {
                "title": "acl",
                "url":   "/cs/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/cs/dep/advcl.html",
            },
            {
                "title": "advmod:emph",
                "url":   "/cs/dep/advmod-emph.html",
            },
            {
                "title": "advmod",
                "url":   "/cs/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/cs/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/cs/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/cs/dep/aux_.html",
            },
            {
                "title": "auxpass:reflex",
                "url":   "/cs/dep/auxpass-reflex.html",
            },
            {
                "title": "auxpass",
                "url":   "/cs/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/cs/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/cs/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/cs/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/cs/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/cs/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/cs/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/cs/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/cs/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/cs/dep/dep.html",
            },
            {
                "title": "det:numgov",
                "url":   "/cs/dep/det-numgov.html",
            },
            {
                "title": "det:nummod",
                "url":   "/cs/dep/det-nummod.html",
            },
            {
                "title": "det",
                "url":   "/cs/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/cs/dep/discourse.html",
            },
            {
                "title": "dobj",
                "url":   "/cs/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/cs/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/cs/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/cs/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/cs/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/cs/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/cs/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/cs/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/cs/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/cs/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/cs/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/cs/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/cs/dep/nsubjpass.html",
            },
            {
                "title": "nummod:gov",
                "url":   "/cs/dep/nummod-gov.html",
            },
            {
                "title": "nummod",
                "url":   "/cs/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/cs/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/cs/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/cs/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/cs/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/cs/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/cs/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/cs/dep/xcomp.html",
            },
        ],

        "cs-overview": [
            {
                "title": "Dependencies",
                "url":   "/cs/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/cs/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/cs/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/cs/feat/index.html",
            },
            {
                "title": "Czech Format Extension",
                "url":   "/cs/overview/format.html",
            },
            {
                "title": "Introduction",
                "url":   "/cs/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/cs/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/cs/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/cs/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/cs/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/cs/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/cs/overview/tokenization.html",
            },
        ],

        "he-pos": [
            {
                "title": "ADJ",
                "url":   "/he/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/he/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/he/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/he/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/he/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/he/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/he/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/he/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/he/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/he/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/he/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/he/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/he/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/he/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/he/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/he/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/he/pos/X.html",
            },
        ],

        "he-feat": [
            {
                "title": "Animacy",
                "url":   "/he/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/he/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/he/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/he/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/he/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/he/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/he/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/he/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/he/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/he/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/he/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/he/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/he/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/he/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/he/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/he/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/he/feat/Voice.html",
            },
        ],

        "he-dep": [
            {
                "title": "acl",
                "url":   "/he/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/he/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/he/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/he/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/he/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/he/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/he/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/he/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/he/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/he/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/he/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/he/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/he/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/he/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/he/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/he/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/he/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/he/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/he/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/he/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/he/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/he/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/he/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/he/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/he/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/he/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/he/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/he/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/he/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/he/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/he/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/he/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/he/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/he/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/he/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/he/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/he/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/he/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/he/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/he/dep/xcomp.html",
            },
        ],

        "he-overview": [
            {
                "title": "Dependencies",
                "url":   "/he/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/he/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/he/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/he/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/he/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/he/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/he/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/he/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/he/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/he/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/he/overview/tokenization.html",
            },
        ],

        "es-pos": [
            {
                "title": "ADJ",
                "url":   "/es/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/es/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/es/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/es/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/es/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/es/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/es/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/es/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/es/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/es/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/es/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/es/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/es/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/es/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/es/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/es/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/es/pos/X.html",
            },
        ],

        "es-feat": [
            {
                "title": "Animacy",
                "url":   "/es/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/es/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/es/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/es/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/es/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/es/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/es/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/es/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/es/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/es/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/es/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/es/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/es/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/es/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/es/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/es/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/es/feat/Voice.html",
            },
        ],

        "es-dep": [
            {
                "title": "acl",
                "url":   "/es/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/es/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/es/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/es/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/es/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/es/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/es/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/es/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/es/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/es/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/es/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/es/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/es/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/es/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/es/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/es/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/es/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/es/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/es/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/es/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/es/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/es/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/es/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/es/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/es/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/es/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/es/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/es/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/es/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/es/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/es/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/es/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/es/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/es/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/es/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/es/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/es/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/es/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/es/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/es/dep/xcomp.html",
            },
        ],

        "es-overview": [
            {
                "title": "Dependencies",
                "url":   "/es/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/es/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/es/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/es/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/es/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/es/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/es/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/es/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/es/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/es/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/es/overview/tokenization.html",
            },
        ],

        "fr-pos": [
            {
                "title": "ADJ",
                "url":   "/fr/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/fr/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/fr/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/fr/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/fr/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/fr/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/fr/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/fr/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/fr/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/fr/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/fr/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/fr/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/fr/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/fr/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/fr/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/fr/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/fr/pos/X.html",
            },
        ],

        "fr-feat": [
            {
                "title": "Animacy",
                "url":   "/fr/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/fr/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/fr/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/fr/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/fr/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/fr/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/fr/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/fr/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/fr/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/fr/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/fr/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/fr/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/fr/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/fr/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/fr/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/fr/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/fr/feat/Voice.html",
            },
        ],

        "fr-dep": [
            {
                "title": "acl:relcl",
                "url":   "/fr/dep/acl-relcl.html",
            },
            {
                "title": "acl",
                "url":   "/fr/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/fr/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/fr/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/fr/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/fr/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/fr/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/fr/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/fr/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/fr/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/fr/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/fr/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/fr/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/fr/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/fr/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/fr/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/fr/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/fr/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/fr/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/fr/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/fr/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/fr/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/fr/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/fr/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/fr/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/fr/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/fr/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/fr/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/fr/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/fr/dep/neg.html",
            },
            {
                "title": "nmod:poss",
                "url":   "/fr/dep/nmod-poss.html",
            },
            {
                "title": "nmod",
                "url":   "/fr/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/fr/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/fr/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/fr/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/fr/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/fr/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/fr/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/fr/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/fr/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/fr/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/fr/dep/xcomp.html",
            },
        ],

        "fr-overview": [
            {
                "title": "Dependencies",
                "url":   "/fr/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/fr/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/fr/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/fr/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/fr/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/fr/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/fr/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/fr/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/fr/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/fr/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/fr/overview/tokenization.html",
            },
        ],

        "de-pos": [
            {
                "title": "ADJ",
                "url":   "/de/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/de/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/de/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/de/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/de/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/de/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/de/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/de/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/de/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/de/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/de/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/de/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/de/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/de/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/de/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/de/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/de/pos/X.html",
            },
        ],

        "de-feat": [
            {
                "title": "Animacy",
                "url":   "/de/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/de/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/de/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/de/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/de/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/de/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/de/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/de/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/de/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/de/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/de/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/de/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/de/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/de/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/de/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/de/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/de/feat/Voice.html",
            },
        ],

        "de-dep": [
            {
                "title": "acl:relcl",
                "url":   "/de/dep/acl-relcl.html",
            },
            {
                "title": "acl",
                "url":   "/de/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/de/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/de/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/de/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/de/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/de/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/de/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/de/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/de/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/de/dep/ccomp.html",
            },
            {
                "title": "compound:prt",
                "url":   "/de/dep/compound-prt.html",
            },
            {
                "title": "compound",
                "url":   "/de/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/de/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/de/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/de/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/de/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/de/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/de/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/de/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/de/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/de/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/de/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/de/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/de/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/de/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/de/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/de/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/de/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/de/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/de/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/de/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/de/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/de/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/de/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/de/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/de/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/de/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/de/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/de/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/de/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/de/dep/xcomp.html",
            },
        ],

        "de-overview": [
            {
                "title": "Dependencies",
                "url":   "/de/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/de/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/de/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/de/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/de/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/de/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/de/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/de/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/de/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/de/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/de/overview/tokenization.html",
            },
        ],

        "el-pos": [
            {
                "title": "ADJ",
                "url":   "/el/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/el/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/el/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/el/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/el/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/el/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/el/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/el/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/el/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/el/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/el/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/el/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/el/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/el/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/el/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/el/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/el/pos/X.html",
            },
        ],

        "el-feat": [
            {
                "title": "Animacy",
                "url":   "/el/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/el/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/el/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/el/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/el/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/el/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/el/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/el/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/el/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/el/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/el/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/el/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/el/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/el/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/el/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/el/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/el/feat/Voice.html",
            },
        ],

        "el-dep": [
            {
                "title": "acl:relcl",
                "url":   "/el/dep/acl-rel.html",
            },
            {
                "title": "acl",
                "url":   "/el/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/el/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/el/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/el/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/el/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/el/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/el/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/el/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/el/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/el/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/el/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/el/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/el/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/el/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/el/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/el/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/el/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/el/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/el/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/el/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/el/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/el/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/el/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/el/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/el/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/el/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/el/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/el/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/el/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/el/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/el/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/el/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/el/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/el/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/el/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/el/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/el/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/el/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/el/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/el/dep/xcomp.html",
            },
        ],

        "el-overview": [
            {
                "title": "Dependencies",
                "url":   "/el/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/el/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/el/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/el/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/el/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/el/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/el/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/el/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/el/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/el/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/el/overview/tokenization.html",
            },
        ],

        "eu-pos": [
            {
                "title": "ADJ",
                "url":   "/eu/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/eu/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/eu/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/eu/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/eu/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/eu/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/eu/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/eu/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/eu/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/eu/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/eu/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/eu/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/eu/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/eu/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/eu/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/eu/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/eu/pos/X.html",
            },
        ],

        "eu-feat": [
            {
                "title": "Animacy",
                "url":   "/eu/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/eu/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/eu/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/eu/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/eu/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/eu/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/eu/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/eu/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/eu/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/eu/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/eu/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/eu/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/eu/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/eu/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/eu/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/eu/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/eu/feat/Voice.html",
            },
        ],

        "eu-dep": [
            {
                "title": "acl",
                "url":   "/eu/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/eu/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/eu/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/eu/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/eu/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/eu/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/eu/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/eu/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/eu/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/eu/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/eu/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/eu/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/eu/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/eu/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/eu/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/eu/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/eu/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/eu/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/eu/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/eu/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/eu/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/eu/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/eu/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/eu/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/eu/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/eu/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/eu/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/eu/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/eu/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/eu/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/eu/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/eu/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/eu/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/eu/dep/parataxis.html",
            },
            {
                "title": "",
                "url":   "/eu/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/eu/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/eu/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/eu/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/eu/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/eu/dep/xcomp.html",
            },
        ],

        "eu-overview": [
            {
                "title": "Dependencies",
                "url":   "/eu/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/eu/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/eu/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/eu/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/eu/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/eu/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/eu/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/eu/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/eu/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/eu/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/eu/overview/tokenization.html",
            },
        ],

        "it-pos": [
            {
                "title": "ADJ",
                "url":   "/it/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/it/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/it/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/it/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/it/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/it/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/it/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/it/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/it/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/it/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/it/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/it/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/it/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/it/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/it/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/it/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/it/pos/X.html",
            },
        ],

        "it-feat": [
            {
                "title": "Animacy",
                "url":   "/it/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/it/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/it/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/it/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/it/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/it/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/it/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/it/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/it/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/it/feat/Number.html",
            },
            {
                "title": "Number",
                "url":   "/it/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/it/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/it/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/it/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/it/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/it/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/it/feat/Voice.html",
            },
        ],

        "it-dep": [
            {
                "title": "acl",
                "url":   "/it/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/it/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/it/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/it/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/it/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/it/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/it/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/it/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/it/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/it/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/it/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/it/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/it/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/it/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/it/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/it/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/it/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/it/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/it/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/it/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/it/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/it/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/it/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/it/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/it/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/it/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/it/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/it/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/it/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/it/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/it/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/it/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/it/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/it/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/it/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/it/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/it/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/it/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/it/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/it/dep/xcomp.html",
            },
        ],

        "it-overview": [
            {
                "title": "Dependencies",
                "url":   "/it/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/it/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/it/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/it/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/it/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/it/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/it/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/it/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/it/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/it/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/it/overview/tokenization.html",
            },
        ],

        "ko-pos": [
            {
                "title": "ADJ",
                "url":   "/ko/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/ko/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/ko/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/ko/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/ko/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/ko/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/ko/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/ko/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/ko/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/ko/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/ko/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/ko/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/ko/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/ko/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/ko/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/ko/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/ko/pos/X.html",
            },
        ],

        "ko-feat": [
            {
                "title": "Animacy",
                "url":   "/ko/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/ko/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/ko/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/ko/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/ko/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/ko/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/ko/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/ko/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/ko/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/ko/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/ko/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/ko/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/ko/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/ko/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/ko/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/ko/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/ko/feat/Voice.html",
            },
        ],

        "ko-dep": [
            {
                "title": "acl",
                "url":   "/ko/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/ko/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/ko/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/ko/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/ko/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/ko/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/ko/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/ko/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/ko/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/ko/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/ko/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/ko/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/ko/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/ko/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/ko/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/ko/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/ko/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/ko/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/ko/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/ko/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/ko/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/ko/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/ko/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/ko/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/ko/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/ko/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/ko/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/ko/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/ko/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/ko/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/ko/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/ko/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/ko/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/ko/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/ko/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/ko/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/ko/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/ko/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/ko/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/ko/dep/xcomp.html",
            },
        ],

        "ko-overview": [
            {
                "title": "Dependencies",
                "url":   "/ko/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/ko/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/ko/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/ko/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/ko/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/ko/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/ko/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/ko/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/ko/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/ko/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/ko/overview/tokenization.html",
            },
        ],

        "ja-pos": [
            {
                "title": "ADJ",
                "url":   "/ja/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/ja/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/ja/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/ja/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/ja/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/ja/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/ja/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/ja/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/ja/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/ja/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/ja/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/ja/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/ja/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/ja/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/ja/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/ja/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/ja/pos/X.html",
            },
        ],

        "ja-feat": [
            {
                "title": "Animacy",
                "url":   "/ja/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/ja/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/ja/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/ja/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/ja/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/ja/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/ja/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/ja/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/ja/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/ja/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/ja/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/ja/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/ja/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/ja/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/ja/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/ja/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/ja/feat/Voice.html",
            },
        ],

        "ja-dep": [
            {
                "title": "acl",
                "url":   "/ja/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/ja/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/ja/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/ja/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/ja/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/ja/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/ja/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/ja/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/ja/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/ja/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/ja/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/ja/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/ja/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/ja/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/ja/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/ja/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/ja/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/ja/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/ja/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/ja/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/ja/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/ja/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/ja/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/ja/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/ja/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/ja/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/ja/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/ja/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/ja/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/ja/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/ja/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/ja/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/ja/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/ja/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/ja/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/ja/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/ja/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/ja/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/ja/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/ja/dep/xcomp.html",
            },
        ],

        "ja-overview": [
            {
                "title": "Dependencies",
                "url":   "/ja/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/ja/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/ja/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/ja/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/ja/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/ja/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/ja/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/ja/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/ja/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/ja/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/ja/overview/tokenization.html",
            },
        ],

        "zh-pos": [
            {
                "title": "ADJ",
                "url":   "/zh/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/zh/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/zh/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/zh/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/zh/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/zh/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/zh/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/zh/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/zh/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/zh/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/zh/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/zh/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/zh/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/zh/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/zh/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/zh/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/zh/pos/X.html",
            },
        ],

        "zh-feat": [
            {
                "title": "Animacy",
                "url":   "/zh/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/zh/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/zh/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/zh/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/zh/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/zh/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/zh/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/zh/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/zh/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/zh/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/zh/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/zh/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/zh/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/zh/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/zh/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/zh/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/zh/feat/Voice.html",
            },
        ],

        "zh-dep": [
            {
                "title": "acl",
                "url":   "/zh/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/zh/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/zh/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/zh/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/zh/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/zh/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/zh/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/zh/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/zh/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/zh/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/zh/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/zh/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/zh/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/zh/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/zh/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/zh/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/zh/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/zh/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/zh/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/zh/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/zh/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/zh/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/zh/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/zh/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/zh/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/zh/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/zh/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/zh/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/zh/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/zh/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/zh/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/zh/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/zh/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/zh/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/zh/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/zh/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/zh/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/zh/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/zh/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/zh/dep/xcomp.html",
            },
        ],

        "zh-overview": [
            {
                "title": "Dependencies",
                "url":   "/zh/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/zh/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/zh/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/zh/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/zh/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/zh/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/zh/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/zh/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/zh/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/zh/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/zh/overview/tokenization.html",
            },
        ],

        "ro-pos": [
            {
                "title": "ADJ",
                "url":   "/ro/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/ro/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/ro/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/ro/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/ro/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/ro/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/ro/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/ro/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/ro/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/ro/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/ro/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/ro/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/ro/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/ro/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/ro/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/ro/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/ro/pos/X.html",
            },
        ],

        "ro-feat": [
            {
                "title": "Animacy",
                "url":   "/ro/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/ro/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/ro/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/ro/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/ro/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/ro/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/ro/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/ro/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/ro/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/ro/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/ro/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/ro/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/ro/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/ro/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/ro/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/ro/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/ro/feat/Voice.html",
            },
        ],

        "ro-dep": [
            {
                "title": "acl",
                "url":   "/ro/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/ro/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/ro/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/ro/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/ro/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/ro/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/ro/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/ro/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/ro/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/ro/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/ro/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/ro/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/ro/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/ro/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/ro/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/ro/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/ro/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/ro/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/ro/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/ro/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/ro/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/ro/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/ro/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/ro/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/ro/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/ro/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/ro/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/ro/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/ro/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/ro/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/ro/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/ro/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/ro/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/ro/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/ro/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/ro/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/ro/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/ro/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/ro/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/ro/dep/xcomp.html",
            },
        ],

        "ro-overview": [
            {
                "title": "Dependencies",
                "url":   "/ro/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/ro/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/ro/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/ro/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/ro/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/ro/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/ro/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/ro/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/ro/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/ro/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/ro/overview/tokenization.html",
            },
        ],

        "sl-pos": [
            {
                "title": "ADJ",
                "url":   "/sl/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/sl/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/sl/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/sl/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/sl/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/sl/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/sl/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/sl/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/sl/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/sl/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/sl/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/sl/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/sl/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/sl/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/sl/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/sl/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/sl/pos/X.html",
            },
        ],

        "sl-feat": [
            {
                "title": "Abbr",
                "url":   "/sl/feat/Abbreviation.html",
            },
            {
                "title": "Animacy",
                "url":   "/sl/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/sl/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/sl/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/sl/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/sl/feat/Degree.html",
            },
            {
                "title": "Foreign",
                "url":   "/sl/feat/Foreign.html",
            },
            {
                "title": "Gender[psor]",
                "url":   "/sl/feat/Gender-psor.html",
            },
            {
                "title": "Gender",
                "url":   "/sl/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/sl/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/sl/feat/Negative.html",
            },
            {
                "title": "NumForm",
                "url":   "/sl/feat/NumForm.html",
            },
            {
                "title": "NumType",
                "url":   "/sl/feat/NumType.html",
            },
            {
                "title": "Number[psor]",
                "url":   "/sl/feat/Number-psor.html",
            },
            {
                "title": "Number",
                "url":   "/sl/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/sl/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/sl/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/sl/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/sl/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/sl/feat/Tense.html",
            },
            {
                "title": "Variant",
                "url":   "/sl/feat/Variant.html",
            },
            {
                "title": "VerbForm",
                "url":   "/sl/feat/VerbForm.html",
            },
        ],

        "sl-dep": [
            {
                "title": "acl",
                "url":   "/sl/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/sl/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/sl/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/sl/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/sl/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/sl/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/sl/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/sl/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/sl/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/sl/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/sl/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/sl/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/sl/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/sl/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/sl/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/sl/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/sl/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/sl/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/sl/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/sl/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/sl/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/sl/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/sl/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/sl/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/sl/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/sl/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/sl/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/sl/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/sl/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/sl/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/sl/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/sl/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/sl/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/sl/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/sl/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/sl/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/sl/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/sl/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/sl/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/sl/dep/xcomp.html",
            },
        ],

        "sl-overview": [
            {
                "title": "Dependencies",
                "url":   "/sl/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/sl/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/sl/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/sl/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/sl/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/sl/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/sl/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/sl/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/sl/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/sl/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/sl/overview/tokenization.html",
            },
        ],

        "da-pos": [
            {
                "title": "ADJ",
                "url":   "/da/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/da/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/da/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/da/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/da/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/da/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/da/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/da/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/da/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/da/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/da/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/da/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/da/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/da/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/da/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/da/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/da/pos/X.html",
            },
        ],

        "da-feat": [
            {
                "title": "Animacy",
                "url":   "/da/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/da/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/da/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/da/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/da/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/da/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/da/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/da/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/da/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/da/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/da/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/da/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/da/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/da/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/da/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/da/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/da/feat/Voice.html",
            },
        ],

        "da-dep": [
            {
                "title": "acl",
                "url":   "/da/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/da/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/da/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/da/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/da/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/da/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/da/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/da/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/da/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/da/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/da/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/da/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/da/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/da/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/da/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/da/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/da/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/da/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/da/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/da/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/da/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/da/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/da/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/da/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/da/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/da/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/da/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/da/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/da/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/da/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/da/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/da/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/da/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/da/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/da/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/da/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/da/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/da/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/da/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/da/dep/xcomp.html",
            },
        ],

        "da-overview": [
            {
                "title": "Dependencies",
                "url":   "/da/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/da/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/da/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/da/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/da/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/da/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/da/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/da/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/da/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/da/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/da/overview/tokenization.html",
            },
        ],

        "hr-pos": [
            {
                "title": "ADJ",
                "url":   "/hr/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/hr/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/hr/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/hr/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/hr/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/hr/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/hr/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/hr/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/hr/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/hr/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/hr/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/hr/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/hr/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/hr/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/hr/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/hr/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/hr/pos/X.html",
            },
        ],

        "hr-feat": [
            {
                "title": "Animacy",
                "url":   "/hr/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/hr/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/hr/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/hr/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/hr/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/hr/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/hr/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/hr/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/hr/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/hr/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/hr/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/hr/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/hr/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/hr/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/hr/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/hr/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/hr/feat/Voice.html",
            },
        ],

        "hr-dep": [
            {
                "title": "acl",
                "url":   "/hr/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/hr/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/hr/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/hr/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/hr/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/hr/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/hr/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/hr/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/hr/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/hr/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/hr/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/hr/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/hr/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/hr/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/hr/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/hr/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/hr/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/hr/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/hr/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/hr/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/hr/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/hr/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/hr/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/hr/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/hr/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/hr/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/hr/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/hr/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/hr/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/hr/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/hr/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/hr/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/hr/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/hr/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/hr/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/hr/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/hr/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/hr/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/hr/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/hr/dep/xcomp.html",
            },
        ],

        "hr-overview": [
            {
                "title": "Dependencies",
                "url":   "/hr/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/hr/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/hr/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/hr/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/hr/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/hr/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/hr/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/hr/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/hr/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/hr/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/hr/overview/tokenization.html",
            },
        ],

        "id-pos": [
            {
                "title": "ADJ",
                "url":   "/id/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/id/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/id/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/id/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/id/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/id/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/id/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/id/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/id/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/id/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/id/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/id/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/id/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/id/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/id/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/id/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/id/pos/X.html",
            },
        ],

        "id-feat": [
            {
                "title": "Animacy",
                "url":   "/id/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/id/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/id/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/id/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/id/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/id/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/id/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/id/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/id/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/id/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/id/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/id/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/id/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/id/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/id/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/id/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/id/feat/Voice.html",
            },
        ],

        "id-dep": [
            {
                "title": "acl",
                "url":   "/id/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/id/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/id/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/id/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/id/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/id/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/id/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/id/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/id/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/id/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/id/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/id/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/id/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/id/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/id/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/id/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/id/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/id/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/id/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/id/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/id/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/id/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/id/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/id/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/id/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/id/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/id/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/id/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/id/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/id/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/id/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/id/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/id/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/id/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/id/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/id/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/id/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/id/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/id/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/id/dep/xcomp.html",
            },
        ],

        "id-overview": [
            {
                "title": "Dependencies",
                "url":   "/id/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/id/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/id/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/id/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/id/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/id/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/id/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/id/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/id/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/id/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/id/overview/tokenization.html",
            },
        ],

        "am-pos": [
            {
                "title": "ADJ",
                "url":   "/am/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/am/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/am/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/am/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/am/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/am/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/am/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/am/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/am/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/am/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/am/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/am/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/am/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/am/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/am/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/am/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/am/pos/X.html",
            },
        ],

        "am-feat": [
            {
                "title": "Animacy",
                "url":   "/am/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/am/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/am/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/am/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/am/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/am/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/am/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/am/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/am/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/am/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/am/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/am/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/am/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/am/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/am/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/am/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/am/feat/Voice.html",
            },
        ],

        "am-dep": [
            {
                "title": "acl",
                "url":   "/am/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/am/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/am/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/am/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/am/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/am/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/am/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/am/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/am/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/am/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/am/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/am/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/am/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/am/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/am/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/am/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/am/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/am/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/am/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/am/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/am/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/am/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/am/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/am/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/am/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/am/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/am/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/am/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/am/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/am/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/am/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/am/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/am/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/am/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/am/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/am/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/am/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/am/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/am/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/am/dep/xcomp.html",
            },
        ],

        "am-overview": [
            {
                "title": "Dependencies",
                "url":   "/am/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/am/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/am/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/am/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/am/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/am/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/am/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/am/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/am/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/am/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/am/overview/tokenization.html",
            },
        ],

        "hi-pos": [
            {
                "title": "ADJ",
                "url":   "/hi/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/hi/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/hi/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/hi/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/hi/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/hi/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/hi/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/hi/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/hi/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/hi/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/hi/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/hi/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/hi/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/hi/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/hi/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/hi/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/hi/pos/X.html",
            },
        ],

        "hi-feat": [
            {
                "title": "Animacy",
                "url":   "/hi/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/hi/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/hi/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/hi/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/hi/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/hi/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/hi/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/hi/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/hi/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/hi/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/hi/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/hi/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/hi/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/hi/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/hi/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/hi/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/hi/feat/Voice.html",
            },
        ],

        "hi-dep": [
            {
                "title": "acl",
                "url":   "/hi/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/hi/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/hi/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/hi/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/hi/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/hi/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/hi/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/hi/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/hi/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/hi/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/hi/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/hi/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/hi/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/hi/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/hi/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/hi/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/hi/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/hi/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/hi/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/hi/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/hi/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/hi/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/hi/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/hi/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/hi/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/hi/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/hi/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/hi/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/hi/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/hi/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/hi/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/hi/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/hi/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/hi/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/hi/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/hi/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/hi/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/hi/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/hi/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/hi/dep/xcomp.html",
            },
        ],

        "hi-overview": [
            {
                "title": "Dependencies",
                "url":   "/hi/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/hi/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/hi/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/hi/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/hi/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/hi/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/hi/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/hi/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/hi/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/hi/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/hi/overview/tokenization.html",
            },
        ],

        "grc-pos": [
            {
                "title": "ADJ",
                "url":   "/grc/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/grc/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/grc/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/grc/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/grc/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/grc/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/grc/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/grc/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/grc/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/grc/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/grc/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/grc/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/grc/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/grc/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/grc/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/grc/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/grc/pos/X.html",
            },
        ],

        "grc-feat": [
            {
                "title": "Animacy",
                "url":   "/grc/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/grc/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/grc/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/grc/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/grc/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/grc/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/grc/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/grc/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/grc/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/grc/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/grc/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/grc/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/grc/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/grc/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/grc/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/grc/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/grc/feat/Voice.html",
            },
        ],

        "grc-dep": [
            {
                "title": "acl",
                "url":   "/grc/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/grc/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/grc/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/grc/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/grc/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/grc/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/grc/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/grc/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/grc/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/grc/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/grc/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/grc/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/grc/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/grc/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/grc/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/grc/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/grc/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/grc/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/grc/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/grc/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/grc/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/grc/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/grc/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/grc/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/grc/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/grc/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/grc/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/grc/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/grc/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/grc/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/grc/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/grc/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/grc/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/grc/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/grc/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/grc/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/grc/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/grc/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/grc/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/grc/dep/xcomp.html",
            },
        ],

        "grc-overview": [
            {
                "title": "Dependencies",
                "url":   "/grc/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/grc/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/grc/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/grc/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/grc/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/grc/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/grc/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/grc/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/grc/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/grc/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/grc/overview/tokenization.html",
            },
        ],

        "la-pos": [
            {
                "title": "ADJ",
                "url":   "/la/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/la/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/la/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/la/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/la/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/la/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/la/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/la/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/la/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/la/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/la/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/la/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/la/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/la/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/la/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/la/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/la/pos/X.html",
            },
        ],

        "la-feat": [
            {
                "title": "Animacy",
                "url":   "/la/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/la/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/la/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/la/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/la/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/la/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/la/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/la/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/la/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/la/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/la/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/la/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/la/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/la/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/la/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/la/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/la/feat/Voice.html",
            },
        ],

        "la-dep": [
            {
                "title": "acl",
                "url":   "/la/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/la/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/la/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/la/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/la/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/la/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/la/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/la/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/la/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/la/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/la/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/la/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/la/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/la/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/la/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/la/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/la/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/la/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/la/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/la/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/la/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/la/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/la/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/la/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/la/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/la/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/la/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/la/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/la/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/la/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/la/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/la/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/la/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/la/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/la/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/la/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/la/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/la/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/la/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/la/dep/xcomp.html",
            },
        ],

        "la-overview": [
            {
                "title": "Dependencies",
                "url":   "/la/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/la/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/la/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/la/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/la/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/la/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/la/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/la/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/la/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/la/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/la/overview/tokenization.html",
            },
        ],

        "ca-pos": [
            {
                "title": "ADJ",
                "url":   "/ca/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/ca/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/ca/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/ca/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/ca/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/ca/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/ca/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/ca/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/ca/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/ca/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/ca/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/ca/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/ca/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/ca/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/ca/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/ca/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/ca/pos/X.html",
            },
        ],

        "ca-feat": [
            {
                "title": "Animacy",
                "url":   "/ca/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/ca/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/ca/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/ca/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/ca/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/ca/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/ca/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/ca/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/ca/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/ca/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/ca/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/ca/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/ca/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/ca/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/ca/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/ca/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/ca/feat/Voice.html",
            },
        ],

        "ca-dep": [
            {
                "title": "acl",
                "url":   "/ca/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/ca/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/ca/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/ca/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/ca/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/ca/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/ca/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/ca/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/ca/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/ca/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/ca/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/ca/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/ca/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/ca/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/ca/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/ca/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/ca/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/ca/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/ca/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/ca/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/ca/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/ca/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/ca/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/ca/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/ca/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/ca/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/ca/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/ca/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/ca/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/ca/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/ca/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/ca/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/ca/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/ca/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/ca/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/ca/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/ca/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/ca/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/ca/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/ca/dep/xcomp.html",
            },
        ],

        "ca-overview": [
            {
                "title": "Dependencies",
                "url":   "/ca/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/ca/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/ca/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/ca/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/ca/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/ca/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/ca/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/ca/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/ca/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/ca/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/ca/overview/tokenization.html",
            },
        ],

        "ar-pos": [
            {
                "title": "ADJ",
                "url":   "/ar/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/ar/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/ar/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/ar/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/ar/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/ar/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/ar/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/ar/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/ar/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/ar/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/ar/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/ar/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/ar/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/ar/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/ar/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/ar/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/ar/pos/X.html",
            },
        ],

        "ar-feat": [
            {
                "title": "Animacy",
                "url":   "/ar/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/ar/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/ar/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/ar/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/ar/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/ar/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/ar/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/ar/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/ar/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/ar/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/ar/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/ar/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/ar/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/ar/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/ar/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/ar/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/ar/feat/Voice.html",
            },
        ],

        "ar-dep": [
            {
                "title": "acl",
                "url":   "/ar/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/ar/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/ar/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/ar/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/ar/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/ar/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/ar/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/ar/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/ar/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/ar/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/ar/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/ar/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/ar/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/ar/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/ar/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/ar/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/ar/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/ar/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/ar/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/ar/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/ar/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/ar/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/ar/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/ar/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/ar/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/ar/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/ar/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/ar/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/ar/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/ar/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/ar/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/ar/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/ar/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/ar/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/ar/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/ar/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/ar/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/ar/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/ar/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/ar/dep/xcomp.html",
            },
        ],

        "ar-overview": [
            {
                "title": "Dependencies",
                "url":   "/ar/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/ar/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/ar/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/ar/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/ar/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/ar/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/ar/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/ar/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/ar/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/ar/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/ar/overview/tokenization.html",
            },
        ],

        "et-pos": [
            {
                "title": "ADJ",
                "url":   "/et/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/et/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/et/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/et/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/et/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/et/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/et/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/et/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/et/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/et/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/et/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/et/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/et/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/et/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/et/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/et/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/et/pos/X.html",
            },
        ],

        "et-feat": [
            {
                "title": "Animacy",
                "url":   "/et/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/et/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/et/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/et/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/et/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/et/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/et/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/et/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/et/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/et/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/et/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/et/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/et/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/et/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/et/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/et/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/et/feat/Voice.html",
            },
        ],

        "et-dep": [
            {
                "title": "acl",
                "url":   "/et/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/et/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/et/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/et/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/et/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/et/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/et/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/et/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/et/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/et/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/et/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/et/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/et/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/et/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/et/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/et/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/et/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/et/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/et/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/et/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/et/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/et/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/et/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/et/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/et/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/et/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/et/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/et/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/et/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/et/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/et/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/et/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/et/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/et/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/et/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/et/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/et/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/et/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/et/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/et/dep/xcomp.html",
            },
        ],

        "et-overview": [
            {
                "title": "Dependencies",
                "url":   "/et/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/et/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/et/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/et/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/et/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/et/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/et/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/et/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/et/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/et/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/et/overview/tokenization.html",
            },
        ],

        "pt-pos": [
            {
                "title": "ADJ",
                "url":   "/pt/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/pt/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/pt/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/pt/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/pt/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/pt/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/pt/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/pt/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/pt/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/pt/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/pt/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/pt/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/pt/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/pt/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/pt/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/pt/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/pt/pos/X.html",
            },
        ],

        "pt-feat": [
            {
                "title": "Animacy",
                "url":   "/pt/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/pt/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/pt/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/pt/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/pt/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/pt/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/pt/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/pt/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/pt/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/pt/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/pt/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/pt/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/pt/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/pt/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/pt/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/pt/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/pt/feat/Voice.html",
            },
        ],

        "pt-dep": [
            {
                "title": "acl",
                "url":   "/pt/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/pt/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/pt/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/pt/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/pt/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/pt/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/pt/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/pt/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/pt/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/pt/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/pt/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/pt/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/pt/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/pt/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/pt/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/pt/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/pt/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/pt/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/pt/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/pt/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/pt/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/pt/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/pt/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/pt/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/pt/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/pt/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/pt/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/pt/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/pt/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/pt/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/pt/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/pt/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/pt/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/pt/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/pt/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/pt/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/pt/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/pt/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/pt/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/pt/dep/xcomp.html",
            },
        ],

        "pt-overview": [
            {
                "title": "Dependencies",
                "url":   "/pt/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/pt/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/pt/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/pt/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/pt/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/pt/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/pt/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/pt/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/pt/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/pt/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/pt/overview/tokenization.html",
            },
        ],

        "nl-pos": [
            {
                "title": "ADJ",
                "url":   "/nl/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/nl/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/nl/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/nl/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/nl/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/nl/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/nl/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/nl/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/nl/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/nl/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/nl/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/nl/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/nl/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/nl/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/nl/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/nl/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/nl/pos/X.html",
            },
        ],

        "nl-feat": [
            {
                "title": "Animacy",
                "url":   "/nl/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/nl/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/nl/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/nl/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/nl/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/nl/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/nl/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/nl/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/nl/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/nl/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/nl/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/nl/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/nl/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/nl/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/nl/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/nl/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/nl/feat/Voice.html",
            },
        ],

        "nl-dep": [
            {
                "title": "acl",
                "url":   "/nl/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/nl/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/nl/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/nl/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/nl/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/nl/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/nl/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/nl/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/nl/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/nl/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/nl/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/nl/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/nl/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/nl/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/nl/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/nl/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/nl/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/nl/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/nl/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/nl/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/nl/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/nl/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/nl/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/nl/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/nl/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/nl/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/nl/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/nl/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/nl/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/nl/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/nl/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/nl/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/nl/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/nl/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/nl/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/nl/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/nl/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/nl/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/nl/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/nl/dep/xcomp.html",
            },
        ],

        "nl-overview": [
            {
                "title": "Dependencies",
                "url":   "/nl/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/nl/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/nl/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/nl/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/nl/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/nl/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/nl/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/nl/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/nl/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/nl/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/nl/overview/tokenization.html",
            },
        ],

        "ta-pos": [
            {
                "title": "ADJ",
                "url":   "/ta/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/ta/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/ta/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/ta/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/ta/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/ta/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/ta/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/ta/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/ta/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/ta/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/ta/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/ta/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/ta/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/ta/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/ta/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/ta/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/ta/pos/X.html",
            },
        ],

        "ta-feat": [
            {
                "title": "Animacy",
                "url":   "/ta/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/ta/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/ta/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/ta/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/ta/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/ta/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/ta/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/ta/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/ta/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/ta/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/ta/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/ta/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/ta/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/ta/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/ta/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/ta/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/ta/feat/Voice.html",
            },
        ],

        "ta-dep": [
            {
                "title": "acl",
                "url":   "/ta/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/ta/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/ta/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/ta/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/ta/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/ta/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/ta/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/ta/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/ta/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/ta/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/ta/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/ta/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/ta/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/ta/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/ta/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/ta/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/ta/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/ta/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/ta/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/ta/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/ta/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/ta/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/ta/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/ta/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/ta/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/ta/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/ta/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/ta/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/ta/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/ta/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/ta/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/ta/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/ta/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/ta/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/ta/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/ta/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/ta/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/ta/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/ta/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/ta/dep/xcomp.html",
            },
        ],

        "ta-overview": [
            {
                "title": "Dependencies",
                "url":   "/ta/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/ta/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/ta/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/ta/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/ta/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/ta/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/ta/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/ta/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/ta/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/ta/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/ta/overview/tokenization.html",
            },
        ],

        "pl-pos": [
            {
                "title": "ADJ",
                "url":   "/pl/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/pl/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/pl/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/pl/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/pl/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/pl/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/pl/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/pl/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/pl/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/pl/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/pl/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/pl/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/pl/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/pl/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/pl/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/pl/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/pl/pos/X.html",
            },
        ],

        "pl-feat": [
            {
                "title": "Animacy",
                "url":   "/pl/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/pl/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/pl/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/pl/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/pl/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/pl/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/pl/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/pl/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/pl/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/pl/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/pl/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/pl/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/pl/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/pl/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/pl/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/pl/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/pl/feat/Voice.html",
            },
        ],

        "pl-dep": [
            {
                "title": "acl",
                "url":   "/pl/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/pl/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/pl/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/pl/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/pl/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/pl/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/pl/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/pl/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/pl/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/pl/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/pl/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/pl/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/pl/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/pl/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/pl/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/pl/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/pl/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/pl/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/pl/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/pl/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/pl/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/pl/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/pl/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/pl/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/pl/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/pl/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/pl/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/pl/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/pl/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/pl/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/pl/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/pl/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/pl/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/pl/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/pl/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/pl/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/pl/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/pl/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/pl/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/pl/dep/xcomp.html",
            },
        ],

        "pl-overview": [
            {
                "title": "Dependencies",
                "url":   "/pl/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/pl/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/pl/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/pl/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/pl/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/pl/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/pl/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/pl/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/pl/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/pl/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/pl/overview/tokenization.html",
            },
        ],

        "no-pos": [
            {
                "title": "ADJ",
                "url":   "/no/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/no/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/no/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/no/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/no/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/no/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/no/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/no/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/no/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/no/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/no/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/no/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/no/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/no/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/no/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/no/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/no/pos/X.html",
            },
        ],

        "no-feat": [
            {
                "title": "Animacy",
                "url":   "/no/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/no/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/no/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/no/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/no/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/no/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/no/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/no/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/no/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/no/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/no/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/no/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/no/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/no/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/no/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/no/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/no/feat/Voice.html",
            },
        ],

        "no-dep": [
            {
                "title": "acl:relcl",
                "url":   "/no/dep/acl-relcl.html",
            },
            {
                "title": "acl",
                "url":   "/no/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/no/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/no/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/no/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/no/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/no/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/no/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/no/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/no/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/no/dep/ccomp.html",
            },
            {
                "title": "compound:prt",
                "url":   "/no/dep/compound-prt.html",
            },
            {
                "title": "compound",
                "url":   "/no/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/no/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/no/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/no/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/no/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/no/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/no/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/no/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/no/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/no/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/no/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/no/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/no/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/no/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/no/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/no/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/no/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/no/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/no/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/no/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/no/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/no/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/no/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/no/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/no/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/no/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/no/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/no/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/no/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/no/dep/xcomp.html",
            },
        ],

        "no-overview": [
            {
                "title": "Dependencies",
                "url":   "/no/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/no/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/no/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/no/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/no/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/no/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/no/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/no/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/no/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/no/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/no/overview/tokenization.html",
            },
        ],

        "cu-pos": [
            {
                "title": "ADJ",
                "url":   "/cu/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/cu/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/cu/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/cu/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/cu/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/cu/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/cu/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/cu/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/cu/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/cu/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/cu/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/cu/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/cu/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/cu/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/cu/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/cu/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/cu/pos/X.html",
            },
        ],

        "cu-feat": [
            {
                "title": "Animacy",
                "url":   "/cu/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/cu/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/cu/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/cu/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/cu/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/cu/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/cu/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/cu/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/cu/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/cu/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/cu/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/cu/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/cu/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/cu/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/cu/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/cu/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/cu/feat/Voice.html",
            },
        ],

        "cu-dep": [
            {
                "title": "acl",
                "url":   "/cu/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/cu/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/cu/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/cu/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/cu/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/cu/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/cu/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/cu/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/cu/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/cu/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/cu/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/cu/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/cu/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/cu/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/cu/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/cu/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/cu/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/cu/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/cu/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/cu/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/cu/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/cu/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/cu/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/cu/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/cu/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/cu/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/cu/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/cu/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/cu/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/cu/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/cu/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/cu/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/cu/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/cu/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/cu/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/cu/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/cu/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/cu/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/cu/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/cu/dep/xcomp.html",
            },
        ],

        "cu-overview": [
            {
                "title": "Dependencies",
                "url":   "/cu/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/cu/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/cu/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/cu/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/cu/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/cu/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/cu/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/cu/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/cu/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/cu/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/cu/overview/tokenization.html",
            },
        ],

        "got-pos": [
            {
                "title": "ADJ",
                "url":   "/got/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/got/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/got/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/got/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/got/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/got/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/got/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/got/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/got/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/got/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/got/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/got/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/got/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/got/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/got/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/got/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/got/pos/X.html",
            },
        ],

        "got-feat": [
            {
                "title": "Animacy",
                "url":   "/got/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/got/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/got/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/got/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/got/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/got/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/got/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/got/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/got/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/got/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/got/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/got/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/got/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/got/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/got/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/got/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/got/feat/Voice.html",
            },
        ],

        "got-dep": [
            {
                "title": "acl",
                "url":   "/got/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/got/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/got/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/got/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/got/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/got/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/got/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/got/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/got/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/got/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/got/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/got/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/got/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/got/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/got/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/got/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/got/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/got/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/got/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/got/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/got/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/got/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/got/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/got/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/got/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/got/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/got/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/got/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/got/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/got/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/got/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/got/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/got/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/got/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/got/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/got/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/got/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/got/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/got/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/got/dep/xcomp.html",
            },
        ],

        "got-overview": [
            {
                "title": "Dependencies",
                "url":   "/got/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/got/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/got/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/got/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/got/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/got/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/got/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/got/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/got/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/got/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/got/overview/tokenization.html",
            },
        ],

        "kk-pos": [
            {
                "title": "ADJ",
                "url":   "/kk/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/kk/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/kk/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/kk/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/kk/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/kk/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/kk/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/kk/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/kk/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/kk/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/kk/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/kk/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/kk/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/kk/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/kk/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/kk/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/kk/pos/X.html",
            },
        ],

        "kk-feat": [
            {
                "title": "Animacy",
                "url":   "/kk/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/kk/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/kk/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/kk/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/kk/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/kk/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/kk/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/kk/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/kk/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/kk/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/kk/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/kk/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/kk/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/kk/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/kk/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/kk/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/kk/feat/Voice.html",
            },
        ],

        "kk-dep": [
            {
                "title": "acl",
                "url":   "/kk/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/kk/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/kk/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/kk/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/kk/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/kk/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/kk/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/kk/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/kk/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/kk/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/kk/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/kk/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/kk/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/kk/dep/csubj.html",
            },
            {
                "title": "dep",
                "url":   "/kk/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/kk/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/kk/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/kk/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/kk/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/kk/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/kk/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/kk/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/kk/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/kk/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/kk/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/kk/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/kk/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/kk/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/kk/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/kk/dep/nsubj.html",
            },
            {
                "title": "nummod",
                "url":   "/kk/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/kk/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/kk/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/kk/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/kk/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/kk/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/kk/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/kk/dep/xcomp.html",
            },
        ],

        "kk-overview": [
            {
                "title": "Dependencies",
                "url":   "/kk/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/kk/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/kk/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/kk/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/kk/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/kk/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/kk/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/kk/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/kk/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/kk/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/tr/overview/tokenization.html",
            },
        ],

        "uk-pos": [
            {
                "title": "ADJ",
                "url":   "/uk/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/uk/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/uk/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/uk/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/uk/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/uk/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/uk/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/uk/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/uk/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/uk/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/uk/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/uk/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/uk/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/uk/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/uk/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/uk/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/uk/pos/X.html",
            },
        ],

        "uk-feat": [
            {
                "title": "Animacy",
                "url":   "/uk/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/uk/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/uk/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/uk/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/uk/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/uk/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/uk/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/uk/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/uk/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/uk/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/uk/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/uk/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/uk/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/uk/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/uk/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/uk/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/uk/feat/Voice.html",
            },
        ],

        "uk-dep": [
            {
                "title": "acl",
                "url":   "/uk/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/uk/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/uk/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/uk/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/uk/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/uk/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/uk/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/uk/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/uk/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/uk/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/uk/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/uk/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/uk/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/uk/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/uk/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/uk/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/uk/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/uk/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/uk/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/uk/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/uk/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/uk/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/uk/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/uk/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/uk/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/uk/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/uk/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/uk/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/uk/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/uk/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/uk/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/uk/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/uk/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/uk/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/uk/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/uk/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/uk/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/uk/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/uk/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/uk/dep/xcomp.html",
            },
        ],

        "uk-overview": [
            {
                "title": "Dependencies",
                "url":   "/uk/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/uk/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/uk/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/uk/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/uk/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/uk/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/uk/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/uk/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/uk/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/uk/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/uk/overview/tokenization.html",
            },
        ],

        "gl-pos": [
            {
                "title": "ADJ",
                "url":   "/gl/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/gl/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/gl/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/gl/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/gl/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/gl/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/gl/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/gl/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/gl/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/gl/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/gl/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/gl/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/gl/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/gl/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/gl/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/gl/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/gl/pos/X.html",
            },
        ],

        "gl-feat": [
            {
                "title": "Animacy",
                "url":   "/gl/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/gl/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/gl/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/gl/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/gl/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/gl/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/gl/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/gl/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/gl/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/gl/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/gl/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/gl/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/gl/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/gl/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/gl/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/gl/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/gl/feat/Voice.html",
            },
        ],

        "gl-dep": [
            {
                "title": "acl",
                "url":   "/gl/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/gl/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/gl/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/gl/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/gl/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/gl/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/gl/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/gl/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/gl/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/gl/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/gl/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/gl/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/gl/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/gl/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/gl/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/gl/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/gl/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/gl/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/gl/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/gl/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/gl/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/gl/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/gl/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/gl/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/gl/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/gl/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/gl/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/gl/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/gl/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/gl/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/gl/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/gl/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/gl/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/gl/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/gl/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/gl/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/gl/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/gl/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/gl/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/gl/dep/xcomp.html",
            },
        ],

        "gl-overview": [
            {
                "title": "Dependencies",
                "url":   "/gl/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/gl/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/gl/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/gl/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/gl/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/gl/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/gl/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/gl/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/gl/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/gl/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/gl/overview/tokenization.html",
            },
        ],

        "ru-pos": [
            {
                "title": "ADJ",
                "url":   "/ru/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/ru/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/ru/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/ru/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/ru/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/ru/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/ru/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/ru/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/ru/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/ru/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/ru/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/ru/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/ru/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/ru/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/ru/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/ru/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/ru/pos/X.html",
            },
        ],

        "ru-feat": [
            {
                "title": "Animacy",
                "url":   "/ru/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/ru/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/ru/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/ru/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/ru/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/ru/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/ru/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/ru/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/ru/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/ru/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/ru/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/ru/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/ru/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/ru/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/ru/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/ru/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/ru/feat/Voice.html",
            },
        ],

        "ru-dep": [
            {
                "title": "acl",
                "url":   "/ru/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/ru/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/ru/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/ru/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/ru/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/ru/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/ru/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/ru/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/ru/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/ru/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/ru/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/ru/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/ru/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/ru/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/ru/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/ru/dep/dep.html",
            },
            {
                "title": "det:numgov",
                "url":   "/ru/dep/det-numgov.html",
            },
            {
                "title": "det:nummod",
                "url":   "/ru/dep/det-nummod.html",
            },
            {
                "title": "det",
                "url":   "/ru/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/ru/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/ru/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/ru/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/ru/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/ru/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/ru/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/ru/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/ru/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/ru/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/ru/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/ru/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/ru/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/ru/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/ru/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/ru/dep/nsubjpass.html",
            },
            {
                "title": "nummod:gov",
                "url":   "/ru/dep/nummod-gov.html",
            },
            {
                "title": "nummod",
                "url":   "/ru/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/ru/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/ru/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/ru/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/ru/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/ru/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/ru/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/ru/dep/xcomp.html",
            },
        ],

        "ru-overview": [
            {
                "title": "Dependencies",
                "url":   "/ru/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/ru/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/ru/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/ru/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/ru/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/ru/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/ru/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/ru/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/ru/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/ru/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/ru/overview/tokenization.html",
            },
        ],

        "lv-pos": [
            {
                "title": "ADJ",
                "url":   "/lv/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/lv/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/lv/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/lv/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/lv/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/lv/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/lv/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/lv/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/lv/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/lv/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/lv/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/lv/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/lv/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/lv/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/lv/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/lv/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/lv/pos/X.html",
            },
        ],

        "lv-feat": [
            {
                "title": "Animacy",
                "url":   "/lv/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/lv/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/lv/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/lv/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/lv/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/lv/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/lv/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/lv/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/lv/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/lv/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/lv/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/lv/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/lv/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/lv/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/lv/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/lv/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/lv/feat/Voice.html",
            },
        ],

        "lv-dep": [
            {
                "title": "acl",
                "url":   "/lv/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/lv/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/lv/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/lv/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/lv/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/lv/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/lv/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/lv/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/lv/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/lv/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/lv/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/lv/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/lv/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/lv/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/lv/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/lv/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/lv/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/lv/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/lv/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/lv/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/lv/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/lv/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/lv/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/lv/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/lv/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/lv/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/lv/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/lv/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/lv/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/lv/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/lv/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/lv/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/lv/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/lv/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/lv/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/lv/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/lv/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/lv/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/lv/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/lv/dep/xcomp.html",
            },
        ],

        "lv-overview": [
            {
                "title": "Dependencies",
                "url":   "/lv/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/lv/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/lv/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/lv/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/lv/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/lv/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/lv/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/lv/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/lv/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/lv/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/lv/overview/tokenization.html",
            },
        ],

        "tr-pos": [
            {
                "title": "ADJ",
                "url":   "/tr/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/tr/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/tr/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/tr/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/tr/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/tr/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/tr/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/tr/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/tr/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/tr/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/tr/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/tr/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/tr/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/tr/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/tr/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/tr/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/tr/pos/X.html",
            },
        ],

        "tr-feat": [
            {
                "title": "Animacy",
                "url":   "/tr/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/tr/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/tr/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/tr/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/tr/feat/Degree.html",
            },
            {
                "title": "Evidential",
                "url":   "/tr/feat/Evidential.html",
            },
            {
                "title": "Gender",
                "url":   "/tr/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/tr/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/tr/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/tr/feat/NumType.html",
            },
            {
                "title": "Number[psor]",
                "url":   "/tr/feat/Number-psor.html",
            },
            {
                "title": "Number",
                "url":   "/tr/feat/Number.html",
            },
            {
                "title": "Person[psor]",
                "url":   "/tr/feat/Person-psor.html",
            },
            {
                "title": "Person",
                "url":   "/tr/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/tr/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/tr/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/tr/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/tr/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/tr/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/tr/feat/Voice.html",
            },
        ],

        "tr-dep": [
            {
                "title": "acl",
                "url":   "/tr/dep/acl.html",
            },
            {
                "title": "advcl:cond",
                "url":   "/tr/dep/advcl-cond.html",
            },
            {
                "title": "advcl",
                "url":   "/tr/dep/advcl.html",
            },
            {
                "title": "advmod:emph",
                "url":   "/tr/dep/advmod-emph.html",
            },
            {
                "title": "advmod",
                "url":   "/tr/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/tr/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/tr/dep/appos.html",
            },
            {
                "title": "aux:q",
                "url":   "/tr/dep/aux-q.html",
            },
            {
                "title": "aux",
                "url":   "/tr/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/tr/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/tr/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/tr/dep/cc.html",
            },
            {
                "title": "ccomp:cau",
                "url":   "/tr/dep/ccomp-cau.html",
            },
            {
                "title": "ccomp",
                "url":   "/tr/dep/ccomp.html",
            },
            {
                "title": "compound:redup",
                "url":   "/tr/dep/compound-redup.html",
            },
            {
                "title": "compound",
                "url":   "/tr/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/tr/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/tr/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/tr/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/tr/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/tr/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/tr/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/tr/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/tr/dep/dislocated.html",
            },
            {
                "title": "dobj:cau",
                "url":   "/tr/dep/dobj-cau.html",
            },
            {
                "title": "dobj",
                "url":   "/tr/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/tr/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/tr/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/tr/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/tr/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/tr/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/tr/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/tr/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/tr/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/tr/dep/neg.html",
            },
            {
                "title": "nmod:cau",
                "url":   "/tr/dep/nmod-cau.html",
            },
            {
                "title": "nmod:comp",
                "url":   "/tr/dep/nmod-comp.html",
            },
            {
                "title": "nmod:own",
                "url":   "/tr/dep/nmod-own.html",
            },
            {
                "title": "nmod:part",
                "url":   "/tr/dep/nmod-part.html",
            },
            {
                "title": "nmod:pass",
                "url":   "/tr/dep/nmod-pass.html",
            },
            {
                "title": "nmod:poss",
                "url":   "/tr/dep/nmod-poss.html",
            },
            {
                "title": "nmod:tmod",
                "url":   "/tr/dep/nmod-tmod.html",
            },
            {
                "title": "nmod",
                "url":   "/tr/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/tr/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/tr/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/tr/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/tr/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/tr/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/tr/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/tr/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/tr/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/tr/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/tr/dep/xcomp.html",
            },
        ],

        "tr-overview": [
            {
                "title": "Dependencies",
                "url":   "/tr/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/tr/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/tr/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/tr/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/tr/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/tr/overview/morphology.html",
            },
            {
                "title": "Notes",
                "url":   "/tr/overview/notes.html",
            },
            {
                "title": "POS tags",
                "url":   "/tr/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/tr/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/tr/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/tr/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/tr/overview/tokenization.html",
            },
        ],

        "template-pos": [
            {
                "title": "ADJ",
                "url":   "/template/pos/ADJ.html",
            },
            {
                "title": "ADP",
                "url":   "/template/pos/ADP.html",
            },
            {
                "title": "ADV",
                "url":   "/template/pos/ADV.html",
            },
            {
                "title": "AUX",
                "url":   "/template/pos/AUX_.html",
            },
            {
                "title": "CONJ",
                "url":   "/template/pos/CONJ.html",
            },
            {
                "title": "DET",
                "url":   "/template/pos/DET.html",
            },
            {
                "title": "INTJ",
                "url":   "/template/pos/INTJ.html",
            },
            {
                "title": "NOUN",
                "url":   "/template/pos/NOUN.html",
            },
            {
                "title": "NUM",
                "url":   "/template/pos/NUM.html",
            },
            {
                "title": "PART",
                "url":   "/template/pos/PART.html",
            },
            {
                "title": "PRON",
                "url":   "/template/pos/PRON.html",
            },
            {
                "title": "PROPN",
                "url":   "/template/pos/PROPN.html",
            },
            {
                "title": "PUNCT",
                "url":   "/template/pos/PUNCT.html",
            },
            {
                "title": "SCONJ",
                "url":   "/template/pos/SCONJ.html",
            },
            {
                "title": "SYM",
                "url":   "/template/pos/SYM.html",
            },
            {
                "title": "VERB",
                "url":   "/template/pos/VERB.html",
            },
            {
                "title": "X",
                "url":   "/template/pos/X.html",
            },
        ],

        "template-feat": [
            {
                "title": "Animacy",
                "url":   "/template/feat/Animacy.html",
            },
            {
                "title": "Aspect",
                "url":   "/template/feat/Aspect.html",
            },
            {
                "title": "Case",
                "url":   "/template/feat/Case.html",
            },
            {
                "title": "Definite",
                "url":   "/template/feat/Definite.html",
            },
            {
                "title": "Degree",
                "url":   "/template/feat/Degree.html",
            },
            {
                "title": "Gender",
                "url":   "/template/feat/Gender.html",
            },
            {
                "title": "Mood",
                "url":   "/template/feat/Mood.html",
            },
            {
                "title": "Negative",
                "url":   "/template/feat/Negative.html",
            },
            {
                "title": "NumType",
                "url":   "/template/feat/NumType.html",
            },
            {
                "title": "Number",
                "url":   "/template/feat/Number.html",
            },
            {
                "title": "Person",
                "url":   "/template/feat/Person.html",
            },
            {
                "title": "Poss",
                "url":   "/template/feat/Poss.html",
            },
            {
                "title": "PronType",
                "url":   "/template/feat/PronType.html",
            },
            {
                "title": "Reflex",
                "url":   "/template/feat/Reflex.html",
            },
            {
                "title": "Tense",
                "url":   "/template/feat/Tense.html",
            },
            {
                "title": "VerbForm",
                "url":   "/template/feat/VerbForm.html",
            },
            {
                "title": "Voice",
                "url":   "/template/feat/Voice.html",
            },
        ],

        "template-dep": [
            {
                "title": "acl",
                "url":   "/template/dep/acl.html",
            },
            {
                "title": "advcl",
                "url":   "/template/dep/advcl.html",
            },
            {
                "title": "advmod",
                "url":   "/template/dep/advmod.html",
            },
            {
                "title": "amod",
                "url":   "/template/dep/amod.html",
            },
            {
                "title": "appos",
                "url":   "/template/dep/appos.html",
            },
            {
                "title": "aux",
                "url":   "/template/dep/aux_.html",
            },
            {
                "title": "auxpass",
                "url":   "/template/dep/auxpass.html",
            },
            {
                "title": "case",
                "url":   "/template/dep/case.html",
            },
            {
                "title": "cc",
                "url":   "/template/dep/cc.html",
            },
            {
                "title": "ccomp",
                "url":   "/template/dep/ccomp.html",
            },
            {
                "title": "compound",
                "url":   "/template/dep/compound.html",
            },
            {
                "title": "conj",
                "url":   "/template/dep/conj.html",
            },
            {
                "title": "cop",
                "url":   "/template/dep/cop.html",
            },
            {
                "title": "csubj",
                "url":   "/template/dep/csubj.html",
            },
            {
                "title": "csubjpass",
                "url":   "/template/dep/csubjpass.html",
            },
            {
                "title": "dep",
                "url":   "/template/dep/dep.html",
            },
            {
                "title": "det",
                "url":   "/template/dep/det.html",
            },
            {
                "title": "discourse",
                "url":   "/template/dep/discourse.html",
            },
            {
                "title": "dislocated",
                "url":   "/template/dep/dislocated.html",
            },
            {
                "title": "dobj",
                "url":   "/template/dep/dobj.html",
            },
            {
                "title": "expl",
                "url":   "/template/dep/expl.html",
            },
            {
                "title": "foreign",
                "url":   "/template/dep/foreign.html",
            },
            {
                "title": "goeswith",
                "url":   "/template/dep/goeswith.html",
            },
            {
                "title": "iobj",
                "url":   "/template/dep/iobj.html",
            },
            {
                "title": "list",
                "url":   "/template/dep/list.html",
            },
            {
                "title": "mark",
                "url":   "/template/dep/mark.html",
            },
            {
                "title": "mwe",
                "url":   "/template/dep/mwe.html",
            },
            {
                "title": "name",
                "url":   "/template/dep/name.html",
            },
            {
                "title": "neg",
                "url":   "/template/dep/neg.html",
            },
            {
                "title": "nmod",
                "url":   "/template/dep/nmod.html",
            },
            {
                "title": "nsubj",
                "url":   "/template/dep/nsubj.html",
            },
            {
                "title": "nsubjpass",
                "url":   "/template/dep/nsubjpass.html",
            },
            {
                "title": "nummod",
                "url":   "/template/dep/nummod.html",
            },
            {
                "title": "parataxis",
                "url":   "/template/dep/parataxis.html",
            },
            {
                "title": "punct",
                "url":   "/template/dep/punct.html",
            },
            {
                "title": "remnant",
                "url":   "/template/dep/remnant.html",
            },
            {
                "title": "reparandum",
                "url":   "/template/dep/reparandum.html",
            },
            {
                "title": "root",
                "url":   "/template/dep/root.html",
            },
            {
                "title": "vocative",
                "url":   "/template/dep/vocative.html",
            },
            {
                "title": "xcomp",
                "url":   "/template/dep/xcomp.html",
            },
        ],

        "template-overview": [
            {
                "title": "Dependencies",
                "url":   "/template/dep/all.html",
            },
            {
                "title": "Dependencies",
                "url":   "/template/dep/index.html",
            },
            {
                "title": "Features",
                "url":   "/template/feat/all.html",
            },
            {
                "title": "Features",
                "url":   "/template/feat/index.html",
            },
            {
                "title": "Introduction",
                "url":   "/template/overview/introduction.html",
            },
            {
                "title": "Morphology",
                "url":   "/template/overview/morphology.html",
            },
            {
                "title": "POS tags",
                "url":   "/template/pos/all.html",
            },
            {
                "title": "POS tags",
                "url":   "/template/pos/index.html",
            },
            {
                "title": "Syntax",
                "url":   "/template/overview/specific-syntax.html",
            },
            {
                "title": "Syntax",
                "url":   "/template/overview/syntax.html",
            },
            {
                "title": "Tokenization",
                "url":   "/template/overview/tokenization.html",
            },
        ],

        "tagset-conversion": [
            {
                "title": "Tagset ar::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/ar-conll-uposf.html",
            },
            {
                "title": "Tagset ar::conll2007 conversion to universal POS tags and features",
                "url":   "/tagset-conversion/ar-conll2007-uposf.html",
            },
            {
                "title": "Tagset ar::padt conversion to universal POS tags and features",
                "url":   "/tagset-conversion/ar-padt-uposf.html",
            },
            {
                "title": "Tagset bg::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/bg-conll-uposf.html",
            },
            {
                "title": "Tagset bn::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/bn-conll-uposf.html",
            },
            {
                "title": "Tagset ca::conll2009 conversion to universal POS tags and features",
                "url":   "/tagset-conversion/ca-conll2009-uposf.html",
            },
            {
                "title": "Tagset cs::ajka conversion to universal POS tags and features",
                "url":   "/tagset-conversion/cs-ajka-uposf.html",
            },
            {
                "title": "Tagset cs::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/cs-conll-uposf.html",
            },
            {
                "title": "Tagset cs::multext conversion to universal POS tags and features",
                "url":   "/tagset-conversion/cs-multext-uposf.html",
            },
            {
                "title": "Tagset cs::pdt conversion to universal POS tags and features",
                "url":   "/tagset-conversion/cs-pdt-uposf.html",
            },
            {
                "title": "Tagset da::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/da-conll-uposf.html",
            },
            {
                "title": "Tagset de::conll2009 conversion to universal POS tags and features",
                "url":   "/tagset-conversion/de-conll2009-uposf.html",
            },
            {
                "title": "Tagset de::smor conversion to universal POS tags and features",
                "url":   "/tagset-conversion/de-smor-uposf.html",
            },
            {
                "title": "Tagset de::stts conversion to universal POS tags and features",
                "url":   "/tagset-conversion/de-stts-uposf.html",
            },
            {
                "title": "Tagset el::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/el-conll-uposf.html",
            },
            {
                "title": "Tagset en::penn conversion to universal POS tags and features",
                "url":   "/tagset-conversion/en-penn-uposf.html",
            },
            {
                "title": "Tagset es::conll2009 conversion to universal POS tags and features",
                "url":   "/tagset-conversion/es-conll2009-uposf.html",
            },
            {
                "title": "Tagset et::puudepank conversion to universal POS tags and features",
                "url":   "/tagset-conversion/et-puudepank-uposf.html",
            },
            {
                "title": "Tagset eu::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/eu-conll-uposf.html",
            },
            {
                "title": "Tagset fa::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/fa-conll-uposf.html",
            },
            {
                "title": "Tagset fi::turku conversion to universal POS tags and features",
                "url":   "/tagset-conversion/fi-turku-uposf.html",
            },
            {
                "title": "Tagset grc::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/grc-conll-uposf.html",
            },
            {
                "title": "Tagset he::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/he-conll-uposf.html",
            },
            {
                "title": "Tagset hi::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/hi-conll-uposf.html",
            },
            {
                "title": "Tagset hr::multext conversion to universal POS tags and features",
                "url":   "/tagset-conversion/hr-multext-uposf.html",
            },
            {
                "title": "Tagset hu::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/hu-conll-uposf.html",
            },
            {
                "title": "Tagset conversion tables",
                "url":   "/tagset-conversion/index.html",
            },
            {
                "title": "Tagset it::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/it-conll-uposf.html",
            },
            {
                "title": "Tagset it::isdt conversion to universal POS tags and features",
                "url":   "/tagset-conversion/it-isdt-uposf.html",
            },
            {
                "title": "Tagset ja::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/ja-conll-uposf.html",
            },
            {
                "title": "Tagset ja::ipadic conversion to universal POS tags and features",
                "url":   "/tagset-conversion/ja-ipadic-uposf.html",
            },
            {
                "title": "Tagset la::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/la-conll-uposf.html",
            },
            {
                "title": "Tagset la::itconll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/la-itconll-uposf.html",
            },
            {
                "title": "Tagset mt::mlss conversion to universal POS tags and features",
                "url":   "/tagset-conversion/mt-mlss-uposf.html",
            },
            {
                "title": "Tagset nl::cgn conversion to universal POS tags and features",
                "url":   "/tagset-conversion/nl-cgn-uposf.html",
            },
            {
                "title": "Tagset nl::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/nl-conll-uposf.html",
            },
            {
                "title": "Tagset pl::ipipan conversion to universal POS tags and features",
                "url":   "/tagset-conversion/pl-ipipan-uposf.html",
            },
            {
                "title": "Tagset pt::cintil conversion to universal POS tags and features",
                "url":   "/tagset-conversion/pt-cintil-uposf.html",
            },
            {
                "title": "Tagset pt::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/pt-conll-uposf.html",
            },
            {
                "title": "Tagset ro::multext conversion to universal POS tags and features",
                "url":   "/tagset-conversion/ro-multext-uposf.html",
            },
            {
                "title": "Tagset ro::rdt conversion to universal POS tags and features",
                "url":   "/tagset-conversion/ro-rdt-uposf.html",
            },
            {
                "title": "Tagset ru::syntagrus conversion to universal POS tags and features",
                "url":   "/tagset-conversion/ru-syntagrus-uposf.html",
            },
            {
                "title": "Tagset sk::snk conversion to universal POS tags and features",
                "url":   "/tagset-conversion/sk-snk-uposf.html",
            },
            {
                "title": "Tagset sl::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/sl-conll-uposf.html",
            },
            {
                "title": "Tagset sl::multext conversion to universal POS tags and features",
                "url":   "/tagset-conversion/sl-multext-uposf.html",
            },
            {
                "title": "Tagset sv::mamba conversion to universal POS tags and features",
                "url":   "/tagset-conversion/sv-mamba-uposf.html",
            },
            {
                "title": "Tagset sv::parole conversion to universal POS tags and features",
                "url":   "/tagset-conversion/sv-parole-uposf.html",
            },
            {
                "title": "Tagset sv::suc conversion to universal POS tags and features",
                "url":   "/tagset-conversion/sv-suc-uposf.html",
            },
            {
                "title": "Tagset ta::tamiltb conversion to universal POS tags and features",
                "url":   "/tagset-conversion/ta-tamiltb-uposf.html",
            },
            {
                "title": "Tagset te::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/te-conll-uposf.html",
            },
            {
                "title": "Tagset tr::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/tr-conll-uposf.html",
            },
            {
                "title": "Tagset zh::conll conversion to universal POS tags and features",
                "url":   "/tagset-conversion/zh-conll-uposf.html",
            },
        ],

        "2015-08-23-uppsala": [
            {
                "title": "Uppsala Group on Clitics and Reflexive Pronouns",
                "url":   "/2015-08-23-uppsala/clitics.html",
            },
            {
                "title": "Uppsala Group on Conversion Best Practice and Tools",
                "url":   "/2015-08-23-uppsala/conversion.html",
            },
            {
                "title": "Uppsala Group on Coordination and Names",
                "url":   "/2015-08-23-uppsala/coordination.html",
            },
            {
                "title": "Uppsala Group on Copula and Cleft Sentences",
                "url":   "/2015-08-23-uppsala/copula.html",
            },
            {
                "title": "Uppsala Group on Determiners and Pronouns",
                "url":   "/2015-08-23-uppsala/determiners.html",
            },
            {
                "title": "Uppsala Group on Ellipsis",
                "url":   "/2015-08-23-uppsala/ellipsis.html",
            },
            {
                "title": "Uppsala Group on Future of UD",
                "url":   "/2015-08-23-uppsala/future.html",
            },
            {
                "title": "Uppsala Group on Multiword Expressions",
                "url":   "/2015-08-23-uppsala/mwe.html",
            },
            {
                "title": "Uppsala Group on Particles and Adpositions",
                "url":   "/2015-08-23-uppsala/particles.html",
            },
            {
                "title": "Uppsala Group on Tokenisation",
                "url":   "/2015-08-23-uppsala/tokenization.html",
            },
        ],

    };

    return {
        listing: listing,
    };
})(window);
// -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
// vim:set ft=javascript ts=4 sw=4 sts=4 cindent:

var Config = (function(window, undefined) {

    var bratCollData = {
	'entity_types': [
// this is optional
        {
            'type': 'SPAN_DEFAULT',
            'bgColor': '#7fa2ff',
            'borderColor': 'darken'
        },
        {
            'type': 'ARC_DEFAULT',
            'color': 'black',
            'arrowHead': 'triangle,5',
            'labelArrow': 'triangle,3,5',
        },
        {
            'type': 'token',
            'labels': [ '\u00A0\u00A0' ], // non-breaking space for empty
        },
        {
            'type': '-',
            'labels': [ '\u00A0\u00A0' ], // non-breaking space for empty
        }
	],
	'entity_attribute_types': [],
	'relation_types': [
// this is optional
//         {
//             'type': 'subj',
//             'labels': [ 'subj' ],
//             'dashArray': '3,3',
//             'color': 'green',
//             'args': [
//                 {
//                     'role': 'arg1',
//                     'targets': [ 'token' ]
//                 },
//                 {
//                     'role': 'arg2',
//                     'targets': [ 'token' ]
//                 }
//             ]
//         }
        ],
	'event_types': [],
    };

    return {
        bratCollData: bratCollData,
    };
})(window);
// -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; -*-
// vim:set ft=javascript ts=2 sw=2 sts=2 cindent:
var Configuration = (function(window, undefined) {
    var abbrevsOn = true;
    var textBackgrounds = "striped";
    var svgWidth = '100%';
    var rapidModeOn = false;
    var confirmModeOn = true;
    var autorefreshOn = false;
    var typeCollapseLimit = 30;
    
    var visual = {
      margin: { x: 2, y: 1 },
      arcTextMargin: 1,
      boxSpacing: 1,
      curlyHeight: 4,
      arcSpacing: 9, //10;
      arcStartHeight: 19, //23; //25;
    }

    return {
      abbrevsOn: abbrevsOn,
      textBackgrounds: textBackgrounds,
      visual: visual,
      svgWidth: svgWidth,
      rapidModeOn: rapidModeOn,
      confirmModeOn: confirmModeOn,
      autorefreshOn: autorefreshOn,
      typeCollapseLimit: typeCollapseLimit,
    };
})(window);
// -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
// vim:set ft=javascript ts=4 sw=4 sts=4 cindent:

/*
CoNLL-U format library for JavaScript.
Home: http://github.com/spyysalo/conllu.js
Format: http://universaldependencies.github.io/docs/format.html

Author: Sampo Pyysalo
License: MIT (http://opensource.org/licenses/MIT)
*/

var ConllU = (function(window, undefined) {

    /*
     * ConllU.Document: represents CoNLL-U document
     */

    var Document = function() {
        this.reset();
    };

    Document.prototype.reset = function() {
        this.sentences = [];
        this.error = false;
        this.logger = function(s) { /* no-op */ };
        this.strict = null; // pick heuristically
    };

    Document.prototype.log = function(message) {
        this.logger(message);
    };

    Document.prototype.logError = function(message) {
        this.log('error: ' + message);
        this.error = true;
    };

    /* Parse CoNLL-U format, return Document.
     * (see http://universaldependencies.github.io/docs/format.html)
     *
     * CoNLL-U files contain three types of lines:
     * 1.  Word lines
     * 2.  Blank lines marking sentence boundaries
     * 3.  Comment lines starting with a hash ("#")        
     *
     * Each word line has the following format
     * 1.  ID: Word index, integer starting at 1 for each new sentence; 
     *     may be a range for tokens with multiple words.
     * 2.  FORM: Word form or punctuation symbol.
     * 3.  LEMMA: Lemma or stem of word form.
     * 4.  CPOSTAG: Google universal part-of-speech tag from the 
     *     Universal POS tag set.
     * 5.  POSTAG: Language-specific part-of-speech tag; underscore 
     *     if not available.
     * 6.  FEATS: List of morphological features from the Universal 
     *     feature inventory or from a defined language-specific extension;
     *      underscore if not available.
     * 7.  HEAD: Head of the current token, which is either a value of ID
     *     or zero (0).
     * 8.  DEPREL: Universal Stanford dependency relation to the HEAD
     *     (root iff HEAD = 0) or a defined language-specific subtype
     *     of one.
     * 9.  DEPS: List of secondary dependencies (head-deprel pairs).
     * 10. MISC: Any other annotation.
     */
    Document.prototype.parse = function(input, logger, strict) {
        // discard previous state, if any
        this.reset();

        if (logger !== undefined) {
            this.logger = logger;
        }
        if (strict !== undefined) {
            this.strict = strict;
        }
        
        // TODO: handle other newline formats
        var lines = input.split('\n');

        if (this.strict === null) {
            this.strict = selectParsingMode(input, this.logger);
        }

        // select splitter to use for dividing the lines into fields.
        var splitter = selectFieldSplitter(input, this.logger, this.strict);

        var elements = [],            
            comments = [],
            beforeSentence = true;
        for (var idx=0; idx<lines.length; idx++) {
            var line = lines[idx], that = this;

            var logLineError = function(message) {
                that.logError('line '+(idx+1)+': '+message+' ("'+line+'")');
                that.error = true;
            }

            if (isComment(line)) {
                if (beforeSentence) {
                    comments.push(line);
                } else {
                    logLineError('comments must precede sentence, ignoring');
                }
                continue;
            }

            // non-comment, assume inside sentence until terminated by
            // blank line
            beforeSentence = false;

            var fields = splitter(line);

            if (fields.length === 0) {
                // empty line, terminates sentence
                if (elements.length !== 0) {
                    var sId = 'S' + (this.sentences.length+1);
                    var sentence = new Sentence(sId, elements, comments);
                    this.sentences.push(sentence);
                } else {
                    logLineError('empty sentence, ignoring');
                }
                // reset
                elements = [];
                comments = [];
                beforeSentence = true;
                continue;
            }

            if (fields.length !== 10) {
                logLineError('expected 10 fields, got '+fields.length);
                repairFields(fields, this.logger);
            }

            var element = new Element(fields, idx, line);

            var issues = element.validate();
            for (var j=0; j<issues.length; j++) {
                logLineError(issues[j]);
            }
            if (issues.length !== 0) {
                if (!element.repair(this.logger)) {
                    logLineError('repair failed, discarding line');
                    continue; // failed, ignore line
                }
            }

            elements.push(element);
        }

        // If elements is non-empty, last sentence ended without its
        // expected terminating empty line. Process, but warn if strict.
        if (elements.length !== 0) {
            if (this.strict) {
                this.logError('missing blank line after last sentence');
            }
            var sId = 'S' + (this.sentences.length+1);
            var sentence = new Sentence(sId, elements, comments);
            this.sentences.push(sentence);
            // reset
            elements = [];
            comments = [];
            beforeSentence = true;
        }

        // If comments is non-empty, there were comments after the
        // terminating empty line. Warn and discard.
        if (comments.length !== 0) {
            this.logError('comments may not occur after last sentence, '+
                          'ignoring');
        }

        return this;
    }

    Document.prototype.toBrat = function(logger) {
        if (logger !== undefined) {
            this.logger = logger;
        }

        // merge brat data over all sentences
        var mergedBratData = {},
            textOffset = 0;
        var categories = [
            'entities',
            'attributes',
            'relations',
            'comments',
            'styles',
            'sentlabels'
        ];
        for (var i=0; i<categories.length; i++) {
            mergedBratData[categories[i]] = [];
        }
        mergedBratData['text'] = '';
        for (var i=0; i<this.sentences.length; i++) {
            var sentence = this.sentences[i];

            var issues = sentence.validate();
            for (var j=0; j<issues.length; j++) {
                this.logError(issues[j]);
            }
            if (issues.length !== 0) {
                if (!sentence.repair(this.logger)) {
                    this.logError('repair failed, discarding sentence');
                    continue;
                }
            }
            sentence.setBaseOffset(textOffset !== 0 ? textOffset + 1 : 0);
            bratData = sentence.toBrat();
            
            // merge
            if (mergedBratData['text'].length !== 0) {
                mergedBratData['text'] += '\n';
                textOffset += 1;
            }
            mergedBratData['text'] += bratData['text'];
            textOffset += bratData['text'].length;
            for (var j=0; j<categories.length; j++) {
                var c = categories[j];
                mergedBratData[c] = mergedBratData[c].concat(bratData[c]);
            }
        }

        // to avoid brat breakage on error, don't send empty text
        if (mergedBratData['text'].length === 0) {
            mergedBratData['text'] = '<EMPTY>';
        }

        mergedBratData['error'] = this.error;

        return mergedBratData;
    };

    /*
     * ConllU.Sentence: represents CoNLL-U sentence
     */

    var Sentence = function(sentenceId, elements, comments) {
        this.id = sentenceId;
        this.elements = elements;
        this.comments = comments;
        this.baseOffset = 0;
    };

    // set offset of first character in sentence (for standoff
    // generation)
    Sentence.prototype.setBaseOffset = function(baseOffset) {
        this.baseOffset = baseOffset;
    }

    Sentence.prototype.dependencies = function() {
        var dependencies = [];

        for (var i=0; i<this.elements.length; i++) {
            var element = this.elements[i];
            dependencies = dependencies.concat(element.dependencies());
        }

        return dependencies;
    };

    Sentence.prototype.words = function() {
        return this.elements.filter(function(e) { 
            return e.isWord();
        });
    };

    Sentence.prototype.multiwords = function() {
        return this.elements.filter(function(e) {
            return e.isMultiword();
        });
    };

    Sentence.prototype.tokens = function() {
        // extract token sequence by omitting word IDs that are
        // included in a multiword token range.
        var multiwords = this.multiwords();
        var inRange = {};
        for (var i=0; i<multiwords.length; i++) {
            var mw = multiwords[i];
            for (var j=mw.rangeFrom(); j<=mw.rangeTo(); j++) {
                inRange[j] = true;
            }
        }
        return this.elements.filter(function(e) {
            return e.isToken(inRange);
        });
    };

    // return words with possible modifications for visualization with
    // brat
    Sentence.prototype.bratWords = function() {
        var words = this.words();
        
        for (var i=0; i<words.length; i++) {
            if (isRtl(words[i].form)) {
                words[i] = deepCopy(words[i]);
                words[i].form = rtlFix(words[i].form);
            }
        }

        return words;
    };

    // return tokens with possible modifications for visualization
    // with brat
    Sentence.prototype.bratTokens = function() {
        var tokens = this.tokens();

        for (var i=0; i<tokens.length; i++) {
            tokens[i] = deepCopy(tokens[i]);
            tokens[i].form = rtlFix(tokens[i].form);
        }

        return tokens;
    };

    // return the text of the sentence for visualization with brat
    Sentence.prototype.bratText = function() {
        var words = this.bratWords();
        var tokens = this.bratTokens();

        var wordText = words.map(function(w) { return w.form }).join(' ');
        var tokenText = tokens.map(function(w) { return w.form }).join(' ');

        var combinedText = wordText;
        if (wordText != tokenText) {
            combinedText += '\n' + tokenText;
        }

        return combinedText;
    };

    // return the annotated text spans of the sentence for visualization
    // with brat.
    Sentence.prototype.bratSpans = function() {
        var spans = [],
            offset = this.baseOffset;

        // create an annotation for each word
        var words = this.bratWords();
        for (var i=0; i<words.length; i++) {
            var length = words[i].form.length;
            spans.push([this.id+'-T'+words[i].id, words[i].cpostag, 
                        [[offset, offset+length]]]);
            offset += length + 1;
        }

        return spans;
    }

    // return attributes of sentence annotations for visualization
    // with brat.
    Sentence.prototype.bratAttributes = function() {
        var words = this.words();

        // create attributes for word features
        var attributes = [],
            aidseq = 1;
        for (var i=0; i<words.length; i++) {
            var word = words[i],
                tid = this.id+'-T'+word.id;
            var nameVals = word.features();
            for (var j=0; j<nameVals.length; j++) {
                var name = nameVals[j][0], 
                    value = nameVals[j][1];
                attributes.push([this.id+'-A'+aidseq++, name, tid, value]);
            }
        }

        return attributes;
    };

    // return relations for sentence dependencies for visualization
    // with brat.
    Sentence.prototype.bratRelations = function() {
        var dependencies = this.dependencies();
        var relations = [];

        for (var i=0; i<dependencies.length; i++) {
            var dep = dependencies[i];
            relations.push([this.id+'-R'+i, dep[2], 
                            [ [ 'arg1', this.id+'-T'+dep[1] ],
                              [ 'arg2', this.id+'-T'+dep[0] ] ] ]);
        }

        return relations;
    };

    // return comments (notes) on sentence annotations for
    // visualization with brat.
    Sentence.prototype.bratComments = function() {
        var words = this.words();

        // TODO: better visualization for LEMMA, POSTAG, and MISC.
        var comments = [];
        for (var i=0; i<words.length; i++) {
            var word = words[i],
                tid = this.id+'-T'+word.id,
                label = 'AnnotatorNotes';
            comments.push([tid, label, 'Lemma: ' + word.lemma]);
            if (word.postag !== '_') {
                comments.push([tid, label, 'Postag: ' + word.postag]);
            }
            if (word.misc !== '_') {
                comments.push([tid, label, 'Misc: ' + word.misc]);
            }
        }

        return comments;
    };

    // Return styles on sentence annotations for visualization with
    // brat. Note: this feature is an extension of both the CoNLL-U
    // comment format and the basic brat data format.
    Sentence.prototype.bratStyles = function() {
        var styles = [],
            wildcards = [];

        for (var i=0; i<this.comments.length; i++) {
            var comment = this.comments[i];

            m = comment.match(/^(\#\s*visual-style\s+)(.*)/);
            if (!m) {
                continue;
            }
            var styleSpec = m[2];

            // Attempt to parse as a visual style specification. The
            // expected format is "REF<SPACE>STYLE", where REF
            // is either a single ID (for a span), a space-separated
            // ID1 ID2 TYPE triple (for a relation), or a special
            // wildcard value like "arcs", and STYLE is either
            // a colon-separated key-value pair or a color.
            m = styleSpec.match(/^([^\t]+)\s+(\S+)\s*$/);
            if (!m) {
                // TODO: avoid console.log
                console.log('warning: failed to parse: "'+comment+'"');
                continue;
            }
            var reference = m[1], style = m[2];

            // split style into key and value, adding a key to
            // color-only styles as needed for the reference type.
            var key, value;
            m = style.match(/^(\S+):(\S+)$/);
            if (m) {
                key = m[1];
                value = m[2];
            } else {
                value = style;
                if (reference === 'arcs' || reference.indexOf(' ') !== -1) {
                    key = 'color';
                } else {
                    key = 'bgColor';
                }
            }

            // store wildcards for separate later processing
            if (reference.match(/^(nodes|arcs)$/)) {
                wildcards.push([reference, key, value]);
                continue;
            }
            
            // adjust every ID in reference for brat
            if (reference.indexOf(' ') === -1) {
                reference = this.id + '-T' + reference;
            } else {
                reference = reference.split(' ');
                reference[0] = this.id + '-T' + reference[0];
                reference[1] = this.id + '-T' + reference[1];
            }

            styles.push([reference, key, value]);
        }

        // for expanding wildcards, first determine which words / arcs
        // styles have already been set, and then add the style to
        // everything that hasn't.
        var setStyle = {};
        for (var i=0; i<styles.length; i++) {
            setStyle[styles[i][0].concat([styles[i][1]])] = true;
        }
        for (var i=0; i<wildcards.length; i++) {
            var reference = wildcards[i][0],
                key = wildcards[i][1],
                value = wildcards[i][2];
            if (reference === 'nodes') {
                var words = this.words();
                for (var j=0; j<words.length; j++) {
                    var r = this.id + '-T' + words[j].id;
                    if (!setStyle[r.concat([key])]) {
                        styles.push([r, key, value]);
                        setStyle[r.concat([key])] = true;
                    }
                }
            } else if (reference === 'arcs') {
                var deps = this.dependencies();
                for (var j=0; j<deps.length; j++) {
                    var r = [this.id + '-T' + deps[j][1],
                             this.id + '-T' + deps[j][0],
                             deps[j][2]];
                    if (!setStyle[r.concat([key])]) {
                        styles.push([r, key, value]);
                        setStyle[r.concat([key])] = true;
                    }
                }
            } else {
                console.log('internal error');
            }
        }
        
        return styles;
    };

    // Return label of sentence for visualization with brat, or null
    // if not defined. Note: this feature is an extension of both the
    // CoNLL-U comment format and the basic brat data format.
    Sentence.prototype.bratLabel = function() {
        var label = null;

        for (var i=0; i<this.comments.length; i++) {
            var comment = this.comments[i];

            m = comment.match(/^(\#\s*sentence-label\b)(.*)/);
            if (!m) {
                continue;
            }
            label = m[2].trim();
        }
        return label;
    };

    // Return representation of sentence in brat embedded format (see
    // http://brat.nlplab.org/embed.html).
    // Note: "styles" is an extension, not part of the basic format.
    Sentence.prototype.toBrat = function() {
        var text = this.bratText();
        var spans = this.bratSpans();
        var attributes = this.bratAttributes();
        var relations = this.bratRelations();
        var comments = this.bratComments();
        var styles = this.bratStyles();
        var labels = [this.bratLabel()];

        return {
            'text': text,
            'entities': spans,
            'attributes': attributes,
            'relations': relations,
            'comments': comments,
            'styles': styles,
            'sentlabels': labels,
        };
    };

    Sentence.prototype.elementById = function() {
        var elementById = {};

        for (var i=0; i<this.elements.length; i++) {
            elementById[this.elements[i].id] = this.elements[i];
        }

        return elementById;
    };

    Sentence.prototype.addError = function(issue, element, issues) {
        issues.push('line '+(element.lineidx+1)+': '+issue+' ("'+element.line+'")');
    }

    // Check validity of the sentence. Return list of strings
    // representing issues found in validation (empty list if none).
    Sentence.prototype.validate = function() {
        var issues = [];

        this.validateUniqueIds(issues);
        this.validateWordSequence(issues);
        this.validateMultiwordSequence(issues);
        this.validateReferences(issues);

        return issues;
    };

    // Check for presence of ID duplicates
    Sentence.prototype.validateUniqueIds = function(issues) {
        issues = (issues !== undefined ? issues : []);

        var initialIssueCount = issues.length;
        var elementById = {};

        for (var i=0; i<this.elements.length; i++) {
            var element = this.elements[i];
            if (elementById[element.id] !== undefined) {
                this.addError('non-unique ID "'+element.id+'"',
                              element, issues);
            }
            elementById[element.id] = element;
        }

        return issues.length === initialIssueCount;
    };

    // Check validity of word ID sequence (should be 1,2,3,...)
    Sentence.prototype.validateWordSequence = function(issues) {
        issues = (issues !== undefined ? issues : []);

        var initialIssueCount = issues.length;
        var expectedId = 1;

        for (var i=0; i<this.elements.length; i++) {
            var element = this.elements[i];

            if (element.isMultiword()) {
                continue; // only check simple word sequence here
            }
            
            if (parseInt(element.id, 10) !== expectedId) {
                this.addError('word IDs should be 1,2,3,..., ' +
                              'expected '+expectedId+', got '+element.id,
                              element, issues);
            }
            expectedId = parseInt(element.id, 10) + 1;
        }

        return issues.length === initialIssueCount;
    };

    // Check that multiword token ranges are valid
    Sentence.prototype.validateMultiwordSequence = function(issues) {
        issues = (issues !== undefined ? issues : []);

        var initialIssueCount = issues.length;
        var expectedId = 1;

        for (var i=0; i<this.elements.length; i++) {
            var element = this.elements[i];

            if (element.isMultiword() && element.rangeFrom() !== expectedId) {
                this.addError('multiword tokens must appear before '+
                              'first word in their range',
                              element, issues);
            } else {
                expectedId = parseInt(element.id, 10) + 1;
            }
        }

        return issues.length === initialIssueCount;
    };

    // Check validity of ID references in HEAD and DEPS.
    Sentence.prototype.validateReferences = function(issues) {
        issues = (issues !== undefined ? issues : []);
        
        var initialIssueCount = issues.length;
        var elementById = this.elementById();

        for (var i=0; i<this.elements.length; i++) {
            var element = this.elements[i];

            // validate HEAD
            if (!element.validHeadReference(elementById)) {
                this.addError('HEAD is not valid ID: "'+element.head+'"',
                              element, issues);
            }
            
            // validate DEPS
            var elemDeps = element.dependencies(true);
            for (var j=0; j<elemDeps.length; j++) {
                var head = elemDeps[j][1];
                if (head !== '0' && elementById[head] === undefined) {
                    this.addError('invalid ID "'+head+'" in DEPS',
                                  element, issues);
                }
            }
        }

        return issues.length === initialIssueCount;
    };

    Sentence.prototype.repair = function(log) {
        log = (log !== undefined ? log : nullLogger);

        if (!this.validateUniqueIds()) {
            this.repairUniqueIds(log);
        }

        if (!this.validateWordSequence()) {
            this.repairWordSequence(log);
        }

        if (!this.validateMultiwordSequence()) {
            this.repairMultiwordSequence(log);
        }

        if (!this.validateReferences()) {
            this.repairReferences(log);
        }

        var issues = this.validate();
        return issues.length === 0;
    };

    Sentence.prototype.repairUniqueIds = function(log) {
        log = (log !== undefined ? log : nullLogger);

        var elementById = {},
            filtered = [];

        for (var i=0; i<this.elements.length; i++) {
            var element = this.elements[i];
            if (elementById[element.id] === undefined) {
                elementById[element.id] = element;
                filtered.push(element);
            } else {
                log('repair: remove element with duplicate ID "'+element.id+'"');
            }
        }
        this.elements = filtered;
    
        return true;
    };

    Sentence.prototype.repairWordSequence = function(log) {
        log('TODO: implement ConllU.Sentence.repairWordSequence()');
        return true;
    };

    Sentence.prototype.repairMultiwordSequence = function(log) {
        log('TODO: implement ConllU.Sentence.repairMultiwordSequence()');
        return true;
    };

    Sentence.prototype.repairReferences = function(log) {
        log = (log !== undefined ? log : nullLogger);

        var elementById = this.elementById();

        for (var i=0; i<this.elements.length; i++) {
            var element = this.elements[i];

            // repair HEAD if not valid
            if (!element.validHeadReference(elementById)) {
                log('repair: blanking invalid HEAD');
                element.head = null;
            }

            // repair DEPS if not valid
            if (element.deps === '_') {
                continue;
            }
            var deparr = element.deps.split('|'),
                filtered = [];
            for (var j=0; j<deparr.length; j++) {
                var dep = deparr[j];
                var m = dep.match(dependencyRegex);
                if (m) {
                    var head = m[1], deprel = m[2];
                    if (head === '0' || elementById[head] !== undefined) {
                        filtered.push(dep);
                    } else {
                        log('repair: removing invalid ID from DEPS');
                        error = true;
                    }
                } else {
                    console.log('internal error: repairReferences(): ' +
                                'invalid DEPS');
                }
            }
            if (filtered.length === 0) {
                element.deps = '_';
            } else {
                element.deps = filtered.join('|');
            }
        }
        return true;
    };

    /*
     * ConllU.Element: represents CoNLL-U word or multiword token
     */

    // represents CoNLL-U word or multiword token
    var Element = function(fields, lineidx, line) {
        this.id = fields[0];
        this.form = fields[1];
        this.lemma = fields[2];
        this.cpostag = fields[3];
        this.postag = fields[4];
        this.feats = fields[5];
        this.head = fields[6];
        this.deprel = fields[7];
        this.deps = fields[8];
        this.misc = fields[9];
        this.lineidx = lineidx;
        this.line = line;
    };

    // constraints that hold for all fields
    Element.prototype.validateField = function(field, name, issues) {
        name = (name !== undefined ? name : 'field');
        issues = (issues !== undefined ? issues : []);
        
        if (field === undefined) {
            issues.push('invalid '+name);
            return false;
        } else if (field.length === 0) {
            issues.push(name+' must not be empty: "'+field+'"');
            return false;
        } else if (hasSpace(field)) {
            issues.push(name+' must not contain space: "'+field+'"');
            return false;
        } else {
            return true;
        }
    };

    Element.prototype.validateId = function(id, issues) {
        issues = (issues !== undefined ? issues : []);
        
        if (!this.validateField(id, 'ID', issues)) {
            return false;
        } else if (id.match(/^\d+$/)) {
            if (id === '0') {
                issues.push('ID indices must start from 1: "'+id+'"');
                return false;
            } else {
                return true;
            }
        } else if (id.match(/^(\d+)-(\d+)$/)) {
            m = id.match(/^(\d+)-(\d+)$/);
            if (!m) {
                console.log('internal error');
                return false;
            }
            var start = parseInt(m[1], 10), 
                end = parseInt(m[2], 10);
            if (end < start) {
                issues.push('ID ranges must have start <= end: "'+id+'"');
                return false;
            } else {
                return true;
            }
        } else {
            issues.push('ID must be integer or range: "'+id+'"');
            return false;
        }
    };

    Element.prototype.validateForm = function(form, issues) {
        issues = (issues !== undefined ? issues : []);
        
        if (!this.validateField(form, 'FORM', issues)) {
            return false;
        } else {
            return true;
        }
    };

    Element.prototype.validateLemma = function(lemma, issues) {
        issues = (issues !== undefined ? issues : []);
        
        if (!this.validateField(lemma, 'LEMMA', issues)) {
            return false;
        } else {
            return true;
        }
    };
    
    Element.prototype.validateCpostag = function(cpostag, issues) {
        issues = (issues !== undefined ? issues : []);
        
        if (!this.validateField(cpostag, 'CPOSTAG', issues)) {
            return false;
        } else {
            return true;
        }
    };

    Element.prototype.validatePostag = function(postag, issues) {
        issues = (issues !== undefined ? issues : []);
        
        if (!this.validateField(postag, 'POSTAG', issues)) {
            return false;
        } else {
            return true;
        }
    };

    Element.prototype.validateFeats = function(feats, issues) {
        issues = (issues !== undefined ? issues : []);
        
        if (!this.validateField(feats, 'FEATS', issues)) {
            return false;
        } else if (feats === '_') {
            return true;
        }
        var initialIssueCount = issues.length;
        var featarr = feats.split('|');
        var featmap = {};
        var prevName = null;
        for (var i=0; i<featarr.length; i++) {
            var feat = featarr[i];
            var m = feat.match(featureRegex);
            if (!m) {
                // TODO more descriptive issue
                issues.push('invalid FEATS entry: "'+feat+'"');
                continue;
            }
            var name = m[1], valuestr = m[2];
            if (prevName !== null && 
                name.toLowerCase() < prevName.toLowerCase()) {
                issues.push('features must be ordered alphabetically ' +
                            '(case-insensitive): "'+name+'" < "'+prevName+'"');
                noIssue = false;
            }
            prevName = name;
            var values = valuestr.split(',');
            var valuemap = {}, validValues = [];
            for (var j=0; j<values.length; j++) {
                var value = values[j];
                var m = value.match(featureValueRegex);
                if (!m) {
                    issues.push('invalid FEATS value: "'+value+'"');
                    continue;
                }
                if (valuemap[value] !== undefined) {
                    issues.push('duplicate feature value: "'+value+'"');
                    continue;
                }
                valuemap[value] = true;
                validValues.push(value);
            }
            if (featmap[name] !== undefined) {
                issues.push('duplicate feature name: "'+name+'"');
                continue;
            }
            if (validValues.length !== 0) {
                featmap[name] = validValues;
            }
        }
        return issues.length === initialIssueCount;
    };

    Element.prototype.validateHead = function(head, issues) {
        issues = (issues !== undefined ? issues : []);

        // TODO: consider checking that DEPREL is "root" iff HEAD is 0.
        
        if (head === null) {
            return true; // exceptional case for Element.repair()
        } else if (!this.validateField(head, 'HEAD', issues)) {
            return false;
        } else if (!head.match(/^\d+$/)) {
            issues.push('HEAD must be an ID or zero: "'+head+'"');
            return false;
        } else {
            return true;
        }
    };

    Element.prototype.validateDeprel = function(deprel, issues) {
        issues = (issues !== undefined ? issues : []);

        if (!this.validateField(deprel, 'DEPREL', issues)) {
            return false;
        } else {
            return true;
        }
    };

    Element.prototype.validateDeps = function(deps, issues) {
        issues = (issues !== undefined ? issues : []);

        // TODO: consider checking that deprel is "root" iff head is 0.

        if (!this.validateField(deps, 'DEPS', issues)) {
            return false;
        } else if (deps === '_') {
            return true;
        }
        var deparr = deps.split('|');
        var prevHead = null;
        // TODO: don't short-circuit on first error
        for (var i=0; i<deparr.length; i++) {
            var dep = deparr[i];
            m = dep.match(/^(\d+):(\S+)$/);
            if (!m) {
                // TODO more descriptive issue
                issues.push('invalid DEPS: "'+deps+'"');
                return false;
            }
            var head = m[1], deprel = m[2];
            if (prevHead !== null &&
                parseInt(head, 10) < parseInt(prevHead, 10)) {
                issues.push('DEPS must be ordered by head index');
                return false;
            }
            prevHead = head;
        }
        return true;
    };

    Element.prototype.validateMisc = function(misc, issues) {
        issues = (issues !== undefined ? issues : []);

        if (!this.validateField(misc, 'MISC', issues)) {
            return false;
        } else {
            return true;
        }
    };

    Element.prototype.validHeadReference = function(elementById) {
        return (this.head === '_' || this.head === null || this.head === '0' ||
                elementById[this.head] !== undefined);
    };

    Element.prototype.isWord = function() {
        // word iff ID is an integer
        return !!this.id.match(/^\d+$/);
    };

    Element.prototype.isMultiword = function() {
        return !!this.id.match(/^\d+-\d+$/);
    };

    Element.prototype.rangeFrom = function() {
        return parseInt(this.id.match(/^(\d+)-\d+$/)[1], 10);
    };

    Element.prototype.rangeTo = function() {
        return parseInt(this.id.match(/^\d+-(\d+)$/)[1], 10);
    };

    Element.prototype.isToken = function(inRange) {
        // token iff multiword or not included in a multiword range
        return this.isMultiword() || !inRange[this.id];
    };

    // return list of (DEPENDENT, HEAD, DEPREL) lists
    Element.prototype.dependencies = function(skipHead) {
        skipHead = (skipHead !== undefined ? skipHead : false);

        var elemDeps = [];
        if (!skipHead && this.head !== '_' && this.head !== null) {
            elemDeps.push([this.id, this.head, this.deprel]);
        }
        if (this.deps != '_') {
            var deparr = this.deps.split('|');
            for (var i=0; i<deparr.length; i++) {
                var dep = deparr[i];
                m = dep.match(dependencyRegex);
                if (m) {
                    elemDeps.push([this.id, m[1], m[2]]);
                } else {
                    console.log('internal error: dependencies(): invalid DEPS');
                }
            }
        }
        return elemDeps;
    }

    // return list of (name, value) pairs
    Element.prototype.features = function() {
        var nameVals = [];
        if (this.feats === '_') {
            return [];
        }
        var featarr = this.feats.split('|');
        for (var i=0; i<featarr.length; i++) {
            var feat = featarr[i];
            var m = feat.match(featureRegex);
            if (!m) {
                continue;
            }
            var name = m[1], valuestr = m[2];
            var values = valuestr.split(',');
            for (var j=0; j<values.length; j++) {
                var value = values[j];
                var m = value.match(featureValueRegex);
                if (!m) {
                    continue;
                }
                nameVals.push([name, value]);
            }
        }
        return nameVals;
    };

    // Check validity of the element. Return list of strings
    // representing issues found in validation (empty list if none).
    Element.prototype.validate = function() {
        var issues = [];

        this.validateId(this.id, issues);
        this.validateForm(this.form, issues);
        
        // multiword tokens (elements with range IDs) are (locally) valid
        // iff all remaining fields (3-10) contain just an underscore.
        if (this.isMultiword()) {
            if (this.lemma != '_' || 
                this.cpostag != '_' ||
                this.postag != '_' ||
                this.feats != '_' ||
                this.head != '_' ||
                this.deprel != '_' ||
                this.deps != '_' ||
                this.misc != '_') {
                issues.push('non-underscore field for multiword token');
            }
            return issues;
        }
        // if we're here, not a multiword token.

        this.validateLemma(this.lemma, issues);
        this.validateCpostag(this.cpostag, issues);
        this.validatePostag(this.postag, issues);
        this.validateFeats(this.feats, issues);
        this.validateHead(this.head, issues);
        this.validateDeprel(this.deprel, issues);
        this.validateDeps(this.deps, issues);
        this.validateMisc(this.misc, issues);

        return issues;
    };

    // Attempt to repair a non-valid element. Return true iff the
    // element is valid following repair, false otherwise.
    Element.prototype.repair = function(log) {
        log = (log !== undefined ? log : nullLogger);

        if (!this.validateId(this.id)) {
            return false; // can't be helped
        } 

        if (!this.validateForm(this.form)) {
            log('repair: blanking invalid FORM');
            this.form = '<ERROR>';
        }

        if (this.isMultiword()) {
            // valid as long as everything is blank
            this.lemma = '_'; 
            this.cpostag = '_';
            this.postag = '_';
            this.feats = '_';
            this.head = '_';
            this.deprel = '_';
            this.deps = '_';
            this.misc = '_';
            return true;
        }
        // if we're here, not a multiword token.

        if(!this.validateLemma(this.lemma)) {
            log('repair: blanking invalid LEMMA');
            this.lemma = '<ERROR>';
        }

        if(!this.validateCpostag(this.cpostag)) {
            log('repair: blanking invalid CPOSTAG');
            this.cpostag = '_'; // TODO: not valid
        }

        if(!this.validatePostag(this.postag)) {
            log('repair: blanking invalid POSTAG');
            this.postag = '_';
        }

        if(!this.validateFeats(this.feats)) {
            log('repair: blanking invalid FEATS');
            this.feats = '_';
        }

        if(!this.validateHead(this.head)) {
            log('repair: blanking invalid HEAD');
            this.head = null; // note: exceptional case
        }

        if(!this.validateDeprel(this.deprel)) {
            log('repair: blanking invalid DEPREL');
            this.deprel = '_'; // TODO: not valid
        }

        if(!this.validateDeps(this.deps)) {
            log('repair: blanking invalid DEPS');
            this.deps = '_';
        }

        if(!this.validateMisc(this.misc)) {
            log('repair: blanking invalid MISC');
            this.misc = '_';
        }

        var issues = this.validate();
        return issues.length === 0;
    };

    /*
     * Miscellaneous support functions.
     */

    var repairFields = function(fields, logger) {
        if (logger === undefined) {
            logger = nullLogger;
        }
        if (fields.length > 10) {
            logger('repair: discarding fields > 10');
            fields = fields.slice(0, 10);
        } else {
            logger('repair: filling in empty ("_") for missing fields');
            for (var m=0; m<10-fields.length; m++) {
                fields.push('_');
            }
        }
    };

    var strictFieldSplitter = function(line) {
        // strict CoNLL format parsing: only split on TAB, no extra space.
        if (line.length === 0) {
            return [];
        } else {
            return line.split('\t');
        }
    }

    var looseFieldSplitter = function(line) {
        // loose CoNLL format parsing: split on any space sequence, trim
        // surrounding space.    
        line = line.trim();
        if (line.length === 0) {
            return [];
        } else {
            return line.split(/\s+/);
        }
    }

    var selectParsingMode = function(conll, log) {
        // return whether to use strict mode parsing

        // very simple heuristic: any TABs in the input trigger
        // strict parsing, loose only if none present.
        if (conll.indexOf('\t') !== -1) {
            log('note: TAB found, parsing CoNLL-U in strict mode.')
            return true;
        } else {
            log('note: no TAB found, parsing CoNLL-U in loose mode.')
            return false;
        }
    };

    var selectFieldSplitter = function(conll, log, strict) {
        // return function to use for dividing lines into fields.
        if (strict) {
            return strictFieldSplitter;
        } else {
            return looseFieldSplitter;
        }
    };

    var isComment = function(line) {
        return line.length !== 0 && line[0] === '#';
    };

    var hasSpace = function(s) {
        return !!s.match(/\s/);
    };

    var nullLogger = function(message) {
        return null;
    }

    /* 
     * Return true iff given string only contains characters from a
     * right-to-left Unicode block and is not empty.
     */
    var isRtl = function(s) {
        // range from http://stackoverflow.com/a/14824756
        return !!s.match(/^[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]+$/);
    };

    /*
     * Return given token with possible modifications to accommodate
     * issues in brat rendering of right-to-left text
     * (https://github.com/UniversalDependencies/docs/issues/52)
     */
    var rtlFix = function(s) {
        var prefix = '\u02D1',
            suffix = '\u02D1';
        if (isRtl(s)) {
            s = prefix + s + suffix;
        }
        return s;
    };

    /*
     * Return a deep copy of the given object. Note: not particularly
     * efficient, and all fields must be serializable for this to work
     * correctly.
     */
    var deepCopy = function(o) {
        return JSON.parse(JSON.stringify(o));
    };

    /*
     * Regular expressions for various parts of the format.
     * See https://github.com/UniversalDependencies/docs/issues/33
     */

    // match single (feature, value[s]) pair in FEATS
    var featureRegex = /^([A-Z0-9][a-zA-Z0-9]*(?:\[[a-z0-9]+\])?)=([A-Z0-9][a-zA-Z0-9]*(?:,[A-Z0-9][a-zA-Z0-9]*)*)$/;

    // match single feature value in FEATS
    var featureValueRegex = /^[A-Z0-9][a-zA-Z0-9]*$/;

    // match single (head, deprel) pair in DEPS
    var dependencyRegex = /^(\d+):(.*)$/;

    return {
	Document: Document,
	Sentence: Sentence,
        Element: Element,
    };

})(window);
// -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; -*-
// vim:set ft=javascript ts=2 sw=2 sts=2 cindent:

var Dispatcher = (function($, window, undefined) {
    var Dispatcher = function() {
      var that = this;

      var table = {};

      var on = function on(message, host, handler) {
        if (handler === undefined) {
          handler = host;
          host = on.caller;
        }
        if (table[message] === undefined) {
          table[message] = [];
        }
        table[message].push([host, handler]);
        return this;
      };

      // Notify listeners that we encountered an error in an asynch call
      var inAsynchError = false; // To avoid error avalanches
      var handleAsynchError = function(e) {
        if (!inAsynchError) {
          inAsynchError = true;
          // TODO: Hook printout into dispatch elsewhere?
          console.warn('Handled async error:', e);
          that.post('dispatchAsynchError', [e]);
          inAsynchError = false;
        } else {
          console.warn('Dropped asynch error:', e);
        }
      };

      var post = function post(asynch, message, args, returnType) {
        if (typeof(asynch) !== 'number') {
          // no asynch parameter
          returnType = args;
          args = message;
          message = asynch;
          asynch = null;
        }
        if (args === undefined) {
          args = [];
        }
        var results = [];
        // DEBUG: if (typeof(message) != "string" || !(message.match(/mouse/) || message == "hideComment")) console.log(message, args);

        if (typeof(message) === 'function') {
          // someone was lazy and sent a simple function
          var host = post.caller;
          if (asynch !== null) {
            result = setTimeout(function() {
              try {
                message.apply(host, args);
              } catch(e) {
                handleAsynchError(e);
              }
            }, asynch);
          } else {
            result = message.apply(host, args);
          }
          results.push(result);
        } else {
          // a proper message, propagate to all interested parties
          var todo = table[message];
          if (todo !== undefined) {
            $.each(todo, function(itemNo, item) {
              var result;
              if (asynch !== null) {
                result = setTimeout(function() {
                  try {
                    item[1].apply(item[0], args);
                  } catch (e) {
                    handleAsynchError(e);
                  }
                }, asynch);
              } else {
                result = item[1].apply(item[0], args);
              }
              results.push(result);
            });
/* DEBUG
          } else {
            console.warn('Message ' + message + ' has no subscribers.'); // DEBUG
*/
          }
        }
        if (returnType == 'any') {
          var i = results.length;
          while (i--) {
            if (results[i] !== false) return results[i];
          }
          return false;
        }
        if (returnType == 'all') {
          var i = results.length;
          while (i--) {
            if (results[i] === false) return results[i];
          }
        }
        return results;
      };

      var proxy = function(destination, message) {
        this.on(message, function() {
          destination.post(message, Array.prototype.slice.call(arguments));
        });
      };

      var dispatcher = {
        on: on,
        post: post,
        proxy: proxy,
      };
      Dispatcher.dispatchers.push(dispatcher);
      return dispatcher;
    };

    Dispatcher.dispatchers = [];
    Dispatcher.post = function(asynch, message, args, returnType) {
      $.each(Dispatcher.dispatchers, function(dispatcherNo, dispatcher) {
        dispatcher.post(asynch, message, args, returnType);
      });
    };

    return Dispatcher;
})(jQuery, window);
/*!
 *  Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome
 *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
 */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.4.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}
/*!
 *  Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome
 *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
 */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.4.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}
/**
	Head JS		The only script in your <HEAD>
	Copyright	Tero Piirainen (tipiirai)
	License		MIT / http://bit.ly/mit-license
	Version		0.9
	
	http://headjs.com
*/(function(a){var b=a.documentElement,c,d,e=[],f=[],g={},h={},i=a.createElement("script").async===true||"MozAppearance"in a.documentElement.style||window.opera;var j=window.head_conf&&head_conf.head||"head",k=window[j]=window[j]||function(){k.ready.apply(null,arguments)};var l=0,m=1,n=2,o=3;i?k.js=function(){var a=arguments,b=a[a.length-1],c=[];t(b)||(b=null),s(a,function(d,e){d!=b&&(d=r(d),c.push(d),x(d,b&&e==a.length-2?function(){u(c)&&p(b)}:null))});return k}:k.js=function(){var a=arguments,b=[].slice.call(a,1),d=b[0];if(!c){f.push(function(){k.js.apply(null,a)});return k}d?(s(b,function(a){t(a)||w(r(a))}),x(r(a[0]),t(d)?d:function(){k.js.apply(null,b)})):x(r(a[0]));return k},k.ready=function(a,b){if(a=="dom"){d?p(b):e.push(b);return k}t(a)&&(b=a,a="ALL");var c=h[a];if(c&&c.state==o||a=="ALL"&&u()&&d){p(b);return k}var f=g[a];f?f.push(b):f=g[a]=[b];return k},k.ready("dom",function(){c&&u()&&s(g.ALL,function(a){p(a)}),k.feature&&k.feature("domloaded",true)});function p(a){a._done||(a(),a._done=1)}function q(a){var b=a.split("/"),c=b[b.length-1],d=c.indexOf("?");return d!=-1?c.substring(0,d):c}function r(a){var b;if(typeof a=="object")for(var c in a)a[c]&&(b={name:c,url:a[c]});else b={name:q(a),url:a};var d=h[b.name];if(d&&d.url===b.url)return d;h[b.name]=b;return b}function s(a,b){if(a){typeof a=="object"&&(a=[].slice.call(a));for(var c=0;c<a.length;c++)b.call(a,a[c],c)}}function t(a){return Object.prototype.toString.call(a)=="[object Function]"}function u(a){a=a||h;var b=false,c=0;for(var d in a){if(a[d].state!=o)return false;b=true,c++}return b||c===0}function v(a){a.state=l,s(a.onpreload,function(a){a.call()})}function w(a,b){a.state||(a.state=m,a.onpreload=[],y({src:a.url,type:"cache"},function(){v(a)}))}function x(a,b){if(a.state==o&&b)return b();if(a.state==n)return k.ready(a.name,b);if(a.state==m)return a.onpreload.push(function(){x(a,b)});a.state=n,y(a.url,function(){a.state=o,b&&b(),s(g[a.name],function(a){p(a)}),d&&u()&&s(g.ALL,function(a){p(a)})})}function y(c,d){var e=a.createElement("script");e.type="text/"+(c.type||"javascript"),e.src=c.src||c,e.async=false,e.onreadystatechange=e.onload=function(){var a=e.readyState;!d.done&&(!a||/loaded|complete/.test(a))&&(d(),d.done=true)},b.appendChild(e)}setTimeout(function(){c=true,s(f,function(a){a()})},0);function z(){d||(d=true,s(e,function(a){p(a)}))}window.addEventListener?(a.addEventListener("DOMContentLoaded",z,false),window.addEventListener("onload",z,false)):window.attachEvent&&(a.attachEvent("onreadystatechange",function(){a.readyState==="complete"&&z()}),window.frameElement==null&&b.doScroll&&function(){try{b.doScroll("left"),z()}catch(a){setTimeout(arguments.callee,1);return}}(),window.attachEvent("onload",z)),!a.readyState&&a.addEventListener&&(a.readyState="loading",a.addEventListener("DOMContentLoaded",handler=function(){a.removeEventListener("DOMContentLoaded",handler,false),a.readyState="complete"},false))})(document)
/**
	Head JS		The only script in your <HEAD>
	Copyright	Tero Piirainen (tipiirai)
	License		MIT / http://bit.ly/mit-license
	Version		0.9
	
	http://headjs.com
*/(function(a){var b=a.documentElement,c,d,e=[],f=[],g={},h={},i=a.createElement("script").async===true||"MozAppearance"in a.documentElement.style||window.opera;var j=window.head_conf&&head_conf.head||"head",k=window[j]=window[j]||function(){k.ready.apply(null,arguments)};var l=0,m=1,n=2,o=3;i?k.js=function(){var a=arguments,b=a[a.length-1],c=[];t(b)||(b=null),s(a,function(d,e){d!=b&&(d=r(d),c.push(d),x(d,b&&e==a.length-2?function(){u(c)&&p(b)}:null))});return k}:k.js=function(){var a=arguments,b=[].slice.call(a,1),d=b[0];if(!c){f.push(function(){k.js.apply(null,a)});return k}d?(s(b,function(a){t(a)||w(r(a))}),x(r(a[0]),t(d)?d:function(){k.js.apply(null,b)})):x(r(a[0]));return k},k.ready=function(a,b){if(a=="dom"){d?p(b):e.push(b);return k}t(a)&&(b=a,a="ALL");var c=h[a];if(c&&c.state==o||a=="ALL"&&u()&&d){p(b);return k}var f=g[a];f?f.push(b):f=g[a]=[b];return k},k.ready("dom",function(){c&&u()&&s(g.ALL,function(a){p(a)}),k.feature&&k.feature("domloaded",true)});function p(a){a._done||(a(),a._done=1)}function q(a){var b=a.split("/"),c=b[b.length-1],d=c.indexOf("?");return d!=-1?c.substring(0,d):c}function r(a){var b;if(typeof a=="object")for(var c in a)a[c]&&(b={name:c,url:a[c]});else b={name:q(a),url:a};var d=h[b.name];if(d&&d.url===b.url)return d;h[b.name]=b;return b}function s(a,b){if(a){typeof a=="object"&&(a=[].slice.call(a));for(var c=0;c<a.length;c++)b.call(a,a[c],c)}}function t(a){return Object.prototype.toString.call(a)=="[object Function]"}function u(a){a=a||h;var b=false,c=0;for(var d in a){if(a[d].state!=o)return false;b=true,c++}return b||c===0}function v(a){a.state=l,s(a.onpreload,function(a){a.call()})}function w(a,b){a.state||(a.state=m,a.onpreload=[],y({src:a.url,type:"cache"},function(){v(a)}))}function x(a,b){if(a.state==o&&b)return b();if(a.state==n)return k.ready(a.name,b);if(a.state==m)return a.onpreload.push(function(){x(a,b)});a.state=n,y(a.url,function(){a.state=o,b&&b(),s(g[a.name],function(a){p(a)}),d&&u()&&s(g.ALL,function(a){p(a)})})}function y(c,d){var e=a.createElement("script");e.type="text/"+(c.type||"javascript"),e.src=c.src||c,e.async=false,e.onreadystatechange=e.onload=function(){var a=e.readyState;!d.done&&(!a||/loaded|complete/.test(a))&&(d(),d.done=true)},b.appendChild(e)}setTimeout(function(){c=true,s(f,function(a){a()})},0);function z(){d||(d=true,s(e,function(a){p(a)}))}window.addEventListener?(a.addEventListener("DOMContentLoaded",z,false),window.addEventListener("onload",z,false)):window.attachEvent&&(a.attachEvent("onreadystatechange",function(){a.readyState==="complete"&&z()}),window.frameElement==null&&b.doScroll&&function(){try{b.doScroll("left"),z()}catch(a){setTimeout(arguments.callee,1);return}}(),window.attachEvent("onload",z)),!a.readyState&&a.addEventListener&&(a.readyState="loading",a.addEventListener("DOMContentLoaded",handler=function(){a.removeEventListener("DOMContentLoaded",handler,false),a.readyState="complete"},false))})(document)
/*! Hint.css - v1.3.4 - 2015-02-28
* http://kushagragour.in/lab/hint/
* Copyright (c) 2015 Kushagra Gour; Licensed MIT */

/*-------------------------------------*\
	HINT.css - A CSS tooltip library
\*-------------------------------------*/
/**
 * HINT.css is a tooltip library made in pure CSS.
 *
 * Source: https://github.com/chinchang/hint.css
 * Demo: http://kushagragour.in/lab/hint/
 *
 * Release under The MIT License
 *
 */
/**
 * source: hint-core.scss
 *
 * Defines the basic styling for the tooltip.
 * Each tooltip is made of 2 parts:
 * 	1) body (:after)
 * 	2) arrow (:before)
 *
 * Classes added:
 * 	1) hint
 */
.hint, [data-hint] {
  position: relative;
  display: inline-block;
  /**
   * tooltip arrow
   */
  /**
   * tooltip body
   */ }
  .hint:before, .hint:after, [data-hint]:before, [data-hint]:after {
    position: absolute;
    -webkit-transform: translate3d(0, 0, 0);
    -moz-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    visibility: hidden;
    opacity: 0;
    z-index: 1000000;
    pointer-events: none;
    -webkit-transition: 0.3s ease;
    -moz-transition: 0.3s ease;
    transition: 0.3s ease;
    -webkit-transition-delay: 0ms;
    -moz-transition-delay: 0ms;
    transition-delay: 0ms; }
  .hint:hover:before, .hint:hover:after, .hint:focus:before, .hint:focus:after, [data-hint]:hover:before, [data-hint]:hover:after, [data-hint]:focus:before, [data-hint]:focus:after {
    visibility: visible;
    opacity: 1; }
  .hint:hover:before, .hint:hover:after, [data-hint]:hover:before, [data-hint]:hover:after {
    -webkit-transition-delay: 100ms;
    -moz-transition-delay: 100ms;
    transition-delay: 100ms; }
  .hint:before, [data-hint]:before {
    content: '';
    position: absolute;
    background: transparent;
    border: 6px solid transparent;
    z-index: 1000001; }
  .hint:after, [data-hint]:after {
    content: attr(data-hint);
    background: #383838;
    color: white;
    padding: 8px 10px;
    font-size: 12px;
    line-height: 12px;
/*  white-space: nowrap; */ /* SMP */
    min-width: 20em; /* SMP */
    max-width: 20em; /* SMP */
    font-weight: normal;
 }

/**
 * source: hint-position.scss
 *
 * Defines the positoning logic for the tooltips.
 *
 * Classes added:
 * 	1) hint--top
 * 	2) hint--bottom
 * 	3) hint--left
 * 	4) hint--right
 */
/**
 * set default color for tooltip arrows
 */
.hint--top:before {
  border-top-color: #383838; }

.hint--bottom:before {
  border-bottom-color: #383838; }

.hint--left:before {
  border-left-color: #383838; }

.hint--right:before {
  border-right-color: #383838; }

/**
 * top tooltip
 */
.hint--top:before {
  margin-bottom: -12px; }
.hint--top:after {
  margin-left: -18px; }
.hint--top:before, .hint--top:after {
  bottom: 100%;
  left: 50%; }
.hint--top:hover:after, .hint--top:hover:before, .hint--top:focus:after, .hint--top:focus:before {
  -webkit-transform: translateY(-8px);
  -moz-transform: translateY(-8px);
  transform: translateY(-8px); }

/**
 * bottom tooltip
 */
.hint--bottom:before {
  margin-top: -12px; }
.hint--bottom:after {
  margin-left: -18px; }
.hint--bottom:before, .hint--bottom:after {
  top: 100%;
  left: 50%; }
.hint--bottom:hover:after, .hint--bottom:hover:before, .hint--bottom:focus:after, .hint--bottom:focus:before {
  -webkit-transform: translateY(8px);
  -moz-transform: translateY(8px);
  transform: translateY(8px); }

/**
 * right tooltip
 */
.hint--right:before {
  margin-left: -12px;
  margin-bottom: -6px; }
.hint--right:after {
  margin-bottom: -14px; }
.hint--right:before, .hint--right:after {
  left: 100%;
  bottom: 50%; }
.hint--right:hover:after, .hint--right:hover:before, .hint--right:focus:after, .hint--right:focus:before {
  -webkit-transform: translateX(8px);
  -moz-transform: translateX(8px);
  transform: translateX(8px); }

/**
 * left tooltip
 */
.hint--left:before {
  margin-right: -12px;
  margin-bottom: -6px; }
.hint--left:after {
  margin-bottom: -14px; }
.hint--left:before, .hint--left:after {
  right: 100%;
  bottom: 50%; }
.hint--left:hover:after, .hint--left:hover:before, .hint--left:focus:after, .hint--left:focus:before {
  -webkit-transform: translateX(-8px);
  -moz-transform: translateX(-8px);
  transform: translateX(-8px); }

/**
 * source: hint-theme.scss
 *
 * Defines basic theme for tooltips.
 *
 */
.hint, [data-hint] {
  /**
   * tooltip body
   */ }
  .hint:after, [data-hint]:after {
    text-shadow: 0 -1px 0px black;
    box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.3); }

/**
 * source: hint-color-types.scss
 *
 * Contains tooltips of various types based on color differences.
 *
 * Classes added:
 * 	1) hint--error
 * 	2) hint--warning
 * 	3) hint--info
 * 	4) hint--success
 *
 */
/**
 * Error
 */
.hint--error:after {
  background-color: #b34e4d;
  text-shadow: 0 -1px 0px #592726; }
.hint--error.hint--top:before {
  border-top-color: #b34e4d; }
.hint--error.hint--bottom:before {
  border-bottom-color: #b34e4d; }
.hint--error.hint--left:before {
  border-left-color: #b34e4d; }
.hint--error.hint--right:before {
  border-right-color: #b34e4d; }

/**
 * Warning
 */
.hint--warning:after {
  background-color: #c09854;
  text-shadow: 0 -1px 0px #6c5328; }
.hint--warning.hint--top:before {
  border-top-color: #c09854; }
.hint--warning.hint--bottom:before {
  border-bottom-color: #c09854; }
.hint--warning.hint--left:before {
  border-left-color: #c09854; }
.hint--warning.hint--right:before {
  border-right-color: #c09854; }

/**
 * Info
 */
.hint--info:after {
  background-color: #3986ac;
  text-shadow: 0 -1px 0px #193b4d; }
.hint--info.hint--top:before {
  border-top-color: #3986ac; }
.hint--info.hint--bottom:before {
  border-bottom-color: #3986ac; }
.hint--info.hint--left:before {
  border-left-color: #3986ac; }
.hint--info.hint--right:before {
  border-right-color: #3986ac; }

/**
 * Success
 */
.hint--success:after {
  background-color: #458746;
  text-shadow: 0 -1px 0px #1a321a; }
.hint--success.hint--top:before {
  border-top-color: #458746; }
.hint--success.hint--bottom:before {
  border-bottom-color: #458746; }
.hint--success.hint--left:before {
  border-left-color: #458746; }
.hint--success.hint--right:before {
  border-right-color: #458746; }

/**
 * source: hint-always.scss
 *
 * Defines a persisted tooltip which shows always.
 *
 * Classes added:
 * 	1) hint--always
 *
 */
.hint--always:after, .hint--always:before {
  opacity: 1;
  visibility: visible; }
.hint--always.hint--top:after, .hint--always.hint--top:before {
  -webkit-transform: translateY(-8px);
  -moz-transform: translateY(-8px);
  transform: translateY(-8px); }
.hint--always.hint--bottom:after, .hint--always.hint--bottom:before {
  -webkit-transform: translateY(8px);
  -moz-transform: translateY(8px);
  transform: translateY(8px); }
.hint--always.hint--left:after, .hint--always.hint--left:before {
  -webkit-transform: translateX(-8px);
  -moz-transform: translateX(-8px);
  transform: translateX(-8px); }
.hint--always.hint--right:after, .hint--always.hint--right:before {
  -webkit-transform: translateX(8px);
  -moz-transform: translateX(8px);
  transform: translateX(8px); }

/**
 * source: hint-rounded.scss
 *
 * Defines rounded corner tooltips.
 *
 * Classes added:
 * 	1) hint--rounded
 *
 */
.hint--rounded:after {
  border-radius: 4px; }

/**
 * source: hint-effects.scss
 *
 * Defines various transition effects for the tooltips.
 *
 * Classes added:
 * 	1) hint--no-animate
 * 	2) hint--bounce
 *
 */
.hint--no-animate:before, .hint--no-animate:after {
  -webkit-transition-duration: 0ms;
  -moz-transition-duration: 0ms;
  transition-duration: 0ms; }

.hint--bounce:before, .hint--bounce:after {
  -webkit-transition: opacity 0.3s ease, visibility 0.3s ease, -webkit-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);
  -moz-transition: opacity 0.3s ease, visibility 0.3s ease, -moz-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);
  transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24); }
/*
 * jQuery UI CSS Framework 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Theming/API
 */

/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
.ui-helper-clearfix { display: inline-block; }
/* required comment for clearfix to work in Opera \*/
* html .ui-helper-clearfix { height:1%; }
.ui-helper-clearfix { display:block; }
/* end clearfix */
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }


/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }


/* Icons
----------------------------------*/

/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }


/* Misc visuals
----------------------------------*/

/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }


/*
 * jQuery UI CSS Framework 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Theming/API
 *
 * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Lucida%20Grande,%20Lucida%20Sans,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=5px&bgColorHeader=5c9ccc&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=55&borderColorHeader=4297d7&fcHeader=ffffff&iconColorHeader=d8e7f3&bgColorContent=fcfdfd&bgTextureContent=06_inset_hard.png&bgImgOpacityContent=100&borderColorContent=a6c9e2&fcContent=222222&iconColorContent=469bdd&bgColorDefault=dfeffc&bgTextureDefault=02_glass.png&bgImgOpacityDefault=85&borderColorDefault=c5dbec&fcDefault=1c425f&iconColorDefault=6da8d5&bgColorHover=d0e5f5&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=79b7e7&fcHover=1d5987&iconColorHover=217bc0&bgColorActive=f5f8f9&bgTextureActive=06_inset_hard.png&bgImgOpacityActive=100&borderColorActive=79b7e7&fcActive=e17009&iconColorActive=f9bd01&bgColorHighlight=fbec88&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=fad42e&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
 */


/* Component containers
----------------------------------*/
.ui-widget { font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; font-size: 0.9em; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #a6c9e2; background: #fcfdfd url(images/ui-bg_inset-hard_100_fcfdfd_1x100.png) 50% bottom repeat-x; color: #222222; }
.ui-widget-content a { color: #222222; }
.ui-widget-header { border: 1px solid #4297d7; background: #5c9ccc url(images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
.ui-widget-header a { color: #ffffff; }

/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #c5dbec; background: #dfeffc url(images/ui-bg_glass_85_dfeffc_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c425f; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c425f; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #79b7e7; background: #d0e5f5 url(images/ui-bg_glass_75_d0e5f5_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1d5987; }
.ui-state-hover a, .ui-state-hover a:hover { color: #1d5987; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #79b7e7; background: #f5f8f9 url(images/ui-bg_inset-hard_100_f5f8f9_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #e17009; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #e17009; text-decoration: none; }
.ui-widget :active { outline: none; }

/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight  {border: 1px solid #fad42e; background: #fbec88 url(images/ui-bg_flat_55_fbec88_40x100.png) 50% 50% repeat-x; color: #363636; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary,  .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }

/* Icons
----------------------------------*/

/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_469bdd_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_469bdd_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_d8e7f3_256x240.png); }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_6da8d5_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_217bc0_256x240.png); }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_f9bd01_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }

/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }


/* Misc visuals
----------------------------------*/

/* Corner radius */
.ui-corner-tl { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; }
.ui-corner-tr { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; }
.ui-corner-bl { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; border-bottom-left-radius: 5px; }
.ui-corner-br { -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; border-bottom-right-radius: 5px; }
.ui-corner-top { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; }
.ui-corner-bottom { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; border-bottom-right-radius: 5px; }
.ui-corner-right {  -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; border-bottom-right-radius: 5px; }
.ui-corner-left { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; border-bottom-left-radius: 5px; }
.ui-corner-all { -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }

/* Overlays */
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
 * jQuery UI Resizable 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Resizable#theming
 */
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;
	/* http://bugs.jqueryui.com/ticket/7233
	 - Resizable: resizable handles fail to work in IE if transparent and content overlaps
	*/
	background-image:url(data:);
}
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
 * jQuery UI Selectable 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Selectable#theming
 */
.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
/*
 * jQuery UI Accordion 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Accordion#theming
 */
/* IE/Win - Fix animation bug - #4615 */
.ui-accordion { width: 100%; }
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
.ui-accordion .ui-accordion-content-active { display: block; }
/*
 * jQuery UI Autocomplete 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Autocomplete#theming
 */
.ui-autocomplete { position: absolute; cursor: default; }	

/* workarounds */
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */

/*
 * jQuery UI Menu 1.8.12
 *
 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Menu#theming
 */
.ui-menu {
	list-style:none;
	padding: 2px;
	margin: 0;
	display:block;
	float: left;
}
.ui-menu .ui-menu {
	margin-top: -3px;
}
.ui-menu .ui-menu-item {
	margin:0;
	padding: 0;
	zoom: 1;
	float: left;
	clear: left;
	width: 100%;
}
.ui-menu .ui-menu-item a {
	text-decoration:none;
	display:block;
	padding:.2em .4em;
	line-height:1.5;
	zoom:1;
}
.ui-menu .ui-menu-item a.ui-state-hover,
.ui-menu .ui-menu-item a.ui-state-active {
	font-weight: normal;
	margin: -1px;
}
/*
 * jQuery UI Button 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Button#theming
 */
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
.ui-button-icons-only { width: 3.4em; } 
button.ui-button-icons-only { width: 3.7em; } 

/*button text element */
.ui-button .ui-button-text { display: block; line-height: 1.4;  }
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }

/*button icon element(s) */
.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }

/*button sets*/
.ui-buttonset { margin-right: 7px; }
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }

/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
/*
 * jQuery UI Dialog 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Dialog#theming
 */
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative;  }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } 
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/*
 * jQuery UI Slider 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Slider#theming
 */
.ui-slider { position: relative; text-align: left; }
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }

.ui-slider-horizontal { height: .8em; }
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
.ui-slider-horizontal .ui-slider-range-max { right: 0; }

.ui-slider-vertical { width: .8em; height: 100px; }
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
 * jQuery UI Tabs 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Tabs#theming
 */
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
/* links in UI tabs, adjust to fit more in tab bar */
.ui-tabs .ui-tabs-nav li a {
    float: left;
    padding: .25em .25em;
    text-decoration: none;
    font-size: 90%;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
.ui-tabs .ui-tabs-hide { display: none !important; }
/*
 * jQuery UI Datepicker 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Datepicker#theming
 */
.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px;  }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker select.ui-datepicker-month, 
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }

/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; }

/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }

/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
    display: none; /*sorry for IE5*/
    display/**/: block; /*sorry for IE5*/
    position: absolute; /*must have*/
    z-index: -1; /*must have*/
    filter: mask(); /*must have*/
    top: -4px; /*must have*/
    left: -4px; /*must have*/
    width: 200px; /*must have*/
    height: 200px; /*must have*/
}/*
 * jQuery UI Progressbar 1.8.12
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Progressbar#theming
 */
.ui-progressbar { height:2em; text-align: left; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
/*!
 * jQuery UI 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI
 */
(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.16",
keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({propAttr:c.fn.prop||c.fn.attr,_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=
this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,
"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":
"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,
outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,
"tabindex"),d=isNaN(b);return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&
a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&
c.ui.isOverAxis(b,e,i)}})}})(jQuery);
;/*!
 * jQuery UI Widget 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Widget
 */
(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)try{b(d).triggerHandler("remove")}catch(e){}k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){try{b(this).triggerHandler("remove")}catch(d){}});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=
function(h){return!!b.data(h,a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):
d;if(e&&d.charAt(0)==="_")return h;e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=
b.extend(true,{},this.options,this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+
"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",
c);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
;/*!
 * jQuery UI Mouse 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Mouse
 *
 * Depends:
 *	jquery.ui.widget.js
 */
(function(b){var d=false;b(document).mouseup(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"&&a.target.nodeName?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=
this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
;/*
 * jQuery UI Position 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Position
 */
(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY,
left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+=
k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-=
m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left=
d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+=
a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b),
g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery);
;/*
 * jQuery UI Draggable 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Draggables
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.mouse.js
 *	jquery.ui.widget.js
 */
(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;if(b.iframeFix)d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;
this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});
this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true},
_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=
false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,
10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||
!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&
a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=
this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),
10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),
10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,
(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!=
"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),
10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+
this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&
!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.left<g[0])e=g[0]+this.offset.click.left;
if(a.pageY-this.offset.click.top<g[1])h=g[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>g[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.top<g[1]||h-this.offset.click.top>g[3])?h:!(h-this.offset.click.top<g[1])?h-b.grid[1]:h+b.grid[1]:h;e=b.grid[0]?this.originalPageX+Math.round((e-this.originalPageX)/
b.grid[0])*b.grid[0]:this.originalPageX;e=g?!(e-this.offset.click.left<g[0]||e-this.offset.click.left>g[2])?e:!(e-this.offset.click.left<g[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<
526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,
c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.16"});d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var h=d.data(this,"sortable");if(h&&!h.options.disabled){c.sortables.push({instance:h,shouldRevert:h.options.revert});
h.refreshPositions();h._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=
false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",true);
this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;
c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&
this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=
a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!=
"x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<
c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-
c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable","snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,
width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,h=b.offset.left,g=h+c.helperProportions.width,n=b.offset.top,o=n+c.helperProportions.height,i=c.snapElements.length-1;i>=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e<h&&h<l+e&&k-e<n&&n<m+e||j-e<h&&h<l+e&&k-e<o&&o<m+e||j-e<g&&g<l+e&&k-e<n&&n<m+e||j-e<g&&g<l+e&&k-e<o&&
o<m+e){if(f.snapMode!="inner"){var p=Math.abs(k-o)<=e,q=Math.abs(m-n)<=e,r=Math.abs(j-g)<=e,s=Math.abs(l-h)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l}).left-c.margins.left}var t=
p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(k-n)<=e;q=Math.abs(m-o)<=e;r=Math.abs(j-h)<=e;s=Math.abs(l-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m-c.helperProportions.height,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[i].snapping&&
(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=p||q||r||s||t}else{c.snapElements[i].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),
10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery);
;/*
 * jQuery UI Droppable 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Droppables
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.mouse.js
 *	jquery.ui.draggable.js
 */
(function(d){d.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"},_create:function(){var a=this.options,b=a.accept;this.isover=0;this.isout=1;this.accept=d.isFunction(b)?b:function(c){return c.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};d.ui.ddmanager.droppables[a.scope]=d.ui.ddmanager.droppables[a.scope]||[];d.ui.ddmanager.droppables[a.scope].push(this);
a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var a=d.ui.ddmanager.droppables[this.options.scope],b=0;b<a.length;b++)a[b]==this&&a.splice(b,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(a,b){if(a=="accept")this.accept=d.isFunction(b)?b:function(c){return c.is(b)};d.Widget.prototype._setOption.apply(this,arguments)},_activate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&
this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass);
this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g=
d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop",
a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.16"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height;
switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>=
i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!=
"none";if(c[f].visible){e=="mousedown"&&c[f]._activate.call(c[f],b);c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight}}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem||
a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},dragStart:function(a,b){a.element.parents(":not(body,html)").bind("scroll.droppable",function(){a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)})},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);
if(c=!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e=d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})},dragStop:function(a,b){a.element.parents(":not(body,html)").unbind("scroll.droppable");
a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)}}})(jQuery);
;/*
 * jQuery UI Resizable 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Resizables
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.mouse.js
 *	jquery.ui.widget.js
 */
(function(e){e.widget("ui.resizable",e.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1E3},_create:function(){var b=this,a=this.options;this.element.addClass("ui-resizable");e.extend(this,{_aspectRatio:!!a.aspectRatio,aspectRatio:a.aspectRatio,originalElement:this.element,
_proportionallyResizeElements:[],_helper:a.helper||a.ghost||a.animate?a.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){/relative/.test(this.element.css("position"))&&e.browser.opera&&this.element.css({position:"relative",top:"auto",left:"auto"});this.element.wrap(e('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),
top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=
this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",
nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d<c.length;d++){var f=e.trim(c[d]),g=e('<div class="ui-resizable-handle '+("ui-resizable-"+f)+'"></div>');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor==
String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection();
this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){if(!a.disabled){e(this).removeClass("ui-resizable-autohide");b._handles.show()}},function(){if(!a.disabled)if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();
var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=
false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"});
this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff=
{width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis];
if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},
_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,
{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateVirtualBoundaries:function(b){var a=this.options,c,d,f;a={minWidth:k(a.minWidth)?a.minWidth:0,maxWidth:k(a.maxWidth)?a.maxWidth:Infinity,minHeight:k(a.minHeight)?a.minHeight:0,maxHeight:k(a.maxHeight)?a.maxHeight:
Infinity};if(this._aspectRatio||b){b=a.minHeight*this.aspectRatio;d=a.minWidth/this.aspectRatio;c=a.maxHeight*this.aspectRatio;f=a.maxWidth/this.aspectRatio;if(b>a.minWidth)a.minWidth=b;if(d>a.minHeight)a.minHeight=d;if(c<a.maxWidth)a.maxWidth=c;if(f<a.maxHeight)a.maxHeight=f}this._vBoundaries=a},_updateCache:function(b){this.offset=this.helper.offset();if(k(b.left))this.position.left=b.left;if(k(b.top))this.position.top=b.top;if(k(b.height))this.size.height=b.height;if(k(b.width))this.size.width=
b.width},_updateRatio:function(b){var a=this.position,c=this.size,d=this.axis;if(k(b.height))b.width=b.height*this.aspectRatio;else if(k(b.width))b.height=b.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(d=="nw"){b.top=a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this._vBoundaries,c=this.axis,d=k(b.width)&&a.maxWidth&&a.maxWidth<b.width,f=k(b.height)&&a.maxHeight&&a.maxHeight<b.height,g=k(b.width)&&a.minWidth&&
a.minWidth>b.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height,l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&l)b.left=i-a.minWidth;if(d&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=
null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a<this._proportionallyResizeElements.length;a++){var c=this._proportionallyResizeElements[a];if(!this.borderDif){var d=[c.css("borderTopWidth"),c.css("borderRightWidth"),c.css("borderBottomWidth"),c.css("borderLeftWidth")],f=[c.css("paddingTop"),c.css("paddingRight"),c.css("paddingBottom"),c.css("paddingLeft")];this.borderDif=e.map(d,function(g,h){g=parseInt(g,10)||
0;h=parseInt(f[h],10)||0;return g+h})}e.browser.msie&&(e(b).is(":hidden")||e(b).parents(":hidden").length)||c.css({height:b.height()-this.borderDif[0]-this.borderDif[2]||0,width:b.width()-this.borderDif[1]-this.borderDif[3]||0})}},_renderProxy:function(){var b=this.options;this.elementOffset=this.element.offset();if(this._helper){this.helper=this.helper||e('<div style="overflow:hidden;"></div>');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+
a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+
c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);
b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.16"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),
10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-
f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var l=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:l.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(l.css("position"))){c._revertToRelativePosition=true;l.css({position:"absolute",top:"auto",left:"auto"})}l.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?
e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=
e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,
step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=
e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;
var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:
a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-
d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,
f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,
display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=
e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=
d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery);
;/*
 * jQuery UI Selectable 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Selectables
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.mouse.js
 *	jquery.ui.widget.js
 */
(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"),
selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("<div class='ui-selectable-helper'></div>")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX,
c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting",
c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d=
this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.right<b||a.top>i||a.bottom<g);else if(d.tolerance=="fit")k=a.left>b&&a.right<h&&a.top>g&&a.bottom<i;if(k){if(a.selected){a.$element.removeClass("ui-selected");a.selected=false}if(a.unselecting){a.$element.removeClass("ui-unselecting");
a.unselecting=false}if(!a.selecting){a.$element.addClass("ui-selecting");a.selecting=true;f._trigger("selecting",c,{selecting:a.element})}}else{if(a.selecting)if(c.metaKey&&a.startselected){a.$element.removeClass("ui-selecting");a.selecting=false;a.$element.addClass("ui-selected");a.selected=true}else{a.$element.removeClass("ui-selecting");a.selecting=false;if(a.startselected){a.$element.addClass("ui-unselecting");a.unselecting=true}f._trigger("unselecting",c,{unselecting:a.element})}if(a.selected)if(!c.metaKey&&
!a.startselected){a.$element.removeClass("ui-selected");a.selected=false;a.$element.addClass("ui-unselecting");a.unselecting=true;f._trigger("unselecting",c,{unselecting:a.element})}}}});return false}},_mouseStop:function(c){var f=this;this.dragged=false;e(".ui-unselecting",this.element[0]).each(function(){var d=e.data(this,"selectable-item");d.$element.removeClass("ui-unselecting");d.unselecting=false;d.startselected=false;f._trigger("unselected",c,{unselected:d.element})});e(".ui-selecting",this.element[0]).each(function(){var d=
e.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected");d.selecting=false;d.selected=true;d.startselected=true;f._trigger("selected",c,{selected:d.element})});this._trigger("stop",c);this.helper.remove();return false}});e.extend(e.ui.selectable,{version:"1.8.16"})})(jQuery);
;/*
 * jQuery UI Sortable 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Sortables
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.mouse.js
 *	jquery.ui.widget.js
 */
(function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable");
this.refresh();this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a===
"disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&
!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,
left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};
this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=
document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);
return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop+b.scrollSpeed;else if(a.pageY-this.overflowOffset.top<
b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop-b.scrollSpeed;if(this.overflowOffset.left+this.scrollParent[0].offsetWidth-a.pageX<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft+b.scrollSpeed;else if(a.pageX-this.overflowOffset.left<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft-b.scrollSpeed}else{if(a.pageY-d(document).scrollTop()<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()-
b.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()+b.scrollSpeed);if(a.pageX-d(document).scrollLeft()<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()-b.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()+b.scrollSpeed)}c!==false&&d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,
a)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(b=this.items.length-1;b>=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],
e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();
c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):
this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,
dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},
toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+j<k&&b+l>g&&b+l<h;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||
this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?j:g<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<h&&i<e+this.helperProportions.height/2&&f-this.helperProportions.height/2<k},_intersectsWithPointer:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left,a.width);b=b&&a;a=this._getDragVerticalDirection();
var c=this._getDragHorizontalDirection();if(!b)return false;return this.floating?c&&c=="right"||a=="down"?2:1:a&&(a=="down"?2:1)},_intersectsWithSides:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top+a.height/2,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left+a.width/2,a.width);var c=this._getDragVerticalDirection(),e=this._getDragHorizontalDirection();return this.floating&&e?e=="right"&&a||e=="left"&&!a:c&&(c=="down"&&b||c=="up"&&!b)},
_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();
if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),
this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(a){this.items=[];this.containers=[this];var b=this.items,c=[[d.isFunction(this.options.items)?this.options.items.call(this.element[0],a,{item:this.currentItem}):d(this.options.items,this.element),
this]],e=this._connectWith();if(e)for(var f=e.length-1;f>=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h<g;h++){i=d(e[h]);i.data("sortable-item",a);b.push({item:i,instance:a,width:0,height:0,left:0,top:0})}}},refreshPositions:function(a){if(this.offsetParent&&
this.helper)this.offset.parent=this._getParentOffset();for(var b=this.items.length-1;b>=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=
this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=
d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||
0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",
a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-
f)<b){b=Math.abs(h-f);e=this.items[g]}}if(e||this.options.dropOnEmpty){this.currentContainer=this.containers[c];e?this._rearrange(a,e,null,true):this._rearrange(a,null,this.containers[c].element,true);this._trigger("change",a,this._uiHash());this.containers[c]._trigger("change",a,this._uiHash(this));this.options.placeholder.update(this.currentContainer,this.placeholder);this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}}},_createHelper:function(a){var b=
this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a,this.currentItem])):b.helper=="clone"?this.currentItem.clone():this.currentItem;a.parents("body").length||d(b.appendTo!="parent"?b.appendTo:this.currentItem[0].parentNode)[0].appendChild(a[0]);if(a[0]==this.currentItem[0])this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")};if(a[0].style.width==
""||b.forceHelperSize)a.width(this.currentItem.width());if(a[0].style.height==""||b.forceHelperSize)a.height(this.currentItem.height());return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=
this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a=
{top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),
10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?
document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)){var b=d(a.containment)[0];a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),
10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(a,b){if(!b)b=
this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&
this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0]))this.offset.relative=this._getRelativeOffset();
var f=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])f=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-
this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;f=this.originalPageX+Math.round((f-this.originalPageX)/b.grid[0])*b.grid[0];f=this.containment?!(f-this.offset.click.left<this.containment[0]||f-this.offset.click.left>this.containment[2])?f:!(f-this.offset.click.left<this.containment[0])?f-b.grid[0]:f+b.grid[0]:f}}return{top:g-
this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())}},_rearrange:function(a,b,c,e){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],
this.direction=="down"?b.item[0]:b.item[0].nextSibling);this.counter=this.counter?++this.counter:1;var f=this,g=this.counter;window.setTimeout(function(){g==f.counter&&f.refreshPositions(!e)},0)},_clear:function(a,b){this.reverting=false;var c=[];!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem);this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var e in this._storedCSS)if(this._storedCSS[e]=="auto"||this._storedCSS[e]=="static")this._storedCSS[e]=
"";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!b&&c.push(function(f){this._trigger("receive",f,this._uiHash(this.fromOutside))});if((this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!b)c.push(function(f){this._trigger("update",f,this._uiHash())});if(!d.ui.contains(this.element[0],this.currentItem[0])){b||c.push(function(f){this._trigger("remove",
f,this._uiHash())});for(e=this.containers.length-1;e>=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,
this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",
a,this._uiHash());for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}return false}b||this._trigger("beforeStop",a,this._uiHash());this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.helper[0]!=this.currentItem[0]&&this.helper.remove();this.helper=null;if(!b){for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}this.fromOutside=false;return true},_trigger:function(){d.Widget.prototype._trigger.apply(this,arguments)===false&&this.cancel()},
_uiHash:function(a){var b=a||this;return{helper:b.helper,placeholder:b.placeholder||d([]),position:b.position,originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,sender:a?a.element:null}}});d.extend(d.ui.sortable,{version:"1.8.16"})})(jQuery);
;/*
 * jQuery UI Accordion 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Accordion
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 */
(function(c){c.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix");
a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion",
function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a=
this.options;if(a.icons){c("<span></span>").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex");
this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons();
b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target);
a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+
c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options;
if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);
if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(),
e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight||
e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false",
"aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.16",
animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/);
f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide",
paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery);
;/*
 * jQuery UI Autocomplete 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Autocomplete
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 */
(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.propAttr("readOnly"))){g=
false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!=
a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)};
this.menu=d("<ul></ul>").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&&
a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");
d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&&
b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source=
this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==false)return this._search(a)},_search:function(a){this.pending++;this.element.addClass("ui-autocomplete-loading");this.source({term:a},this.response)},_response:function(a){if(!this.options.disabled&&a&&a.length){a=this._normalize(a);this._suggest(a);this._trigger("open")}else this.close();
this.pending--;this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing);if(this.menu.element.is(":visible")){this.menu.element.hide();this.menu.deactivate();this._trigger("close",a)}},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(a){if(a.length&&a[0].label&&a[0].value)return a;return d.map(a,function(b){if(typeof b==="string")return{label:b,value:b};return d.extend({label:b.label||
b.value,value:b.value||b.label},b)})},_suggest:function(a){var b=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(b,a);this.menu.deactivate();this.menu.refresh();b.show();this._resizeMenu();b.position(d.extend({of:this.element},this.options.position));this.options.autoFocus&&this.menu.next(new d.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth(),this.element.outerWidth()))},_renderMenu:function(a,b){var g=this;
d.each(b,function(c,f){g._renderItem(a,f)})},_renderItem:function(a,b){return d("<li></li>").data("item.autocomplete",b).append(d("<a></a>").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,
"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery);
(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex",
-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.scrollTop(),c=this.element.height();if(b<0)this.element.scrollTop(g+b);else b>=c&&this.element.scrollTop(g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");
this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b,
this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[d.fn.prop?"prop":"attr"]("scrollHeight")},select:function(e){this._trigger("selected",e,{item:this.active})}})})(jQuery);
;/*
 * jQuery UI Button 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Button
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 */
(function(b){var h,i,j,g,l=function(){var a=b(this).find(":ui-button");setTimeout(function(){a.button("refresh")},1)},k=function(a){var c=a.name,e=a.form,f=b([]);if(c)f=e?b(e).find("[name='"+c+"']"):b("[name='"+c+"']",a.ownerDocument).filter(function(){return!this.form});return f};b.widget("ui.button",{options:{disabled:null,text:true,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",l);if(typeof this.options.disabled!==
"boolean")this.options.disabled=this.element.propAttr("disabled");this._determineButtonType();this.hasTitle=!!this.buttonElement.attr("title");var a=this,c=this.options,e=this.type==="checkbox"||this.type==="radio",f="ui-state-hover"+(!e?" ui-state-active":"");if(c.label===null)c.label=this.buttonElement.html();if(this.element.is(":disabled"))c.disabled=true;this.buttonElement.addClass("ui-button ui-widget ui-state-default ui-corner-all").attr("role","button").bind("mouseenter.button",function(){if(!c.disabled){b(this).addClass("ui-state-hover");
this===h&&b(this).addClass("ui-state-active")}}).bind("mouseleave.button",function(){c.disabled||b(this).removeClass(f)}).bind("click.button",function(d){if(c.disabled){d.preventDefault();d.stopImmediatePropagation()}});this.element.bind("focus.button",function(){a.buttonElement.addClass("ui-state-focus")}).bind("blur.button",function(){a.buttonElement.removeClass("ui-state-focus")});if(e){this.element.bind("change.button",function(){g||a.refresh()});this.buttonElement.bind("mousedown.button",function(d){if(!c.disabled){g=
false;i=d.pageX;j=d.pageY}}).bind("mouseup.button",function(d){if(!c.disabled)if(i!==d.pageX||j!==d.pageY)g=true})}if(this.type==="checkbox")this.buttonElement.bind("click.button",function(){if(c.disabled||g)return false;b(this).toggleClass("ui-state-active");a.buttonElement.attr("aria-pressed",a.element[0].checked)});else if(this.type==="radio")this.buttonElement.bind("click.button",function(){if(c.disabled||g)return false;b(this).addClass("ui-state-active");a.buttonElement.attr("aria-pressed","true");
var d=a.element[0];k(d).not(d).map(function(){return b(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")});else{this.buttonElement.bind("mousedown.button",function(){if(c.disabled)return false;b(this).addClass("ui-state-active");h=this;b(document).one("mouseup",function(){h=null})}).bind("mouseup.button",function(){if(c.disabled)return false;b(this).removeClass("ui-state-active")}).bind("keydown.button",function(d){if(c.disabled)return false;if(d.keyCode==b.ui.keyCode.SPACE||
d.keyCode==b.ui.keyCode.ENTER)b(this).addClass("ui-state-active")}).bind("keyup.button",function(){b(this).removeClass("ui-state-active")});this.buttonElement.is("a")&&this.buttonElement.keyup(function(d){d.keyCode===b.ui.keyCode.SPACE&&b(this).click()})}this._setOption("disabled",c.disabled);this._resetButton()},_determineButtonType:function(){this.type=this.element.is(":checkbox")?"checkbox":this.element.is(":radio")?"radio":this.element.is("input")?"input":"button";if(this.type==="checkbox"||this.type===
"radio"){var a=this.element.parents().filter(":last"),c="label[for='"+this.element.attr("id")+"']";this.buttonElement=a.find(c);if(!this.buttonElement.length){a=a.length?a.siblings():this.element.siblings();this.buttonElement=a.filter(c);if(!this.buttonElement.length)this.buttonElement=a.find(c)}this.element.addClass("ui-helper-hidden-accessible");(a=this.element.is(":checked"))&&this.buttonElement.addClass("ui-state-active");this.buttonElement.attr("aria-pressed",a)}else this.buttonElement=this.element},
widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible");this.buttonElement.removeClass("ui-button ui-widget ui-state-default ui-corner-all ui-state-hover ui-state-active  ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only").removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html());this.hasTitle||this.buttonElement.removeAttr("title");
b.Widget.prototype.destroy.call(this)},_setOption:function(a,c){b.Widget.prototype._setOption.apply(this,arguments);if(a==="disabled")c?this.element.propAttr("disabled",true):this.element.propAttr("disabled",false);else this._resetButton()},refresh:function(){var a=this.element.is(":disabled");a!==this.options.disabled&&this._setOption("disabled",a);if(this.type==="radio")k(this.element[0]).each(function(){b(this).is(":checked")?b(this).button("widget").addClass("ui-state-active").attr("aria-pressed",
"true"):b(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")});else if(this.type==="checkbox")this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false")},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var a=this.buttonElement.removeClass("ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only"),
c=b("<span></span>").addClass("ui-button-text").html(this.options.label).appendTo(a.empty()).text(),e=this.options.icons,f=e.primary&&e.secondary,d=[];if(e.primary||e.secondary){if(this.options.text)d.push("ui-button-text-icon"+(f?"s":e.primary?"-primary":"-secondary"));e.primary&&a.prepend("<span class='ui-button-icon-primary ui-icon "+e.primary+"'></span>");e.secondary&&a.append("<span class='ui-button-icon-secondary ui-icon "+e.secondary+"'></span>");if(!this.options.text){d.push(f?"ui-button-icons-only":
"ui-button-icon-only");this.hasTitle||a.attr("title",c)}}else d.push("ui-button-text-only");a.addClass(d.join(" "))}}});b.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(a,c){a==="disabled"&&this.buttons.button("option",a,c);b.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var a=this.element.css("direction")===
"ltr";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(a?"ui-corner-left":"ui-corner-right").end().filter(":last").addClass(a?"ui-corner-right":"ui-corner-left").end().end()},destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");
b.Widget.prototype.destroy.call(this)}})})(jQuery);
;/*
 * jQuery UI Dialog 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Dialog
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *  jquery.ui.button.js
 *	jquery.ui.draggable.js
 *	jquery.ui.mouse.js
 *	jquery.ui.position.js
 *	jquery.ui.resizable.js
 */
(function(c,l){var m={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},n={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true},o=c.attrFn||{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true,click:true};c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,
position:{my:"center",at:"center",collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||"&#160;",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("<div></div>")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+
b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&!i.isDefaultPrevented()&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),
h=c('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("<span></span>")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("<span></span>").addClass("ui-dialog-title").attr("id",
e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");
a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d,e;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!==
b.uiDialog[0]){e=c(this).css("z-index");isNaN(e)||(d=Math.max(d,e))}});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()};c.ui.dialog.maxZ+=1;
d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target===
f[0]&&e.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false,e=c("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,
function(){return!(d=true)});if(d){c.each(a,function(f,h){h=c.isFunction(h)?{click:h,text:f}:h;var i=c('<button type="button"></button>').click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.each(h,function(j,k){if(j!=="click")j in o?i[j](k):i.attr(j,k)});c.fn.button&&i.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",
handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition,
originalSize:f.originalSize,position:f.position,size:f.size}}a=a===l?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize",
f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop",f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0],e;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):
[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,f){if(+b[g]===b[g]){d[g]=b[g];b[g]=f}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(e=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(c.extend({of:window},a));e||this.uiDialog.hide()},_setOptions:function(a){var b=this,d={},e=false;c.each(a,function(g,f){b._setOption(g,f);
if(g in m)e=true;if(g in n)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"):
e.removeClass("ui-dialog-disabled");break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||"&#160;"));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a=
this.options,b,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height-
b,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.16",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),
create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()<c.ui.dialog.overlay.maxZ)return false})},1);c(document).bind("keydown.dialog-overlay",function(d){if(a.options.closeOnEscape&&!d.isDefaultPrevented()&&d.keyCode&&d.keyCode===c.ui.keyCode.ESCAPE){a.close(d);d.preventDefault()}});c(window).bind("resize.dialog-overlay",c.ui.dialog.overlay.resize)}var b=(this.oldInstances.pop()||
c("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&&
c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a<b?c(window).height()+"px":a+"px"}else return c(document).height()+"px"},width:function(){var a,b;if(c.browser.msie){a=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth);b=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return a<b?c(window).width()+"px":a+"px"}else return c(document).width()+
"px"},resize:function(){var a=c([]);c.each(c.ui.dialog.overlay.instances,function(){a=a.add(this)});a.css({width:0,height:0}).css({width:c.ui.dialog.overlay.width(),height:c.ui.dialog.overlay.height()})}});c.extend(c.ui.dialog.overlay.prototype,{destroy:function(){c.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);
;/*
 * jQuery UI Slider 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Slider
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.mouse.js
 *	jquery.ui.widget.js
 */
(function(d){d.widget("ui.slider",d.ui.mouse,{widgetEventPrefix:"slide",options:{animate:false,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null},_create:function(){var a=this,b=this.options,c=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f=b.values&&b.values.length||1,e=[];this._mouseSliding=this._keySliding=false;this._animateOff=true;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+
this.orientation+" ui-widget ui-widget-content ui-corner-all"+(b.disabled?" ui-slider-disabled ui-disabled":""));this.range=d([]);if(b.range){if(b.range===true){if(!b.values)b.values=[this._valueMin(),this._valueMin()];if(b.values.length&&b.values.length!==2)b.values=[b.values[0],b.values[0]]}this.range=d("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(b.range==="min"||b.range==="max"?" ui-slider-range-"+b.range:""))}for(var j=c.length;j<f;j+=1)e.push("<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>");
this.handles=c.add(d(e.join("")).appendTo(a.element));this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(g){g.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur();else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(g){d(this).data("index.ui-slider-handle",
g)});this.handles.keydown(function(g){var k=true,l=d(this).data("index.ui-slider-handle"),i,h,m;if(!a.options.disabled){switch(g.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:k=false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");i=a._start(g,l);if(i===false)return}break}m=a.options.step;i=a.options.values&&a.options.values.length?
(h=a.values(l)):(h=a.value());switch(g.keyCode){case d.ui.keyCode.HOME:h=a._valueMin();break;case d.ui.keyCode.END:h=a._valueMax();break;case d.ui.keyCode.PAGE_UP:h=a._trimAlignValue(i+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=a._trimAlignValue(i-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(i===a._valueMax())return;h=a._trimAlignValue(i+m);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(i===a._valueMin())return;h=a._trimAlignValue(i-
m);break}a._slide(g,l,h);return k}}).keyup(function(g){var k=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(g,k);a._change(g,k);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy();
return this},_mouseCapture:function(a){var b=this.options,c,f,e,j,g;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:a.pageX,y:a.pageY});f=this._valueMax()-this._valueMin()+1;j=this;this.handles.each(function(k){var l=Math.abs(c-j.values(k));if(f>l){f=l;e=d(this);g=k}});if(b.range===true&&this.values(1)===b.min){g+=1;e=d(this.handles[g])}if(this._start(a,g)===false)return false;
this._mouseSliding=true;j._handleIndex=g;e.addClass("ui-state-active").focus();b=e.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-e.width()/2,top:a.pageY-b.top-e.height()/2-(parseInt(e.css("borderTopWidth"),10)||0)-(parseInt(e.css("borderBottomWidth"),10)||0)+(parseInt(e.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(a,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(a){var b=
this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b;if(this.orientation==="horizontal"){b=
this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);
c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var f;if(this.options.values&&this.options.values.length){f=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>f||b===1&&c<f))c=f;if(c!==this.values(b)){f=this.values();f[b]=c;a=this._trigger("slide",a,{handle:this.handles[b],value:c,values:f});this.values(b?0:1);a!==false&&this.values(b,c,true)}}else if(c!==this.value()){a=this._trigger("slide",a,{handle:this.handles[b],value:c});
a!==false&&this.value(c)}},_stop:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);c.values=this.values()}this._trigger("stop",a,c)},_change:function(a,b){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);c.values=this.values()}this._trigger("change",a,c)}},value:function(a){if(arguments.length){this.options.value=
this._trimAlignValue(a);this._refreshValue();this._change(null,0)}else return this._value()},values:function(a,b){var c,f,e;if(arguments.length>1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}else if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;f=arguments[0];for(e=0;e<c.length;e+=1){c[e]=this._trimAlignValue(f[e]);this._change(null,e)}this._refreshValue()}else return this.options.values&&this.options.values.length?this._values(a):
this.value();else return this._values()},_setOption:function(a,b){var c,f=0;if(d.isArray(this.options.values))f=this.options.values.length;d.Widget.prototype._setOption.apply(this,arguments);switch(a){case "disabled":if(b){this.handles.filter(".ui-state-focus").blur();this.handles.removeClass("ui-state-hover");this.handles.propAttr("disabled",true);this.element.addClass("ui-disabled")}else{this.handles.propAttr("disabled",false);this.element.removeClass("ui-disabled")}break;case "orientation":this._detectOrientation();
this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue();break;case "value":this._animateOff=true;this._refreshValue();this._change(null,0);this._animateOff=false;break;case "values":this._animateOff=true;this._refreshValue();for(c=0;c<f;c+=1)this._change(null,c);this._animateOff=false;break}},_value:function(){var a=this.options.value;return a=this._trimAlignValue(a)},_values:function(a){var b,c;if(arguments.length){b=this.options.values[a];
return b=this._trimAlignValue(b)}else{b=this.options.values.slice();for(c=0;c<b.length;c+=1)b[c]=this._trimAlignValue(b[c]);return b}},_trimAlignValue:function(a){if(a<=this._valueMin())return this._valueMin();if(a>=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a=
this.options.range,b=this.options,c=this,f=!this._animateOff?b.animate:false,e,j={},g,k,l,i;if(this.options.values&&this.options.values.length)this.handles.each(function(h){e=(c.values(h)-c._valueMin())/(c._valueMax()-c._valueMin())*100;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";d(this).stop(1,1)[f?"animate":"css"](j,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(h===0)c.range.stop(1,1)[f?"animate":"css"]({left:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({width:e-
g+"%"},{queue:false,duration:b.animate})}else{if(h===0)c.range.stop(1,1)[f?"animate":"css"]({bottom:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({height:e-g+"%"},{queue:false,duration:b.animate})}g=e});else{k=this.value();l=this._valueMin();i=this._valueMax();e=i!==l?(k-l)/(i-l)*100:0;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[f?"animate":"css"](j,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[f?"animate":"css"]({width:e+"%"},
b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[f?"animate":"css"]({width:100-e+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[f?"animate":"css"]({height:e+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[f?"animate":"css"]({height:100-e+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.16"})})(jQuery);
;/*
 * jQuery UI Tabs 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Tabs
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 */
(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&&
e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=
d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]||
(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all");
this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected=
this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active");
if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));
this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+
g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",
function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")};
this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected=
-1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";
d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=
d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b,
e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]);
j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove();
if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1<this.anchors.length?1:-1));e.disabled=d.map(d.grep(e.disabled,function(h){return h!=b}),function(h){return h>=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null,
this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this},
load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c,
"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},
url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.16"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k<a.anchors.length?k:0)},b);j&&j.stopPropagation()});e=a._unrotate||(a._unrotate=!e?function(j){j.clientX&&
a.rotate(null)}:function(){t=c.selected;h()});if(b){this.element.bind("tabsshow",h);this.anchors.bind(c.event+".tabs",e);h()}else{clearTimeout(a.rotation);this.element.unbind("tabsshow",h);this.anchors.unbind(c.event+".tabs",e);delete this._rotate;delete this._unrotate}return this}})})(jQuery);
;/*
 * jQuery UI Datepicker 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Datepicker
 *
 * Depends:
 *	jquery.ui.core.js
 */
(function(d,C){function M(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass=
"ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su",
"Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",
minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false,disabled:false};d.extend(this._defaults,this.regional[""]);this.dpDiv=N(d('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}function N(a){return a.bind("mouseout",
function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");b.length&&b.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");if(!(d.datepicker._isDisabledDatepicker(J.inline?a.parent()[0]:J.input[0])||!b.length)){b.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
b.addClass("ui-state-hover");b.hasClass("ui-datepicker-prev")&&b.addClass("ui-datepicker-prev-hover");b.hasClass("ui-datepicker-next")&&b.addClass("ui-datepicker-next-hover")}})}function H(a,b){d.extend(a,b);for(var c in b)if(b[c]==null||b[c]==C)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.16"}});var B=(new Date).getTime(),J;d.extend(M.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},
setDefaults:function(a){H(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,
"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:N(d('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}},_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",
function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b);b.settings.disabled&&this._disableDatepicker(a)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&b.append.remove();if(c){b.append=d('<span class="'+this._appendClass+'">'+c+"</span>");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c==
"focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("<img/>").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('<button type="button"></button>').addClass(this._triggerClass).html(f==""?c:d("<img/>").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():
d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;g<f.length;g++)if(f[g].length>h){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,
b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),true);this._updateDatepicker(b);this._updateAlternate(b);b.settings.disabled&&this._disableDatepicker(a);b.dpDiv.css("display","block")}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=
1;this._dialogInput=d('<input type="text" id="'+("dp"+this.uuid)+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}H(a.settings,e||{});b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/
2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=
d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=
a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().removeClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a,
"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().addClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==
a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return true;return false},_getInst:function(a){try{return d.data(a,"datepicker")}catch(b){throw"Missing instance data for this datepicker";}},_optionDatepicker:function(a,b,c){var e=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?d.extend({},d.datepicker._defaults):e?b=="all"?
d.extend({},e.settings):this._get(e,b):null;var f=b||{};if(typeof b=="string"){f={};f[b]=c}if(e){this._curInst==e&&this._hideDatepicker();var h=this._getDateDatepicker(a,true),i=this._getMinMaxDate(e,"min"),g=this._getMinMaxDate(e,"max");H(e.settings,f);if(i!==null&&f.dateFormat!==C&&f.minDate===C)e.settings.minDate=this._formatDate(e,i);if(g!==null&&f.dateFormat!==C&&f.maxDate===C)e.settings.maxDate=this._formatDate(e,g);this._attachments(d(a),e);this._autoSize(e);this._setDate(e,h);this._updateAlternate(e);
this._updateDatepicker(e)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){(a=this._getInst(a))&&this._updateDatepicker(a)},_setDateDatepicker:function(a,b){if(a=this._getInst(a)){this._setDate(a,b);this._updateDatepicker(a);this._updateAlternate(a)}},_getDateDatepicker:function(a,b){(a=this._getInst(a))&&!a.inline&&this._setDateFromField(a,b);return a?this._getDate(a):null},_doKeyDown:function(a){var b=d.datepicker._getInst(a.target),c=true,e=b.dpDiv.is(".ui-datepicker-rtl");
b._keyEvent=true;if(d.datepicker._datepickerShowing)switch(a.keyCode){case 9:d.datepicker._hideDatepicker();c=false;break;case 13:c=d("td."+d.datepicker._dayOverClass+":not(."+d.datepicker._currentClass+")",b.dpDiv);c[0]&&d.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,c[0]);if(a=d.datepicker._get(b,"onSelect")){c=d.datepicker._formatDate(b);a.apply(b.input?b.input[0]:null,[c,b])}else d.datepicker._hideDatepicker();return false;case 27:d.datepicker._hideDatepicker();break;case 33:d.datepicker._adjustDate(a.target,
a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 34:d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 35:if(a.ctrlKey||a.metaKey)d.datepicker._clearDate(a.target);c=a.ctrlKey||a.metaKey;break;case 36:if(a.ctrlKey||a.metaKey)d.datepicker._gotoToday(a.target);c=a.ctrlKey||a.metaKey;break;case 37:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?+1:-1,"D");c=
a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 38:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,-7,"D");c=a.ctrlKey||a.metaKey;break;case 39:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?-1:+1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,
"stepMonths"),"M");break;case 40:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,+7,"D");c=a.ctrlKey||a.metaKey;break;default:c=false}else if(a.keyCode==36&&a.ctrlKey)d.datepicker._showDatepicker(this);else c=false;if(c){a.preventDefault();a.stopPropagation()}},_doKeyPress:function(a){var b=d.datepicker._getInst(a.target);if(d.datepicker._get(b,"constrainInput")){b=d.datepicker._possibleChars(d.datepicker._get(b,"dateFormat"));var c=String.fromCharCode(a.charCode==C?a.keyCode:a.charCode);
return a.ctrlKey||a.metaKey||c<" "||!b||b.indexOf(c)>-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",
a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);if(d.datepicker._curInst&&d.datepicker._curInst!=b){d.datepicker._datepickerShowing&&d.datepicker._triggerOnClose(d.datepicker._curInst);d.datepicker._curInst.dpDiv.stop(true,true)}var c=d.datepicker._get(b,"beforeShow");c=c?c.apply(a,[a,b]):{};if(c!==false){H(b.settings,c);b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value=
"";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);
c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.datepicker._datepickerShowing=
true;d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}}},_updateDatepicker:function(a){this.maxRows=4;var b=d.datepicker._getBorders(a.dpDiv);J=a;a.dpDiv.empty().append(this._generateHTML(a));var c=a.dpDiv.find("iframe.ui-datepicker-cover");c.length&&c.css({left:-b[0],top:-b[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});
a.dpDiv.find("."+this._dayOverClass+" a").mouseover();b=this._getNumberOfMonths(a);c=b[1];a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");c>1&&a.dpDiv.addClass("ui-datepicker-multi-"+c).css("width",17*c+"em");a.dpDiv[(b[0]!=1||b[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&
!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var e=a.yearshtml;setTimeout(function(){e===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);e=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),
h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=
this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_triggerOnClose:function(a){var b=this._get(a,"onClose");if(b)b.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a])},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);
this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();d.datepicker._triggerOnClose(b);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},
_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):
0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e["selected"+(c=="M"?
"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);
this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");
if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?
b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=A+1<a.length&&a.charAt(A+1)==p)&&A++;return p},m=function(p){var D=
o(p);p=new RegExp("^\\d{1,"+(p=="@"?14:p=="!"?20:p=="y"&&D?4:p=="o"?3:2)+"}");p=b.substring(q).match(p);if(!p)throw"Missing number at position "+q;q+=p[0].length;return parseInt(p[0],10)},n=function(p,D,K){p=d.map(o(p)?K:D,function(w,x){return[[x,w]]}).sort(function(w,x){return-(w[1].length-x[1].length)});var E=-1;d.each(p,function(w,x){w=x[1];if(b.substr(q,w.length).toLowerCase()==w.toLowerCase()){E=x[0];q+=w.length;return false}});if(E!=-1)return E+1;else throw"Unknown name at position "+q;},s=
function(){if(b.charAt(q)!=a.charAt(A))throw"Unexpected literal at position "+q;q++},q=0,A=0;A<a.length;A++)if(k)if(a.charAt(A)=="'"&&!o("'"))k=false;else s();else switch(a.charAt(A)){case "d":l=m("d");break;case "D":n("D",f,h);break;case "o":u=m("o");break;case "m":j=m("m");break;case "M":j=n("M",i,g);break;case "y":c=m("y");break;case "@":var v=new Date(m("@"));c=v.getFullYear();j=v.getMonth()+1;l=v.getDate();break;case "!":v=new Date((m("!")-this._ticksTo1970)/1E4);c=v.getFullYear();j=v.getMonth()+
1;l=v.getDate();break;case "'":if(o("'"))s();else k=true;break;default:s()}if(q<b.length)throw"Extra/unparsed characters found in date: "+b.substring(q);if(c==-1)c=(new Date).getFullYear();else if(c<100)c+=(new Date).getFullYear()-(new Date).getFullYear()%100+(c<=e?0:-100);if(u>-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,j-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=j||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",
COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:
null)||this._defaults.monthNames;var i=function(o){(o=k+1<a.length&&a.charAt(k+1)==o)&&k++;return o},g=function(o,m,n){m=""+m;if(i(o))for(;m.length<n;)m="0"+m;return m},j=function(o,m,n,s){return i(o)?s[m]:n[m]},l="",u=false;if(b)for(var k=0;k<a.length;k++)if(u)if(a.charAt(k)=="'"&&!i("'"))u=false;else l+=a.charAt(k);else switch(a.charAt(k)){case "d":l+=g("d",b.getDate(),2);break;case "D":l+=j("D",b.getDay(),e,f);break;case "o":l+=g("o",Math.round(((new Date(b.getFullYear(),b.getMonth(),b.getDate())).getTime()-
(new Date(b.getFullYear(),0,0)).getTime())/864E5),3);break;case "m":l+=g("m",b.getMonth()+1,2);break;case "M":l+=j("M",b.getMonth(),h,c);break;case "y":l+=i("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case "@":l+=b.getTime();break;case "!":l+=b.getTime()*1E4+this._ticksTo1970;break;case "'":if(i("'"))l+="'";else u=true;break;default:l+=a.charAt(k)}return l},_possibleChars:function(a){for(var b="",c=false,e=function(h){(h=f+1<a.length&&a.charAt(f+1)==h)&&f++;return h},f=
0;f<a.length;f++)if(c)if(a.charAt(f)=="'"&&!e("'"))c=false;else b+=a.charAt(f);else switch(a.charAt(f)){case "d":case "m":case "y":case "@":b+="0123456789";break;case "D":case "M":return null;case "'":if(e("'"))b+="'";else c=true;break;default:b+=a.charAt(f)}return b},_get:function(a,b){return a.settings[b]!==C?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),e=a.lastVal=a.input?a.input.val():null,f,h;f=h=this._getDefaultDate(a);
var i=this._getFormatConfig(a);try{f=this.parseDate(c,e,i)||h}catch(g){this.log(g);e=b?"":e}a.selectedDay=f.getDate();a.drawMonth=a.selectedMonth=f.getMonth();a.drawYear=a.selectedYear=f.getFullYear();a.currentDay=e?f.getDate():0;a.currentMonth=e?f.getMonth():0;a.currentYear=e?f.getFullYear():0;this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var e=function(h){var i=new Date;
i.setDate(i.getDate()+h);return i},f=function(h){try{return d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),h,d.datepicker._getFormatConfig(a))}catch(i){}var g=(h.toLowerCase().match(/^c/)?d.datepicker._getDate(a):null)||new Date,j=g.getFullYear(),l=g.getMonth();g=g.getDate();for(var u=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,k=u.exec(h);k;){switch(k[2]||"d"){case "d":case "D":g+=parseInt(k[1],10);break;case "w":case "W":g+=parseInt(k[1],10)*7;break;case "m":case "M":l+=parseInt(k[1],10);g=
Math.min(g,d.datepicker._getDaysInMonth(j,l));break;case "y":case "Y":j+=parseInt(k[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(j,l));break}k=u.exec(h)}return new Date(j,l,g)};if(b=(b=b==null||b===""?c:typeof b=="string"?f(b):typeof b=="number"?isNaN(b)?c:e(b):new Date(b.getTime()))&&b.toString()=="Invalid Date"?c:b){b.setHours(0);b.setMinutes(0);b.setSeconds(0);b.setMilliseconds(0)}return this._daylightSavingAdjust(b)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>
12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&
a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?
new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&n<k?k:n;this._daylightSavingAdjust(new Date(m,g,1))>n;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a));
n=this._canAdjustMonth(a,-1,m,g)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+B+".datepicker._adjustDate('#"+a.id+"', -"+j+", 'M');\" title=\""+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>":f?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>";var s=this._get(a,"nextText");s=!h?s:this.formatDate(s,this._daylightSavingAdjust(new Date(m,
g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+B+".datepicker._adjustDate('#"+a.id+"', +"+j+", 'M');\" title=\""+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>":f?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>";j=this._get(a,"currentText");s=this._get(a,"gotoCurrent")&&
a.currentDay?u:b;j=!h?j:this.formatDate(j,s,this._getFormatConfig(a));h=!a.inline?'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+B+'.datepicker._hideDatepicker();">'+this._get(a,"closeText")+"</button>":"";e=e?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?h:"")+(this._isInRange(a,s)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+
B+".datepicker._gotoToday('#"+a.id+"');\">"+j+"</button>":"")+(c?"":h)+"</div>":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");s=this._get(a,"dayNames");this._get(a,"dayNamesShort");var q=this._get(a,"dayNamesMin"),A=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),D=this._get(a,"showOtherMonths"),K=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var E=this._getDefaultDate(a),w="",x=0;x<i[0];x++){var O=
"";this.maxRows=4;for(var G=0;G<i[1];G++){var P=this._daylightSavingAdjust(new Date(m,g,a.selectedDay)),t=" ui-corner-all",y="";if(l){y+='<div class="ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:y+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:y+=" ui-datepicker-group-middle";t="";break}y+='">'}y+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+t+'">'+(/all|left/.test(t)&&
x==0?c?f:n:"")+(/all|right/.test(t)&&x==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,x>0||G>0,A,v)+'</div><table class="ui-datepicker-calendar"><thead><tr>';var z=j?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(t=0;t<7;t++){var r=(t+h)%7;z+="<th"+((t+h+6)%7>=5?' class="ui-datepicker-week-end"':"")+'><span title="'+s[r]+'">'+q[r]+"</span></th>"}y+=z+"</tr></thead><tbody>";z=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,
z);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;z=Math.ceil((t+z)/7);this.maxRows=z=l?this.maxRows>z?this.maxRows:z:z;r=this._daylightSavingAdjust(new Date(m,g,1-t));for(var Q=0;Q<z;Q++){y+="<tr>";var R=!j?"":'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(r)+"</td>";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[r]):[true,""],F=r.getMonth()!=g,L=F&&!K||!I[0]||k&&r<k||o&&r>o;R+='<td class="'+((t+h+6)%7>=5?" ui-datepicker-week-end":"")+(F?" ui-datepicker-other-month":"")+(r.getTime()==
P.getTime()&&g==a.selectedMonth&&a._keyEvent||E.getTime()==r.getTime()&&E.getTime()==P.getTime()?" "+this._dayOverClass:"")+(L?" "+this._unselectableClass+" ui-state-disabled":"")+(F&&!D?"":" "+I[1]+(r.getTime()==u.getTime()?" "+this._currentClass:"")+(r.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!F||D)&&I[2]?' title="'+I[2]+'"':"")+(L?"":' onclick="DP_jQuery_'+B+".datepicker._selectDay('#"+a.id+"',"+r.getMonth()+","+r.getFullYear()+', this);return false;"')+">"+(F&&!D?"&#xa0;":L?'<span class="ui-state-default">'+
r.getDate()+"</span>":'<a class="ui-state-default'+(r.getTime()==b.getTime()?" ui-state-highlight":"")+(r.getTime()==u.getTime()?" ui-state-active":"")+(F?" ui-priority-secondary":"")+'" href="#">'+r.getDate()+"</a>")+"</td>";r.setDate(r.getDate()+1);r=this._daylightSavingAdjust(r)}y+=R+"</tr>"}g++;if(g>11){g=0;m++}y+="</tbody></table>"+(l?"</div>"+(i[0]>0&&G==i[1]-1?'<div class="ui-datepicker-row-break"></div>':""):"");O+=y}w+=O}w+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':
"");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='<div class="ui-datepicker-title">',o="";if(h||!j)o+='<span class="ui-datepicker-month">'+i[b]+"</span>";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+B+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" >";for(var n=0;n<12;n++)if((!i||n>=e.getMonth())&&
(!m||n<=f.getMonth()))o+='<option value="'+n+'"'+(n==b?' selected="selected"':"")+">"+g[n]+"</option>";o+="</select>"}u||(k+=o+(h||!(j&&l)?"&#xa0;":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+='<span class="ui-datepicker-year">'+c+"</span>";else{g=this._get(a,"yearRange").split(":");var s=(new Date).getFullYear();i=function(q){q=q.match(/c[+-].*/)?c+parseInt(q.substring(1),10):q.match(/[+-].*/)?s+parseInt(q,10):parseInt(q,10);return isNaN(q)?s:q};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,
e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+B+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" >";b<=g;b++)a.yearshtml+='<option value="'+b+'"'+(b==c?' selected="selected"':"")+">"+b+"</option>";a.yearshtml+="</select>";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?"&#xa0;":"")+o;k+="</div>";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c=="Y"?b:0),f=a.drawMonth+
(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&b<c?c:b;return b=a&&b>a?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");if(b)b.apply(a.input?
a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);c=this._daylightSavingAdjust(new Date(c,
e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,
"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=function(a){if(!this.length)return this;
if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));return this.each(function(){typeof a==
"string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.16";window["DP_jQuery_"+B]=d})(jQuery);
;/*
 * jQuery UI Progressbar 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Progressbar
 *
 * Depends:
 *   jquery.ui.core.js
 *   jquery.ui.widget.js
 */
(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow");
this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*
this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.16"})})(jQuery);
;/*
 * jQuery UI Effects 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/
 */
jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1],
16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,
a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d=
a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor",
"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,
0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,
211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,
d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})};
f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,
[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.16",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c,a){var b;switch(c[0]){case "top":b=
0;break;case "middle":b=0.5;break;case "bottom":b=1;break;default:b=c[0]/a.height}switch(c[1]){case "left":c=0;break;case "center":c=0.5;break;case "right":c=1;break;default:c=c[1]/a.width}return{x:c,y:b}},createWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent();var a={width:c.outerWidth(true),height:c.outerHeight(true),"float":c.css("float")},b=f("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),
d=document.activeElement;c.wrap(b);if(c[0]===d||f.contains(c[0],d))f(d).focus();b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(e,g){a[g]=c.css(g);if(isNaN(parseInt(a[g],10)))a[g]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){var a,b=document.activeElement;
if(c.parent().is(".ui-effects-wrapper")){a=c.parent().replaceWith(c);if(c[0]===b||f.contains(c[0],b))f(b).focus();return a}return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});
return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,
arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/
2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,
d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,
a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,
d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g))+b},easeOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*a)*Math.sin((a*e-c)*2*Math.PI/g)+d+b},easeInOutElastic:function(c,a,b,d,e){c=1.70158;var g=
0,h=d;if(a==0)return b;if((a/=e/2)==2)return b+d;g||(g=e*0.3*1.5);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);if(a<1)return-0.5*h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)+b;return h*Math.pow(2,-10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)*0.5+d+b},easeInBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*(a/=e)*a*((g+1)*a-g)+b},easeOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*((a=a/e-1)*a*((g+1)*a+g)+1)+b},easeInOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;
if((a/=e/2)<1)return d/2*a*a*(((g*=1.525)+1)*a-g)+b;return d/2*((a-=2)*a*(((g*=1.525)+1)*a+g)+2)+b},easeInBounce:function(c,a,b,d,e){return d-f.easing.easeOutBounce(c,e-a,0,d,e)+b},easeOutBounce:function(c,a,b,d,e){return(a/=e)<1/2.75?d*7.5625*a*a+b:a<2/2.75?d*(7.5625*(a-=1.5/2.75)*a+0.75)+b:a<2.5/2.75?d*(7.5625*(a-=2.25/2.75)*a+0.9375)+b:d*(7.5625*(a-=2.625/2.75)*a+0.984375)+b},easeInOutBounce:function(c,a,b,d,e){if(a<e/2)return f.easing.easeInBounce(c,a*2,0,d,e)*0.5+b;return f.easing.easeOutBounce(c,
a*2-e,0,d,e)*0.5+d*0.5+b}})}(jQuery);
;/*
 * jQuery UI Effects Blind 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Blind
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(b){b.effects.blind=function(c){return this.queue(function(){var a=b(this),g=["position","top","bottom","left","right"],f=b.effects.setMode(a,c.options.mode||"hide"),d=c.options.direction||"vertical";b.effects.save(a,g);a.show();var e=b.effects.createWrapper(a).css({overflow:"hidden"}),h=d=="vertical"?"height":"width";d=d=="vertical"?e.height():e.width();f=="show"&&e.css(h,0);var i={};i[h]=f=="show"?d:0;e.animate(i,c.duration,c.options.easing,function(){f=="hide"&&a.hide();b.effects.restore(a,
g);b.effects.removeWrapper(a);c.callback&&c.callback.apply(a[0],arguments);a.dequeue()})})}})(jQuery);
;/*
 * jQuery UI Effects Bounce 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Bounce
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(e){e.effects.bounce=function(b){return this.queue(function(){var a=e(this),l=["position","top","bottom","left","right"],h=e.effects.setMode(a,b.options.mode||"effect"),d=b.options.direction||"up",c=b.options.distance||20,m=b.options.times||5,i=b.duration||250;/show|hide/.test(h)&&l.push("opacity");e.effects.save(a,l);a.show();e.effects.createWrapper(a);var f=d=="up"||d=="down"?"top":"left";d=d=="up"||d=="left"?"pos":"neg";c=b.options.distance||(f=="top"?a.outerHeight({margin:true})/3:a.outerWidth({margin:true})/
3);if(h=="show")a.css("opacity",0).css(f,d=="pos"?-c:c);if(h=="hide")c/=m*2;h!="hide"&&m--;if(h=="show"){var g={opacity:1};g[f]=(d=="pos"?"+=":"-=")+c;a.animate(g,i/2,b.options.easing);c/=2;m--}for(g=0;g<m;g++){var j={},k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing);c=h=="hide"?c*2:c/2}if(h=="hide"){g={opacity:0};g[f]=(d=="pos"?"-=":"+=")+c;a.animate(g,i/2,b.options.easing,function(){a.hide();e.effects.restore(a,l);e.effects.removeWrapper(a);
b.callback&&b.callback.apply(this,arguments)})}else{j={};k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing,function(){e.effects.restore(a,l);e.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments)})}a.queue("fx",function(){a.dequeue()});a.dequeue()})}})(jQuery);
;/*
 * jQuery UI Effects Clip 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Clip
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(b){b.effects.clip=function(e){return this.queue(function(){var a=b(this),i=["position","top","bottom","left","right","height","width"],f=b.effects.setMode(a,e.options.mode||"hide"),c=e.options.direction||"vertical";b.effects.save(a,i);a.show();var d=b.effects.createWrapper(a).css({overflow:"hidden"});d=a[0].tagName=="IMG"?d:a;var g={size:c=="vertical"?"height":"width",position:c=="vertical"?"top":"left"};c=c=="vertical"?d.height():d.width();if(f=="show"){d.css(g.size,0);d.css(g.position,
c/2)}var h={};h[g.size]=f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h,{queue:false,duration:e.duration,easing:e.options.easing,complete:function(){f=="hide"&&a.hide();b.effects.restore(a,i);b.effects.removeWrapper(a);e.callback&&e.callback.apply(a[0],arguments);a.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Drop 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Drop
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(c){c.effects.drop=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right","opacity"],e=c.effects.setMode(a,d.options.mode||"hide"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a);var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true})/2:a.outerWidth({margin:true})/2);if(e=="show")a.css("opacity",0).css(f,b=="pos"?-g:g);var i={opacity:e==
"show"?1:0};i[f]=(e=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Explode 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Explode
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(j){j.effects.explode=function(a){return this.queue(function(){var c=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3,d=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3;a.options.mode=a.options.mode=="toggle"?j(this).is(":visible")?"hide":"show":a.options.mode;var b=j(this).show().css("visibility","hidden"),g=b.offset();g.top-=parseInt(b.css("marginTop"),10)||0;g.left-=parseInt(b.css("marginLeft"),10)||0;for(var h=b.outerWidth(true),i=b.outerHeight(true),e=0;e<c;e++)for(var f=
0;f<d;f++)b.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+
e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery);
;/*
 * jQuery UI Effects Fade 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Fade
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Fold 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Fold
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],
10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery);
;/*
 * jQuery UI Effects Highlight 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Highlight
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&&
this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Pulsate 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Pulsate
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c<times;c++){b.animate({opacity:animateTo},duration,a.options.easing);animateTo=(animateTo+1)%2}b.animate({opacity:animateTo},duration,
a.options.easing,function(){animateTo==0&&b.hide();a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()}).dequeue()})}})(jQuery);
;/*
 * jQuery UI Effects Scale 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Scale
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(c){c.effects.puff=function(b){return this.queue(function(){var a=c(this),e=c.effects.setMode(a,b.options.mode||"hide"),g=parseInt(b.options.percent,10)||150,h=g/100,i={height:a.height(),width:a.width()};c.extend(b.options,{fade:true,mode:e,percent:e=="hide"?g:100,from:e=="hide"?i:{height:i.height*h,width:i.width*h}});a.effect("scale",b.options,b.duration,b.callback);a.dequeue()})};c.effects.scale=function(b){return this.queue(function(){var a=c(this),e=c.extend(true,{},b.options),g=c.effects.setMode(a,
b.options.mode||"effect"),h=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:g=="hide"?0:100),i=b.options.direction||"both",f=b.options.origin;if(g!="effect"){e.origin=f||["middle","center"];e.restore=true}f={height:a.height(),width:a.width()};a.from=b.options.from||(g=="show"?{height:0,width:0}:f);h={y:i!="horizontal"?h/100:1,x:i!="vertical"?h/100:1};a.to={height:f.height*h.y,width:f.width*h.x};if(b.options.fade){if(g=="show"){a.from.opacity=0;a.to.opacity=1}if(g=="hide"){a.from.opacity=
1;a.to.opacity=0}}e.from=a.from;e.to=a.to;e.mode=g;a.effect("size",e,b.duration,b.callback);a.dequeue()})};c.effects.size=function(b){return this.queue(function(){var a=c(this),e=["position","top","bottom","left","right","width","height","overflow","opacity"],g=["position","top","bottom","left","right","overflow","opacity"],h=["width","height","overflow"],i=["fontSize"],f=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],k=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],
p=c.effects.setMode(a,b.options.mode||"effect"),n=b.options.restore||false,m=b.options.scale||"both",l=b.options.origin,j={height:a.height(),width:a.width()};a.from=b.options.from||j;a.to=b.options.to||j;if(l){l=c.effects.getBaseline(l,j);a.from.top=(j.height-a.from.height)*l.y;a.from.left=(j.width-a.from.width)*l.x;a.to.top=(j.height-a.to.height)*l.y;a.to.left=(j.width-a.to.width)*l.x}var d={from:{y:a.from.height/j.height,x:a.from.width/j.width},to:{y:a.to.height/j.height,x:a.to.width/j.width}};
if(m=="box"||m=="both"){if(d.from.y!=d.to.y){e=e.concat(f);a.from=c.effects.setTransition(a,f,d.from.y,a.from);a.to=c.effects.setTransition(a,f,d.to.y,a.to)}if(d.from.x!=d.to.x){e=e.concat(k);a.from=c.effects.setTransition(a,k,d.from.x,a.from);a.to=c.effects.setTransition(a,k,d.to.x,a.to)}}if(m=="content"||m=="both")if(d.from.y!=d.to.y){e=e.concat(i);a.from=c.effects.setTransition(a,i,d.from.y,a.from);a.to=c.effects.setTransition(a,i,d.to.y,a.to)}c.effects.save(a,n?e:g);a.show();c.effects.createWrapper(a);
a.css("overflow","hidden").css(a.from);if(m=="content"||m=="both"){f=f.concat(["marginTop","marginBottom"]).concat(i);k=k.concat(["marginLeft","marginRight"]);h=e.concat(f).concat(k);a.find("*[width]").each(function(){child=c(this);n&&c.effects.save(child,h);var o={height:child.height(),width:child.width()};child.from={height:o.height*d.from.y,width:o.width*d.from.x};child.to={height:o.height*d.to.y,width:o.width*d.to.x};if(d.from.y!=d.to.y){child.from=c.effects.setTransition(child,f,d.from.y,child.from);
child.to=c.effects.setTransition(child,f,d.to.y,child.to)}if(d.from.x!=d.to.x){child.from=c.effects.setTransition(child,k,d.from.x,child.from);child.to=c.effects.setTransition(child,k,d.to.x,child.to)}child.css(child.from);child.animate(child.to,b.duration,b.options.easing,function(){n&&c.effects.restore(child,h)})})}a.animate(a.to,{queue:false,duration:b.duration,easing:b.options.easing,complete:function(){a.to.opacity===0&&a.css("opacity",a.from.opacity);p=="hide"&&a.hide();c.effects.restore(a,
n?e:g);c.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Shake 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Shake
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(d){d.effects.shake=function(a){return this.queue(function(){var b=d(this),j=["position","top","bottom","left","right"];d.effects.setMode(b,a.options.mode||"effect");var c=a.options.direction||"left",e=a.options.distance||20,l=a.options.times||3,f=a.duration||a.options.duration||140;d.effects.save(b,j);b.show();d.effects.createWrapper(b);var g=c=="up"||c=="down"?"top":"left",h=c=="up"||c=="left"?"pos":"neg";c={};var i={},k={};c[g]=(h=="pos"?"-=":"+=")+e;i[g]=(h=="pos"?"+=":"-=")+e*2;k[g]=
(h=="pos"?"-=":"+=")+e*2;b.animate(c,f,a.options.easing);for(e=1;e<l;e++)b.animate(i,f,a.options.easing).animate(k,f,a.options.easing);b.animate(i,f,a.options.easing).animate(c,f/2,a.options.easing,function(){d.effects.restore(b,j);d.effects.removeWrapper(b);a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()});b.dequeue()})}})(jQuery);
;/*
 * jQuery UI Effects Slide 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Slide
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(c){c.effects.slide=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right"],f=c.effects.setMode(a,d.options.mode||"show"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a).css({overflow:"hidden"});var g=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var e=d.options.distance||(g=="top"?a.outerHeight({margin:true}):a.outerWidth({margin:true}));if(f=="show")a.css(g,b=="pos"?isNaN(e)?"-"+e:-e:e);
var i={};i[g]=(f=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+e;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){f=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Transfer 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Transfer
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(e){e.effects.transfer=function(a){return this.queue(function(){var b=e(this),c=e(a.options.to),d=c.offset();c={top:d.top,left:d.left,height:c.innerHeight(),width:c.innerWidth()};d=b.offset();var f=e('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments);
b.dequeue()})})}})(jQuery);
;
/*!
 * jQuery UI 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI
 */
(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.16",
keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({propAttr:c.fn.prop||c.fn.attr,_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=
this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,
"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":
"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,
outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,
"tabindex"),d=isNaN(b);return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&
a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&
c.ui.isOverAxis(b,e,i)}})}})(jQuery);
;/*!
 * jQuery UI Widget 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Widget
 */
(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)try{b(d).triggerHandler("remove")}catch(e){}k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){try{b(this).triggerHandler("remove")}catch(d){}});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=
function(h){return!!b.data(h,a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):
d;if(e&&d.charAt(0)==="_")return h;e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=
b.extend(true,{},this.options,this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+
"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",
c);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
;/*!
 * jQuery UI Mouse 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Mouse
 *
 * Depends:
 *	jquery.ui.widget.js
 */
(function(b){var d=false;b(document).mouseup(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"&&a.target.nodeName?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=
this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
;/*
 * jQuery UI Position 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Position
 */
(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY,
left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+=
k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-=
m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left=
d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+=
a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b),
g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery);
;/*
 * jQuery UI Draggable 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Draggables
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.mouse.js
 *	jquery.ui.widget.js
 */
(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;if(b.iframeFix)d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;
this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});
this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true},
_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=
false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,
10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||
!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&
a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=
this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),
10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),
10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,
(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!=
"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),
10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+
this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&
!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.left<g[0])e=g[0]+this.offset.click.left;
if(a.pageY-this.offset.click.top<g[1])h=g[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>g[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.top<g[1]||h-this.offset.click.top>g[3])?h:!(h-this.offset.click.top<g[1])?h-b.grid[1]:h+b.grid[1]:h;e=b.grid[0]?this.originalPageX+Math.round((e-this.originalPageX)/
b.grid[0])*b.grid[0]:this.originalPageX;e=g?!(e-this.offset.click.left<g[0]||e-this.offset.click.left>g[2])?e:!(e-this.offset.click.left<g[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<
526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,
c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.16"});d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var h=d.data(this,"sortable");if(h&&!h.options.disabled){c.sortables.push({instance:h,shouldRevert:h.options.revert});
h.refreshPositions();h._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=
false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",true);
this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;
c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&
this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=
a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!=
"x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<
c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-
c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable","snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,
width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,h=b.offset.left,g=h+c.helperProportions.width,n=b.offset.top,o=n+c.helperProportions.height,i=c.snapElements.length-1;i>=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e<h&&h<l+e&&k-e<n&&n<m+e||j-e<h&&h<l+e&&k-e<o&&o<m+e||j-e<g&&g<l+e&&k-e<n&&n<m+e||j-e<g&&g<l+e&&k-e<o&&
o<m+e){if(f.snapMode!="inner"){var p=Math.abs(k-o)<=e,q=Math.abs(m-n)<=e,r=Math.abs(j-g)<=e,s=Math.abs(l-h)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l}).left-c.margins.left}var t=
p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(k-n)<=e;q=Math.abs(m-o)<=e;r=Math.abs(j-h)<=e;s=Math.abs(l-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m-c.helperProportions.height,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[i].snapping&&
(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=p||q||r||s||t}else{c.snapElements[i].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),
10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery);
;/*
 * jQuery UI Droppable 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Droppables
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.mouse.js
 *	jquery.ui.draggable.js
 */
(function(d){d.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"},_create:function(){var a=this.options,b=a.accept;this.isover=0;this.isout=1;this.accept=d.isFunction(b)?b:function(c){return c.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};d.ui.ddmanager.droppables[a.scope]=d.ui.ddmanager.droppables[a.scope]||[];d.ui.ddmanager.droppables[a.scope].push(this);
a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var a=d.ui.ddmanager.droppables[this.options.scope],b=0;b<a.length;b++)a[b]==this&&a.splice(b,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(a,b){if(a=="accept")this.accept=d.isFunction(b)?b:function(c){return c.is(b)};d.Widget.prototype._setOption.apply(this,arguments)},_activate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&
this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass);
this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g=
d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop",
a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.16"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height;
switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>=
i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!=
"none";if(c[f].visible){e=="mousedown"&&c[f]._activate.call(c[f],b);c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight}}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem||
a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},dragStart:function(a,b){a.element.parents(":not(body,html)").bind("scroll.droppable",function(){a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)})},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);
if(c=!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e=d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})},dragStop:function(a,b){a.element.parents(":not(body,html)").unbind("scroll.droppable");
a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)}}})(jQuery);
;/*
 * jQuery UI Resizable 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Resizables
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.mouse.js
 *	jquery.ui.widget.js
 */
(function(e){e.widget("ui.resizable",e.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1E3},_create:function(){var b=this,a=this.options;this.element.addClass("ui-resizable");e.extend(this,{_aspectRatio:!!a.aspectRatio,aspectRatio:a.aspectRatio,originalElement:this.element,
_proportionallyResizeElements:[],_helper:a.helper||a.ghost||a.animate?a.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){/relative/.test(this.element.css("position"))&&e.browser.opera&&this.element.css({position:"relative",top:"auto",left:"auto"});this.element.wrap(e('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),
top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=
this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",
nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d<c.length;d++){var f=e.trim(c[d]),g=e('<div class="ui-resizable-handle '+("ui-resizable-"+f)+'"></div>');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor==
String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection();
this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){if(!a.disabled){e(this).removeClass("ui-resizable-autohide");b._handles.show()}},function(){if(!a.disabled)if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();
var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=
false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"});
this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff=
{width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis];
if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},
_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,
{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateVirtualBoundaries:function(b){var a=this.options,c,d,f;a={minWidth:k(a.minWidth)?a.minWidth:0,maxWidth:k(a.maxWidth)?a.maxWidth:Infinity,minHeight:k(a.minHeight)?a.minHeight:0,maxHeight:k(a.maxHeight)?a.maxHeight:
Infinity};if(this._aspectRatio||b){b=a.minHeight*this.aspectRatio;d=a.minWidth/this.aspectRatio;c=a.maxHeight*this.aspectRatio;f=a.maxWidth/this.aspectRatio;if(b>a.minWidth)a.minWidth=b;if(d>a.minHeight)a.minHeight=d;if(c<a.maxWidth)a.maxWidth=c;if(f<a.maxHeight)a.maxHeight=f}this._vBoundaries=a},_updateCache:function(b){this.offset=this.helper.offset();if(k(b.left))this.position.left=b.left;if(k(b.top))this.position.top=b.top;if(k(b.height))this.size.height=b.height;if(k(b.width))this.size.width=
b.width},_updateRatio:function(b){var a=this.position,c=this.size,d=this.axis;if(k(b.height))b.width=b.height*this.aspectRatio;else if(k(b.width))b.height=b.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(d=="nw"){b.top=a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this._vBoundaries,c=this.axis,d=k(b.width)&&a.maxWidth&&a.maxWidth<b.width,f=k(b.height)&&a.maxHeight&&a.maxHeight<b.height,g=k(b.width)&&a.minWidth&&
a.minWidth>b.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height,l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&l)b.left=i-a.minWidth;if(d&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=
null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a<this._proportionallyResizeElements.length;a++){var c=this._proportionallyResizeElements[a];if(!this.borderDif){var d=[c.css("borderTopWidth"),c.css("borderRightWidth"),c.css("borderBottomWidth"),c.css("borderLeftWidth")],f=[c.css("paddingTop"),c.css("paddingRight"),c.css("paddingBottom"),c.css("paddingLeft")];this.borderDif=e.map(d,function(g,h){g=parseInt(g,10)||
0;h=parseInt(f[h],10)||0;return g+h})}e.browser.msie&&(e(b).is(":hidden")||e(b).parents(":hidden").length)||c.css({height:b.height()-this.borderDif[0]-this.borderDif[2]||0,width:b.width()-this.borderDif[1]-this.borderDif[3]||0})}},_renderProxy:function(){var b=this.options;this.elementOffset=this.element.offset();if(this._helper){this.helper=this.helper||e('<div style="overflow:hidden;"></div>');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+
a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+
c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);
b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.16"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),
10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-
f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var l=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:l.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(l.css("position"))){c._revertToRelativePosition=true;l.css({position:"absolute",top:"auto",left:"auto"})}l.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?
e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=
e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,
step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=
e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;
var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:
a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-
d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,
f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,
display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=
e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=
d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery);
;/*
 * jQuery UI Selectable 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Selectables
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.mouse.js
 *	jquery.ui.widget.js
 */
(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"),
selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("<div class='ui-selectable-helper'></div>")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX,
c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting",
c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d=
this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.right<b||a.top>i||a.bottom<g);else if(d.tolerance=="fit")k=a.left>b&&a.right<h&&a.top>g&&a.bottom<i;if(k){if(a.selected){a.$element.removeClass("ui-selected");a.selected=false}if(a.unselecting){a.$element.removeClass("ui-unselecting");
a.unselecting=false}if(!a.selecting){a.$element.addClass("ui-selecting");a.selecting=true;f._trigger("selecting",c,{selecting:a.element})}}else{if(a.selecting)if(c.metaKey&&a.startselected){a.$element.removeClass("ui-selecting");a.selecting=false;a.$element.addClass("ui-selected");a.selected=true}else{a.$element.removeClass("ui-selecting");a.selecting=false;if(a.startselected){a.$element.addClass("ui-unselecting");a.unselecting=true}f._trigger("unselecting",c,{unselecting:a.element})}if(a.selected)if(!c.metaKey&&
!a.startselected){a.$element.removeClass("ui-selected");a.selected=false;a.$element.addClass("ui-unselecting");a.unselecting=true;f._trigger("unselecting",c,{unselecting:a.element})}}}});return false}},_mouseStop:function(c){var f=this;this.dragged=false;e(".ui-unselecting",this.element[0]).each(function(){var d=e.data(this,"selectable-item");d.$element.removeClass("ui-unselecting");d.unselecting=false;d.startselected=false;f._trigger("unselected",c,{unselected:d.element})});e(".ui-selecting",this.element[0]).each(function(){var d=
e.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected");d.selecting=false;d.selected=true;d.startselected=true;f._trigger("selected",c,{selected:d.element})});this._trigger("stop",c);this.helper.remove();return false}});e.extend(e.ui.selectable,{version:"1.8.16"})})(jQuery);
;/*
 * jQuery UI Sortable 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Sortables
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.mouse.js
 *	jquery.ui.widget.js
 */
(function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable");
this.refresh();this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a===
"disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&
!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,
left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};
this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=
document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);
return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop+b.scrollSpeed;else if(a.pageY-this.overflowOffset.top<
b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop-b.scrollSpeed;if(this.overflowOffset.left+this.scrollParent[0].offsetWidth-a.pageX<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft+b.scrollSpeed;else if(a.pageX-this.overflowOffset.left<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft-b.scrollSpeed}else{if(a.pageY-d(document).scrollTop()<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()-
b.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()+b.scrollSpeed);if(a.pageX-d(document).scrollLeft()<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()-b.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()+b.scrollSpeed)}c!==false&&d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,
a)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(b=this.items.length-1;b>=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],
e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();
c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):
this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,
dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},
toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+j<k&&b+l>g&&b+l<h;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||
this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?j:g<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<h&&i<e+this.helperProportions.height/2&&f-this.helperProportions.height/2<k},_intersectsWithPointer:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left,a.width);b=b&&a;a=this._getDragVerticalDirection();
var c=this._getDragHorizontalDirection();if(!b)return false;return this.floating?c&&c=="right"||a=="down"?2:1:a&&(a=="down"?2:1)},_intersectsWithSides:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top+a.height/2,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left+a.width/2,a.width);var c=this._getDragVerticalDirection(),e=this._getDragHorizontalDirection();return this.floating&&e?e=="right"&&a||e=="left"&&!a:c&&(c=="down"&&b||c=="up"&&!b)},
_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();
if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),
this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(a){this.items=[];this.containers=[this];var b=this.items,c=[[d.isFunction(this.options.items)?this.options.items.call(this.element[0],a,{item:this.currentItem}):d(this.options.items,this.element),
this]],e=this._connectWith();if(e)for(var f=e.length-1;f>=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h<g;h++){i=d(e[h]);i.data("sortable-item",a);b.push({item:i,instance:a,width:0,height:0,left:0,top:0})}}},refreshPositions:function(a){if(this.offsetParent&&
this.helper)this.offset.parent=this._getParentOffset();for(var b=this.items.length-1;b>=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=
this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=
d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||
0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",
a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-
f)<b){b=Math.abs(h-f);e=this.items[g]}}if(e||this.options.dropOnEmpty){this.currentContainer=this.containers[c];e?this._rearrange(a,e,null,true):this._rearrange(a,null,this.containers[c].element,true);this._trigger("change",a,this._uiHash());this.containers[c]._trigger("change",a,this._uiHash(this));this.options.placeholder.update(this.currentContainer,this.placeholder);this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}}},_createHelper:function(a){var b=
this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a,this.currentItem])):b.helper=="clone"?this.currentItem.clone():this.currentItem;a.parents("body").length||d(b.appendTo!="parent"?b.appendTo:this.currentItem[0].parentNode)[0].appendChild(a[0]);if(a[0]==this.currentItem[0])this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")};if(a[0].style.width==
""||b.forceHelperSize)a.width(this.currentItem.width());if(a[0].style.height==""||b.forceHelperSize)a.height(this.currentItem.height());return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=
this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a=
{top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),
10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?
document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)){var b=d(a.containment)[0];a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),
10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(a,b){if(!b)b=
this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&
this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0]))this.offset.relative=this._getRelativeOffset();
var f=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])f=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-
this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;f=this.originalPageX+Math.round((f-this.originalPageX)/b.grid[0])*b.grid[0];f=this.containment?!(f-this.offset.click.left<this.containment[0]||f-this.offset.click.left>this.containment[2])?f:!(f-this.offset.click.left<this.containment[0])?f-b.grid[0]:f+b.grid[0]:f}}return{top:g-
this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())}},_rearrange:function(a,b,c,e){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],
this.direction=="down"?b.item[0]:b.item[0].nextSibling);this.counter=this.counter?++this.counter:1;var f=this,g=this.counter;window.setTimeout(function(){g==f.counter&&f.refreshPositions(!e)},0)},_clear:function(a,b){this.reverting=false;var c=[];!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem);this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var e in this._storedCSS)if(this._storedCSS[e]=="auto"||this._storedCSS[e]=="static")this._storedCSS[e]=
"";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!b&&c.push(function(f){this._trigger("receive",f,this._uiHash(this.fromOutside))});if((this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!b)c.push(function(f){this._trigger("update",f,this._uiHash())});if(!d.ui.contains(this.element[0],this.currentItem[0])){b||c.push(function(f){this._trigger("remove",
f,this._uiHash())});for(e=this.containers.length-1;e>=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,
this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",
a,this._uiHash());for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}return false}b||this._trigger("beforeStop",a,this._uiHash());this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.helper[0]!=this.currentItem[0]&&this.helper.remove();this.helper=null;if(!b){for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}this.fromOutside=false;return true},_trigger:function(){d.Widget.prototype._trigger.apply(this,arguments)===false&&this.cancel()},
_uiHash:function(a){var b=a||this;return{helper:b.helper,placeholder:b.placeholder||d([]),position:b.position,originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,sender:a?a.element:null}}});d.extend(d.ui.sortable,{version:"1.8.16"})})(jQuery);
;/*
 * jQuery UI Accordion 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Accordion
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 */
(function(c){c.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix");
a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion",
function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a=
this.options;if(a.icons){c("<span></span>").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex");
this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons();
b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target);
a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+
c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options;
if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);
if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(),
e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight||
e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false",
"aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.16",
animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/);
f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide",
paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery);
;/*
 * jQuery UI Autocomplete 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Autocomplete
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 */
(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.propAttr("readOnly"))){g=
false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!=
a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)};
this.menu=d("<ul></ul>").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&&
a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");
d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&&
b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source=
this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==false)return this._search(a)},_search:function(a){this.pending++;this.element.addClass("ui-autocomplete-loading");this.source({term:a},this.response)},_response:function(a){if(!this.options.disabled&&a&&a.length){a=this._normalize(a);this._suggest(a);this._trigger("open")}else this.close();
this.pending--;this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing);if(this.menu.element.is(":visible")){this.menu.element.hide();this.menu.deactivate();this._trigger("close",a)}},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(a){if(a.length&&a[0].label&&a[0].value)return a;return d.map(a,function(b){if(typeof b==="string")return{label:b,value:b};return d.extend({label:b.label||
b.value,value:b.value||b.label},b)})},_suggest:function(a){var b=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(b,a);this.menu.deactivate();this.menu.refresh();b.show();this._resizeMenu();b.position(d.extend({of:this.element},this.options.position));this.options.autoFocus&&this.menu.next(new d.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth(),this.element.outerWidth()))},_renderMenu:function(a,b){var g=this;
d.each(b,function(c,f){g._renderItem(a,f)})},_renderItem:function(a,b){return d("<li></li>").data("item.autocomplete",b).append(d("<a></a>").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,
"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery);
(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex",
-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.scrollTop(),c=this.element.height();if(b<0)this.element.scrollTop(g+b);else b>=c&&this.element.scrollTop(g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");
this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b,
this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[d.fn.prop?"prop":"attr"]("scrollHeight")},select:function(e){this._trigger("selected",e,{item:this.active})}})})(jQuery);
;/*
 * jQuery UI Button 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Button
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 */
(function(b){var h,i,j,g,l=function(){var a=b(this).find(":ui-button");setTimeout(function(){a.button("refresh")},1)},k=function(a){var c=a.name,e=a.form,f=b([]);if(c)f=e?b(e).find("[name='"+c+"']"):b("[name='"+c+"']",a.ownerDocument).filter(function(){return!this.form});return f};b.widget("ui.button",{options:{disabled:null,text:true,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",l);if(typeof this.options.disabled!==
"boolean")this.options.disabled=this.element.propAttr("disabled");this._determineButtonType();this.hasTitle=!!this.buttonElement.attr("title");var a=this,c=this.options,e=this.type==="checkbox"||this.type==="radio",f="ui-state-hover"+(!e?" ui-state-active":"");if(c.label===null)c.label=this.buttonElement.html();if(this.element.is(":disabled"))c.disabled=true;this.buttonElement.addClass("ui-button ui-widget ui-state-default ui-corner-all").attr("role","button").bind("mouseenter.button",function(){if(!c.disabled){b(this).addClass("ui-state-hover");
this===h&&b(this).addClass("ui-state-active")}}).bind("mouseleave.button",function(){c.disabled||b(this).removeClass(f)}).bind("click.button",function(d){if(c.disabled){d.preventDefault();d.stopImmediatePropagation()}});this.element.bind("focus.button",function(){a.buttonElement.addClass("ui-state-focus")}).bind("blur.button",function(){a.buttonElement.removeClass("ui-state-focus")});if(e){this.element.bind("change.button",function(){g||a.refresh()});this.buttonElement.bind("mousedown.button",function(d){if(!c.disabled){g=
false;i=d.pageX;j=d.pageY}}).bind("mouseup.button",function(d){if(!c.disabled)if(i!==d.pageX||j!==d.pageY)g=true})}if(this.type==="checkbox")this.buttonElement.bind("click.button",function(){if(c.disabled||g)return false;b(this).toggleClass("ui-state-active");a.buttonElement.attr("aria-pressed",a.element[0].checked)});else if(this.type==="radio")this.buttonElement.bind("click.button",function(){if(c.disabled||g)return false;b(this).addClass("ui-state-active");a.buttonElement.attr("aria-pressed","true");
var d=a.element[0];k(d).not(d).map(function(){return b(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")});else{this.buttonElement.bind("mousedown.button",function(){if(c.disabled)return false;b(this).addClass("ui-state-active");h=this;b(document).one("mouseup",function(){h=null})}).bind("mouseup.button",function(){if(c.disabled)return false;b(this).removeClass("ui-state-active")}).bind("keydown.button",function(d){if(c.disabled)return false;if(d.keyCode==b.ui.keyCode.SPACE||
d.keyCode==b.ui.keyCode.ENTER)b(this).addClass("ui-state-active")}).bind("keyup.button",function(){b(this).removeClass("ui-state-active")});this.buttonElement.is("a")&&this.buttonElement.keyup(function(d){d.keyCode===b.ui.keyCode.SPACE&&b(this).click()})}this._setOption("disabled",c.disabled);this._resetButton()},_determineButtonType:function(){this.type=this.element.is(":checkbox")?"checkbox":this.element.is(":radio")?"radio":this.element.is("input")?"input":"button";if(this.type==="checkbox"||this.type===
"radio"){var a=this.element.parents().filter(":last"),c="label[for='"+this.element.attr("id")+"']";this.buttonElement=a.find(c);if(!this.buttonElement.length){a=a.length?a.siblings():this.element.siblings();this.buttonElement=a.filter(c);if(!this.buttonElement.length)this.buttonElement=a.find(c)}this.element.addClass("ui-helper-hidden-accessible");(a=this.element.is(":checked"))&&this.buttonElement.addClass("ui-state-active");this.buttonElement.attr("aria-pressed",a)}else this.buttonElement=this.element},
widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible");this.buttonElement.removeClass("ui-button ui-widget ui-state-default ui-corner-all ui-state-hover ui-state-active  ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only").removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html());this.hasTitle||this.buttonElement.removeAttr("title");
b.Widget.prototype.destroy.call(this)},_setOption:function(a,c){b.Widget.prototype._setOption.apply(this,arguments);if(a==="disabled")c?this.element.propAttr("disabled",true):this.element.propAttr("disabled",false);else this._resetButton()},refresh:function(){var a=this.element.is(":disabled");a!==this.options.disabled&&this._setOption("disabled",a);if(this.type==="radio")k(this.element[0]).each(function(){b(this).is(":checked")?b(this).button("widget").addClass("ui-state-active").attr("aria-pressed",
"true"):b(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")});else if(this.type==="checkbox")this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false")},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var a=this.buttonElement.removeClass("ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only"),
c=b("<span></span>").addClass("ui-button-text").html(this.options.label).appendTo(a.empty()).text(),e=this.options.icons,f=e.primary&&e.secondary,d=[];if(e.primary||e.secondary){if(this.options.text)d.push("ui-button-text-icon"+(f?"s":e.primary?"-primary":"-secondary"));e.primary&&a.prepend("<span class='ui-button-icon-primary ui-icon "+e.primary+"'></span>");e.secondary&&a.append("<span class='ui-button-icon-secondary ui-icon "+e.secondary+"'></span>");if(!this.options.text){d.push(f?"ui-button-icons-only":
"ui-button-icon-only");this.hasTitle||a.attr("title",c)}}else d.push("ui-button-text-only");a.addClass(d.join(" "))}}});b.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(a,c){a==="disabled"&&this.buttons.button("option",a,c);b.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var a=this.element.css("direction")===
"ltr";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(a?"ui-corner-left":"ui-corner-right").end().filter(":last").addClass(a?"ui-corner-right":"ui-corner-left").end().end()},destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");
b.Widget.prototype.destroy.call(this)}})})(jQuery);
;/*
 * jQuery UI Dialog 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Dialog
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *  jquery.ui.button.js
 *	jquery.ui.draggable.js
 *	jquery.ui.mouse.js
 *	jquery.ui.position.js
 *	jquery.ui.resizable.js
 */
(function(c,l){var m={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},n={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true},o=c.attrFn||{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true,click:true};c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,
position:{my:"center",at:"center",collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||"&#160;",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("<div></div>")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+
b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&!i.isDefaultPrevented()&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),
h=c('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("<span></span>")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("<span></span>").addClass("ui-dialog-title").attr("id",
e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");
a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d,e;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!==
b.uiDialog[0]){e=c(this).css("z-index");isNaN(e)||(d=Math.max(d,e))}});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()};c.ui.dialog.maxZ+=1;
d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target===
f[0]&&e.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false,e=c("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,
function(){return!(d=true)});if(d){c.each(a,function(f,h){h=c.isFunction(h)?{click:h,text:f}:h;var i=c('<button type="button"></button>').click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.each(h,function(j,k){if(j!=="click")j in o?i[j](k):i.attr(j,k)});c.fn.button&&i.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",
handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition,
originalSize:f.originalSize,position:f.position,size:f.size}}a=a===l?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize",
f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop",f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0],e;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):
[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,f){if(+b[g]===b[g]){d[g]=b[g];b[g]=f}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(e=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(c.extend({of:window},a));e||this.uiDialog.hide()},_setOptions:function(a){var b=this,d={},e=false;c.each(a,function(g,f){b._setOption(g,f);
if(g in m)e=true;if(g in n)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"):
e.removeClass("ui-dialog-disabled");break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||"&#160;"));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a=
this.options,b,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height-
b,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.16",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),
create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()<c.ui.dialog.overlay.maxZ)return false})},1);c(document).bind("keydown.dialog-overlay",function(d){if(a.options.closeOnEscape&&!d.isDefaultPrevented()&&d.keyCode&&d.keyCode===c.ui.keyCode.ESCAPE){a.close(d);d.preventDefault()}});c(window).bind("resize.dialog-overlay",c.ui.dialog.overlay.resize)}var b=(this.oldInstances.pop()||
c("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&&
c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a<b?c(window).height()+"px":a+"px"}else return c(document).height()+"px"},width:function(){var a,b;if(c.browser.msie){a=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth);b=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return a<b?c(window).width()+"px":a+"px"}else return c(document).width()+
"px"},resize:function(){var a=c([]);c.each(c.ui.dialog.overlay.instances,function(){a=a.add(this)});a.css({width:0,height:0}).css({width:c.ui.dialog.overlay.width(),height:c.ui.dialog.overlay.height()})}});c.extend(c.ui.dialog.overlay.prototype,{destroy:function(){c.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);
;/*
 * jQuery UI Slider 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Slider
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.mouse.js
 *	jquery.ui.widget.js
 */
(function(d){d.widget("ui.slider",d.ui.mouse,{widgetEventPrefix:"slide",options:{animate:false,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null},_create:function(){var a=this,b=this.options,c=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f=b.values&&b.values.length||1,e=[];this._mouseSliding=this._keySliding=false;this._animateOff=true;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+
this.orientation+" ui-widget ui-widget-content ui-corner-all"+(b.disabled?" ui-slider-disabled ui-disabled":""));this.range=d([]);if(b.range){if(b.range===true){if(!b.values)b.values=[this._valueMin(),this._valueMin()];if(b.values.length&&b.values.length!==2)b.values=[b.values[0],b.values[0]]}this.range=d("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(b.range==="min"||b.range==="max"?" ui-slider-range-"+b.range:""))}for(var j=c.length;j<f;j+=1)e.push("<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>");
this.handles=c.add(d(e.join("")).appendTo(a.element));this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(g){g.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur();else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(g){d(this).data("index.ui-slider-handle",
g)});this.handles.keydown(function(g){var k=true,l=d(this).data("index.ui-slider-handle"),i,h,m;if(!a.options.disabled){switch(g.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:k=false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");i=a._start(g,l);if(i===false)return}break}m=a.options.step;i=a.options.values&&a.options.values.length?
(h=a.values(l)):(h=a.value());switch(g.keyCode){case d.ui.keyCode.HOME:h=a._valueMin();break;case d.ui.keyCode.END:h=a._valueMax();break;case d.ui.keyCode.PAGE_UP:h=a._trimAlignValue(i+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=a._trimAlignValue(i-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(i===a._valueMax())return;h=a._trimAlignValue(i+m);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(i===a._valueMin())return;h=a._trimAlignValue(i-
m);break}a._slide(g,l,h);return k}}).keyup(function(g){var k=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(g,k);a._change(g,k);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy();
return this},_mouseCapture:function(a){var b=this.options,c,f,e,j,g;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:a.pageX,y:a.pageY});f=this._valueMax()-this._valueMin()+1;j=this;this.handles.each(function(k){var l=Math.abs(c-j.values(k));if(f>l){f=l;e=d(this);g=k}});if(b.range===true&&this.values(1)===b.min){g+=1;e=d(this.handles[g])}if(this._start(a,g)===false)return false;
this._mouseSliding=true;j._handleIndex=g;e.addClass("ui-state-active").focus();b=e.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-e.width()/2,top:a.pageY-b.top-e.height()/2-(parseInt(e.css("borderTopWidth"),10)||0)-(parseInt(e.css("borderBottomWidth"),10)||0)+(parseInt(e.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(a,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(a){var b=
this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b;if(this.orientation==="horizontal"){b=
this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);
c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var f;if(this.options.values&&this.options.values.length){f=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>f||b===1&&c<f))c=f;if(c!==this.values(b)){f=this.values();f[b]=c;a=this._trigger("slide",a,{handle:this.handles[b],value:c,values:f});this.values(b?0:1);a!==false&&this.values(b,c,true)}}else if(c!==this.value()){a=this._trigger("slide",a,{handle:this.handles[b],value:c});
a!==false&&this.value(c)}},_stop:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);c.values=this.values()}this._trigger("stop",a,c)},_change:function(a,b){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);c.values=this.values()}this._trigger("change",a,c)}},value:function(a){if(arguments.length){this.options.value=
this._trimAlignValue(a);this._refreshValue();this._change(null,0)}else return this._value()},values:function(a,b){var c,f,e;if(arguments.length>1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}else if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;f=arguments[0];for(e=0;e<c.length;e+=1){c[e]=this._trimAlignValue(f[e]);this._change(null,e)}this._refreshValue()}else return this.options.values&&this.options.values.length?this._values(a):
this.value();else return this._values()},_setOption:function(a,b){var c,f=0;if(d.isArray(this.options.values))f=this.options.values.length;d.Widget.prototype._setOption.apply(this,arguments);switch(a){case "disabled":if(b){this.handles.filter(".ui-state-focus").blur();this.handles.removeClass("ui-state-hover");this.handles.propAttr("disabled",true);this.element.addClass("ui-disabled")}else{this.handles.propAttr("disabled",false);this.element.removeClass("ui-disabled")}break;case "orientation":this._detectOrientation();
this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue();break;case "value":this._animateOff=true;this._refreshValue();this._change(null,0);this._animateOff=false;break;case "values":this._animateOff=true;this._refreshValue();for(c=0;c<f;c+=1)this._change(null,c);this._animateOff=false;break}},_value:function(){var a=this.options.value;return a=this._trimAlignValue(a)},_values:function(a){var b,c;if(arguments.length){b=this.options.values[a];
return b=this._trimAlignValue(b)}else{b=this.options.values.slice();for(c=0;c<b.length;c+=1)b[c]=this._trimAlignValue(b[c]);return b}},_trimAlignValue:function(a){if(a<=this._valueMin())return this._valueMin();if(a>=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a=
this.options.range,b=this.options,c=this,f=!this._animateOff?b.animate:false,e,j={},g,k,l,i;if(this.options.values&&this.options.values.length)this.handles.each(function(h){e=(c.values(h)-c._valueMin())/(c._valueMax()-c._valueMin())*100;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";d(this).stop(1,1)[f?"animate":"css"](j,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(h===0)c.range.stop(1,1)[f?"animate":"css"]({left:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({width:e-
g+"%"},{queue:false,duration:b.animate})}else{if(h===0)c.range.stop(1,1)[f?"animate":"css"]({bottom:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({height:e-g+"%"},{queue:false,duration:b.animate})}g=e});else{k=this.value();l=this._valueMin();i=this._valueMax();e=i!==l?(k-l)/(i-l)*100:0;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[f?"animate":"css"](j,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[f?"animate":"css"]({width:e+"%"},
b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[f?"animate":"css"]({width:100-e+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[f?"animate":"css"]({height:e+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[f?"animate":"css"]({height:100-e+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.16"})})(jQuery);
;/*
 * jQuery UI Tabs 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Tabs
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 */
(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&&
e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=
d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]||
(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all");
this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected=
this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active");
if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));
this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+
g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",
function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")};
this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected=
-1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";
d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=
d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b,
e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]);
j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove();
if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1<this.anchors.length?1:-1));e.disabled=d.map(d.grep(e.disabled,function(h){return h!=b}),function(h){return h>=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null,
this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this},
load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c,
"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},
url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.16"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k<a.anchors.length?k:0)},b);j&&j.stopPropagation()});e=a._unrotate||(a._unrotate=!e?function(j){j.clientX&&
a.rotate(null)}:function(){t=c.selected;h()});if(b){this.element.bind("tabsshow",h);this.anchors.bind(c.event+".tabs",e);h()}else{clearTimeout(a.rotation);this.element.unbind("tabsshow",h);this.anchors.unbind(c.event+".tabs",e);delete this._rotate;delete this._unrotate}return this}})})(jQuery);
;/*
 * jQuery UI Datepicker 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Datepicker
 *
 * Depends:
 *	jquery.ui.core.js
 */
(function(d,C){function M(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass=
"ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su",
"Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",
minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false,disabled:false};d.extend(this._defaults,this.regional[""]);this.dpDiv=N(d('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}function N(a){return a.bind("mouseout",
function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");b.length&&b.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");if(!(d.datepicker._isDisabledDatepicker(J.inline?a.parent()[0]:J.input[0])||!b.length)){b.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
b.addClass("ui-state-hover");b.hasClass("ui-datepicker-prev")&&b.addClass("ui-datepicker-prev-hover");b.hasClass("ui-datepicker-next")&&b.addClass("ui-datepicker-next-hover")}})}function H(a,b){d.extend(a,b);for(var c in b)if(b[c]==null||b[c]==C)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.16"}});var B=(new Date).getTime(),J;d.extend(M.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},
setDefaults:function(a){H(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,
"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:N(d('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}},_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",
function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b);b.settings.disabled&&this._disableDatepicker(a)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&b.append.remove();if(c){b.append=d('<span class="'+this._appendClass+'">'+c+"</span>");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c==
"focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("<img/>").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('<button type="button"></button>').addClass(this._triggerClass).html(f==""?c:d("<img/>").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():
d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;g<f.length;g++)if(f[g].length>h){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,
b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),true);this._updateDatepicker(b);this._updateAlternate(b);b.settings.disabled&&this._disableDatepicker(a);b.dpDiv.css("display","block")}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=
1;this._dialogInput=d('<input type="text" id="'+("dp"+this.uuid)+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}H(a.settings,e||{});b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/
2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=
d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=
a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().removeClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a,
"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().addClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==
a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return true;return false},_getInst:function(a){try{return d.data(a,"datepicker")}catch(b){throw"Missing instance data for this datepicker";}},_optionDatepicker:function(a,b,c){var e=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?d.extend({},d.datepicker._defaults):e?b=="all"?
d.extend({},e.settings):this._get(e,b):null;var f=b||{};if(typeof b=="string"){f={};f[b]=c}if(e){this._curInst==e&&this._hideDatepicker();var h=this._getDateDatepicker(a,true),i=this._getMinMaxDate(e,"min"),g=this._getMinMaxDate(e,"max");H(e.settings,f);if(i!==null&&f.dateFormat!==C&&f.minDate===C)e.settings.minDate=this._formatDate(e,i);if(g!==null&&f.dateFormat!==C&&f.maxDate===C)e.settings.maxDate=this._formatDate(e,g);this._attachments(d(a),e);this._autoSize(e);this._setDate(e,h);this._updateAlternate(e);
this._updateDatepicker(e)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){(a=this._getInst(a))&&this._updateDatepicker(a)},_setDateDatepicker:function(a,b){if(a=this._getInst(a)){this._setDate(a,b);this._updateDatepicker(a);this._updateAlternate(a)}},_getDateDatepicker:function(a,b){(a=this._getInst(a))&&!a.inline&&this._setDateFromField(a,b);return a?this._getDate(a):null},_doKeyDown:function(a){var b=d.datepicker._getInst(a.target),c=true,e=b.dpDiv.is(".ui-datepicker-rtl");
b._keyEvent=true;if(d.datepicker._datepickerShowing)switch(a.keyCode){case 9:d.datepicker._hideDatepicker();c=false;break;case 13:c=d("td."+d.datepicker._dayOverClass+":not(."+d.datepicker._currentClass+")",b.dpDiv);c[0]&&d.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,c[0]);if(a=d.datepicker._get(b,"onSelect")){c=d.datepicker._formatDate(b);a.apply(b.input?b.input[0]:null,[c,b])}else d.datepicker._hideDatepicker();return false;case 27:d.datepicker._hideDatepicker();break;case 33:d.datepicker._adjustDate(a.target,
a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 34:d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 35:if(a.ctrlKey||a.metaKey)d.datepicker._clearDate(a.target);c=a.ctrlKey||a.metaKey;break;case 36:if(a.ctrlKey||a.metaKey)d.datepicker._gotoToday(a.target);c=a.ctrlKey||a.metaKey;break;case 37:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?+1:-1,"D");c=
a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 38:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,-7,"D");c=a.ctrlKey||a.metaKey;break;case 39:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?-1:+1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,
"stepMonths"),"M");break;case 40:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,+7,"D");c=a.ctrlKey||a.metaKey;break;default:c=false}else if(a.keyCode==36&&a.ctrlKey)d.datepicker._showDatepicker(this);else c=false;if(c){a.preventDefault();a.stopPropagation()}},_doKeyPress:function(a){var b=d.datepicker._getInst(a.target);if(d.datepicker._get(b,"constrainInput")){b=d.datepicker._possibleChars(d.datepicker._get(b,"dateFormat"));var c=String.fromCharCode(a.charCode==C?a.keyCode:a.charCode);
return a.ctrlKey||a.metaKey||c<" "||!b||b.indexOf(c)>-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",
a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);if(d.datepicker._curInst&&d.datepicker._curInst!=b){d.datepicker._datepickerShowing&&d.datepicker._triggerOnClose(d.datepicker._curInst);d.datepicker._curInst.dpDiv.stop(true,true)}var c=d.datepicker._get(b,"beforeShow");c=c?c.apply(a,[a,b]):{};if(c!==false){H(b.settings,c);b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value=
"";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);
c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.datepicker._datepickerShowing=
true;d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}}},_updateDatepicker:function(a){this.maxRows=4;var b=d.datepicker._getBorders(a.dpDiv);J=a;a.dpDiv.empty().append(this._generateHTML(a));var c=a.dpDiv.find("iframe.ui-datepicker-cover");c.length&&c.css({left:-b[0],top:-b[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});
a.dpDiv.find("."+this._dayOverClass+" a").mouseover();b=this._getNumberOfMonths(a);c=b[1];a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");c>1&&a.dpDiv.addClass("ui-datepicker-multi-"+c).css("width",17*c+"em");a.dpDiv[(b[0]!=1||b[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&
!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var e=a.yearshtml;setTimeout(function(){e===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);e=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),
h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=
this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_triggerOnClose:function(a){var b=this._get(a,"onClose");if(b)b.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a])},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);
this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();d.datepicker._triggerOnClose(b);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},
_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):
0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e["selected"+(c=="M"?
"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);
this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");
if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?
b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=A+1<a.length&&a.charAt(A+1)==p)&&A++;return p},m=function(p){var D=
o(p);p=new RegExp("^\\d{1,"+(p=="@"?14:p=="!"?20:p=="y"&&D?4:p=="o"?3:2)+"}");p=b.substring(q).match(p);if(!p)throw"Missing number at position "+q;q+=p[0].length;return parseInt(p[0],10)},n=function(p,D,K){p=d.map(o(p)?K:D,function(w,x){return[[x,w]]}).sort(function(w,x){return-(w[1].length-x[1].length)});var E=-1;d.each(p,function(w,x){w=x[1];if(b.substr(q,w.length).toLowerCase()==w.toLowerCase()){E=x[0];q+=w.length;return false}});if(E!=-1)return E+1;else throw"Unknown name at position "+q;},s=
function(){if(b.charAt(q)!=a.charAt(A))throw"Unexpected literal at position "+q;q++},q=0,A=0;A<a.length;A++)if(k)if(a.charAt(A)=="'"&&!o("'"))k=false;else s();else switch(a.charAt(A)){case "d":l=m("d");break;case "D":n("D",f,h);break;case "o":u=m("o");break;case "m":j=m("m");break;case "M":j=n("M",i,g);break;case "y":c=m("y");break;case "@":var v=new Date(m("@"));c=v.getFullYear();j=v.getMonth()+1;l=v.getDate();break;case "!":v=new Date((m("!")-this._ticksTo1970)/1E4);c=v.getFullYear();j=v.getMonth()+
1;l=v.getDate();break;case "'":if(o("'"))s();else k=true;break;default:s()}if(q<b.length)throw"Extra/unparsed characters found in date: "+b.substring(q);if(c==-1)c=(new Date).getFullYear();else if(c<100)c+=(new Date).getFullYear()-(new Date).getFullYear()%100+(c<=e?0:-100);if(u>-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,j-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=j||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",
COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:
null)||this._defaults.monthNames;var i=function(o){(o=k+1<a.length&&a.charAt(k+1)==o)&&k++;return o},g=function(o,m,n){m=""+m;if(i(o))for(;m.length<n;)m="0"+m;return m},j=function(o,m,n,s){return i(o)?s[m]:n[m]},l="",u=false;if(b)for(var k=0;k<a.length;k++)if(u)if(a.charAt(k)=="'"&&!i("'"))u=false;else l+=a.charAt(k);else switch(a.charAt(k)){case "d":l+=g("d",b.getDate(),2);break;case "D":l+=j("D",b.getDay(),e,f);break;case "o":l+=g("o",Math.round(((new Date(b.getFullYear(),b.getMonth(),b.getDate())).getTime()-
(new Date(b.getFullYear(),0,0)).getTime())/864E5),3);break;case "m":l+=g("m",b.getMonth()+1,2);break;case "M":l+=j("M",b.getMonth(),h,c);break;case "y":l+=i("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case "@":l+=b.getTime();break;case "!":l+=b.getTime()*1E4+this._ticksTo1970;break;case "'":if(i("'"))l+="'";else u=true;break;default:l+=a.charAt(k)}return l},_possibleChars:function(a){for(var b="",c=false,e=function(h){(h=f+1<a.length&&a.charAt(f+1)==h)&&f++;return h},f=
0;f<a.length;f++)if(c)if(a.charAt(f)=="'"&&!e("'"))c=false;else b+=a.charAt(f);else switch(a.charAt(f)){case "d":case "m":case "y":case "@":b+="0123456789";break;case "D":case "M":return null;case "'":if(e("'"))b+="'";else c=true;break;default:b+=a.charAt(f)}return b},_get:function(a,b){return a.settings[b]!==C?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),e=a.lastVal=a.input?a.input.val():null,f,h;f=h=this._getDefaultDate(a);
var i=this._getFormatConfig(a);try{f=this.parseDate(c,e,i)||h}catch(g){this.log(g);e=b?"":e}a.selectedDay=f.getDate();a.drawMonth=a.selectedMonth=f.getMonth();a.drawYear=a.selectedYear=f.getFullYear();a.currentDay=e?f.getDate():0;a.currentMonth=e?f.getMonth():0;a.currentYear=e?f.getFullYear():0;this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var e=function(h){var i=new Date;
i.setDate(i.getDate()+h);return i},f=function(h){try{return d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),h,d.datepicker._getFormatConfig(a))}catch(i){}var g=(h.toLowerCase().match(/^c/)?d.datepicker._getDate(a):null)||new Date,j=g.getFullYear(),l=g.getMonth();g=g.getDate();for(var u=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,k=u.exec(h);k;){switch(k[2]||"d"){case "d":case "D":g+=parseInt(k[1],10);break;case "w":case "W":g+=parseInt(k[1],10)*7;break;case "m":case "M":l+=parseInt(k[1],10);g=
Math.min(g,d.datepicker._getDaysInMonth(j,l));break;case "y":case "Y":j+=parseInt(k[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(j,l));break}k=u.exec(h)}return new Date(j,l,g)};if(b=(b=b==null||b===""?c:typeof b=="string"?f(b):typeof b=="number"?isNaN(b)?c:e(b):new Date(b.getTime()))&&b.toString()=="Invalid Date"?c:b){b.setHours(0);b.setMinutes(0);b.setSeconds(0);b.setMilliseconds(0)}return this._daylightSavingAdjust(b)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>
12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&
a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?
new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&n<k?k:n;this._daylightSavingAdjust(new Date(m,g,1))>n;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a));
n=this._canAdjustMonth(a,-1,m,g)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+B+".datepicker._adjustDate('#"+a.id+"', -"+j+", 'M');\" title=\""+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>":f?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>";var s=this._get(a,"nextText");s=!h?s:this.formatDate(s,this._daylightSavingAdjust(new Date(m,
g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+B+".datepicker._adjustDate('#"+a.id+"', +"+j+", 'M');\" title=\""+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>":f?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>";j=this._get(a,"currentText");s=this._get(a,"gotoCurrent")&&
a.currentDay?u:b;j=!h?j:this.formatDate(j,s,this._getFormatConfig(a));h=!a.inline?'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+B+'.datepicker._hideDatepicker();">'+this._get(a,"closeText")+"</button>":"";e=e?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?h:"")+(this._isInRange(a,s)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+
B+".datepicker._gotoToday('#"+a.id+"');\">"+j+"</button>":"")+(c?"":h)+"</div>":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");s=this._get(a,"dayNames");this._get(a,"dayNamesShort");var q=this._get(a,"dayNamesMin"),A=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),D=this._get(a,"showOtherMonths"),K=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var E=this._getDefaultDate(a),w="",x=0;x<i[0];x++){var O=
"";this.maxRows=4;for(var G=0;G<i[1];G++){var P=this._daylightSavingAdjust(new Date(m,g,a.selectedDay)),t=" ui-corner-all",y="";if(l){y+='<div class="ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:y+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:y+=" ui-datepicker-group-middle";t="";break}y+='">'}y+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+t+'">'+(/all|left/.test(t)&&
x==0?c?f:n:"")+(/all|right/.test(t)&&x==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,x>0||G>0,A,v)+'</div><table class="ui-datepicker-calendar"><thead><tr>';var z=j?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(t=0;t<7;t++){var r=(t+h)%7;z+="<th"+((t+h+6)%7>=5?' class="ui-datepicker-week-end"':"")+'><span title="'+s[r]+'">'+q[r]+"</span></th>"}y+=z+"</tr></thead><tbody>";z=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,
z);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;z=Math.ceil((t+z)/7);this.maxRows=z=l?this.maxRows>z?this.maxRows:z:z;r=this._daylightSavingAdjust(new Date(m,g,1-t));for(var Q=0;Q<z;Q++){y+="<tr>";var R=!j?"":'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(r)+"</td>";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[r]):[true,""],F=r.getMonth()!=g,L=F&&!K||!I[0]||k&&r<k||o&&r>o;R+='<td class="'+((t+h+6)%7>=5?" ui-datepicker-week-end":"")+(F?" ui-datepicker-other-month":"")+(r.getTime()==
P.getTime()&&g==a.selectedMonth&&a._keyEvent||E.getTime()==r.getTime()&&E.getTime()==P.getTime()?" "+this._dayOverClass:"")+(L?" "+this._unselectableClass+" ui-state-disabled":"")+(F&&!D?"":" "+I[1]+(r.getTime()==u.getTime()?" "+this._currentClass:"")+(r.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!F||D)&&I[2]?' title="'+I[2]+'"':"")+(L?"":' onclick="DP_jQuery_'+B+".datepicker._selectDay('#"+a.id+"',"+r.getMonth()+","+r.getFullYear()+', this);return false;"')+">"+(F&&!D?"&#xa0;":L?'<span class="ui-state-default">'+
r.getDate()+"</span>":'<a class="ui-state-default'+(r.getTime()==b.getTime()?" ui-state-highlight":"")+(r.getTime()==u.getTime()?" ui-state-active":"")+(F?" ui-priority-secondary":"")+'" href="#">'+r.getDate()+"</a>")+"</td>";r.setDate(r.getDate()+1);r=this._daylightSavingAdjust(r)}y+=R+"</tr>"}g++;if(g>11){g=0;m++}y+="</tbody></table>"+(l?"</div>"+(i[0]>0&&G==i[1]-1?'<div class="ui-datepicker-row-break"></div>':""):"");O+=y}w+=O}w+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':
"");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='<div class="ui-datepicker-title">',o="";if(h||!j)o+='<span class="ui-datepicker-month">'+i[b]+"</span>";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+B+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" >";for(var n=0;n<12;n++)if((!i||n>=e.getMonth())&&
(!m||n<=f.getMonth()))o+='<option value="'+n+'"'+(n==b?' selected="selected"':"")+">"+g[n]+"</option>";o+="</select>"}u||(k+=o+(h||!(j&&l)?"&#xa0;":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+='<span class="ui-datepicker-year">'+c+"</span>";else{g=this._get(a,"yearRange").split(":");var s=(new Date).getFullYear();i=function(q){q=q.match(/c[+-].*/)?c+parseInt(q.substring(1),10):q.match(/[+-].*/)?s+parseInt(q,10):parseInt(q,10);return isNaN(q)?s:q};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,
e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+B+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" >";b<=g;b++)a.yearshtml+='<option value="'+b+'"'+(b==c?' selected="selected"':"")+">"+b+"</option>";a.yearshtml+="</select>";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?"&#xa0;":"")+o;k+="</div>";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c=="Y"?b:0),f=a.drawMonth+
(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&b<c?c:b;return b=a&&b>a?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");if(b)b.apply(a.input?
a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);c=this._daylightSavingAdjust(new Date(c,
e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,
"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=function(a){if(!this.length)return this;
if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));return this.each(function(){typeof a==
"string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.16";window["DP_jQuery_"+B]=d})(jQuery);
;/*
 * jQuery UI Progressbar 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Progressbar
 *
 * Depends:
 *   jquery.ui.core.js
 *   jquery.ui.widget.js
 */
(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow");
this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*
this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.16"})})(jQuery);
;/*
 * jQuery UI Effects 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/
 */
jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1],
16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,
a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d=
a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor",
"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,
0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,
211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,
d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})};
f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,
[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.16",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c,a){var b;switch(c[0]){case "top":b=
0;break;case "middle":b=0.5;break;case "bottom":b=1;break;default:b=c[0]/a.height}switch(c[1]){case "left":c=0;break;case "center":c=0.5;break;case "right":c=1;break;default:c=c[1]/a.width}return{x:c,y:b}},createWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent();var a={width:c.outerWidth(true),height:c.outerHeight(true),"float":c.css("float")},b=f("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),
d=document.activeElement;c.wrap(b);if(c[0]===d||f.contains(c[0],d))f(d).focus();b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(e,g){a[g]=c.css(g);if(isNaN(parseInt(a[g],10)))a[g]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){var a,b=document.activeElement;
if(c.parent().is(".ui-effects-wrapper")){a=c.parent().replaceWith(c);if(c[0]===b||f.contains(c[0],b))f(b).focus();return a}return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});
return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,
arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/
2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,
d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,
a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,
d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g))+b},easeOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*a)*Math.sin((a*e-c)*2*Math.PI/g)+d+b},easeInOutElastic:function(c,a,b,d,e){c=1.70158;var g=
0,h=d;if(a==0)return b;if((a/=e/2)==2)return b+d;g||(g=e*0.3*1.5);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);if(a<1)return-0.5*h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)+b;return h*Math.pow(2,-10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)*0.5+d+b},easeInBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*(a/=e)*a*((g+1)*a-g)+b},easeOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*((a=a/e-1)*a*((g+1)*a+g)+1)+b},easeInOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;
if((a/=e/2)<1)return d/2*a*a*(((g*=1.525)+1)*a-g)+b;return d/2*((a-=2)*a*(((g*=1.525)+1)*a+g)+2)+b},easeInBounce:function(c,a,b,d,e){return d-f.easing.easeOutBounce(c,e-a,0,d,e)+b},easeOutBounce:function(c,a,b,d,e){return(a/=e)<1/2.75?d*7.5625*a*a+b:a<2/2.75?d*(7.5625*(a-=1.5/2.75)*a+0.75)+b:a<2.5/2.75?d*(7.5625*(a-=2.25/2.75)*a+0.9375)+b:d*(7.5625*(a-=2.625/2.75)*a+0.984375)+b},easeInOutBounce:function(c,a,b,d,e){if(a<e/2)return f.easing.easeInBounce(c,a*2,0,d,e)*0.5+b;return f.easing.easeOutBounce(c,
a*2-e,0,d,e)*0.5+d*0.5+b}})}(jQuery);
;/*
 * jQuery UI Effects Blind 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Blind
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(b){b.effects.blind=function(c){return this.queue(function(){var a=b(this),g=["position","top","bottom","left","right"],f=b.effects.setMode(a,c.options.mode||"hide"),d=c.options.direction||"vertical";b.effects.save(a,g);a.show();var e=b.effects.createWrapper(a).css({overflow:"hidden"}),h=d=="vertical"?"height":"width";d=d=="vertical"?e.height():e.width();f=="show"&&e.css(h,0);var i={};i[h]=f=="show"?d:0;e.animate(i,c.duration,c.options.easing,function(){f=="hide"&&a.hide();b.effects.restore(a,
g);b.effects.removeWrapper(a);c.callback&&c.callback.apply(a[0],arguments);a.dequeue()})})}})(jQuery);
;/*
 * jQuery UI Effects Bounce 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Bounce
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(e){e.effects.bounce=function(b){return this.queue(function(){var a=e(this),l=["position","top","bottom","left","right"],h=e.effects.setMode(a,b.options.mode||"effect"),d=b.options.direction||"up",c=b.options.distance||20,m=b.options.times||5,i=b.duration||250;/show|hide/.test(h)&&l.push("opacity");e.effects.save(a,l);a.show();e.effects.createWrapper(a);var f=d=="up"||d=="down"?"top":"left";d=d=="up"||d=="left"?"pos":"neg";c=b.options.distance||(f=="top"?a.outerHeight({margin:true})/3:a.outerWidth({margin:true})/
3);if(h=="show")a.css("opacity",0).css(f,d=="pos"?-c:c);if(h=="hide")c/=m*2;h!="hide"&&m--;if(h=="show"){var g={opacity:1};g[f]=(d=="pos"?"+=":"-=")+c;a.animate(g,i/2,b.options.easing);c/=2;m--}for(g=0;g<m;g++){var j={},k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing);c=h=="hide"?c*2:c/2}if(h=="hide"){g={opacity:0};g[f]=(d=="pos"?"-=":"+=")+c;a.animate(g,i/2,b.options.easing,function(){a.hide();e.effects.restore(a,l);e.effects.removeWrapper(a);
b.callback&&b.callback.apply(this,arguments)})}else{j={};k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing,function(){e.effects.restore(a,l);e.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments)})}a.queue("fx",function(){a.dequeue()});a.dequeue()})}})(jQuery);
;/*
 * jQuery UI Effects Clip 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Clip
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(b){b.effects.clip=function(e){return this.queue(function(){var a=b(this),i=["position","top","bottom","left","right","height","width"],f=b.effects.setMode(a,e.options.mode||"hide"),c=e.options.direction||"vertical";b.effects.save(a,i);a.show();var d=b.effects.createWrapper(a).css({overflow:"hidden"});d=a[0].tagName=="IMG"?d:a;var g={size:c=="vertical"?"height":"width",position:c=="vertical"?"top":"left"};c=c=="vertical"?d.height():d.width();if(f=="show"){d.css(g.size,0);d.css(g.position,
c/2)}var h={};h[g.size]=f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h,{queue:false,duration:e.duration,easing:e.options.easing,complete:function(){f=="hide"&&a.hide();b.effects.restore(a,i);b.effects.removeWrapper(a);e.callback&&e.callback.apply(a[0],arguments);a.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Drop 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Drop
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(c){c.effects.drop=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right","opacity"],e=c.effects.setMode(a,d.options.mode||"hide"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a);var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true})/2:a.outerWidth({margin:true})/2);if(e=="show")a.css("opacity",0).css(f,b=="pos"?-g:g);var i={opacity:e==
"show"?1:0};i[f]=(e=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Explode 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Explode
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(j){j.effects.explode=function(a){return this.queue(function(){var c=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3,d=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3;a.options.mode=a.options.mode=="toggle"?j(this).is(":visible")?"hide":"show":a.options.mode;var b=j(this).show().css("visibility","hidden"),g=b.offset();g.top-=parseInt(b.css("marginTop"),10)||0;g.left-=parseInt(b.css("marginLeft"),10)||0;for(var h=b.outerWidth(true),i=b.outerHeight(true),e=0;e<c;e++)for(var f=
0;f<d;f++)b.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+
e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery);
;/*
 * jQuery UI Effects Fade 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Fade
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Fold 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Fold
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],
10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery);
;/*
 * jQuery UI Effects Highlight 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Highlight
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&&
this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Pulsate 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Pulsate
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c<times;c++){b.animate({opacity:animateTo},duration,a.options.easing);animateTo=(animateTo+1)%2}b.animate({opacity:animateTo},duration,
a.options.easing,function(){animateTo==0&&b.hide();a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()}).dequeue()})}})(jQuery);
;/*
 * jQuery UI Effects Scale 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Scale
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(c){c.effects.puff=function(b){return this.queue(function(){var a=c(this),e=c.effects.setMode(a,b.options.mode||"hide"),g=parseInt(b.options.percent,10)||150,h=g/100,i={height:a.height(),width:a.width()};c.extend(b.options,{fade:true,mode:e,percent:e=="hide"?g:100,from:e=="hide"?i:{height:i.height*h,width:i.width*h}});a.effect("scale",b.options,b.duration,b.callback);a.dequeue()})};c.effects.scale=function(b){return this.queue(function(){var a=c(this),e=c.extend(true,{},b.options),g=c.effects.setMode(a,
b.options.mode||"effect"),h=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:g=="hide"?0:100),i=b.options.direction||"both",f=b.options.origin;if(g!="effect"){e.origin=f||["middle","center"];e.restore=true}f={height:a.height(),width:a.width()};a.from=b.options.from||(g=="show"?{height:0,width:0}:f);h={y:i!="horizontal"?h/100:1,x:i!="vertical"?h/100:1};a.to={height:f.height*h.y,width:f.width*h.x};if(b.options.fade){if(g=="show"){a.from.opacity=0;a.to.opacity=1}if(g=="hide"){a.from.opacity=
1;a.to.opacity=0}}e.from=a.from;e.to=a.to;e.mode=g;a.effect("size",e,b.duration,b.callback);a.dequeue()})};c.effects.size=function(b){return this.queue(function(){var a=c(this),e=["position","top","bottom","left","right","width","height","overflow","opacity"],g=["position","top","bottom","left","right","overflow","opacity"],h=["width","height","overflow"],i=["fontSize"],f=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],k=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],
p=c.effects.setMode(a,b.options.mode||"effect"),n=b.options.restore||false,m=b.options.scale||"both",l=b.options.origin,j={height:a.height(),width:a.width()};a.from=b.options.from||j;a.to=b.options.to||j;if(l){l=c.effects.getBaseline(l,j);a.from.top=(j.height-a.from.height)*l.y;a.from.left=(j.width-a.from.width)*l.x;a.to.top=(j.height-a.to.height)*l.y;a.to.left=(j.width-a.to.width)*l.x}var d={from:{y:a.from.height/j.height,x:a.from.width/j.width},to:{y:a.to.height/j.height,x:a.to.width/j.width}};
if(m=="box"||m=="both"){if(d.from.y!=d.to.y){e=e.concat(f);a.from=c.effects.setTransition(a,f,d.from.y,a.from);a.to=c.effects.setTransition(a,f,d.to.y,a.to)}if(d.from.x!=d.to.x){e=e.concat(k);a.from=c.effects.setTransition(a,k,d.from.x,a.from);a.to=c.effects.setTransition(a,k,d.to.x,a.to)}}if(m=="content"||m=="both")if(d.from.y!=d.to.y){e=e.concat(i);a.from=c.effects.setTransition(a,i,d.from.y,a.from);a.to=c.effects.setTransition(a,i,d.to.y,a.to)}c.effects.save(a,n?e:g);a.show();c.effects.createWrapper(a);
a.css("overflow","hidden").css(a.from);if(m=="content"||m=="both"){f=f.concat(["marginTop","marginBottom"]).concat(i);k=k.concat(["marginLeft","marginRight"]);h=e.concat(f).concat(k);a.find("*[width]").each(function(){child=c(this);n&&c.effects.save(child,h);var o={height:child.height(),width:child.width()};child.from={height:o.height*d.from.y,width:o.width*d.from.x};child.to={height:o.height*d.to.y,width:o.width*d.to.x};if(d.from.y!=d.to.y){child.from=c.effects.setTransition(child,f,d.from.y,child.from);
child.to=c.effects.setTransition(child,f,d.to.y,child.to)}if(d.from.x!=d.to.x){child.from=c.effects.setTransition(child,k,d.from.x,child.from);child.to=c.effects.setTransition(child,k,d.to.x,child.to)}child.css(child.from);child.animate(child.to,b.duration,b.options.easing,function(){n&&c.effects.restore(child,h)})})}a.animate(a.to,{queue:false,duration:b.duration,easing:b.options.easing,complete:function(){a.to.opacity===0&&a.css("opacity",a.from.opacity);p=="hide"&&a.hide();c.effects.restore(a,
n?e:g);c.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Shake 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Shake
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(d){d.effects.shake=function(a){return this.queue(function(){var b=d(this),j=["position","top","bottom","left","right"];d.effects.setMode(b,a.options.mode||"effect");var c=a.options.direction||"left",e=a.options.distance||20,l=a.options.times||3,f=a.duration||a.options.duration||140;d.effects.save(b,j);b.show();d.effects.createWrapper(b);var g=c=="up"||c=="down"?"top":"left",h=c=="up"||c=="left"?"pos":"neg";c={};var i={},k={};c[g]=(h=="pos"?"-=":"+=")+e;i[g]=(h=="pos"?"+=":"-=")+e*2;k[g]=
(h=="pos"?"-=":"+=")+e*2;b.animate(c,f,a.options.easing);for(e=1;e<l;e++)b.animate(i,f,a.options.easing).animate(k,f,a.options.easing);b.animate(i,f,a.options.easing).animate(c,f/2,a.options.easing,function(){d.effects.restore(b,j);d.effects.removeWrapper(b);a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()});b.dequeue()})}})(jQuery);
;/*
 * jQuery UI Effects Slide 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Slide
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(c){c.effects.slide=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right"],f=c.effects.setMode(a,d.options.mode||"show"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a).css({overflow:"hidden"});var g=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var e=d.options.distance||(g=="top"?a.outerHeight({margin:true}):a.outerWidth({margin:true}));if(f=="show")a.css(g,b=="pos"?isNaN(e)?"-"+e:-e:e);
var i={};i[g]=(f=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+e;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){f=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
 * jQuery UI Effects Transfer 1.8.16
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Transfer
 *
 * Depends:
 *	jquery.effects.core.js
 */
(function(e){e.effects.transfer=function(a){return this.queue(function(){var b=e(this),c=e(a.options.to),d=c.offset();c={top:d.top,left:d.left,height:c.innerHeight(),width:c.innerWidth()};d=b.offset();var f=e('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments);
b.dequeue()})})}})(jQuery);
;
/*
 * jQuery Address Plugin v1.5
 * http://www.asual.com/jquery/address/
 *
 * Copyright (c) 2009-2010 Rostislav Hristov
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * Date: 2012-11-18 23:51:44 +0200 (Sun, 18 Nov 2012)
 */
(function(c){c.address=function(){var r=function(a){a=c.extend(c.Event(a),function(){for(var b={},f=c.address.parameterNames(),m=0,p=f.length;m<p;m++)b[f[m]]=c.address.parameter(f[m]);return{value:c.address.value(),path:c.address.path(),pathNames:c.address.pathNames(),parameterNames:f,parameters:b,queryString:c.address.queryString()}}.call(c.address));c(c.address).trigger(a);return a},s=function(a){return Array.prototype.slice.call(a)},h=function(){c().bind.apply(c(c.address),Array.prototype.slice.call(arguments));
return c.address},ea=function(){c().unbind.apply(c(c.address),Array.prototype.slice.call(arguments));return c.address},E=function(){return z.pushState&&d.state!==g},U=function(){return("/"+i.pathname.replace(new RegExp(d.state),"")+i.search+(L()?"#"+L():"")).replace(T,"/")},L=function(){var a=i.href.indexOf("#");return a!=-1?t(i.href.substr(a+1),k):""},u=function(){return E()?U():L()},V=function(){return"javascript"},O=function(a){a=a.toString();return(d.strict&&a.substr(0,1)!="/"?"/":"")+a},t=function(a,
b){if(d.crawlable&&b)return(a!==""?"!":"")+a;return a.replace(/^\!/,"")},v=function(a,b){return parseInt(a.css(b),10)},H=function(){if(!x){var a=u();if(decodeURI(e)!=decodeURI(a))if(w&&A<7)i.reload();else{w&&!F&&d.history&&q(M,50);_old=e;e=a;G(k)}}},G=function(a){var b=r(W);a=r(a?X:Y);q(fa,10);if(b.isDefaultPrevented()||a.isDefaultPrevented())ga()},ga=function(){e=_old;if(E())z.popState({},"",d.state.replace(/\/$/,"")+(e===""?"/":e));else{x=n;if(B)if(d.history)i.hash="#"+t(e,n);else i.replace("#"+
t(e,n));else if(e!=u())if(d.history)i.hash="#"+t(e,n);else i.replace("#"+t(e,n));w&&!F&&d.history&&q(M,50);if(B)q(function(){x=k},1);else x=k}},fa=function(){if(d.tracker!=="null"&&d.tracker!==I){var a=c.isFunction(d.tracker)?d.tracker:j[d.tracker],b=(i.pathname+i.search+(c.address&&!E()?c.address.value():"")).replace(/\/\//,"/").replace(/^\/$/,"");if(c.isFunction(a))a(b);else if(c.isFunction(j.urchinTracker))j.urchinTracker(b);else if(j.pageTracker!==g&&c.isFunction(j.pageTracker._trackPageview))j.pageTracker._trackPageview(b);
else j._gaq!==g&&c.isFunction(j._gaq.push)&&j._gaq.push(["_trackPageview",decodeURI(b)])}},M=function(){var a=V()+":"+k+";document.open();document.writeln('<html><head><title>"+o.title.replace(/\'/g,"\\'")+"</title><script>var "+C+' = "'+encodeURIComponent(u()).replace(/\'/g,"\\'")+(o.domain!=i.hostname?'";document.domain="'+o.domain:"")+"\";<\/script></head></html>');document.close();";if(A<7)l.src=a;else l.contentWindow.location.replace(a)},$=function(){if(J&&Z!=-1){var a,b,f=J.substr(Z+1).split("&");
for(a=0;a<f.length;a++){b=f[a].split("=");if(/^(autoUpdate|crawlable|history|strict|wrap)$/.test(b[0]))d[b[0]]=isNaN(b[1])?/^(true|yes)$/i.test(b[1]):parseInt(b[1],10)!==0;if(/^(state|tracker)$/.test(b[0]))d[b[0]]=b[1]}J=I}_old=e;e=u()},ba=function(){if(!aa){aa=n;$();var a=function(){ha.call(this);ia.call(this)},b=c("body").ajaxComplete(a);a();if(d.wrap){c("body > *").wrapAll('<div style="padding:'+(v(b,"marginTop")+v(b,"paddingTop"))+"px "+(v(b,"marginRight")+v(b,"paddingRight"))+"px "+(v(b,"marginBottom")+
v(b,"paddingBottom"))+"px "+(v(b,"marginLeft")+v(b,"paddingLeft"))+'px;" />').parent().wrap('<div id="'+C+'" style="height:100%;overflow:auto;position:relative;'+(B&&!window.statusbar.visible?"resize:both;":"")+'" />');c("html, body").css({height:"100%",margin:0,padding:0,overflow:"hidden"});B&&c('<style type="text/css" />').appendTo("head").text("#"+C+"::-webkit-resizer { background-color: #fff; }")}if(w&&!F){a=o.getElementsByTagName("frameset")[0];l=o.createElement((a?"":"i")+"frame");l.src=V()+
":"+k;if(a){a.insertAdjacentElement("beforeEnd",l);a[a.cols?"cols":"rows"]+=",0";l.noResize=n;l.frameBorder=l.frameSpacing=0}else{l.style.display="none";l.style.width=l.style.height=0;l.tabIndex=-1;o.body.insertAdjacentElement("afterBegin",l)}q(function(){c(l).bind("load",function(){var f=l.contentWindow;_old=e;e=f[C]!==g?f[C]:"";if(e!=u()){G(k);i.hash=t(e,n)}});l.contentWindow[C]===g&&M()},50)}q(function(){r("init");G(k)},1);if(!E())if(w&&A>7||!w&&F)if(j.addEventListener)j.addEventListener(K,H,k);
else j.attachEvent&&j.attachEvent("on"+K,H);else ja(H,50);"state"in window.history&&c(window).trigger("popstate")}},ha=function(){var a,b=c("a"),f=b.size(),m=-1,p=function(){if(++m!=f){a=c(b.get(m));a.is('[rel*="address:"]')&&a.address('[rel*="address:"]');q(p,1)}};q(p,1)},ia=function(){if(d.crawlable){var a=i.pathname.replace(/\/$/,"");c("body").html().indexOf("_escaped_fragment_")!=-1&&c('a[href]:not([href^=http]), a[href*="'+document.domain+'"]').each(function(){var b=c(this).attr("href").replace(/^http:/,
"").replace(new RegExp(a+"/?$"),"");if(b===""||b.indexOf("_escaped_fragment_")!=-1)c(this).attr("href","#"+encodeURI(decodeURIComponent(b.replace(/\/(.*)\?_escaped_fragment_=(.*)$/,"!$2"))))})}},g,I=null,C="jQueryAddress",K="hashchange",W="change",X="internalChange",Y="externalChange",n=true,k=false,d={autoUpdate:n,crawlable:k,history:n,strict:n,wrap:k},D=c.browser,A=parseFloat(D.version),w=!c.support.opacity,B=D.webkit||D.safari,j=function(){try{return top.document!==g&&top.document.title!==g?top:
window}catch(a){return window}}(),o=j.document,z=j.history,i=j.location,ja=setInterval,q=setTimeout,T=/\/{2,9}/g;D=navigator.userAgent;var F="on"+K in j,l,J=c("script:last").attr("src"),Z=J?J.indexOf("?"):-1,P=o.title,x=k,aa=k,ca=n,N=k,e=u();_old=e;if(w){A=parseFloat(D.substr(D.indexOf("MSIE")+4));if(o.documentMode&&o.documentMode!=A)A=o.documentMode!=8?7:8;var da=o.onpropertychange;o.onpropertychange=function(){da&&da.call(o);if(o.title!=P&&o.title.indexOf("#"+u())!=-1)o.title=P}}if(z.navigationMode)z.navigationMode=
"compatible";if(document.readyState=="complete")var ka=setInterval(function(){if(c.address){ba();clearInterval(ka)}},50);else{$();c(ba)}c(window).bind("popstate",function(){if(decodeURI(e)!=decodeURI(u())){_old=e;e=u();G(k)}}).bind("unload",function(){if(j.removeEventListener)j.removeEventListener(K,H,k);else j.detachEvent&&j.detachEvent("on"+K,H)});return{bind:function(){return h.apply(this,s(arguments))},unbind:function(){return ea.apply(this,s(arguments))},init:function(){return h.apply(this,["init"].concat(s(arguments)))},
change:function(){return h.apply(this,[W].concat(s(arguments)))},internalChange:function(){return h.apply(this,[X].concat(s(arguments)))},externalChange:function(){return h.apply(this,[Y].concat(s(arguments)))},baseURL:function(){var a=i.href;if(a.indexOf("#")!=-1)a=a.substr(0,a.indexOf("#"));if(/\/$/.test(a))a=a.substr(0,a.length-1);return a},autoUpdate:function(a){if(a!==g){d.autoUpdate=a;return this}return d.autoUpdate},crawlable:function(a){if(a!==g){d.crawlable=a;return this}return d.crawlable},
history:function(a){if(a!==g){d.history=a;return this}return d.history},state:function(a){if(a!==g){d.state=a;var b=U();if(d.state!==g)if(z.pushState)b.substr(0,3)=="/#/"&&i.replace(d.state.replace(/^\/$/,"")+b.substr(2));else b!="/"&&b.replace(/^\/#/,"")!=L()&&q(function(){i.replace(d.state.replace(/^\/$/,"")+"/#"+b)},1);return this}return d.state},strict:function(a){if(a!==g){d.strict=a;return this}return d.strict},tracker:function(a){if(a!==g){d.tracker=a;return this}return d.tracker},wrap:function(a){if(a!==
g){d.wrap=a;return this}return d.wrap},update:function(){N=n;this.value(e);N=k;return this},title:function(a){if(a!==g){q(function(){P=o.title=a;if(ca&&l&&l.contentWindow&&l.contentWindow.document){l.contentWindow.document.title=a;ca=k}},50);return this}return o.title},value:function(a){if(a!==g){a=O(a);if(a=="/")a="";if(e==a&&!N)return;_old=e;e=a;if(d.autoUpdate||N){G(n);if(E())z[d.history?"pushState":"replaceState"]({},"",d.state.replace(/\/$/,"")+(e===""?"/":e));else{x=n;if(B)if(d.history)i.hash=
"#"+t(e,n);else i.replace("#"+t(e,n));else if(e!=u())if(d.history)i.hash="#"+t(e,n);else i.replace("#"+t(e,n));w&&!F&&d.history&&q(M,50);if(B)q(function(){x=k},1);else x=k}}return this}return O(e)},path:function(a){if(a!==g){var b=this.queryString(),f=this.hash();this.value(a+(b?"?"+b:"")+(f?"#"+f:""));return this}return O(e).split("#")[0].split("?")[0]},pathNames:function(){var a=this.path(),b=a.replace(T,"/").split("/");if(a.substr(0,1)=="/"||a.length===0)b.splice(0,1);a.substr(a.length-1,1)=="/"&&
b.splice(b.length-1,1);return b},queryString:function(a){if(a!==g){var b=this.hash();this.value(this.path()+(a?"?"+a:"")+(b?"#"+b:""));return this}a=e.split("?");return a.slice(1,a.length).join("?").split("#")[0]},parameter:function(a,b,f){var m,p;if(b!==g){var Q=this.parameterNames();p=[];b=b===g||b===I?"":b.toString();for(m=0;m<Q.length;m++){var R=Q[m],y=this.parameter(R);if(typeof y=="string")y=[y];if(R==a)y=b===I||b===""?[]:f?y.concat([b]):[b];for(var S=0;S<y.length;S++)p.push(R+"="+y[S])}c.inArray(a,
Q)==-1&&b!==I&&b!==""&&p.push(a+"="+b);this.queryString(p.join("&"));return this}if(b=this.queryString()){f=[];p=b.split("&");for(m=0;m<p.length;m++){b=p[m].split("=");b[0]==a&&f.push(b.slice(1).join("="))}if(f.length!==0)return f.length!=1?f:f[0]}},parameterNames:function(){var a=this.queryString(),b=[];if(a&&a.indexOf("=")!=-1){a=a.split("&");for(var f=0;f<a.length;f++){var m=a[f].split("=")[0];c.inArray(m,b)==-1&&b.push(m)}}return b},hash:function(a){if(a!==g){this.value(e.split("#")[0]+(a?"#"+
a:""));return this}a=e.split("#");return a.slice(1,a.length).join("#")}}}();c.fn.address=function(r){var s;if(typeof r=="string"){s=r;r=undefined}c(this).attr("address")||c(s?s:this).live("click",function(h){if(h.shiftKey||h.ctrlKey||h.metaKey||h.which==2)return true;if(c(this).is("a")){h.preventDefault();h=r?r.call(this):/address:/.test(c(this).attr("rel"))?c(this).attr("rel").split("address:")[1].split(" ")[0]:c.address.state()!==undefined&&!/^\/?$/.test(c.address.state())?c(this).attr("href").replace(new RegExp("^(.*"+
c.address.state()+"|\\.)"),""):c(this).attr("href").replace(/^(#\!?|\.)/,"");c.address.value(h)}}).live("submit",function(h){if(c(this).is("form")){h.preventDefault();h=c(this).attr("action");h=r?r.call(this):(h.indexOf("?")!=-1?h.replace(/&$/,""):h+"?")+c(this).serialize();c.address.value(h)}}).attr("address",true);return this}})(jQuery);
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; connect-src 'self'">
    <title>Page not found &middot; GitHub Pages</title>
    <style type="text/css" media="screen">
      body {
        background-color: #f1f1f1;
        margin: 0;
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
      }

      .container { margin: 50px auto 40px auto; width: 600px; text-align: center; }

      a { color: #4183c4; text-decoration: none; }
      a:hover { text-decoration: underline; }

      h1 { width: 800px; position:relative; left: -100px; letter-spacing: -1px; line-height: 60px; font-size: 60px; font-weight: 100; margin: 0px 0 50px 0; text-shadow: 0 1px 0 #fff; }
      p { color: rgba(0, 0, 0, 0.5); margin: 20px 0; line-height: 1.6; }

      ul { list-style: none; margin: 25px 0; padding: 0; }
      li { display: table-cell; font-weight: bold; width: 1%; }

      .logo { display: inline-block; margin-top: 35px; }
      .logo-img-2x { display: none; }
      @media
      only screen and (-webkit-min-device-pixel-ratio: 2),
      only screen and (   min--moz-device-pixel-ratio: 2),
      only screen and (     -o-min-device-pixel-ratio: 2/1),
      only screen and (        min-device-pixel-ratio: 2),
      only screen and (                min-resolution: 192dpi),
      only screen and (                min-resolution: 2dppx) {
        .logo-img-1x { display: none; }
        .logo-img-2x { display: inline-block; }
      }

      #suggestions {
        margin-top: 35px;
        color: #ccc;
      }
      #suggestions a {
        color: #666666;
        font-weight: 200;
        font-size: 14px;
        margin: 0 10px;
      }

    </style>
  </head>
  <body>

    <div class="container">

      <h1>404</h1>
      <p><strong>File not found</strong></p>

      <p>
        The site configured at this address does not
        contain the requested file.
      </p>

      <p>
        If this is your site, make sure that the filename case matches the URL.<br>
        For root URLs (like <code>http://example.com/</code>) you must provide an
        <code>index.html</code> file.
      </p>

      <p>
        <a href="https://help.github.com/pages/">Read the full documentation</a>
        for more information about using <strong>GitHub Pages</strong>.
      </p>

      <div id="suggestions">
        <a href="https://status.github.com">GitHub Status</a> &mdash;
        <a href="https://twitter.com/githubstatus">@githubstatus</a>
      </div>

      <a href="/" class="logo logo-img-1x">
        <img width="32" height="32" title="" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFMTZCRDY3REIzRjAxMUUyQUQzREIxQzRENUFFNUM5NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFMTZCRDY3RUIzRjAxMUUyQUQzREIxQzRENUFFNUM5NiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkUxNkJENjdCQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkUxNkJENjdDQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+SM9MCAAAA+5JREFUeNrEV11Ik1EY3s4+ddOp29Q5b0opCgKFsoKoi5Kg6CIhuwi6zLJLoYLopq4qsKKgi4i6CYIoU/q5iDAKs6syoS76IRWtyJ+p7cdt7sf1PGOD+e0c3dygAx/67ZzzPM95/877GYdHRg3ZjMXFxepQKNS6sLCwJxqNNuFpiMfjVs4ZjUa/pmmjeD6VlJS8NpvNT4QQ7mxwjSsJiEQim/1+/9lgMHgIr5ohuxG1WCw9Vqv1clFR0dCqBODElV6v90ogEDjGdYbVjXhpaendioqK07CIR7ZAqE49PT09BPL2PMgTByQGsYiZlQD4uMXtdr+JxWINhgINYhGT2MsKgMrm2dnZXgRXhaHAg5jEJodUAHxux4LudHJE9RdEdA+i3Juz7bGHe4mhE9FNrgwBCLirMFV9Okh5eflFh8PR5nK5nDabrR2BNJlKO0T35+Li4n4+/J+/JQCxhmu5h3uJoXNHPbmWZAHMshWB8l5/ipqammaAf0zPDDx1ONV3vurdidqwAQL+pEc8sLcAe1CCvQ3YHxIW8Pl85xSWNC1hADDIv0rIE/o4J0k3kww4xSlwIhcq3EFFOm7KN/hUGOQkt0CFa5WpNJlMvxBEz/IVQAxg/ZRZl9wiHA63yDYieM7DnLP5CiAGsC7I5sgtYKJGWe2A8seFqgFJrJjEPY1Cn3pJ8/9W1e5VWsFDTEmFrBcoDhZJEQkXuhICMyKpjhahqN21hRYATKfUOlDmkygrR4o4C0VOLGJKrOITKB4jijzdXygBKixyC5TDQdnk/Pz8qRw6oOWGlsTKGOQW6OH6FBWsyePxdOXLTgxiyebILZCjz+GLgMIKnXNzc49YMlcRdHXcSwxFVgTInQhC9G33UhNoJLuqq6t345p9y3eUy8OTk5PjAHuI9uo4b07FBaOhsu0A4Unc+T1TU1Nj3KsSSE5yJ65jqF2DDd8QqWYmAZrIM2VlZTdnZmb6AbpdV9V6ec9znf5Q7HjYumdRE0JOp3MjitO4SFa+cZz8Umqe3TCbSLvdfkR/kWDdNQl5InuTcysOcpFT35ZrbBxx4p3JAHlZVVW1D/634VRt+FvLBgK/v5LV9WS+10xMTEwtRw7XvqOL+e2Q8V3AYIOIAXQ26/heWVnZCVfcyKHg2CBgTpmPmjYM8l24GyaUHyaIh7XwfR9ErE8qHoDfn2LTNAVC0HX6MFcBIP8Bi+6F6cdW/DICkANRfx99fEYFQ7Nph5i/uQiA214gno7K+guhaiKg9gC62+M8eR7XsBsYJ4ilam60Fb7r7uAj8wFyuwM1oIOWgfmDy6RXEEQzJMPe23DXrVS7rtyD3Df8z/FPgAEAzWU5Ku59ZAUAAAAASUVORK5CYII=">
      </a>

      <a href="/" class="logo logo-img-2x">
        <img width="32" height="32" title="" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpEQUM1QkUxRUI0MUMxMUUyQUQzREIxQzRENUFFNUM5NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpEQUM1QkUxRkI0MUMxMUUyQUQzREIxQzRENUFFNUM5NiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkUxNkJENjdGQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkUxNkJENjgwQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+hfPRaQAAB6lJREFUeNrsW2mME2UYbodtt+2222u35QheoCCYGBQligIJgkZJNPzgigoaTEj8AdFEMfADfyABkgWiiWcieK4S+QOiHAYUj2hMNKgYlEujpNttu9vttbvdw+chU1K6M535pt3ubHCSyezR+b73eb73+t7vrfXsufOW4bz6+vom9/b23ovnNNw34b5xYGAgODg46Mbt4mesVmsWd1qSpHhdXd2fuP/Afcput5/A88xwymcdBgLqenp6FuRyuWV4zu/v759QyWBjxoz5t76+/gun09mK5xFyakoCAPSaTCazNpvNPoYVbh6O1YKGRF0u13sNDQ27QMzfpiAAKj0lnU6/gBVfAZW2WWpwwVzy0IgP3G73FpjI6REhAGA9qVRqA1b9mVoBVyIC2tDi8Xg24+dUzQiAbS/s7Ox8G2o/3mKCC+Zw0efzPQEfcVjYrARX3dbV1bUtHo8fMgt42f+Mp0yUTVQbdWsAHVsikdiHkHaPxcQXQufXgUBgMRxme9U0AAxfH4vFvjM7eF6UkbJS5qoQwEQGA57Ac5JllFyUVZZ5ckUEgMVxsK2jlSYzI+QXJsiyjzNEAJyJAzb/KQa41jJKL8pODMQiTEAymXw5n8/P0IjD3bh7Rgog59aanxiIRTVvV/oj0tnHca/WMrVwODwB3raTGxzkBg/gnZVapFV62Wy2n5AO70HM/5wbJ0QnXyQSaVPDIuNZzY0V3ntHMwxiwHA0Gj2Np7ecIBDgaDAYXKCQJM1DhrgJ3nhulcPbl8j4NmHe46X/g60fwbz3aewjkqFQaAqebWU1AOqyQwt8Id6qEHMc97zu7u7FGGsn7HAiVuosVw7P35C1nccdgSCxop1dHeZswmfHMnxBo6ZTk+jN8dl/vF7vWofDsa+MLN9oEUBMxOb3+1eoEsBVw6Zmua49r8YmhAKDiEPcMwBsxMiqQ+ixzPFxZyqRpXARG/YOr1ObFJ0gUskXBbamcR1OKmMUvDxHRAu8/LmY3jFLMUpFqz9HxG65smYJdyKyECOxDiEAe/p1gjF2oonivZAsxVgl2daa4EQWCW6J55qFAFFZiJWYLxNQy2qOSUzGRsyXCUDIeliwAHEO4WSlWQBRFoZakXcKmCXmyXAKs0Ve9vl8q42WoIYpJU4hV3hKcNs8m9gl7p/xQ73eF5kB4j5mNrWmTJRNwAzqiV1CxjVTZCIkEq+Z1bZFZSN2CenmVAFVy4Plz8xKAGWjjAKFk6lCBMDR/MJjLLMSQNm43xAiQKTaA+9/wewhDjL+JVI1kkTSSOTcKbMTwPqESAot6dn6Fr1gHwVJju6IRuyiByPuUUBAg5DGkAgBmxlvdgIEK9gDkohdY/BJo4CAG0R8miRSsGABkgVQs4KXu098IgUXSSRsFAoKZiVAVDY2WUiiPTjYRi41KwGisrGsLtlsth8Fiwnz2fBkQvWfRtlE3iF2yW63/yCacXZ1dW02GwGyTFaRd4idJnCKHRaCxYRHoG5LTKT6SyiToP1fJHbmAYPYRR0UnZQtMnA6s0zg+GZBlt0Gdo7EPHgpE3Q6nZ8YyLhc8Xj8MJh/aKTAY+5FPAKHLE7RdwuYJZmNwzyCMkBCYyKROJBMJl9B/PXXCjjmCmDOVzH3fiPpObEWGqoKe4EBl8v1hlqsdLvd23mkxHM9pc9kMpmno9HoeTii7ewbHEZPPx1ztLS1tV3AnGuMjiNjvbQFuHw6zDo5By7dTPAQNBgMLrRarTkSls1mnwT7uwp9virx9QzbW/HuV/j5d/b+6jniKlllP8lkeONJDk+dq9GsQTnC4fB1heO0K47Hwe7WdDr9nAKgXwOBwHI+C45Htj1d6sd429TUNEcmUdc+PRaLHcvn87dXW4ugzdsaGxufL94NFv9zi1J7GVbhlvb2dnaJ3SVrxfc+n2+NTsZ7/H7/Mr3g5XdSIHyJSH1PZ+7fToyl2+ErqilgZ4NaLYB9goVGaHjR93Hv1ZrU4XDsFT20kH3PObzbWk0CgG1jacVIUnAQb9F+VexyLMzkpcLv0IJV7AHQIOCAUYHx7v5qgScmYHtTqSAyZLEJTK22Bie4iq3xsqpm4SAf9Hq9a2DnJ4uLK3SEULcdRvp3i3zHySqpficxEdsQc1NrlYXXvR+O7qASSezXB+h1SuUomgg9LL8BUoV4749EIolKh+EiqWmqVEZlDgHks2pxHw7xTqUQw9J5NcAXOK10AGIoZ6Zli6JY6Z1Q461KoZ4NiKLHarW+KDsxlDUPHZ5zPQZqUVDPJsTqb5n9malbpAh8C2XXDLl62+WZIDFRUlNVOiwencnNU3aQEkL+cDMSoLvZo2fQB7AJssNAuFuvorlDVVkkg2I87+jo2K2QAVphDrfyViK5VqtO34OkaxXCp+7drdDBCAdubm6eidX+2WwqT5komwh4YQLk+H4aE93h8Xg2gvHekQZOGSgLZTLyDTLJ4Lx9/KZWKBSainT4Iy3FqQBfnUZR42PKQFksBr9QKVXCPusD3OiA/RkQ5kP8qV/Jl1WywAp/6+dcmPM2zL1UrUahe4JqfnWWKXIul3uUbfP8njAFLW1OFr3gdFtZ72cNH+PtQT7/brW+NXqJAHh0y9V8/U/A1U7AfwIMAD7mS3pCbuWJAAAAAElFTkSuQmCC">
      </a>
    </div>
  </body>
</html>
/**
 * Timeago is a jQuery plugin that makes it easy to support automatically
 * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
 *
 * @name timeago
 * @version 1.5.2
 * @requires jQuery v1.2.3+
 * @author Ryan McGeary
 * @license MIT License - http://www.opensource.org/licenses/mit-license.php
 *
 * For usage and examples, visit:
 * http://timeago.yarp.com/
 *
 * Copyright (c) 2008-2015, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
 */

(function (factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else if (typeof module === 'object' && typeof module.exports === 'object') {
    factory(require('jquery'));
  } else {
    // Browser globals
    factory(jQuery);
  }
}(function ($) {
  $.timeago = function(timestamp) {
    if (timestamp instanceof Date) {
      return inWords(timestamp);
    } else if (typeof timestamp === "string") {
      return inWords($.timeago.parse(timestamp));
    } else if (typeof timestamp === "number") {
      return inWords(new Date(timestamp));
    } else {
      return inWords($.timeago.datetime(timestamp));
    }
  };
  var $t = $.timeago;

  $.extend($.timeago, {
    settings: {
      refreshMillis: 60000,
      allowPast: true,
      allowFuture: false,
      localeTitle: false,
      cutoff: 0,
      autoDispose: true,
      strings: {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: "ago",
        suffixFromNow: "from now",
        inPast: 'any moment now',
        seconds: "less than a minute",
        minute: "about a minute",
        minutes: "%d minutes",
        hour: "about an hour",
        hours: "about %d hours",
        day: "a day",
        days: "%d days",
        month: "about a month",
        months: "%d months",
        year: "about a year",
        years: "%d years",
        wordSeparator: " ",
        numbers: []
      }
    },

    inWords: function(distanceMillis) {
      if (!this.settings.allowPast && ! this.settings.allowFuture) {
          throw 'timeago allowPast and allowFuture settings can not both be set to false.';
      }

      var $l = this.settings.strings;
      var prefix = $l.prefixAgo;
      var suffix = $l.suffixAgo;
      if (this.settings.allowFuture) {
        if (distanceMillis < 0) {
          prefix = $l.prefixFromNow;
          suffix = $l.suffixFromNow;
        }
      }

      if (!this.settings.allowPast && distanceMillis >= 0) {
        return this.settings.strings.inPast;
      }

      var seconds = Math.abs(distanceMillis) / 1000;
      var minutes = seconds / 60;
      var hours = minutes / 60;
      var days = hours / 24;
      var years = days / 365;

      function substitute(stringOrFunction, number) {
        var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
        var value = ($l.numbers && $l.numbers[number]) || number;
        return string.replace(/%d/i, value);
      }

      var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
        seconds < 90 && substitute($l.minute, 1) ||
        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
        minutes < 90 && substitute($l.hour, 1) ||
        hours < 24 && substitute($l.hours, Math.round(hours)) ||
        hours < 42 && substitute($l.day, 1) ||
        days < 30 && substitute($l.days, Math.round(days)) ||
        days < 45 && substitute($l.month, 1) ||
        days < 365 && substitute($l.months, Math.round(days / 30)) ||
        years < 1.5 && substitute($l.year, 1) ||
        substitute($l.years, Math.round(years));

      var separator = $l.wordSeparator || "";
      if ($l.wordSeparator === undefined) { separator = " "; }
      return $.trim([prefix, words, suffix].join(separator));
    },

    parse: function(iso8601) {
      var s = $.trim(iso8601);
      s = s.replace(/\.\d+/,""); // remove milliseconds
      s = s.replace(/-/,"/").replace(/-/,"/");
      s = s.replace(/T/," ").replace(/Z/," UTC");
      s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
      s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
      return new Date(s);
    },
    datetime: function(elem) {
      var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
      return $t.parse(iso8601);
    },
    isTime: function(elem) {
      // jQuery's `is()` doesn't play well with HTML5 in IE
      return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
    }
  });

  // functions that can be called via $(el).timeago('action')
  // init is default when no action is given
  // functions are called with context of a single element
  var functions = {
    init: function() {
      var refresh_el = $.proxy(refresh, this);
      refresh_el();
      var $s = $t.settings;
      if ($s.refreshMillis > 0) {
        this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
      }
    },
    update: function(timestamp) {
      var date = (timestamp instanceof Date) ? timestamp : $t.parse(timestamp);
      $(this).data('timeago', { datetime: date });
      if ($t.settings.localeTitle) $(this).attr("title", date.toLocaleString());
      refresh.apply(this);
    },
    updateFromDOM: function() {
      $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
      refresh.apply(this);
    },
    dispose: function () {
      if (this._timeagoInterval) {
        window.clearInterval(this._timeagoInterval);
        this._timeagoInterval = null;
      }
    }
  };

  $.fn.timeago = function(action, options) {
    var fn = action ? functions[action] : functions.init;
    if (!fn) {
      throw new Error("Unknown function name '"+ action +"' for timeago");
    }
    // each over objects here and call the requested function
    this.each(function() {
      fn.call(this, options);
    });
    return this;
  };

  function refresh() {
    var $s = $t.settings;

    //check if it's still visible
    if ($s.autoDispose && !$.contains(document.documentElement,this)) {
      //stop if it has been removed
      $(this).timeago("dispose");
      return this;
    }

    var data = prepareData(this);

    if (!isNaN(data.datetime)) {
      if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
        $(this).text(inWords(data.datetime));
      }
    }
    return this;
  }

  function prepareData(element) {
    element = $(element);
    if (!element.data("timeago")) {
      element.data("timeago", { datetime: $t.datetime(element) });
      var text = $.trim(element.text());
      if ($t.settings.localeTitle) {
        element.attr("title", element.data('timeago').datetime.toLocaleString());
      } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
        element.attr("title", text);
      }
    }
    return element.data("timeago");
  }

  function inWords(date) {
    return $t.inWords(distance(date));
  }

  function distance(date) {
    return (new Date().getTime() - date.getTime());
  }

  // fix for IE6 suckage
  document.createElement("abbr");
  document.createElement("time");
}));
/*! jQuery v1.7.1 jquery.com | jquery.org/license */
(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function cb(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function ca(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bE.test(a)?d(a,e):ca(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)ca(a+"["+e+"]",b[e],c,d);else d(a,b)}function b_(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bT,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bP),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bC(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bx:by,g=0,h=e.length;if(d>0){if(c!=="border")for(;g<h;g++)c||(d-=parseFloat(f.css(a,"padding"+e[g]))||0),c==="margin"?d+=parseFloat(f.css(a,c+e[g]))||0:d-=parseFloat(f.css(a,"border"+e[g]+"Width"))||0;return d+"px"}d=bz(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0;if(c)for(;g<h;g++)d+=parseFloat(f.css(a,"padding"+e[g]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+e[g]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+e[g]))||0);return d+"px"}function bp(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c+(i[c][d].namespace?".":"")+i[c][d].namespace,i[c][d],i[c][d].data)}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test("Â ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?m(g):h==="function"&&(!a.unique||!o.has(g))&&c.push(g)},n=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,l=j||0,j=0,k=c.length;for(;c&&l<k;l++)if(c[l].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}i=!1,c&&(a.once?e===!0?o.disable():c=[]:d&&d.length&&(e=d.shift(),o.fireWith(e[0],e[1])))},o={add:function(){if(c){var a=c.length;m(arguments),i?k=c.length:e&&e!==!0&&(j=a,n(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){i&&f<=k&&(k--,f<=l&&l--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&o.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(i?a.once||d.push([b,c]):(!a.once||!e)&&n(b,c));return this},fire:function(){o.fireWith(this,arguments);return this},fired:function(){return!!e}};return o};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p,q=c.createElement("div"),r=c.documentElement;q.setAttribute("className","t"),q.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="<div "+n+"><div></div></div>"+"<table "+n+" cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="<div style='width:4px;'></div>",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h=null;if(typeof a=="undefined"){if(this.length){h=f.data(this[0]);if(this[0].nodeType===1&&!f._data(this[0],"parsedAttrs")){e=this[0].attributes;for(var i=0,j=e.length;i<j;i++)g=e[i].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),l(this[0],g,h[g]));f._data(this[0],"parsedAttrs",!0)}}return h}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split("."),d[1]=d[1]?"."+d[1]:"";if(c===b){h=this.triggerHandler("getData"+d[1]+"!",[d[0]]),h===b&&this.length&&(h=f.data(this[0],a),h=l(this[0],a,h));return h===b&&d[1]?this.data(d[0]):h}return this.each(function(){var b=f(this),e=[d[0],c];b.triggerHandler("setData"+d[1]+"!",e),f.data(this,a,c),b.triggerHandler("changeData"+d[1]+"!",e)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise()}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h<g;h++)e=d[h],e&&(c=f.propFix[e]||e,f.attr(a,e,""),a.removeAttribute(v?e:c),u.test(e)&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};
f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=[],j,k,l,m,n,o,p,q,r,s,t;g[0]=c,c.delegateTarget=this;if(e&&!c.target.disabled&&(!c.button||c.type!=="click")){m=f(this),m.context=this.ownerDocument||this;for(l=c.target;l!=this;l=l.parentNode||this){o={},q=[],m[0]=l;for(j=0;j<e;j++)r=d[j],s=r.selector,o[s]===b&&(o[s]=r.quick?H(l,r.quick):m.is(s)),o[s]&&q.push(r);q.length&&i.push({elem:l,matches:q})}}d.length>e&&i.push({elem:this,matches:d.slice(e)});for(j=0;j<i.length&&!c.isPropagationStopped();j++){p=i[j],c.currentTarget=p.elem;for(k=0;k<p.matches.length&&!c.isImmediatePropagationStopped();k++){r=p.matches[k];if(h||!c.namespace&&!r.namespace||c.namespace_re&&c.namespace_re.test(r.namespace))c.data=r.data,c.handleObj=r,n=((f.event.special[r.origType]||{}).handle||r.handler).apply(p.elem,g),n!==b&&(c.result=n,n===!1&&(c.preventDefault(),c.stopPropagation()))}}return c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0)}),d._submit_attached=!0)})},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on.call(this,a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.type+"."+e.namespace:e.type,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.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(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.POS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function()
{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bp)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bn(k[i]);else bn(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bq=/alpha\([^)]*\)/i,br=/opacity=([^)]*)/,bs=/([A-Z]|^ms)/g,bt=/^-?\d+(?:px)?$/i,bu=/^-?\d/,bv=/^([\-+])=([\-+.\de]+)/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bv.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bC(a,b,d);f.swap(a,bw,function(){e=bC(a,b,d)});return e}},set:function(a,b){if(!bt.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cv(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cu("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cu("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cv(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cn.test(h)?(o=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),o?(f._data(this,"toggle"+i,o==="show"?"hide":"show"),j[o]()):j[h]()):(k=co.exec(h),l=j.cur(),k?(m=parseFloat(k[2]),n=k[3]||(f.cssNumber[i]?"":"px"),n!=="px"&&(f.style(this,i,(m||1)+n),l=(m||1)/j.cur()*l,f.style(this,i,l+n)),k[1]&&(m=(k[1]==="-="?-1:1)*m+l),j.custom(l,m,n)):j.custom(l,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:cu("show",1),slideUp:cu("hide",1),slideToggle:cu("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cr||cs(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){e.options.hide&&f._data(e.elem,"fxshow"+e.prop)===b&&f._data(e.elem,"fxshow"+e.prop,e.start)},h()&&f.timers.push(h)&&!cp&&(cp=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cr||cs(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cp),cp=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(["width","height"],function(a,b){f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cy(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.support.fixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.support.fixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
/* http://keith-wood.name/svg.html
   SVG for jQuery v1.4.3.
   Written by Keith Wood (kbwood{at}iinet.com.au) August 2007.
   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
   Please attribute the author if you use it. */
(function($){function SVGManager(){this._settings=[];this._extensions=[];this.regional=[];this.regional['']={errorLoadingText:'Error loading',notSupportedText:'This browser does not support SVG'};this.local=this.regional[''];this._uuid=new Date().getTime();this._renesis=detectActiveX('RenesisX.RenesisCtrl')}function detectActiveX(a){try{return!!(window.ActiveXObject&&new ActiveXObject(a))}catch(e){return false}}var p='svgwrapper';$.extend(SVGManager.prototype,{markerClassName:'hasSVG',svgNS:'http://www.w3.org/2000/svg',xlinkNS:'http://www.w3.org/1999/xlink',_wrapperClass:SVGWrapper,_attrNames:{class_:'class',in_:'in',alignmentBaseline:'alignment-baseline',baselineShift:'baseline-shift',clipPath:'clip-path',clipRule:'clip-rule',colorInterpolation:'color-interpolation',colorInterpolationFilters:'color-interpolation-filters',colorRendering:'color-rendering',dominantBaseline:'dominant-baseline',enableBackground:'enable-background',fillOpacity:'fill-opacity',fillRule:'fill-rule',floodColor:'flood-color',floodOpacity:'flood-opacity',fontFamily:'font-family',fontSize:'font-size',fontSizeAdjust:'font-size-adjust',fontStretch:'font-stretch',fontStyle:'font-style',fontVariant:'font-variant',fontWeight:'font-weight',glyphOrientationHorizontal:'glyph-orientation-horizontal',glyphOrientationVertical:'glyph-orientation-vertical',horizAdvX:'horiz-adv-x',horizOriginX:'horiz-origin-x',imageRendering:'image-rendering',letterSpacing:'letter-spacing',lightingColor:'lighting-color',markerEnd:'marker-end',markerMid:'marker-mid',markerStart:'marker-start',stopColor:'stop-color',stopOpacity:'stop-opacity',strikethroughPosition:'strikethrough-position',strikethroughThickness:'strikethrough-thickness',strokeDashArray:'stroke-dasharray',strokeDashOffset:'stroke-dashoffset',strokeLineCap:'stroke-linecap',strokeLineJoin:'stroke-linejoin',strokeMiterLimit:'stroke-miterlimit',strokeOpacity:'stroke-opacity',strokeWidth:'stroke-width',textAnchor:'text-anchor',textDecoration:'text-decoration',textRendering:'text-rendering',underlinePosition:'underline-position',underlineThickness:'underline-thickness',vertAdvY:'vert-adv-y',vertOriginY:'vert-origin-y',wordSpacing:'word-spacing',writingMode:'writing-mode'},_attachSVG:function(a,b){var c=(a.namespaceURI==this.svgNS?a:null);var a=(c?null:a);if($(a||c).hasClass(this.markerClassName)){return}if(typeof b=='string'){b={loadURL:b}}else if(typeof b=='function'){b={onLoad:b}}$(a||c).addClass(this.markerClassName);try{if(!c){c=document.createElementNS(this.svgNS,'svg');c.setAttribute('version','1.1');c.setAttribute('width',a.clientWidth);c.setAttribute('height',a.clientHeight);a.appendChild(c)}this._afterLoad(a,c,b||{})}catch(e){if($.browser.msie){if(!a.id){a.id='svg'+(this._uuid++)}this._settings[a.id]=b;a.innerHTML='<embed type="image/svg+xml" width="100%" '+'height="100%" src="'+(b.initPath||'')+'blank.svg"/>'}else{a.innerHTML='<p class="svg_error">'+this.local.notSupportedText+'</p>'}}},_registerSVG:function(){for(var i=0;i<document.embeds.length;i++){var a=document.embeds[i].parentNode;if(!$(a).hasClass($.svg.markerClassName)||$.data(a,p)){continue}var b=null;try{b=document.embeds[i].getSVGDocument()}catch(e){setTimeout($.svg._registerSVG,250);return}b=(b?b.documentElement:null);if(b){$.svg._afterLoad(a,b)}}},_afterLoad:function(a,b,c){var c=c||this._settings[a.id];this._settings[a?a.id:'']=null;var d=new this._wrapperClass(b,a);$.data(a||b,p,d);try{if(c.loadURL){d.load(c.loadURL,c)}if(c.settings){d.configure(c.settings)}if(c.onLoad&&!c.loadURL){c.onLoad.apply(a||b,[d])}}catch(e){alert(e)}},_getSVG:function(a){a=(typeof a=='string'?$(a)[0]:(a.jquery?a[0]:a));return $.data(a,p)},_destroySVG:function(a){var b=$(a);if(!b.hasClass(this.markerClassName)){return}b.removeClass(this.markerClassName);if(a.namespaceURI!=this.svgNS){b.empty()}$.removeData(a,p)},addExtension:function(a,b){this._extensions.push([a,b])}});function SVGWrapper(a,b){this._svg=a;this._container=b;for(var i=0;i<$.svg._extensions.length;i++){var c=$.svg._extensions[i];this[c[0]]=new c[1](this)}}$.extend(SVGWrapper.prototype,{_width:function(){return(this._container?this._container.clientWidth:this._svg.width)},_height:function(){return(this._container?this._container.clientHeight:this._svg.height)},root:function(){return this._svg},configure:function(a,b){if(b){for(var i=this._svg.attributes.length-1;i>=0;i--){var c=this._svg.attributes.item(i);if(!(c.nodeName=='onload'||c.nodeName=='version'||c.nodeName.substring(0,5)=='xmlns')){this._svg.attributes.removeNamedItem(c.nodeName)}}}for(var d in a){this._svg.setAttribute(d,a[d])}return this},getElementById:function(a){return this._svg.ownerDocument.getElementById(a)},change:function(a,b){if(a){for(var c in b){if(b[c]==null){a.removeAttribute(c)}else{a.setAttribute(c,b[c])}}}return this},_args:function(b,c,d){c.splice(0,0,'parent');c.splice(c.length,0,'settings');var e={};var f=0;if(b[0]!=null&&b[0].jquery){b[0]=b[0][0]}if(b[0]!=null&&!(typeof b[0]=='object'&&b[0].nodeName)){e['parent']=null;f=1}for(var i=0;i<b.length;i++){e[c[i+f]]=b[i]}if(d){$.each(d,function(i,a){if(typeof e[a]=='object'){e.settings=e[a];e[a]=null}})}return e},title:function(a,b,c){var d=this._args(arguments,['text']);var e=this._makeNode(d.parent,'title',d.settings||{});e.appendChild(this._svg.ownerDocument.createTextNode(d.text));return e},describe:function(a,b,c){var d=this._args(arguments,['text']);var e=this._makeNode(d.parent,'desc',d.settings||{});e.appendChild(this._svg.ownerDocument.createTextNode(d.text));return e},defs:function(a,b,c){var d=this._args(arguments,['id'],['id']);return this._makeNode(d.parent,'defs',$.extend((d.id?{id:d.id}:{}),d.settings||{}))},symbol:function(a,b,c,d,e,f,g){var h=this._args(arguments,['id','x1','y1','width','height']);return this._makeNode(h.parent,'symbol',$.extend({id:h.id,viewBox:h.x1+' '+h.y1+' '+h.width+' '+h.height},h.settings||{}))},marker:function(a,b,c,d,e,f,g,h){var i=this._args(arguments,['id','refX','refY','mWidth','mHeight','orient'],['orient']);return this._makeNode(i.parent,'marker',$.extend({id:i.id,refX:i.refX,refY:i.refY,markerWidth:i.mWidth,markerHeight:i.mHeight,orient:i.orient||'auto'},i.settings||{}))},style:function(a,b,c){var d=this._args(arguments,['styles']);var e=this._makeNode(d.parent,'style',$.extend({type:'text/css'},d.settings||{}));e.appendChild(this._svg.ownerDocument.createTextNode(d.styles));if($.browser.opera){$('head').append('<style type="text/css">'+d.styles+'</style>')}return e},script:function(a,b,c,d){var e=this._args(arguments,['script','type'],['type']);var f=this._makeNode(e.parent,'script',$.extend({type:e.type||'text/javascript'},e.settings||{}));f.appendChild(this._svg.ownerDocument.createTextNode(this._escapeXML(e.script)));if(!$.browser.mozilla){$.globalEval(e.script)}return f},linearGradient:function(a,b,c,d,e,f,g,h){var i=this._args(arguments,['id','stops','x1','y1','x2','y2'],['x1']);var j=$.extend({id:i.id},(i.x1!=null?{x1:i.x1,y1:i.y1,x2:i.x2,y2:i.y2}:{}));return this._gradient(i.parent,'linearGradient',$.extend(j,i.settings||{}),i.stops)},radialGradient:function(a,b,c,d,e,r,f,g,h){var i=this._args(arguments,['id','stops','cx','cy','r','fx','fy'],['cx']);var j=$.extend({id:i.id},(i.cx!=null?{cx:i.cx,cy:i.cy,r:i.r,fx:i.fx,fy:i.fy}:{}));return this._gradient(i.parent,'radialGradient',$.extend(j,i.settings||{}),i.stops)},_gradient:function(a,b,c,d){var e=this._makeNode(a,b,c);for(var i=0;i<d.length;i++){var f=d[i];this._makeNode(e,'stop',$.extend({offset:f[0],stopColor:f[1]},(f[2]!=null?{stopOpacity:f[2]}:{})))}return e},pattern:function(a,b,x,y,c,d,e,f,g,h,i){var j=this._args(arguments,['id','x','y','width','height','vx','vy','vwidth','vheight'],['vx']);var k=$.extend({id:j.id,x:j.x,y:j.y,width:j.width,height:j.height},(j.vx!=null?{viewBox:j.vx+' '+j.vy+' '+j.vwidth+' '+j.vheight}:{}));return this._makeNode(j.parent,'pattern',$.extend(k,j.settings||{}))},mask:function(a,b,x,y,c,d,e){var f=this._args(arguments,['id','x','y','width','height']);return this._makeNode(f.parent,'mask',$.extend({id:f.id,x:f.x,y:f.y,width:f.width,height:f.height},f.settings||{}))},createPath:function(){return new SVGPath()},createText:function(){return new SVGText()},svg:function(a,x,y,b,c,d,e,f,g,h){var i=this._args(arguments,['x','y','width','height','vx','vy','vwidth','vheight'],['vx']);var j=$.extend({x:i.x,y:i.y,width:i.width,height:i.height},(i.vx!=null?{viewBox:i.vx+' '+i.vy+' '+i.vwidth+' '+i.vheight}:{}));return this._makeNode(i.parent,'svg',$.extend(j,i.settings||{}))},group:function(a,b,c){var d=this._args(arguments,['id'],['id']);return this._makeNode(d.parent,'g',$.extend({id:d.id},d.settings||{}))},use:function(a,x,y,b,c,d,e){var f=this._args(arguments,['x','y','width','height','ref']);if(typeof f.x=='string'){f.ref=f.x;f.settings=f.y;f.x=f.y=f.width=f.height=null}var g=this._makeNode(f.parent,'use',$.extend({x:f.x,y:f.y,width:f.width,height:f.height},f.settings||{}));g.setAttributeNS($.svg.xlinkNS,'href',f.ref);return g},link:function(a,b,c){var d=this._args(arguments,['ref']);var e=this._makeNode(d.parent,'a',d.settings);e.setAttributeNS($.svg.xlinkNS,'href',d.ref);return e},image:function(a,x,y,b,c,d,e){var f=this._args(arguments,['x','y','width','height','ref']);var g=this._makeNode(f.parent,'image',$.extend({x:f.x,y:f.y,width:f.width,height:f.height},f.settings||{}));g.setAttributeNS($.svg.xlinkNS,'href',f.ref);return g},path:function(a,b,c){var d=this._args(arguments,['path']);return this._makeNode(d.parent,'path',$.extend({d:(d.path.path?d.path.path():d.path)},d.settings||{}))},rect:function(a,x,y,b,c,d,e,f){var g=this._args(arguments,['x','y','width','height','rx','ry'],['rx']);return this._makeNode(g.parent,'rect',$.extend({x:g.x,y:g.y,width:g.width,height:g.height},(g.rx?{rx:g.rx,ry:g.ry}:{}),g.settings||{}))},circle:function(a,b,c,r,d){var e=this._args(arguments,['cx','cy','r']);return this._makeNode(e.parent,'circle',$.extend({cx:e.cx,cy:e.cy,r:e.r},e.settings||{}))},ellipse:function(a,b,c,d,e,f){var g=this._args(arguments,['cx','cy','rx','ry']);return this._makeNode(g.parent,'ellipse',$.extend({cx:g.cx,cy:g.cy,rx:g.rx,ry:g.ry},g.settings||{}))},line:function(a,b,c,d,e,f){var g=this._args(arguments,['x1','y1','x2','y2']);return this._makeNode(g.parent,'line',$.extend({x1:g.x1,y1:g.y1,x2:g.x2,y2:g.y2},g.settings||{}))},polyline:function(a,b,c){var d=this._args(arguments,['points']);return this._poly(d.parent,'polyline',d.points,d.settings)},polygon:function(a,b,c){var d=this._args(arguments,['points']);return this._poly(d.parent,'polygon',d.points,d.settings)},_poly:function(a,b,c,d){var e='';for(var i=0;i<c.length;i++){e+=c[i].join()+' '}return this._makeNode(a,b,$.extend({points:$.trim(e)},d||{}))},text:function(a,x,y,b,c){var d=this._args(arguments,['x','y','value']);if(typeof d.x=='string'&&arguments.length<4){d.value=d.x;d.settings=d.y;d.x=d.y=null}return this._text(d.parent,'text',d.value,$.extend({x:(d.x&&isArray(d.x)?d.x.join(' '):d.x),y:(d.y&&isArray(d.y)?d.y.join(' '):d.y)},d.settings||{}))},textpath:function(a,b,c,d){var e=this._args(arguments,['path','value']);var f=this._text(e.parent,'textPath',e.value,e.settings||{});f.setAttributeNS($.svg.xlinkNS,'href',e.path);return f},_text:function(a,b,c,d){var e=this._makeNode(a,b,d);if(typeof c=='string'){e.appendChild(e.ownerDocument.createTextNode(c))}else{for(var i=0;i<c._parts.length;i++){var f=c._parts[i];if(f[0]=='tspan'){var g=this._makeNode(e,f[0],f[2]);g.appendChild(e.ownerDocument.createTextNode(f[1]));e.appendChild(g)}else if(f[0]=='tref'){var g=this._makeNode(e,f[0],f[2]);g.setAttributeNS($.svg.xlinkNS,'href',f[1]);e.appendChild(g)}else if(f[0]=='textpath'){var h=$.extend({},f[2]);h.href=null;var g=this._makeNode(e,f[0],h);g.setAttributeNS($.svg.xlinkNS,'href',f[2].href);g.appendChild(e.ownerDocument.createTextNode(f[1]));e.appendChild(g)}else{e.appendChild(e.ownerDocument.createTextNode(f[1]))}}}return e},other:function(a,b,c){var d=this._args(arguments,['name']);return this._makeNode(d.parent,d.name,d.settings||{})},_makeNode:function(a,b,c){a=a||this._svg;var d=this._svg.ownerDocument.createElementNS($.svg.svgNS,b);for(var b in c){var e=c[b];if(e!=null&&e!=null&&(typeof e!='string'||e!='')){d.setAttribute($.svg._attrNames[b]||b,e)}}a.appendChild(d);return d},add:function(b,c){var d=this._args((arguments.length==1?[null,b]:arguments),['node']);var f=this;d.parent=d.parent||this._svg;try{if($.svg._renesis){throw'Force traversal';}d.parent.appendChild(d.node.cloneNode(true))}catch(e){d.node=(d.node.jquery?d.node:$(d.node));d.node.each(function(){var a=f._cloneAsSVG(this);if(a){d.parent.appendChild(a)}})}return this},_cloneAsSVG:function(a){var b=null;if(a.nodeType==1){b=this._svg.ownerDocument.createElementNS($.svg.svgNS,this._checkName(a.nodeName));for(var i=0;i<a.attributes.length;i++){var c=a.attributes.item(i);if(c.nodeName!='xmlns'&&c.nodeValue){if(c.prefix=='xlink'){b.setAttributeNS($.svg.xlinkNS,c.localName,c.nodeValue)}else{b.setAttribute(this._checkName(c.nodeName),c.nodeValue)}}}for(var i=0;i<a.childNodes.length;i++){var d=this._cloneAsSVG(a.childNodes[i]);if(d){b.appendChild(d)}}}else if(a.nodeType==3){if($.trim(a.nodeValue)){b=this._svg.ownerDocument.createTextNode(a.nodeValue)}}else if(a.nodeType==4){if($.trim(a.nodeValue)){try{b=this._svg.ownerDocument.createCDATASection(a.nodeValue)}catch(e){b=this._svg.ownerDocument.createTextNode(a.nodeValue.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'))}}}return b},_checkName:function(a){a=(a.substring(0,1)>='A'&&a.substring(0,1)<='Z'?a.toLowerCase():a);return(a.substring(0,4)=='svg:'?a.substring(4):a)},load:function(h,j){j=(typeof j=='boolean'?{addTo:j}:(typeof j=='function'?{onLoad:j}:j||{}));if(!j.addTo){this.clear(false)}var k=[this._svg.getAttribute('width'),this._svg.getAttribute('height')];var l=this;var m=function(a){a=$.svg.local.errorLoadingText+': '+a;if(j.onLoad){j.onLoad.apply(l._container||l._svg,[l,a])}else{l.text(null,10,20,a)}};var n=function(a){var b=new ActiveXObject('Microsoft.XMLDOM');b.validateOnParse=false;b.resolveExternals=false;b.async=false;b.loadXML(a);if(b.parseError.errorCode!=0){m(b.parseError.reason);return null}return b};var o=function(a){if(!a){return}if(a.documentElement.nodeName!='svg'){var b=a.getElementsByTagName('parsererror');var c=(b.length?b[0].getElementsByTagName('div'):[]);m(!b.length?'???':(c.length?c[0]:b[0]).firstChild.nodeValue);return}var d={};for(var i=0;i<a.documentElement.attributes.length;i++){var f=a.documentElement.attributes.item(i);if(!(f.nodeName=='version'||f.nodeName.substring(0,5)=='xmlns')){d[f.nodeName]=f.nodeValue}}l.configure(d,true);var g=a.documentElement.childNodes;for(var i=0;i<g.length;i++){try{if($.svg._renesis){throw'Force traversal';}l._svg.appendChild(g[i].cloneNode(true));if(g[i].nodeName=='script'){$.globalEval(g[i].textContent)}}catch(e){l.add(null,g[i])}}if(!j.changeSize){l.configure({width:k[0],height:k[1]})}if(j.onLoad){j.onLoad.apply(l._container||l._svg,[l])}};if(h.match('<svg')){o($.browser.msie?n(h):new DOMParser().parseFromString(h,'text/xml'))}else{$.ajax({url:h,dataType:($.browser.msie?'text':'xml'),success:function(a){o($.browser.msie?n(a):a)},error:function(a,b,c){m(b+(c?' '+c.message:''))}})}return this},remove:function(a){a=(a.jquery?a[0]:a);a.parentNode.removeChild(a);return this},clear:function(a){if(a){this.configure({},true)}while(this._svg.firstChild){this._svg.removeChild(this._svg.firstChild)}return this},toSVG:function(a){a=a||this._svg;return(typeof XMLSerializer=='undefined'?this._toSVG(a):new XMLSerializer().serializeToString(a))},_toSVG:function(a){var b='';if(!a){return b}if(a.nodeType==3){b=a.nodeValue}else if(a.nodeType==4){b='<![CDATA['+a.nodeValue+']]>'}else{b='<'+a.nodeName;if(a.attributes){for(var i=0;i<a.attributes.length;i++){var c=a.attributes.item(i);if(!($.trim(c.nodeValue)==''||c.nodeValue.match(/^\[object/)||c.nodeValue.match(/^function/))){b+=' '+(c.namespaceURI==$.svg.xlinkNS?'xlink:':'')+c.nodeName+'="'+c.nodeValue+'"'}}}if(a.firstChild){b+='>';var d=a.firstChild;while(d){b+=this._toSVG(d);d=d.nextSibling}b+='</'+a.nodeName+'>'}else{b+='/>'}}return b},_escapeXML:function(a){a=a.replace(/&/g,'&amp;');a=a.replace(/</g,'&lt;');a=a.replace(/>/g,'&gt;');return a}});function SVGPath(){this._path=''}$.extend(SVGPath.prototype,{reset:function(){this._path='';return this},move:function(x,y,a){a=(isArray(x)?y:a);return this._coords((a?'m':'M'),x,y)},line:function(x,y,a){a=(isArray(x)?y:a);return this._coords((a?'l':'L'),x,y)},horiz:function(x,a){this._path+=(a?'h':'H')+(isArray(x)?x.join(' '):x);return this},vert:function(y,a){this._path+=(a?'v':'V')+(isArray(y)?y.join(' '):y);return this},curveC:function(a,b,c,d,x,y,e){e=(isArray(a)?b:e);return this._coords((e?'c':'C'),a,b,c,d,x,y)},smoothC:function(a,b,x,y,c){c=(isArray(a)?b:c);return this._coords((c?'s':'S'),a,b,x,y)},curveQ:function(a,b,x,y,c){c=(isArray(a)?b:c);return this._coords((c?'q':'Q'),a,b,x,y)},smoothQ:function(x,y,a){a=(isArray(x)?y:a);return this._coords((a?'t':'T'),x,y)},_coords:function(a,b,c,d,e,f,g){if(isArray(b)){for(var i=0;i<b.length;i++){var h=b[i];this._path+=(i==0?a:' ')+h[0]+','+h[1]+(h.length<4?'':' '+h[2]+','+h[3]+(h.length<6?'':' '+h[4]+','+h[5]))}}else{this._path+=a+b+','+c+(d==null?'':' '+d+','+e+(f==null?'':' '+f+','+g))}return this},arc:function(a,b,c,d,e,x,y,f){f=(isArray(a)?b:f);this._path+=(f?'a':'A');if(isArray(a)){for(var i=0;i<a.length;i++){var g=a[i];this._path+=(i==0?'':' ')+g[0]+','+g[1]+' '+g[2]+' '+(g[3]?'1':'0')+','+(g[4]?'1':'0')+' '+g[5]+','+g[6]}}else{this._path+=a+','+b+' '+c+' '+(d?'1':'0')+','+(e?'1':'0')+' '+x+','+y}return this},close:function(){this._path+='z';return this},path:function(){return this._path}});SVGPath.prototype.moveTo=SVGPath.prototype.move;SVGPath.prototype.lineTo=SVGPath.prototype.line;SVGPath.prototype.horizTo=SVGPath.prototype.horiz;SVGPath.prototype.vertTo=SVGPath.prototype.vert;SVGPath.prototype.curveCTo=SVGPath.prototype.curveC;SVGPath.prototype.smoothCTo=SVGPath.prototype.smoothC;SVGPath.prototype.curveQTo=SVGPath.prototype.curveQ;SVGPath.prototype.smoothQTo=SVGPath.prototype.smoothQ;SVGPath.prototype.arcTo=SVGPath.prototype.arc;function SVGText(){this._parts=[]}$.extend(SVGText.prototype,{reset:function(){this._parts=[];return this},string:function(a){this._parts[this._parts.length]=['text',a];return this},span:function(a,b){this._parts[this._parts.length]=['tspan',a,b];return this},ref:function(a,b){this._parts[this._parts.length]=['tref',a,b];return this},path:function(a,b,c){this._parts[this._parts.length]=['textpath',b,$.extend({href:a},c||{})];return this}});$.fn.svg=function(a){var b=Array.prototype.slice.call(arguments,1);if(typeof a=='string'&&a=='get'){return $.svg['_'+a+'SVG'].apply($.svg,[this[0]].concat(b))}return this.each(function(){if(typeof a=='string'){$.svg['_'+a+'SVG'].apply($.svg,[this].concat(b))}else{$.svg._attachSVG(this,a||{})}})};function isArray(a){return(a&&a.constructor==Array)}$.svg=new SVGManager()})(jQuery);
/* http://keith-wood.name/svg.html
   SVG/jQuery DOM compatibility for jQuery v1.4.3.
   Written by Keith Wood (kbwood{at}iinet.com.au) April 2009.
   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
   Please attribute the author if you use it. */
(function($){$.fn.addClass=function(e){return function(d){d=d||'';return this.each(function(){if(isSVGElem(this)){var c=this;$.each(d.split(/\s+/),function(i,a){var b=(c.className?c.className.baseVal:c.getAttribute('class'));if($.inArray(a,b.split(/\s+/))==-1){b+=(b?' ':'')+a;(c.className?c.className.baseVal=b:c.setAttribute('class',b))}})}else{e.apply($(this),[d])}})}}($.fn.addClass);$.fn.removeClass=function(e){return function(d){d=d||'';return this.each(function(){if(isSVGElem(this)){var c=this;$.each(d.split(/\s+/),function(i,a){var b=(c.className?c.className.baseVal:c.getAttribute('class'));b=$.grep(b.split(/\s+/),function(n,i){return n!=a}).join(' ');(c.className?c.className.baseVal=b:c.setAttribute('class',b))})}else{e.apply($(this),[d])}})}}($.fn.removeClass);$.fn.toggleClass=function(c){return function(a,b){return this.each(function(){if(isSVGElem(this)){if(typeof b!=='boolean'){b=!$(this).hasClass(a)}$(this)[(b?'add':'remove')+'Class'](a)}else{c.apply($(this),[a,b])}})}}($.fn.toggleClass);$.fn.hasClass=function(d){return function(b){b=b||'';var c=false;this.each(function(){if(isSVGElem(this)){var a=(this.className?this.className.baseVal:this.getAttribute('class')).split(/\s+/);c=($.inArray(b,a)>-1)}else{c=(d.apply($(this),[b]))}return!c});return c}}($.fn.hasClass);$.fn.attr=function(h){return function(b,c,d){if(typeof b==='string'&&c===undefined){var e=h.apply(this,[b,c,d]);if(e&&e.baseVal&&e.baseVal.numberOfItems!=null){c='';e=e.baseVal;for(var i=0;i<e.numberOfItems;i++){var f=e.getItem(i);switch(f.type){case 1:c+=' matrix('+f.matrix.a+','+f.matrix.b+','+f.matrix.c+','+f.matrix.d+','+f.matrix.e+','+f.matrix.f+')';break;case 2:c+=' translate('+f.matrix.e+','+f.matrix.f+')';break;case 3:c+=' scale('+f.matrix.a+','+f.matrix.d+')';break;case 4:c+=' rotate('+f.angle+')';break;case 5:c+=' skewX('+f.angle+')';break;case 6:c+=' skewY('+f.angle+')';break}}e=c.substring(1)}return(e&&e.baseVal?e.baseVal.valueAsString:e)}var g=b;if(typeof b==='string'){g={};g[b]=c}return this.each(function(){if(isSVGElem(this)){for(var n in g){var a=($.isFunction(g[n])?g[n]():g[n]);(d?this.style[n]=a:this.setAttribute(n,a))}}else{h.apply($(this),[b,c,d])}})}}($.fn.attr);$.fn.removeAttr=function(b){return function(a){return this.each(function(){if(isSVGElem(this)){(this[a]&&this[a].baseVal?this[a].baseVal.value='':this.setAttribute(a,''))}else{b.apply($(this),[a])}})}}($.fn.removeAttr);function anySVG(a){for(var i=0;i<a.length;i++){if(a[i].nodeType==1&&a[i].namespaceURI==$.svg.svgNS){return true}}return false}$.expr.relative['+']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['+']);$.expr.relative['>']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['>']);$.expr.relative['']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['']);$.expr.relative['~']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['~']);$.expr.find.ID=function(d){return function(a,b,c){return(isSVGElem(b)?[b.ownerDocument.getElementById(a[1])]:d(a,b,c))}}($.expr.find.ID);var j=document.createElement('div');j.appendChild(document.createComment(''));if(j.getElementsByTagName('*').length>0){$.expr.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==='*'){var d=[];for(var i=0;c[i]||c.item(i);i++){if((c[i]||c.item(i)).nodeType===1){d.push(c[i]||c.item(i))}}c=d}return c}}$.expr.preFilter.CLASS=function(a,b,c,d,f,g){a=' '+a[1].replace(/\\/g,'')+' ';if(g){return a}for(var i=0,elem={};elem!=null;i++){elem=b[i];if(!elem){try{elem=b.item(i)}catch(e){}}if(elem){var h=(!isSVGElem(elem)?elem.className:(elem.className?elem.className.baseVal:'')||elem.getAttribute('class'));if(f^(h&&(' '+h+' ').indexOf(a)>-1)){if(!c)d.push(elem)}else if(c){b[i]=false}}}return false};$.expr.filter.CLASS=function(a,b){var c=(!isSVGElem(a)?a.className:(a.className?a.className.baseVal:a.getAttribute('class')));return(' '+c+' ').indexOf(b)>-1};$.expr.filter.ATTR=function(g){return function(c,d){var e=null;if(isSVGElem(c)){e=d[1];$.expr.attrHandle[e]=function(a){var b=a.getAttribute(e);return b&&b.baseVal||b}}var f=g(c,d);if(e){$.expr.attrHandle[e]=null}return f}}($.expr.filter.ATTR);function isSVGElem(a){return(a.nodeType==1&&a.namespaceURI==$.svg.svgNS)}})(jQuery);
/**
 * Timeago is a jQuery plugin that makes it easy to support automatically
 * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
 *
 * @name timeago
 * @version 1.5.2
 * @requires jQuery v1.2.3+
 * @author Ryan McGeary
 * @license MIT License - http://www.opensource.org/licenses/mit-license.php
 *
 * For usage and examples, visit:
 * http://timeago.yarp.com/
 *
 * Copyright (c) 2008-2015, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
 */

(function (factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else if (typeof module === 'object' && typeof module.exports === 'object') {
    factory(require('jquery'));
  } else {
    // Browser globals
    factory(jQuery);
  }
}(function ($) {
  $.timeago = function(timestamp) {
    if (timestamp instanceof Date) {
      return inWords(timestamp);
    } else if (typeof timestamp === "string") {
      return inWords($.timeago.parse(timestamp));
    } else if (typeof timestamp === "number") {
      return inWords(new Date(timestamp));
    } else {
      return inWords($.timeago.datetime(timestamp));
    }
  };
  var $t = $.timeago;

  $.extend($.timeago, {
    settings: {
      refreshMillis: 60000,
      allowPast: true,
      allowFuture: false,
      localeTitle: false,
      cutoff: 0,
      autoDispose: true,
      strings: {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: "ago",
        suffixFromNow: "from now",
        inPast: 'any moment now',
        seconds: "less than a minute",
        minute: "about a minute",
        minutes: "%d minutes",
        hour: "about an hour",
        hours: "about %d hours",
        day: "a day",
        days: "%d days",
        month: "about a month",
        months: "%d months",
        year: "about a year",
        years: "%d years",
        wordSeparator: " ",
        numbers: []
      }
    },

    inWords: function(distanceMillis) {
      if (!this.settings.allowPast && ! this.settings.allowFuture) {
          throw 'timeago allowPast and allowFuture settings can not both be set to false.';
      }

      var $l = this.settings.strings;
      var prefix = $l.prefixAgo;
      var suffix = $l.suffixAgo;
      if (this.settings.allowFuture) {
        if (distanceMillis < 0) {
          prefix = $l.prefixFromNow;
          suffix = $l.suffixFromNow;
        }
      }

      if (!this.settings.allowPast && distanceMillis >= 0) {
        return this.settings.strings.inPast;
      }

      var seconds = Math.abs(distanceMillis) / 1000;
      var minutes = seconds / 60;
      var hours = minutes / 60;
      var days = hours / 24;
      var years = days / 365;

      function substitute(stringOrFunction, number) {
        var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
        var value = ($l.numbers && $l.numbers[number]) || number;
        return string.replace(/%d/i, value);
      }

      var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
        seconds < 90 && substitute($l.minute, 1) ||
        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
        minutes < 90 && substitute($l.hour, 1) ||
        hours < 24 && substitute($l.hours, Math.round(hours)) ||
        hours < 42 && substitute($l.day, 1) ||
        days < 30 && substitute($l.days, Math.round(days)) ||
        days < 45 && substitute($l.month, 1) ||
        days < 365 && substitute($l.months, Math.round(days / 30)) ||
        years < 1.5 && substitute($l.year, 1) ||
        substitute($l.years, Math.round(years));

      var separator = $l.wordSeparator || "";
      if ($l.wordSeparator === undefined) { separator = " "; }
      return $.trim([prefix, words, suffix].join(separator));
    },

    parse: function(iso8601) {
      var s = $.trim(iso8601);
      s = s.replace(/\.\d+/,""); // remove milliseconds
      s = s.replace(/-/,"/").replace(/-/,"/");
      s = s.replace(/T/," ").replace(/Z/," UTC");
      s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
      s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
      return new Date(s);
    },
    datetime: function(elem) {
      var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
      return $t.parse(iso8601);
    },
    isTime: function(elem) {
      // jQuery's `is()` doesn't play well with HTML5 in IE
      return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
    }
  });

  // functions that can be called via $(el).timeago('action')
  // init is default when no action is given
  // functions are called with context of a single element
  var functions = {
    init: function() {
      var refresh_el = $.proxy(refresh, this);
      refresh_el();
      var $s = $t.settings;
      if ($s.refreshMillis > 0) {
        this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
      }
    },
    update: function(timestamp) {
      var date = (timestamp instanceof Date) ? timestamp : $t.parse(timestamp);
      $(this).data('timeago', { datetime: date });
      if ($t.settings.localeTitle) $(this).attr("title", date.toLocaleString());
      refresh.apply(this);
    },
    updateFromDOM: function() {
      $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
      refresh.apply(this);
    },
    dispose: function () {
      if (this._timeagoInterval) {
        window.clearInterval(this._timeagoInterval);
        this._timeagoInterval = null;
      }
    }
  };

  $.fn.timeago = function(action, options) {
    var fn = action ? functions[action] : functions.init;
    if (!fn) {
      throw new Error("Unknown function name '"+ action +"' for timeago");
    }
    // each over objects here and call the requested function
    this.each(function() {
      fn.call(this, options);
    });
    return this;
  };

  function refresh() {
    var $s = $t.settings;

    //check if it's still visible
    if ($s.autoDispose && !$.contains(document.documentElement,this)) {
      //stop if it has been removed
      $(this).timeago("dispose");
      return this;
    }

    var data = prepareData(this);

    if (!isNaN(data.datetime)) {
      if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
        $(this).text(inWords(data.datetime));
      }
    }
    return this;
  }

  function prepareData(element) {
    element = $(element);
    if (!element.data("timeago")) {
      element.data("timeago", { datetime: $t.datetime(element) });
      var text = $.trim(element.text());
      if ($t.settings.localeTitle) {
        element.attr("title", element.data('timeago').datetime.toLocaleString());
      } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
        element.attr("title", text);
      }
    }
    return element.data("timeago");
  }

  function inWords(date) {
    return $t.inWords(distance(date));
  }

  function distance(date) {
    return (new Date().getTime() - date.getTime());
  }

  // fix for IE6 suckage
  document.createElement("abbr");
  document.createElement("time");
}));
/*! jQuery v1.7.1 jquery.com | jquery.org/license */
(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function cb(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function ca(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bE.test(a)?d(a,e):ca(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)ca(a+"["+e+"]",b[e],c,d);else d(a,b)}function b_(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bT,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bP),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bC(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bx:by,g=0,h=e.length;if(d>0){if(c!=="border")for(;g<h;g++)c||(d-=parseFloat(f.css(a,"padding"+e[g]))||0),c==="margin"?d+=parseFloat(f.css(a,c+e[g]))||0:d-=parseFloat(f.css(a,"border"+e[g]+"Width"))||0;return d+"px"}d=bz(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0;if(c)for(;g<h;g++)d+=parseFloat(f.css(a,"padding"+e[g]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+e[g]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+e[g]))||0);return d+"px"}function bp(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c+(i[c][d].namespace?".":"")+i[c][d].namespace,i[c][d],i[c][d].data)}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test("Â ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?m(g):h==="function"&&(!a.unique||!o.has(g))&&c.push(g)},n=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,l=j||0,j=0,k=c.length;for(;c&&l<k;l++)if(c[l].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}i=!1,c&&(a.once?e===!0?o.disable():c=[]:d&&d.length&&(e=d.shift(),o.fireWith(e[0],e[1])))},o={add:function(){if(c){var a=c.length;m(arguments),i?k=c.length:e&&e!==!0&&(j=a,n(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){i&&f<=k&&(k--,f<=l&&l--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&o.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(i?a.once||d.push([b,c]):(!a.once||!e)&&n(b,c));return this},fire:function(){o.fireWith(this,arguments);return this},fired:function(){return!!e}};return o};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p,q=c.createElement("div"),r=c.documentElement;q.setAttribute("className","t"),q.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="<div "+n+"><div></div></div>"+"<table "+n+" cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="<div style='width:4px;'></div>",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h=null;if(typeof a=="undefined"){if(this.length){h=f.data(this[0]);if(this[0].nodeType===1&&!f._data(this[0],"parsedAttrs")){e=this[0].attributes;for(var i=0,j=e.length;i<j;i++)g=e[i].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),l(this[0],g,h[g]));f._data(this[0],"parsedAttrs",!0)}}return h}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split("."),d[1]=d[1]?"."+d[1]:"";if(c===b){h=this.triggerHandler("getData"+d[1]+"!",[d[0]]),h===b&&this.length&&(h=f.data(this[0],a),h=l(this[0],a,h));return h===b&&d[1]?this.data(d[0]):h}return this.each(function(){var b=f(this),e=[d[0],c];b.triggerHandler("setData"+d[1]+"!",e),f.data(this,a,c),b.triggerHandler("changeData"+d[1]+"!",e)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise()}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h<g;h++)e=d[h],e&&(c=f.propFix[e]||e,f.attr(a,e,""),a.removeAttribute(v?e:c),u.test(e)&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};
f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=[],j,k,l,m,n,o,p,q,r,s,t;g[0]=c,c.delegateTarget=this;if(e&&!c.target.disabled&&(!c.button||c.type!=="click")){m=f(this),m.context=this.ownerDocument||this;for(l=c.target;l!=this;l=l.parentNode||this){o={},q=[],m[0]=l;for(j=0;j<e;j++)r=d[j],s=r.selector,o[s]===b&&(o[s]=r.quick?H(l,r.quick):m.is(s)),o[s]&&q.push(r);q.length&&i.push({elem:l,matches:q})}}d.length>e&&i.push({elem:this,matches:d.slice(e)});for(j=0;j<i.length&&!c.isPropagationStopped();j++){p=i[j],c.currentTarget=p.elem;for(k=0;k<p.matches.length&&!c.isImmediatePropagationStopped();k++){r=p.matches[k];if(h||!c.namespace&&!r.namespace||c.namespace_re&&c.namespace_re.test(r.namespace))c.data=r.data,c.handleObj=r,n=((f.event.special[r.origType]||{}).handle||r.handler).apply(p.elem,g),n!==b&&(c.result=n,n===!1&&(c.preventDefault(),c.stopPropagation()))}}return c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0)}),d._submit_attached=!0)})},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on.call(this,a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.type+"."+e.namespace:e.type,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.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(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.POS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function()
{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bp)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bn(k[i]);else bn(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bq=/alpha\([^)]*\)/i,br=/opacity=([^)]*)/,bs=/([A-Z]|^ms)/g,bt=/^-?\d+(?:px)?$/i,bu=/^-?\d/,bv=/^([\-+])=([\-+.\de]+)/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bv.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bC(a,b,d);f.swap(a,bw,function(){e=bC(a,b,d)});return e}},set:function(a,b){if(!bt.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cv(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cu("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cu("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cv(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cn.test(h)?(o=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),o?(f._data(this,"toggle"+i,o==="show"?"hide":"show"),j[o]()):j[h]()):(k=co.exec(h),l=j.cur(),k?(m=parseFloat(k[2]),n=k[3]||(f.cssNumber[i]?"":"px"),n!=="px"&&(f.style(this,i,(m||1)+n),l=(m||1)/j.cur()*l,f.style(this,i,l+n)),k[1]&&(m=(k[1]==="-="?-1:1)*m+l),j.custom(l,m,n)):j.custom(l,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:cu("show",1),slideUp:cu("hide",1),slideToggle:cu("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cr||cs(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){e.options.hide&&f._data(e.elem,"fxshow"+e.prop)===b&&f._data(e.elem,"fxshow"+e.prop,e.start)},h()&&f.timers.push(h)&&!cp&&(cp=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cr||cs(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cp),cp=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(["width","height"],function(a,b){f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cy(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.support.fixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.support.fixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
/* http://keith-wood.name/svg.html
   SVG for jQuery v1.4.3.
   Written by Keith Wood (kbwood{at}iinet.com.au) August 2007.
   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
   Please attribute the author if you use it. */
(function($){function SVGManager(){this._settings=[];this._extensions=[];this.regional=[];this.regional['']={errorLoadingText:'Error loading',notSupportedText:'This browser does not support SVG'};this.local=this.regional[''];this._uuid=new Date().getTime();this._renesis=detectActiveX('RenesisX.RenesisCtrl')}function detectActiveX(a){try{return!!(window.ActiveXObject&&new ActiveXObject(a))}catch(e){return false}}var p='svgwrapper';$.extend(SVGManager.prototype,{markerClassName:'hasSVG',svgNS:'http://www.w3.org/2000/svg',xlinkNS:'http://www.w3.org/1999/xlink',_wrapperClass:SVGWrapper,_attrNames:{class_:'class',in_:'in',alignmentBaseline:'alignment-baseline',baselineShift:'baseline-shift',clipPath:'clip-path',clipRule:'clip-rule',colorInterpolation:'color-interpolation',colorInterpolationFilters:'color-interpolation-filters',colorRendering:'color-rendering',dominantBaseline:'dominant-baseline',enableBackground:'enable-background',fillOpacity:'fill-opacity',fillRule:'fill-rule',floodColor:'flood-color',floodOpacity:'flood-opacity',fontFamily:'font-family',fontSize:'font-size',fontSizeAdjust:'font-size-adjust',fontStretch:'font-stretch',fontStyle:'font-style',fontVariant:'font-variant',fontWeight:'font-weight',glyphOrientationHorizontal:'glyph-orientation-horizontal',glyphOrientationVertical:'glyph-orientation-vertical',horizAdvX:'horiz-adv-x',horizOriginX:'horiz-origin-x',imageRendering:'image-rendering',letterSpacing:'letter-spacing',lightingColor:'lighting-color',markerEnd:'marker-end',markerMid:'marker-mid',markerStart:'marker-start',stopColor:'stop-color',stopOpacity:'stop-opacity',strikethroughPosition:'strikethrough-position',strikethroughThickness:'strikethrough-thickness',strokeDashArray:'stroke-dasharray',strokeDashOffset:'stroke-dashoffset',strokeLineCap:'stroke-linecap',strokeLineJoin:'stroke-linejoin',strokeMiterLimit:'stroke-miterlimit',strokeOpacity:'stroke-opacity',strokeWidth:'stroke-width',textAnchor:'text-anchor',textDecoration:'text-decoration',textRendering:'text-rendering',underlinePosition:'underline-position',underlineThickness:'underline-thickness',vertAdvY:'vert-adv-y',vertOriginY:'vert-origin-y',wordSpacing:'word-spacing',writingMode:'writing-mode'},_attachSVG:function(a,b){var c=(a.namespaceURI==this.svgNS?a:null);var a=(c?null:a);if($(a||c).hasClass(this.markerClassName)){return}if(typeof b=='string'){b={loadURL:b}}else if(typeof b=='function'){b={onLoad:b}}$(a||c).addClass(this.markerClassName);try{if(!c){c=document.createElementNS(this.svgNS,'svg');c.setAttribute('version','1.1');c.setAttribute('width',a.clientWidth);c.setAttribute('height',a.clientHeight);a.appendChild(c)}this._afterLoad(a,c,b||{})}catch(e){if($.browser.msie){if(!a.id){a.id='svg'+(this._uuid++)}this._settings[a.id]=b;a.innerHTML='<embed type="image/svg+xml" width="100%" '+'height="100%" src="'+(b.initPath||'')+'blank.svg"/>'}else{a.innerHTML='<p class="svg_error">'+this.local.notSupportedText+'</p>'}}},_registerSVG:function(){for(var i=0;i<document.embeds.length;i++){var a=document.embeds[i].parentNode;if(!$(a).hasClass($.svg.markerClassName)||$.data(a,p)){continue}var b=null;try{b=document.embeds[i].getSVGDocument()}catch(e){setTimeout($.svg._registerSVG,250);return}b=(b?b.documentElement:null);if(b){$.svg._afterLoad(a,b)}}},_afterLoad:function(a,b,c){var c=c||this._settings[a.id];this._settings[a?a.id:'']=null;var d=new this._wrapperClass(b,a);$.data(a||b,p,d);try{if(c.loadURL){d.load(c.loadURL,c)}if(c.settings){d.configure(c.settings)}if(c.onLoad&&!c.loadURL){c.onLoad.apply(a||b,[d])}}catch(e){alert(e)}},_getSVG:function(a){a=(typeof a=='string'?$(a)[0]:(a.jquery?a[0]:a));return $.data(a,p)},_destroySVG:function(a){var b=$(a);if(!b.hasClass(this.markerClassName)){return}b.removeClass(this.markerClassName);if(a.namespaceURI!=this.svgNS){b.empty()}$.removeData(a,p)},addExtension:function(a,b){this._extensions.push([a,b])}});function SVGWrapper(a,b){this._svg=a;this._container=b;for(var i=0;i<$.svg._extensions.length;i++){var c=$.svg._extensions[i];this[c[0]]=new c[1](this)}}$.extend(SVGWrapper.prototype,{_width:function(){return(this._container?this._container.clientWidth:this._svg.width)},_height:function(){return(this._container?this._container.clientHeight:this._svg.height)},root:function(){return this._svg},configure:function(a,b){if(b){for(var i=this._svg.attributes.length-1;i>=0;i--){var c=this._svg.attributes.item(i);if(!(c.nodeName=='onload'||c.nodeName=='version'||c.nodeName.substring(0,5)=='xmlns')){this._svg.attributes.removeNamedItem(c.nodeName)}}}for(var d in a){this._svg.setAttribute(d,a[d])}return this},getElementById:function(a){return this._svg.ownerDocument.getElementById(a)},change:function(a,b){if(a){for(var c in b){if(b[c]==null){a.removeAttribute(c)}else{a.setAttribute(c,b[c])}}}return this},_args:function(b,c,d){c.splice(0,0,'parent');c.splice(c.length,0,'settings');var e={};var f=0;if(b[0]!=null&&b[0].jquery){b[0]=b[0][0]}if(b[0]!=null&&!(typeof b[0]=='object'&&b[0].nodeName)){e['parent']=null;f=1}for(var i=0;i<b.length;i++){e[c[i+f]]=b[i]}if(d){$.each(d,function(i,a){if(typeof e[a]=='object'){e.settings=e[a];e[a]=null}})}return e},title:function(a,b,c){var d=this._args(arguments,['text']);var e=this._makeNode(d.parent,'title',d.settings||{});e.appendChild(this._svg.ownerDocument.createTextNode(d.text));return e},describe:function(a,b,c){var d=this._args(arguments,['text']);var e=this._makeNode(d.parent,'desc',d.settings||{});e.appendChild(this._svg.ownerDocument.createTextNode(d.text));return e},defs:function(a,b,c){var d=this._args(arguments,['id'],['id']);return this._makeNode(d.parent,'defs',$.extend((d.id?{id:d.id}:{}),d.settings||{}))},symbol:function(a,b,c,d,e,f,g){var h=this._args(arguments,['id','x1','y1','width','height']);return this._makeNode(h.parent,'symbol',$.extend({id:h.id,viewBox:h.x1+' '+h.y1+' '+h.width+' '+h.height},h.settings||{}))},marker:function(a,b,c,d,e,f,g,h){var i=this._args(arguments,['id','refX','refY','mWidth','mHeight','orient'],['orient']);return this._makeNode(i.parent,'marker',$.extend({id:i.id,refX:i.refX,refY:i.refY,markerWidth:i.mWidth,markerHeight:i.mHeight,orient:i.orient||'auto'},i.settings||{}))},style:function(a,b,c){var d=this._args(arguments,['styles']);var e=this._makeNode(d.parent,'style',$.extend({type:'text/css'},d.settings||{}));e.appendChild(this._svg.ownerDocument.createTextNode(d.styles));if($.browser.opera){$('head').append('<style type="text/css">'+d.styles+'</style>')}return e},script:function(a,b,c,d){var e=this._args(arguments,['script','type'],['type']);var f=this._makeNode(e.parent,'script',$.extend({type:e.type||'text/javascript'},e.settings||{}));f.appendChild(this._svg.ownerDocument.createTextNode(this._escapeXML(e.script)));if(!$.browser.mozilla){$.globalEval(e.script)}return f},linearGradient:function(a,b,c,d,e,f,g,h){var i=this._args(arguments,['id','stops','x1','y1','x2','y2'],['x1']);var j=$.extend({id:i.id},(i.x1!=null?{x1:i.x1,y1:i.y1,x2:i.x2,y2:i.y2}:{}));return this._gradient(i.parent,'linearGradient',$.extend(j,i.settings||{}),i.stops)},radialGradient:function(a,b,c,d,e,r,f,g,h){var i=this._args(arguments,['id','stops','cx','cy','r','fx','fy'],['cx']);var j=$.extend({id:i.id},(i.cx!=null?{cx:i.cx,cy:i.cy,r:i.r,fx:i.fx,fy:i.fy}:{}));return this._gradient(i.parent,'radialGradient',$.extend(j,i.settings||{}),i.stops)},_gradient:function(a,b,c,d){var e=this._makeNode(a,b,c);for(var i=0;i<d.length;i++){var f=d[i];this._makeNode(e,'stop',$.extend({offset:f[0],stopColor:f[1]},(f[2]!=null?{stopOpacity:f[2]}:{})))}return e},pattern:function(a,b,x,y,c,d,e,f,g,h,i){var j=this._args(arguments,['id','x','y','width','height','vx','vy','vwidth','vheight'],['vx']);var k=$.extend({id:j.id,x:j.x,y:j.y,width:j.width,height:j.height},(j.vx!=null?{viewBox:j.vx+' '+j.vy+' '+j.vwidth+' '+j.vheight}:{}));return this._makeNode(j.parent,'pattern',$.extend(k,j.settings||{}))},mask:function(a,b,x,y,c,d,e){var f=this._args(arguments,['id','x','y','width','height']);return this._makeNode(f.parent,'mask',$.extend({id:f.id,x:f.x,y:f.y,width:f.width,height:f.height},f.settings||{}))},createPath:function(){return new SVGPath()},createText:function(){return new SVGText()},svg:function(a,x,y,b,c,d,e,f,g,h){var i=this._args(arguments,['x','y','width','height','vx','vy','vwidth','vheight'],['vx']);var j=$.extend({x:i.x,y:i.y,width:i.width,height:i.height},(i.vx!=null?{viewBox:i.vx+' '+i.vy+' '+i.vwidth+' '+i.vheight}:{}));return this._makeNode(i.parent,'svg',$.extend(j,i.settings||{}))},group:function(a,b,c){var d=this._args(arguments,['id'],['id']);return this._makeNode(d.parent,'g',$.extend({id:d.id},d.settings||{}))},use:function(a,x,y,b,c,d,e){var f=this._args(arguments,['x','y','width','height','ref']);if(typeof f.x=='string'){f.ref=f.x;f.settings=f.y;f.x=f.y=f.width=f.height=null}var g=this._makeNode(f.parent,'use',$.extend({x:f.x,y:f.y,width:f.width,height:f.height},f.settings||{}));g.setAttributeNS($.svg.xlinkNS,'href',f.ref);return g},link:function(a,b,c){var d=this._args(arguments,['ref']);var e=this._makeNode(d.parent,'a',d.settings);e.setAttributeNS($.svg.xlinkNS,'href',d.ref);return e},image:function(a,x,y,b,c,d,e){var f=this._args(arguments,['x','y','width','height','ref']);var g=this._makeNode(f.parent,'image',$.extend({x:f.x,y:f.y,width:f.width,height:f.height},f.settings||{}));g.setAttributeNS($.svg.xlinkNS,'href',f.ref);return g},path:function(a,b,c){var d=this._args(arguments,['path']);return this._makeNode(d.parent,'path',$.extend({d:(d.path.path?d.path.path():d.path)},d.settings||{}))},rect:function(a,x,y,b,c,d,e,f){var g=this._args(arguments,['x','y','width','height','rx','ry'],['rx']);return this._makeNode(g.parent,'rect',$.extend({x:g.x,y:g.y,width:g.width,height:g.height},(g.rx?{rx:g.rx,ry:g.ry}:{}),g.settings||{}))},circle:function(a,b,c,r,d){var e=this._args(arguments,['cx','cy','r']);return this._makeNode(e.parent,'circle',$.extend({cx:e.cx,cy:e.cy,r:e.r},e.settings||{}))},ellipse:function(a,b,c,d,e,f){var g=this._args(arguments,['cx','cy','rx','ry']);return this._makeNode(g.parent,'ellipse',$.extend({cx:g.cx,cy:g.cy,rx:g.rx,ry:g.ry},g.settings||{}))},line:function(a,b,c,d,e,f){var g=this._args(arguments,['x1','y1','x2','y2']);return this._makeNode(g.parent,'line',$.extend({x1:g.x1,y1:g.y1,x2:g.x2,y2:g.y2},g.settings||{}))},polyline:function(a,b,c){var d=this._args(arguments,['points']);return this._poly(d.parent,'polyline',d.points,d.settings)},polygon:function(a,b,c){var d=this._args(arguments,['points']);return this._poly(d.parent,'polygon',d.points,d.settings)},_poly:function(a,b,c,d){var e='';for(var i=0;i<c.length;i++){e+=c[i].join()+' '}return this._makeNode(a,b,$.extend({points:$.trim(e)},d||{}))},text:function(a,x,y,b,c){var d=this._args(arguments,['x','y','value']);if(typeof d.x=='string'&&arguments.length<4){d.value=d.x;d.settings=d.y;d.x=d.y=null}return this._text(d.parent,'text',d.value,$.extend({x:(d.x&&isArray(d.x)?d.x.join(' '):d.x),y:(d.y&&isArray(d.y)?d.y.join(' '):d.y)},d.settings||{}))},textpath:function(a,b,c,d){var e=this._args(arguments,['path','value']);var f=this._text(e.parent,'textPath',e.value,e.settings||{});f.setAttributeNS($.svg.xlinkNS,'href',e.path);return f},_text:function(a,b,c,d){var e=this._makeNode(a,b,d);if(typeof c=='string'){e.appendChild(e.ownerDocument.createTextNode(c))}else{for(var i=0;i<c._parts.length;i++){var f=c._parts[i];if(f[0]=='tspan'){var g=this._makeNode(e,f[0],f[2]);g.appendChild(e.ownerDocument.createTextNode(f[1]));e.appendChild(g)}else if(f[0]=='tref'){var g=this._makeNode(e,f[0],f[2]);g.setAttributeNS($.svg.xlinkNS,'href',f[1]);e.appendChild(g)}else if(f[0]=='textpath'){var h=$.extend({},f[2]);h.href=null;var g=this._makeNode(e,f[0],h);g.setAttributeNS($.svg.xlinkNS,'href',f[2].href);g.appendChild(e.ownerDocument.createTextNode(f[1]));e.appendChild(g)}else{e.appendChild(e.ownerDocument.createTextNode(f[1]))}}}return e},other:function(a,b,c){var d=this._args(arguments,['name']);return this._makeNode(d.parent,d.name,d.settings||{})},_makeNode:function(a,b,c){a=a||this._svg;var d=this._svg.ownerDocument.createElementNS($.svg.svgNS,b);for(var b in c){var e=c[b];if(e!=null&&e!=null&&(typeof e!='string'||e!='')){d.setAttribute($.svg._attrNames[b]||b,e)}}a.appendChild(d);return d},add:function(b,c){var d=this._args((arguments.length==1?[null,b]:arguments),['node']);var f=this;d.parent=d.parent||this._svg;try{if($.svg._renesis){throw'Force traversal';}d.parent.appendChild(d.node.cloneNode(true))}catch(e){d.node=(d.node.jquery?d.node:$(d.node));d.node.each(function(){var a=f._cloneAsSVG(this);if(a){d.parent.appendChild(a)}})}return this},_cloneAsSVG:function(a){var b=null;if(a.nodeType==1){b=this._svg.ownerDocument.createElementNS($.svg.svgNS,this._checkName(a.nodeName));for(var i=0;i<a.attributes.length;i++){var c=a.attributes.item(i);if(c.nodeName!='xmlns'&&c.nodeValue){if(c.prefix=='xlink'){b.setAttributeNS($.svg.xlinkNS,c.localName,c.nodeValue)}else{b.setAttribute(this._checkName(c.nodeName),c.nodeValue)}}}for(var i=0;i<a.childNodes.length;i++){var d=this._cloneAsSVG(a.childNodes[i]);if(d){b.appendChild(d)}}}else if(a.nodeType==3){if($.trim(a.nodeValue)){b=this._svg.ownerDocument.createTextNode(a.nodeValue)}}else if(a.nodeType==4){if($.trim(a.nodeValue)){try{b=this._svg.ownerDocument.createCDATASection(a.nodeValue)}catch(e){b=this._svg.ownerDocument.createTextNode(a.nodeValue.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'))}}}return b},_checkName:function(a){a=(a.substring(0,1)>='A'&&a.substring(0,1)<='Z'?a.toLowerCase():a);return(a.substring(0,4)=='svg:'?a.substring(4):a)},load:function(h,j){j=(typeof j=='boolean'?{addTo:j}:(typeof j=='function'?{onLoad:j}:j||{}));if(!j.addTo){this.clear(false)}var k=[this._svg.getAttribute('width'),this._svg.getAttribute('height')];var l=this;var m=function(a){a=$.svg.local.errorLoadingText+': '+a;if(j.onLoad){j.onLoad.apply(l._container||l._svg,[l,a])}else{l.text(null,10,20,a)}};var n=function(a){var b=new ActiveXObject('Microsoft.XMLDOM');b.validateOnParse=false;b.resolveExternals=false;b.async=false;b.loadXML(a);if(b.parseError.errorCode!=0){m(b.parseError.reason);return null}return b};var o=function(a){if(!a){return}if(a.documentElement.nodeName!='svg'){var b=a.getElementsByTagName('parsererror');var c=(b.length?b[0].getElementsByTagName('div'):[]);m(!b.length?'???':(c.length?c[0]:b[0]).firstChild.nodeValue);return}var d={};for(var i=0;i<a.documentElement.attributes.length;i++){var f=a.documentElement.attributes.item(i);if(!(f.nodeName=='version'||f.nodeName.substring(0,5)=='xmlns')){d[f.nodeName]=f.nodeValue}}l.configure(d,true);var g=a.documentElement.childNodes;for(var i=0;i<g.length;i++){try{if($.svg._renesis){throw'Force traversal';}l._svg.appendChild(g[i].cloneNode(true));if(g[i].nodeName=='script'){$.globalEval(g[i].textContent)}}catch(e){l.add(null,g[i])}}if(!j.changeSize){l.configure({width:k[0],height:k[1]})}if(j.onLoad){j.onLoad.apply(l._container||l._svg,[l])}};if(h.match('<svg')){o($.browser.msie?n(h):new DOMParser().parseFromString(h,'text/xml'))}else{$.ajax({url:h,dataType:($.browser.msie?'text':'xml'),success:function(a){o($.browser.msie?n(a):a)},error:function(a,b,c){m(b+(c?' '+c.message:''))}})}return this},remove:function(a){a=(a.jquery?a[0]:a);a.parentNode.removeChild(a);return this},clear:function(a){if(a){this.configure({},true)}while(this._svg.firstChild){this._svg.removeChild(this._svg.firstChild)}return this},toSVG:function(a){a=a||this._svg;return(typeof XMLSerializer=='undefined'?this._toSVG(a):new XMLSerializer().serializeToString(a))},_toSVG:function(a){var b='';if(!a){return b}if(a.nodeType==3){b=a.nodeValue}else if(a.nodeType==4){b='<![CDATA['+a.nodeValue+']]>'}else{b='<'+a.nodeName;if(a.attributes){for(var i=0;i<a.attributes.length;i++){var c=a.attributes.item(i);if(!($.trim(c.nodeValue)==''||c.nodeValue.match(/^\[object/)||c.nodeValue.match(/^function/))){b+=' '+(c.namespaceURI==$.svg.xlinkNS?'xlink:':'')+c.nodeName+'="'+c.nodeValue+'"'}}}if(a.firstChild){b+='>';var d=a.firstChild;while(d){b+=this._toSVG(d);d=d.nextSibling}b+='</'+a.nodeName+'>'}else{b+='/>'}}return b},_escapeXML:function(a){a=a.replace(/&/g,'&amp;');a=a.replace(/</g,'&lt;');a=a.replace(/>/g,'&gt;');return a}});function SVGPath(){this._path=''}$.extend(SVGPath.prototype,{reset:function(){this._path='';return this},move:function(x,y,a){a=(isArray(x)?y:a);return this._coords((a?'m':'M'),x,y)},line:function(x,y,a){a=(isArray(x)?y:a);return this._coords((a?'l':'L'),x,y)},horiz:function(x,a){this._path+=(a?'h':'H')+(isArray(x)?x.join(' '):x);return this},vert:function(y,a){this._path+=(a?'v':'V')+(isArray(y)?y.join(' '):y);return this},curveC:function(a,b,c,d,x,y,e){e=(isArray(a)?b:e);return this._coords((e?'c':'C'),a,b,c,d,x,y)},smoothC:function(a,b,x,y,c){c=(isArray(a)?b:c);return this._coords((c?'s':'S'),a,b,x,y)},curveQ:function(a,b,x,y,c){c=(isArray(a)?b:c);return this._coords((c?'q':'Q'),a,b,x,y)},smoothQ:function(x,y,a){a=(isArray(x)?y:a);return this._coords((a?'t':'T'),x,y)},_coords:function(a,b,c,d,e,f,g){if(isArray(b)){for(var i=0;i<b.length;i++){var h=b[i];this._path+=(i==0?a:' ')+h[0]+','+h[1]+(h.length<4?'':' '+h[2]+','+h[3]+(h.length<6?'':' '+h[4]+','+h[5]))}}else{this._path+=a+b+','+c+(d==null?'':' '+d+','+e+(f==null?'':' '+f+','+g))}return this},arc:function(a,b,c,d,e,x,y,f){f=(isArray(a)?b:f);this._path+=(f?'a':'A');if(isArray(a)){for(var i=0;i<a.length;i++){var g=a[i];this._path+=(i==0?'':' ')+g[0]+','+g[1]+' '+g[2]+' '+(g[3]?'1':'0')+','+(g[4]?'1':'0')+' '+g[5]+','+g[6]}}else{this._path+=a+','+b+' '+c+' '+(d?'1':'0')+','+(e?'1':'0')+' '+x+','+y}return this},close:function(){this._path+='z';return this},path:function(){return this._path}});SVGPath.prototype.moveTo=SVGPath.prototype.move;SVGPath.prototype.lineTo=SVGPath.prototype.line;SVGPath.prototype.horizTo=SVGPath.prototype.horiz;SVGPath.prototype.vertTo=SVGPath.prototype.vert;SVGPath.prototype.curveCTo=SVGPath.prototype.curveC;SVGPath.prototype.smoothCTo=SVGPath.prototype.smoothC;SVGPath.prototype.curveQTo=SVGPath.prototype.curveQ;SVGPath.prototype.smoothQTo=SVGPath.prototype.smoothQ;SVGPath.prototype.arcTo=SVGPath.prototype.arc;function SVGText(){this._parts=[]}$.extend(SVGText.prototype,{reset:function(){this._parts=[];return this},string:function(a){this._parts[this._parts.length]=['text',a];return this},span:function(a,b){this._parts[this._parts.length]=['tspan',a,b];return this},ref:function(a,b){this._parts[this._parts.length]=['tref',a,b];return this},path:function(a,b,c){this._parts[this._parts.length]=['textpath',b,$.extend({href:a},c||{})];return this}});$.fn.svg=function(a){var b=Array.prototype.slice.call(arguments,1);if(typeof a=='string'&&a=='get'){return $.svg['_'+a+'SVG'].apply($.svg,[this[0]].concat(b))}return this.each(function(){if(typeof a=='string'){$.svg['_'+a+'SVG'].apply($.svg,[this].concat(b))}else{$.svg._attachSVG(this,a||{})}})};function isArray(a){return(a&&a.constructor==Array)}$.svg=new SVGManager()})(jQuery);
/*
 * jQuery Address Plugin v1.5
 * http://www.asual.com/jquery/address/
 *
 * Copyright (c) 2009-2010 Rostislav Hristov
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * Date: 2012-11-18 23:51:44 +0200 (Sun, 18 Nov 2012)
 */
(function(c){c.address=function(){var r=function(a){a=c.extend(c.Event(a),function(){for(var b={},f=c.address.parameterNames(),m=0,p=f.length;m<p;m++)b[f[m]]=c.address.parameter(f[m]);return{value:c.address.value(),path:c.address.path(),pathNames:c.address.pathNames(),parameterNames:f,parameters:b,queryString:c.address.queryString()}}.call(c.address));c(c.address).trigger(a);return a},s=function(a){return Array.prototype.slice.call(a)},h=function(){c().bind.apply(c(c.address),Array.prototype.slice.call(arguments));
return c.address},ea=function(){c().unbind.apply(c(c.address),Array.prototype.slice.call(arguments));return c.address},E=function(){return z.pushState&&d.state!==g},U=function(){return("/"+i.pathname.replace(new RegExp(d.state),"")+i.search+(L()?"#"+L():"")).replace(T,"/")},L=function(){var a=i.href.indexOf("#");return a!=-1?t(i.href.substr(a+1),k):""},u=function(){return E()?U():L()},V=function(){return"javascript"},O=function(a){a=a.toString();return(d.strict&&a.substr(0,1)!="/"?"/":"")+a},t=function(a,
b){if(d.crawlable&&b)return(a!==""?"!":"")+a;return a.replace(/^\!/,"")},v=function(a,b){return parseInt(a.css(b),10)},H=function(){if(!x){var a=u();if(decodeURI(e)!=decodeURI(a))if(w&&A<7)i.reload();else{w&&!F&&d.history&&q(M,50);_old=e;e=a;G(k)}}},G=function(a){var b=r(W);a=r(a?X:Y);q(fa,10);if(b.isDefaultPrevented()||a.isDefaultPrevented())ga()},ga=function(){e=_old;if(E())z.popState({},"",d.state.replace(/\/$/,"")+(e===""?"/":e));else{x=n;if(B)if(d.history)i.hash="#"+t(e,n);else i.replace("#"+
t(e,n));else if(e!=u())if(d.history)i.hash="#"+t(e,n);else i.replace("#"+t(e,n));w&&!F&&d.history&&q(M,50);if(B)q(function(){x=k},1);else x=k}},fa=function(){if(d.tracker!=="null"&&d.tracker!==I){var a=c.isFunction(d.tracker)?d.tracker:j[d.tracker],b=(i.pathname+i.search+(c.address&&!E()?c.address.value():"")).replace(/\/\//,"/").replace(/^\/$/,"");if(c.isFunction(a))a(b);else if(c.isFunction(j.urchinTracker))j.urchinTracker(b);else if(j.pageTracker!==g&&c.isFunction(j.pageTracker._trackPageview))j.pageTracker._trackPageview(b);
else j._gaq!==g&&c.isFunction(j._gaq.push)&&j._gaq.push(["_trackPageview",decodeURI(b)])}},M=function(){var a=V()+":"+k+";document.open();document.writeln('<html><head><title>"+o.title.replace(/\'/g,"\\'")+"</title><script>var "+C+' = "'+encodeURIComponent(u()).replace(/\'/g,"\\'")+(o.domain!=i.hostname?'";document.domain="'+o.domain:"")+"\";<\/script></head></html>');document.close();";if(A<7)l.src=a;else l.contentWindow.location.replace(a)},$=function(){if(J&&Z!=-1){var a,b,f=J.substr(Z+1).split("&");
for(a=0;a<f.length;a++){b=f[a].split("=");if(/^(autoUpdate|crawlable|history|strict|wrap)$/.test(b[0]))d[b[0]]=isNaN(b[1])?/^(true|yes)$/i.test(b[1]):parseInt(b[1],10)!==0;if(/^(state|tracker)$/.test(b[0]))d[b[0]]=b[1]}J=I}_old=e;e=u()},ba=function(){if(!aa){aa=n;$();var a=function(){ha.call(this);ia.call(this)},b=c("body").ajaxComplete(a);a();if(d.wrap){c("body > *").wrapAll('<div style="padding:'+(v(b,"marginTop")+v(b,"paddingTop"))+"px "+(v(b,"marginRight")+v(b,"paddingRight"))+"px "+(v(b,"marginBottom")+
v(b,"paddingBottom"))+"px "+(v(b,"marginLeft")+v(b,"paddingLeft"))+'px;" />').parent().wrap('<div id="'+C+'" style="height:100%;overflow:auto;position:relative;'+(B&&!window.statusbar.visible?"resize:both;":"")+'" />');c("html, body").css({height:"100%",margin:0,padding:0,overflow:"hidden"});B&&c('<style type="text/css" />').appendTo("head").text("#"+C+"::-webkit-resizer { background-color: #fff; }")}if(w&&!F){a=o.getElementsByTagName("frameset")[0];l=o.createElement((a?"":"i")+"frame");l.src=V()+
":"+k;if(a){a.insertAdjacentElement("beforeEnd",l);a[a.cols?"cols":"rows"]+=",0";l.noResize=n;l.frameBorder=l.frameSpacing=0}else{l.style.display="none";l.style.width=l.style.height=0;l.tabIndex=-1;o.body.insertAdjacentElement("afterBegin",l)}q(function(){c(l).bind("load",function(){var f=l.contentWindow;_old=e;e=f[C]!==g?f[C]:"";if(e!=u()){G(k);i.hash=t(e,n)}});l.contentWindow[C]===g&&M()},50)}q(function(){r("init");G(k)},1);if(!E())if(w&&A>7||!w&&F)if(j.addEventListener)j.addEventListener(K,H,k);
else j.attachEvent&&j.attachEvent("on"+K,H);else ja(H,50);"state"in window.history&&c(window).trigger("popstate")}},ha=function(){var a,b=c("a"),f=b.size(),m=-1,p=function(){if(++m!=f){a=c(b.get(m));a.is('[rel*="address:"]')&&a.address('[rel*="address:"]');q(p,1)}};q(p,1)},ia=function(){if(d.crawlable){var a=i.pathname.replace(/\/$/,"");c("body").html().indexOf("_escaped_fragment_")!=-1&&c('a[href]:not([href^=http]), a[href*="'+document.domain+'"]').each(function(){var b=c(this).attr("href").replace(/^http:/,
"").replace(new RegExp(a+"/?$"),"");if(b===""||b.indexOf("_escaped_fragment_")!=-1)c(this).attr("href","#"+encodeURI(decodeURIComponent(b.replace(/\/(.*)\?_escaped_fragment_=(.*)$/,"!$2"))))})}},g,I=null,C="jQueryAddress",K="hashchange",W="change",X="internalChange",Y="externalChange",n=true,k=false,d={autoUpdate:n,crawlable:k,history:n,strict:n,wrap:k},D=c.browser,A=parseFloat(D.version),w=!c.support.opacity,B=D.webkit||D.safari,j=function(){try{return top.document!==g&&top.document.title!==g?top:
window}catch(a){return window}}(),o=j.document,z=j.history,i=j.location,ja=setInterval,q=setTimeout,T=/\/{2,9}/g;D=navigator.userAgent;var F="on"+K in j,l,J=c("script:last").attr("src"),Z=J?J.indexOf("?"):-1,P=o.title,x=k,aa=k,ca=n,N=k,e=u();_old=e;if(w){A=parseFloat(D.substr(D.indexOf("MSIE")+4));if(o.documentMode&&o.documentMode!=A)A=o.documentMode!=8?7:8;var da=o.onpropertychange;o.onpropertychange=function(){da&&da.call(o);if(o.title!=P&&o.title.indexOf("#"+u())!=-1)o.title=P}}if(z.navigationMode)z.navigationMode=
"compatible";if(document.readyState=="complete")var ka=setInterval(function(){if(c.address){ba();clearInterval(ka)}},50);else{$();c(ba)}c(window).bind("popstate",function(){if(decodeURI(e)!=decodeURI(u())){_old=e;e=u();G(k)}}).bind("unload",function(){if(j.removeEventListener)j.removeEventListener(K,H,k);else j.detachEvent&&j.detachEvent("on"+K,H)});return{bind:function(){return h.apply(this,s(arguments))},unbind:function(){return ea.apply(this,s(arguments))},init:function(){return h.apply(this,["init"].concat(s(arguments)))},
change:function(){return h.apply(this,[W].concat(s(arguments)))},internalChange:function(){return h.apply(this,[X].concat(s(arguments)))},externalChange:function(){return h.apply(this,[Y].concat(s(arguments)))},baseURL:function(){var a=i.href;if(a.indexOf("#")!=-1)a=a.substr(0,a.indexOf("#"));if(/\/$/.test(a))a=a.substr(0,a.length-1);return a},autoUpdate:function(a){if(a!==g){d.autoUpdate=a;return this}return d.autoUpdate},crawlable:function(a){if(a!==g){d.crawlable=a;return this}return d.crawlable},
history:function(a){if(a!==g){d.history=a;return this}return d.history},state:function(a){if(a!==g){d.state=a;var b=U();if(d.state!==g)if(z.pushState)b.substr(0,3)=="/#/"&&i.replace(d.state.replace(/^\/$/,"")+b.substr(2));else b!="/"&&b.replace(/^\/#/,"")!=L()&&q(function(){i.replace(d.state.replace(/^\/$/,"")+"/#"+b)},1);return this}return d.state},strict:function(a){if(a!==g){d.strict=a;return this}return d.strict},tracker:function(a){if(a!==g){d.tracker=a;return this}return d.tracker},wrap:function(a){if(a!==
g){d.wrap=a;return this}return d.wrap},update:function(){N=n;this.value(e);N=k;return this},title:function(a){if(a!==g){q(function(){P=o.title=a;if(ca&&l&&l.contentWindow&&l.contentWindow.document){l.contentWindow.document.title=a;ca=k}},50);return this}return o.title},value:function(a){if(a!==g){a=O(a);if(a=="/")a="";if(e==a&&!N)return;_old=e;e=a;if(d.autoUpdate||N){G(n);if(E())z[d.history?"pushState":"replaceState"]({},"",d.state.replace(/\/$/,"")+(e===""?"/":e));else{x=n;if(B)if(d.history)i.hash=
"#"+t(e,n);else i.replace("#"+t(e,n));else if(e!=u())if(d.history)i.hash="#"+t(e,n);else i.replace("#"+t(e,n));w&&!F&&d.history&&q(M,50);if(B)q(function(){x=k},1);else x=k}}return this}return O(e)},path:function(a){if(a!==g){var b=this.queryString(),f=this.hash();this.value(a+(b?"?"+b:"")+(f?"#"+f:""));return this}return O(e).split("#")[0].split("?")[0]},pathNames:function(){var a=this.path(),b=a.replace(T,"/").split("/");if(a.substr(0,1)=="/"||a.length===0)b.splice(0,1);a.substr(a.length-1,1)=="/"&&
b.splice(b.length-1,1);return b},queryString:function(a){if(a!==g){var b=this.hash();this.value(this.path()+(a?"?"+a:"")+(b?"#"+b:""));return this}a=e.split("?");return a.slice(1,a.length).join("?").split("#")[0]},parameter:function(a,b,f){var m,p;if(b!==g){var Q=this.parameterNames();p=[];b=b===g||b===I?"":b.toString();for(m=0;m<Q.length;m++){var R=Q[m],y=this.parameter(R);if(typeof y=="string")y=[y];if(R==a)y=b===I||b===""?[]:f?y.concat([b]):[b];for(var S=0;S<y.length;S++)p.push(R+"="+y[S])}c.inArray(a,
Q)==-1&&b!==I&&b!==""&&p.push(a+"="+b);this.queryString(p.join("&"));return this}if(b=this.queryString()){f=[];p=b.split("&");for(m=0;m<p.length;m++){b=p[m].split("=");b[0]==a&&f.push(b.slice(1).join("="))}if(f.length!==0)return f.length!=1?f:f[0]}},parameterNames:function(){var a=this.queryString(),b=[];if(a&&a.indexOf("=")!=-1){a=a.split("&");for(var f=0;f<a.length;f++){var m=a[f].split("=")[0];c.inArray(m,b)==-1&&b.push(m)}}return b},hash:function(a){if(a!==g){this.value(e.split("#")[0]+(a?"#"+
a:""));return this}a=e.split("#");return a.slice(1,a.length).join("#")}}}();c.fn.address=function(r){var s;if(typeof r=="string"){s=r;r=undefined}c(this).attr("address")||c(s?s:this).live("click",function(h){if(h.shiftKey||h.ctrlKey||h.metaKey||h.which==2)return true;if(c(this).is("a")){h.preventDefault();h=r?r.call(this):/address:/.test(c(this).attr("rel"))?c(this).attr("rel").split("address:")[1].split(" ")[0]:c.address.state()!==undefined&&!/^\/?$/.test(c.address.state())?c(this).attr("href").replace(new RegExp("^(.*"+
c.address.state()+"|\\.)"),""):c(this).attr("href").replace(/^(#\!?|\.)/,"");c.address.value(h)}}).live("submit",function(h){if(c(this).is("form")){h.preventDefault();h=c(this).attr("action");h=r?r.call(this):(h.indexOf("?")!=-1?h.replace(/&$/,""):h+"?")+c(this).serialize();c.address.value(h)}}).attr("address",true);return this}})(jQuery);
/* http://keith-wood.name/svg.html
   SVG/jQuery DOM compatibility for jQuery v1.4.3.
   Written by Keith Wood (kbwood{at}iinet.com.au) April 2009.
   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
   Please attribute the author if you use it. */
(function($){$.fn.addClass=function(e){return function(d){d=d||'';return this.each(function(){if(isSVGElem(this)){var c=this;$.each(d.split(/\s+/),function(i,a){var b=(c.className?c.className.baseVal:c.getAttribute('class'));if($.inArray(a,b.split(/\s+/))==-1){b+=(b?' ':'')+a;(c.className?c.className.baseVal=b:c.setAttribute('class',b))}})}else{e.apply($(this),[d])}})}}($.fn.addClass);$.fn.removeClass=function(e){return function(d){d=d||'';return this.each(function(){if(isSVGElem(this)){var c=this;$.each(d.split(/\s+/),function(i,a){var b=(c.className?c.className.baseVal:c.getAttribute('class'));b=$.grep(b.split(/\s+/),function(n,i){return n!=a}).join(' ');(c.className?c.className.baseVal=b:c.setAttribute('class',b))})}else{e.apply($(this),[d])}})}}($.fn.removeClass);$.fn.toggleClass=function(c){return function(a,b){return this.each(function(){if(isSVGElem(this)){if(typeof b!=='boolean'){b=!$(this).hasClass(a)}$(this)[(b?'add':'remove')+'Class'](a)}else{c.apply($(this),[a,b])}})}}($.fn.toggleClass);$.fn.hasClass=function(d){return function(b){b=b||'';var c=false;this.each(function(){if(isSVGElem(this)){var a=(this.className?this.className.baseVal:this.getAttribute('class')).split(/\s+/);c=($.inArray(b,a)>-1)}else{c=(d.apply($(this),[b]))}return!c});return c}}($.fn.hasClass);$.fn.attr=function(h){return function(b,c,d){if(typeof b==='string'&&c===undefined){var e=h.apply(this,[b,c,d]);if(e&&e.baseVal&&e.baseVal.numberOfItems!=null){c='';e=e.baseVal;for(var i=0;i<e.numberOfItems;i++){var f=e.getItem(i);switch(f.type){case 1:c+=' matrix('+f.matrix.a+','+f.matrix.b+','+f.matrix.c+','+f.matrix.d+','+f.matrix.e+','+f.matrix.f+')';break;case 2:c+=' translate('+f.matrix.e+','+f.matrix.f+')';break;case 3:c+=' scale('+f.matrix.a+','+f.matrix.d+')';break;case 4:c+=' rotate('+f.angle+')';break;case 5:c+=' skewX('+f.angle+')';break;case 6:c+=' skewY('+f.angle+')';break}}e=c.substring(1)}return(e&&e.baseVal?e.baseVal.valueAsString:e)}var g=b;if(typeof b==='string'){g={};g[b]=c}return this.each(function(){if(isSVGElem(this)){for(var n in g){var a=($.isFunction(g[n])?g[n]():g[n]);(d?this.style[n]=a:this.setAttribute(n,a))}}else{h.apply($(this),[b,c,d])}})}}($.fn.attr);$.fn.removeAttr=function(b){return function(a){return this.each(function(){if(isSVGElem(this)){(this[a]&&this[a].baseVal?this[a].baseVal.value='':this.setAttribute(a,''))}else{b.apply($(this),[a])}})}}($.fn.removeAttr);function anySVG(a){for(var i=0;i<a.length;i++){if(a[i].nodeType==1&&a[i].namespaceURI==$.svg.svgNS){return true}}return false}$.expr.relative['+']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['+']);$.expr.relative['>']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['>']);$.expr.relative['']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['']);$.expr.relative['~']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['~']);$.expr.find.ID=function(d){return function(a,b,c){return(isSVGElem(b)?[b.ownerDocument.getElementById(a[1])]:d(a,b,c))}}($.expr.find.ID);var j=document.createElement('div');j.appendChild(document.createComment(''));if(j.getElementsByTagName('*').length>0){$.expr.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==='*'){var d=[];for(var i=0;c[i]||c.item(i);i++){if((c[i]||c.item(i)).nodeType===1){d.push(c[i]||c.item(i))}}c=d}return c}}$.expr.preFilter.CLASS=function(a,b,c,d,f,g){a=' '+a[1].replace(/\\/g,'')+' ';if(g){return a}for(var i=0,elem={};elem!=null;i++){elem=b[i];if(!elem){try{elem=b.item(i)}catch(e){}}if(elem){var h=(!isSVGElem(elem)?elem.className:(elem.className?elem.className.baseVal:'')||elem.getAttribute('class'));if(f^(h&&(' '+h+' ').indexOf(a)>-1)){if(!c)d.push(elem)}else if(c){b[i]=false}}}return false};$.expr.filter.CLASS=function(a,b){var c=(!isSVGElem(a)?a.className:(a.className?a.className.baseVal:a.getAttribute('class')));return(' '+c+' ').indexOf(b)>-1};$.expr.filter.ATTR=function(g){return function(c,d){var e=null;if(isSVGElem(c)){e=d[1];$.expr.attrHandle[e]=function(a){var b=a.getAttribute(e);return b&&b.baseVal||b}}var f=g(c,d);if(e){$.expr.attrHandle[e]=null}return f}}($.expr.filter.ATTR);function isSVGElem(a){return(a.nodeType==1&&a.namespaceURI==$.svg.svgNS)}})(jQuery);

		<script type="text/javascript" src="annodoc/head.load.min.js"></script>
		<script type="text/javascript">

	var root = 'annodoc/'; // filled in by jekyll
    head.js(
        // External libraries
        root + 'jquery.min.js',
        root + 'jquery.svg.min.js',
        root + 'jquery.svgdom.min.js',
        root + 'jquery.timeago.js',
        root + 'jquery-ui.min.js',
        root + 'waypoints.min.js',
        root + 'jquery.address.min.js',

        // brat helper modules
        root + 'configuration.js',
        root + 'util.js',
        root + 'annotation_log.js',
        root + 'webfont.js',
        // brat modules
        root + 'dispatcher.js',
        root + 'url_monitor.js',
        root + 'visualizer.js',

        // embedding configuration
        root + 'config.js',
        // project-specific collection data
        root + 'collections.js',

        // NOTE: non-local libraries
        root +'annodoc.js',
        root +'conllu.js'
    );

    var webFontURLs = [
//        root + 'Astloch-Bold.ttf',
        root + 'PT_Sans-Caption-Web-Regular.ttf',
        root + 'Liberation_Sans-Regular.ttf'
    ];

</script>
		
/* -*- Mode: CSS; tab-width: 2; indent-tabs-mode: nil; -*- */
/* vim:set ft=css ts=2 sw=2 sts=2 autoindent: */

/* Styling for annotations */



.annodoc .annodoc #svg {
  margin: 34px auto 100px;
  padding-top: 15px;
}

.annodoc .center_wrapper {
  display: table;
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
}
.annodoc .center_wrapper > div {
  display: table-cell;
  vertical-align: middle;
}
.annodoc .center_wrapper > div > div {
  /* match jQuery UI .ui-widget-content color */
  color: #2e6e9e;
  margin-left: auto;
  margin-right: auto;
  width: 30em;
  font-size: 12px;
  font-family: 'Liberation Sans', Verdana, Arial, Helvetica, sans-serif;
}
.annodoc .center_wrapper > div > div h1 {
  text-align: center;
  font-size: 14px;
}
.annodoc .annodoc #no_svg_wrapper {
  display: none;
}

.annodoc svg {
  width: 100%;
  height: 1px;
  border: 1px solid #7fa2ff;
  font-size: 15px;
  /* font-size: 14px; */
}
.annodoc svg.reselect {
  border: 1px solid #ff3333;
}
.annodoc div.haserror > svg {
  border: 1px solid red;
}

/* "basic" font */
.annodoc text {
  font-size: 13px;
  font-family: 'Liberation Sans', Verdana, Arial, Helvetica, sans-serif;
}

.annodoc @-webkit-keyframes pulse {
  0% { color: #9999ff; margin-top: 0; }
  50% { color: #ffffff; margin-top: 7px; }
  100% { color: #9999ff; margin-top: 0; }
}

.annodoc path {
  pointer-events: none;
}

/* "basic" font */
.annodoc .span text {
  /*  font-size: 10.5px; */
  font-size: 10px;
  text-anchor: middle;
  font-family: 'PT Sans Caption', sans-serif;
  pointer-events: none;
}

/* this should likely match span font */
.annodoc .span_type_label {
  font-size: 11px;
  font-family: 'PT Sans Caption', sans-serif;
}
/* this should likely match arc font */
.annodoc .arc_type_label {
  font-size: 11px;
  font-family: 'PT Sans Caption', sans-serif;
}
.annodoc .attribute_type_label .ui-button-text {
  font-size: 11px;
  font-family: 'PT Sans Caption', sans-serif;
}

.annodoc .span rect {
  stroke-width: 0.75;
}

.annodoc .glyph {
  fill: #444444;
  font-family: sans-serif;
  font-weight: bold;
}
.annodoc .attribute_warning {
  baseline-shift: super;
  font-size: 70%;
}

.annodoc .span rect.False_positive {
  stroke: #ff4141;
  stroke-width: 2;
}

.annodoc .shadow_True_positive {
  fill: #00ff00;
}

.annodoc .shadow_False_positive {
  fill: #ff4141;
}

.annodoc .annodoc #commentpopup.comment_False_positive {
  background-color: #ff4141;
}

.annodoc .span rect.False_negative {
  stroke: #c20000;
  fill: #ffffff;
  stroke-width: 2;
}

.annodoc .shadow_False_negative {
  fill: #c20000;
}

.annodoc .annodoc #commentpopup.comment_False_negative {
  background-color: #c20000;
}

.annodoc .span rect.AnnotationError {
  stroke-width: 1;
}

.annodoc .shadow_AnnotationError {
  fill: #ff0000;
}

.annodoc .annodoc #commentpopup.comment_AnnotationError {
  background-color: #ff7777;
}

.annodoc .span rect.AnnotationWarning {
  stroke-width: 1;
}

.annodoc .shadow_AnnotationWarning {
  fill: #ff8800;
}

.annodoc .annodoc #commentpopup.comment_AnnotationWarning {
  background-color: #ff9900;
}

.annodoc .shadow_AnnotatorNotes {
  /* fill: #00cc00; */
  fill: #3ab7ee;
}
.annodoc .annodoc #commentpopup.comment_AnnotatorNotes {
  /* background-color: #00cc00; */
  /* background-color: #DAF7DC; */
  background-color: #d7e7ee;
}

.annodoc .shadow_Normalized {
  /* fill: #00cc00; */
  fill: #3aee37;
}
.annodoc .annodoc #commentpopup.comment_Normalized {
  /* background-color: #00cc00; */
  /* background-color: #DAF7DC; */
  background-color: #d7eee7;
}
rect.Normalized {
  stroke-width: 1.5;
}

.annodoc .shadow_AnnotationIncomplete {
  fill: #aaaaaa;
}
.annodoc .span rect.AnnotationIncomplete {
  stroke: #002200;
  stroke-width: 0.5;
  fill: #ffffff;
}
.annodoc .annodoc #commentpopup.comment_AnnotationIncomplete {
  background-color: #ffff77;
}

.annodoc .shadow_AnnotationUnconfirmed {
  fill: #eeeeee;
}
.annodoc .span rect.AnnotationUnconfirmed {
  stroke: #002200;
  stroke-width: 0.5;
  opacity : 0.5;
}
.annodoc .annodoc #commentpopup.comment_AnnotationUnconfirmed {
  background-color: #ddddff;
}

.annodoc .span rect.True_positive {
  /* stroke: #007700; */
}

rect.shadow_EditHighlight {
  fill: #ffff99;
}
.annodoc .shadow_EditHighlight_arc {
  stroke: #ffff99;
}

.annodoc .span path {
  fill: none;
}

.annodoc .span path.curly {
  /* 'stroke' def here blocks 'stroke' set in JS..? */
  /* stroke: grey; */
  stroke-width: 0.5;
}

.annodoc .span path.boxcross {
  stroke: black;
  opacity: 0.5;
}

.annodoc .arcs path {
  stroke: #989898;
  fill: none;
  stroke-width: 1;
}

.annodoc .arcs .highlight path {
  stroke: #000000;
  stroke-width: 1.5;
  opacity: 1;
}
.annodoc .arcs .highlight text {
  stroke: black;
  fill: black;
  stroke-width: 0.5;
}
.annodoc .span.highlight rect {
  stroke-width: 2px;
}
.annodoc .span rect.reselect {
  stroke-width: 2px;
}
.annodoc .span rect.reselectTarget {
  stroke-width: 2px;
}
.annodoc .arcs .reselect path {
  stroke-width: 2px;
  stroke: #ff0000 !important;
}
.annodoc .arcs .reselect text {
  fill: #ff0000 !important;
}

.annodoc .span rect.badTarget {
  stroke: #f00;
}

.annodoc .arcs text {
  font-size: 9px;
  text-anchor: middle;
  font-family: 'PT Sans Caption', sans-serif;
  /* dominant-baseline: central; */
  cursor: default;
}

.annodoc .background0 {
  stroke: none;
  fill: #ffffff;
}

.annodoc .background1 {
  stroke: none;
  fill: #eeeeee;
}

.annodoc .backgroundHighlight {
  stroke: none;
  fill: #ffff99;
}

.annodoc .sentnum text {
  display: none; /* sentence number disabled for the time being. */
  fill: #999999;
  text-anchor: end;
}

.annodoc .sentnum path {
  display: none; /* sentence number disabled for the time being. */
  stroke: #999999;
  stroke-width: 1px;
}

.annodoc .span_cue {
  fill: #eeeeee !important;
}

.annodoc .drag_stroke {
  stroke: black;
}
.annodoc .drag_fill {
  fill: black;
}


.annodoc .dialog {
  display: none;
}
.annodoc .annodoc #span_free_div, #arc_free_div {
  float: left;
}
fieldset {
  /* match relevant parts of jQuery UI .ui-widget-content */
  border: 1px solid #a6c9e2;
  /* round corners, same as jQuery UI .ui-corner-all */
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
  /* to prevent border from running into legend */
  margin-top: 5px;
}
fieldset legend {
  font-weight: bold;
  font-size: 90%;
  -moz-border-radius: 3px;
  -webkit-border-radius: 3px;
  border-radius: 3px;
  background-color: #70a8d2;
  color: white;
  padding-left: 0.5em;
  padding-right: 0.5em;
}
.annodoc .label-like {
   /* for UI consistency, this should match the "fieldset legend" style;
      sorry about the duplication. */
  font-family: monospace;
  font-weight: bold;
  font-size: 90%;
  /* match jQuery UI .ui-widget-content color */
  color: #2e6e9e;
}   

.annodoc .accesskey {
  text-decoration: underline;
}


.annodoc .shadow {
  -moz-box-shadow: 5px 5px 5px #444444;
  -webkit-box-shadow: 5px 5px 5px #444444;
  box-shadow: 5px 5px 5px #444444;
}

.annodoc .annodoc #span_selected, #arc_origin, #arc_target {
  font-weight: bold;
}

.annodoc .annodoc #commentpopup {
  font-family: 'Liberation Sans', Verdana, Arial, Helvetica, sans-serif;
  position: fixed;
  top: 0;
  left: 0;
  opacity: 0.95;
  padding: 10px;
  display: none;
  border: 1px outset #000000;
  background-color: #f5f5f9;
  /* background-color: #d7e7ee; */
  /* background-color: #eeeeee; */
  color: #000000;
  z-index: 20;
  -moz-box-shadow: 5px 5px 5px #aaaaaa;
  -webkit-box-shadow: 5px 5px 5px #aaaaaa;
  box-shadow: 5px 5px 5px #aaaaaa;
  -moz-border-radius: 3px;
  -webkit-border-radius: 3px;
  border-radius: 3px;
  max-width: 80%;
}
.annodoc .annodoc #more_info_readme {
  height: 350px;
}
.annodoc .annodoc #readme_container {
  position: relative;
}
.annodoc .annodoc #more_readme_button {                   
  padding: 2px 5px;
  position: absolute;
  top: -2px;
  right: -2px;
}

.annodoc .comment_id {
  vertical-align: top;
  float:right;
  font-size: 75%;
  font-family: monospace;
  color: #333333;
}
.annodoc .comment_type {
  /* font-weight: bold; */
}
.annodoc .comment_text {
  font-weight: bold;
}
.annodoc .comment_type_id_wrapper {
  /* padding for floating ID */
  padding-right: 2em;
}
.annodoc .norm_info_label {
  font-weight: bold;
  font-size: 80%;
}
.annodoc .norm_info_value {
  font-size: 80%;
}
.annodoc .norm_info_img {
  float: right;
  margin-left: 1em;
}

.annodoc .annodoc #search_form select {
  width: 100%; /* deal with overflowing selectboxes */
}

.annodoc .scroll_fset {
  height: 200px;
}
.annodoc .scroll_fset fieldset {
  height: 100%;
  overflow-x: hidden;
  overflow-y: hidden;
}
/* HACK to display <legend> properly */
  .scroll_fset {
    margin-bottom: 2.5em;
  }
  .scroll_fset fieldset {
    padding-bottom: 2em;
  }
/* end HACK */
.annodoc .scroll_fset div.scroller {
  overflow: auto;
  width: 100%;
  height: 100%;
}

.annodoc .annodoc #span_highlight_link, #arc_highlight_link, #viewspan_highlight_link {
  float: right;
}

.annodoc .unselectable {
   -moz-user-select: -moz-none;
   -khtml-user-select: none;
   -webkit-user-select: none;
   -o-user-select: none;
   user-select: none;
   cursor: default;
}
@font-face {
  font-family: 'Liberation Sans';
  font-style: normal;
  font-weight: normal;
  src: local('Liberation Sans'), local('Liberation-Sans'), url('Liberation_Sans-Regular.ttf') format('truetype');
}
@font-face {
  font-family: 'PT Sans Caption';
  font-style: normal;
  font-weight: normal;
  src: local('PT Sans Caption'), local('PTSans-Caption'), url('PT_Sans-Caption-Web-Regular.ttf') format('truetype');
}

/* Apple iPad, iPhone, iPod */

* {
   -webkit-tap-highlight-color: rgba(0,0,0,0);
   -webkit-text-size-adjust: none;
   select: none;
}

.annodoc .span rect.AddedAnnotation {
  stroke: #ff4141;
  stroke-width: 2;
}
.annodoc .shadow_AddedAnnotation {
  fill: #ff4141;
}
.annodoc .annodoc #commentpopup.comment_AddedAnnotation {
  background-color: #ffcccc;
}
.annodoc .span rect.MissingAnnotation {
  stroke: #ffffff;
  stroke-width: 2;
}
.annodoc .shadow_MissingAnnotation {
  fill: #ff4141;
  opacity: 0.3;
}
.annodoc .annodoc #commentpopup.comment_MissingAnnotation {
  background-color: #ffcccc;
}
.annodoc .span rect.MissingAnnotation + text {
  opacity: 0.5;
}
.annodoc .span rect.ChangedAnnotation {
  stroke: #ffff99;
  stroke-width: 2;
}
.annodoc .shadow_ChangedAnnotation {
  fill: #ff4141;
}
.annodoc .annodoc #commentpopup.comment_ChangedAnnotation {
  background-color: #ffcccc;
}

/* embedding */
pre.embedding {
  padding: 0;
  border: none;
  overflow: visible;
  -webkit-border-radius: 0;
  -moz-border-radius: 0;
  border-radius: 0;
  background: none;
  white-space: normal;
}

/* same margins for <div> and <pre><div> variants */
.annodoc .embedding,
.annodoc .embedding.embedded-wrapper {
  margin: 0.5em 0;
}

pre.embedded-wrapper,
code.embedded-wrapper,
.annodoc .embedded-wrapper {
  margin: 0;
  padding: 0;
  border: none;
  -webkit-border-radius: 0;
  -moz-border-radius: 0;
  border-radius: 0;
  background: none;
  white-space: normal;
}

.annodoc .show-hide-div {
  text-align: right;
}
.annodoc .show-hide-toggle {
  margin-top: 0px;
  margin-bottom: 0px;
  margin-left: 10px;
  margin-right: 10px;
  -moz-border-bottom-left-radius: 0px;
  -moz-border-bottom-right-radius: 0px;
  -webkit-border-bottom-left-radius: 0px;
  -webkit-border-bottom-right-radius: 0px;
  border-bottom-left-radius: 0px;
  border-bottom-right-radius: 0px;
}
.annodoc .show-hide-toggle .ui-button-text {
  padding: 1px 4px;
  font-size: 9px;
}
.annodoc .embedded-brat-data {
  display: block;
  width: 100%;
  height: 100px;
  font-size: 11px;
  border: 2px inset
}
a.doclink {
/*   text-decoration: none; */
}
a.doclabel {
  /* doc link with type label as text (#72), style based on .code */
  line-height: 1.4;
/*   font: 12px 'Bitstream Vera Sans Mono','Courier',monospace; */
  font-family: 'Bitstream Vera Sans Mono','Courier',monospace;
  margin: 0 2px;
/*   padding: 0 5px; */
}

.annodoc .ui-tabs .ui-tabs-nav li { 
    /* override tab font text */
    font-size: 90%;
}
/* -*- Mode: CSS; tab-width: 4; indent-tabs-mode: nil; -*- */
/* vim:set ft=css ts=4 sw=4 sts=4 autoindent: */

/* style for home page and manual. */

/* @import url(http://fonts.googleapis.com/css?family=Open+Sans:400,600,700,400italic,600italic,700italic&subset=latin&effect=smoothing); */
@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700,400italic,600italic,700italic&subset=latin&effect=smoothing);

/* override sentence number text style from brat style-vis.css */
.sentnum text {
  display: none;
}

/* override sentence number separator line style from brat style-vis.css */
.sentnum path {
  display: none;
}

img.bordered {
  border: 1px solid black;
}

#oldbrowser {
  color: red;
}

#partialsupport {
  font-weight: bold;
}

a img {
  border: 0;
}
a.fronta:visited {
  color: #2200cc;
}
.smallgray {
  font-size: 80%;
  color:gray; 
}
.smallgray a {
  color:gray; 
}
.smallgray a:visited {
  color:#aaaaaa; 
}

span.tt {
  font-family: 'Bitstream Vera Sans Mono','Courier',monospace;
}

pre {
  display: block;
  white-space: pre;
  background-color: #F8F8F8;
  border: 1px solid #CCC;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px;
}

code {
  line-height: 1.4;
/*   font: 12px 'Bitstream Vera Sans Mono','Courier',monospace; */
  font-family: 'Bitstream Vera Sans Mono','Courier',monospace;
  margin: 0 2px;
/*   padding: 0 5px; */
/*   border: 1px solid #EAEAEA; */
/*   background-color: #F8F8F8; */
/*   border-radius: 3px; */
}


pre > code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent;
}

.hp-unselectable {
   -moz-user-select: -moz-none;
   -khtml-user-select: none;
   -webkit-user-select: none;
   -o-user-select: none;
   user-select: none;
   cursor: default;
}

#notebox {
  width:50%;
  margin:auto;
  text-align:center;
  background-color: #ffcccc;
  padding: 10px;
  margin: 20px;
}

.redacted {
/*  background-color: #ffcccc; */
  background-color: #cccccc;
}

/* img {
  -moz-box-shadow: 5px 5px 5px #999999;
  -webkit-box-shadow: 5px 5px 5px #999999;
  box-shadow: 5px 5px 5px #999999;
  margin-bottom: 0.5em;
} */

img.right {
  border: 1px solid black;
  margin:auto;
  float:right;
  margin-left: 5em;
  margin-bottom: 3em;
  margin-top: 1em;
}

img.tinymargin {
  margin: 0.3em;
}

img.icon {
  vertical-align:bottom;
  -moz-box-shadow: 0px 0px 0px #ffffff;
  -webkit-box-shadow: 0px 0px 0px #ffffff;
  box-shadow: 0px 0px 0px #ffffff;
  margin-bottom: 0;
}

body { 
/*   font-family: 'Open Sans', arial, sans-serif; */
/*  font-family: 'Open Sans', Arial, freesans, sans-serif; */
  font-family: 'Source Sans Pro', Arial, freesans, sans-serif;
  font-size: 14px;
  overflow-y: scroll;
}

h2 {
  margin-top: 2em;
}

h2.nomargin {
  margin-top: 0em;
}

h3 {
  margin-top: 1.5em;
}

.infolabel {
  font-weight: bold;  
  display: inline-block;
  width: 10em;
}

.h1subtitle {
  font-size: 15px;
  color: gray;
}

div.center {
  display: block;
  margin-left: auto;
  margin-right: auto;
}
#hp-header {
  padding-top: 5px;
  margin-bottom: 5px;
  padding-left: 2em;
}
#menulogo {
  padding-top: 0.5em;
  padding-right: 1em;
  float: right;  
}
#hp-header span a {
  text-decoration: none; /* no underline in menu */
}
#hp-header > span, #hp-header > a {
  margin-right: 20px;
}

.bigbutton {
  font-size: 20px !important;
  padding: 10px 15px !important;
}

#hp-header > .hp-logo {
  font-size: 1.2em;
  margin-top: -10px;
  margin-bottom: -10px;
  text-shadow: #000 0 0 3px;
}

#main {
  max-width:1000px;
  min-width: 600px;
  padding-left: 2em;
  padding-right: 2em;
}
#content {
  margin: 15px;
}

#manual-main {
  max-width:960px;
  padding-left: 2em;
  padding-right: 2em;
  padding-bottom: 5em;
}

#footer {
  font-size: 9px;
  color: #999;
  border-top: 1px solid lightgray;
  margin-top: 10em;
  margin-bottom: 5em;
  clear: both;
}

.footer-text {
  float: right;
}

.footer-logo {
  float: right;
}

.footer-logo img {
  margin: 0;
  -moz-box-shadow: 0px 0px 0px #fff;
  -webkit-box-shadow: 0px 0px 0px #fff;
  box-shadow: 0px 0px 0px #fff;
}

#titleblock {
  height: 100px;
}

#downloadblock {
  float: right;
  width: 300px;
  margin: 1em;
}

.image {
  margin-left: 2em;    
}

.image .caption { 
  font-size: 85%;
  font-family: sans-serif;
  text-align: center;
}

#browsersupport {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

.annotation_wrapper { 
  margin-left: 2em;
  border: 1px solid;
  border-color: rgb(136, 136, 136);
  background-color: rgb(207, 226, 243);
}

.annotation_wrapper td {
  padding: 1em;
}

.annotation_table {
  border: none;
}

.annotation_table th {
  padding: 0em;
  font-family: monospace;
  font-size: 13px;
}

.annotation_table td {
  padding: 0;
  font-family: monospace;
  font-size: 13px;
}

td.complete {
  background-color: lightgreen;
}

td.partial {
  background-color: #F0F0B0;
}

td.none {
  /* background-color: #FF9090;*/
  background-color: white;
}

.rounded {
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
}

/* "starburst" CSS adapted from
   http://matthewjamestaylor.com/blog/css3-starbursts */
.starburst {
  z-index:1000;
  position: relative;
  top: 0px;
  left: 600px;
  margin-left:-2.5em;
  margin-top: -0.5em;
  background: none;
  text-align: center;
  text-decoration: none;
  -webkit-transform:rotate(-90deg);
  -moz-transform:rotate(-90deg);
  rotation: -90deg;
}
.starburst span {
  display: block;
  -webkit-transform:rotate(22.5deg);
  -moz-transform:rotate(22.5deg);
  rotation: 22.5deg;
}
.starburst,
.starburst span {
  width: 5em;
  height: 5em;
  display: block;
  /* border-radius: 3em; */
  font-size: 13px;
  -webkit-transition: 0.3s ease;
  -moz-transition: 0.3s ease;
  transition: 0.3s ease;
}
.starburst span.star,
.starburst span.sshadow {
  position: absolute;
}
.starburst span.star {
  color: white;
  font-weight: bold;
  background: #6da6d1;
}
.starburst span.sshadow {
  -moz-box-shadow: 0px 0px 0px 2px #fff;
  -webkit-box-shadow: 0px 0px 0px 2px #fff;
  box-shadow: 0px 0px 0px 2px #fff;
}
.starburst:hover {
  -webkit-transform:rotate(-112.5deg);
  -moz-transform:rotate(-112.5deg);
  rotation: -112.5deg;
}
.starburst:hover span {
  border-radius: 0px;
}
.starburst:hover span.star {
  text-shadow: 0 0 6px #fff, 0 0 2px #fff;
}
.starburst:hover span.sshadow {
  -moz-box-shadow: 0px 0px 10px 10px #fff;
  -webkit-box-shadow: 0px 0px 10px 10px #fff;
  box-shadow: 0px 0px 10px 10px #fff;
}
.center-wrapper {
  width: 5em;
  height: 5em; 
  display: table;
}
.center-wrapper p {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
}

div.example {
  border: 1px solid gray;
  margin:1em;
  padding:2em 2em 0.5em 2em;
}

#indextable {
  margin-left: 3em;
  margin-top: 2em;
  /* border: 1px solid lightgray; */
  font-size: 120%;
}

#indextable td, #indextable th {
  padding: 5px;
}

#indextable td.right {
  text-align: right;  
}

#indextable th {
  border-bottom: 1px solid lightgray;
}

table.typeindex {
  vertical-align: top;
  border-collapse:collapse;
  border: 1px solid lightgray;
}

table.typeindex td {
  vertical-align:top;
  border-left: 1px solid lightgray;
  border-right: 1px solid lightgray;
}

table.typeindex thead th {
  padding: 0.5em;
  border-left: 1px solid lightgray;
  border-right: 1px solid lightgray;
}

table.typeindex tbody td {
  padding-left: 0.5em;
  padding-right: 0.5em;
}

table.category {
  margin: 1em;
  width: 100%;
}

table.category td {
  border: none;
}

.jquery-ui-tabs > div {
  font-size: 120%;
}

table.statustable {
  float: right;
  margin-left: 3em;
  border: 1px solid lightgray;
  font-size: 90%;
  color: #444;
}

em strong, b {
  font-weight: 700;
  color: darkred;
}

h1, h2, h3, strong {
  font-weight: 600; /* standard, not extra bold */
}

h4, h5, h6 {
  font-weight: normal;
  text-decoration: underline;
}

.widespan {
    display: inline-block;
    width: 8%;
}

.doublewidespan {
    display: inline-block;
    width: 16%;
}


.tagspan {
    display: inline-block;
    width: 1em;
}

.genreicon {
    margin-left: 2px;
}

.flagspan {
    display: inline-block;
    width: 5%;
    padding-left: 2em;
}

.propertylogo {
    vertical-align: middle;
    height: 1em;
    margin: 0 1em;
    margin-left: auto;
    margin-right: auto;
}
   

.flag {
    vertical-align: middle;
    border: solid grey 1px;
    height: 1em;
    margin: 0 1em;
}

.license {
   vertical-align: middle;
   height: 1.2em;
}

.validationpass {
    color: green;
}

.validationfail {
    color: red;
}


div.bordered-char {
  display:inline-block;
  width: 9px;
  height: 9px;
  border: 1px solid black;
  border-radius: 2px;
  margin: 1px;
}
div.bordered-char span {
  display:inline-block;
  left: 0px;
  width: 100%;
  text-align: center;
  font-size: 11px;
  line-height: 8.5px;
  font-weight: bold;
}
// -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; -*-
// vim:set ft=javascript ts=2 sw=2 sts=2 cindent:
var URLMonitor = (function($, window, undefined) {
    var URLMonitor = function(dispatcher) {
      var that = this;

      var reloadData = true;
      var changed = false;
      var newIntArgs = {};

      that.url_hash = new URLHash();

      // Thanks to:
      // http://kilianvalkhof.com/2010/javascript/the-case-of-the-disappearing-favicon/
      var setFavicon = function() {
        var link = $('link[type="image/x-icon"]').remove().attr("href");
        $('<link href="'+ link +'" rel="shortcut icon" type="image/x-icon" />').appendTo('head');
      }

      var updateURL = function() {
        var new_hash = that.url_hash.getHash();
        changed = false;
        window.location.hash = new_hash;
        setFavicon();
        dispatcher.post('hideForm');
      };

      var setArguments = function(args, force) {
        var oldArgs = that.url_hash.arguments;
        var argSplit = URLHash.splitArgs(args);

        if (!Util.isEqual(that.url_hash.extArguments, argSplit[1])) {
          changed = true;
          that.url_hash.setArguments(args);
          dispatcher.post('argsChanged', [args, oldArgs]);
        }
        if (changed) {
          // hashchange event will trigger
          newIntArgs = argSplit[0];
          updateURL();
        } else if (force || !Util.isEqual(that.url_hash.intArguments, argSplit[0])) {
          // hash is same, but internal arguments differ
          that.url_hash.setArguments(args);
          // hashchange event won't trigger, but internal args have
          // changed, so we have to do updateState's job
          dispatcher.post('hideForm');
          dispatcher.post('argsChanged', [args, oldArgs]);
          dispatcher.post('current', [that.url_hash.collection,
              that.url_hash.document, that.url_hash.arguments, reloadData]);
          reloadData = true;
        }
      };

      var setDocument = function(doc, args) {
        var oldDoc = that.url_hash.document;
        if (oldDoc !== doc) {
          changed = true;
          that.url_hash.setDocument(doc);
          dispatcher.post('docChanged', [doc, oldDoc]);
        }
        setArguments(args || null);
      };

      var setCollection = function(coll, doc, args) {
        var oldColl = that.url_hash.collection;
        if (oldColl !== coll) {
          changed = true;
          that.url_hash.setCollection(coll);

          // keep "blind" down while loading new collection
          $('#waiter').dialog('open');

          dispatcher.post('ajax', [{
              action: 'getCollectionInformation',
              collection: coll
            }, 'collectionLoaded', {
              collection: coll,
              keep: true
            }]);
          dispatcher.post('collectionChanged', [coll, oldColl]);
        }
        setDocument(doc || '', args);
      }

      var updateState = function() {
        dispatcher.post('makeAjaxObsolete');
        if (!changed) {
          var new_url_hash = URLHash.parse(window.location.hash);
          setCollection(new_url_hash.collection, new_url_hash.document,
              $.extend(new_url_hash.arguments, newIntArgs));
          that.url_hash = new_url_hash;
          newIntArgs = {};
        }
       
        dispatcher.post('current', [that.url_hash.collection,
            that.url_hash.document, that.url_hash.arguments, reloadData]);
        reloadData = true;
      };

      var forceUpdate = function() {
        $(window).trigger('hashchange');
      };

      var preventReloadByURL = function() {
        reloadData = false;
      }
      var allowReloadByURL = function() {
        reloadData = true;
      }

      var init = function() {
        $(window).bind('hashchange', updateState);
        forceUpdate();
      }

      dispatcher.
          on('forceUpdate', forceUpdate).
          on('setArguments', setArguments).
          on('setDocument', setDocument).
          on('setCollection', setCollection).
          on('preventReloadByURL', preventReloadByURL).
          on('allowReloadByURL', allowReloadByURL).
          on('init', init);
    };

    return URLMonitor;
})(jQuery, window);

var URLHash = (function($, window, undefined) {
    var URLHash = function(collection, _document, _arguments) {
      var that = this;
      that.collection = collection;
      that.document = _document || '';
      that.arguments = _arguments || {};
      that.calcArgs();
    }

    URLHash.prototype = {
      calcArgs: function() {
        var args = URLHash.splitArgs(this.arguments);
        this.intArguments = args[0];
        this.extArguments = args[1];
      },

      setArgument: function(argument, value) {
        if (!this.arguments) {
          this.arguments = {};
        }
        this.arguments[argument] = value;
        this.calcArgs();
        return this;
      },

      setArguments: function(_arguments) {
        // the $.extend here basically takes a copy; raw assignment
        // would allow changes of the args to alter original, which
        // could be e.g. the "args" of search results
        this.arguments = $.extend({}, _arguments || {});
        this.calcArgs();
        return this;
      },

      setDocument: function(_document) {
        this.document = _document;
        return this;
      },

      setCollection: function(collection) {
        this.collection = collection;
        return this;
      },

      getHash: function() {
        var url_hash = this.collection + this.document;

        var url_args = Util.param(this.extArguments);

        if (url_args.length) {
          url_hash += '?' + url_args;
        }

        if (url_hash.length) {
          url_hash = '#' + url_hash;
        }

        return url_hash;
      },
    };

    // arguments that do not appear in the URL
    var INT_ARGS = ['match', 'matchfocus', 'edited'];

    URLHash.splitArgs = function(args) {
      var intArgs = {};
      var extArgs = $.extend({}, args);
      var intArgNameLen = INT_ARGS.length;
      for (var i = 0; i < intArgNameLen; i++) {
        intArgs[INT_ARGS[i]] = extArgs[INT_ARGS[i]];
        delete extArgs[INT_ARGS[i]];
      }
      return [intArgs, extArgs];
    };

    // TODO: Document and conform variables to the rest of the object
    URLHash.parse = function(hash) {
      if (hash.length) {
        // Remove the leading hash (#)
        hash = hash.substr(1);
      }

      var pathAndArgs = hash.split('?');
      var path = pathAndArgs[0] || '';
      var argsStr = pathAndArgs[1] || '';
      var coll;
      var slashPos = path.lastIndexOf('/');
      if (slashPos === -1) {
        coll = '/';
      } else {
        coll = path.substr(0, slashPos + 1);
        if (coll[coll.length - 1] !== '/') {
          coll += '/';
        }
        if (coll[0] !== '/') {
          coll = '/' + coll;
        }
      }
      var doc = path.substr(slashPos + 1);
      var args = Util.deparam(argsStr);
      return new URLHash(coll, doc, args);
    };

    return URLHash;
})(jQuery, window)
// -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; -*-
// vim:set ft=javascript ts=2 sw=2 sts=2 cindent:
var Util = (function(window, undefined) {

    var fontLoadTimeout = 5000; // 5 seconds

    var monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

    var isMac = navigator.platform == 'MacIntel'; // XXX should we go broader?

    var cmp = function(a,b) {
      return a < b ? -1 : a > b ? 1 : 0;
    }

    var cmpArrayOnFirstElement = function(a,b) {
      a = a[0];
      b = b[0];
      return a < b ? -1 : a > b ? 1 : 0;
    }

    var unitAgo = function(n, unit) {
      if (n == 1) return "" + n + " " + unit + " ago";
      return "" + n + " " + unit + "s ago";
    };

    var formatTimeAgo = function(time) {
      if (time == -1000) {
        return "never"; // FIXME make the server return the server time!
      }

      var nowDate = new Date();
      var now = nowDate.getTime();
      var diff = Math.floor((now - time) / 1000);
      if (!diff) return "just now";
      if (diff < 60) return unitAgo(diff, "second");
      diff = Math.floor(diff / 60);
      if (diff < 60) return unitAgo(diff, "minute");
      diff = Math.floor(diff / 60);
      if (diff < 24) return unitAgo(diff, "hour");
      diff = Math.floor(diff / 24);
      if (diff < 7) return unitAgo(diff, "day");
      if (diff < 28) return unitAgo(Math.floor(diff / 7), "week");
      var thenDate = new Date(time);
      var result = thenDate.getDate() + ' ' + monthNames[thenDate.getMonth()];
      if (thenDate.getYear() != nowDate.getYear()) {
        result += ' ' + thenDate.getFullYear();
      }
      return result;
    }

    var realBBox = function(span) {
      var box = span.rect.getBBox();
      var chunkTranslation = span.chunk.translation;
      var rowTranslation = span.chunk.row.translation;
      box.x += chunkTranslation.x + rowTranslation.x;
      box.y += chunkTranslation.y + rowTranslation.y;
      return box;
    }

    var escapeHTML = function(str) {
      return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
    }

    var escapeHTMLandQuotes = function(str) {
      return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;');
    }

    var escapeHTMLwithNewlines = function(str) {
      return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\n/g,'<br/>');
    }

    var escapeQuotes = function(str) {
      // we only use double quotes for HTML attributes
      return str.replace(/\"/g,'&quot;');
    }

    var getSpanLabels = function(spanTypes, spanType) {
      var type = spanTypes[spanType];
      return type && type.labels || [];
    }

    var spanDisplayForm = function(spanTypes, spanType) {
      var labels = getSpanLabels(spanTypes, spanType);
      return labels[0] || spanType;
    }

    var getArcLabels = function(spanTypes, spanType, arcType, relationTypesHash) {
      var type = spanTypes[spanType];
      var arcTypes = type && type.arcs || [];
      var arcDesc = null;
      // also consider matches without suffix number, if any
      var noNumArcType;
      if (arcType) {
          var splitType = arcType.match(/^(.*?)(\d*)$/);
          noNumArcType = splitType[1];
      }
      $.each(arcTypes, function(arcno, arcDescI) {
        if (arcDescI.type == arcType || arcDescI.type == noNumArcType) {
          arcDesc = arcDescI;
          return false;
        }
      });
      // fall back to relation types for unconfigured or missing def
      if (!arcDesc) {
        arcDesc = $.extend({}, relationTypesHash[arcType] || relationTypesHash[noNumArcType]);
      }
      return arcDesc && arcDesc.labels || [];
    }

    var arcDisplayForm = function(spanTypes, spanType, arcType, relationTypesHash) {
      var labels = getArcLabels(spanTypes, spanType, arcType, relationTypesHash);
      return labels[0] || arcType;
    }

    // TODO: switching to use of $.param(), this function should
    // be deprecated and removed.
    var objectToUrlStr = function(o) {
      a = [];
      $.each(o, function(key,value) {
        a.push(key+"="+encodeURIComponent(value));
      });
      return a.join("&");
    }

    // color name RGB list, converted from
    // http://www.w3schools.com/html/html_colornames.asp
    // with perl as
    //     perl -e 'print "var colors = {\n"; while(<>) { /(\S+)\s+\#([0-9a-z]{2})([0-9a-z]{2})([0-9a-z]{2})\s*/i or die "Failed to parse $_"; ($r,$g,$b)=(hex($2),hex($3),hex($4)); print "    '\''",lc($1),"'\'':\[$r,$g,$b\],\n" } print "};\n" '
    var colors = {
        'aliceblue':[240,248,255],
        'antiquewhite':[250,235,215],
        'aqua':[0,255,255],
        'aquamarine':[127,255,212],
        'azure':[240,255,255],
        'beige':[245,245,220],
        'bisque':[255,228,196],
        'black':[0,0,0],
        'blanchedalmond':[255,235,205],
        'blue':[0,0,255],
        'blueviolet':[138,43,226],
        'brown':[165,42,42],
        'burlywood':[222,184,135],
        'cadetblue':[95,158,160],
        'chartreuse':[127,255,0],
        'chocolate':[210,105,30],
        'coral':[255,127,80],
        'cornflowerblue':[100,149,237],
        'cornsilk':[255,248,220],
        'crimson':[220,20,60],
        'cyan':[0,255,255],
        'darkblue':[0,0,139],
        'darkcyan':[0,139,139],
        'darkgoldenrod':[184,134,11],
        'darkgray':[169,169,169],
        'darkgrey':[169,169,169],
        'darkgreen':[0,100,0],
        'darkkhaki':[189,183,107],
        'darkmagenta':[139,0,139],
        'darkolivegreen':[85,107,47],
        'darkorange':[255,140,0],
        'darkorchid':[153,50,204],
        'darkred':[139,0,0],
        'darksalmon':[233,150,122],
        'darkseagreen':[143,188,143],
        'darkslateblue':[72,61,139],
        'darkslategray':[47,79,79],
        'darkslategrey':[47,79,79],
        'darkturquoise':[0,206,209],
        'darkviolet':[148,0,211],
        'deeppink':[255,20,147],
        'deepskyblue':[0,191,255],
        'dimgray':[105,105,105],
        'dimgrey':[105,105,105],
        'dodgerblue':[30,144,255],
        'firebrick':[178,34,34],
        'floralwhite':[255,250,240],
        'forestgreen':[34,139,34],
        'fuchsia':[255,0,255],
        'gainsboro':[220,220,220],
        'ghostwhite':[248,248,255],
        'gold':[255,215,0],
        'goldenrod':[218,165,32],
        'gray':[128,128,128],
        'grey':[128,128,128],
        'green':[0,128,0],
        'greenyellow':[173,255,47],
        'honeydew':[240,255,240],
        'hotpink':[255,105,180],
        'indianred':[205,92,92],
        'indigo':[75,0,130],
        'ivory':[255,255,240],
        'khaki':[240,230,140],
        'lavender':[230,230,250],
        'lavenderblush':[255,240,245],
        'lawngreen':[124,252,0],
        'lemonchiffon':[255,250,205],
        'lightblue':[173,216,230],
        'lightcoral':[240,128,128],
        'lightcyan':[224,255,255],
        'lightgoldenrodyellow':[250,250,210],
        'lightgray':[211,211,211],
        'lightgrey':[211,211,211],
        'lightgreen':[144,238,144],
        'lightpink':[255,182,193],
        'lightsalmon':[255,160,122],
        'lightseagreen':[32,178,170],
        'lightskyblue':[135,206,250],
        'lightslategray':[119,136,153],
        'lightslategrey':[119,136,153],
        'lightsteelblue':[176,196,222],
        'lightyellow':[255,255,224],
        'lime':[0,255,0],
        'limegreen':[50,205,50],
        'linen':[250,240,230],
        'magenta':[255,0,255],
        'maroon':[128,0,0],
        'mediumaquamarine':[102,205,170],
        'mediumblue':[0,0,205],
        'mediumorchid':[186,85,211],
        'mediumpurple':[147,112,216],
        'mediumseagreen':[60,179,113],
        'mediumslateblue':[123,104,238],
        'mediumspringgreen':[0,250,154],
        'mediumturquoise':[72,209,204],
        'mediumvioletred':[199,21,133],
        'midnightblue':[25,25,112],
        'mintcream':[245,255,250],
        'mistyrose':[255,228,225],
        'moccasin':[255,228,181],
        'navajowhite':[255,222,173],
        'navy':[0,0,128],
        'oldlace':[253,245,230],
        'olive':[128,128,0],
        'olivedrab':[107,142,35],
        'orange':[255,165,0],
        'orangered':[255,69,0],
        'orchid':[218,112,214],
        'palegoldenrod':[238,232,170],
        'palegreen':[152,251,152],
        'paleturquoise':[175,238,238],
        'palevioletred':[216,112,147],
        'papayawhip':[255,239,213],
        'peachpuff':[255,218,185],
        'peru':[205,133,63],
        'pink':[255,192,203],
        'plum':[221,160,221],
        'powderblue':[176,224,230],
        'purple':[128,0,128],
        'red':[255,0,0],
        'rosybrown':[188,143,143],
        'royalblue':[65,105,225],
        'saddlebrown':[139,69,19],
        'salmon':[250,128,114],
        'sandybrown':[244,164,96],
        'seagreen':[46,139,87],
        'seashell':[255,245,238],
        'sienna':[160,82,45],
        'silver':[192,192,192],
        'skyblue':[135,206,235],
        'slateblue':[106,90,205],
        'slategray':[112,128,144],
        'slategrey':[112,128,144],
        'snow':[255,250,250],
        'springgreen':[0,255,127],
        'steelblue':[70,130,180],
        'tan':[210,180,140],
        'teal':[0,128,128],
        'thistle':[216,191,216],
        'tomato':[255,99,71],
        'turquoise':[64,224,208],
        'violet':[238,130,238],
        'wheat':[245,222,179],
        'white':[255,255,255],
        'whitesmoke':[245,245,245],
        'yellow':[255,255,0],
        'yellowgreen':[154,205,50],
    };

    // color parsing function originally from
    // http://plugins.jquery.com/files/jquery.color.js.txt
    // (with slight modifications)

    // Parse strings looking for color tuples [255,255,255]
    var rgbNumRE = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/;
    var rgbPercRE = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/;
    var rgbHash6RE = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/;
    var rgbHash3RE = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/;

    var strToRgb = function(color) {
      var result;

      // Check if we're already dealing with an array of colors
//         if ( color && color.constructor == Array && color.length == 3 )
//             return color;
      
      // Look for rgb(num,num,num)
      if (result = rgbNumRE.exec(color))
        return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];
      
      // Look for rgb(num%,num%,num%)
      if (result = rgbPercRE.exec(color))
        return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
      
      // Look for #a0b1c2
      if (result = rgbHash6RE.exec(color))
        return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
      
      // Look for #fff
      if (result = rgbHash3RE.exec(color))
        return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
      
      // Otherwise, we're most likely dealing with a named color
      return colors[$.trim(color).toLowerCase()];
    }

    var rgbToStr = function(rgb) {
      // TODO: there has to be a better way, even in JS
      var r = Math.floor(rgb[0]).toString(16);
      var g = Math.floor(rgb[1]).toString(16);
      var b = Math.floor(rgb[2]).toString(16);
      // pad
      r = r.length < 2 ? '0' + r : r;
      g = g.length < 2 ? '0' + g : g;
      b = b.length < 2 ? '0' + b : b;        
      return ('#'+r+g+b);
    }
    
    // Functions rgbToHsl and hslToRgb originally from 
    // http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
    // implementation of functions in Wikipedia
    // (with slight modifications)

    // RGB to HSL color conversion
    var rgbToHsl = function(rgb) {
      var r = rgb[0]/255, g = rgb[1]/255, b = rgb[2]/255;
      var max = Math.max(r, g, b), min = Math.min(r, g, b);
      var h, s, l = (max + min) / 2;

      if (max == min) {
        h = s = 0; // achromatic
      } else {
        var d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        switch (max) {
          case r: h = (g - b) / d + (g < b ? 6 : 0); break;
          case g: h = (b - r) / d + 2; break;
          case b: h = (r - g) / d + 4; break;
        }
        h /= 6;
      }
      
      return [h, s, l];
    }

    var hue2rgb = function(p, q, t) {
      if (t < 0) t += 1;
      if (t > 1) t -= 1;
      if (t < 1/6) return p + (q - p) * 6 * t;
      if (t < 1/2) return q;
      if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
      return p;
    }

    var hslToRgb = function(hsl) {
      var h = hsl[0], s = hsl[1], l = hsl[2];

      var r, g, b;

      if (s == 0) {
        r = g = b = l; // achromatic
      } else {
        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        r = hue2rgb(p, q, h + 1/3);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1/3);
      }
      
      return [r * 255, g * 255, b * 255];
    }

    var adjustLightnessCache = {};

    // given color string and -1<=adjust<=1, returns color string
    // where lightness (in the HSL sense) is adjusted by the given
    // amount, the larger the lighter: -1 gives black, 1 white, and 0
    // the given color.
    var adjustColorLightness = function(colorstr, adjust) {
      if (!(colorstr in adjustLightnessCache)) {
        adjustLightnessCache[colorstr] = {}
      }
      if (!(adjust in adjustLightnessCache[colorstr])) {
        var rgb = strToRgb(colorstr);
        if (rgb === undefined) {
          // failed color string conversion; just return the input
          adjustLightnessCache[colorstr][adjust] = colorstr;
        } else {
          var hsl = rgbToHsl(rgb);
          if (adjust > 0.0) {
            hsl[2] = 1.0 - ((1.0-hsl[2])*(1.0-adjust));
          } else {
            hsl[2] = (1.0+adjust)*hsl[2];
          }
          var lightRgb = hslToRgb(hsl);
          adjustLightnessCache[colorstr][adjust] = rgbToStr(lightRgb);
        }
      }
      return adjustLightnessCache[colorstr][adjust];
    }

    // Partially stolen from: http://documentcloud.github.com/underscore/
    // MIT-License
    // TODO: Mention in LICENSE.md
    var isEqual = function(a, b) {
      // Check object identity.
      if (a === b) return true;
      // Different types?
      var atype = typeof(a), btype = typeof(b);
      if (atype != btype) return false;
      // Basic equality test (watch out for coercions).
      if (a == b) return true;
      // One is falsy and the other truthy.
      if ((!a && b) || (a && !b)) return false;
      // If a is not an object by this point, we can't handle it.
      if (atype !== 'object') return false;
      // Check for different array lengths before comparing contents.
      if (a.length && (a.length !== b.length)) return false;
      // Nothing else worked, deep compare the contents.
      for (var key in b) if (!(key in a)) return false;
      // Recursive comparison of contents.
      for (var key in a) if (!(key in b) || !isEqual(a[key], b[key])) return false;
      return true;
    };

    var keyValRE = /^([^=]+)=(.*)$/; // key=value
    var isDigitsRE = /^[0-9]+$/;

    var deparam = function(str) {
      var args = str.split('&');
      var len = args.length;
      if (!len) return null;
      var result = {};
      for (var i = 0; i < len; i++) {
        var parts = args[i].match(keyValRE);
        if (!parts || parts.length != 3) break;
        var val = [];
        var arr = parts[2].split(',');
        var sublen = arr.length;
        for (var j = 0; j < sublen; j++) {
          var innermost = [];
          // map empty arguments ("" in URL) to empty arrays
          // (innermost remains [])
          if (arr[j].length) {
            var arrsplit = arr[j].split('~');
            var subsublen = arrsplit.length;
            for (var k = 0; k < subsublen; k++) {
              if(arrsplit[k].match(isDigitsRE)) {
                // convert digits into ints ...
                innermost.push(parseInt(arrsplit[k], 10));
              }
              else {
                // ... anything else remains a string.
                innermost.push(arrsplit[k]);
              }
            }
          }
          val.push(innermost);
        }
        result[parts[1]] = val;
      }
      return result;
    };

    var paramArray = function(val) {
      val = val || [];
      var len = val.length;
      var arr = [];
      for (var i = 0; i < len; i++) {
        if ($.isArray(val[i])) {
          arr.push(val[i].join('~'));
        } else {
          // non-array argument; this is an error from the caller
          console.error('param: Error: received non-array-in-array argument [', i, ']', ':', val[i], '(fix caller)');
        }
      }
      return arr;
    };

    var param = function(args) {
      if (!args) return '';
      var vals = [];
      for (var key in args) {
        if (args.hasOwnProperty(key)) {
          var val = args[key];
          if (val == undefined) {
            console.error('Error: received argument', key, 'with value', val);
            continue;
          }
          // values normally expected to be arrays, but some callers screw
          // up, so check
          if ($.isArray(val)) {
            var arr = paramArray(val);
            vals.push(key + '=' + arr.join(','));
          } else {
            // non-array argument; this is an error from the caller
            console.error('param: Error: received non-array argument', key, ':', val, '(fix caller)');
          }
        }
      }
      return vals.join('&');
    };

    var profiles = {};
    var profileStarts = {};
    var profileOn = false;
    var profileEnable = function(on) {
      if (on === undefined) on = true;
      profileOn = on;
    }; // profileEnable
    var profileClear = function() {
      if (!profileOn) return;
      profiles = {};
      profileStarts = {};
    }; // profileClear
    var profileStart = function(label) {
      if (!profileOn) return;
      profileStarts[label] = new Date();
    }; // profileStart
    var profileEnd = function(label) {
      if (!profileOn) return;
      var profileElapsed = new Date() - profileStarts[label]
      if (!profiles[label]) profiles[label] = 0;
      profiles[label] += profileElapsed;
    }; // profileEnd
    var profileReport = function() {
      if (!profileOn) return;
      if (window.console) {
        $.each(profiles, function(label, time) {
          console.log("profile " + label, time);
        });
        console.log("-------");
      }
    }; // profileReport
    
    // container: ID or jQuery element
    // collData: the collection data (in the format of the result of
    //   http://.../brat/ajax.cgi?action=getCollectionInformation&collection=...
    // docData: the document data (in the format of the result of
    //   http://.../brat/ajax.cgi?action=getDocument&collection=...&document=...
    // returns the embedded visualizer's dispatcher object
    var embed = function(container, collData, docData, webFontURLs,
                         dispatcher, options) {
      if (dispatcher === undefined) {
          dispatcher = new Dispatcher();
      }
      if (options === undefined) {
          options = {};
      }

      var visualizer = new Visualizer(dispatcher, container, webFontURLs,
                                      options);

      docData.collection = null;

      dispatcher.post('collectionLoaded', [collData]);
      dispatcher.post('requestRenderData', [docData]);

      return dispatcher;
    };

    // container: ID or jQuery element
    // collDataURL: the URL of the collection data, or collection data
    //   object (if pre-fetched)
    // docDataURL: the url of the document data (if pre-fetched, use
    //   simple `embed` instead)
    // callback: optional; the callback to call afterwards; it will be
    //   passed the embedded visualizer's dispatcher object
    var embedByURL = function(container, collDataURL, docDataURL, webFontURLs, callback) {
      var collData, docData;
      var handler = function() {
        if (collData && docData) {
          var dispatcher = embed(container, collData, docData, webFontURLs);
          if (callback) callback(dispatcher);
        }
      };
      if (typeof(container) == 'string') {
        $.getJSON(collDataURL, function(data) { collData = data; handler(); });
      } else {
        collData = collDataURL;
      }
      $.getJSON(docDataURL, function(data) { docData = data; handler(); });
    };

    var fontsLoaded = false;
    var fontNotifyList = false;

    var proceedWithFonts = function() {
      if (fontsLoaded) return;

      fontsLoaded = true;
      $.each(fontNotifyList, function(dispatcherNo, dispatcher) {
        dispatcher.post('triggerRender');
      });
      fontNotifyList = null;
    };

    var loadFonts = function(webFontURLs, dispatcher) {
	    return;
	    
      if (fontsLoaded) {
        dispatcher.post('triggerRender');
        return;
      }

      if (fontNotifyList) {
        fontNotifyList.push(dispatcher);
        return;
      }

      fontNotifyList = [dispatcher];

      webFontURLs = webFontURLs || [
        'Astloch-Bold.ttf',
        'PT_Sans-Caption-Web-Regular.ttf',
        'Liberation_Sans-Regular.ttf'
      ];

      var families = [];
      $.each(webFontURLs, function(urlNo, url) {
	      //alert(url+":"+/Astloch/i.test(url))
        if (/Astloch/i.test(url)) families.push('Astloch');
        else if (/PT.*Sans.*Caption/i.test(url)) families.push('PT Sans Caption');
        else if (/Liberation.*Sans/i.test(url)) families.push('Liberation Sans');
      });

      webFontURLs = {
        families: families,
        urls: webFontURLs
      }

      var webFontConfig = {
        custom: webFontURLs,
        active: proceedWithFonts,
        inactive: proceedWithFonts,
        fontactive: function(fontFamily, fontDescription) {
          // Note: Enable for font debugging
          // console.log("font active: ", fontFamily, fontDescription);
        },
        fontloading: function(fontFamily, fontDescription) {
          // Note: Enable for font debugging
          // console.log("font loading:", fontFamily, fontDescription);
        },
      };

      WebFont.load(webFontConfig);

      setTimeout(function() {
        if (!fontsLoaded) {
          console.error('Timeout in loading fonts');
          proceedWithFonts();
        }
      }, fontLoadTimeout);
    };

    var areFontsLoaded = function() {
//       return fontsLoaded;
      return true;
    };


    return {
      profileEnable: profileEnable,
      profileClear: profileClear,
      profileStart: profileStart,
      profileEnd: profileEnd,
      profileReport: profileReport,
      formatTimeAgo: formatTimeAgo,
      realBBox: realBBox,
      getSpanLabels: getSpanLabels,
      spanDisplayForm: spanDisplayForm,
      getArcLabels: getArcLabels,
      arcDisplayForm: arcDisplayForm,
      escapeQuotes: escapeQuotes,
      escapeHTML: escapeHTML,
      escapeHTMLandQuotes: escapeHTMLandQuotes,
      escapeHTMLwithNewlines: escapeHTMLwithNewlines,
      cmp: cmp,
      rgbToHsl: rgbToHsl,
      hslToRgb: hslToRgb,
      adjustColorLightness: adjustColorLightness,
      objectToUrlStr: objectToUrlStr,
      isEqual: isEqual,
      paramArray: paramArray,
      param: param,
      deparam: deparam,
      embed: embed,
      embedByURL: embedByURL,
      isMac: isMac,
      loadFonts: loadFonts,
      areFontsLoaded: areFontsLoaded,
    };

})(window);
// -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; -*-
// vim:set ft=javascript ts=2 sw=2 sts=2 cindent:

var Visualizer = (function($, window, undefined) {
    var DocumentData = function(text) {
      this.text = text;
      this.chunks = [];
      this.spans = {};
      this.eventDescs = {};
      this.sentComment = {};
      this.arcs = [];
      this.arcById = {};
      this.markedSent = {};
      this.spanAnnTexts = {};
      this.towers = {};
      // this.sizes = {};
    };

    var Fragment = function(id, span, from, to) {
      this.id = id;
      this.span = span;
      this.from = from;
      this.to = to;
      // this.towerId = undefined;
      // this.drawOrder = undefined;
    };

    var Span = function(id, type, offsets, generalType) {
      this.id = id;
      this.type = type;
      this.totalDist = 0;
      this.numArcs = 0;
      this.generalType = generalType;
      this.headFragment = null;
      this.unsegmentedOffsets = offsets;
      this.offsets = [];
      this.segmentedOffsetsMap = {};
      // this.unsegmentedOffsets = undefined;
      // this.from = undefined;
      // this.to = undefined;
      // this.wholeFrom = undefined;
      // this.wholeTo = undefined;
      // this.headFragment = undefined;
      // this.chunk = undefined;
      // this.marked = undefined;
      // this.avgDist = undefined;
      // this.curly = undefined;
      // this.comment = undefined; // { type: undefined, text: undefined };
      // this.annotatorNotes = undefined;
      // this.drawCurly = undefined;
      // this.glyphedLabelText = undefined;
      // this.group = undefined;
      // this.height = undefined;
      // this.highlightPos = undefined;
      // this.indexNumber = undefined;
      // this.labelText = undefined;
      // this.nestingDepth = undefined;
      // this.nestingDepthLR = undefined;
      // this.nestingDepthRL = undefined;
      // this.nestingHeight = undefined;
      // this.nestingHeightLR = undefined;
      // this.nestingHeightRL = undefined;
      // this.rect = undefined;
      // this.rectBox = undefined;
      // this.refedIndexSum = undefined;
      // this.right = undefined;
      // this.totaldist = undefined;
      // this.width = undefined;
      this.initContainers();
    };

    Span.prototype.initContainers = function(offsets) {
      this.incoming = [];
      this.outgoing = [];
      this.attributes = {};
      this.attributeText = [];
      this.attributeCues = {};
      this.attributeCueFor = {};
      this.attributeMerge = {}; // for box, cross, etc. that are span-global
      this.fragments = [];
      this.normalizations = [];
    };

    Span.prototype.splitMultilineOffsets = function(text) {
      this.segmentedOffsetsMap = {};

      for (var fi = 0, nfi = 0; fi < this.unsegmentedOffsets.length; fi++) {
        var begin = this.unsegmentedOffsets[fi][0];
        var end = this.unsegmentedOffsets[fi][1];
      
        for (var ti = begin; ti < end; ti++) {
          var c = text.charAt(ti);
          if (c == '\n' || c == '\r') {
            if (begin !== null) {
              this.offsets.push([begin, ti])
              this.segmentedOffsetsMap[nfi++] = fi;
              begin = null;
            }
          }
          else if (begin === null) {
            begin = ti;
          }
        }
      
        if (begin !== null) {
          this.offsets.push([begin, end]);
          this.segmentedOffsetsMap[nfi++] = fi;
        }
      }
    };

    Span.prototype.copy = function(id) {
      var span = $.extend(new Span(), this); // clone
      span.id = id;
      // protect from shallow copy
      span.initContainers();
      span.unsegmentedOffsets = this.unsegmentedOffsets.slice();
      // read-only; shallow copy is fine
      span.offsets = this.offsets;
      span.segmentedOffsetsMap = this.segmentedOffsetsMap;
      return span;
    };

    var EventDesc = function(id, triggerId, roles, klass) {
      this.id = id;
      this.triggerId = triggerId;
      var roleList = this.roles = [];
      $.each(roles, function(roleNo, role) {
        roleList.push({ type: role[0], targetId: role[1] });
      });
      if (klass == "equiv") {
        this.equiv = true;
      } else if (klass == "relation") {
        this.relation = true;
      }
      // this.leftSpans = undefined;
      // this.rightSpans = undefined;
      // this.annotatorNotes = undefined;
    };

    var Chunk = function(index, text, from, to, space, spans) {
      this.index = index;
      this.text = text;
      this.from = from;
      this.to = to;
      this.space = space;
      this.fragments = [];
      // this.sentence = undefined;
      // this.group = undefined;
      // this.highlightGroup = undefined;
      // this.markedTextStart = undefined;
      // this.markedTextEnd = undefined;
      // this.nextSpace = undefined;
      // this.right = undefined;
      // this.row = undefined;
      // this.textX = undefined;
      // this.translation = undefined;
    }

    var Arc = function(eventDesc, role, dist, eventNo) {
      this.origin = eventDesc.id;
      this.target = role.targetId;
      this.dist = dist;
      this.type = role.type;
      this.shadowClass = eventDesc.shadowClass;
      this.jumpHeight = 0;
      if (eventDesc.equiv) {
        this.equiv = true;
        this.eventDescId = eventNo;
        eventDesc.equivArc = this;
      } else if (eventDesc.relation) {
        this.relation = true;
        this.eventDescId = eventNo;
      }
      // this.marked = undefined;
    };

    var Row = function(svg) {
      this.group = svg.group();
      this.background = svg.group(this.group);
      this.chunks = [];
      this.hasAnnotations = false;
      this.maxArcHeight = 0;
      this.maxSpanHeight = 0;
    };

    var Measurements = function(widths, height, y) {
      this.widths = widths;
      this.height = height;
      this.y = y;
    };

    // A naive whitespace tokeniser
    var tokenise = function(text) {
      var tokenOffsets = [];
      var tokenStart = null;
      var lastCharPos = null;

      for (var i = 0; i < text.length; i++) {
        var c = text[i];
        // Have we found the start of a token?
        if (tokenStart == null && !/\s/.test(c)) {
          tokenStart = i;
          lastCharPos = i;
        // Have we found the end of a token?
        } else if (/\s/.test(c) && tokenStart != null) {
          tokenOffsets.push([tokenStart, i]);
          tokenStart = null;
        // Is it a non-whitespace character?
        } else if (!/\s/.test(c)) {
          lastCharPos = i;
        }
      }
      // Do we have a trailing token?
      if (tokenStart != null) {
        tokenOffsets.push([tokenStart, lastCharPos + 1]);
      }

      return tokenOffsets;
    };

    // A naive newline sentence splitter
    var sentenceSplit = function(text) {
      var sentenceOffsets = [];
      var sentStart = null;
      var lastCharPos = null;

      for (var i = 0; i < text.length; i++) {
        var c = text[i];
        // Have we found the start of a sentence?
        if (sentStart == null && !/\s/.test(c)) {
          sentStart = i;
          lastCharPos = i;
        // Have we found the end of a sentence?
        } else if (c == '\n' && sentStart != null) {
          sentenceOffsets.push([sentStart, i]);
          sentStart = null;
        // Is it a non-whitespace character?
        } else if (!/\s/.test(c)) {
          lastCharPos = i;
        }
      }
      // Do we have a trailing sentence without a closing newline?
      if (sentStart != null) {
        sentenceOffsets.push([sentStart, lastCharPos + 1]);
      }

      return sentenceOffsets;
    };

    // Sets default values for a wide range of optional attributes
    var setSourceDataDefaults = function(sourceData) {
      // The following are empty lists if not set
      $.each([
          'attributes',
          'comments',
          'entities',
          'equivs',
          'events',
          'modifications',
          'normalizations',
          'relations',
          'triggers',
          ], function(attrNo, attr) {
        if (sourceData[attr] === undefined) {
          sourceData[attr] = [];
        }
      });

      // If we lack sentence offsets we fall back on naive sentence splitting
      if (sourceData.sentence_offsets === undefined) {
        sourceData.sentence_offsets = sentenceSplit(sourceData.text);
      }
      // Similarily we fall back on whitespace tokenisation
      if (sourceData.token_offsets === undefined) {
        sourceData.token_offsets = tokenise(sourceData.text);
      }
    };

    // Set default values for a variety of collection attributes
    var setCollectionDefaults = function(collectionData) {
      // The following are empty lists if not set
      $.each([
          'entity_attribute_types',
          'entity_types',
          'event_attribute_types',
          'event_types',
          'relation_attribute_types',
          'relation_types',
          'unconfigured_types',
          ], function(attrNo, attr) {
        if (collectionData[attr] === undefined) {
          collectionData[attr] = [];
        }
      });
    };

    var Visualizer = function(dispatcher, svgId, webFontURLs,
                              visualizerOptions) {
      if (visualizerOptions === undefined) {
          visualizerOptions = {};
      }
      var $svgDiv = $('#' + svgId);
      if (!$svgDiv.length) {
        throw Error('Could not find container with id="' + svgId + '"');
      }
      var that = this;

      // OPTIONS
      var roundCoordinates = true; // try to have exact pixel offsets
      var boxTextMargin = { x: 0, y: 1.5 }; // effect is inverse of "margin" for some reason
      var highlightRounding = { x: 3, y:3 }; // rx, ry for highlight boxes
      var spaceWidths = {
        ' ': 4,
        '\u00a0': 4,
        '\u200b': 0,
        '\u3000': 8,
        '\t': 12,
        '\n': 4
      };
      var coloredCurlies = true; // color curlies by box BG
      var arcSlant = 15; //10;
      var minArcSlant = 8;
      var arcHorizontalSpacing = 10; // min space boxes with connecting arc
      var rowSpacing = -5;          // for some funny reason approx. -10 gives "tight" packing.
      var sentNumMargin = 20;
      var smoothArcCurves = true;   // whether to use curves (vs lines) in arcs
      var smoothArcSteepness = 0.5; // steepness of smooth curves (control point)
      var reverseArcControlx = 5;   // control point distance for "UFO catchers"

      // "shadow" effect settings (note, error, incompelete)
      var rectShadowSize = 3;
      var rectShadowRounding = 2.5;
      var arcLabelShadowSize = 1;
      var arcLabelShadowRounding = 5;
      var shadowStroke = 2.5; // TODO XXX: this doesn't affect anything..?

      // "marked" effect settings (edited, focus, match)
      var markedSpanSize = 6;
      var markedArcSize = 2;
      var markedArcStroke = 7; // TODO XXX: this doesn't seem to do anything..?

      var rowPadding = 2;
      var nestingAdjustYStepSize = 2; // size of height adjust for nested/nesting spans
      var nestingAdjustXStepSize = 1; // size of height adjust for nested/nesting spans

      var highlightSequence = '#FF9632;#FFCC00;#FF9632'; // yellow - deep orange
      //var highlightSequence = '#FFFC69;#FFCC00;#FFFC69'; // a bit toned town
      var highlightSpanSequence = highlightSequence;
      var highlightArcSequence =  highlightSequence;
      var highlightTextSequence = highlightSequence;
      var highlightDuration = '2s';
      // different sequence for "mere" matches (as opposed to "focus" and
      // "edited" highlights)
      var highlightMatchSequence = '#FFFF00'; // plain yellow

      var fragmentConnectorDashArray = '1,3,3,3';
      var fragmentConnectorColor = '#000000';

      // set caller options
      if (visualizerOptions.sentNumMargin !== undefined) {
          sentNumMargin = visualizerOptions.sentNumMargin;
          // TODO: more, ideally allow any option above to be set
      }

      // END OPTIONS


      var svg;
      var $svg;
      var data = null;
      var sourceData = null;
      var requestedData = null;
      var coll, doc, args;
      var relationTypesHash;
      var isRenderRequested;
      var isCollectionLoaded = false;
      var entityAttributeTypes = null;
      var eventAttributeTypes = null;
      var spanTypes = null;
      var highlightGroup;
      var collapseArcs = false;
      var collapseArcSpace = false;

      // var commentPrioLevels = ['Unconfirmed', 'Incomplete', 'Warning', 'Error', 'AnnotatorNotes'];
      // XXX Might need to be tweaked - inserted diff levels
      var commentPrioLevels = [
        'Unconfirmed', 'Incomplete', 'Warning', 'Error', 'AnnotatorNotes',
        'AddedAnnotation', 'MissingAnnotation', 'ChangedAnnotation'];

      this.arcDragOrigin = null; // TODO

      // due to silly Chrome bug, I have to make it pay attention
      var forceRedraw = function() {
        if (!$.browser.chrome) return; // not needed
        $svg.css('margin-bottom', 1);
        setTimeout(function() { $svg.css('margin-bottom', 0); }, 0);
      }

      var rowBBox = function(span) {
        var box = $.extend({}, span.rectBox); // clone
        var chunkTranslation = span.chunk.translation;
        box.x += chunkTranslation.x;
        box.y += chunkTranslation.y;
        return box;
      };

      var commentPriority = function(commentClass) {
        if (commentClass === undefined) return -1;
        var len = commentPrioLevels.length;
        for (var i = 0; i < len; i++) {
          if (commentClass.indexOf(commentPrioLevels[i]) != -1) return i;
        }
        return 0;
      };

      var clearSVG = function() {
        data = null;
        sourceData = null;
        svg.clear();
        $svgDiv.hide();
      };

      var setMarked = function(markedType) {
        $.each(args[markedType] || [], function(markedNo, marked) {
          if (marked[0] == 'sent') {
            data.markedSent[marked[1]] = true;
          } else if (marked[0] == 'equiv') { // [equiv, Equiv, T1]
            $.each(sourceData.equivs, function(equivNo, equiv) {
              if (equiv[1] == marked[1]) {
                var len = equiv.length;
                for (var i = 2; i < len; i++) {
                  if (equiv[i] == marked[2]) {
                    // found it
                    len -= 3;
                    for (var i = 1; i <= len; i++) {
                      var arc = data.eventDescs[equiv[0] + "*" + i].equivArc;
                      arc.marked = markedType;
                    }
                    return; // next equiv
                  }
                }
              }
            });
          } else if (marked.length == 2) {
            markedText.push([parseInt(marked[0], 10), parseInt(marked[1], 10), markedType]);
          } else {
            var span = data.spans[marked[0]];
            if (span) {
              if (marked.length == 3) { // arc
                $.each(span.outgoing, function(arcNo, arc) {
                  if (arc.target == marked[2] && arc.type == marked[1]) {
                    arc.marked = markedType;
                  }
                });
              } else { // span
                span.marked = markedType;
              }
            } else {
              var eventDesc = data.eventDescs[marked[0]];
              if (eventDesc) { // relation
                var relArc = eventDesc.roles[0];
                $.each(data.spans[eventDesc.triggerId].outgoing, function(arcNo, arc) {
                  if (arc.target == relArc.targetId && arc.type == relArc.type) {
                    arc.marked = markedType;
                  }
                });
              } else { // try for trigger
                $.each(data.eventDescs, function(eventDescNo, eventDesc) {
                  if (eventDesc.triggerId == marked[0]) {
                    data.spans[eventDesc.id].marked = markedType;
                  }
                });
              }
            }
          }
        });
      };

      var findArcHeight = function(fromIndex, toIndex, fragmentHeights) {
        var height = 0;
        for (var i = fromIndex; i <= toIndex; i++) {
          if (fragmentHeights[i] > height) height = fragmentHeights[i];
        }
        height += Configuration.visual.arcSpacing;
        return height;
      }

      var adjustFragmentHeights = function(fromIndex, toIndex, fragmentHeights, height) {
        for (var i = fromIndex; i <= toIndex; i++) {
          if (fragmentHeights[i] < height) fragmentHeights[i] = height;
        }
      }

      var fragmentComparator = function(a, b) {
        var tmp;
        var aSpan = a.span;
        var bSpan = b.span;

        // spans with more fragments go first
        tmp = aSpan.fragments.length - bSpan.fragments.length;
        if (tmp) {
          return tmp < 0 ? 1 : -1;
        }

        // longer arc distances go last
        tmp = aSpan.avgDist - bSpan.avgDist;
        if (tmp) {
          return tmp < 0 ? -1 : 1;
        }
        // spans with more arcs go last
        tmp = aSpan.numArcs - bSpan.numArcs;
        if (tmp) {
          return tmp < 0 ? -1 : 1;
        }
        // compare the span widths,
        // put wider on bottom so they don't mess with arcs, or shorter
        // on bottom if there are no arcs.
        var ad = a.to - a.from;
        var bd = b.to - b.from;
        tmp = ad - bd;
        if (aSpan.numArcs == 0 && bSpan.numArcs == 0) {
          tmp = -tmp;
        }
        if (tmp) {
          return tmp < 0 ? 1 : -1;
        }
        tmp = aSpan.refedIndexSum - bSpan.refedIndexSum;
        if (tmp) {
          return tmp < 0 ? -1 : 1;
        }
        // if no other criterion is found, sort by type to maintain
        // consistency
        // TODO: isn't there a cmp() in JS?
        if (aSpan.type < bSpan.type) {
          return -1;
        } else if (aSpan.type > bSpan.type) {
          return 1;
        }

        return 0;
      };


      var setData = function(_sourceData) {
        if (!args) args = {};
        sourceData = _sourceData;
        dispatcher.post('newSourceData', [sourceData]);
        data = new DocumentData(sourceData.text);

        // collect annotation data
        $.each(sourceData.entities, function(entityNo, entity) {
          // offsets given as array of (start, end) pairs
          var span =
              //      (id,        type,      offsets,   generalType)
              new Span(entity[0], entity[1], entity[2], 'entity');
          span.splitMultilineOffsets(data.text);
          data.spans[entity[0]] = span;
        });
        var triggerHash = {};
        $.each(sourceData.triggers, function(triggerNo, trigger) {
                       //       (id,         type,       offsets,    generalType)
          var triggerSpan = new Span(trigger[0], trigger[1], trigger[2], 'trigger');
          triggerSpan.splitMultilineOffsets(data.text);
          triggerHash[trigger[0]] = [triggerSpan, []]; // triggerSpan, eventlist
        });
        $.each(sourceData.events, function(eventNo, eventRow) {
          var eventDesc = data.eventDescs[eventRow[0]] =
              //           (id,          triggerId,   roles,        klass)
              new EventDesc(eventRow[0], eventRow[1], eventRow[2]);
          var trigger = triggerHash[eventDesc.triggerId];
          var span = trigger[0].copy(eventDesc.id);
          trigger[1].push(span);
          data.spans[eventDesc.id] = span;
        });

        // XXX modifications: delete later
        $.each(sourceData.modifications, function(modNo, mod) {
          // mod: [id, spanId, modification]
          if (!data.spans[mod[2]]) {
            dispatcher.post('messages', [[['<strong>ERROR</strong><br/>Event ' + mod[2] + ' (referenced from modification ' + mod[0] + ') does not occur in document ' + data.document + '<br/>(please correct the source data)', 'error', 5]]]);
            return;
          }
          data.spans[mod[2]][mod[1]] = true;
        });

        var midpointComparator = function(a, b) {
          var tmp = a.from + a.to - b.from - b.to;
          if (!tmp) return 0;
          return tmp < 0 ? -1 : 1;
        };
        // split spans into span fragments (for discontinuous spans)
        $.each(data.spans, function(spanNo, span) {
          $.each(span.offsets, function(offsetsNo, offsets) {
            var from = parseInt(offsets[0], 10);
            var to = parseInt(offsets[1], 10);
            var fragment = new Fragment(offsetsNo, span, from, to);
            span.fragments.push(fragment);
          });
          // ensure ascending order
          span.fragments.sort(midpointComparator);
          span.wholeFrom = span.fragments[0].from;
          span.wholeTo = span.fragments[span.fragments.length - 1].to;
          span.headFragment = span.fragments[(true) ? span.fragments.length - 1 : 0]; // TODO configurable!
        });

        var spanComparator = function(a, b) {
          var aSpan = data.spans[a];
          var bSpan = data.spans[b];
          var tmp = aSpan.headFragment.from + aSpan.headFragment.to - bSpan.headFragment.from - bSpan.headFragment.to;
          if (tmp) {
            return tmp < 0 ? -1 : 1;
          }
          return 0;
        };
        $.each(sourceData.equivs, function(equivNo, equiv) {
          // equiv: ['*', 'Equiv', spanId...]
          equiv[0] = "*" + equivNo;
          var equivSpans = equiv.slice(2);
          var okEquivSpans = [];
          // collect the equiv spans in an array
          $.each(equivSpans, function(equivSpanNo, equivSpan) {
            if (data.spans[equivSpan]) okEquivSpans.push(equivSpan);
            // TODO: #404, inform the user with a message?
          });
          // sort spans in the equiv by their midpoint
          okEquivSpans.sort(spanComparator);
          // generate the arcs
          var len = okEquivSpans.length;
          for (var i = 1; i < len; i++) {
            var eventDesc = data.eventDescs[equiv[0] + '*' + i] =
                //           (id,                  triggerId,           roles,                         klass)
                new EventDesc(okEquivSpans[i - 1], okEquivSpans[i - 1], [[equiv[1], okEquivSpans[i]]], 'equiv');
            eventDesc.leftSpans = okEquivSpans.slice(0, i);
            eventDesc.rightSpans = okEquivSpans.slice(i);
          }
        });
        $.each(sourceData.relations, function(relNo, rel) {
          // rel[2] is args, rel[2][a][0] is role and rel[2][a][1] is value for a in (0,1)
          var argsDesc = relationTypesHash[rel[1]];
          argsDesc = argsDesc && argsDesc.args;
          var t1, t2;
          if (argsDesc) {
            // sort the arguments according to the config
            var args = {}
            args[rel[2][0][0]] = rel[2][0][1];
            args[rel[2][1][0]] = rel[2][1][1];
            t1 = args[argsDesc[0].role];
            t2 = args[argsDesc[1].role];
          } else {
            // (or leave as-is in its absence)
            t1 = rel[2][0][1];
            t2 = rel[2][1][1];
          }
          data.eventDescs[rel[0]] =
              //           (id, triggerId, roles,          klass)
              new EventDesc(t1, t1,        [[rel[1], t2]], 'relation');
        });

        // attributes
        $.each(sourceData.attributes, function(attrNo, attr) {
          // attr: [id, name, spanId, value, cueSpanId

          // TODO: might wish to check what's appropriate for the type
          // instead of using the first attribute def found
          var attrType = (eventAttributeTypes[attr[1]] ||
                          entityAttributeTypes[attr[1]]);
          var attrValue = attrType && attrType.values[attrType.bool || attr[3]];
          var span = data.spans[attr[2]];
          if (!span) {
            dispatcher.post('messages', [[['Annotation ' + attr[2] + ', referenced from attribute ' + attr[0] + ', does not exist.', 'error']]]);
            return;
          }
          var valText = (attrValue && attrValue.name) || attr[3];
          var attrText = attrType
            ? (attrType.bool ? attrType.name : (attrType.name + ': ' + valText))
            : (attr[3] === true ? attr[1] : attr[1] + ': ' + attr[3]);
          span.attributeText.push(attrText);
          span.attributes[attr[1]] = attr[3];
          if (attr[4]) { // cue
            span.attributeCues[attr[1]] = attr[4];
            var cueSpan = data.spans[attr[4]];
            cueSpan.attributeCueFor[data.spans[1]] = attr[2];
            cueSpan.cue = 'CUE'; // special css type
          }
          $.extend(span.attributeMerge, attrValue);
        });

        // comments
        $.each(sourceData.comments, function(commentNo, comment) {
          // comment: [entityId, type, text]

          // TODO error handling

          // sentence id: ['sent', sentId]
          if (comment[0] instanceof Array && comment[0][0] == 'sent') {
            // sentence comment
            var sent = comment[0][1];
            var text = comment[2];
            if (data.sentComment[sent]) {
              text = data.sentComment[sent].text + '<br/>' + text;
            }
            data.sentComment[sent] = { type: comment[1], text: text };
          } else {
            var id = comment[0];
            var trigger = triggerHash[id];
            var eventDesc = data.eventDescs[id];
            var commentEntities =
                trigger
                ? trigger[1] // trigger: [span, ...]
                : id in data.spans
                  ? [data.spans[id]] // span: [span]
                  : id in data.eventDescs
                    ? [data.eventDescs[id]] // arc: [eventDesc]
                    : [];
            $.each(commentEntities, function(entityId, entity) {
              // if duplicate comment for entity:
              // overwrite type, concatenate comment with a newline
              if (!entity.comment) {
                entity.comment = { type: comment[1], text: comment[2] };
              } else {
                entity.comment.type = comment[1];
                entity.comment.text += "\n" + comment[2];
              }
              // partially duplicate marking of annotator note comments
              if (comment[1] == "AnnotatorNotes") {
                entity.annotatorNotes = comment[2];
              }
              // prioritize type setting when multiple comments are present
              if (commentPriority(comment[1]) > commentPriority(entity.shadowClass)) {
                entity.shadowClass = comment[1];
              }
            });
          }
        });

        // normalizations
        $.each(sourceData.normalizations, function(normNo, norm) {
          var id = norm[0];
          var normType = norm[1];
          var target = norm[2];
          var refdb = norm[3];
          var refid = norm[4];
          var reftext = norm[5];

          // grab entity / event the normalization applies to
          var span = data.spans[target];
          if (!span) {
            dispatcher.post('messages', [[['Annotation ' + target + ', referenced from normalization ' + id + ', does not exist.', 'error']]]);
            return;
          }

          // TODO: do we have any possible use for the normType?
          span.normalizations.push([refdb, refid, reftext]);

          // quick hack for span box visual style
          span.normalized = 'Normalized';
        });

        // prepare span boundaries for token containment testing
        var sortedFragments = [];
        $.each(data.spans, function(spanNo, span) {
          $.each(span.fragments, function(fragmentNo, fragment) {
            sortedFragments.push(fragment);
          });
        });
        // sort fragments by beginning, then by end
        sortedFragments.sort(function(a, b) {
          var x = a.from;
          var y = b.from;
          if (x == y) {
            x = a.to;
            y = b.to;
          }
          return ((x < y) ? -1 : ((x > y) ? 1 : 0));
        });
        var currentFragmentId = 0;
        var startFragmentId = 0;
        var numFragments = sortedFragments.length;
        var lastTo = 0;
        var firstFrom = null;
        var chunkNo = 0;
        var space;
        var chunk = null;
        // token containment testing (chunk recognition)
        $.each(sourceData.token_offsets, function() {
          var from = this[0];
          var to = this[1];
          if (firstFrom === null) firstFrom = from;

          // Replaced for speedup; TODO check correctness
          // inSpan = false;
          // $.each(data.spans, function(spanNo, span) {
          //   if (span.from < to && to < span.to) {
          //     // it does; no word break
          //     inSpan = true;
          //     return false;
          //   }
          // });

          // Is the token end inside a span?
          if (startFragmentId && to > sortedFragments[startFragmentId - 1].to) {
            while (startFragmentId < numFragments && to > sortedFragments[startFragmentId].from) {
              startFragmentId++;
            }
          }
          currentFragmentId = startFragmentId;
          while (currentFragmentId < numFragments && to >= sortedFragments[currentFragmentId].to) {
            currentFragmentId++;
          }
          // if yes, the next token is in the same chunk
          if (currentFragmentId < numFragments && to > sortedFragments[currentFragmentId].from) {
            return;
          }

          // otherwise, create the chunk found so far
          space = data.text.substring(lastTo, firstFrom);
          var text = data.text.substring(firstFrom, to);
          if (chunk) chunk.nextSpace = space;
          //               (index,     text, from,      to, space) {
          chunk = new Chunk(chunkNo++, text, firstFrom, to, space);
          chunk.lastSpace = space;
          data.chunks.push(chunk);
          lastTo = to;
          firstFrom = null;
        });
        var numChunks = chunkNo;

        // find sentence boundaries in relation to chunks
        chunkNo = 0;
        var sentenceNo = 0;
        var pastFirst = false;
        $.each(sourceData.sentence_offsets, function() {
          var from = this[0];
          if (chunkNo >= numChunks) return false;
          if (data.chunks[chunkNo].from > from) return;
          var chunk;
          while (chunkNo < numChunks && (chunk = data.chunks[chunkNo]).from < from) {
            chunkNo++;
          }
          chunkNo++;
          if (pastFirst && from <= chunk.from) {
            var numNL = chunk.space.split("\n").length - 1;
            if (!numNL) numNL = 1;
            sentenceNo += numNL;
            chunk.sentence = sentenceNo;
          } else {
            pastFirst = true;
          }
        });

        // assign fragments to appropriate chunks
        var currentChunkId = 0;
        var chunk;
        $.each(sortedFragments, function(fragmentId, fragment) {
          while (fragment.to > (chunk = data.chunks[currentChunkId]).to) currentChunkId++;
          chunk.fragments.push(fragment);
          fragment.text = chunk.text.substring(fragment.from - chunk.from, fragment.to - chunk.from);
          fragment.chunk = chunk;
        });

        // assign arcs to spans; calculate arc distances
        $.each(data.eventDescs, function(eventNo, eventDesc) {
          var dist = 0;
          var origin = data.spans[eventDesc.id];
          if (!origin) {
            // TODO: include missing trigger ID in error message
            dispatcher.post('messages', [[['<strong>ERROR</strong><br/>Trigger for event "' + eventDesc.id + '" not found in ' + data.document + '<br/>(please correct the source data)', 'error', 5]]]);
            return;
          }
          var here = origin.headFragment.from + origin.headFragment.to;
          $.each(eventDesc.roles, function(roleNo, role) {
            var target = data.spans[role.targetId];
            if (!target) {
              dispatcher.post('messages', [[['<strong>ERROR</strong><br/>"' + role.targetId + '" (referenced from "' + eventDesc.id + '") not found in ' + data.document + '<br/>(please correct the source data)', 'error', 5]]]);
              return;
            }
            var there = target.headFragment.from + target.headFragment.to;
            var dist = Math.abs(here - there);
            var arc = new Arc(eventDesc, role, dist, eventNo);
            origin.totalDist += dist;
            origin.numArcs++;
            target.totalDist += dist;
            target.numArcs++;
            data.arcs.push(arc);
            target.incoming.push(arc);
            origin.outgoing.push(arc);
            // ID dict for easy access. TODO: have a function defining the
            // (origin,type,target)->id mapping (see also annotator_ui.js)
            var arcId = origin.id + '--' + role.type + '--' + target.id;
            data.arcById[arcId] = arc;
          }); // roles
        }); // eventDescs

        // highlighting
        markedText = [];
        setMarked('edited'); // set by editing process
        setMarked('focus'); // set by URL
        setMarked('matchfocus'); // set by search process, focused match
        setMarked('match'); // set by search process, other (non-focused) match

        $.each(data.spans, function(spanId, span) {
          // calculate average arc distances
          // average distance of arcs (0 for no arcs)
          span.avgDist = span.numArcs ? span.totalDist / span.numArcs : 0;
          lastSpan = span;

          // collect fragment texts into span texts
          var fragmentTexts = [];
          $.each(span.fragments, function(fragmentNo, fragment) {
            // TODO heuristics
            fragmentTexts.push(fragment.text);
          });
          span.text = fragmentTexts.join('');
        }); // data.spans

        for (var i = 0; i < 2; i++) {
          // preliminary sort to assign heights for basic cases
          // (first round) and cases resolved in the previous
          // round(s).
          $.each(data.chunks, function(chunkNo, chunk) {
            // sort
            chunk.fragments.sort(fragmentComparator);
            // renumber
            $.each(chunk.fragments, function(fragmentNo, fragment) {
              fragment.indexNumber = fragmentNo;
            });
          });
          // nix the sums, so we can sum again
          $.each(data.spans, function(spanNo, span) {
            span.refedIndexSum = 0;
          });
          // resolved cases will now have indexNumber set
          // to indicate their relative order. Sum those for referencing cases
          // for use in iterative resorting
          $.each(data.arcs, function(arcNo, arc) {
            data.spans[arc.origin].refedIndexSum += data.spans[arc.target].headFragment.indexNumber;
          });
        }

        // Final sort of fragments in chunks for drawing purposes
        // Also identify the marked text boundaries regarding chunks
        $.each(data.chunks, function(chunkNo, chunk) {
          // and make the next sort take this into account. Note that this will
          // now resolve first-order dependencies between sort orders but not
          // second-order or higher.
          chunk.fragments.sort(fragmentComparator);
          $.each(chunk.fragments, function(fragmentNo, fragment) {
            fragment.drawOrder = fragmentNo;
          });
        });

        data.spanDrawOrderPermutation = Object.keys(data.spans);
        data.spanDrawOrderPermutation.sort(function(a, b) {
          var spanA = data.spans[a];
          var spanB = data.spans[b];

          // We're jumping all over the chunks, but it's enough that
          // we're doing everything inside each chunk in the right
          // order. should it become necessary to actually do these in
          // linear order, put in a similar condition for
          // spanX.headFragment.chunk.index; but it should not be
          // needed.

          var tmp = spanA.headFragment.drawOrder - spanB.headFragment.drawOrder;
          if (tmp) return tmp < 0 ? -1 : 1;

          return 0;
        });

        // resort the spans for linear order by center
        sortedFragments.sort(midpointComparator);

        // sort fragments into towers, calculate average arc distances
        var lastFragment = null;
        var towerId = -1;
        $.each(sortedFragments, function(i, fragment) {
          if (!lastFragment || (lastFragment.from != fragment.from || lastFragment.to != fragment.to)) {
            towerId++;
          }
          fragment.towerId = towerId;
          lastFragment = fragment;
        }); // sortedFragments

        // find curlies (only the first fragment drawn in a tower)
        $.each(data.spanDrawOrderPermutation, function(spanIdNo, spanId) {
          var span = data.spans[spanId];

          $.each(span.fragments, function(fragmentNo, fragment) {
            if (!data.towers[fragment.towerId]) {
              data.towers[fragment.towerId] = [];
              fragment.drawCurly = true;
              fragment.span.drawCurly = true;
            }
            data.towers[fragment.towerId].push(fragment);
          });
        });

        var spanAnnTexts = {};
        $.each(data.chunks, function(chunkNo, chunk) {
          chunk.markedTextStart = [];
          chunk.markedTextEnd = [];

          $.each(chunk.fragments, function(fragmentNo, fragment) {
            if (chunk.firstFragmentIndex == undefined) {
              chunk.firstFragmentIndex = fragment.towerId;
            }
            chunk.lastFragmentIndex = fragment.towerId;

            var spanLabels = Util.getSpanLabels(spanTypes, fragment.span.type);
            fragment.labelText = Util.spanDisplayForm(spanTypes, fragment.span.type);
            // Find the most appropriate label according to text width
            if (Configuration.abbrevsOn && spanLabels) {
              var labelIdx = 1; // first abbrev
              var maxLength = (fragment.to - fragment.from) / 0.8;
              while (fragment.labelText.length > maxLength &&
                  spanLabels[labelIdx]) {
                fragment.labelText = spanLabels[labelIdx];
                labelIdx++;
              }
            }

            var svgtext = svg.createText(); // one "text" element per row
            var postfixArray = [];
            var prefix = '';
            var postfix = '';
            var warning = false;
            $.each(fragment.span.attributes, function(attrType, valType) {
              // TODO: might wish to check what's appropriate for the type
              // instead of using the first attribute def found
              var attr = (eventAttributeTypes[attrType] ||
                          entityAttributeTypes[attrType]);
              if (!attr) {
                // non-existent type
                warning = true;
                return;
              }
              var val = attr.values[attr.bool || valType];
              if (!val) {
                // non-existent value
                warning = true;
                return;
              }
              if ($.isEmptyObject(val)) {
                // defined, but lacks any visual presentation
                warning = true;
                return;
              }
              if (val.glyph) {
                if (val.position == "left") {
                  prefix = val.glyph + prefix;
                  var tspan_attrs = { 'class' : 'glyph' };
                  if (val.glyphColor) {
                    tspan_attrs.fill = val.glyphColor;
                  }
                  svgtext.span(val.glyph, tspan_attrs);
                } else { // XXX right is implied - maybe change
                  postfixArray.push([attr, val]);
                  postfix += val.glyph;
                }
              }
            });
            var text = fragment.labelText;
            if (prefix !== '') {
              text = prefix + ' ' + text;
              svgtext.string(' ');
            }
            svgtext.string(fragment.labelText);
            if (postfixArray.length) {
              text += ' ' + postfix;
              svgtext.string(' ');
              $.each(postfixArray, function(elNo, el) {
                var tspan_attrs = { 'class' : 'glyph' };
                if (el[1].glyphColor) {
                  tspan_attrs.fill = el[1].glyphColor;
                }
                svgtext.span(el[1].glyph, tspan_attrs);
              });
            }
            if (warning) {
              svgtext.span("#", { 'class': 'glyph attribute_warning' });
              text += ' #';
            }
            fragment.glyphedLabelText = text;

            if (!spanAnnTexts[text]) {
              spanAnnTexts[text] = true;
              data.spanAnnTexts[text] = svgtext;
            }
          }); // chunk.fragments
        }); // chunks

        var numChunks = data.chunks.length;
        // note the location of marked text with respect to chunks
        var startChunk = 0;
        var currentChunk;
        // sort by "from"; we don't need to sort by "to" as well,
        // because unlike spans, chunks are disjunct
        markedText.sort(function(a, b) {
          return Util.cmp(a[0], b[0]);
        });
        $.each(markedText, function(textNo, textPos) {
          var from = textPos[0];
          var to = textPos[1];
          var markedType = textPos[2];
          if (from < 0) from = 0;
          if (to < 0) to = 0;
          if (to >= data.text.length) to = data.text.length - 1;
          if (from > to) from = to;
          while (startChunk < numChunks) {
            var chunk = data.chunks[startChunk];
            if (from <= chunk.to) {
              chunk.markedTextStart.push([textNo, true, from - chunk.from, null, markedType]);
              break;
            }
            startChunk++;
          }
          if (startChunk == numChunks) {
            dispatcher.post('messages', [[['Wrong text offset', 'error']]]);
            return;
          }
          currentChunk = startChunk;
          while (currentChunk < numChunks) {
            var chunk = data.chunks[currentChunk];
            if (to <= chunk.to) {
              chunk.markedTextEnd.push([textNo, false, to - chunk.from]);
              break
            }
            currentChunk++;
          }
          if (currentChunk == numChunks) {
            dispatcher.post('messages', [[['Wrong text offset', 'error']]]);
            var chunk = data.chunks[data.chunks.length - 1];
            chunk.markedTextEnd.push([textNo, false, chunk.text.length]);
            return;
          }
        }); // markedText

        dispatcher.post('dataReady', [data]);
      };

      var resetData = function() {
        setData(sourceData);
        renderData();
      }

      var translate = function(element, x, y) {
        $(element.group).attr('transform', 'translate(' + x + ', ' + y + ')');
        element.translation = { x: x, y: y };
      };

      var showMtime = function() {
        if (data.mtime) {
            // we're getting seconds and need milliseconds
            //$('#document_ctime').text("Created: " + Annotator.formatTime(1000 * data.ctime)).css("display", "inline");
            $('#document_mtime').text("Last modified: " + Util.formatTimeAgo(1000 * data.mtime)).css("display", "inline");
        } else {
            //$('#document_ctime').css("display", "none");
            $('#document_mtime').css("display", "none");
        }
      };

      var addHeaderAndDefs = function() {
        var commentName = (coll + '/' + doc).replace('--', '-\\-');
        $svg.append('<!-- document: ' + commentName + ' -->');
        var defs = svg.defs();
        var $blurFilter = $('<filter id="Gaussian_Blur"><feGaussianBlur in="SourceGraphic" stdDeviation="2" /></filter>');
        svg.add(defs, $blurFilter);
        return defs;
      }

      var getTextMeasurements = function(textsHash, options, callback) {
        // make some text elements, find out the dimensions
        var textMeasureGroup = svg.group(options);

        // changed from $.each because of #264 ('length' can appear)
        for (var text in textsHash) {
          if (textsHash.hasOwnProperty(text)) {
            svg.text(textMeasureGroup, 0, 0, text);
          }
        }

        // measuring goes on here
        var widths = {};
        $(textMeasureGroup).find('text').each(function(svgTextNo, svgText) {
          var text = $(svgText).text();
          widths[text] = this.getComputedTextLength();

          if (callback) {
            $.each(textsHash[text], function(text, object) {
              callback(object, svgText);
            });
          }
        });
        var bbox = textMeasureGroup.getBBox();
        svg.remove(textMeasureGroup);

        return new Measurements(widths, bbox.height, bbox.y);
      };

      var getTextAndSpanTextMeasurements = function() {
        // get the span text sizes
        var chunkTexts = {}; // set of span texts
        $.each(data.chunks, function(chunkNo, chunk) {
          chunk.row = undefined; // reset
          if (!chunkTexts.hasOwnProperty(chunk.text)) chunkTexts[chunk.text] = []
          var chunkText = chunkTexts[chunk.text];

          // here we also need all the spans that are contained in
          // chunks with this text, because we need to know the position
          // of the span text within the respective chunk text
          chunkText.push.apply(chunkText, chunk.fragments);
          // and also the markedText boundaries
          chunkText.push.apply(chunkText, chunk.markedTextStart);
          chunkText.push.apply(chunkText, chunk.markedTextEnd);
        });
        var textSizes = getTextMeasurements(
          chunkTexts,
          undefined,
          function(fragment, text) {
            if (fragment instanceof Fragment) { // it's a fragment!
              // measure the fragment text position in pixels
              var firstChar = fragment.from - fragment.chunk.from;
              if (firstChar < 0) {
                firstChar = 0;
                dispatcher.post('messages', [[['<strong>WARNING</strong>' +
                  '<br/> ' +
                  'The fragment [' + fragment.from + ', ' + fragment.to + '] (' + fragment.text + ') is not ' +
                  'contained in its designated chunk [' +
                  fragment.chunk.from + ', ' + fragment.chunk.to + '] most likely ' +
                  'due to the fragment starting or ending with a space, please ' +
                  'verify the sanity of your data since we are unable to ' +
                  'visualise this fragment correctly and will drop leading ' +
                  'space characters'
                  , 'warning', 15]]]);
              }
              var lastChar = fragment.to - fragment.chunk.from - 1;

              // Adjust for XML whitespace (#832, #1009)
              var textUpToFirstChar = fragment.chunk.text.substring(0, firstChar);
              var textUpToLastChar = fragment.chunk.text.substring(0, lastChar);
              var textUpToFirstCharUnspaced = textUpToFirstChar.replace(/\s\s+/g, ' ');
              var textUpToLastCharUnspaced = textUpToLastChar.replace(/\s\s+/g, ' ');
              firstChar -= textUpToFirstChar.length - textUpToFirstCharUnspaced.length;
              lastChar -= textUpToLastChar.length - textUpToLastCharUnspaced.length;

              var startPos, endPos;
              if (firstChar < fragment.chunk.text.length) {
                startPos = text.getStartPositionOfChar(firstChar).x;
              } else {
                startPos = text.getComputedTextLength();
              }
              endPos = (lastChar < firstChar)
                ? startPos
                : text.getEndPositionOfChar(lastChar).x;
              fragment.curly = {
                from: startPos,
                to: endPos
              };
            } else { // it's markedText [id, start?, char#, offset]
              if (fragment[2] < 0) fragment[2] = 0;
              if (!fragment[2]) { // start
                fragment[3] = text.getStartPositionOfChar(fragment[2]).x;
              } else {
                fragment[3] = text.getEndPositionOfChar(fragment[2] - 1).x + 1;
              }
            }
          });

        // get the fragment annotation text sizes
        var fragmentTexts = {};
        var noSpans = true;
        $.each(data.spans, function(spanNo, span) {
          $.each(span.fragments, function(fragmentNo, fragment) {
            fragmentTexts[fragment.glyphedLabelText] = true;
            noSpans = false;
          });
        });
        if (noSpans) fragmentTexts.$ = true; // dummy so we can at least get the height
        var fragmentSizes = getTextMeasurements(fragmentTexts, {'class': 'span'});

        return {
          texts: textSizes,
          fragments: fragmentSizes
        };
      };

      var addArcTextMeasurements = function(sizes) {
        // get the arc annotation text sizes (for all labels)
        var arcTexts = {};
        $.each(data.arcs, function(arcNo, arc) {
          var labels = Util.getArcLabels(spanTypes, data.spans[arc.origin].type, arc.type, relationTypesHash);
          if (!labels.length) labels = [arc.type];
          $.each(labels, function(labelNo, label) {
            arcTexts[label] = true;
          });
        });
        var arcSizes = getTextMeasurements(arcTexts, {'class': 'arcs'});
        sizes.arcs = arcSizes;
      };

      var adjustTowerAnnotationSizes = function() {
        // find biggest annotation in each tower
        $.each(data.towers, function(towerNo, tower) {
          var maxWidth = 0;
          $.each(tower, function(fragmentNo, fragment) {
            var width = data.sizes.fragments.widths[fragment.glyphedLabelText];
            if (width > maxWidth) maxWidth = width;
          }); // tower
          $.each(tower, function(fragmentNo, fragment) {
            fragment.width = maxWidth;
          }); // tower
        }); // data.towers
      };

      var makeArrow = function(defs, spec) {
        var parsedSpec = spec.split(',');
        var type = parsedSpec[0];
        if (type == 'none') return;

        var width = 5;
        var height = 5;
        var color = "black";
        if ($.isNumeric(parsedSpec[1]) && parsedSpec[2]) {
          if ($.isNumeric(parsedSpec[2]) && parsedSpec[3]) {
            // 3 args, 2 numeric: assume width, height, color
            width = parsedSpec[1];
            height = parsedSpec[2];
            color = parsedSpec[3] || 'black';
          } else {
            // 2 args, 1 numeric: assume width/height, color
            width = height = parsedSpec[1];
            color = parsedSpec[2] || 'black';
          }
        } else {
          // other: assume color only
          width = height = 5;
          color = parsedSpec[1] || 'black';
        }
        // hash needs to be replaced as IDs don't permit it.
        var arrowId = 'arrow_' + spec.replace(/#/g, '').replace(/,/g, '_');

        var arrow;
        if (type == 'triangle') {
          arrow = svg.marker(defs, arrowId,
            width, height / 2, width, height, 'auto',
            {
              markerUnits: 'strokeWidth',
              'fill': color,
            });
          svg.polyline(arrow, [[0, 0], [width, height / 2], [0, height], [width / 12, height / 2]]);
        }
        return arrowId;
      }

      var createStyleMap = function(styles) {
          var styleMap = {};
          
          if (!styles) {
              return styleMap;
          }
          for (var i=0; i<styles.length; i++) {
              var style = styles[i];

              if (!style || style.length !== 3) {
                  console.log('warning: invalid style spec:', style);
                  continue;
              }

              var targetRef = style[0],
                  attribute = style[1],
                  value = style[2];

              // map multipart IDs to reference string by joining w/space
              if (targetRef instanceof Array) {
                  targetRef = targetRef.join(' ');
              }

              if (styleMap[targetRef] === undefined) {
                  styleMap[targetRef] = {};
              }
              if (styleMap[targetRef][attribute] !== undefined) {
                  console.log('warning: multiple style definitions for',
                              targetRef, attribute);
              }
              styleMap[targetRef][attribute] = value;
          }
          return styleMap;
      };

      var drawing = false;
      var redraw = false;

      var renderDataReal = function(sourceData) {
Util.profileEnd('before render');
Util.profileStart('render');
Util.profileStart('init');

        if (!sourceData && !data) {
          dispatcher.post('doneRendering', [coll, doc, args]);
          return;
        }
        $svgDiv.show();
        if ((sourceData && sourceData.collection && (sourceData.document !== doc || sourceData.collection !== coll)) || drawing) {
          redraw = true;
          dispatcher.post('doneRendering', [coll, doc, args]);
          return;
        }
        redraw = false;
        drawing = true;

        if (sourceData) setData(sourceData);
        showMtime();

        // clear the SVG
        svg.clear(true);
        if (!data || data.length == 0) return;

        // establish the width according to the enclosing element
        canvasWidth = that.forceWidth || $svgDiv.width();

        var defs = addHeaderAndDefs();

        var backgroundGroup = svg.group({ 'class': 'background' });
        var glowGroup = svg.group({ 'class': 'glow' });
        highlightGroup = svg.group({ 'class': 'highlight' });
        var textGroup = svg.group({ 'class': 'text' });

Util.profileEnd('init');
Util.profileStart('measures');

        var sizes = getTextAndSpanTextMeasurements();
        data.sizes = sizes;

        var styleMap = createStyleMap(sourceData['styles']);

        adjustTowerAnnotationSizes();
        var maxTextWidth = 0;
        for (var text in sizes.texts.widths) {
          if (sizes.texts.widths.hasOwnProperty(text)) {
            var width = sizes.texts.widths[text]
            if (width > maxTextWidth) maxTextWidth = width;
          }
        }

Util.profileEnd('measures');
Util.profileStart('chunks');

        var currentX = Configuration.visual.margin.x + sentNumMargin + rowPadding;
        var rows = [];
        var fragmentHeights = [];
        var sentenceToggle = 0;
        var sentenceNumber = 0;
        var row = new Row(svg);
        row.sentence = ++sentenceNumber;
        row.backgroundIndex = sentenceToggle;
        row.index = 0;
        var rowIndex = 0;
        var twoBarWidths; // HACK to avoid measuring space's width
        var openTextHighlights = {};
        var textMarkedRows = [];

        addArcTextMeasurements(sizes);

        // reserve places for spans
        var floors = [];
        var reservations = []; // reservations[chunk][floor] = [[from, to, headroom]...]
        for (var i = 0; i <= data.lastFragmentIndex; i++) {
          reservation[i] = {};
        }
        var inf = 1.0/0.0;

        $.each(data.spanDrawOrderPermutation, function(spanIdNo, spanId) {
          var span = data.spans[spanId];
          var spanDesc = spanTypes[span.type];
          var bgColor = ((spanDesc && spanDesc.bgColor) ||
                          (spanTypes.SPAN_DEFAULT &&
                          spanTypes.SPAN_DEFAULT.bgColor) || '#ffffff');
          if (bgColor == "hidden") {
            span.hidden = true;
            return;
          }

          var f1 = span.fragments[0];
          var f2 = span.fragments[span.fragments.length - 1];

          var x1 = (f1.curly.from + f1.curly.to - f1.width) / 2 -
              Configuration.visual.margin.x;
          var i1 = f1.chunk.index;

          var x2 = (f2.curly.from + f2.curly.to + f2.width) / 2 +
              Configuration.visual.margin.x;
          var i2 = f2.chunk.index;

          // Start from the ground level, going up floor by floor.
          // If no more floors, make a new available one.
          // If a floor is available and there is no carpet, mark it as carpet.
          // If a floor is available and there is carpet and height
          //   difference is at least fragment height + curly, OK.
          // If a floor is not available, forget about carpet.
          // --
          // When OK, calculate exact ceiling.
          // If there isn't one, make a new floor, copy reservations
          //   from floor below (with decreased ceiling)
          // Make the reservation from the carpet to just below the
          //   current floor.
          //
          // TODO drawCurly and height could be prettified to only check
          // actual positions of curlies
          var carpet = 0;
          var outside = true;
          var thisCurlyHeight = span.drawCurly ? Configuration.visual.curlyHeight : 0;
          var height = sizes.fragments.height + thisCurlyHeight + Configuration.visual.boxSpacing +
              2 * Configuration.visual.margin.y - 3;
          $.each(floors, function(floorNo, floor) {
            var floorAvailable = true;
            for (var i = i1; i <= i2; i++) {
              if (!(reservations[i] && reservations[i][floor])) continue;
              var from = (i == i1) ? x1 : -inf;
              var to = (i == i2) ? x2 : inf;
              $.each(reservations[i][floor], function(resNo, res) {
                if (res[0] < to && from < res[1]) {
                  floorAvailable = false;
                  return false;
                }
              });
            }
            if (floorAvailable) {
              if (carpet === null) {
                carpet = floor;
              } else if (height + carpet <= floor) {
                // found our floor!
                outside = false;
                return false;
              }
            } else {
              carpet = null;
            }
          });
          var reslen = reservations.length;
          var makeNewFloorIfNeeded = function(floor) {
            var floorNo = $.inArray(floor, floors);
            if (floorNo == -1) {
              floors.push(floor);
              floors.sort(Util.cmp);
              floorNo = $.inArray(floor, floors);
              if (floorNo != 0) {
                // copy reservations from the floor below
                var parquet = floors[floorNo - 1];
                for (var i = 0; i <= reslen; i++) {
                  if (reservations[i]) {
                    if (!reservations[i][parquet]) {
                      reservations[i][parquet] = [];
                    }
                    var footroom = floor - parquet;
                    $.each(reservations[i][parquet], function(resNo, res) {
                      if (res[2] > footroom) {
                        if (!reservations[i][floor]) {
                          reservations[i][floor] = [];
                        }
                        reservations[i][floor].push([res[0], res[1], res[2] - footroom]);
                      }
                    });
                  }
                }
              }
            }
            return floorNo;
          }
          var ceiling = carpet + height;
          var ceilingNo = makeNewFloorIfNeeded(ceiling);
          var carpetNo = makeNewFloorIfNeeded(carpet);
          // make the reservation
          var floor, floorNo;
          for (floorNo = carpetNo;
              (floor = floors[floorNo]) !== undefined && floor < ceiling;
              floorNo++) {
            var headroom = ceiling - floor;
            for (var i = i1; i <= i2; i++) {
              var from = (i == i1) ? x1 : 0;
              var to = (i == i2) ? x2 : inf;
              if (!reservations[i]) reservations[i] = {};
              if (!reservations[i][floor]) reservations[i][floor] = [];
              reservations[i][floor].push([from, to, headroom]); // XXX maybe add fragment; probably unnecessary
            }
          }
          span.floor = carpet + thisCurlyHeight;
        });

        $.each(data.chunks, function(chunkNo, chunk) {
          var spaceWidth = 0;
          if (chunk.lastSpace) {
            var spaceLen = chunk.lastSpace.length || 0;
            var spacePos = chunk.lastSpace.lastIndexOf("\n") + 1;
            if (spacePos || !chunkNo || !chunk.sentence) {
              // indent if
              // * non-first paragraph first word
              // * first paragraph first word
              // * non-first word in a line
              // don't indent if
              // * the first word in a non-paragraph line
              for (var i = spacePos; i < spaceLen; i++) spaceWidth += spaceWidths[chunk.lastSpace[i]] || 0;
              currentX += spaceWidth;
            }
          }

          chunk.group = svg.group(row.group);
          chunk.highlightGroup = svg.group(chunk.group);

          var y = 0;
          var minArcDist;
          var hasLeftArcs, hasRightArcs, hasInternalArcs;
          var hasAnnotations;
          var chunkFrom = Infinity;
          var chunkTo = 0;
          var chunkHeight = 0;
          var spacing = 0;
          var spacingChunkId = null;
          var spacingRowBreak = 0;

          $.each(chunk.fragments, function(fragmentNo, fragment) {
            var span = fragment.span;
            var spanDesc = spanTypes[span.type];
            var bgColor = ((spanDesc && spanDesc.bgColor) ||
                           (spanTypes.SPAN_DEFAULT &&
                            spanTypes.SPAN_DEFAULT.bgColor) || '#ffffff');
            if (span.hidden) return;
            var fgColor = ((spanDesc && spanDesc.fgColor) ||
                           (spanTypes.SPAN_DEFAULT &&
                            spanTypes.SPAN_DEFAULT.fgColor) || '#000000');
            var borderColor = ((spanDesc && spanDesc.borderColor) ||
                               (spanTypes.SPAN_DEFAULT &&
                                spanTypes.SPAN_DEFAULT.borderColor) || '#000000');

            // set span-specific styles
            if (styleMap[span.id]) {
                if (styleMap[span.id]['fgColor'] !== undefined) {
                    fgColor = styleMap[span.id]['fgColor'];
                }
                if (styleMap[span.id]['bgColor'] !== undefined) {
                    bgColor = styleMap[span.id]['bgColor'];
                }
                if (styleMap[span.id]['borderColor'] !== undefined) {
                    borderColor = styleMap[span.id]['borderColor'];
                }
            }

            // special case: if the border 'color' value is 'darken',
            // then just darken the BG color a bit for the border.
            if (borderColor == 'darken') {
                borderColor = Util.adjustColorLightness(bgColor, -0.6);
            }

            fragment.group = svg.group(chunk.group, {
              'class': 'span',
            });

            var fragmentHeight = 0;

            if (!y) y = -sizes.texts.height;
            var x = (fragment.curly.from + fragment.curly.to) / 2;

            // XXX is it maybe sizes.texts?
            var yy = y + sizes.fragments.y;
            var hh = sizes.fragments.height;
            var ww = fragment.width;
            var xx = x - ww / 2;

            // text margin fine-tuning
            yy += boxTextMargin.y;
            hh -= 2*boxTextMargin.y;
            xx += boxTextMargin.x;
            ww -= 2*boxTextMargin.x;
            var rectClass = 'span_' + (span.cue || span.type) + ' span_default'; // TODO XXX first part unneeded I think; remove

            // attach e.g. "False_positive" into the type
            if (span.comment && span.comment.type) { rectClass += ' '+span.comment.type; }
            var bx = xx - Configuration.visual.margin.x - boxTextMargin.x;
            var by = yy - Configuration.visual.margin.y;
            var bw = ww + 2 * Configuration.visual.margin.x;
            var bh = hh + 2 * Configuration.visual.margin.y;

            if (roundCoordinates) {
              x  = (x|0)+0.5;
              bx = (bx|0)+0.5;
            }

            var shadowRect;
            var markedRect;
            if (span.marked) {
              markedRect = svg.rect(chunk.highlightGroup,
                  bx - markedSpanSize, by - markedSpanSize,
                  bw + 2 * markedSpanSize, bh + 2 * markedSpanSize, {

                  // filter: 'url(#Gaussian_Blur)',
                  'class': "shadow_EditHighlight",
                  rx: markedSpanSize,
                  ry: markedSpanSize,
              });
              svg.other(markedRect, 'animate', {
                'data-type': span.marked,
                attributeName: 'fill',
                values: (span.marked == 'match'? highlightMatchSequence
                         : highlightSpanSequence),
                dur: highlightDuration,
                repeatCount: 'indefinite',
                begin: 'indefinite'
              });
              chunkFrom = Math.min(bx - markedSpanSize, chunkFrom);
              chunkTo = Math.max(bx + bw + markedSpanSize, chunkTo);
              fragmentHeight = Math.max(bh + 2 * markedSpanSize, fragmentHeight);
            }
            // .match() removes unconfigured shadows, which were
            // always showing up as black.
            // TODO: don't hard-code configured shadowclasses.
            if (span.shadowClass &&
                span.shadowClass.match('True_positive|False_positive|False_negative|AnnotationError|AnnotationWarning|AnnotatorNotes|Normalized|AnnotationIncomplete|AnnotationUnconfirmed|rectEditHighlight|EditHighlight_arc|MissingAnnotation|ChangedAnnotation ')) {
              shadowRect = svg.rect(fragment.group,
                  bx - rectShadowSize, by - rectShadowSize,
                  bw + 2 * rectShadowSize, bh + 2 * rectShadowSize, {
                  'class': 'shadow_' + span.shadowClass,
                  filter: 'url(#Gaussian_Blur)',
                  rx: rectShadowRounding,
                  ry: rectShadowRounding,
              });
              chunkFrom = Math.min(bx - rectShadowSize, chunkFrom);
              chunkTo = Math.max(bx + bw + rectShadowSize, chunkTo);
              fragmentHeight = Math.max(bh + 2 * rectShadowSize, fragmentHeight);
            }
            fragment.rect = svg.rect(fragment.group,
                bx, by, bw, bh, {

                'class': rectClass,
                fill: bgColor,
                stroke: borderColor,
                rx: Configuration.visual.margin.x,
                ry: Configuration.visual.margin.y,
                'data-span-id': span.id,
                'data-fragment-id': span.segmentedOffsetsMap[fragment.id],
                'strokeDashArray': span.attributeMerge.dashArray,
              });

            // TODO XXX: quick nasty hack to allow normalizations
            // to be marked visually; do something cleaner!
            if (span.normalized) {
              $(fragment.rect).addClass(span.normalized);
            }

            fragment.right = bx + bw; // TODO put it somewhere nicer?
            if (!(span.shadowClass || span.marked)) {
              chunkFrom = Math.min(bx, chunkFrom);
              chunkTo = Math.max(bx + bw, chunkTo);
              fragmentHeight = Math.max(bh, fragmentHeight);
            }

            fragment.rectBox = { x: bx, y: by - span.floor, width: bw, height: bh };
            fragment.height = span.floor + hh + 3 * Configuration.visual.margin.y + Configuration.visual.curlyHeight + Configuration.visual.arcSpacing;
            var spacedTowerId = fragment.towerId * 2;
            if (!fragmentHeights[spacedTowerId] || fragmentHeights[spacedTowerId] < fragment.height) {
              fragmentHeights[spacedTowerId] = fragment.height;
            }
            $(fragment.rect).attr('y', yy - Configuration.visual.margin.y - span.floor);
            if (shadowRect) {
              $(shadowRect).attr('y', yy - rectShadowSize - Configuration.visual.margin.y - span.floor);
            }
            if (markedRect) {
              $(markedRect).attr('y', yy - markedSpanSize - Configuration.visual.margin.y - span.floor);
            }
            if (span.attributeMerge.box === "crossed") {
              svg.path(fragment.group, svg.createPath().
                  move(xx, yy - Configuration.visual.margin.y - span.floor).
                  line(xx + fragment.width,
                    yy + hh + Configuration.visual.margin.y - span.floor),
                  { 'class': 'boxcross' });
              svg.path(fragment.group, svg.createPath().
                  move(xx + fragment.width, yy - Configuration.visual.margin.y - span.floor).
                  line(xx, yy + hh + Configuration.visual.margin.y - span.floor),
                  { 'class': 'boxcross' });
            }
            svg.text(fragment.group, x, y - span.floor, data.spanAnnTexts[fragment.glyphedLabelText], { fill: fgColor });

            // Make curlies to show the fragment
            if (fragment.drawCurly) {
              var curlyColor = 'grey';
              if (coloredCurlies) {
                var spanDesc = spanTypes[span.type];
                var bgColor = ((spanDesc && spanDesc.bgColor) ||
                               (spanTypes.SPAN_DEFAULT &&
                                spanTypes.SPAN_DEFAULT.fgColor) ||
                               '#000000');
                curlyColor = Util.adjustColorLightness(bgColor, -0.6);
              }

              var bottom = yy + hh + Configuration.visual.margin.y - span.floor + 1;
              svg.path(fragment.group, svg.createPath()
                  .move(fragment.curly.from, bottom + Configuration.visual.curlyHeight)
                  .curveC(fragment.curly.from, bottom,
                    x, bottom + Configuration.visual.curlyHeight,
                    x, bottom)
                  .curveC(x, bottom + Configuration.visual.curlyHeight,
                    fragment.curly.to, bottom,
                    fragment.curly.to, bottom + Configuration.visual.curlyHeight),
                {
                  'class': 'curly',
                  'stroke': curlyColor,
                });
              chunkFrom = Math.min(fragment.curly.from, chunkFrom);
              chunkTo = Math.max(fragment.curly.to, chunkTo);
              fragmentHeight = Math.max(Configuration.visual.curlyHeight, fragmentHeight);
            }

            if (fragment == span.headFragment) {
              // find the gap to fit the backwards arcs, but only on
              // head fragment - other fragments don't have arcs
              $.each(span.incoming, function(arcId, arc) {
                var leftSpan = data.spans[arc.origin];
                var origin = leftSpan.headFragment.chunk;
                var border;
                if (chunk.index == origin.index) {
                  hasInternalArcs = true;
                }
                if (origin.row) {
                  var labels = Util.getArcLabels(spanTypes, leftSpan.type, arc.type, relationTypesHash);
                  if (!labels.length) labels = [arc.type];
                  if (origin.row.index == rowIndex) {
                    // same row, but before this
                    border = origin.translation.x + leftSpan.fragments[leftSpan.fragments.length - 1].right;
                  } else {
                    border = Configuration.visual.margin.x + sentNumMargin + rowPadding;
                  }
                  var labelNo = Configuration.abbrevsOn ? labels.length - 1 : 0;
                  var smallestLabelWidth = sizes.arcs.widths[labels[labelNo]] + 2 * minArcSlant;
                  var gap = currentX + bx - border;
                  var arcSpacing = smallestLabelWidth - gap;
                  if (!hasLeftArcs || spacing < arcSpacing) {
                    spacing = arcSpacing;
                    spacingChunkId = origin.index + 1;
                  }
                  arcSpacing = smallestLabelWidth - bx;
                  if (!hasLeftArcs || spacingRowBreak < arcSpacing) {
                    spacingRowBreak = arcSpacing;
                  }
                  hasLeftArcs = true;
                } else {
                  hasRightArcs = true;
                }
              });
              $.each(span.outgoing, function(arcId, arc) {
                var leftSpan = data.spans[arc.target];
                var target = leftSpan.headFragment.chunk;
                var border;
                if (target.row) {
                  var labels = Util.getArcLabels(spanTypes, span.type, arc.type, relationTypesHash);
                  if (!labels.length) labels = [arc.type];
                  if (target.row.index == rowIndex) {
                    // same row, but before this
                    border = target.translation.x + leftSpan.fragments[leftSpan.fragments.length - 1].right;
                  } else {
                    border = Configuration.visual.margin.x + sentNumMargin + rowPadding;
                  }
                  var labelNo = Configuration.abbrevsOn ? labels.length - 1 : 0;
                  var smallestLabelWidth = sizes.arcs.widths[labels[labelNo]] + 2 * minArcSlant;
                  var gap = currentX + bx - border;
                  var arcSpacing = smallestLabelWidth - gap;
                  if (!hasLeftArcs || spacing < arcSpacing) {
                    spacing = arcSpacing;
                    spacingChunkId = target.index + 1;
                  }
                  arcSpacing = smallestLabelWidth - bx;
                  if (!hasLeftArcs || spacingRowBreak < arcSpacing) {
                    spacingRowBreak = arcSpacing;
                  }
                  hasLeftArcs = true;
                } else {
                  hasRightArcs = true;
                }
              });
            }
            fragmentHeight += span.floor || Configuration.visual.curlyHeight;
            if (fragmentHeight > chunkHeight) chunkHeight = fragmentHeight;
            hasAnnotations = true;
          }); // fragments

          // positioning of the chunk
          chunk.right = chunkTo;
          var textWidth = sizes.texts.widths[chunk.text];
          chunkHeight += sizes.texts.height;
          var boxX = -Math.min(chunkFrom, 0);
          var boxWidth =
              Math.max(textWidth, chunkTo) -
              Math.min(0, chunkFrom);
          // if (hasLeftArcs) {
            // TODO change this with smallestLeftArc
            // var spacing = arcHorizontalSpacing - (currentX - lastArcBorder);
            // arc too small?
          if (spacing > 0) currentX += spacing;
          // }
          var rightBorderForArcs = hasRightArcs ? arcHorizontalSpacing : (hasInternalArcs ? arcSlant : 0);

          var lastX = currentX;
          var lastRow = row;

          if (chunk.sentence) {
            while (sentenceNumber < chunk.sentence) {
              sentenceNumber++;
              row.arcs = svg.group(row.group, { 'class': 'arcs' });
              rows.push(row);
              row = new Row(svg);
              sentenceToggle = 1 - sentenceToggle;
              row.backgroundIndex = sentenceToggle;
              row.index = ++rowIndex;
            }
            sentenceToggle = 1 - sentenceToggle;
          }

          if (chunk.sentence ||
              currentX + boxWidth + rightBorderForArcs >= canvasWidth - 2 * Configuration.visual.margin.x) {
            // the chunk does not fit
            row.arcs = svg.group(row.group, { 'class': 'arcs' });
            // TODO: related to issue #571
            // replace arcHorizontalSpacing with a calculated value
            currentX = Configuration.visual.margin.x + sentNumMargin + rowPadding +
                (hasLeftArcs ? arcHorizontalSpacing : (hasInternalArcs ? arcSlant : 0)) +
                spaceWidth;
            if (hasLeftArcs) {
              var adjustedCurTextWidth = sizes.texts.widths[chunk.text] + arcHorizontalSpacing;
              if (adjustedCurTextWidth > maxTextWidth) {
                maxTextWidth = adjustedCurTextWidth;
              }
            }
            if (spacingRowBreak > 0) {
              currentX += spacingRowBreak;
              spacing = 0; // do not center intervening elements
            }

            // new row
            rows.push(row);

            svg.remove(chunk.group);
            row = new Row(svg);
            row.backgroundIndex = sentenceToggle;
            row.index = ++rowIndex;
            svg.add(row.group, chunk.group);
            chunk.group = row.group.lastElementChild;
            $(chunk.group).children("g[class='span']").
              each(function(index, element) {
                  chunk.fragments[index].group = element;
              });
            $(chunk.group).find("rect[data-span-id]").
              each(function(index, element) {
                  chunk.fragments[index].rect = element;
              });
          }

          // break the text highlights when the row breaks
          if (row.index !== lastRow.index) {
            $.each(openTextHighlights, function(textId, textDesc) {
              if (textDesc[3] != lastX) {
                var newDesc = [lastRow, textDesc[3], lastX + boxX, textDesc[4]];
                textMarkedRows.push(newDesc);
              }
              textDesc[3] = currentX;
            });
          }

          // open text highlights
          $.each(chunk.markedTextStart, function(textNo, textDesc) {
            textDesc[3] += currentX + boxX;
            openTextHighlights[textDesc[0]] = textDesc;
          });

          // close text highlights
          $.each(chunk.markedTextEnd, function(textNo, textDesc) {
            textDesc[3] += currentX + boxX;
            var startDesc = openTextHighlights[textDesc[0]];
            delete openTextHighlights[textDesc[0]];
            markedRow = [row, startDesc[3], textDesc[3], startDesc[4]];
            textMarkedRows.push(markedRow);
          });

          // XXX check this - is it used? should it be lastRow?
          if (hasAnnotations) row.hasAnnotations = true;

          if (chunk.sentence) {
            row.sentence = ++sentenceNumber;
          }

          if (spacing > 0) {
            // if we added a gap, center the intervening elements
            spacing /= 2;
            var firstChunkInRow = row.chunks[row.chunks.length - 1];
            if (firstChunkInRow === undefined) {
              console.log('warning: firstChunkInRow undefined, chunk:', chunk);
            } else { // valid firstChunkInRow
              if (spacingChunkId < firstChunkInRow.index) {
                spacingChunkId = firstChunkInRow.index + 1;
              }
              for (var chunkIndex = spacingChunkId; chunkIndex < chunk.index; chunkIndex++) {
                var movedChunk = data.chunks[chunkIndex];
                translate(movedChunk, movedChunk.translation.x + spacing, 0);
                movedChunk.textX += spacing;
              }
            }
          }

          row.chunks.push(chunk);
          chunk.row = row;

          translate(chunk, currentX + boxX, 0);
          chunk.textX = currentX + boxX;

          currentX += boxWidth;
        }); // chunks

        // finish the last row
        row.arcs = svg.group(row.group, { 'class': 'arcs' });
        rows.push(row);

Util.profileEnd('chunks');
Util.profileStart('arcsPrep');

        var arrows = {};
        var arrow = makeArrow(defs, 'none');
        if (arrow) arrows['none'] = arrow;

        var len = fragmentHeights.length;
        for (var i = 0; i < len; i++) {
          if (!fragmentHeights[i] || fragmentHeights[i] < Configuration.visual.arcStartHeight) {
            fragmentHeights[i] = Configuration.visual.arcStartHeight;
          }
        }

        // find out how high the arcs have to go
        $.each(data.arcs, function(arcNo, arc) {
          arc.jumpHeight = 0;
          var fromFragment = data.spans[arc.origin].headFragment;
          var toFragment = data.spans[arc.target].headFragment;
          if (fromFragment.span.hidden || toFragment.span.hidden) {
            arc.hidden = true;
            return;
          }
          if (fromFragment.towerId > toFragment.towerId) {
            var tmp = fromFragment; fromFragment = toFragment; toFragment = tmp;
          }
          var from, to;
          if (fromFragment.chunk.index == toFragment.chunk.index) {
            from = fromFragment.towerId;
            to = toFragment.towerId;
          } else {
            from = fromFragment.towerId + 1;
            to = toFragment.towerId - 1;
          }
          for (var i = from; i <= to; i++) {
            if (arc.jumpHeight < fragmentHeights[i * 2]) arc.jumpHeight = fragmentHeights[i * 2];
          }
        });

        // sort the arcs
        data.arcs.sort(function(a, b) {
          // first write those that have less to jump over
          var tmp = a.jumpHeight - b.jumpHeight;
          if (tmp) return tmp < 0 ? -1 : 1;
          // if equal, then those that span less distance
          tmp = a.dist - b.dist;
          if (tmp) return tmp < 0 ? -1 : 1;
          // if equal, then those where heights of the targets are smaller
          tmp = data.spans[a.origin].headFragment.height + data.spans[a.target].headFragment.height -
            data.spans[b.origin].headFragment.height - data.spans[b.target].headFragment.height;
          if (tmp) return tmp < 0 ? -1 : 1;
          // if equal, then those with the lower origin
          tmp = data.spans[a.origin].headFragment.height - data.spans[b.origin].headFragment.height;
          if (tmp) return tmp < 0 ? -1 : 1;
          // if equal, they're just equal.
          return 0;
        });

        // see which fragments are in each row
        var heightsStart = 0;
        var heightsRowsAdded = 0;
        $.each(rows, function(rowId, row) {
          var seenFragment = false;
          row.heightsStart = row.heightsEnd = heightsStart;
          $.each(row.chunks, function(chunkId, chunk) {
            if (chunk.lastFragmentIndex !== undefined) {
              // fragmentful chunk
              seenFragment = true;
              var heightsIndex = chunk.lastFragmentIndex * 2 + heightsRowsAdded;
              if (row.heightsEnd < heightsIndex) {
                row.heightsEnd = heightsIndex;
              }
              var heightsIndex = chunk.firstFragmentIndex * 2 + heightsRowsAdded;
              if (row.heightsStart > heightsIndex) {
                row.heightsStart = heightsIndex;
              }
            }
          });
          fragmentHeights.splice(row.heightsStart, 0, Configuration.visual.arcStartHeight);
          heightsRowsAdded++;

          row.heightsAdjust = heightsRowsAdded;
          if (seenFragment) {
            row.heightsEnd += 2;
          }
          heightsStart = row.heightsEnd + 1;
        });

        // draw the drag arc marker
        var arrowhead = svg.marker(defs, 'drag_arrow',
          5, 2.5, 5, 5, 'auto',
          {
            markerUnits: 'strokeWidth',
            'class': 'drag_fill',
          });
        svg.polyline(arrowhead, [[0, 0], [5, 2.5], [0, 5], [0.2, 2.5]]);
        var arcDragArc = svg.path(svg.createPath(), {
          markerEnd: 'url(#drag_arrow)',
          'class': 'drag_stroke',
          fill: 'none',
          visibility: 'hidden',
        });
        dispatcher.post('arcDragArcDrawn', [arcDragArc]);

Util.profileEnd('arcsPrep');
Util.profileStart('arcs');

        var arcCache = {};
        // add the arcs
        $.each(data.arcs, function(arcNo, arc) {
          if (arc.hidden) return;
          // separate out possible numeric suffix from type
          var noNumArcType;
          var splitArcType;
          if (arc.type) {
            splitArcType = arc.type.match(/^(.*?)(\d*)$/);
            noNumArcType = splitArcType[1];
          }

          var originSpan = data.spans[arc.origin];
          var targetSpan = data.spans[arc.target];

          var leftToRight = originSpan.headFragment.towerId < targetSpan.headFragment.towerId;
          var left, right;
          if (leftToRight) {
            left = originSpan.headFragment;
            right = targetSpan.headFragment;
          } else {
            left = targetSpan.headFragment;
            right = originSpan.headFragment;
          }

          // fall back on relation types in case we still don't have
          // an arc description, with final fallback to unnumbered relation
          var arcDesc = relationTypesHash[arc.type] || relationTypesHash[noNumArcType];

          // if it's not a relationship, see if we can find it in span
          // descriptions
          // TODO: might make more sense to reformat this as dict instead
          // of searching through the list every type
          var spanDesc = spanTypes[originSpan.type];
          if (!arcDesc && spanDesc && spanDesc.arcs) {
            $.each(spanDesc.arcs, function(arcDescNo, arcDescIter) {
                if (arcDescIter.type == arc.type) {
                  arcDesc = arcDescIter;
                }
              });
          }
          // last fall back on unnumbered type if not found in full
          if (!arcDesc && noNumArcType && noNumArcType != arc.type &&
            spanDesc && spanDesc.arcs) {
            $.each(spanDesc.arcs, function(arcDescNo, arcDescIter) {
                if (arcDescIter.type == noNumArcType) {
                  arcDesc = arcDescIter;
                }
              });
          }
          // empty default
          if (!arcDesc) arcDesc = {};

          var color = ((arcDesc && arcDesc.color) ||
                       (spanTypes.ARC_DEFAULT && spanTypes.ARC_DEFAULT.color) ||
                       '#000000');
          if (color == 'hidden') return;
          var symmetric = arcDesc && arcDesc.properties && arcDesc.properties.symmetric;
          var dashArray = arcDesc && arcDesc.dashArray;
          var arrowHead = ((arcDesc && arcDesc.arrowHead) ||
                           (spanTypes.ARC_DEFAULT && spanTypes.ARC_DEFAULT.arrowHead) ||
                           'triangle,5');
          var labelArrowHead = ((arcDesc && arcDesc.labelArrow) ||
                                (spanTypes.ARC_DEFAULT && spanTypes.ARC_DEFAULT.labelArrow) ||
                                'triangle,5');

          // set arc-specific styles
          var arcRef = [arc.origin, arc.target, arc.type].join(' ');
          if (styleMap[arcRef]) {
              if (styleMap[arcRef]['color'] !== undefined) {
                  color = styleMap[arcRef]['color'];
              }
              if (styleMap[arcRef]['symmetric'] !== undefined) {
                  symmetric = styleMap[arcRef]['symmetric'];
              }
              if (styleMap[arcRef]['dashArray'] !== undefined) {
                  dashArray = styleMap[arcRef]['dashArray'];
              }
              if (styleMap[arcRef]['arrowHead'] !== undefined) {
                  arrowHead = styleMap[arcRef]['arrowHead'];
              }
              if (styleMap[arcRef]['labelArrow'] !== undefined) {
                  labelArrowHead = styleMap[arcRef]['labelArrow'];
              }
          }

          arrowHead +=  ',' + color;
          labelArrowHead += ',' + color;

          var leftBox = rowBBox(left);
          var rightBox = rowBBox(right);
          var leftRow = left.chunk.row.index;
          var rightRow = right.chunk.row.index;

          if (!arrows[arrowHead]) {
            var arrow = makeArrow(defs, arrowHead);
            if (arrow) arrows[arrowHead] = arrow;
          }
          if (!arrows[labelArrowHead]) {
            var arrow = makeArrow(defs, labelArrowHead);
            if (arrow) arrows[labelArrowHead] = arrow;
          }

          // find the next height
          var height;

          var fromIndex2, toIndex2;
          if (left.chunk.index == right.chunk.index) {
            fromIndex2 = left.towerId * 2 + left.chunk.row.heightsAdjust;
            toIndex2 = right.towerId * 2 + right.chunk.row.heightsAdjust;
          } else {
            fromIndex2 = left.towerId * 2 + 1 + left.chunk.row.heightsAdjust;
            toIndex2 = right.towerId * 2 - 1 + right.chunk.row.heightsAdjust;
          }
          if (!collapseArcSpace) {
            height = findArcHeight(fromIndex2, toIndex2, fragmentHeights);
            adjustFragmentHeights(fromIndex2, toIndex2, fragmentHeights, height);

            // Adjust the height to align with pixels when rendered

            // TODO: on at least Chrome, this doesn't make a difference:
            // the lines come out pixel-width even without it. Check.
            height += 0.5;
          }
          var leftSlantBound, rightSlantBound;

          var chunkReverse = false;
          var ufoCatcher = originSpan.headFragment.chunk.index == targetSpan.headFragment.chunk.index;
          if (ufoCatcher) {
            chunkReverse =
              leftBox.x + leftBox.width / 2 < rightBox.x + rightBox.width / 2;
          }
          var ufoCatcherMod = ufoCatcher ? chunkReverse ? -0.5 : 0.5 : 1;

          for (var rowIndex = leftRow; rowIndex <= rightRow; rowIndex++) {
            var row = rows[rowIndex];
            if (row.chunks.length) {
              row.hasAnnotations = true;

              if (collapseArcSpace) {
                var fromIndex2R = rowIndex == leftRow ? fromIndex2 : row.heightsStart;
                var toIndex2R = rowIndex == rightRow ? toIndex2 : row.heightsEnd;
                height = findArcHeight(fromIndex2R, toIndex2R, fragmentHeights);
              }

              var arcGroup = svg.group(row.arcs, {
                  'data-from': arc.origin,
                  'data-to': arc.target
              });
              var from, to;

              if (rowIndex == leftRow) {
                from = leftBox.x + (chunkReverse ? 0 : leftBox.width);
              } else {
                from = sentNumMargin;
              }

              if (rowIndex == rightRow) {
                to = rightBox.x + (chunkReverse ? rightBox.width : 0);
              } else {
                to = canvasWidth - 2 * Configuration.visual.margin.y;
              }

              var adjustHeight = true;
              if (collapseArcs) {
                var arcCacheKey = arc.type + ' ' + rowIndex + ' ' + from + ' ' + to;
                if (rowIndex == leftRow) arcCacheKey = left.span.id + ' ' + arcCacheKey;
                if (rowIndex == rightRow) arcCacheKey += ' ' + right.span.id;
                var rowHeight = arcCache[arcCacheKey];
                if (rowHeight !== undefined) {
                  height = rowHeight;
                  adjustHeight = false;
                } else {
                  arcCache[arcCacheKey] = height;
                }
              }

              if (collapseArcSpace && adjustHeight) {
                adjustFragmentHeights(fromIndex2R, toIndex2R, fragmentHeights, height);

                // Adjust the height to align with pixels when rendered

                // TODO: on at least Chrome, this doesn't make a difference:
                // the lines come out pixel-width even without it. Check.
                height += 0.5;
              }

              var originType = data.spans[arc.origin].type;
              var arcLabels = Util.getArcLabels(spanTypes, originType, arc.type, relationTypesHash);
              var labelText = Util.arcDisplayForm(spanTypes, originType, arc.type, relationTypesHash);
              // if (Configuration.abbrevsOn && !ufoCatcher && arcLabels) {
              if (Configuration.abbrevsOn && arcLabels) {
                var labelIdx = 1; // first abbreviation
                // strictly speaking 2*arcSlant would be needed to allow for
                // the full-width arcs to fit, but judged unabbreviated text
                // to be more important than the space for arcs.
                var maxLength = (to - from) - (arcSlant);
                while (sizes.arcs.widths[labelText] > maxLength &&
                       arcLabels[labelIdx]) {
                  labelText = arcLabels[labelIdx];
                  labelIdx++;
                }
              }

              var shadowGroup;
              if (arc.shadowClass || arc.marked) {
                shadowGroup = svg.group(arcGroup);
              }
              var options = {
                'fill': color,
                'data-arc-role': arc.type,
                'data-arc-origin': arc.origin,
                'data-arc-target': arc.target,
                // TODO: confirm this is unused and remove.
                //'data-arc-id': arc.id,
                'data-arc-ed': arc.eventDescId,
              };

              // construct SVG text, showing possible trailing index
              // numbers (as in e.g. "Theme2") as subscripts
              var svgText;
              if (!splitArcType[2]) {
                  // no subscript, simple string suffices
                  svgText = labelText;
              } else {
                  // Need to parse out possible numeric suffixes to avoid
                  // duplicating number in label and its subscript
                  var splitLabelText = labelText.match(/^(.*?)(\d*)$/);
                  var noNumLabelText = splitLabelText[1];

                  svgText = svg.createText();
                  // TODO: to address issue #453, attaching options also
                  // to spans, not only primary text. Make sure there
                  // are no problems with this.
                  svgText.span(noNumLabelText, options);
                  var subscriptSettings = {
                    'dy': '0.3em',
                    'font-size': '80%'
                  };
                  // alternate possibility
  //                 var subscriptSettings = {
  //                   'baseline-shift': 'sub',
  //                   'font-size': '80%'
  //                 };
                  $.extend(subscriptSettings, options);
                  svgText.span(splitArcType[2], subscriptSettings);
              }

              // guess at the correct baseline shift to get vertical centering.
              // (CSS dominant-baseline can't be used as not all SVG rendereds support it.)
              var baseline_shift = sizes.arcs.height / 4;
              var text = svg.text(arcGroup, (from + to) / 2, -height + baseline_shift,
                                  svgText, options);

              var width = sizes.arcs.widths[labelText];
              var textBox = {
                x: (from + to - width) / 2,
                width: width,
                y: -height - sizes.arcs.height / 2,
                height: sizes.arcs.height,
              }
              if (arc.marked) {
                var markedRect = svg.rect(shadowGroup,
                    textBox.x - markedArcSize, textBox.y - markedArcSize,
                    textBox.width + 2 * markedArcSize, textBox.height + 2 * markedArcSize, {
                      // filter: 'url(#Gaussian_Blur)',
                      'class': "shadow_EditHighlight",
                      rx: markedArcSize,
                      ry: markedArcSize,
                });
                svg.other(markedRect, 'animate', {
                  'data-type': arc.marked,
                  attributeName: 'fill',
                  values: (arc.marked == 'match' ? highlightMatchSequence
                           : highlightArcSequence),
                  dur: highlightDuration,
                  repeatCount: 'indefinite',
                  begin: 'indefinite'
                });
              }
              if (arc.shadowClass) {
                svg.rect(shadowGroup,
                    textBox.x - arcLabelShadowSize,
                    textBox.y - arcLabelShadowSize,
                    textBox.width  + 2 * arcLabelShadowSize,
                    textBox.height + 2 * arcLabelShadowSize, {
                      'class': 'shadow_' + arc.shadowClass,
                      filter: 'url(#Gaussian_Blur)',
                      rx: arcLabelShadowRounding,
                      ry: arcLabelShadowRounding,
                });
              }
              var textStart = textBox.x;
              var textEnd = textBox.x + textBox.width;

              // adjust by margin for arc drawing
              textStart -= Configuration.visual.arcTextMargin;
              textEnd += Configuration.visual.arcTextMargin;

              if (from > to) {
                var tmp = textStart; textStart = textEnd; textEnd = tmp;
              }

              var path;

              if (roundCoordinates) {
                // don't ask
                height = (height|0)+0.5;
              }
              if (height > row.maxArcHeight) row.maxArcHeight = height;

              var myArrowHead   = ((styleMap[arcRef] && styleMap[arcRef]['arrowHead']) ||
                                   (arcDesc && arcDesc.arrowHead) ||
                                   (spanTypes.ARC_DEFAULT && spanTypes.ARC_DEFAULT.arrowHead));
              var arrowName = (symmetric ? myArrowHead || 'none' :
                    (leftToRight ? 'none' : myArrowHead || 'triangle,5')
                  ) + ',' + color;
              var arrowType = arrows[arrowName];
              var arrowDecl = arrowType && ('url(#' + arrowType + ')');

              var arrowAtLabelAdjust = 0;
              var labelArrowDecl = null;
              var myLabelArrowHead = ((styleMap[arcRef] && styleMap[arcRef]['labelArrow']) ||
                                      (arcDesc && arcDesc.labelArrow) ||
                                      (spanTypes.ARC_DEFAULT && spanTypes.ARC_DEFAULT.labelArrow));
              if (myLabelArrowHead) {
                var labelArrowName = (leftToRight ?
                    symmetric && myLabelArrowHead || 'none' :
                    myLabelArrowHead || 'triangle,5') + ',' + color;
                var labelArrowSplit = labelArrowName.split(',');
                arrowAtLabelAdjust = labelArrowSplit[0] != 'none' && parseInt(labelArrowSplit[1], 10) || 0;
                var labelArrowType = arrows[labelArrowName];
                var labelArrowDecl = labelArrowType && ('url(#' + labelArrowType + ')');
                if (ufoCatcher) arrowAtLabelAdjust = -arrowAtLabelAdjust;
              }
              var arrowStart = textStart - arrowAtLabelAdjust;
              path = svg.createPath().move(arrowStart, -height);
              if (rowIndex == leftRow) {
                var cornerx = from + ufoCatcherMod * arcSlant;
                // for normal cases, should not be past textStart even if narrow
                if (!ufoCatcher && cornerx > arrowStart - 1) { cornerx = arrowStart - 1; }
                if (smoothArcCurves) {
                  var controlx = ufoCatcher ? cornerx + 2*ufoCatcherMod*reverseArcControlx : smoothArcSteepness*from+(1-smoothArcSteepness)*cornerx;
                  var endy = leftBox.y + (leftToRight || arc.equiv ? leftBox.height / 2 : Configuration.visual.margin.y);
                  // no curving for short lines covering short vertical
                  // distances, the arrowheads can go off (#925)
                  if (Math.abs(-height-endy) < 2 &&
                      Math.abs(cornerx-from) < 5) {
                    endy = -height;
                  }
                  line = path.line(cornerx, -height).
                      curveQ(controlx, -height, from, endy);
                } else {
                  path.line(cornerx, -height).
                      line(from, leftBox.y + (leftToRight || arc.equiv ? leftBox.height / 2 : Configuration.visual.margin.y));
                }
              } else {
                path.line(from, -height);
              }
              svg.path(arcGroup, path, {
                  markerEnd: arrowDecl,
                  markerStart: labelArrowDecl,
                  style: 'stroke: ' + color,
                  'strokeDashArray': dashArray,
              });
              if (arc.marked) {
                svg.path(shadowGroup, path, {
                    'class': 'shadow_EditHighlight_arc',
                    strokeWidth: markedArcStroke,
                    'strokeDashArray': dashArray,
                });
                svg.other(markedRect, 'animate', {
                  'data-type': arc.marked,
                  attributeName: 'fill',
                  values: (arc.marked == 'match' ? highlightMatchSequence
                           : highlightArcSequence),
                  dur: highlightDuration,
                  repeatCount: 'indefinite',
                  begin: 'indefinite'
                });
              }
              if (arc.shadowClass) {
                svg.path(shadowGroup, path, {
                    'class': 'shadow_' + arc.shadowClass,
                    strokeWidth: shadowStroke,
                    'strokeDashArray': dashArray,
                });
              }
              if (!symmetric) {
                myArrowHead = ((arcDesc && arcDesc.arrowHead) ||
                               (spanTypes.ARC_DEFAULT && spanTypes.ARC_DEFAULT.arrowHead));
                arrowName = (leftToRight ?
                    myArrowHead || 'triangle,5' :
                    'none') + ',' + color;
              }
              var arrowType = arrows[arrowName];
              var arrowDecl = arrowType && ('url(#' + arrowType + ')');

              var arrowAtLabelAdjust = 0;
              var labelArrowDecl = null;
              var myLabelArrowHead = ((arcDesc && arcDesc.labelArrow) ||
                                      (spanTypes.ARC_DEFAULT && spanTypes.ARC_DEFAULT.labelArrow));
              if (myLabelArrowHead) {
                var labelArrowName = (leftToRight ?
                    myLabelArrowHead || 'triangle,5' :
                    symmetric && myLabelArrowHead || 'none') + ',' + color;
                var labelArrowSplit = labelArrowName.split(',');
                arrowAtLabelAdjust = labelArrowSplit[0] != 'none' && parseInt(labelArrowSplit[1], 10) || 0;
                var labelArrowType = arrows[labelArrowName];
                var labelArrowDecl = labelArrowType && ('url(#' + labelArrowType + ')');
                if (ufoCatcher) arrowAtLabelAdjust = -arrowAtLabelAdjust;
              }
              var arrowEnd = textEnd + arrowAtLabelAdjust;
              path = svg.createPath().move(arrowEnd, -height);
              if (rowIndex == rightRow) {
                var cornerx  = to - ufoCatcherMod * arcSlant;
                // TODO: duplicates above in part, make funcs
                // for normal cases, should not be past textEnd even if narrow
                if (!ufoCatcher && cornerx < arrowEnd + 1) { cornerx = arrowEnd + 1; }
                if (smoothArcCurves) {
                  var controlx = ufoCatcher ? cornerx - 2*ufoCatcherMod*reverseArcControlx : smoothArcSteepness*to+(1-smoothArcSteepness)*cornerx;
                  var endy = rightBox.y + (leftToRight && !arc.equiv ? Configuration.visual.margin.y : rightBox.height / 2);
                  // no curving for short lines covering short vertical
                  // distances, the arrowheads can go off (#925)
                  if (Math.abs(-height-endy) < 2 &&
                      Math.abs(cornerx-to) < 5) {
                    endy = -height;
                  }
                  path.line(cornerx, -height).
                      curveQ(controlx, -height, to, endy);
                } else {
                  path.line(cornerx, -height).
                      line(to, rightBox.y + (leftToRight && !arc.equiv ? Configuration.visual.margin.y : rightBox.height / 2));
                }
              } else {
                path.line(to, -height);
              }
              svg.path(arcGroup, path, {
                  markerEnd: arrowDecl,
                  markerStart: labelArrowDecl,
                  style: 'stroke: ' + color,
                  'strokeDashArray': dashArray,
              });
              if (arc.marked) {
                svg.path(shadowGroup, path, {
                    'class': 'shadow_EditHighlight_arc',
                    strokeWidth: markedArcStroke,
                    'strokeDashArray': dashArray,
                });
              }
              if (shadowGroup) {
                svg.path(shadowGroup, path, {
                    'class': 'shadow_' + arc.shadowClass,
                    strokeWidth: shadowStroke,
                    'strokeDashArray': dashArray,
                });
              }
            }
          } // arc rows
        }); // arcs

Util.profileEnd('arcs');
Util.profileStart('fragmentConnectors');

        $.each(data.spans, function(spanNo, span) {
          var numConnectors = span.fragments.length - 1;
          for (var connectorNo = 0; connectorNo < numConnectors; connectorNo++) {
            var left = span.fragments[connectorNo];
            var right = span.fragments[connectorNo + 1];

            var leftBox = rowBBox(left);
            var rightBox = rowBBox(right);
            var leftRow = left.chunk.row.index;
            var rightRow = right.chunk.row.index;

            for (var rowIndex = leftRow; rowIndex <= rightRow; rowIndex++) {
              var row = rows[rowIndex];
              if (row.chunks.length) {
                row.hasAnnotations = true;

                if (rowIndex == leftRow) {
                  from = leftBox.x + leftBox.width;
                } else {
                  from = sentNumMargin;
                }

                if (rowIndex == rightRow) {
                  to = rightBox.x;
                } else {
                  to = canvasWidth - 2 * Configuration.visual.margin.y;
                }

                var height = leftBox.y + leftBox.height - Configuration.visual.margin.y;
                if (roundCoordinates) {
                  // don't ask
                  height = (height|0)+0.5;
                }

                var path = svg.createPath().move(from, height).line(to, height);
                svg.path(row.arcs, path, {
                  style: 'stroke: ' + fragmentConnectorColor,
                  'strokeDashArray': fragmentConnectorDashArray
                });
              }
            } // rowIndex
          } // connectorNo
        }); // spans

Util.profileEnd('fragmentConnectors');
Util.profileStart('rows');

        // position the rows
        var y = Configuration.visual.margin.y;
        var sentNumGroup = svg.group({'class': 'sentnum'});
        var currentSent;
        $.each(rows, function(rowId, row) {
          // find the maximum fragment height
          $.each(row.chunks, function(chunkId, chunk) {
            $.each(chunk.fragments, function(fragmentId, fragment) {
              if (row.maxSpanHeight < fragment.height) row.maxSpanHeight = fragment.height;
            });
          });
          if (row.sentence) {
            currentSent = row.sentence;
          }
          // SLOW (#724) and replaced with calculations:
          //
          // var rowBox = row.group.getBBox();
          // // Make it work on IE
          // rowBox = { x: rowBox.x, y: rowBox.y, height: rowBox.height, width: rowBox.width };
          // // Make it work on Firefox and Opera
          // if (rowBox.height == -Infinity) {
          //   rowBox = { x: 0, y: 0, height: 0, width: 0 };
          // }

          // XXX TODO HACK: find out where 5 and 1.5 come from!
          // This is the fix for #724, but the numbers are guessed.
          var rowBoxHeight = Math.max(row.maxArcHeight + 5, row.maxSpanHeight + 1.5); // XXX TODO HACK: why 5, 1.5?
          if (row.hasAnnotations) {
            // rowBox.height = -rowBox.y + rowSpacing;
            rowBoxHeight += rowSpacing + 1.5; // XXX TODO HACK: why 1.5?
          } else {
            rowBoxHeight -= 5; // XXX TODO HACK: why -5?
          }

          rowBoxHeight += rowPadding;
          var bgClass;
          if (data.markedSent[currentSent]) {
            // specifically highlighted
            bgClass = 'backgroundHighlight';
          } else if (Configuration.textBackgrounds == "striped") {
            // give every other sentence a different bg class
            bgClass = 'background'+ row.backgroundIndex;
          } else {
            // plain "standard" bg
            bgClass = 'background0';
          }
          svg.rect(backgroundGroup,
            0, y + sizes.texts.y + sizes.texts.height,
            canvasWidth, rowBoxHeight + sizes.texts.height + 1, {
            'class': bgClass,
          });
          y += rowBoxHeight;
          y += sizes.texts.height;
          row.textY = y - rowPadding;
          if (row.sentence) {
            var sentence_hash = new URLHash(coll, doc, { focus: [[ 'sent', row.sentence ]] } );
            var link = svg.link(sentNumGroup, sentence_hash.getHash());
            var text = svg.text(link, sentNumMargin - Configuration.visual.margin.x, y - rowPadding,
                '' + row.sentence, { 'data-sent': row.sentence });
            var sentComment = data.sentComment[row.sentence];
            if (sentComment) {
              var box = text.getBBox();
              svg.remove(text);
              // TODO: using rectShadowSize, but this shadow should
              // probably have its own setting for shadow size
              shadowRect = svg.rect(sentNumGroup,
                  box.x - rectShadowSize, box.y - rectShadowSize,
                  box.width + 2 * rectShadowSize, box.height + 2 * rectShadowSize, {

                  'class': 'shadow_' + sentComment.type,
                  filter: 'url(#Gaussian_Blur)',
                  rx: rectShadowRounding,
                  ry: rectShadowRounding,
                  'data-sent': row.sentence,
              });
              var text = svg.text(sentNumGroup, sentNumMargin - Configuration.visual.margin.x, y - rowPadding,
                  '' + row.sentence, { 'data-sent': row.sentence });
            }
          }

          var rowY = y - rowPadding;
          if (roundCoordinates) {
            rowY = rowY|0;
          }
          translate(row, 0, rowY);
          y += Configuration.visual.margin.y;
        });
        y += Configuration.visual.margin.y;

Util.profileEnd('rows');
Util.profileStart('chunkFinish');

        // chunk index sort functions for overlapping fragment drawing
        // algorithm; first for left-to-right pass, sorting primarily
        // by start offset, second for right-to-left pass by end
        // offset. Secondary sort by fragment length in both cases.
        var currentChunk;
        var lrChunkComp = function(a,b) {
          var ac = currentChunk.fragments[a];
          var bc = currentChunk.fragments[b]
          var startDiff = Util.cmp(ac.from, bc.from);
          return startDiff != 0 ? startDiff : Util.cmp(bc.to-bc.from, ac.to-ac.from);
        }
        var rlChunkComp = function(a,b) {
          var ac = currentChunk.fragments[a];
          var bc = currentChunk.fragments[b]
          var endDiff = Util.cmp(bc.to, ac.to);
          return endDiff != 0 ? endDiff : Util.cmp(bc.to-bc.from, ac.to-ac.from);
        }

        var sentenceText = null;
        $.each(data.chunks, function(chunkNo, chunk) {
          // context for sort
          currentChunk = chunk;

          // text rendering
          if (chunk.sentence) {
            if (sentenceText) {
              // svg.text(textGroup, sentenceText); // avoids jQuerySVG bug
              svg.text(textGroup, 0, 0, sentenceText);
            }
            sentenceText = null;
          }
          if (!sentenceText) {
            sentenceText = svg.createText();
          }
          var nextChunk = data.chunks[chunkNo + 1];
          var nextSpace = nextChunk ? nextChunk.space : '';
          sentenceText.span(chunk.text + nextSpace, {
            x: chunk.textX,
            y: chunk.row.textY,
            'data-chunk-id': chunk.index
          });

          // chunk backgrounds
          if (chunk.fragments.length) {
            var orderedIdx = [];
            for (var i=chunk.fragments.length-1; i>=0; i--) {
              orderedIdx.push(i);
            }

            // Mark entity nesting height/depth (number of
            // nested/nesting entities). To account for crossing
            // brackets in a (mostly) reasonable way, determine
            // depth/height separately in a left-to-right traversal
            // and a right-to-left traversal.
            orderedIdx.sort(lrChunkComp);

            var openFragments = [];
            for(var i=0; i<orderedIdx.length; i++) {
              var current = chunk.fragments[orderedIdx[i]];
              current.nestingHeightLR = 0;
              current.nestingDepthLR = 0;
              var stillOpen = [];
              for(var o=0; o<openFragments.length; o++) {
                if(openFragments[o].to > current.from) {
                  stillOpen.push(openFragments[o]);
                  openFragments[o].nestingHeightLR++;
                }
              }
              openFragments = stillOpen;
              current.nestingDepthLR=openFragments.length;
              openFragments.push(current);
            }

            // re-sort for right-to-left traversal by end position
            orderedIdx.sort(rlChunkComp);

            openFragments = [];
            for(var i=0; i<orderedIdx.length; i++) {
              var current = chunk.fragments[orderedIdx[i]];
              current.nestingHeightRL = 0;
              current.nestingDepthRL = 0;
              var stillOpen = [];
              for(var o=0; o<openFragments.length; o++) {
                if(openFragments[o].from < current.to) {
                  stillOpen.push(openFragments[o]);
                  openFragments[o].nestingHeightRL++;
                }
              }
              openFragments = stillOpen;
              current.nestingDepthRL=openFragments.length;
              openFragments.push(current);
            }

            // the effective depth and height are the max of those
            // for the left-to-right and right-to-left traversals.
            for(var i=0; i<orderedIdx.length; i++) {
              var c = chunk.fragments[orderedIdx[i]];
              c.nestingHeight = c.nestingHeightLR > c.nestingHeightRL ? c.nestingHeightLR : c.nestingHeightRL;
              c.nestingDepth = c.nestingDepthLR > c.nestingDepthRL ? c.nestingDepthLR : c.nestingDepthRL;
            }

            // Re-order by nesting height and draw in order
            orderedIdx.sort(function(a,b) { return Util.cmp(chunk.fragments[b].nestingHeight, chunk.fragments[a].nestingHeight) });

            for(var i=0; i<chunk.fragments.length; i++) {
              var fragment = chunk.fragments[orderedIdx[i]];
              var spanDesc = spanTypes[fragment.span.type];
              var bgColor = ((spanDesc && spanDesc.bgColor) ||
                             (spanTypes.SPAN_DEFAULT && spanTypes.SPAN_DEFAULT.bgColor) ||
                             '#ffffff');
              if (fragment.span.hidden) continue;

              // Tweak for nesting depth/height. Recognize just three
              // levels for now: normal, nested, and nesting, where
              // nested+nesting yields normal. (Currently testing
              // minor tweak: don't shrink for depth 1 as the nesting
              // highlight will grow anyway [check nestingDepth > 1])
              var shrink = 0;
              if(fragment.nestingDepth > 1 && fragment.nestingHeight == 0) {
                  shrink = 1;
              } else if(fragment.nestingDepth == 0 && fragment.nestingHeight > 0) {
                  shrink = -1;
              }
              var yShrink = shrink * nestingAdjustYStepSize;
              var xShrink = shrink * nestingAdjustXStepSize;
              // bit lighter
              var lightBgColor = Util.adjustColorLightness(bgColor, 0.8);
              // tweak for Y start offset (and corresponding height
              // reduction): text rarely hits font max height, so this
              // tends to look better
              var yStartTweak = 1;
              // store to have same mouseover highlight without recalc
              fragment.highlightPos = {
                  x: chunk.textX + fragment.curly.from + xShrink,
                  y: chunk.row.textY + sizes.texts.y + yShrink + yStartTweak,
                  w: fragment.curly.to - fragment.curly.from - 2*xShrink,
                  h: sizes.texts.height - 2*yShrink - yStartTweak,
              };
              svg.rect(highlightGroup,
                  fragment.highlightPos.x, fragment.highlightPos.y,
                  fragment.highlightPos.w, fragment.highlightPos.h,
                  { fill: lightBgColor, //opacity:1,
                    rx: highlightRounding.x,
                    ry: highlightRounding.y,
                  });
            }
          }
        });
        if (sentenceText) {
          // svg.text(textGroup, sentenceText); // avoids jQuerySVG bug
          svg.text(textGroup, 0, 0, sentenceText);
        }

        // draw the markedText
        $.each(textMarkedRows, function(textRowNo, textRowDesc) { // row, from, to
          var textHighlight = svg.rect(highlightGroup,
              textRowDesc[1] - 2, textRowDesc[0].textY - sizes.fragments.height,
              textRowDesc[2] - textRowDesc[1] + 4, sizes.fragments.height + 4,
              { fill: 'yellow' } // TODO: put into css file, as default - turn into class
          );
          // NOTE: changing highlightTextSequence here will give
          // different-colored highlights
          // TODO: entirely different settings for non-animations?
          var markedType = textRowDesc[3];
          svg.other(textHighlight, 'animate', {
            'data-type': markedType,
            attributeName: 'fill',
            values: (markedType == 'match' ? highlightMatchSequence
                     : highlightTextSequence),
            dur: highlightDuration,
            repeatCount: 'indefinite',
            begin: 'indefinite'
          });
        });


Util.profileEnd('chunkFinish');
Util.profileStart('finish');

        svg.path(sentNumGroup, svg.createPath().
          move(sentNumMargin, 0).
          line(sentNumMargin, y));

        // resize the SVG
        var width = maxTextWidth + sentNumMargin + 2 * Configuration.visual.margin.x + 1;
        if (width > canvasWidth) canvasWidth = width;

        $svg.width(canvasWidth);
        $svg.height(y);
        $svgDiv.height(y);

Util.profileEnd('finish');
Util.profileEnd('render');
Util.profileReport();


        drawing = false;
        if (redraw) {
          redraw = false;
          renderDataReal();
        }
        $svg.find('animate').each(function() {
          if (this.beginElement) { // protect against non-SMIL browsers
            this.beginElement();
          }
        });
        dispatcher.post('doneRendering', [coll, doc, args]);
      };

      var renderErrors = {
        unableToReadTextFile: true,
        annotationFileNotFound: true,
        isDirectoryError: true
      };
      var renderData = function(sourceData) {
        Util.profileEnd('invoke getDocument');
        if (sourceData && sourceData.exception) {
          if (renderErrors[sourceData.exception]) {
            dispatcher.post('renderError:' + sourceData.exception, [sourceData]);
          } else {
            dispatcher.post('unknownError', [sourceData.exception]);
          }
        } else {
          // Fill in default values that don't necessarily go over the protocol
          if (sourceData) {
            setSourceDataDefaults(sourceData);
          }

          dispatcher.post('startedRendering', [coll, doc, args]);
          dispatcher.post('spin');
          setTimeout(function() {
              try {
                renderDataReal(sourceData);
              } catch (e) {
                // We are sure not to be drawing anymore, reset the state
                drawing = false;
                // TODO: Hook printout into dispatch elsewhere?
                console.warn('Rendering terminated due to: ' + e, e.stack);
                dispatcher.post('renderError: Fatal', [sourceData, e]);
              }
              dispatcher.post('unspin');
          }, 0);
        }
      };

      var renderDocument = function() {
        Util.profileStart('invoke getDocument');
        dispatcher.post('ajax', [{
            action: 'getDocument',
            collection: coll,
            'document': doc,
          }, 'renderData', {
            collection: coll,
            'document': doc
          }]);
      };

      var triggerRender = function() {
        if (svg && ((isRenderRequested && isCollectionLoaded) || requestedData) && Util.areFontsLoaded()) {
          isRenderRequested = false;
          if (requestedData) {

Util.profileClear();
Util.profileStart('before render');

            renderData(requestedData);
          } else if (doc.length) {

Util.profileClear();
Util.profileStart('before render');

            renderDocument();
          } else {
            dispatcher.post(0, 'renderError:noFileSpecified');
          }
        }
      };

      var requestRenderData = function(sourceData) {
        requestedData = sourceData;
        triggerRender();
      };

      var collectionChanged = function() {
        isCollectionLoaded = false;
      };

      var gotCurrent = function(_coll, _doc, _args, reloadData) {
        coll = _coll;
        doc  = _doc;
        args = _args;
        if (reloadData) {
          isRenderRequested = true;
          triggerRender();
        }
      };


      // event handlers

      var highlight, highlightArcs, highlightSpans, commentId;

      var onMouseOver = function(evt) {
        var target = $(evt.target);
        var id;
        if (id = target.attr('data-span-id')) {
          commentId = id;
          var span = data.spans[id];
          dispatcher.post('displaySpanComment', [
              evt, target, id, span.type, span.attributeText,
              span.text,
              span.comment && span.comment.text,
              span.comment && span.comment.type,
              span.normalizations]);

          var spanDesc = spanTypes[span.type];
          var bgColor = ((spanDesc && spanDesc.bgColor) ||
                         (spanTypes.SPAN_DEFAULT && spanTypes.SPAN_DEFAULT.bgColor) ||
                         '#ffffff');
          if (span.hidden) return;
          highlight = [];
          $.each(span.fragments, function(fragmentNo, fragment) {
            highlight.push(svg.rect(highlightGroup,
                                 fragment.highlightPos.x, fragment.highlightPos.y,
                                 fragment.highlightPos.w, fragment.highlightPos.h,
                                 { 'fill': bgColor, opacity:0.75,
                                   rx: highlightRounding.x,
                                   ry: highlightRounding.y,
                                 }));
          });

          if (that.arcDragOrigin) {
            target.parent().addClass('highlight');
          } else {
            var equivs = {};
            var spans = {};
            spans[id] = true;
            var spanIds = [];
            // find all arcs, normal and equiv. Equivs need to go far (#1023)
            var addArcAndSpan = function(arc, span) {
              if (arc.equiv) {
                equivs[arc.eventDescId.substr(0, arc.eventDescId.indexOf('*', 2) + 1)] = true;
                var eventDesc = data.eventDescs[arc.eventDescId];
                $.each(eventDesc.leftSpans.concat(eventDesc.rightSpans), function(ospanId, ospan) {
                  spans[ospan] = true;
                });
              } else {
                spans[arc.origin] = true;
              }
            }
            $.each(span.incoming, function(arcNo, arc) {
                addArcAndSpan(arc, arc.origin);
            });
            $.each(span.outgoing, function(arcNo, arc) {
                addArcAndSpan(arc, arc.target);
            });
            var equivSelector = [];
            $.each(equivs, function(equiv, dummy) {
              equivSelector.push('[data-arc-ed^="' + equiv + '"]');
            });

            highlightArcs = $svg.
                find(equivSelector.join(', ')).
                parent().
                add('g[data-from="' + id + '"], g[data-to="' + id + '"]' + equivSelector).
                addClass('highlight');

            $.each(spans, function(spanId, dummy) {
                spanIds.push('rect[data-span-id="' + spanId + '"]');
            });
            highlightSpans = $svg.
                find(spanIds.join(', ')).
                parent().
                addClass('highlight');
          }
          forceRedraw();
        } else if (!that.arcDragOrigin && (id = target.attr('data-arc-role'))) {
          var originSpanId = target.attr('data-arc-origin');
          var targetSpanId = target.attr('data-arc-target');
          var role = target.attr('data-arc-role');
          var symmetric = (relationTypesHash &&
                           relationTypesHash[role] &&
                           relationTypesHash[role].properties &&
                           relationTypesHash[role].properties.symmetric);
          // NOTE: no commentText, commentType for now
          var arcEventDescId = target.attr('data-arc-ed');
          var commentText = '';
          var commentType = '';
          var arcId;
          if (arcEventDescId) {
            var eventDesc = data.eventDescs[arcEventDescId];
            var comment = eventDesc.comment;
            if (comment) {
              commentText = comment.text;
              commentType = comment.type;
              if (commentText == '' && commentType) {
                  // default to type if missing text
                  commentText = commentType;
              }
            }
            if (eventDesc.relation) {
              // among arcs, only ones corresponding to relations have
              // "independent" IDs
              arcId = arcEventDescId;
            }
          }
          var originSpanType = data.spans[originSpanId].type || '';
          var targetSpanType = data.spans[targetSpanId].type || '';
          dispatcher.post('displayArcComment', [
              evt, target, symmetric, arcId,
              originSpanId, originSpanType, role,
              targetSpanId, targetSpanType,
              commentText, commentType]);
          highlightArcs = $svg.
              find('g[data-from="' + originSpanId + '"][data-to="' + targetSpanId + '"]').
              addClass('highlight');
          highlightSpans = $($svg).
              find('rect[data-span-id="' + originSpanId + '"], rect[data-span-id="' + targetSpanId + '"]').
              parent().
              addClass('highlight');
        } else if (id = target.attr('data-sent')) {
          var comment = data.sentComment[id];
          if (comment) {
            dispatcher.post('displaySentComment', [evt, target, comment.text, comment.type]);
          }
        }
      };

      var onMouseOut = function(evt) {
        var target = $(evt.target);
        target.removeClass('badTarget');
        dispatcher.post('hideComment');
        if (highlight) {
          $.each(highlight, function() {
            svg.remove(this);
          });
          highlight = undefined;
        }
        if (highlightSpans) {
          highlightArcs.removeClass('highlight');
          highlightSpans.removeClass('highlight');
          highlightSpans = undefined;
        }
        forceRedraw();
      };

      var setAbbrevs = function(_abbrevsOn) {
        // TODO: this is a slightly weird place to tweak the configuration
        Configuration.abbrevsOn = _abbrevsOn;
        dispatcher.post('configurationChanged');
      }

      var setTextBackgrounds = function(_textBackgrounds) {
        Configuration.textBackgrounds = _textBackgrounds;
        dispatcher.post('configurationChanged');
      }

      var setLayoutDensity = function(_density) {
        //dispatcher.post('messages', [[['Setting layout density ' + _density, 'comment']]]);
        // TODO: store standard settings instead of hard-coding
        // them here (again)
        if (_density < 2) {
          // dense
          Configuration.visual.margin = { x: 1, y: 0 };
          Configuration.visual.boxSpacing = 1;
          Configuration.visual.curlyHeight = 1;
          Configuration.visual.arcSpacing = 7;
          Configuration.visual.arcStartHeight = 18
        } else if(_density > 2) {
          // spacious
          Configuration.visual.margin = { x: 2, y: 1 };
          Configuration.visual.boxSpacing = 3;
          Configuration.visual.curlyHeight = 6;
          Configuration.visual.arcSpacing = 12;
          Configuration.visual.arcStartHeight = 23;
        } else {
          // standard
          Configuration.visual.margin = { x: 2, y: 1 };
          Configuration.visual.boxSpacing = 1;
          Configuration.visual.curlyHeight = 4;
          Configuration.visual.arcSpacing = 9;
          Configuration.visual.arcStartHeight = 19;
        }
        dispatcher.post('configurationChanged');
      }

      var setSvgWidth = function(_width) {
        $svgDiv.width(_width);
        if (Configuration.svgWidth != _width) {
          Configuration.svgWidth = _width;
          dispatcher.post('configurationChanged');
        }
      }

      $svgDiv = $($svgDiv).hide();

      // register event listeners
      var registerHandlers = function(element, events) {
        $.each(events, function(eventNo, eventName) {
            element.bind(eventName,
              function(evt) {
                dispatcher.post(eventName, [evt], 'all');
              }
            );
        });
      };
      registerHandlers($svgDiv, [
          'mouseover', 'mouseout', 'mousemove',
          'mouseup', 'mousedown',
          'dragstart',
          'dblclick', 'click'
      ]);
      registerHandlers($(document), [
          'keydown', 'keypress',
          'touchstart', 'touchend'
      ]);
      registerHandlers($(window), [
          'resize'
      ]);

      // create the svg wrapper
      $svgDiv.svg({
          onLoad: function(_svg) {
              that.svg = svg = _svg;
              $svg = $(svg._svg);

              /* XXX HACK REMOVED - not efficient?

              // XXX HACK to allow off-DOM SVG element creation
              // we need to replace the jQuery SVG's _makeNode function
              // with a modified one.
              // Be aware of potential breakage upon jQuery SVG upgrade.
              svg._makeNode = function(parent, name, settings) {
                  // COMMENTED OUT: parent = parent || this._svg;
                  var node = this._svg.ownerDocument.createElementNS($.svg.svgNS, name);
                  for (var name in settings) {
                    var value = settings[name];
                    if (value != null && value != null &&
                        (typeof value != 'string' || value != '')) {
                      node.setAttribute($.svg._attrNames[name] || name, value);
                    }
                  }
                  // ADDED IN:
                  if (parent)
                    parent.appendChild(node);
                  return node;
                };
              */

              triggerRender();
          }
      });

      var loadSpanTypes = function(types) {
        $.each(types, function(typeNo, type) {
          if (type) {
            spanTypes[type.type] = type;
            var children = type.children;
            if (children && children.length) {
              loadSpanTypes(children);
            }
          }
        });
      }

      var loadAttributeTypes = function(response_types) {
        var processed = {};
        $.each(response_types, function(aTypeNo, aType) {
          processed[aType.type] = aType;
          // count the values; if only one, it's a boolean attribute
          var values = [];
          for (var i in aType.values) {
            if (aType.values.hasOwnProperty(i)) {
              values.push(i);
            }
          }
          if (values.length == 1) {
            aType.bool = values[0];
          }
        });
        return processed;
      }

      var loadRelationTypes = function(relation_types) {
        $.each(relation_types, function(relTypeNo, relType) {
          if (relType) {
            relationTypesHash[relType.type] = relType;
            var children = relType.children;
            if (children && children.length) {
              loadRelationTypes(children);
            }
          }
        });
      }

      var collectionLoaded = function(response) {
        if (!response.exception) {
          setCollectionDefaults(response);
          eventAttributeTypes = loadAttributeTypes(response.event_attribute_types);
          entityAttributeTypes = loadAttributeTypes(response.entity_attribute_types);
          spanTypes = {};
          loadSpanTypes(response.entity_types);
          loadSpanTypes(response.event_types);
          loadSpanTypes(response.unconfigured_types);
          relationTypesHash = {};
          loadRelationTypes(response.relation_types);
          loadRelationTypes(response.unconfigured_types);
          // TODO XXX: isn't the following completely redundant with
          // loadRelationTypes?
          $.each(response.relation_types, function(relTypeNo, relType) {
            relationTypesHash[relType.type] = relType;
          });
          var arcBundle = (response.visual_options || {}).arc_bundle || 'none';
          collapseArcs = arcBundle == "all";
          collapseArcSpace = arcBundle != "none";

          dispatcher.post('spanAndAttributeTypesLoaded', [spanTypes, entityAttributeTypes, eventAttributeTypes, relationTypesHash]);

          isCollectionLoaded = true;
          triggerRender();
        } else {
          // exception on collection load; allow visualizer_ui
          // collectionLoaded to handle this
        }
      };

      var isReloadOkay = function() {
        // do not reload while the user is in the dialog
        return !drawing;
      };

      Util.loadFonts(webFontURLs, dispatcher);

      dispatcher.
          on('collectionChanged', collectionChanged).
          on('collectionLoaded', collectionLoaded).
          on('renderData', renderData).
          on('triggerRender', triggerRender).
          on('requestRenderData', requestRenderData).
          on('isReloadOkay', isReloadOkay).
          on('resetData', resetData).
          on('abbrevs', setAbbrevs).
          on('textBackgrounds', setTextBackgrounds).
          on('layoutDensity', setLayoutDensity).
          on('svgWidth', setSvgWidth).
          on('current', gotCurrent).
          on('clearSVG', clearSVG).
          on('mouseover', onMouseOver).
          on('mouseout', onMouseOut);
    };

    return Visualizer;
})(jQuery, window);
// Generated by CoffeeScript 1.6.2
/*!
jQuery Waypoints - v2.0.5
Copyright (c) 2011-2014 Caleb Troughton
Licensed under the MIT license.
https://github.com/imakewebthings/jquery-waypoints/blob/master/licenses.txt
*/
(function(){var t=[].indexOf||function(t){for(var e=0,n=this.length;e<n;e++){if(e in this&&this[e]===t)return e}return-1},e=[].slice;(function(t,e){if(typeof define==="function"&&define.amd){return define("waypoints",["jquery"],function(n){return e(n,t)})}else{return e(t.jQuery,t)}})(window,function(n,r){var i,o,l,s,f,u,c,a,h,d,p,y,v,w,g,m;i=n(r);a=t.call(r,"ontouchstart")>=0;s={horizontal:{},vertical:{}};f=1;c={};u="waypoints-context-id";p="resize.waypoints";y="scroll.waypoints";v=1;w="waypoints-waypoint-ids";g="waypoint";m="waypoints";o=function(){function t(t){var e=this;this.$element=t;this.element=t[0];this.didResize=false;this.didScroll=false;this.id="context"+f++;this.oldScroll={x:t.scrollLeft(),y:t.scrollTop()};this.waypoints={horizontal:{},vertical:{}};this.element[u]=this.id;c[this.id]=this;t.bind(y,function(){var t;if(!(e.didScroll||a)){e.didScroll=true;t=function(){e.doScroll();return e.didScroll=false};return r.setTimeout(t,n[m].settings.scrollThrottle)}});t.bind(p,function(){var t;if(!e.didResize){e.didResize=true;t=function(){n[m]("refresh");return e.didResize=false};return r.setTimeout(t,n[m].settings.resizeThrottle)}})}t.prototype.doScroll=function(){var t,e=this;t={horizontal:{newScroll:this.$element.scrollLeft(),oldScroll:this.oldScroll.x,forward:"right",backward:"left"},vertical:{newScroll:this.$element.scrollTop(),oldScroll:this.oldScroll.y,forward:"down",backward:"up"}};if(a&&(!t.vertical.oldScroll||!t.vertical.newScroll)){n[m]("refresh")}n.each(t,function(t,r){var i,o,l;l=[];o=r.newScroll>r.oldScroll;i=o?r.forward:r.backward;n.each(e.waypoints[t],function(t,e){var n,i;if(r.oldScroll<(n=e.offset)&&n<=r.newScroll){return l.push(e)}else if(r.newScroll<(i=e.offset)&&i<=r.oldScroll){return l.push(e)}});l.sort(function(t,e){return t.offset-e.offset});if(!o){l.reverse()}return n.each(l,function(t,e){if(e.options.continuous||t===l.length-1){return e.trigger([i])}})});return this.oldScroll={x:t.horizontal.newScroll,y:t.vertical.newScroll}};t.prototype.refresh=function(){var t,e,r,i=this;r=n.isWindow(this.element);e=this.$element.offset();this.doScroll();t={horizontal:{contextOffset:r?0:e.left,contextScroll:r?0:this.oldScroll.x,contextDimension:this.$element.width(),oldScroll:this.oldScroll.x,forward:"right",backward:"left",offsetProp:"left"},vertical:{contextOffset:r?0:e.top,contextScroll:r?0:this.oldScroll.y,contextDimension:r?n[m]("viewportHeight"):this.$element.height(),oldScroll:this.oldScroll.y,forward:"down",backward:"up",offsetProp:"top"}};return n.each(t,function(t,e){return n.each(i.waypoints[t],function(t,r){var i,o,l,s,f;i=r.options.offset;l=r.offset;o=n.isWindow(r.element)?0:r.$element.offset()[e.offsetProp];if(n.isFunction(i)){i=i.apply(r.element)}else if(typeof i==="string"){i=parseFloat(i);if(r.options.offset.indexOf("%")>-1){i=Math.ceil(e.contextDimension*i/100)}}r.offset=o-e.contextOffset+e.contextScroll-i;if(r.options.onlyOnScroll&&l!=null||!r.enabled){return}if(l!==null&&l<(s=e.oldScroll)&&s<=r.offset){return r.trigger([e.backward])}else if(l!==null&&l>(f=e.oldScroll)&&f>=r.offset){return r.trigger([e.forward])}else if(l===null&&e.oldScroll>=r.offset){return r.trigger([e.forward])}})})};t.prototype.checkEmpty=function(){if(n.isEmptyObject(this.waypoints.horizontal)&&n.isEmptyObject(this.waypoints.vertical)){this.$element.unbind([p,y].join(" "));return delete c[this.id]}};return t}();l=function(){function t(t,e,r){var i,o;if(r.offset==="bottom-in-view"){r.offset=function(){var t;t=n[m]("viewportHeight");if(!n.isWindow(e.element)){t=e.$element.height()}return t-n(this).outerHeight()}}this.$element=t;this.element=t[0];this.axis=r.horizontal?"horizontal":"vertical";this.callback=r.handler;this.context=e;this.enabled=r.enabled;this.id="waypoints"+v++;this.offset=null;this.options=r;e.waypoints[this.axis][this.id]=this;s[this.axis][this.id]=this;i=(o=this.element[w])!=null?o:[];i.push(this.id);this.element[w]=i}t.prototype.trigger=function(t){if(!this.enabled){return}if(this.callback!=null){this.callback.apply(this.element,t)}if(this.options.triggerOnce){return this.destroy()}};t.prototype.disable=function(){return this.enabled=false};t.prototype.enable=function(){this.context.refresh();return this.enabled=true};t.prototype.destroy=function(){delete s[this.axis][this.id];delete this.context.waypoints[this.axis][this.id];return this.context.checkEmpty()};t.getWaypointsByElement=function(t){var e,r;r=t[w];if(!r){return[]}e=n.extend({},s.horizontal,s.vertical);return n.map(r,function(t){return e[t]})};return t}();d={init:function(t,e){var r;e=n.extend({},n.fn[g].defaults,e);if((r=e.handler)==null){e.handler=t}this.each(function(){var t,r,i,s;t=n(this);i=(s=e.context)!=null?s:n.fn[g].defaults.context;if(!n.isWindow(i)){i=t.closest(i)}i=n(i);r=c[i[0][u]];if(!r){r=new o(i)}return new l(t,r,e)});n[m]("refresh");return this},disable:function(){return d._invoke.call(this,"disable")},enable:function(){return d._invoke.call(this,"enable")},destroy:function(){return d._invoke.call(this,"destroy")},prev:function(t,e){return d._traverse.call(this,t,e,function(t,e,n){if(e>0){return t.push(n[e-1])}})},next:function(t,e){return d._traverse.call(this,t,e,function(t,e,n){if(e<n.length-1){return t.push(n[e+1])}})},_traverse:function(t,e,i){var o,l;if(t==null){t="vertical"}if(e==null){e=r}l=h.aggregate(e);o=[];this.each(function(){var e;e=n.inArray(this,l[t]);return i(o,e,l[t])});return this.pushStack(o)},_invoke:function(t){this.each(function(){var e;e=l.getWaypointsByElement(this);return n.each(e,function(e,n){n[t]();return true})});return this}};n.fn[g]=function(){var t,r;r=arguments[0],t=2<=arguments.length?e.call(arguments,1):[];if(d[r]){return d[r].apply(this,t)}else if(n.isFunction(r)){return d.init.apply(this,arguments)}else if(n.isPlainObject(r)){return d.init.apply(this,[null,r])}else if(!r){return n.error("jQuery Waypoints needs a callback function or handler option.")}else{return n.error("The "+r+" method does not exist in jQuery Waypoints.")}};n.fn[g].defaults={context:r,continuous:true,enabled:true,horizontal:false,offset:0,triggerOnce:false};h={refresh:function(){return n.each(c,function(t,e){return e.refresh()})},viewportHeight:function(){var t;return(t=r.innerHeight)!=null?t:i.height()},aggregate:function(t){var e,r,i;e=s;if(t){e=(i=c[n(t)[0][u]])!=null?i.waypoints:void 0}if(!e){return[]}r={horizontal:[],vertical:[]};n.each(r,function(t,i){n.each(e[t],function(t,e){return i.push(e)});i.sort(function(t,e){return t.offset-e.offset});r[t]=n.map(i,function(t){return t.element});return r[t]=n.unique(r[t])});return r},above:function(t){if(t==null){t=r}return h._filter(t,"vertical",function(t,e){return e.offset<=t.oldScroll.y})},below:function(t){if(t==null){t=r}return h._filter(t,"vertical",function(t,e){return e.offset>t.oldScroll.y})},left:function(t){if(t==null){t=r}return h._filter(t,"horizontal",function(t,e){return e.offset<=t.oldScroll.x})},right:function(t){if(t==null){t=r}return h._filter(t,"horizontal",function(t,e){return e.offset>t.oldScroll.x})},enable:function(){return h._invoke("enable")},disable:function(){return h._invoke("disable")},destroy:function(){return h._invoke("destroy")},extendFn:function(t,e){return d[t]=e},_invoke:function(t){var e;e=n.extend({},s.vertical,s.horizontal);return n.each(e,function(e,n){n[t]();return true})},_filter:function(t,e,r){var i,o;i=c[n(t)[0][u]];if(!i){return[]}o=[];n.each(i.waypoints[e],function(t,e){if(r(i,e)){return o.push(e)}});o.sort(function(t,e){return t.offset-e.offset});return n.map(o,function(t){return t.element})}};n[m]=function(){var t,n;n=arguments[0],t=2<=arguments.length?e.call(arguments,1):[];if(h[n]){return h[n].apply(null,t)}else{return h.aggregate.call(null,n)}};n[m].settings={resizeThrottle:100,scrollThrottle:30};return i.on("load.waypoints",function(){return n[m]("refresh")})})}).call(this);
// Generated by CoffeeScript 1.6.2
/*!
jQuery Waypoints - v2.0.5
Copyright (c) 2011-2014 Caleb Troughton
Licensed under the MIT license.
https://github.com/imakewebthings/jquery-waypoints/blob/master/licenses.txt
*/
(function(){var t=[].indexOf||function(t){for(var e=0,n=this.length;e<n;e++){if(e in this&&this[e]===t)return e}return-1},e=[].slice;(function(t,e){if(typeof define==="function"&&define.amd){return define("waypoints",["jquery"],function(n){return e(n,t)})}else{return e(t.jQuery,t)}})(window,function(n,r){var i,o,l,s,f,u,c,a,h,d,p,y,v,w,g,m;i=n(r);a=t.call(r,"ontouchstart")>=0;s={horizontal:{},vertical:{}};f=1;c={};u="waypoints-context-id";p="resize.waypoints";y="scroll.waypoints";v=1;w="waypoints-waypoint-ids";g="waypoint";m="waypoints";o=function(){function t(t){var e=this;this.$element=t;this.element=t[0];this.didResize=false;this.didScroll=false;this.id="context"+f++;this.oldScroll={x:t.scrollLeft(),y:t.scrollTop()};this.waypoints={horizontal:{},vertical:{}};this.element[u]=this.id;c[this.id]=this;t.bind(y,function(){var t;if(!(e.didScroll||a)){e.didScroll=true;t=function(){e.doScroll();return e.didScroll=false};return r.setTimeout(t,n[m].settings.scrollThrottle)}});t.bind(p,function(){var t;if(!e.didResize){e.didResize=true;t=function(){n[m]("refresh");return e.didResize=false};return r.setTimeout(t,n[m].settings.resizeThrottle)}})}t.prototype.doScroll=function(){var t,e=this;t={horizontal:{newScroll:this.$element.scrollLeft(),oldScroll:this.oldScroll.x,forward:"right",backward:"left"},vertical:{newScroll:this.$element.scrollTop(),oldScroll:this.oldScroll.y,forward:"down",backward:"up"}};if(a&&(!t.vertical.oldScroll||!t.vertical.newScroll)){n[m]("refresh")}n.each(t,function(t,r){var i,o,l;l=[];o=r.newScroll>r.oldScroll;i=o?r.forward:r.backward;n.each(e.waypoints[t],function(t,e){var n,i;if(r.oldScroll<(n=e.offset)&&n<=r.newScroll){return l.push(e)}else if(r.newScroll<(i=e.offset)&&i<=r.oldScroll){return l.push(e)}});l.sort(function(t,e){return t.offset-e.offset});if(!o){l.reverse()}return n.each(l,function(t,e){if(e.options.continuous||t===l.length-1){return e.trigger([i])}})});return this.oldScroll={x:t.horizontal.newScroll,y:t.vertical.newScroll}};t.prototype.refresh=function(){var t,e,r,i=this;r=n.isWindow(this.element);e=this.$element.offset();this.doScroll();t={horizontal:{contextOffset:r?0:e.left,contextScroll:r?0:this.oldScroll.x,contextDimension:this.$element.width(),oldScroll:this.oldScroll.x,forward:"right",backward:"left",offsetProp:"left"},vertical:{contextOffset:r?0:e.top,contextScroll:r?0:this.oldScroll.y,contextDimension:r?n[m]("viewportHeight"):this.$element.height(),oldScroll:this.oldScroll.y,forward:"down",backward:"up",offsetProp:"top"}};return n.each(t,function(t,e){return n.each(i.waypoints[t],function(t,r){var i,o,l,s,f;i=r.options.offset;l=r.offset;o=n.isWindow(r.element)?0:r.$element.offset()[e.offsetProp];if(n.isFunction(i)){i=i.apply(r.element)}else if(typeof i==="string"){i=parseFloat(i);if(r.options.offset.indexOf("%")>-1){i=Math.ceil(e.contextDimension*i/100)}}r.offset=o-e.contextOffset+e.contextScroll-i;if(r.options.onlyOnScroll&&l!=null||!r.enabled){return}if(l!==null&&l<(s=e.oldScroll)&&s<=r.offset){return r.trigger([e.backward])}else if(l!==null&&l>(f=e.oldScroll)&&f>=r.offset){return r.trigger([e.forward])}else if(l===null&&e.oldScroll>=r.offset){return r.trigger([e.forward])}})})};t.prototype.checkEmpty=function(){if(n.isEmptyObject(this.waypoints.horizontal)&&n.isEmptyObject(this.waypoints.vertical)){this.$element.unbind([p,y].join(" "));return delete c[this.id]}};return t}();l=function(){function t(t,e,r){var i,o;if(r.offset==="bottom-in-view"){r.offset=function(){var t;t=n[m]("viewportHeight");if(!n.isWindow(e.element)){t=e.$element.height()}return t-n(this).outerHeight()}}this.$element=t;this.element=t[0];this.axis=r.horizontal?"horizontal":"vertical";this.callback=r.handler;this.context=e;this.enabled=r.enabled;this.id="waypoints"+v++;this.offset=null;this.options=r;e.waypoints[this.axis][this.id]=this;s[this.axis][this.id]=this;i=(o=this.element[w])!=null?o:[];i.push(this.id);this.element[w]=i}t.prototype.trigger=function(t){if(!this.enabled){return}if(this.callback!=null){this.callback.apply(this.element,t)}if(this.options.triggerOnce){return this.destroy()}};t.prototype.disable=function(){return this.enabled=false};t.prototype.enable=function(){this.context.refresh();return this.enabled=true};t.prototype.destroy=function(){delete s[this.axis][this.id];delete this.context.waypoints[this.axis][this.id];return this.context.checkEmpty()};t.getWaypointsByElement=function(t){var e,r;r=t[w];if(!r){return[]}e=n.extend({},s.horizontal,s.vertical);return n.map(r,function(t){return e[t]})};return t}();d={init:function(t,e){var r;e=n.extend({},n.fn[g].defaults,e);if((r=e.handler)==null){e.handler=t}this.each(function(){var t,r,i,s;t=n(this);i=(s=e.context)!=null?s:n.fn[g].defaults.context;if(!n.isWindow(i)){i=t.closest(i)}i=n(i);r=c[i[0][u]];if(!r){r=new o(i)}return new l(t,r,e)});n[m]("refresh");return this},disable:function(){return d._invoke.call(this,"disable")},enable:function(){return d._invoke.call(this,"enable")},destroy:function(){return d._invoke.call(this,"destroy")},prev:function(t,e){return d._traverse.call(this,t,e,function(t,e,n){if(e>0){return t.push(n[e-1])}})},next:function(t,e){return d._traverse.call(this,t,e,function(t,e,n){if(e<n.length-1){return t.push(n[e+1])}})},_traverse:function(t,e,i){var o,l;if(t==null){t="vertical"}if(e==null){e=r}l=h.aggregate(e);o=[];this.each(function(){var e;e=n.inArray(this,l[t]);return i(o,e,l[t])});return this.pushStack(o)},_invoke:function(t){this.each(function(){var e;e=l.getWaypointsByElement(this);return n.each(e,function(e,n){n[t]();return true})});return this}};n.fn[g]=function(){var t,r;r=arguments[0],t=2<=arguments.length?e.call(arguments,1):[];if(d[r]){return d[r].apply(this,t)}else if(n.isFunction(r)){return d.init.apply(this,arguments)}else if(n.isPlainObject(r)){return d.init.apply(this,[null,r])}else if(!r){return n.error("jQuery Waypoints needs a callback function or handler option.")}else{return n.error("The "+r+" method does not exist in jQuery Waypoints.")}};n.fn[g].defaults={context:r,continuous:true,enabled:true,horizontal:false,offset:0,triggerOnce:false};h={refresh:function(){return n.each(c,function(t,e){return e.refresh()})},viewportHeight:function(){var t;return(t=r.innerHeight)!=null?t:i.height()},aggregate:function(t){var e,r,i;e=s;if(t){e=(i=c[n(t)[0][u]])!=null?i.waypoints:void 0}if(!e){return[]}r={horizontal:[],vertical:[]};n.each(r,function(t,i){n.each(e[t],function(t,e){return i.push(e)});i.sort(function(t,e){return t.offset-e.offset});r[t]=n.map(i,function(t){return t.element});return r[t]=n.unique(r[t])});return r},above:function(t){if(t==null){t=r}return h._filter(t,"vertical",function(t,e){return e.offset<=t.oldScroll.y})},below:function(t){if(t==null){t=r}return h._filter(t,"vertical",function(t,e){return e.offset>t.oldScroll.y})},left:function(t){if(t==null){t=r}return h._filter(t,"horizontal",function(t,e){return e.offset<=t.oldScroll.x})},right:function(t){if(t==null){t=r}return h._filter(t,"horizontal",function(t,e){return e.offset>t.oldScroll.x})},enable:function(){return h._invoke("enable")},disable:function(){return h._invoke("disable")},destroy:function(){return h._invoke("destroy")},extendFn:function(t,e){return d[t]=e},_invoke:function(t){var e;e=n.extend({},s.vertical,s.horizontal);return n.each(e,function(e,n){n[t]();return true})},_filter:function(t,e,r){var i,o;i=c[n(t)[0][u]];if(!i){return[]}o=[];n.each(i.waypoints[e],function(t,e){if(r(i,e)){return o.push(e)}});o.sort(function(t,e){return t.offset-e.offset});return n.map(o,function(t){return t.element})}};n[m]=function(){var t,n;n=arguments[0],t=2<=arguments.length?e.call(arguments,1):[];if(h[n]){return h[n].apply(null,t)}else{return h.aggregate.call(null,n)}};n[m].settings={resizeThrottle:100,scrollThrottle:30};return i.on("load.waypoints",function(){return n[m]("refresh")})})}).call(this);
/*
 * Copyright 2012 Small Batch, Inc.
 *
 * 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(window,document,undefined){
function i(a){return function(){return this[a]}}var j;function n(a,b){var c=arguments.length>2?Array.prototype.slice.call(arguments,2):[];return function(){c.push.apply(c,arguments);return b.apply(a,c)}};function o(a,b){this.K=a;this.c=b}o.prototype.createElement=function(a,b,c){a=this.K.createElement(a);if(b)for(var d in b)if(b.hasOwnProperty(d))d=="style"?q(this,a,b[d]):a.setAttribute(d,b[d]);c&&a.appendChild(this.K.createTextNode(c));return a};function r(a,b,c){a=a.K.getElementsByTagName(b)[0];if(!a)a=document.documentElement;if(a&&a.lastChild){a.insertBefore(c,a.lastChild);return true}return false}function aa(a,b){function c(){document.body?b():setTimeout(c,0)}c()}
function s(a,b){if(b.parentNode){b.parentNode.removeChild(b);return true}return false}function t(a,b){return a.createElement("link",{rel:"stylesheet",href:b})}function u(a,b){return a.createElement("script",{src:b})}function v(a,b,c){a=b.className.split(/\s+/);for(var d=0,e=a.length;d<e;d++)if(a[d]==c)return;a.push(c);b.className=a.join(" ").replace(/^\s+/,"")}
function w(a,b,c){a=b.className.split(/\s+/);for(var d=[],e=0,f=a.length;e<f;e++)a[e]!=c&&d.push(a[e]);b.className=d.join(" ").replace(/^\s+/,"").replace(/\s+$/,"")}function x(a,b,c){a=b.className.split(/\s+/);b=0;for(var d=a.length;b<d;b++)if(a[b]==c)return true;return false}function q(a,b,c){if(a.c.getName()=="MSIE")b.style.cssText=c;else b.setAttribute("style",c)};function y(a,b,c,d,e,f,g,h){this.Ba=a;this.Ha=b;this.oa=c;this.na=d;this.Ea=e;this.Da=f;this.ma=g;this.Ia=h}j=y.prototype;j.getName=i("Ba");j.xa=i("Ha");j.Y=i("oa");j.ua=i("na");j.va=i("Ea");j.wa=i("Da");j.ta=i("ma");j.w=i("Ia");function z(a,b){this.c=a;this.k=b}var ba=new y("Unknown","Unknown","Unknown","Unknown","Unknown","Unknown",undefined,false);z.prototype.parse=function(){return this.c.indexOf("MSIE")!=-1?ca(this):this.c.indexOf("Opera")!=-1?da(this):this.c.indexOf("AppleWebKit")!=-1?ea(this):this.c.indexOf("Gecko")!=-1?fa(this):ba};
function A(a){var b=C(a,a.c,/(iPod|iPad|iPhone|Android)/,1);if(b!="")return b;a=C(a,a.c,/(Linux|Mac_PowerPC|Macintosh|Windows)/,1);if(a!=""){if(a=="Mac_PowerPC")a="Macintosh";return a}return"Unknown"}function D(a){var b=C(a,a.c,/(OS X|Windows NT|Android) ([^;)]+)/,2);if(b)return b;if(b=C(a,a.c,/(iPhone )?OS ([\d_]+)/,2))return b;if(a=C(a,a.c,/Linux ([i\d]+)/,1))return a;return"Unknown"}
function ca(a){var b=C(a,a.c,/(MSIE [\d\w\.]+)/,1);if(b!=""){var c=b.split(" ");b=c[0];c=c[1];return new y(b,c,b,c,A(a),D(a),E(a,a.k),F(a,c)>=6)}return new y("MSIE","Unknown","MSIE","Unknown",A(a),D(a),E(a,a.k),false)}
function da(a){var b="Unknown",c="Unknown",d=C(a,a.c,/(Presto\/[\d\w\.]+)/,1);if(d!=""){c=d.split("/");b=c[0];c=c[1]}else{if(a.c.indexOf("Gecko")!=-1)b="Gecko";d=C(a,a.c,/rv:([^\)]+)/,1);if(d!="")c=d}if(a.c.indexOf("Version/")!=-1){d=C(a,a.c,/Version\/([\d\.]+)/,1);if(d!="")return new y("Opera",d,b,c,A(a),D(a),E(a,a.k),F(a,d)>=10)}d=C(a,a.c,/Opera[\/ ]([\d\.]+)/,1);if(d!="")return new y("Opera",d,b,c,A(a),D(a),E(a,a.k),F(a,d)>=10);return new y("Opera","Unknown",b,c,A(a),D(a),E(a,a.k),false)}
function ea(a){var b=A(a),c=D(a),d=C(a,a.c,/AppleWebKit\/([\d\.\+]+)/,1);if(d=="")d="Unknown";var e="Unknown";if(a.c.indexOf("Chrome")!=-1)e="Chrome";else if(a.c.indexOf("Safari")!=-1)e="Safari";else if(a.c.indexOf("AdobeAIR")!=-1)e="AdobeAIR";var f="Unknown";if(a.c.indexOf("Version/")!=-1)f=C(a,a.c,/Version\/([\d\.\w]+)/,1);else if(e=="Chrome")f=C(a,a.c,/Chrome\/([\d\.]+)/,1);else if(e=="AdobeAIR")f=C(a,a.c,/AdobeAIR\/([\d\.]+)/,1);var g=false;if(e=="AdobeAIR"){g=C(a,f,/\d+\.(\d+)/,1);g=F(a,f)>2||
F(a,f)==2&&parseInt(g,10)>=5}else{g=C(a,d,/\d+\.(\d+)/,1);g=F(a,d)>=526||F(a,d)>=525&&parseInt(g,10)>=13}return new y(e,f,"AppleWebKit",d,b,c,E(a,a.k),g)}
function fa(a){var b="Unknown",c="Unknown",d=false;if(a.c.indexOf("Firefox")!=-1){b="Firefox";var e=C(a,a.c,/Firefox\/([\d\w\.]+)/,1);if(e!=""){d=C(a,e,/\d+\.(\d+)/,1);c=e;d=e!=""&&F(a,e)>=3&&parseInt(d,10)>=5}}else if(a.c.indexOf("Mozilla")!=-1)b="Mozilla";e=C(a,a.c,/rv:([^\)]+)/,1);if(e=="")e="Unknown";else if(!d){d=F(a,e);var f=parseInt(C(a,e,/\d+\.(\d+)/,1),10),g=parseInt(C(a,e,/\d+\.\d+\.(\d+)/,1),10);d=d>1||d==1&&f>9||d==1&&f==9&&g>=2||e.match(/1\.9\.1b[123]/)!=null||e.match(/1\.9\.1\.[\d\.]+/)!=
null}return new y(b,c,"Gecko",e,A(a),D(a),E(a,a.k),d)}function F(a,b){a=C(a,b,/(\d+)/,1);if(a!="")return parseInt(a,10);return-1}function C(a,b,c,d){if((a=b.match(c))&&a[d])return a[d];return""}function E(a,b){if(b.documentMode)return b.documentMode};function ga(a,b,c,d){this.a=a;this.g=b;this.U=c;this.j=d||ha;this.h=new G("-")}var ha="wf";function H(a){v(a.a,a.g,a.h.e(a.j,"loading"));I(a,"loading")}function J(a){w(a.a,a.g,a.h.e(a.j,"loading"));x(a.a,a.g,a.h.e(a.j,"active"))||v(a.a,a.g,a.h.e(a.j,"inactive"));I(a,"inactive")}function ia(a){w(a.a,a.g,a.h.e(a.j,"loading"));w(a.a,a.g,a.h.e(a.j,"inactive"));v(a.a,a.g,a.h.e(a.j,"active"));I(a,"active")}function I(a,b,c,d){a.U[b]&&a.U[b](c,d)};function ja(){this.fa={}}function ka(a,b){var c=[];for(var d in b)if(b.hasOwnProperty(d)){var e=a.fa[d];e&&c.push(e(b[d]))}return c};function L(a,b,c,d,e){this.a=a;this.A=b;this.n=c;this.u=d;this.D=e;this.V=0;this.ja=this.ea=false}L.prototype.watch=function(a,b,c,d,e){for(var f=a.length,g=0;g<f;g++){var h=a[g];b[h]||(b[h]=["n4"]);this.V+=b[h].length}if(e)this.ea=e;for(g=0;g<f;g++){h=a[g];e=b[h];for(var l=c[h],k=0,m=e.length;k<m;k++){var B=e[k],p=this.A,K=h;v(p.a,p.g,p.h.e(p.j,K,B,"loading"));I(p,"fontloading",K,B);p=n(this,this.qa);K=n(this,this.ra);(new d(p,K,this.a,this.n,this.u,this.D,h,B,l)).start()}}};
L.prototype.qa=function(a,b){var c=this.A;w(c.a,c.g,c.h.e(c.j,a,b,"loading"));w(c.a,c.g,c.h.e(c.j,a,b,"inactive"));v(c.a,c.g,c.h.e(c.j,a,b,"active"));I(c,"fontactive",a,b);this.ja=true;M(this)};L.prototype.ra=function(a,b){var c=this.A;w(c.a,c.g,c.h.e(c.j,a,b,"loading"));x(c.a,c.g,c.h.e(c.j,a,b,"active"))||v(c.a,c.g,c.h.e(c.j,a,b,"inactive"));I(c,"fontinactive",a,b);M(this)};function M(a){if(--a.V==0&&a.ea)a.ja?ia(a.A):J(a.A)};function N(a,b,c,d,e,f,g,h,l){this.H=a;this.$=b;this.a=c;this.n=d;this.u=e;this.D=f;this.Aa=new la;this.v=new O;this.L=g;this.B=h;this.sa=l||ma;this.O=na(this,oa);this.P=na(this,pa);this.ca=this.O;this.da=this.P;this.Q=P(this,oa);this.R=P(this,pa)}var oa="arial,'URW Gothic L',sans-serif",pa="Georgia,'Century Schoolbook L',serif",ma="BESbswy";N.prototype.start=function(){this.ia=this.D();this.J()};
N.prototype.J=function(){var a=this.n.p(this.Q),b=this.n.p(this.R);if((this.O!=a||this.P!=b)&&this.ca==a&&this.da==b)Q(this,this.H);else if(this.D()-this.ia>=5E3)Q(this,this.$);else{this.ca=a;this.da=b;qa(this)}};function qa(a){a.u(function(b,c){return function(){c.call(b)}}(a,a.J),25)}function Q(a,b){s(a.a,a.Q);s(a.a,a.R);b(a.L,a.B)}function na(a,b){b=P(a,b,true);var c=a.n.p(b);s(a.a,b);return c}function P(a,b,c){b=a.a.createElement("span",{style:R(a,b,a.B,c)},a.sa);r(a.a,"body",b);return b}
function R(a,b,c,d){c=a.v.expand(c);return"position:absolute;top:-999px;left:-999px;font-size:300px;width:auto;height:auto;line-height:normal;margin:0;padding:0;font-variant:normal;font-family:"+(d?"":a.Aa.quote(a.L)+",")+b+";"+c};function S(a,b,c,d,e){this.a=a;this.X=b;this.g=c;this.u=d;this.c=e;this.M=this.N=0}S.prototype.q=function(a,b){this.X.fa[a]=b};S.prototype.load=function(a){var b=new ga(this.a,this.g,a);this.c.w()?ra(this,b,a):J(b)};S.prototype.ya=function(a,b,c,d){var e=a.Z?a.Z():N;if(d)a.load(n(this,this.Ca,b,c,e));else{a=--this.N==0;this.M--;if(a)this.M==0?J(b):H(b);c.watch([],{},{},e,a)}};
S.prototype.Ca=function(a,b,c,d,e,f){var g=--this.N==0;g&&H(a);this.u(n(this,function(h,l,k,m,B,p){h.watch(l,k||{},m||{},B,p)},b,d,e,f,c,g))};function ra(a,b,c){c=ka(a.X,c);a.M=a.N=c.length;for(var d=new L(a.a,b,{p:function(h){return h.offsetWidth}},a.u,function(){return(new Date).getTime()}),e=0,f=c.length;e<f;e++){var g=c[e];g.z(a.c,n(a,a.ya,g,b,d))}};function G(a){this.za=a||sa}var sa="-";G.prototype.e=function(){for(var a=[],b=0;b<arguments.length;b++)a.push(arguments[b].replace(/[\W_]+/g,"").toLowerCase());return a.join(this.za)};function la(){this.ha="'"}la.prototype.quote=function(a){var b=[];a=a.split(/,\s*/);for(var c=0;c<a.length;c++){var d=a[c].replace(/['"]/g,"");d.indexOf(" ")==-1?b.push(d):b.push(this.ha+d+this.ha)}return b.join(",")};function O(){this.G=ta;this.o=ua}var ta=["font-style","font-weight"],ua={"font-style":[["n","normal"],["i","italic"],["o","oblique"]],"font-weight":[["1","100"],["2","200"],["3","300"],["4","400"],["5","500"],["6","600"],["7","700"],["8","800"],["9","900"],["4","normal"],["7","bold"]]};function T(a,b,c){this.aa=a;this.Fa=b;this.o=c}T.prototype.compact=function(a,b){for(var c=0;c<this.o.length;c++)if(b==this.o[c][1]){a[this.aa]=this.o[c][0];return}};
T.prototype.expand=function(a,b){for(var c=0;c<this.o.length;c++)if(b==this.o[c][0]){a[this.aa]=this.Fa+":"+this.o[c][1];return}};O.prototype.compact=function(a){var b=["n","4"];a=a.split(";");for(var c=0,d=a.length;c<d;c++){var e=a[c].replace(/\s+/g,"").split(":");if(e.length==2){var f=e[1];a:{e=e[0];for(var g=0;g<this.G.length;g++)if(e==this.G[g]){e=new T(g,e,this.o[e]);break a}e=null}e&&e.compact(b,f)}}return b.join("")};
O.prototype.expand=function(a){if(a.length!=2)return null;for(var b=[null,null],c=0,d=this.G.length;c<d;c++){var e=this.G[c],f=a.substr(c,1);(new T(c,e,this.o[e])).expand(b,f)}return b[0]&&b[1]?b.join(";")+";":null};window.WebFont=function(){var a=(new z(navigator.userAgent,document)).parse();return new S(new o(document,a),new ja,document.documentElement,function(b,c){setTimeout(b,c)},a)}();window.WebFont.load=window.WebFont.load;window.WebFont.addModule=window.WebFont.q;y.prototype.getName=y.prototype.getName;y.prototype.getVersion=y.prototype.xa;y.prototype.getEngine=y.prototype.Y;y.prototype.getEngineVersion=y.prototype.ua;y.prototype.getPlatform=y.prototype.va;y.prototype.getPlatformVersion=y.prototype.wa;
y.prototype.getDocumentMode=y.prototype.ta;y.prototype.isSupportingWebFont=y.prototype.w;function U(a,b){this.a=a;this.d=b}var va={regular:"n4",bold:"n7",italic:"i4",bolditalic:"i7",r:"n4",b:"n7",i:"i4",bi:"i7"};U.prototype.z=function(a,b){return b(a.w())};
U.prototype.load=function(a){r(this.a,"head",t(this.a,("https:"==document.location.protocol?"https:":"http:")+"//webfonts.fontslive.com/css/"+this.d.key+".css"));var b;b=this.d.families;var c,d,e;c=[];d={};for(var f=0,g=b.length;f<g;f++){e=void 0;var h=void 0;h=void 0;h=b[f].split(":");e=h[0];h=h[1]?wa(this,h[1]):["n4"];e={W:e,T:h};c.push(e.W);d[e.W]=e.T}b={pa:c,T:d};a(b.pa,b.T)};
function wa(a,b){a=b.split(",");b=[];for(var c=0,d=a.length;c<d;c++){var e=a[c];if(e){var f=va[e];b.push(f?f:e)}}return b}window.WebFont.q("ascender",function(a){var b=(new z(navigator.userAgent,document)).parse();return new U(new o(document,b),a)});function V(a,b,c,d,e,f,g,h,l){V.Ga.call(this,a,b,c,d,e,f,g,h,l);a=["Times New Roman","Lucida Sans Unicode","Courier New","Tahoma","Arial","Microsoft Sans Serif","Times","Lucida Console","Sans","Serif","Monospace"];b=a.length;c={};d=P(this,a[0],true);c[this.n.p(d)]=true;for(e=1;e<b;e++){f=a[e];q(this.a,d,R(this,f,this.B,true));c[this.n.p(d)]=true;if(this.B[1]!="4"){q(this.a,d,R(this,f,this.B[0]+"4",true));c[this.n.p(d)]=true}}s(this.a,d);this.t=c;this.la=false}
(function(a,b){function c(){}c.prototype=a.prototype;b.prototype=new c;b.Ga=a;b.Ja=a.prototype})(N,V);var xa={Arimo:true,Cousine:true,Tinos:true};V.prototype.J=function(){var a=this.n.p(this.Q),b=this.n.p(this.R);if(!this.la&&a==b&&this.t[a]){this.t={};this.la=this.t[a]=true}if((this.O!=a||this.P!=b)&&!this.t[a]&&!this.t[b])Q(this,this.H);else if(this.D()-this.ia>=5E3)this.t[a]&&this.t[b]&&xa[this.L]?Q(this,this.H):Q(this,this.$);else qa(this)};function ya(a){this.I=a?a:("https:"==window.location.protocol?"https:":"http:")+za;this.f=[];this.S=[]}var za="//fonts.googleapis.com/css";ya.prototype.e=function(){if(this.f.length==0)throw new Error("No fonts to load !");if(this.I.indexOf("kit=")!=-1)return this.I;for(var a=this.f.length,b=[],c=0;c<a;c++)b.push(this.f[c].replace(/ /g,"+"));a=this.I+"?family="+b.join("%7C");if(this.S.length>0)a+="&subset="+this.S.join(",");return a};function Aa(a){this.f=a;this.ga=[];this.ka={};this.F={};this.v=new O}var Ba={ultralight:"n2",light:"n3",regular:"n4",bold:"n7",italic:"i4",bolditalic:"i7",ul:"n2",l:"n3",r:"n4",b:"n7",i:"i4",bi:"i7"},Ca={latin:ma,cyrillic:"&#1081;&#1103;&#1046;",greek:"&#945;&#946;&#931;",khmer:"&#x1780;&#x1781;&#x1782;",Hanuman:"&#x1780;&#x1781;&#x1782;"};
Aa.prototype.parse=function(){for(var a=this.f.length,b=0;b<a;b++){var c=this.f[b].split(":"),d=c[0],e=["n4"];if(c.length>=2){var f=c[1],g=[];if(f){f=f.split(",");for(var h=f.length,l=0;l<h;l++){var k;k=f[l];if(k.match(/^[\w ]+$/)){var m=Ba[k];if(m)k=m;else{m=k.match(/^(\d*)(\w*)$/);k=m[1];m=m[2];k=(k=this.v.expand([m?m:"n",k?k.substr(0,1):"4"].join("")))?this.v.compact(k):null}}else k="";k&&g.push(k)}}if(g.length>0)e=g;if(c.length==3){c=c[2];g=[];c=c?c.split(","):g;if(c.length>0)if(c=Ca[c[0]])this.F[d]=
c}}if(!this.F[d])if(c=Ca[d])this.F[d]=c;this.ga.push(d);this.ka[d]=e}};function W(a,b,c){this.c=a;this.a=b;this.d=c}W.prototype.z=function(a,b){b(a.w())};W.prototype.Z=function(){if(this.c.Y()=="AppleWebKit")return V;return N};W.prototype.load=function(a){var b=this.a;this.c.getName()=="MSIE"&&this.d.blocking!=true?aa(b,n(this,this.ba,a)):this.ba(a)};
W.prototype.ba=function(a){for(var b=this.a,c=new ya(this.d.api),d=this.d.families,e=d.length,f=0;f<e;f++){var g=d[f].split(":");g.length==3&&c.S.push(g.pop());c.f.push(g.join(":"))}d=new Aa(d);d.parse();r(b,"head",t(b,c.e()));a(d.ga,d.ka,d.F)};window.WebFont.q("google",function(a){var b=(new z(navigator.userAgent,document)).parse();return new W(b,new o(document,b),a)});function X(a,b){this.a=a;this.d=b}X.prototype.load=function(a){for(var b=this.d.urls||[],c=this.d.families||[],d=0,e=b.length;d<e;d++)r(this.a,"head",t(this.a,b[d]));a(c)};X.prototype.z=function(a,b){return b(a.w())};window.WebFont.q("custom",function(a){var b=(new z(navigator.userAgent,document)).parse();return new X(new o(document,b),a)});function Y(a,b,c){this.m=a;this.a=b;this.d=c;this.f=[];this.s={};this.v=new O}Y.prototype.C=function(a){return("https:"==this.m.location.protocol?"https:":"http:")+(this.d.api||"//f.fontdeck.com/s/css/js/")+this.m.document.location.hostname+"/"+a+".js"};
Y.prototype.z=function(a,b){a=this.d.id;var c=this;if(a){this.m.__webfontfontdeckmodule__||(this.m.__webfontfontdeckmodule__={});this.m.__webfontfontdeckmodule__[a]=function(d,e){for(var f=0,g=e.fonts.length;f<g;++f){var h=e.fonts[f];c.f.push(h.name);c.s[h.name]=[c.v.compact("font-weight:"+h.weight+";font-style:"+h.style)]}b(d)};r(this.a,"head",u(this.a,this.C(a)))}else b(true)};Y.prototype.load=function(a){a(this.f,this.s)};
window.WebFont.q("fontdeck",function(a){var b=(new z(navigator.userAgent,document)).parse();return new Y(window,new o(document,b),a)});function Z(a,b,c,d,e){this.m=a;this.c=b;this.a=c;this.k=d;this.d=e;this.f=[];this.s={}}
Z.prototype.z=function(a,b){var c=this,d=c.d.projectId;if(d){var e=u(c.a,c.C(d));e.id="__MonotypeAPIScript__"+d;e.onreadystatechange=function(f){if(e.readyState==="loaded"||e.readyState==="complete"){e.onreadystatechange=null;e.onload(f)}};e.onload=function(){if(c.m["__mti_fntLst"+d]){var f=c.m["__mti_fntLst"+d]();if(f&&f.length){var g;for(g=0;g<f.length;g++)c.f.push(f[g].fontfamily)}}b(a.w())};r(this.a,"head",e)}else b(true)};
Z.prototype.C=function(a){var b=this.protocol(),c=(this.d.api||"fast.fonts.com/jsapi").replace(/^.*http(s?):(\/\/)?/,"");return b+"//"+c+"/"+a+".js"};Z.prototype.load=function(a){a(this.f,this.s)};Z.prototype.protocol=function(){var a=["http:","https:"],b=a[0];if(this.k&&this.k.location&&this.k.location.protocol){var c=0;for(c=0;c<a.length;c++)if(this.k.location.protocol===a[c])return this.k.location.protocol}return b};
window.WebFont.q("monotype",function(a){var b=(new z(navigator.userAgent,document)).parse();return new Z(window,b,new o(document,b),document,a)});function $(a,b,c){this.m=a;this.a=b;this.d=c;this.f=[];this.s={}}$.prototype.C=function(a){var b="https:"==window.location.protocol?"https:":"http:";return(this.d.api||b+"//use.typekit.com")+"/"+a+".js"};$.prototype.z=function(a,b){var c=this.d.id,d=this.d,e=this;if(c){this.m.__webfonttypekitmodule__||(this.m.__webfonttypekitmodule__={});this.m.__webfonttypekitmodule__[c]=function(f){f(a,d,function(g,h,l){e.f=h;e.s=l;b(g)})};r(this.a,"head",u(this.a,this.C(c)))}else b(true)};
$.prototype.load=function(a){a(this.f,this.s)};window.WebFont.q("typekit",function(a){var b=(new z(navigator.userAgent,document)).parse();return new $(window,new o(document,b),a)});window.WebFontConfig&&window.WebFont.load(window.WebFontConfig);
})(this,document);
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2015 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####


"""
called from configedit.cgi by javascript

checks whether the proposed modification of the configuration is syntactically acceptable

"""

import cgi, sys, cgitb, codecs, os
from lib import config

sys.path.append('modules')
from logintools import isloggedin


cgitb.enable()

form = cgi.FieldStorage()

ct = form.getvalue('config',None).decode("utf-8")
cats = form.getvalue('cats',None).decode("utf-8")
funcs = form.getvalue('funcs',None).decode("utf-8")
projectName = form.getvalue('project',None).decode("utf-8")

test = isloggedin("users/")
if not test:sys.exit()
admin = test[0]["admin"]
if not admin:sys.exit()

#print "Content-Type: text/html\n" # blank line: end of headers

def confToDisk(tmp):
	filename=unicode(os.path.join(unicode(os.getcwd()),u"projects",projectName,u"project.cfg"+tmp )).encode("utf-8")
	f=codecs.open(filename,"w","utf-8")
	f.write(ct)
	f.close()
	c=config.checkConfigProject(projectName, filename=u"project.cfg"+tmp,readin=False)	
	
	filename=os.path.join(os.getcwd(),"projects",projectName.encode("utf-8"),(c["configuration"]["categoriesfilename"]+tmp).encode("utf-8") )  
	f=codecs.open(filename,"w","utf-8")
	f.write(cats)
	f.close()

	filename=os.path.join(os.getcwd(),"projects",projectName.encode("utf-8"),(c["configuration"]["functionsfilename"]+tmp).encode("utf-8") )  
	f=codecs.open(filename,"w","utf-8")
	f.write(funcs)
	f.close()

	return config.checkConfigProject(projectName, filename=u"project.cfg"+tmp,readin=True)

def eraseConfig(tmp,c):
	os.remove(unicode(os.path.join(unicode(os.getcwd()),u"projects",projectName,u"project.cfg"+tmp )).encode("utf-8"))
	os.remove(os.path.join(os.getcwd(),"projects",projectName.encode("utf-8"),(c["configuration"]["categoriesfilename"]+tmp).encode("utf-8") )  )
	os.remove(os.path.join(os.getcwd(),"projects",projectName.encode("utf-8"),(c["configuration"]["functionsfilename"]+tmp).encode("utf-8") )  )
	
# first phase: testing with tmp files, without erasing the old files
c=confToDisk(".tmp")
#erase the tmp config files:
eraseConfig(".tmp",c)
# if here no error all is well. we can save normally
c=confToDisk("")
#and 



print "Content-Type: text/json\r\n\r\n"
print '{"answer":"Ok. Saved."}'
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2015 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

"""
page accessible from project.cgi for admins

allows edits of the configuration

"""

import os, cgitb, cgi, sys, glob, codecs
from lib import config, database
sys.path.append('modules')
from logintools import login
from logintools import isloggedin
from logintools import logout
cgitb.enable()



##############################################################################################"

def start():
	form = cgi.FieldStorage()
	thisfile = os.environ.get('SCRIPT_NAME',".")
	userdir = 'users/'

	logint = form.getvalue("login",None)
	if logint == "logout":
		print logout(userdir) # printing cookie header. important!
		print "Content-Type: text/html\n" # blank line: end of headers
		print '<script type="text/javascript">window.location.href=".";</script>'
	project = (form.getvalue("project",None))
	if project: project =project.decode("utf-8")
	
	action = form.getvalue("action",None)
	if action: action =action.decode("utf-8")
	if action:
		if action.startswith("project_"):project=action[8:]
	if project: action=u"project_"+project
	if action:
		#action, userconfig = login(form, userdir, thisfile, action.encode("utf-8"))
		try:action, userconfig = login(form, userdir, thisfile, action)
		except:
			print "Content-Type: text/html\n" # blank line: end of headers
			print "Error! Can't read the user config files. Please check that the user files are readible by the apache user"
			sys.exit("something's wrong")
	else:
		action, userconfig = login(form, userdir, thisfile, action)
	adminLevel, username, realname = int(userconfig["admin"]),userconfig["username"].decode("utf-8"),userconfig["realname"].decode("utf-8")
	
	print "Content-Type: text/html\n" # blank line: end of headers
	projectconfig=None
	try:	
		projectconfig = config.configProject(project) # read in the settings of the current project
	except:
		print "Error! Can't read the configuration files. Please check that the files project.cfg, functions.cfg, and categories.cfg are present in the project folder	"
		sys.exit("something's wrong")
	if not projectconfig:
		##print "Content-Type: text/html\n" # blank line: end of headers
		print "something went seriously wrong: can't read the configuration of the project"
		if project: print project.encode("utf-8")
		##print "Content-Type: text/html\n" # blank line: end of headers
		print '<script type="text/javascript">window.location.href=".";</script>'
		sys.exit("something's wrong")
	try:
		sql=database.SQL(project)
		userid = sql.userid(username, realname)
	except Exception , e:
		from sqlite3 import OperationalError
		if isinstance(e, OperationalError) and str(e)in["attempt to write a readonly database","unable to open database file"]:
			print "Error! Make your database and the path leading there from the arborator base modifiable by the apache user!"
		else:
			print "strange error accessing the database.<br>",str(e)
		sys.exit("something's wrong")
	
	if (not projectconfig) and (not action):
		print "something went seriously wrong: can't read the configuration of the project",project.encode("utf-8")
		#print '<script type="text/javascript">window.location.href=".";</script>'
		sys.exit("something's wrong")
	
	if not adminLevel:
		print "something went seriously wrong: a common can't access the configedit file"
		if project: print project.encode("utf-8")
		##print "Content-Type: text/html\n" # blank line: end of headers
		print '<script type="text/javascript">window.location.href=".";</script>'
		sys.exit("user went astraw!"+username)
	
	return project,projectconfig,sql,thisfile,username,userid,adminLevel,form

	
	

def printhtmlheader(project):
	
	print """<html>
			<head>
			<title>Arborator - {project} Project</title>""".format(project=project)
	print """
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
	<script type="text/javascript" src="script/jquery.js"></script>
	<script type="text/javascript" src="script/jquery-ui-1.8.18.custom.min.js"></script>
	<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen"/>
	<link href="css/arborator.css" rel="stylesheet" type="text/css">
	<script type="text/javascript">	
	project="{project}";
	userid="{userid}";
	username="{username}";
	""".format(project=project,userid=userid,username=username)
	
	print """
	
	checkConfig = function() {
		$("#ent").hide();
		$("#wait").show();
		
		$.ajax({
			type: "POST",
			url: "configCheck.cgi",
			data: {"config":$("#configurationArea").val(),"cats":$("#catconfigurationArea").val(),"funcs":$("#funcconfigurationArea").val(),"project":project}
			})
			.done(function(data){
					if (typeof data.answer === "undefined") $("#message").html(data);
					else $("#message").html(data.answer);
					console.log("got!"+data.answer);
				})
			.fail(function(XMLHttpRequest, textStatus, errorThrown){
				console.log("error",project)
				alert("error in config"+XMLHttpRequest+ '\\n'+textStatus+ "\\n"+errorThrown);
				})
			.always(function() {
				$("#ent").show();
				$("#wait").hide();
				$("#message").show();
				});
			
				
				
		
		
		
	}
	
	</script>
	
	</head>
	"""
	
	
	
def printheadline(project):
	if os.path.exists("projects/"+project+"/"+project+".png"):img="<img src='projects/"+project+"/"+project+".png' align='top' height='18'>"
	else: img=""
	print 	"""<body id="body">
			<div id="center" class="center" style="width:100%">
				<div id="navigation" style="width:100%;margin:0px;border:0px;" class="arbortitle  ui-widget-header ui-helper-clearfix">
				<a href='.' style='position: fixed;left:1px;top:1px'><img src="images/arboratorNano.png" border="0"></a>
				<a href="project.cgi?project={project}" style='position: fixed;left:120px;top:5px;color:white;' title="project overview">{img} {project} Annotation Project</a>
				<div style='margin:5 auto;' id='sentinfo'>Configuration</div>
					
				</div>
		""".format(project=project,img=img)
	if os.path.exists("sitemessage.html"):print open("sitemessage.html").read()




def printconfig(project,projectconfig):
	#project=project
	filecontent=codecs.open("projects/"+project.encode("utf-8")+"/project.cfg","r","utf-8").read()
	catfilecontent=codecs.open("projects/"+project.encode("utf-8")+"/"+projectconfig["configuration"]["categoriesfilename"].encode("utf-8"),"r","utf-8").read()
	funcfilecontent=codecs.open("projects/"+project.encode("utf-8")+"/"+projectconfig["configuration"]["functionsfilename"].encode("utf-8"),"r","utf-8").read()
	
	txt = u"""<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;">
	This is the configuration file of the <b>{project}</b> Project.<br/> Please edit with care. Comments start with "#".
	""".format(project=project)
	#<p class="section-title">Configuration</p>
	txt+=u"""<textarea id="configurationArea" name="configurationArea" style="width:100%;height:400px;color:black;">{c}</textarea>""".format(c=filecontent)
	
	txt+=u"""<br/>Categories
	<textarea id="catconfigurationArea" name="catconfigurationArea" style="width:100%;height:400px;color:black;">{c}</textarea>""".format(c=catfilecontent)
	txt+=u"""<br/>Functions
	<textarea id="funcconfigurationArea" name="funcconfigurationArea" style="width:100%;height:400px;color:black;">{c}</textarea>""".format(c=funcfilecontent)
	txt+=u"""<input type="button" onclick="checkConfig()" class="fg-button ui-state-default ui-corner-all" style="font:normal .8em Arial;padding-top: 2px;width:80px;" value="Save" name="ent" id="ent"><div style="display:none;" id="message"></div><div style="display:none;" id="wait"><img id='ent' src='images/ajax-loader.gif'></div><br/>"""
	#txt+=unicode(projectconfig)
	txt+=u"</div>"
	print txt.encode("utf-8")
	
	
	
	print """<br/><br/><div class="ui-widget ui-widget-content ui-corner-all box">
	<h3>Useful links</h3>
	<p>The <a href='http://bulba.sdsu.edu/jeanette/thesis/PennTags.html'>Penn Treebank tags</a> and the <a href='./corpus/PTB.tags'>corresponding category file for the Arborator</a>.</p>
	<p>This <a href='http://csscolorpicker.com/'>color picker</a> comes in handy.</p>


	</div>
	"""


	         
	         
	         
	
def printfooter(project,username,thisfile, now):
	"""
	"now" contains a list of all users currently online
	"""
	a=u"""<div class="arborfoot fg-toolbar ui-widget-header ui-helper-clearfix">logged in as {username}&nbsp;&nbsp;&nbsp;
	<a href="{thisfile}?login=logout&project={project}">logout</a>""".format(username=username,project=project,thisfile=thisfile)
	print (u"""<div class="arborfoot fg-toolbar ui-widget-header ui-helper-clearfix">logged in as {username}&nbsp;&nbsp;&nbsp;
	<a href="{thisfile}?login=logout&project={project}">logout</a>""".format(username=username,project=project,thisfile=thisfile)).encode("utf-8")
	if username!="guest": print (u'&nbsp;&nbsp;&nbsp;<a href="{thisfile}?login=editaccount&project={project}">edit the account {username}</a>'.format(username=username,project=project,thisfile=thisfile)).encode("utf-8")
	if adminLevel > 1: print (u'&nbsp;&nbsp;&nbsp;<a href="{thisfile}?login=admin&project={project}">User Administration</a>'.format(project=project,thisfile=thisfile)).encode("utf-8")
	#if adminLevel: print (u'&nbsp;&nbsp;&nbsp;<a href="configedit.cgi?project={project}">Edit Configuration</a>'.format(project=project)).encode("utf-8")
	nowstring=u'<a  title="Ask questions or share your feelings with {r}..." href="mailto:{email}?subject=The%20Arborator%20is%20driving%20me%20crazy!">{r} ({u})</a>'
	now = [nowstring.format(u=u,r=unicode(r),email=email) for (u,r,email) in now if u!= username]
	if len(now)==1: print (u'&nbsp;&nbsp;&nbsp;You are not alone! This other user is online now: '+now[0]).encode("utf-8")
	elif len(now)>1: print (u'&nbsp;&nbsp;&nbsp;You are not alone! These other users are online now: '+u", ".join(now)).encode("utf-8")
	print "</div>"
	print '</body></html>'
	#&nbsp;&nbsp;&nbsp;<a href="admin.cgi?project={project}">Corpus Administration</a>
	

		
if __name__ == "__main__":
	
	project,projectconfig,sql,thisfile,username,userid,adminLevel,form = start()
	
	printhtmlheader(project.encode("utf-8"))
	printheadline(project.encode("utf-8"))
		
	printconfig(project,projectconfig)
	
	lastseens,now=sql.usersLastSeen()
	printfooter(project,username,thisfile,now)
	
	










#!/usr/bin/python
# -*- coding: utf-8 -*-

import cgi,sys
from os import environ
sys.path.append('modules')
from logintools import isloggedin
from lib.database import SQL

test = isloggedin("users/")
if not test:sys.exit()
admin = int(test[0]["admin"])
method = environ.get( 'REQUEST_METHOD' )

if method == "POST": # and admin: #or True
	data = cgi.FieldStorage()
	
	project = data.getvalue("project",None).decode("utf-8")
	
	sid = data.getvalue("sid",None)
	nr = data.getvalue("nr",None)
	toknr = data.getvalue("toknr",None)
	connectsplit = data.getvalue("connectsplit",None)
	
	print "Content-Type: text/html\r\n\r\nconnecting trees"
	sql=SQL(project)
	if connectsplit=="connect":sql.connectRight(sid,nr)
	if connectsplit=="split":sql.splitSentence(sid,nr,toknr)
	
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
convert_svg.py  0.1

See <http://svgkit.sourceforge.net/> for documentation, downloads, license, etc.

(c) 2006-2007 Jason Gallicchio.  All rights Reserved.
"""

# Outline of code:
# Read the SVG
# Read the type to convert to
# Read the options (width, height, dpi, quality)  (TODO)
# Generate a hash for caching and check to see if we've already done this (TODO)
# Convert with scripts off
# Check the output file size.
# Send the result back
# needs: apt-get install librsvg2-bin !!!!!!!!!!!! new in 2014!!!


import cgi, cgitb, sys, os, urlparse, time, re, hashlib
from subprocess import *
from lib import config
cgitb.enable() # Show errors to browser.


form = cgi.FieldStorage()
time.sleep(0.1) # Throttle requests
project = form.getvalue("project",None).decode("utf-8")

if project != "quickie":
	

	projectconfig=None
	try:
		projectconfig = config.configProject(project) # read in the settings of the current project
	except:
		pass
	if not projectconfig:
		print "Content-Type: text/html\n" # blank line: end of headers
		print "something went seriously wrong: can't read the configuration of the project",project.encode("utf-8")
		#print "Content-Type: text/html\n" # blank line: end of headers
		#print '<script type="text/javascript">window.location.href=".";</script>'
		sys.exit("something's wrong")

	
#java = '/opt/jdk1.7.0/bin/java'
java = "java"
#batik = '/var/www/cgi-bin/arborator/batik-1.7/batik-rasterizer.jar'
batik = 'export/batik-1.7/batik-rasterizer.jar'

#svg2office = '/var/www/cgi-bin/arborator/svg2office-1.2.2.jar'
svg2office = 'export/svg2office-1.2.2.jar'

#cache='/var/www/html/cache/'
cache= 'export/cache/'

#cacheShow='http://localhost/cache/'
#cacheShow= projectconfig["configuration"]["url"]+'export/cache/'

#url = os.environ["QUERY_STRING"] 
#parsed = urlparse.urlparse(url) 

cacheShow= 'export/cache/'

sys.stderr = sys.stdout
cgi.maxlen = 1024*1024



mediatypes={
  'pdf':'application/pdf',
  'ps':'application/pdf',  # Gets converted after
  'odg':'application/odg',
  'jpg':'image/jpeg',
  'png':'image/png',
  'tif': 'image/tiff',
  'svg':'image/svg+xml'
}

#debug = True
debug = False
redirect = True
#redirect = False

if debug:   print 'Content-type: text/plain\n\n'


if debug:
    print 'Debug mode of convert_svg.py\n'
    print 'form.keys(): ' + form.keys().__str__()+'\n'
    for key in form.keys():
        print 'form['+key+'] = '+form[key].value+'\n'

source = form.getvalue('source',None)
if source == None: source = open("images/arborator.svg","r").read()

#source = open("arborator.svg","r").read()

type = form.getvalue('type',"pdf")
#type = form['type'].value
mime = mediatypes[type]

md5hex = hashlib.md5(source).hexdigest()
svgname = cache+md5hex+'.svg'
outname = cache+md5hex+'.'+type
showname = cacheShow+md5hex+'.'+type

def execute_cmd(cmd):
    #(child_stdin, child_stdout, child_stderr) = os.popen3(cmd)
    
    p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
    
	#(child_stdin,bufsize=bufsize,
	#child_stdout,
	#child_stderr) = (p.stdin, p.stdout, p.stderr)
    
    #print 1/0
    str_stdout = p.stdout.read() # Read until the process quits.
    str_stderr = p.stderr.read() # Read until the process quits.
    if debug:
        print cmd+'\n'
        print 'stdout:'+str_stdout+'\n'
        print 'stderr:'+str_stderr+'\n'
        
#execute_cmd('chown jason users files/*.*')  # Redhat disables chown
        
# If the result doesn't already exist in cached form, create it True or 
if not os.path.isfile(outname) or source!=open(svgname, 'r' ).read():
	
    # work around for the bug in rsvg-convert (failure to render non default fonts if they are not mentioned in "font-family")!
    pattern=re.compile(r"font:([^;]* ([^;]*));",re.I+re.U)
    source=pattern.sub(ur"font:\1; font-family:\2;",source.decode("utf-8")).encode("utf-8")
    
    
    svgfile = open(svgname, 'w')
    svgfile.write(source)
    svgfile.close()
    #if type == 'xml':
	#PrettyPrint(xmlsqlite.tree2xml(1))
    if type == 'svg':
        outname = svgname
    elif type == "odg":
	cmd = java+' -jar ' + svg2office +" "+svgname #+" "+ outname
	if debug: print cmd
	execute_cmd(cmd)
	#print "<br>",cmd
    elif type in ["png","pdf","ps"]:
	 cmd = "rsvg-convert --background-color white -z 3 -f {type} -o {outname} {svgname}".format(type=type,svgname=svgname,outname=outname)
	 execute_cmd(cmd)
    else:
        cmd = java+' -jar ' + batik + ' -d '+cache+' -m '+mime+' '+svgname # -dpi <resolution>  -q <quality>
        if debug: print cmd
        
        execute_cmd(cmd)
        
        if type=='ps':
            inname = cache+md5hex+'.pdf'
            cmd = 'pdftops '+inname+' '+outname
	    
            execute_cmd(cmd)
#time.sleep(1)

if redirect:
	#print 'Content-type: text/plain\n\n'
	print 'Location: '+showname+'\r\n\r\n'
else:
    outfile = open( outname, 'rb')
    image = outfile.read()
    if not debug:
        sys.stdout.write('Content-type: '+mime+'\r\n\r\n')
        sys.stdout.write(image)
    outfile.close()
ADV	{"stroke": "#69399d"}
BNF	{"stroke": "#69399d"}
DIR	{"stroke": "#69399d"}
DTV	{"stroke": "#69399d"}
EXT	{"stroke": "#69399d"}
LGS	{"stroke": "#69399d"}
LOC	{"stroke": "#69399d"}
MNR	{"stroke": "#69399d"}
PRD	{"stroke": "#69399d"}
PRP	{"stroke": "#69399d"}
PUT	{"stroke": "#69399d"}
SBJ	{"stroke": "#69399d"}
TMP	{"stroke": "#69399d"}
VOC	{"stroke": "#69399d"}
APPO	{"stroke": "#69399d"}
HMOD	{"stroke": "#69399d"}
HYPH	{"stroke": "#69399d"}
NAME	{"stroke": "#69399d"}
POSTHON	{"stroke": "#69399d"}
SUFFIX	{"stroke": "#69399d"}
TITLE	{"stroke": "#69399d"}
AMOD	{"stroke": "#69399d"}
CONJ	{"stroke": "#69399d"}
COORD	{"stroke": "#69399d"}
DEP	{"stroke": "#69399d"}
EXTR	{"stroke": "#69399d"}
GAP	{"stroke": "#69399d"}
IM	{"stroke": "#69399d"}
NMOD	{"stroke": "#69399d"}
OBJ	{"stroke": "#69399d"}
OPRD	{"stroke": "#69399d"}
P	{"stroke": "#69399d"}
PMOD	{"stroke": "#69399d"}
PRN	{"stroke": "#69399d"}
PRT	{"stroke": "#69399d"}
ROOT	{"stroke": "#69399d"}
SUB	{"stroke": "#69399d"}
VC	{"stroke": "#69399d"}
CC	{"fill": "#69399d"}
CD	{"fill": "#69399d"}
DT	{"fill": "#69399d"}
EX	{"fill": "#69399d"}
FW	{"fill": "#69399d"}
IN	{"fill": "#69399d"}
JJ	{"fill": "#69399d"}
JJR	{"fill": "#69399d"}
JJS	{"fill": "#69399d"}
LS	{"fill": "#69399d"}
MD	{"fill": "#69399d"}
NN	{"fill": "#69399d"}
NNS	{"fill": "#69399d"}
NNP	{"fill": "#69399d"}
NNPS	{"fill": "#69399d"}
PDT	{"fill": "#69399d"}
POS	{"fill": "#69399d"}
PRP	{"fill": "#69399d"}
PRP$	{"fill": "#69399d"}
RB	{"fill": "#69399d"}
RBR	{"fill": "#69399d"}
RBS	{"fill": "#69399d"}
RP	{"fill": "#69399d"}
SYM	{"fill": "#69399d"}
TO	{"fill": "#69399d"}
UH	{"fill": "#69399d"}
VB	{"fill": "#69399d"}
VBD	{"fill": "#69399d"}
VBG	{"fill": "#69399d"}
VBN	{"fill": "#69399d"}
VBP	{"fill": "#69399d"}
VBZ	{"fill": "#69399d"}
WDT	{"fill": "#69399d"}
WP	{"fill": "#69399d"}
WP$	{"fill": "#69399d"}
WRB	{"fill": "#69399d"}
 label { display: block; }
  select { width: 50px; height:18px;font-size:.6em;margin:5px;}
.lbubble
{
	color:#fff;
	display:none;
position: relative;
width: 135px;
height: 120px;
padding: 5px;
background: #654b6d;
-webkit-border-radius: 10px;
border-radius: 10px;
border-radius: 10px;
z-index: 1111;

}

.lbubble:after
{
content: '';
position: absolute;
border-style: solid;
border-width: 15px 15px 15px 0;
border-color: transparent #654b6d;
display: block;
width: 0;
z-index: 1111;
left: -15px;
top: 50px;
}


.rbubble
{
		color:#fff;
		display:none;

position: relative;
width: 135px;
height: 120px;
padding: 5px;
background: #654b6d;
-webkit-border-radius: 10px;
border-radius: 10px;
border-radius: 10px;
}

.rbubble:after
{
content: '';
position: absolute;
border-style: solid;
border-width: 15px 0 15px 15px;
border-color: transparent #654b6d;
display: block;
width: 0;
z-index: 1111;
right: -15px;
top: 50px;
}
.bubble
{
	color:#fff;display:none;
position: relative;
width: 135px;
height: 120px;
padding: 5px;
background: #654b6d;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
}

.bubble:after
{
content: '';
position: absolute;
border-style: solid;
border-width: 0 15px 15px;
border-color: #654b6d transparent;
display: block;
width: 0;
z-index: 1111;
top: -15px;
left: 57px;
}

textarea, pre { -moz-tab-size:20; -o-tab-size:20; -webkit-tab-size:20; tab-size:20; }


.boxx{
	float:right;
	position: fixed;
	right: 10;
	top: 50;
	z-index: 10;
	width:630;
	padding: 2;
}

.toggleAndBoxx{
	display:none;
	
}
.arrow{
	color: #654b6d;
    font-size: 44px;
    font-style:normal;
    position: absolute;
    top: 200px;
    opacity:0.4;
    cursor:pointer;
    z-index: 12;
	
}
.connect{
	color: #fdfdfd;
	font-size: 60px;
	font-style:normal;
	position: relative;
/* 	opacity:0.4; */
	cursor:pointer;
	z-index: 9;
	float:left;
	display:none;
}
.connect:hover {
	color: #DD137B;
}
.cut{
	color: #fdfdfd;
	font-size: 60px;
	font-style:normal;
	position: relative;
/* 	opacity:0.4; */
	cursor:pointer;
	z-index: 12;
	float:left;
	display:none;
	opacity:0.03;
}

.cut:hover{
	opacity:1;
}

.textual{
	float:right;
	right: 10;
	top: 50;
	z-index: 10;
	width:600;
	border: 1px solid;
	box-shadow: 10px 10px 5px #888888;
	padding: 2;
/* 	opacity:0.8; */
	background-color: white;
}

.textual textarea{
	
	font-size: .7em;
	tab-size: 12;
	-moz-tab-size:12;
/* 	opacity:1; */
/* 	background-color: white; */
}

.toggle{
	float:right;
	position: fixed;
	right: 14;
	top: 40;
	z-index: 10;
	display:inline-block;
	height:9px;
	width:9px;  
	background:url("images/expanded.png");
	cursor:pointer;
}
.toggle.expanded{
	background:url("images/collapsed.png");
}

.compare{
	cursor:pointer;
}

.connectRight{
	cursor:pointer;
}
.check{
	cursor:pointer;
}

.val{
	color: #dd137b;
	font-weight:bold;
}
.status{
	color: #ffffff;
	background-color: black;
	font-weight:bold;
	font-size: .8em;
	cursor:pointer;
}

.currentsentence{
	color: #DD137B;
	text-decoration: underline;
	
	}

.saveimg , .undoimg, .redoimg, .exportimg{
	visibility:hidden;
	cursor:pointer;
	vertical-align: text-top;
	}

.thistree{
	cursor:pointer;
	background: url("images/annotation.png") no-repeat scroll left bottom transparent;
	padding-left: 20px;
	padding-top: 10px;
	font-size: .9em;
	height: 55px;
	font-weight: bold
	}
	
.othertree{
	cursor:pointer;
	background: url("images/annotation.png") no-repeat scroll left bottom transparent;
	padding-left: 20px;
	padding-top: 10px;
	font-size: .7em;
	height: 55px;
	}

a:link, a:visited{
	text-decoration: none;
	color:#654b6d;
}


body span.ui-icon {
    float:left;
    }


a.fadehover {
	position: relative;
	}

img.a {
	position: absolute;
	left: 0;
	top: 0;
	z-index: 10;
        }

img.b {
	position: absolute;
	left: 0;
	top: 0;
	z-index: 9;
	}



a.expanded {
	background: url("images/expanded.png") no-repeat scroll left center transparent  !important;
	padding-left: 15px;
}

a.toggler {
	background: url("images/collapsed.png") no-repeat scroll left center transparent;
	padding-left: 15px;
	padding-right: 10px;
	cursor:pointer;
}

.svgholder{
	background:white; 
	position:relative;
	padding-left:20px;
	overflow:auto;
	
}


.ui-widget-header {
	/*background:#3B3B35 url(images/ui-bg_inset-soft_22_3b3b35_1x100.png) repeat-x scroll 50% 50%;*/
	border:1px solid #59584F;
	color:#FFFFFF;
	font-weight:bold;
}

.fg-toolbar { padding: .5em; margin: 0; width:100%; }
.fg-toolbar .fg-buttonset { margin-right:1.5em; padding-left: 1px; }
.fg-toolbar .fg-button { font-size: 1em;  }


.fg-button { 
	outline: 0; 
	margin:0 1px 0 0; 

	text-decoration:none !important; 
	cursor:pointer; 
	position: relative; 
	text-align: center; 
	transform: scale(1)
/* 	zoom: 1;  */
   }

/* .ui-state-default, .ui-widget-content .ui-state-default { */
/*background:#327E04 url(images/ui-bg_highlight-soft_25_327E04_1x100.png) repeat-x scroll 50% 50%;*/
/* border:1px solid #327E04; */
/* color:#FFFFFF; */
/* } */



.svgframe{
	margin-left: auto; margin-right: auto; 
	border:1px solid #cabcbe;
	padding:3px;
}

.arbortitle{
	position:fixed;
	/* top:0px;left:0px; */
	top:0px;
	width:100%;
	/*background-color: #FFFFFF;*/
	/* border-bottom:2px solid #654b6d; */
	/* margin-bottom:5px; */
	/* padding-bottom:5px; */
	text-align: center;
	margin:0px;
	height:25px;
	z-index:22;
	font-size:13px;

}

.arborfoot{
	position:fixed;bottom:0px;left:0px;
	width:100%;
	text-align: center;
	height:12px;
	margin-top:25px;
	padding:0px;
	text-align: center;
	font-size:10px;
	z-index:22;
	/* z-index:22; */
}



.center {
	width: 940px;
	margin: 0 auto;
	font-size: .8em;
	overflow: auto;
	padding-top: 25px;
	padding-bottom: 10px;
	}

.box {
	margin: 20px auto;  width: 90%; padding:10px;
}

.container {
		height: 130%;
	/* 	margin: 50px 0; */
	/* 	padding-bottom: 70px; */
	/* 	margin-bottom: 70px; */
	/*  */
	/* 	position: relative; */
}


.nav{
	padding-top:2px;
	text-align:center;
	font: italic 10px Times;
	/* font-family: Arial, Helvetica, sans-serif; */
	position:fixed;
	cursor:pointer;
	top:0px;
}


.funcmenu{
	border:thin solid silver; width:60px;height:22px;vertical-align: middle;
	cursor: pointer;
	color: #036;font: italic 10px sans-serif;
	position:absolute;
}

.funcmenu:hover {
/*   color: #FFF; */
/*   background: #dd137b; */

}

a.x {
	color:orange;
	cursor:pointer;
	}
a.surf {
	color:black;
	cursor:pointer;
	}
html, body
{ 
	font: italic 16px Arial, Helvetica, sans-serif;
	height:100%;
	margin:0;
	padding:0;
/* 	top:170; left:0; right:0; */
/* 	background-color: #2a1931; */
	/*;
	width:100%;*/
}


a:hover{
/* 	display:block; */
	text-decoration: underline;
	color:#dd137b;
/* background-repeat: no-repeat; */
/* background-image: url("images/alignator.gif"); */
}


p {margin:10px; font-size:16px;}


table, td, th
{
	padding:2px 7px;
	
	text-align:center;
	font: normal 12px Arial, Helvetica, sans-serif;
	/* border: thin solid black;; */
}

tfoot td
{
	border-top:thick solid #ECE9EA;
}


.login td{
	padding:1px;
		border: thin solid red;

}


.whitable th{
	border-bottom:3px solid black;
	font-weight:bold;
}
.whitable td{
	border-bottom:thin solid black;
}

.whitable tr{
	background-color: #FFFFFF;
	
}

.whitableNarrow th{
	padding: 0px 1px;
	border-bottom:3px solid black;
	font-weight:bold;
}
.whitable td{
	padding: 0px 1px;
	border-bottom:thin solid black;
}

td input {
	width:100%
	}


.ui-widget {
    font-size: 14px;
}



label.standardtext {
	font: normal 10px Arial, Helvetica, sans-serif;
	
	
}

.project_logo{max-height: 20px}
/* fin trucs utile ***************************** */
/*
.comments{
	
	padding:10px;
	width:95%;
	
	margin-top:50px;
	
	}
.comment{
	
	border-top:1px solid #DDDDDD;
	
	
	}
*/
/*
colpick Color Picker / colpick.com
*/

.color-box {
	float:left;
	width:50px;
	height:10px;
	margin-right:4px;
	margin-top:3px;
	border: 0px solid white;
}

/*Main container*/
.colpick {
	z-index: 5555;
	position: absolute;
	width: 346px;
	height: 170px;
	overflow: hidden;
	display: none;
	font-family: Arial, Helvetica, sans-serif;
	background:#ebebeb;
	border: 1px solid #bbb;
	-webkit-border-radius: 5px;
	-moz-border-radius: 5px;
	border-radius: 5px;
	
	/*Prevents selecting text when dragging the selectors*/
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	-o-user-select: none;
	user-select: none;
}
/*Color selection box with gradients*/
.colpick_color {
	position: absolute;
	left: 7px;
	top: 7px;
	width: 156px;
	height: 156px;
	overflow: hidden;
	outline: 1px solid #aaa;
	cursor: crosshair;
}
.colpick_color_overlay1 {
	position: absolute;
	left:0;
	top:0;
	width: 156px;
	height: 156px;
	-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff')"; /* IE8 */
	background: -moz-linear-gradient(left, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%); /* FF3.6+ */
	background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */
	background: -webkit-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ */
	background: -o-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Opera 11.10+ */
	background: -ms-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* IE10+ */
	background: linear-gradient(to right, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
	filter:  progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff'); /* IE6 & IE7 */
}
.colpick_color_overlay2 {
	position: absolute;
	left:0;
	top:0;
	width: 156px;
	height: 156px;
	-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00000000', endColorstr='#000000')"; /* IE8 */
	background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 100%); /* FF3.6+ */
	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(0,0,0,0)), color-stop(100%,rgba(0,0,0,1))); /* Chrome,Safari4+ */
	background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* Chrome10+,Safari5.1+ */
	background: -o-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* Opera 11.10+ */
	background: -ms-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* IE10+ */
	background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* W3C */
	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#000000',GradientType=0 ); /* IE6-9 */
}
/*Circular color selector*/
.colpick_selector_outer {
	background:none;
	position: absolute;
	width: 11px;
	height: 11px;
	margin: -6px 0 0 -6px;
	border: 1px solid black;
	border-radius: 50%;
}
.colpick_selector_inner{
	position: absolute;
	width: 9px;
	height: 9px;
	border: 1px solid white;
	border-radius: 50%;
}
/*Vertical hue bar*/
.colpick_hue {
	position: absolute;
	top: 6px;
	left: 175px;
	width: 19px;
	height: 156px;
	border: 1px solid #aaa;
	cursor: n-resize;
}
/*Hue bar sliding indicator*/
.colpick_hue_arrs {
	position: absolute;
	left: -8px;
	width: 35px;
	height: 7px;
	margin: -7px 0 0 0;
}
.colpick_hue_larr {
	position:absolute;
	width: 0; 
	height: 0; 
	border-top: 6px solid transparent;
	border-bottom: 6px solid transparent;
	border-left: 7px solid #858585;
}
.colpick_hue_rarr {
	position:absolute;
	right:0;
	width: 0; 
	height: 0; 
	border-top: 6px solid transparent;
	border-bottom: 6px solid transparent; 
	border-right: 7px solid #858585; 
}
/*New color box*/
.colpick_new_color {
	position: absolute;
	left: 207px;
	top: 6px;
	width: 60px;
	height: 27px;
	background: #f00;
	border: 1px solid #8f8f8f;
}
/*Current color box*/
.colpick_current_color {
	position: absolute;
	left: 277px;
	top: 6px;
	width: 60px;
	height: 27px;
	background: #f00;
	border: 1px solid #8f8f8f;
}
/*Input field containers*/
.colpick_field, .colpick_hex_field  {
	position: absolute;
	height: 20px;
	width: 60px;
	overflow:hidden;
	background:#f3f3f3;
	color:#b8b8b8;
	font-size:12px;
	border:1px solid #bdbdbd;
	-webkit-border-radius: 3px;
	-moz-border-radius: 3px;
	border-radius: 3px;
}
.colpick_rgb_r {
	top: 40px;
	left: 207px;
}
.colpick_rgb_g {
	top: 67px;
	left: 207px;
}
.colpick_rgb_b {
	top: 94px;
	left: 207px;
}
.colpick_hsb_h {
	top: 40px;
	left: 277px;
}
.colpick_hsb_s {
	top: 67px;
	left: 277px;
}
.colpick_hsb_b {
	top: 94px;
	left: 277px;
}
.colpick_hex_field {
	width: 68px;
	left: 207px;
	top: 121px;
}
/*Text field container on focus*/
.colpick_focus {
	border-color: #999;
}
/*Field label container*/
.colpick_field_letter {
	position: absolute;
	width: 12px;
	height: 20px;
	line-height: 20px;
	padding-left: 4px;
	background: #efefef;
	border-right: 1px solid #bdbdbd;
	font-weight: bold;
	color:#777;
}
/*Text inputs*/
.colpick_field input, .colpick_hex_field input {
	position: absolute;
	right: 11px;
	margin: 0;
	padding: 0;
	height: 20px;
	line-height: 20px;
	background: transparent;
	border: none;
	font-size: 12px;
	font-family: Arial, Helvetica, sans-serif;
	color: #555;
	text-align: right;
	outline: none;
}
.colpick_hex_field input {
	right: 4px;
}
/*Field up/down arrows*/
.colpick_field_arrs {
	position: absolute;
	top: 0;
	right: 0;
	width: 9px;
	height: 21px;
	cursor: n-resize;
}
.colpick_field_uarr {
	position: absolute;
	top: 5px;
	width: 0; 
	height: 0; 
	border-left: 4px solid transparent;
	border-right: 4px solid transparent;
	border-bottom: 4px solid #959595;
}
.colpick_field_darr {
	position: absolute;
	bottom:5px;
	width: 0; 
	height: 0; 
	border-left: 4px solid transparent;
	border-right: 4px solid transparent;
	border-top: 4px solid #959595;
}
/*Submit/Select button*/
.colpick_submit {
	position: absolute;
	left: 207px;
	top: 149px;
	width: 130px;
	height: 22px;
	line-height:22px;
	background: #efefef;
	text-align: center;
	color: #555;
	font-size: 12px;
	font-weight:bold;
	border: 1px solid #bdbdbd;
	-webkit-border-radius: 3px;
	-moz-border-radius: 3px;
	border-radius: 3px;
}
.colpick_submit:hover {
	background:#f3f3f3;
	border-color:#999;
	cursor: pointer;
}

/*full layout with no submit button*/
.colpick_full_ns  .colpick_submit, .colpick_full_ns .colpick_current_color{
	display:none;
}
.colpick_full_ns .colpick_new_color {
	width: 130px;
	height: 25px;
}
.colpick_full_ns .colpick_rgb_r, .colpick_full_ns .colpick_hsb_h {
	top: 42px;
}
.colpick_full_ns .colpick_rgb_g, .colpick_full_ns .colpick_hsb_s {
	top: 73px;
}
.colpick_full_ns .colpick_rgb_b, .colpick_full_ns .colpick_hsb_b {
	top: 104px;
}
.colpick_full_ns .colpick_hex_field {
	top: 135px;
}

/*rgbhex layout*/
.colpick_rgbhex .colpick_hsb_h, .colpick_rgbhex .colpick_hsb_s, .colpick_rgbhex .colpick_hsb_b {
	display:none;
}
.colpick_rgbhex {
	width:282px;
}
.colpick_rgbhex .colpick_field, .colpick_rgbhex .colpick_submit {
	width:68px;
}
.colpick_rgbhex .colpick_new_color {
	width:34px;
	border-right:none;
}
.colpick_rgbhex .colpick_current_color {
	width:34px;
	left:240px;
	border-left:none;
}

/*rgbhex layout, no submit button*/
.colpick_rgbhex_ns  .colpick_submit, .colpick_rgbhex_ns .colpick_current_color{
	display:none;
}
.colpick_rgbhex_ns .colpick_new_color{
	width:68px;
	border: 1px solid #8f8f8f;
}
.colpick_rgbhex_ns .colpick_rgb_r {
	top: 42px;
}
.colpick_rgbhex_ns .colpick_rgb_g {
	top: 73px;
}
.colpick_rgbhex_ns .colpick_rgb_b {
	top: 104px;
}
.colpick_rgbhex_ns .colpick_hex_field {
	top: 135px;
}

/*hex layout*/
.colpick_hex .colpick_hsb_h, .colpick_hex .colpick_hsb_s, .colpick_hex .colpick_hsb_b, .colpick_hex .colpick_rgb_r, .colpick_hex .colpick_rgb_g, .colpick_hex .colpick_rgb_b {
	display:none;
}
.colpick_hex {
	width:206px;
	height:201px;
}
.colpick_hex .colpick_hex_field {
	width:72px;
	height:25px;
	top:168px;
	left:80px;
}
.colpick_hex .colpick_hex_field div, .colpick_hex .colpick_hex_field input {
	height: 25px;
	line-height: 25px;
}
.colpick_hex .colpick_new_color {
	left:9px;
	top:168px;
	width:30px;
	border-right:none;
}
.colpick_hex .colpick_current_color {
	left:39px;
	top:168px;
	width:30px;
	border-left:none;
}
.colpick_hex .colpick_submit {
	left:164px;
	top: 168px;
	width:30px;
	height:25px;
	line-height: 25px;
}

/*hex layout, no submit button*/
.colpick_hex_ns  .colpick_submit, .colpick_hex_ns .colpick_current_color {
	display:none;
}
.colpick_hex_ns .colpick_hex_field {
	width:80px;
}
.colpick_hex_ns .colpick_new_color{
	width:60px;
	border: 1px solid #8f8f8f;
}

/*Dark color scheme*/
.colpick_dark {
	background: #161616;
	border-color: #2a2a2a;
}
.colpick_dark .colpick_color {
	outline-color: #333;
}
.colpick_dark .colpick_hue {
	border-color: #555;
}
.colpick_dark .colpick_field, .colpick_dark .colpick_hex_field {
	background: #101010;
	border-color: #2d2d2d;
}
.colpick_dark .colpick_field_letter {
	background: #131313;
	border-color: #2d2d2d;
	color: #696969;
}
.colpick_dark .colpick_field input, .colpick_dark .colpick_hex_field input {
	color: #7a7a7a;
}
.colpick_dark .colpick_field_uarr {
	border-bottom-color:#696969;
}
.colpick_dark .colpick_field_darr {
	border-top-color:#696969;
}
.colpick_dark .colpick_focus {
	border-color:#444;
}
.colpick_dark .colpick_submit {
	background: #131313;
	border-color:#2d2d2d;
	color:#7a7a7a;
}
.colpick_dark .colpick_submit:hover {
	background-color:#101010;
	border-color:#444;
}
/**
 * jQuery Lined Textarea Plugin
 *   http://alan.blog-city.com/jquerylinedtextarea.htm
 *
 * Copyright (c) 2010 Alan Williamson
 *
 * Released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 * 
 * Usage:
 *   Displays a line number count column to the left of the textarea
 *   
 *   Class up your textarea with a given class, or target it directly
 *   with JQuery Selectors
 *   
 *   $(".lined").linedtextarea({
 *   	selectedLine: 10,
 *    selectedClass: 'lineselect'
 *   });
 *
 */

.linedwrap {
	border: 1px solid #c0c0c0;
	padding: 3px;
}

.linedtextarea {
	padding: 0px;
	margin: 0px;
}

.linedtextarea textarea, .linedwrap .codelines .lineno {
	font-size: 10pt;
	font-family: monospace;
	line-height: normal !important;
}

.linedtextarea textarea {
	padding-right:0.3em;
	padding-top:0.3em;
	border: 0;
}

.linedwrap .lines {
	margin-top: 0px;
	width: 50px;
	float: left;
	overflow: hidden;
	border-right: 1px solid #c0c0c0;
	margin-right: 10px;
}

.linedwrap .codelines {
	padding-top: 5px;
}

.linedwrap .codelines .lineno {
	color:#AAAAAA;
	padding-right: 0.5em;
	padding-top: 0.0em;
	text-align: right;
	white-space: nowrap;
}

.linedwrap .codelines .lineselect {
	color: red;
}
/*
 * jQuery UI CSS Framework 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Theming/API
 */

/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
.ui-helper-clearfix:after { clear: both; }
.ui-helper-clearfix { zoom: 1; }
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }


/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }


/* Icons
----------------------------------*/

/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }


/* Misc visuals
----------------------------------*/

/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }


/*
 * jQuery UI CSS Framework 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Theming/API
 *
 * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=000000&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=000000&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=ece9ea&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=75&borderColorContent=aeb2b7&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=654b6d&fcDefault=555555&iconColorDefault=654b6d&bgColorHover=dd137b&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=654b6d&fcHover=212121&iconColorHover=000000&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=654b6d&fcActive=212121&iconColorActive=dd137b&bgColorHighlight=b9b0a6&bgTextureHighlight=05_inset_soft.png&bgImgOpacityHighlight=55&borderColorHighlight=654b6d&fcHighlight=201f1e&iconColorHighlight=654b6d&bgColorError=fef1ec&bgTextureError=05_inset_soft.png&bgImgOpacityError=95&borderColorError=dd137b&fcError=dd137b&iconColorError=dd137b&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
 */


/* Component containers
----------------------------------*/
.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #aeb2b7; background: #ece9ea url(images/ui-bg_highlight-soft_75_ece9ea_1x100.png) 50% top repeat-x; color: #222222; }
.ui-widget-content a { color: #222222; }
.ui-widget-header { border: 1px solid #000000; background: #000000 url(images/ui-bg_highlight-soft_75_000000_1x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
.ui-widget-header a { color: #ffffff; }

/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #654b6d; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #654b6d; background: #dd137b url(images/ui-bg_glass_75_dd137b_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #654b6d; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; }
.ui-widget :active { outline: none; }

/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight  {border: 1px solid #654b6d; background: #b9b0a6 url(images/ui-bg_inset-soft_55_b9b0a6_1x100.png) 50% bottom repeat-x; color: #201f1e; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #201f1e; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #dd137b; background: #fef1ec url(images/ui-bg_inset-soft_95_fef1ec_1x100.png) 50% bottom repeat-x; color: #dd137b; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #dd137b; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #dd137b; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary,  .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }

/* Icons
----------------------------------*/

/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_654b6d_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_dd137b_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_654b6d_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_dd137b_256x240.png); }

/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }


/* Misc visuals
----------------------------------*/

/* Corner radius */
.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }

/* Overlays */
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
 * jQuery UI Button 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Button#theming
 */
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: hidden; *overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
.ui-button-icons-only { width: 3.4em; } 
button.ui-button-icons-only { width: 3.7em; } 

/*button text element */
.ui-button .ui-button-text { display: block; line-height: 1.4;  }
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }

/*button icon element(s) */
.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }

/*button sets*/
.ui-buttonset { margin-right: 7px; }
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }

/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
/*
 * jQuery UI Dialog 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Dialog#theming
 */
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative;  }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } 
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/*
 * jQuery UI Progressbar 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Progressbar#theming
 */
.ui-progressbar { height:2em; text-align: left; overflow: hidden; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
@charset 'UTF-8';
/*
 * jQuery File Upload UI Plugin CSS 6.3
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

.fileinput-button {
  position: relative;
  overflow: hidden;
  float: left;
  margin-right: 5px;
}
.fileinput-button input {
  position: absolute;
  top: 0;
  right: 0;
  margin: 0;
  border: solid transparent;
  border-width: 0 0 100px 200px;
  opacity: 0;
  filter: alpha(opacity=0);
  -moz-transform: translate(-300px, 0) scale(4);
  direction: ltr;
  cursor: pointer;
}
.fileupload-buttonbar .ui-button,
.fileupload-buttonbar .toggle {
  margin-bottom: 5px;
}
.files .progress {
  width: 200px;
}
.files .progress,
.fileupload-buttonbar .progress {
  height: 20px;
}
.files .ui-progressbar-value,
.fileupload-buttonbar .ui-progressbar-value {
  background: url(../images/progressbar.gif);
}
.fileupload-buttonbar .fade,
.files .fade {
  display: none;
}
.fileupload-loading {
  position: absolute;
  left: 50%;
  width: 128px;
  height: 128px;
  background: url(../images/loading.gif) center no-repeat;
  display: none;
}
.fileupload-processing .fileupload-loading {
  display: block;
}

/* Fix for IE 6: */
*html .fileinput-button {
  margin-right: -2px;
}
*html .fileinput-button .ui-button-text {
  line-height: 24px;
}
*html .fileupload-buttonbar .ui-button {
  margin-left: 3px;
}

/* Fix for IE 7: */
*+html .fileinput-button {
  margin-right: 1px;
}
*+html .fileinput-button .ui-button-text {
  line-height: 24px;
}
*+html .fileupload-buttonbar .ui-button {
  margin-left: 3px;
}

@media (max-width: 480px) {
  .files .preview * {
    width: 40px;
  }
  .files .name * {
    width: 80px;
    display: inline-block;
    word-wrap: break-word;
  }
  .files .progress {
    width: 20px;
  }
  .files .delete {
    width: 60px;
  }
}

/* Fix for Webkit (Safari, Chrome) */
@media screen and (-webkit-min-device-pixel-ratio:0) {
  .fileinput-button {
    margin-top: 2px;
  }
}
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2018 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

import os, cgitb, cgi,time, sys, glob, codecs
from time import asctime, localtime
from lib import config, database
sys.path.append('modules')
from logintools import isloggedin
cgitb.enable()



##############################################################################################"

def start():
	
	thisfile = os.environ.get('SCRIPT_NAME',".")
	loggedin = isloggedin('users/')
	projectconfig=None
	
	if loggedin:
		#print loggedin
		return thisfile, loggedin[0]["username"].decode("utf-8"), int(loggedin[0]["admin"])		
	else:
		#print "user not logged in"
		return thisfile,None, None
		


def xxxdatabase():
	form = cgi.FieldStorage()
	project = (form.getvalue("project",None))
	print 5445465,project
	if project: project =project.decode("utf-8")
	
	try:	
		projectconfig = config.configProject(project) # read in the settings of the current project
	except:
		print "Error! Can't read the configuration files. Please check that the files project.cfg, functions.cfg, and categories.cfg are present in the project folder	"
		sys.exit("something's wrong")

	try:
		sql=database.SQL(project)
		userid = sql.userid(username, realname)
	except Exception , e:
		from sqlite3 import OperationalError
		if isinstance(e, OperationalError) and str(e)in["attempt to write a readonly database","unable to open database file"]:
			print "Error! Make your database and the path leading there from the arborator base modifiable by the apache user!"
		if isinstance(e, KeyError):
			print "Error! the configuration file does not contain all the necessary keys:",str(e)
		else:
			print "strange error accessing the database.<br>",str(e)
			import traceback
			
			traceback.print_exc()
		sys.exit("something's wrong")
	return project,projectconfig,sql,thisfile,username,userid,adminLevel,form
	
	
	
#def export(textname, exportNumber, exportType, project):
	##textname=textname.encode("utf-8")
	#print "<h2>Exporting",textname.encode("utf-8"),"(text number",exportNumber,")</h2>"
	
	#if textname:
		#fc,users,sc,doublegovs=sql.exportAnnotations(int(exportNumber), textname, exportType )
		#if fc==0: 	print "<div style='padding:10;' class='ui-state-error ui-corner-all'>No files were exported, probably because the file is assigned to nobody.</div><br/>"
		#else:		
			#print "Exported",fc,"files and a total of",sc,"sentences.<br/><br/>"
			#if exportType in ["allconll","allxml"]:
				#print "Exported all existing annotated trees into",
				#if exportType =="allconll": print textname.encode("utf-8")+".user.trees.conll files.<br/>"
				#else: print textname.encode("utf-8")+".user.trees.rhaps.xml files.<br/>"
			#else: 
				#print "Exported complete files for each assignment into"
				#if exportType =="todoconll": print textname.encode("utf-8")+".user.complete.conll10 files.<br/>"
				#else: print textname.encode("utf-8")+".user.complete.rhaps.xml files.<br/>"
		#print "<br/><div style='padding:10;'  class='ui-state-highlight ui-corner-all'>",
		#if fc==1:print "The export file for",users[0],"is"
		#else: print "All the",fc,"export files (for",(", ".join(users)).encode("utf-8")+") are"
		#print " in the export folder <strong style='color:#DD137B'><a href='projects/{project}/export' target='_blank'>inside the project folder</a></strong> on the server.</div><br/>".format(project=project.replace("'","\\'").replace('"','\\"').encode("utf-8"))
		#if doublegovs: print "<div style='padding:10;' class='ui-state-error ui-corner-all'>Achtung!<br/> The annotation contains multiple governors for one or more nodes. The lines have been doubled, and thus this is not a common Conll format!</div>"
	#else:
		#print "problem: no textname"
	
#def reaction(project,projectconfig,sql,userid,form,query):
	#"""
	#contains reaction to actions, eg. uploading, erasing, exporting, ...
	#"""
	#filename = form.getvalue("filename",None)
	#if filename: # upload file	
		#from treebankfiles import uploadConll
		#filetype = form.getvalue("filetype",None)
		#print "trying to upload",filename,filetype,"into the database of the project",project.encode("utf-8")
		#if filetype in ["conll4","conll10","conll14"]:
			#print "---------- here we go"
			#nrsentences = uploadConll(sql,filename)
			#print """<div class="ui-widget ui-widget-content ui-corner-all box">I added {nrsentences} sentences from {filename}.</div>""".format(nrsentences=nrsentences,filename=filename)
		#else:	print """<div class="ui-widget ui-widget-content ui-corner-all box">Strange filetype: {filename}.</div>""".format(filename=filename)

	#eraseNumber = form.getvalue("eraseNumber",None)
	#if eraseNumber: # erase text
		#print "erasing text number ",eraseNumber,"<br/>"
		#sc=sql.removeText(int(eraseNumber))
		#print "erased",sc,"sentences."

	##statustextid = form.getvalue("statustextid",None)
	##if statustextid: # erase text
		###print "statustextid, text number:",statustextid,"<br/>"
		##sql.statustext(int(statustextid),userid)

	#exportNumber = form.getvalue("exportNumber",None)
	#textname=None
	#if exportNumber: # export text
		#print '<div class="ui-widget ui-widget-content ui-corner-all box" >'
		
		#textname = form.getvalue("textname",None)
		#exportType = form.getvalue("exportType",None)
		##print exportNumber,type(exportNumber)
		#exportNumber=int(exportNumber)
		#if exportNumber==-1 :
			#for t,tid in sorted( [(t,tid) for tid,t,nrtokens in sql.getall(None, "texts", None, None)]):
				#export(t, tid, exportType, project)
			
		#else: export(textname, exportNumber, exportType, project)
		
		#print '</div>'

		
	#userchoice = form.getlist('userchoice')
	#textid = form.getvalue("textid",None)

	#if userchoice and textid: # change todos
		#removeid = int(form.getvalue("removeid",0))
		#validator = int(form.getvalue("validator",0))
		#if removeid:
			#sql.removetextfromuser(int(textid),removeid)
		#else:
			#u=userchoice[0].split("(")[-1][:-1]
			#sql.addtext2user(textid,u,validator)
			
	
	#exochoice = form.getvalue("exochoice",None)
	#if exochoice:
		
		#textid = form.getvalue("textid",None)
		#exotoknum = form.getvalue("exotoknum",None)
		
		#print "changing exostatus",textid,exochoice
		#sql.setExo(textid, exochoice, exotoknum)
		
	
	#if query: # search results	
		#print """<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;">"""
		
		#res = sql.snippetSearch(query)
		#if res: 
			#print "<h2>Search results for {query}:</h2>".format(query=query)
			#print res.encode("utf-8")
		#else: print "no results for {query}</h2>".format(query=query)
		#print """</div>"""
		
	




#def printhtmlheader(project):
	
	#print """<html>
			#<head>
			#<title>Arborator - {project} Project</title>
	#<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
	#<script type="text/javascript" src="script/jquery.js"></script>
	#<script type="text/javascript" src="script/jquery-ui-1.8.18.custom.min.js"></script>
	#<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen"/>
	#<link href="css/arborator.css" rel="stylesheet" type="text/css">
	#<script type="text/javascript">	
	#project="{project}";
	#userid="{userid}";
	#username="{username}";
	#""".format(project=project,userid=userid,username=username.encode("utf-8"))
	
	#print """
	#$('html').click(function() {
		#$("#exportdiv").css({ display: 'none' } );	
		#$("#userform").css({ display: 'none' } );	
		#$("#exoform").css({ display: 'none' } );	
		#});

	#edit = function(textid,opensentence) {
		#$("#textid").attr("value", textid );
		#$("#opensentence").attr("value", opensentence );
		#$("#editorform").submit();
	#}
	#removeSample = function(textid, text) {
		#$("#eraseNumber").attr("value",textid);
		#$("#question").html("Are you sure that you want to erase the text "+text+" from the database?")
		#$('#dialog').dialog('open');
		#}
	#exportAnnos = function(textid, text, offset) {
		#$("#exportNumber").attr("value",textid);
		#$("#textname").attr("value",text);
		#offset.left=offset.left-280;
		#$("#exportdiv").offset(offset).css({ display: 'inline' } );
		#}	
	#exportGo = function(type) {
		#$("#exportType").attr("value",type);
		#console.log($("#export"))
		#$("#export").submit();
		#$("#dialog").html("<img src='images/loading.gif'>");
		#$("#dialog").dialog({
			#height: 230,
 			#width:150,
			#modal: true,
			#title:"Please wait!!!<br>This can be long.",
			#buttons: {}
			#});
		#$('#dialog').dialog('open');
	#}
	#adduser = function(textid, text, offset) {
		#$("#utextid").attr("value",textid);
		#$("#uremove").attr("value",0);
		#$("#userform").css({ display: 'block',top: offset.top, left: offset.left } );
		#}
		
	#userchoice = function(v) {
		#$("#validator").attr("value",v);
		#$("#uremove").attr("value",0);
				
		#$("#useraddremove").submit();	
		#}
	
	#userremove = function(tid,uid) {	
		#$("#utextid").attr("value",tid);
		#$("#uremove").attr("value",uid);
		#$("#useraddremove").submit();
		#}
	#nextstatus = function(tid) {
		#//console.log("nextstatus",tid)	
	
		#$.ajax({
			#type: "POST",
			#url: "statusChange.cgi",
			#data: {"project":project,"userid":userid,"username":username,"textid":tid}, 
			#success: function(answer){
					#$("#textStatus"+tid).html(answer.status);
					#//console.log("changed!",answer);
					
				#},
			#error: function(XMLHttpRequest, textStatus, errorThrown){
				#console.log("error",project,erasetreeid)
				
				#}
			#});
	
		#}	
	
	
	
	#nl2br = function(str) {   
		#return (str + '').replace(/([^>\\r\\n]?)(\\r\\n|\\n\\r|\\r|\\n)/g, '$1'+ '<br>' +'$2');
	#}

	#startdepeditdatabase = function() {
		#//$("#startdepeditdatabaseform :input").prop('disabled', true);
		#//checkdepeditdatabase();
		#$("#parseStatus").css('background-image', 'url("images/ajax-loader.gif")');  
		#$.ajax({
			#type: "POST",
			#url: "mate.cgi",
			#data: {"project":project,"parserType":$( "#parserType" ).val(), "whoseTrees":$( "#whoseTrees" ).val(), "evaluationPercent":$( "#evaluationPercent" ).val(), "additionnalLexicon":$( "#additionnalLexicon" ).val(), "resultAnnotator":$( "#resultAnnotator" ).val()}, 
			#success: function(answer){
					#$("#parseStatus").html(answer);
					#console.log("finished!",answer);
					#//clearInterval(timer);
				#},
			#error: function(XMLHttpRequest, textStatus, errorThrown){
				#console.log("mate error",project)
				
				#}
			#});
	
		#timer = setInterval(checkdepeditdatabase, 5000);
		#}
	
	#checkdepeditdatabase = function() {
		#$.ajax({
			#url : "mate/parse.log",
			#dataType: "text",
			#cache: false,
			#processData: false,
			#success : function (data) {
				#$("#parseStatus").html(nl2br(data));
				#if (data.match("^Ready"))
					#{
						#console.log("Done");
						#clearInterval(timer);
						#$("#parseStatus").css('background-image', 'none'); 
						#//$("#startdepeditdatabaseform :input").prop('disabled', false);
					#}
				#}
			#});
			

			
		#}
	
	
	#$(function () {
		#$("#dialog").dialog({
			#bgiframe: true,
			#autoOpen: false,
			#height: 350,
 			#width:400,
			#modal: true,
			#buttons: {
				
				#Cancel: function() {
					#$(this).dialog('close');
				#},
				#"OK": function() {
					#$(this).dialog('close');					
					#$("#erase").submit();
				#}
				#}
			#});
			
		#$('#exochoice').change(function ()
			#{
				#$('#exochange').submit();
			#});	
			
		

	#})
	

	#getEvaluation = function(project,uid) {
		#$("#evalubutton"+uid).replaceWith("<img id='evalubutton"+uid+"' src='images/ajax-loader.gif'>");
		#$.ajax({
			#type: "GET",
			#url: "getEvaluation.cgi",
			#data: {"project":project,"uid":uid}, 
			#success: function(answer){
					#$("#evalubutton"+uid).replaceWith(answer);
					#//console.log("got!"+answer);
			
				#},
			#error: function(XMLHttpRequest, textStatus, errorThrown){
				#console.log("error",project)
				#alert("error erasing"+XMLHttpRequest+ '\\n'+textStatus+ "\\n"+errorThrown);
				#}
			#});
		
	#}
	
	#</script>
	
	#</head>
	#"""
	
	
	
#def printheadline(project):
	#if os.path.exists("projects/"+project+"/"+project+".png"):img="<img src='projects/"+project+"/"+project+".png' align='top' height='18'>"
	#else: img=""
	#query = form.getvalue("searchtext","")
	#print 	"""<body id="body">
			#<div id="center" class="center" style="width:100%">
				#<div id="navigation" style="width:100%;margin:0px;border:0px;" class="arbortitle  ui-widget-header ui-helper-clearfix">
				#<a href='.' style='position: fixed;left:1px;top:1px'><img src="images/arboratorNano.png" border="0"></a>
				#<a href="project.cgi?project={project}" style='position: fixed;left:120px;top:5px;color:white;' title="project overview">{img} {project} Annotation Project</a>
				#<div style='margin:5 auto;' id='sentinfo'>Project Overview</div>
					#<form method="get" action="project.cgi"  id="searchform"  style='position: fixed;right:1px;top:1px'>
						#<input type="text" id="searchtext" name="searchtext" value="{query}"/>
						#<input type="hidden" id="project" name="project" value="{project}">
						#<span title="search" onClick="$('#searchform').submit();" style="cursor:pointer;" class="ui-button-icon-primary ui-icon ui-icon-search"></span>
					#</form>
				#</div>
		#""".format(project=project,img=img,query=query)
	#if os.path.exists("sitemessage.html"):print open("sitemessage.html").read()
	#return query


		

#def printmyassignments(project,sql,projectconfig,userid): # assigned texts
	
	#txt = u"""
	
	#<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;">
	#You have been assigned the following texts of the <b>{project}</b> Project<br/>
	#""".format(project=project)

	#txt += u"""Please select the text to annotate:<br/><br/><table class="whitable">"""
	#txt += u"	<tr><td>text name</td><td>number of sentences</td><td>number of tokens</td><td>trees modified by you</td><td>status</td></tr>"
	#scount,mcount,tcount=0,0,0
	#for _,_,tid,todotype,status,comment in sql.getall(None, "todos",["userid"], [userid]):
		#try:
			#tid,t,nrtokens = sql.getall(None, "texts",["rowid"],[tid])[0]
		#except:
			#sql.removeText(tid)
			#print "we found a todo for a non existing text and removed it."
			#continue
		#try: todotype=int(todotype)
		#except: todotype=0
		
		
		#if todotype:txt += u"	<tr><td><b><span class='val'>validation of <br></span><a  style='cursor:pointer;' onclick=edit('"+str(tid)+"',1)>"+ t +"</a></b></td>"
		#else:	txt += u"	<tr><td><b><a  style='cursor:pointer;' onclick=edit('"+str(tid)+"',1)>"+ t +"</a></b></td>"
		#sn=sql.getnumber(None, "sentences", ["textid"], [tid])
		#scount+=sn
		#txt += u"		<td>"+str(sn)+"</td>"
		#tn=sql.getNumberTokensPerText(tid)
		#tcount+=tn
		#txt += u"		<td>"+str(tn)+"</td>"
		#txt += u"		<td>&nbsp;"
		#mc = sql.getNumberTreesPerUserAndText(userid,tid)
		#mcount+=mc
		#txt += str(mc)
		#txt += u"</td>"
		#txt += u"		<td><a  style='cursor:pointer;dispay:block' id='textStatus"+str(tid)+"' "
		#txt+= "onclick=nextstatus('"+str(tid)+"')>"+ projectconfig["text status"].get(status,"ooooo") +"</a></td>" 
		#txt += u"</tr>"
	#txt += u"	<tr> </tr><tr><td>a total of:</td><td>{scount}</td><td>{tcount}</td><td>{mcount}</td></tr>".format(scount=scount,mcount=mcount,tcount=tcount)
	#txt += u"</table></div>"

	#if scount: print txt.encode("utf-8")
	##else: print u"""<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;">
	##You have not been assigned any text of the <b>{project}</b> Project<br/></div>
	##""".format(project=project).encode("utf-8")


#def printalltexts(project,sql,adminLevel):############################# all texts of the project
	#nrt,stotal,ttotal,tanno,tvali=sql.getNumberTexts(),0,0,0,0
	
	#print """
	#<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;" id='projectbox'>
	#The <b>{project}</b> Project has {nrt} texts and {nrs} sentences. <br/>
	#<br/>
	#<table class='whitable'>
	#<thead><tr><th>text name</th><th>number of sentences</th><th>number of tokens</th><th>sentence length</th><th >annotators</th><th>validator</th><th title='non sollicitated trees by users not assigned to this text are listed here.'>other trees</th><th>exo</th><th>&nbsp;</th><th>&nbsp;</th><th>&nbsp;</th></tr></thead>
	#""".format(project=project,nrt=nrt,nrs=sql.getNumberSentences())
	
	#tuples=[]
	#for t,tid,nrtokens in sorted( [(t,tid,nrtokens) for tid,t,nrtokens in sql.getall(None, "texts",None,None)]): # got to get it in alphabetical order .decode("utf-8")
		
		#c1= u"<b><a  style='cursor:pointer;dispay:block'  onclick=edit('{tid}',1)>{t}</a></b></td>".format(tid=tid, t=t)
		#sn=sql.getnumber(None, "sentences", ["textid"], [tid])
		#stotal+=sn
		#c2=str(sn)
		#if not nrtokens:	nrtokens=sql.getNumberTokensPerText(tid) # ,recompute=True
		##nrtokens=sql.getNumberTokensPerText(tid,recompute=True) # recomputes the token number
		#ttotal+=nrtokens
		#c3=str(nrtokens)
		#c4=str(round(float(nrtokens)/sn,2))
		#a,v = "",""
		#tannot,tvalit=0,0
		#reals=[]
		#for _,uid,tid,todotype,status,comment in sql.getall(None, "todos",["textid"], [tid], orderby="order by userid"):
			#try: todotype=int(todotype)
			#except: todotype=0
			#res= sql.getall(None, "users",["rowid"], [uid])
			#if res :
				#uid,user,realname = res[0]
				#if not realname:	realname=sql.realname(user)
				#av=u"<div  style='float:left;margin-right:10px;'>"+realname
				#if status!="0": 	av+= "<i><sub>"+ projectconfig["text status"].get(status,"ooo")+ "</i></sub>"
				#if adminLevel:	av+= "<div class='ui-button-icon-primary ui-icon ui-icon-circle-minus' style='float:right;cursor:pointer;margin-right:15px;'  onclick='var e=arguments[0];e.stopPropagation();userremove({tid},{uid});'></div>".format(tid=tid,uid=uid)
				#av+="</div>"
				#reals+=[realname]
				#if todotype: # validators
					#v+=av
					#tvalit+=1
				#else: # annotators
					#a+=av
					#tannot+=1		
		#if not a: a="<div style='float:left;margin-right:15px;'>nobody yet</div>"
		#if not v: v="<div style='float:left;margin-right:15px;'>nobody yet</div>" 
		#tanno+=tannot
		#tvali+=tvalit		
		#c5,c6=a,v
		
		#otherreals=[r for r, in sql.treesForText(tid) if r and r not in reals]
		##print "otherreals",otherreals, tid
		#if otherreals:	c7=" ".join(otherreals)
		#else:		c7="&nbsp;"
		#if adminLevel:
			## exo stuff:
			#et,esn = sql.getExo(tid)
			#et = sql.exotypes[et] # transform exotypenumber into text
			#if not esn: esns=""
			#else: esns=esn
			#c8= """<a style='cursor:pointer;' onclick='$("#exoform").val("{et}").css({{ top: $(this).offset().top-5, left: $(this).offset().left-5 }}).toggle();$("#exoform select").val("{et}");$("#exotoknum").val("{esn}");var e=arguments[0];e.stopPropagation();$("#etextid").val({tid})'>{et} {esns}</a>""".format(tid=tid,et=et,esn=esn,esns=esns)
			## user assignment:
			#c9 = u"""<div style='float:left;cursor:pointer;' title='asign this text to a user' onclick="var e=arguments[0];e.stopPropagation();adduser({tid},'{t}',{{ top: e.pageY, left: e.pageX }}); ">asign<div style='float:right;cursor:pointer;' class='ui-button-icon-primary ui-icon ui-icon-person' ></div></div>&nbsp;&nbsp;&nbsp; """.format(tid=tid,t=t)
			## export
			#c10 = u"""		<span style='cursor:pointer;'onclick="var e=arguments[0];e.stopPropagation();exportAnnos({tid},'{t}',$(this).offset());"  title='export annotations of this text' ><img src="images/dbexport.bw.png" border="0"></span>""".format(tid=tid,t=t)
		#else:
			#c8= sql.exotypes[sql.getExo(tid)[0]] # only exotype is shown
			#c9= "&nbsp;"			
			#c10 = """&nbsp;"""
			
		#if adminLevel>1:	c11= u"""		<span class='ui-button-icon-primary ui-icon ui-icon-trash' style='cursor:pointer;' onclick="var e=arguments[0]; e.stopPropagation(); removeSample({tid},'{t}');"  title='remove all sentences from this file from database' ></span>""".format(tid=tid,t=t)
		#else: 		c11= "&nbsp;"

		#tuples+=[(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11)]
	
	#for tup in tuples:
		#print ('<tr><td>' + u"</td><td>".join(tup) + "</td></tr>").encode('utf-8')
	
	
	#if adminLevel:allexport="""<span style='cursor:pointer;'onclick="var e=arguments[0];e.stopPropagation();exportAnnos(-1,'all',$(this).offset());"  title='export all annotations - very slow! blocks the database!' ><img src="images/dbexport.bw.png" border="0"></span>"""
	#else:allexport=""
	#evallinkhtml = '<a href="getEvaluation.cgi?project='+project+'">evaluation</a>'+' <a href="getEvaluation.cgi?project='+project+'&csv=csv">csv</a>'
	#nvs=sql.getNumberValidatedSentences()
	#nvt=sql.getNumberValidatedTokens()
	
	#if nrt: # at least some texts exist
		#if tanno: annota="{tanno} annotation assignments<br> (an average of {ava:.2f} assignments per text)".format(tanno=tanno,ava=float(tanno)/nrt)
		#else: annota="&nbsp;"
		#if tvali:	validata="{tvali} validation assignments <br> (an average of {avv:.2f} validators per text)".format(tvali=tvali,avv=float(tvali)/nrt)
		#else:	validata="&nbsp;"
		
		#if stotal:	avsentlen=round(float(ttotal)/stotal,2)
		#else:		avsentlen="n/a"
		#if nvs:		avsentlenval=round(float(nvt)/nvs,2)
		#else:		avsentlenval="n/a"
		
		#print """<TFOOT>	<tr style='padding-top:20;'><td>{nrt} texts</td><td>{stotal} sentences<br>{nvs} validated</td><td>{ttotal} tokens<br>{nvt} validated</td><td>{avsentlen}<br>{avsentlenval} validated</td><td>{annota}</td><td>{validata}</td><td>&nbsp;</td><td>{evallinkhtml}</td><td>&nbsp;</td><td>{allexport}&nbsp;</td><td>&nbsp;</td></tr> 	</TFOOT>""".format(stotal=stotal, nvs=nvs, nvt=nvt, nrt=nrt, ttotal=ttotal,avsentlen=avsentlen, avsentlenval=avsentlenval, annota=annota,validata=validata,evallinkhtml=evallinkhtml if adminLevel else "&nbsp;", allexport=allexport)
	#print "</table></div>"


	
#def printuserassignments(project,projectconfig,sql,adminLevel, lastseens):
	#parser=projectconfig["configuration"]["importAnnotatorName"]
	#teacher=projectconfig["configuration"].get("teacher",None)
	#if teacher:
		#teacherid=sql.userid(teacher)
	
	##scount,tcount=0,0
	
	
	#tablecontent=""
	#something=False
	#for uid,u,rea in sql.getall(None, "users",None,None, orderby="order by realname"):
		#if u!=parser:
			#textids=[]
			#treeidlists=[]
			#totaltokens=[]
			#titles=[]
			##sentenceidlists=[]
			#txt=""
			
			#sc,tc,tnrtrees,lastmodif=0,0,0,[]
			#for _,uid,tid,todotype,status,comment in sql.getall(None, "todos",["userid"], [uid]): # for all the assignments (texts) for the given user
				#try:	todotype=int(todotype)
				#except: 	todotype=0
				#textids+=[tid]
				#status=projectconfig["text status"].get(status,"?")
				#tid,t,nrtokens = sql.getall(None, "texts",["rowid"],[tid])[0]
				#sn=sql.getnumber(None, "sentences", ["textid"], [tid])
				
				#treenrs=sql.treeNrsForUserAndText(uid,tid)
				#trees=sorted([(int(n),asctime(localtime(o))) for (sid,n,o,trid,) in treenrs])
				#treeidlists+=[ [ (trid,sid) for (sid,n,o,trid,) in treenrs] ]
				#nrtrees=len(treenrs)
				#lastmodif+=[o for (sid,n,o,trid,) in treenrs]
				
				
				#if sn==nrtrees:	trees="All"
				#else:		trees=str(trees).replace("(","\n").replace("),","").replace("'","").replace(",",":").replace(")","")[1:-1] or "None"
				
				#tn=nrtokens
				#sc+=sn
				#tc+=tn
				#totaltokens+=[tn]
				#titles+=[t]
				#tnrtrees+=nrtrees
				#if todotype:	txt+= "<span class='val'>"
				#txt+= """			<span title="modified trees: {trees}">{t} (<span class='val'>{nrtrees}</span>/{sn}/{tn}/{status})</span>&nbsp;&nbsp;""".format(t=t.encode("utf-8"),sn=sn,tn=tn,status=status,nrtrees=nrtrees,trees=trees)
				#if todotype:	txt+= "</span>  "
				#something=True
			#if sc:	
				#tablecontent+= "	<tr ><td><b>{u}</b></td><td>".format(u=u)
				#tablecontent+= txt
				#avt="<span class='val'>{tnrtrees} ({avt:.1f}%)</span>".format(tnrtrees=tnrtrees,avt=float(tnrtrees)/sc*100)
			#else:	
				#continue
				#tablecontent+= "uuu"
				#avt=" 0"
			#if lastmodif:		lastmodif=asctime(localtime(sorted(lastmodif)[-1]))
			#if sc:			lastseen=asctime(localtime(lastseens.get(u,(0,0))[0]))
			#else:			lastseen= "&nbsp;"
			#tablecontent+= "			&nbsp;</td><td><b>{avt}</td><td>{sc}/{tc}<td>{lastseen}</td><td>{lastmodif}</td></b></td></tr>".format(tnrtrees=tnrtrees,avt=avt  ,sc=sc,tc=tc, lastseen=lastseen, lastmodif=lastmodif or "&nbsp;")	
			
			#if teacher:
				
				#tablecontent+= "<tr><td>{rea} </td><td colspan=5>".format(rea=rea.encode("utf-8"))
			
				#tablecontent+= '''<input type="button" onclick="getEvaluation('{project}',{uid});" class="fg-button ui-state-default ui-corner-all" style="width:155px;z-index:33;" value="get {rea}'s evaluation" name="evalubutton{uid}" id="evalubutton{uid}" cursor="pointer">'''.format(project=project,uid=uid, rea=rea.encode("utf-8"))
				#tablecontent+= "</td></tr>"
				#tablecontent+= "<tr><td colspan=6 style='padding-bottom:20;'></td></tr>"
				##break
	#if something:
		#print """
		#<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;">
		#The assignments of the <b>{project}</b> Project<br/>
		#""".format(project=project)
		#print """<br/><table class='whitable'>"""
		#print """	<tr>
			#<td>user name</td>
			#<td>assigned texts: <br> text name (trees modified by user, total number of sentences, total number of tokens, status)</td>
			#<td>total number of modified trees</td>
			#<td>total number of sentences / <br>  total number of tokens</td> 
			#<td>last seen</td> <td>last modification</td> """
		##if teacher: print "<td>result</td>"
		#print "	</tr>"
		#print tablecontent
		
		#print "</table></div>"
		
	#if adminLevel>1:
		#print """<div class="ui-widget ui-widget-content ui-corner-all box">
	#<a href="uploader.cgi?project={project}"><span class='ui-button-icon-primary ui-icon ui-icon-folder-open' style='' title='Add Conll or XML files containing annotations to the database' ></span>Add files to the database</a>
	#</div></div></div></div></div></div>&nbsp;""".format(project=project)
	
	#return now


	
#def printmenues(project,sql):
	
	#parser=projectconfig["configuration"]["importAnnotatorName"]
	#print """<form method="get" action="editor.cgi" style="display: none;" id="editorform" >
			#<input type="hidden" id="project" name="project" value="{project}">
			#<input type="hidden" id="textid" name="textid" value="">
			#<input type="hidden" id="opensentence" name="opensentence" value="1">
			
			#<img src="images/ajax-loader.gif" border="0">
			#<img src="images/loading.gif" border="0">
		#</form>
	#""".format(project=project)

	#print """<form method="post" action="project.cgi?project={project}" style="display: none;" id="nextstatusform" >
			#<input type="hidden" id="statustextid" name="statustextid" value="">
		#</form>
	#""".format(project=project)
	##<input type="hidden" id="project" name="project" value="{project}">
	
	#print """<div id="exoform" style=" position:absolute;display: none;" onclick="var e=arguments[0];e.stopPropagation();">
			#<form  method="post" action="project.cgi?project={project}" id="exochange" name="exochange">
				#<input type="hidden" id="etextid" name="textid" value="">
				#<select id="exochoice" name="exochoice" style="width:180px;"  >
					#{options}
				#</select>
				#<input id="exotoknum" type="text" value="0" name="exotoknum" title="number of sentences per student (0=all)" style="width:20px;">
			#</form>
		#</div>		""".format(project=project, options="\n".join(["<option>"+ty+"</option>" for ty in sql.exotypes]))
	
	
	#print """<div id="userform" style="position:absolute; display:none;" onclick="var e=arguments[0];e.stopPropagation();">
			#<form  method="post" action="project.cgi?project={project}" id="useraddremove" name="useradd">
				#<select id="userchoice" name="userchoice" style="width:180px;"  >""".format(project=project)
	#for uid,user,realname in sorted(sql.getall(None, "users",None,None), key=lambda x: x[2].lower()):
			#if user!=parser:
				##print [user]
				#print "			<option>",
				#if realname: print realname.encode('utf-8'),
				#print " ("+user+")</option>"

		
	#print """		</select>
				#<div class='ui-button-icon-primary ui-icon ui-icon-circle-check' style='cursor:pointer; float:right;' onclick="var e=arguments[0];e.stopPropagation();userchoice(1);"  title='asign this user as validatator' ></div>
				#<div class='ui-button-icon-primary ui-icon ui-icon-circle-plus' style='cursor:pointer; float:right;' onclick="var e=arguments[0];e.stopPropagation();userchoice(0);"  title='asign this user as annotator' ></div>
				#<input type="hidden" id="utextid" name="textid" value="">
				#<input type="hidden" id="uremove" name="removeid" value="false">
				#<input type="hidden" id="validator" name="validator" value="0">
			#</form>
		#</div>"""

		
	#print """<div class="ui-widget ui-widget-content ui-corner-all" style="position: absolute; padding: 5px;z-index:20;display:none;font-size:.8em;" id="exportdiv">
			#<form id="export" name="export" method="POST"  action="project.cgi?project={project}" >
			
			
			
			#<div id="radioset" class="ui-buttonset" style="margin-left:15px">
				#export complete files per assignment as<br>
					#<label for="radio1" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only ui-corner-left" onclick='exportGo("todosconll")' title="All assignments are exported as complete CoNLL files, independently of whether the annotator has actually changed the tree or not from the original parse tree. CoNLL files only contain the form, the lemma, the category, and the dependency.">
						#<span class="ui-button-text">
							#CoNLL file
						#</span>
					#</label><label for="radio2" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only  ui-corner-right" onclick='exportGo("todosxml")' title="All assignments are exported as complete XML files, independently of whether the annotator has actually changed the tree or not from the original parse tree.">
						#<span class="ui-button-text">
								#XML file
						#</span>
					#</label>
					
			#</div>
			#<br>
			#<div id="radioset" class="ui-buttonset" style="margin-left:15px">
				#export all trees by any annotator as<br>
					#<label for="radio1" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only ui-corner-left"  onclick='exportGo("allconll")' title="All trees that any annotator has saved are exported, including the original parse trees. The resulting CoNLL files don't necessarily contain all sentences.  CoNLL files only contain the form, the lemma, the category, and the dependency.">
						#<span class="ui-button-text">
							#CoNLL file
						#</span>
					#</label><label for="radio2" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only  ui-corner-right" onclick='exportGo("allxml")' title="All trees that any annotator has saved are exported, including the original parse trees. The resulting XML files don't necessarily contain all sentences.">
						#<span class="ui-button-text">
								#XML file
						#</span>
					#</label>
					
			#</div>
			#<br>
			#<div id="radioset" class="ui-buttonset" style="margin-left:15px">
				#export most recent trees by any annotator as<br>
					#<label for="radio1" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only ui-corner-left"  onclick='exportGo("lastconll")' title="The most recent tree for every sentence is exported, this may be the original parse trees. CoNLL files only contain the form, the lemma, the category, and the dependency.">
						#<span class="ui-button-text">
							#CoNLL file
						#</span>
					#</label><label for="radio2" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only  ui-corner-right" onclick='exportGo("lastxml")' title="The most recent tree for every sentence is exported, this may be the original parse trees.">
						#<span class="ui-button-text">
								#XML file
						#</span>
					#</label>
					
			#</div>
			#<input type="hidden" id="exportNumber" name="exportNumber" value="">
			#<input type="hidden" id="exportType" name="exportType" value="">
			#<input type="hidden" id="textname" name="textname" value="">
			#</form>
		#</div>
		
		#<div id="dialog" title="Confirmation" style="display: none;" >
			#<div class="ui-state-error ui-corner-all" style="margin: 40px;padding: 10pt 0.7em;">
				#<h2 id="question">Are you sure that you want to erase the text?</h2>
			#</div>
		#</div>
		#<form method="post" action="project.cgi?project={project}" style="display: none;" id="erase" >
			#<input type="hidden" id="eraseNumber" name="eraseNumber" value="">
			
		#</form>
		
		#""".format(project=project)

def printmate(project,projectconfig, sql, adminLevel):
	if projectconfig.get("configuration",{}).get("mate",{}):
	
		print """
	<div class="ui-widget ui-widget-content ui-corner-all box" style="text-align:-moz-center;">Mate:
	
		<form  method="post" id="startdepeditdatabaseform" name="startdepeditdatabaseform">
			For the <input type="hidden" id="etextid" name="textid" value="">
				<select id="parserType" name="parserType" style="width:100px;height:25px;"  >
					{parsertypeoptions}
				</select>-based parser, take
			<input type="radio" name="whoseTrees" value="v" checked>the validators' trees <input type="radio" name="whoseTrees">all validated trees,
			and use <input id="evaluationPercent" type="text" value="10" name="evaluationPercent" title="percentage of training data used for evaluation" style="width:33px;">% for evaluation.
			<br/>
			Use <input id="additionnalLexicon" type="text" value="" name="additionnalLexicon" title="name of additional lexicon to use" style="width:93px;"> as additional lexicon and store the parse results under
			<input id="resultAnnotator" type="text" value="mate" name="resultAnnotator" title="username for resulting trees" style="width:63px;">.
			<br/>
			<input type="button" cursor="pointer" id="buttonstartdepeditdatabase" name="buttonstartdepeditdatabase" value="start Mate" style="width:155px;z-index:33;" class="fg-button ui-state-default ui-corner-all" onclick="startdepeditdatabase();">
			
		</form>
		<div id="parseStatus" class="ui-widget ui-widget-content ui-corner-all box" style="background-position: center center;background-repeat:no-repeat;">			
		</div>
	
	</div>&nbsp;<br/>&nbsp;""".format(project=projectconfig.get("mate",None),parsertypeoptions="\n".join(["<option>"+ty+"</option>" for ty in ["graph","transition"]]))

#parserType
#whoseTrees
#evaluationPercent
#additionnalLexicon
#resultAnnotator

	
#def printfooter(project,username,thisfile, now):
	#"""
	#"now" contains a list of all users currently online
	#"""
	#a=u"""<div class="arborfoot fg-toolbar ui-widget-header ui-helper-clearfix">logged in as {username}&nbsp;&nbsp;&nbsp;
	#<a href="{thisfile}?login=logout&project={project}">logout</a>""".format(username=username,project=project,thisfile=thisfile)
	#print (u"""<div class="arborfoot fg-toolbar ui-widget-header ui-helper-clearfix">logged in as {username}&nbsp;&nbsp;&nbsp;
	#<a href="{thisfile}?login=logout&project={project}">logout</a>""".format(username=username,project=project,thisfile=thisfile)).encode("utf-8")
	#if username!="guest": print (u'&nbsp;&nbsp;&nbsp;<a href="{thisfile}?login=editaccount&project={project}">edit the account {username}</a>'.format(username=username,project=project,thisfile=thisfile)).encode("utf-8")
	#if adminLevel > 1: print (u'&nbsp;&nbsp;&nbsp;<a href="{thisfile}?login=admin&project={project}">User Administration</a>'.format(project=project,thisfile=thisfile)).encode("utf-8")
	#if adminLevel: print (u'&nbsp;&nbsp;&nbsp;<a href="configedit.cgi?project={project}">Edit Configuration</a>'.format(project=project)).encode("utf-8")
	#nowstring=u'<a  title="Ask questions or share your feelings with {r}..." href="mailto:{email}?subject=The%20Arborator%20is%20driving%20me%20crazy!">{r} ({u})</a>'
	#now = [nowstring.format(u=u,r=unicode(r),email=email) for (u,r,email) in now if u!= username]
	#if len(now)==1: print (u'&nbsp;&nbsp;&nbsp;You are not alone! This other user is online now: '+now[0]).encode("utf-8")
	#elif len(now)>1: print (u'&nbsp;&nbsp;&nbsp;You are not alone! These other users are online now: '+u", ".join(now)).encode("utf-8")
	#print "</div>"
	#print '</body></html>'
	##&nbsp;&nbsp;&nbsp;<a href="admin.cgi?project={project}">Corpus Administration</a>
	
	
	
	
	
	
	
	
	
	

adminDiv=u"""
	<div class="ui-widget ui-corner-all box" style="text-align:-moz-center;">
		<form  method="post" id="applyToDatabaseForm" name="applyToDatabaseForm">
			Apply the grammar to <input type="hidden" id="etextid" name="textid" value="">
				<select id="projects" name="projects" style="width:100px;height:25px;"  >
					{projects}
				</select>
			and add this string to name of the new project <input id="addName" type="text" value="_{username}" name="addName" title="additional name of new project" style="width:133px;">.
			<br/>
			<input type="button" cursor="pointer" id="buttonstartdepeditdatabase" name="buttonstartdepeditdatabase" value="Apply Depedit to Database" style="z-index:33;" class="fg-button ui-state-default ui-corner-all" onclick="applyGrammar();">
		</form>
		<div id="parseStatus" class="ui-widget ui-widget-content ui-corner-all box" style="background-position: center center;background-repeat:no-repeat;">
		</div>
	</div>&nbsp;<br/>&nbsp;"""
	
	#class="ui-widget ui-widget-content ui-corner-all box" style="text-align:-moz-center;"
	#<input type="radio" name="whoseTrees" value="v" checked>the validators' trees <input type="radio" name="whoseTrees">
	#Use <input id="additionnalLexicon" type="text" value="" name="additionnalLexicon" title="name of additional lexicon to use" style="width:93px;"> as additional lexicon and store the parse results under
	#<input id="resultAnnotator" type="text" value="mate" name="resultAnnotator" title="username for resulting trees" style="width:63px;">.
	#<br/>
			
			
if __name__ == "__main__":
	
	print "Content-Type: text/html\n\n" # blank line: end of headers
	
	
	thisfile,username,adminLevel = start()
	if adminLevel:
		applyToDatabase=adminDiv.format(projects="\n".join(["<option>"+ty+"</option>" for ty in sorted(config.projects, key=unicode.lower)]), username=username)
	else:
		applyToDatabase=""
	html = codecs.open("depedit.html","r","utf-8").read().replace(u"{applyToDatabase}",applyToDatabase)
	print html.encode("utf-8")
	
	
	#printhtmlheader(project.encode("utf-8"))
	#query=printheadline(project.encode("utf-8"))
	
	#reaction(project,projectconfig,sql,userid,form,query)
	#printmyassignments(project,sql,projectconfig,userid)
	#printalltexts(project.encode("utf-8"),sql,adminLevel)
	#lastseens,now=sql.usersLastSeen()
	#if adminLevel:	
		#printuserassignments(project.encode("utf-8"),projectconfig,sql,adminLevel, lastseens)
		#printmate(project.encode("utf-8"),projectconfig,sql,adminLevel)
	
	#printmenues(project.encode("utf-8"),sql)
	#printfooter(project,username,thisfile,now)
	
	










<html>
<head>
	<title>Arborator â Depedit</title>
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
	

	
	<script type="text/javascript" src="script/jquery.js"></script>
	<script type="text/javascript" src="script/raphael.js"></script>
	
	<script type="text/javascript" src="script/jquery-ui-1.8.18.custom.min.js"></script>
	<script type="text/javascript" src="script/jquery.cookie.js"></script>
	
	<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />
	<link rel="stylesheet" type="text/css" href="css/jquery-linedtextarea.css" media="screen" />
	<script type="text/javascript" src="script/jquery-linedtextarea.js"></script>
	
	<script type='text/javascript' > 
	</script>
	<script type="text/javascript" src="script/arborator.draw.js"></script>
<!-- 	<script type="text/javascript" src="script/q.js"></script> -->
	<script type="text/javascript" src="script/jsundoable.js"></script>
	<script type="text/javascript" src="script/colpick.js"></script>
	
	<link href="css/arborator.css" rel="stylesheet" type="text/css">
<!-- 	<link href="css/colpick.css" rel="stylesheet" type="text/css"> -->

	<script  type="text/javascript" >
	
	inwin=null;
	outwin=null;
	
	applyGrammar = function() {
		$("#conllout").val("Computing...");
		$.ajax({
			type: "POST",
			dataType: "text",
			url: "runDepedit.cgi",
			data: {"conllin":$("#conllin").val(),"grammar":$("#grammar").val(), "project":$( "#projects" ).val(), "addName":$( "#addName" ).val()}, 
// 			data: {"project":project,"projects":$( "projects" ).val(), "evaluationPercent":$( "#evaluationPercent" ).val(), "additionnalLexicon":$( "#additionnalLexicon" ).val(), "resultAnnotator":$( "#resultAnnotator" ).val()}
			success: function(answer){
					console.log("success");
					$("#conllout").val(answer);
// 					console.log(nr+"erased!"+answer);
					
				},
			error: function(XMLHttpRequest, textStatus, errorThrown){
				console.log("error")
				alert("error "+XMLHttpRequest+ "\n"+textStatus+ "\n"+errorThrown);
				}
			});
		
		}
		
	
	$(function() {
		
		
		$( "#showin" ).button();
		$( "#showin" ).click( function( event ) {
			event.preventDefault();
			if (inwin==null || inwin.closed) 
				{
					inwin=window.open("q.cgi?autostart=no",
					"#conllin",
					"toolbar=yes,resizable=yes,width=800,left=1,top=220,height="+(screen.availHeight)+";")
				}
			else
				{
					inwin.$("#trees").empty();
					inwin.readConll();
					inwin.setupStyleDialog()
					inwin.drawTrees();
				}
			
			} );
			
		$( "#applygrammar" ).button();
		$( "#applygrammar" ).click( function( event ) {
			event.preventDefault();
			applyGrammar();
			} );
			
		$( "#showout" ).button();
		$( "#showout" ).click( function( event ) {
			event.preventDefault();
			if (outwin==null || outwin.closed) 
			{
					outwin=window.open("q.cgi?autostart=no",
					"#conllout",
					"toolbar=yes,resizable=yes,width=800,left="+(screen.availWidth-800)+",top=220,height="+(screen.availHeight)+";")
					timer = setInterval(checkChild, 500, outwin,"#conllout");
				}
			else
				{
					outwin.$("#trees").empty();
					outwin.readConll();
					outwin.setupStyleDialog()
					outwin.drawTrees();
				}
			
			} );
		$("#grammar").linedtextarea();
	
		});
	
	
	nl2br = function(str) {   
		return (str + '').replace(/([^>\\r\\n]?)(\\r\\n|\\n\\r|\\r|\\n)/g, '$1'+ '<br>' +'$2');
	}

	startdepeditdatabase = function() {
		//$("#startdepeditdatabaseform :input").prop('disabled', true);
		//checkdepeditdatabase();
		$("#parseStatus").css('background-image', 'url("images/ajax-loader.gif")');  
		$.ajax({
			type: "POST",
			url: "runDepedit.cgi",
			data: {"project":project,"parserType":$( "#parserType" ).val(), "whoseTrees":$( "#whoseTrees" ).val(), "evaluationPercent":$( "#evaluationPercent" ).val(), "additionnalLexicon":$( "#additionnalLexicon" ).val(), "resultAnnotator":$( "#resultAnnotator" ).val()}, 
			success: function(answer){
					$("#parseStatus").html(answer);
					console.log("finished!",answer);
					//clearInterval(timer);
				},
			error: function(XMLHttpRequest, textStatus, errorThrown){
				console.log("mate error",project)
				
				}
			});
	
		timer = setInterval(checkdepeditdatabase, 5000);
		}
	
	checkdepeditdatabase = function() {
		$.ajax({
			url : "mate/parse.log",
			dataType: "text",
			cache: false,
			processData: false,
			success : function (data) {
				$("#parseStatus").html(nl2br(data));
				if (data.match("^Ready"))
					{
						console.log("Done");
						clearInterval(timer);
						$("#parseStatus").css('background-image', 'none'); 
						//$("#startdepeditdatabaseform :input").prop('disabled', false);
					}
				}
			});
			

			
		}
	
	
	</script>

	
</head>

<body id="body">
<div id="navigation" style="width:100%;margin:0px;border:0px;" class="arbortitle  ui-widget-header ui-helper-clearfix">
	<a href='.' style='position: fixed;left:1px;top:1px'><img src="images/arboratorNano.png" border="0" title="Arborator Main Page"></a>
	Arborator â Depedit
	<a style="position: fixed;right:1px;top:1px;right:5px;" id='help' title="restart the help animation"><img src="images/help.png" style="vertical-align: text-top" border="0">Help</a>
</div>
<div id="center" class="center" style="width:100%;float:right;top:0px;">
	
	Paste and edit CoNLL file below:
	<div id="textual" style="width:100%; display: flex; ">
		<TEXTAREA NAME="conllin" id="conllin" title="Here you can paste and edit CoNLL files with 4, 10 or 14 columns" style="height:300;  flex: 1; background-image: url('images/conll10.png'); background-repeat: no-repeat; background-position: 98% bottom; font-size: 1em; tab-size: 12; -moz-tab-size:12;">1	Wikinews	_	NP	NP	_	2	nsubj	_	_
2	interviews	_	VVZ	VVZ	_	0	root	_	_
3	President	_	NN	NN	_	2	dobj	_	_
4	of	_	IN	IN	_	3	prep	_	_
5	the	_	DT	DT	_	7	det	_	_
6	International	_	NP	NP	_	7	amod	_	_
7	Brotherhood	_	NP	NP	_	4	pobj	_	_
8	of	_	IN	IN	_	7	prep	_	_
9	Magicians	_	NPS	NPS	_	8	pobj	_	_</TEXTAREA>
		<div style='width:60px;margin:0 15'>
			<label id="showin"><img src='images/cut.png' border='0' title='show trees' style='bottom:0px;'>  </label>
		</div>
	</div>
Paste and edit your DepEdit transformation grammar here:
	<div id="textual" style="width:100%; display: flex;">
		<TEXTAREA NAME="grammar" id="grammar" title="Here you can paste and edit CoNLL files with 4, 10 or 14 columns" style="height:300; flex: 1;font-size: 1.2em; tab-size: 12; -moz-tab-size:12;">;;;Configuration file to convert Stanford Typed Dependencies to Universal Dependencies
;;;Note not all aspects of universal dependencies are covered, especially handling of the 'name' label

;simple rename
func=/^acomp/	none	#1:func=xcomp
func=/^npadvmod/	none	#1:func=nmod:npmod
func=/^num/	none	#1:func=nummod
func=/^rcmod/	none	#1:func=acl:relcl
func=/^vmod/	none	#1:func=nfincl
func=/^quantmod/	none	#1:func=advmod
func=/^tmod/	none	#1:func=npmod:tmod
func=/^predet/	none	#1:func=det:predet
func=/^possessive/	none	#1:func=case
func=/^poss$/	none	#1:func=nmod:poss
func=/^prt/	none	#1:func=compound:prt

;disambiguate nn
func=/nn/&pos=/JJ/	none	#1:func=amod
func=/nn/&pos=/DT/	none	#1:func=det
func=/nn/	none	#1:func=compound
;handle names HERE
;The following undergenerates, probably best to leave off: 
;text=/^(Mr|Mrs|Ms|Dr|Prof(essor)?|Doctor|King|Queen|Prince(ss)?|Attorney|Governer)\.?/&func=/^nn/;pos=/^NN?P/	#1.#2;#2>#1	#1>#2;#2:func=name

;to-infinitive
text=/^[Tt]o$/&func=/aux/	none	#1:func=mark

;adverbial clause modifying a nominal
pos=/^(N.*|P.*)$/;pos=/[NV].*/&func=/advcl/	#1>#2	#2:func=acl

;prepositional phrases
;simple
func=/.*/;func=/^prep$/;func=/pobj/	#1>#2>#3	#3>#2;#2:func=case;#3:func=nmod;#1>#3
;pcomp without mark
func=/.*/;func=/^prep$/;func=/pcomp/	#1>#2>#3>#4	#3>#2;#2:func=_temp;#3:func=ncmod;#1>#3
;pcomp with mark
func=/.*/;func=/^prep$/;func=/pcomp/;func=/mark/	#1>#2>#3>#4	#3>#2;#2:func=case;#3:func=ncmod;#1>#3
func=/_temp/	none	#1:func=mark

;2word mwes
;"apart_from", "as_from", "aside_from", "away_from", "close_by", "close_to", "contrary_to", "far_from", "next_to", "near_to", "out_of", "outside_of", "pursuant_to", "regardless_of", "together_with"
text=/^([Aa]part|[Aa]s|[Aa]side|[Aa]way|[Ff]ar)$/;text=/^from$/	#1.#2	#1>#2;#2:func=mwe
text=/^[Cc]lose$/;text=/^(by|to)$/	#1.#2	#1>#2;#2:func=mwe
text=/^[Cc]ontrary$/;text=/^(to)$/	#1.#2	#1>#2;#2:func=mwe
text=/^([Nn]ext|[Nn]ear|[Pp]ersuant)$/;text=/^(to)$/	#1.#2	#1>#2;#2:func=mwe
text=/^([Oo]ut|[Oo]utside|[Rr]egardless)$/;text=/^(of)$/	#1.#2	#1>#2;#2:func=mwe
text=/^[Tt]ogether$/;text=/^with$/	#1.#2	#1>#2;#2:func=mwe

;3word mwes
;"by_means_of", "in_accordance_with", "in_addition_to", "in_case_of", "in_front_of", "in_lieu_of", "in_place_of", "in_spite_of", "on_account_of", "on_behalf_of", "on_top_of", "with_regard_to", "with_respect_to"
text=/^[Bb]y$/;text=/^means$/;text=/^of$/	#1.#2.#3	#1>#2>#3;#2:func=mwe;#3:func=mwe
text=/^[Ii]n$/;text=/^accordance$/;text=/^with$/	#1.#2.#3	#1>#2>#3;#2:func=mwe;#3:func=mwe
text=/^[Ii]n$/;text=/^addition$/;text=/^to$/	#1.#2.#3	#1>#2>#3;#2:func=mwe;#3:func=mwe
text=/^[Ii]n$/;text=/^(case|front|lieu|place|spite)$/;text=/^of$/	#1.#2.#3	#1>#2>#3;#2:func=mwe;#3:func=mwe
text=/^[Oo]n$/;text=/^(account|behalf|top)$/;text=/^of$/	#1.#2.#3	#1>#2>#3;#2:func=mwe;#3:func=mwe
text=/^[Ww]ith$/;text=/^(regard|respect)$/;text=/^to$/	#1.#2.#3	#1>#2>#3;#2:func=mwe;#3:func=mwe</TEXTAREA>
		<div style='width:60px;margin:0 15'>
			<label for="applygrammar" id="applygrammar"><img src='images/cut.png' border='0' title='apply grammar' style='bottom:0px;'>  </label>
	<!-- 		 style='position:absolute;top:0;' -->
		</div>
	</div>

Result after transformation:
	<div id="textual" style="width:100%; display: flex; font-size: .7em; tab-size: 12; -moz-tab-size:12;">
	<TEXTAREA NAME="conllout" id="conllout" title="Result after transformation" style="width:100%; height:300; background-image: url('images/conll10.png'); background-repeat: no-repeat; background-position: 98% bottom; font-size: 1em; tab-size: 12; -moz-tab-size:12; ">

	</TEXTAREA>
	<div style='width:60px;margin:0 15 '>
		<label id="showout"><img src='images/cut.png' border='0' title='show trees' style='bottom:0px;'>  </label>
	</div>
</div>

		
	

<br>

</div></div>

	<form method="post" action="convert_svg.cgi?project=quickie" name="ex" id="ex" style=" position:fixed;top:6px;right:30px;display:none;"  target="_blank">
		<input type="hidden" id="source" name="source" value="">
		<select id="exptype" name="type" class="ui-button ui-state-default ui-corner-all" style="height:16px;font: italic 10px Times,Serif;border:thin solid silver;" >
			<option>pdf</option>
			<option>ps</option>
			<option>odg</option>
			<option>jpg</option>
			<option>png</option>
			<option value="tif">tiff</option>
			<option>svg</option>
		</select>
		<input type="button" title="export" value="export" class="ui-button ui-state-default ui-corner-all" onClick="exportTree();"  style="padding: 0.0em 0.0em;" >
	</form>
	
	
	<div id="funcform" style="display:none;position:absolute;">
		<form  method="post" id="func" name="func" >
			<select id="funchoice" class='funcmenu' onClick="changeFunc(event);" size=0 style="height:0; width:80px;"  >
			</select>
		</form>
	</div>
	<div id="catform" style="display:none;position:absolute;">
		<form  method="post" id="cat" name="cat" >
			<select id="catchoice" class='funcmenu' onClick="changeCat();"  size=0 style="height:0; width:80px;"  >
			</select>
		</form>
	</div>
	

	<div id="dialog" title="Confirmation" style="display: none;" >
		<div class="ui-state-error ui-corner-all" style="margin: 40px;padding: 10pt 0.7em;">
			<h2 id="question">Are you sure?</h2>
		</div>
	</div>
	{applyToDatabase}
	
</div>
</body></html>
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2015 Kim Gerdes
# kim AT gerdes. fr
# http://arborator.ilpga.fr/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 3 of the GNU Affero General Public License (the "License")
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of of version 3 of the GNU Affero General Public License
# from http://www.gnu.org/licenses/agpl-3.0.html 
# For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

import os, cgitb, cgi, sys
sys.path.append('modules')
from logintools import login
from logintools import isloggedin
from logintools import logout
from lib import config, database
cgitb.enable()

# global variables:
project,projectEsc,projectconfig,sql,thisfile,textid,textname,username,userid,adminLevel,todo,validator,validvalid,exotype,opensentence,sentencenumbe,addEmptyUser=None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None

graphical=0
##############################################
######################### functions

def printhtmlheader():
	"""
	all strings in parameters are already in utf-8
	
	"""
	print (u"""<html><head>
		<title>Arborator - Editor - {title}</title>
		<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">

		<script type="text/javascript" src="script/jquery.js"></script>
		<script type="text/javascript" src="script/raphael.js"></script>
		<script type="text/javascript" src="script/jquery-ui-1.8.18.custom.min.js"></script>

		<script type="text/javascript" src="script/arborator.draw.js"></script>
		<script type="text/javascript" src="script/jsundoable.js"></script>
		<script type="text/javascript" src="script/arborator.edit.js"></script>
		<script type="text/javascript" src="script/raphael.export.js"></script>
		
		<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />
		<link href="css/arborator.css" rel="stylesheet" type="text/css">""".format(title=textname)).encode("utf-8")
	
	
	config.jsDefPrinter(projectconfig) # prints all general, text-independant, stuff for javascript: categories, functions, etc
	
	#print """<script type="text/javascript"> numbersent=""",snr,";"
	print """<script type="text/javascript"> numbersent=1;"""
	print "project= '"+projectEsc.encode("utf-8")+"';"
	#print "uid= '"+"3"+"';" # TODO!
	print "textname= '"+textname.encode("utf-8")+"';"
	print "textid= '"+str(textid)+"';"
	print "userid= "+str(userid)+";"
	print "username= '"+username.encode("utf-8")+"';"
	if adminLevel or (sql.validatorsCanModifyTokens and validator) or sql.usersCanModifyTokens:
		tokenModifyable=1
	else: tokenModifyable=0
	print "tokenModifyable= "+str(tokenModifyable)+";"
	print "todo= '"+str(todo)+"';"
	print "validator= '"+str(validator)+"';"
	if addEmptyUser: print "addEmptyUser= '"+str(addEmptyUser)+"';"
	else: print "addEmptyUser= null;"
	print "validvalid= '"+str(validvalid)+"';"
	print "opensentence= '"+str(opensentence)+"';"
	print "editable= "+str(sql.editable)+";"
	print "filename = null;" # stuff for use in viewer not editor...
	print "nr = null;"
	print "filetype = null;"
	print "function settings() {"
	print projectconfig["look"]["look"]
	print """colored={"dependency":null}; //object of elements to be colored. the colors have to be defined in the configuration. just leave the object empty {} if you don't want colors
	attris[shownfeatures[categoryindex]]=attris["cat"]
	colored[shownfeatures[categoryindex]]=null;
	}
	
	</script>
	</head>"""
	

def printheadline():
	
	if os.path.exists("projects/"+project.encode("utf-8")+"/"+project.encode("utf-8")+".png"):img="<img src='projects/"+project.encode("utf-8")+"/"+project.encode("utf-8")+".png' align='top' height='18'>"
	else: img=""
	
	if adminLevel:openbutton= '<input type="button" id="openall" name="openall" value="open all" style="width:105px;z-index:33;" class="fg-button ui-state-default ui-corner-all " onClick="openAllTrees();">'
	else:openbutton=""
	
	print 	"""<body id="body">
			<div id="navigation" style="width:100%;margin:0px;border:0px;" class="arbortitle  ui-widget-header ui-helper-clearfix">
			<a href='.' style='position: fixed;left:1px;top:1px'><img src="images/arboratorNano.png" border="0"></a>
			<a href="project.cgi?project={project}" style='position: fixed;left:120px;top:5px;color:white;' title="project overview">{img} {project} Annotation Project</a>
				<span style='margin:5 auto;position: relative;top:5px;' id='sentinfo'>{textname} &nbsp; {quantityInfo} </span>
			</div>
			<div id="center" class="center" style="width:100%;">
			<form method="post" action="editor.cgi" name="save" id="save" class="nav" style="top:0px;right:150px;">
				<input type="button" id="sav" name="sav" value="all trees are saved" style="width:155px;z-index:33;" class="fg-button ui-state-default ui-corner-all ui-state-disabled" onClick="saveAllTrees();">
				{openbutton}
			</form>
			<div id="loader" ><img src="images/ajax-loader.gif"/></div>
		""".format(textname=textname.encode("utf-8"), quantityInfo=quantityInfo,project=project.encode("utf-8"),img=img,openbutton=openbutton)
	if os.path.exists("sitemessage.html"):print open("sitemessage.html").read()

	
	#ui-state-disabled


#def printfooter(project,username,thisfile, now):
def printfooter():
	now=lastseens,now=sql.usersLastSeen()	
	print (u"""<div class="arborfoot fg-toolbar ui-widget-header ui-helper-clearfix">logged in as {username}&nbsp;&nbsp;&nbsp;
	<a href="{thisfile}?login=logout&project={project}">logout</a>""".format(username=username,project=projectEsc,thisfile=thisfile)).encode("utf-8")
	if username!="guest": print (u'&nbsp;&nbsp;&nbsp;<a href="{thisfile}?login=editaccount&project={project}">edit the account {username}</a>'.format(username=username,project=projectEsc,thisfile=thisfile)).encode("utf-8")
	if adminLevel > 1: print (u'&nbsp;&nbsp;&nbsp;<a href="{thisfile}?login=admin&project={project}">User Administration</a>'.format(project=projectEsc,thisfile=thisfile)).encode("utf-8")
	
	nowstring=u'<a  title="Ask questions or share your feelings with {r}..." href="mailto:{email}?subject=The%20Arborator%20is%20driving%20me%20crazy!">{r} ({u})</a>'
	now = [nowstring.format(u=u,r=r,email=email) for (u,r,email) in now if u!= username]
	if len(now)==1: print (u'&nbsp;&nbsp;&nbsp;You are not alone! This other user is online now: '+now[0]).encode("utf-8")
	elif len(now)>1: print( u'&nbsp;&nbsp;&nbsp;You are not alone! These other users are online now: '+u", ".join(now)).encode("utf-8")
	print "</div>"
	print '</body></html>'
	#&nbsp;&nbsp;&nbsp;<a href="admin.cgi?project={project}">Corpus Administration</a>
	
	
#def printexport(project):
def printexport():
	print """
	<form method="post" action="convert_svg.cgi?project={project}" name="ex" id="ex" style=" position:fixed;top:6px;right:30px;display:none;" >
		<input type="hidden" id="source" name="source" value="">
		<select id="exptype" name="type" class="ui-button ui-state-default ui-corner-all" style="height:16px;font: italic 10px Times,Serif;border:thin solid silver;" >
			<option>pdf</option>
			<option>ps</option>
			<option>odg</option>
			<option>jpg</option>
			<option>png</option>
			<option value="tif">tiff</option>
			<option>svg</option>
			<option>conll</option>
			<option>xml</option>
		</select>
		<input type="button" title="export" value="export" class="ui-button ui-state-default ui-corner-all" onClick="exportTree();"  style="padding: 0.0em 0.0em;" >
	</form>
	""".format(project=projectEsc.encode("utf-8"))
	
def printmenues():
	#print projectconfig.functions
	# form for functions and form for categories
	print """	
	<div id="funcform" style="display:none;position:absolute;">
		<form  method="post" id="func" name="func" >
			<select id="funchoice" class='funcmenu' onClick="changeFunc(event);"  size=""" +str( len(projectconfig.functions))+""" style="height:"""+str( len(projectconfig.functions)*13.5)+"""px; width:110px;"  >"""
	for f in projectconfig.functions:
		#print "___",f.encode("utf-8")
		print "<option style='color: "+projectconfig.funcDic[f]["stroke"].encode("utf-8")+";'>"+f.encode("utf-8")+"</option>"	
	print """
			</select>
		</form>
	</div>
	
	<div id="catform" style="display:none;position:absolute;">
		<form  method="post" id="cat" name="cat" >
			<select id="catchoice" class='funcmenu' onClick="changeCat();"  size=""" +str( len(projectconfig.categories))+""" style="height:"""+str( len(projectconfig.categories)*13.5)+"""px; " >"""

	for i,c in enumerate(projectconfig.categories): 
		print "<option style='color: "+projectconfig.catDic[c]["fill"].encode("utf-8")+";'>"+c.encode("utf-8")+"</option>"	
	
	print """
			</select>
		</form>
	</div>
	<div style="border-top:1px solid #EEE;">&nbsp;</div>	
	"""
	
	# form for compare
	print """
		<div class="ui-widget ui-widget-content ui-corner-all" style="position: absolute; padding: 5px;z-index:20;display:none;" id="compbox">
			<form id="compform" name="compform" method="POST">
				<div id="complist">
				</div><br>
				<div align="center" id="compasubmit">
					<img class="compare" src="images/compare.png" border="0" align="bottom" onclick='compareSubmit()'>
				</div>
			</form>
		</div>
		"""
	# form for check
	print """
		<div class="ui-widget ui-widget-content ui-corner-all" style="position: absolute; padding: 5px;z-index:20;display:none;" id="checkbox">
			
				<div id="checklist">
				</div><br>
				<div align="center">
					<img src="images/check.png" border="0" align="bottom">
				</div>
			
		</div>
		"""

def printdialogs():
	
	print """
	<div id="dialog" title="Confirmation" style="display: none;" >
		<div class="ui-state-error ui-corner-all" style="margin: 40px;padding: 10pt 0.7em; font-weight:100;">
			<h2 id="question">Are you sure?</h2>
		</div>
	</div>
	
	<div id="fdialog" title="Features">
		<form method="post" action="editor.cgi" id="featureform">
			
			<input type="hidden" id="eusername" name="username" value="">
			<input type="hidden" id="esentenceid" name="sentenceid" value="">
			<table id="featab" class="ui-widget ui-widget-content" style="width:100%;">
				<thead>
					<tr class="ui-widget-header ">
						<th>Attribute</th>
						<th>Value</th>
					</tr>
				</thead>
				<tbody  id="featabody"></tbody>
			</table>
		</form>
	
	</div>
	"""


##############################################################################################"

def start():
	global project,projectEsc,projectconfig,sql,thisfile,textid,textname,username,userid,adminLevel,todo,validator,addEmptyUser,validvalid,exotype,opensentence,quantityInfo,graphical
	form = cgi.FieldStorage()
	thisfile = os.environ.get('SCRIPT_NAME',".")
	userdir = 'users/'

	logint = form.getvalue("login",None)
	if logint == "logout":
		print logout(userdir) # printing cookie header. important!
		print "Content-Type: text/html\n" # blank line: end of headers
		print '<script type="text/javascript">window.location.href=".";</script>'
	project = form.getvalue("project","")
	projectEsc=""
	if project: 
		project =project.decode("utf-8")
		projectEsc=project.replace("'","\\'").replace('"','\\"')
	action = form.getvalue("action",None)
	if action: 
		action =action.decode("utf-8")
		if action.startswith("project_"):project=action[8:]
	if project: action="project_"+project
	
	try:
		action, userconfig = login(form, userdir, thisfile, action)
	except IOError, e:
		print "Content-Type: text/html\n" # blank line: end of headers
		print "login failed",e
		sys.exit()
	#try:
		
	#except IOError, e:
		#print "login failed",e
		#sys.exit()
		#raise IOError('Reading user file failed: %s' % e)
	#except:
		#print "login failed"
	adminLevel, username, realname = int(userconfig["admin"]),userconfig["username"].decode("utf-8"),userconfig["realname"].decode("utf-8")
	
	print "Content-Type: text/html\n" # blank line: end of headers
	projectconfig=None
	try:	projectconfig = config.configProject(project) # read in the settings of the current project
	except:	pass
	if not projectconfig:
		#print "Content-Type: text/html\n" # blank line: end of headers
		print "something went seriously wrong: can't read the configuration of the project",project.encode("utf-8")
		#print '<script type="text/javascript">window.location.href=".";</script>'
		#sys.exit("something's wrong")


	sql=database.SQL(project)
		
	textid = form.getvalue("textid",None)
	if textid: 	textid=int(textid)
	else:		textid=1
	try:
		_,textname,_ = sql.getall(None, "texts",["rowid"],[textid])[0]
	except:
		print "something went seriously wrong: The text you are looking for does not seem to exist!",project.encode("utf-8")
		textname="text not found!"
	userid = sql.userid(username,realname)
	opensentence = form.getvalue("opensentence",1)

	todo = sql.gettodostatus(userid,textid)
	validator=0
	if todo>0: validator=int(todo) # -1 and 0 => 0, 1 => 1
	
	validvalid=sql.validvalid(textid)
	
	exotype, exotoknum =sql.getExo(textid)
	#print "uu",sql.exotypes[exotype],sql.exotypes[exotype]=="graphical feedback"
	if sql.exotypes[exotype] in ["teacher visible","graphical feedback", "percentage"] :
		addEmptyUser=username
	if sql.exotypes[exotype] in ["teacher visible"] :
		validvalid+=[sql.userid(sql.teacher)]
	if sql.exotypes[exotype]=="graphical feedback":
		graphical=1
	if exotoknum and not adminLevel: 	quantityInfo = ">"+str(exotoknum)+" tokens"
	else: 					quantityInfo = str(sql.getnumber(None, "sentences",["textid"],[textid]))+" sentences"
	

	
#def main(project,sql,textid,userid,adminLevel,todo,validvalid,validator):
def main():
	########### essential loop: all the sentences:
	
	for snr,sid,s,tid in sql.getAllSentences(textid, username, userid, adminLevel):
		#print adminLevel, validvalid, validator
		treelinks, firsttreeid, sentenceinfo=sql.links2AllTrees(sid,snr,username,adminLevel, todo, validvalid, validator,addEmptyUser=addEmptyUser)
		status=""
		if todo>=0:status=sql.gettreestatus(sid,userid)
		status="<span id='status{nr}' class='status' onClick='nexttreestatus({sid},{nr})'>{status}</span>".format(status=status,sid=sid, nr=snr)
		connectRight=""
		if adminLevel or (sql.validatorsCanConnect and validator):
			connectRight='''<img class="connectRight" src="images/chain.png" border="0" align="bottom" id='connectRight{nr}' nr='{nr}' title="connect with next tree (+ctrl: split at selcted word)"> '''.format(nr=snr)
		exo=""
		if exotype>1: # 0: no exercise, 1: no feedback
			exo='''<img class="check" src="images/check.png" border="0" align="bottom" id='check{nr}' nr='{nr}' title="check annotation" graphical={graphical}> '''.format(nr=snr, graphical=graphical)
		print '''<div id='sentencediv{nr}' class='sentencediv' style="margin:10px;" sid={sid}  nr={nr}>
				<a id='toggler{nr}' class="toggler" treeid="{firsttreeid}" nr="{nr}" >
					{nr}: {sentence} &nbsp;
				</a> 
				<span id="othertrees{nr}" > {treelinks} </span>
				<img class="saveimg" src="images/save.png" border="0" align="bottom" id='save{nr}' title="save">
				<img class="undoimg" src="images/undo.png" border="0" align="bottom" id='undo{nr}'>
				<img class="redoimg" src="images/redo.png" border="0" align="bottom" id='redo{nr}'> 
				{status}
				<img class="exportimg" src="images/export.png" border="0" align="bottom" id='export{nr}' nr='{nr}' title="export"> 
				{connectRight}
				{exo}
			</div><p><small>{sentenceinfo}</small></p>'''.format(sentence=s.encode("utf-8"),nr=snr, sid=sid, firsttreeid=firsttreeid, project=project.encode("utf-8"), userid=userid, treelinks=treelinks.encode("utf-8"), status=status,connectRight=connectRight,exo=exo,sentenceinfo=sentenceinfo.encode("utf-8"))
		#print "addEmptyUser",addEmptyUser
	#print '<div id="holder" style="background:white; position:relative;"> </div> '


##############################################################################################"

		
if __name__ == "__main__":
	start()
	#printhtmlheader(project.encode("utf-8"),projectconfig,textid,textname.encode("utf-8"),username.encode("utf-8"),userid,todo,validator,addEmptyUser,opensentence,sql)
	printhtmlheader()
	
	#printheadline(project.encode("utf-8"),textname.encode("utf-8"),quantityInfo)
	printheadline()
	printexport()
	#print "todo",todo
	#main(project,sql,textid,userid,adminLevel,todo,validvalid,validator)
	main()
	
	printmenues()
	printdialogs()
	
	printfooter()
	
	
	
#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys,cgi,cgitb
sys.path.append('modules')
from logintools import isloggedin
from lib import database
cgitb.enable()

form = cgi.FieldStorage()

project = form.getvalue('project',None).decode("utf-8")
treeid = form.getvalue('treeid',None)
sentencenr = form.getvalue('sentencenr',None)
username = form.getvalue('username',None).decode("utf-8")

test = isloggedin("users/")
if not test:sys.exit()
admin = test[0]["admin"]
if not admin:sys.exit()

#print "Content-Type: text/html\n" # blank line: end of headers

sql=database.SQL(project)


print 'Content-type: text/html\n\n'
print sql.eraseTree(treeid,sentencenr,username,admin).encode("utf-8")
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2017 Kim Gerdes
# kim AT gerdes. fr
# http://arborator.ilpga.fr/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 3 of the GNU Affero General Public License (the "License")
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of of version 3 of the GNU Affero General Public License
# from http://www.gnu.org/licenses/agpl-3.0.html 
# For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

# this is the export script called from the editor or the viewer for exporting _individual_ trees

from sys      import exit
from xml.dom import minidom
import sys,cgi,cgitb,json
from lib import conll, rhapsoxml
cgitb.enable()


def jswords2nodedic(treefromjson):
	nodedic={}
	for i in words:
		nodedic[int(i)]={}
		for k in words[i]:
			if k=="features":
				for a in words[i][k]:
					nodedic[int(i)][a]=words[i][k][a]
			else:
				nodedic[int(i)][k]=words[i][k]
	return nodedic

def json2tree(jsonsource):
	
	treefromjson=json.loads(jsonsource)
	nodedic={}
	tree = conll.Tree()
	for i in treefromjson["tree"]:
		nodedic[int(i)]={}
		for k in treefromjson["tree"][i]:
			if k=="features":
				for a in treefromjson["tree"][i][k]:
					nodedic[int(i)][a]=treefromjson["tree"][i][k][a]
			else:
				nodedic[int(i)][k]=treefromjson["tree"][i][k]
	tree.sentencefeatures=treefromjson["sentencefeatures"]
	conll.update(tree, nodedic)
	return tree


def exportxml(nodedic):
	print 'Content-type: text/xml;charset=utf-8\n'
	alltext="\n"
	doc,tokens,lexemes, dependencies, text = rhapsoxml.baseXml()
	te=rhapsoxml.addFeat2doc(nodedic,doc,"t","lemma",tokens,lexemes, dependencies)
	text.appendChild(doc.createTextNode(te))
	xmltext= doc.toprettyxml(indent="     ",encoding="utf-8")
	print xmltext
	
	
if __name__ == "__main__":
	form = cgi.FieldStorage()
	jsonsource = form.getvalue('source',None)
	exptype = form.getvalue('type',None)
	if exptype=="conll":
		print 'Content-type: text/plain;charset=utf-8\n\n'
		#print str(json2tree(jsonsource))
		print json2tree(jsonsource).conllu().encode("utf-8")
	elif exptype=="xml":
		nodedic=jswords2nodedic(json.loads(jsonsource))
		exportxml(nodedic)
	else:
		print 'Content-type: text/plain;charset=utf-8\n\nNo data was sent!'
	
Change Log
==========

The following log records some of the most important changes.
Bug fixing is an on-going task, so it is implied in all sub-releases.

1.7beta1 -> 1.7
---------------

1. Bugzilla problem reports fixed:

    26815, 28344, 35165, 36570, 37496, 38498, 40163, 40727, 41205,
    41820, 41947, 41955, 41988, 42014, 42021, 42032, 42075, 42118,
    42137, 42168, 42169, 42196, 42209, 42210, 42212, 42221, 42228,
    42255, 42258, 42311, 42312, 42333, 42381, 42386, 42479, 42504,
    42505, 42543, 42645, 42651, 42697, 42698, 42807, 42961, 42963,
    442968, 3022, 43043, 43045, 43165, 43194, 43195, 43370, 43418,
    443446, 3638, 43657, 43744, 43760, 43764, 43787, 43854, 43871,
    443904, 3953, 44029, 44054, 44092, 44146

2. New features

  * xml:id support (http://www.w3.org/TR/xml-id/).
  * ElementTraversal support (http://www.w3.org/TR/ElementTraversal/).
  * Enhanced DOM Viewer window, contributed from Ivan Andjelkovic,
    a Google Summer of Code student.

3. Improvements

  * Support for cursor="move" on OS X, and cursor="help" on all
    platforms.
  * Improved conversion to/from WMF.
  * data: URIs now supported as external stylesheets, scripts, filters.
  * SVG 1.1 feature strings now reported.
  * The viewBox="" attribute on <svg> is now animatable.
  * Implemented SVGSVGelement.getViewBox().
  * A document's document element can now be replaced.
  * Performing animation hyperlinking backwards is now supported, as
    well as using <a> elements to hyperlink to animations.
  * Updated pdf-transcoder.jar to one built from FOP 0.94.
  * Squiggle's View Source window is now syntax highlighted.

4. Bug fixes

  * XPath interface objects now exposed to ECMAScript.
  * The three other MIME types for ECMAScript are now really supported
    (text/javascript, application/javascript, application/ecmascript).
  * Pseudo-elements now parsed properly in CSS selectors.
  * Avoid null pointer exception when event-base timing specifiers
    in animations reference an element by ID within a <use> shadow tree.
  * Invalid min="" and max="" values on animation elements now ignored.
  * Corrected CSS cascade order for rules of equal specificity.
  * Fixed CSS :lang() processing so that it matches case insenstiviely,
    using xml:lang="" as well as lang="", and using proper hyphen-
    separated tokens.
  * Gradients with gradientUnits="objectBoundingBox" disabled when the
    bounding box has zero width or height.
  * Avoid an array index exception for certain degenerate motion path
    animations.
  * Fixed mouse event button reporting.
  * Avoid null pointer exception when cloning <symbol> and <image>
    elements.
  * Make empty <filter> elements result in no rendering for the
    filtered element.
  * SVGStylable.getPresentationAttribute() now returns null if the given
    presentation attribute is not specified on the element.
  * Specifying textLength="" on text elements that have non-text nodes
    as their first children now works.
  * Avoid numerical accuracy issues when sampling frozen animations.
  * Event listeners are now removed when <text> elements are removed
    from the document.
  * Invalid SVG 1.0 feature string removed.
  * Ensure animation elements are properly deinitialised when they are
    removed from the document.
  * Allowed animations in <use> element shadow trees to have syncbase
    timing specifiers that refer to elements outside the shadow tree.
  * Fixed bug in DOM event removal/dispatch.
  * Ensure the animation engine is resumed if it is sleeping when a
    beginElement() call is made.
  * Avoid a null pointer exception in JSVGScrollPane if the document
    doesn't return a bounds.
  * Avoid a null pointer exception when painting a GraphicsNode on to an
    SVGGraphics2D.
  * Rasterizer -cssUser command line argument now resolved against
    current working directory.
  * Fixed bug in key event dispatching code.
  * Decrease memory required for storing animation elements' previous
    intervals (used for hyperlinking).
  * Avoid infinite loop for animations with multiple, identical begin
    (or end) instance times.
  * Ensure TIFF and JPEG codecs really aren't compiled unless the relevant
    Sun classes are present.
  * Avoid null pointer exception when accessing animated marker orient
    values.
  * Patched Rhino so that the ECMAScript debugger runs on OS X.
  * Ensure the animation engine is initialised before the SVGLoad event
    is dispatched.
  * Restored exception throwing from bridge classes when required
    length attributes are missing.
  * Duplicated codec classes removed from awt-util jar.
  * Avoid null pointer exceptions in DOMMouseEvent objects.
  * Motion animation transforms are now applied after the transform=""
    attribute.
  * Fixed bug in DOM event default action invocation in SVG 1.2 documents.
  * Fixed bug in CSS class name matching, which occured only when an
    element was declared to be a member of multiple classes, where one
    is a prefix of another.
  * Fixed bug on OS X where the zoom interactor overlay was not shown.

5. Misc

  * Official maven artifacts are now supplied.

1.6 -> 1.7beta1
---------------

1. Bugzilla problem reports fixed:

    22260, 25428, 28337, 29417, 29552, 32415, 34217, 34234, 34364,
    34847, 34938, 35349, 35480, 35549, 35655, 35661, 35683, 35722,
    35727, 35773, 35878, 35976, 35977, 36165, 36253, 36278, 36309,
    36455, 36483, 36485, 36493, 36511, 36613, 36614, 36615, 36743,
    36745, 36746, 36747, 36769, 36889, 36924, 36933, 37276, 37497,
    37890, 37891, 37892, 37986, 37989, 38045, 38158, 38176, 38178,
    38183, 38379, 38475, 38497, 38549, 38558, 38568, 38750, 38775,
    38782, 38785, 38831, 38872, 38930, 38932, 38933, 38990, 39032,
    39058, 39202, 39297, 39303, 39318, 39361, 39751, 39755, 39784,
    39831, 39838, 40098, 40167, 40336, 40368, 40392, 40393, 40397,
    40403, 40405, 40594, 40605, 40631, 40681, 40686, 40857, 40898,
    40927, 40994, 41079, 41165, 41216, 41234, 41288, 41329, 41331,
    41336, 41473, 43954

2. New features

  * DOM Level 3 Core/Events/XPath support.
  * Improved WMF transcoder support.
  * Override style declaration support.
  * Near complete SMIL Animation support.
  * SVG 1.2 features:
      * resource documents
      * shapechange and RenderedBBoxChange events
      * SVGGlobal startMouseCapture/stopMouseCapture methods
      * handler elements

3. Improvements

  * The SVG namespace declaration is now not assumed for the document element
    when an SVG declaration is not present.  Certain non-conformant content
    will now be identified as being in error.
  * New MacRenderer implementation that avoids 10x slowdown on
    Tiger.
  * Mutations to flowText contents now cause the rendering to update, and
    flowRegion objects are now rendered.
  * Support for normalized path segments on SVGAnimatedPathData, thanks to
    Andres Toussaint.
  * Updated pdf-transcoder.jar to one built from FOP 0.9alpha1:
      * This fixes text being drawn as text in PDF.
      * JPEG streams being embedded as JPEG.
      * Default bitmap dpi is 300.
      * Most gradients and patterns are now correct (pattern overflow isn't
        handled properly although most of the code is present), and
        gradients with complex repeats are rasterized due to difficulties
        expressing them in PDF.
  * Transcoders with the KEY_EXECUTE_ONLOAD hint set force the document
    to be dynamic.
  * Added JIIO codecs to Batik (not enabled by default)
  * Support for finding XML Parser through JAXP.
  * Enhancements to ArabicTextHandler.
  * Update Manager's MIN_REPAINT time is now adjustable.
  * GraphicsUtil warning about Graphics from BufferedImage
    w/o BUFFERED_IMAGE hint is now controllable with property.
  * JSVGScrollPane now has 'scrollbarAlwaysVisible' mode.
  * Improved Path parser so it is more conformant.
  * Updated Rhino to 1.6R5.
  * Documents in Squiggle's Go menu now have the full URI as a tooltip.
  * Better version number reporting from org.apache.batik.Version.
  * Implemented SVGPathElement.getPathSegAtLength.
  * Implemented 'getFunctionName' for built in CSS functions.
  * Exposed SAXDocumentFactory's parser to subclasses.
  * Added a -snapshotTime option to the rasteriser.
  * Now using Apache XML Commons External, for SAC, DOM 3 Core, SMIL Animation
    and SVG 1.1 DOM bindings.
  * Encoding as specified in a Content-Type header's charset parameter is now
    honoured when scripts are loaded.
  * Changed InterpreterFactory so that multiple MIME types can be associated
    with a given Interpreter.
  * text/javascript, text/ecmascript, application/javascript and
    application/ecmascript are now all supported as script types for Rhino.
    Processing of application/ecmascript is as specified in RFC 4329.
  * Improved integration when running under Mac OS X: Squiggle's menu bar
    is now at the top of the screen, the application window has a proper
    name and functional About/Preferences/Quit menu items, and a Squiggle.app
    application bundle can now be created.
  * JAR files now have Implementation-* attributes.
  * Added an interface for all ImageRenderers to get/set RenderingHints.

3. Bug fixes

  * SVGMatrix.rotate now takes deg rather than radians.
  * Fixed mouseout/mouseover tracking, which regressed some time between
    1.5beta5 and 1.6.
  * Fixed misplaced glyph for mixed LTR and RTL text.
  * Fixed handling of tooltips from title elements.
  * Fixed rendering error (bug 36511).
  * Fix for broken event propagation across 'use' element shadow tree boundary.
  * Fixed a bug in the handling of 'x', 'y', 'width', 'height',
    on feImage elements.
  * Fixed bug in EventTarget.{add,remove}EventListenerNS wrapper
    for Rhino.
  * Fixed memory leak with dynamically added title tooltips.
  * Fixed bug with update regions and filters.
  * Fixed a problem with feSpotLight and feSpecular lighting (bug 36745).
  * Fixed bug with event removal for multiple events and the same target
    (thanks Andreas).
  * Fixed problem with lowercase 'European' vowels (accented vowels).
  * Fixed duplicated fragment identifier bug.
  * Fix to allow transcoders that execute onload to work with SVG 1.2
    documents.
  * Fix incorrect reporting of modifier key states in DOM UI event objects.
  * The broken link image works again now.
  * Convert "" to null for namespace URIs given to public DOM interfaces.
  * Gradient scaleback is now 0.999 rather than 0.97.
  * ttf2svg now emits a document with an SVG namespace declaration.
  * Include the o.a.b.ext.awt.image.spi.{ImageWriter,RegistryEntry} service
    resource files in the codec jar.  (Fixes problems with data: URIs for
    images when Squiggle is run from the jars.)
  * Fixed problem with ZoomAndPan handling overriding user disabling of
    interactors.
  * More sensible handling of source 'URLs' in svgrasterizer.
  * Greyscale sources are now treated as if they have sRGB Gamma.
  * Fixed SVGTextContentElement.getExtentOfChar to use glyph metrics rather
    than the glyph shape bounding box.
  * Changed Errors to UnsupportedOperationExceptions for unimplemented methods.
  * JSVGViewerFrame uses reflection for the Rhino debugger to enable
    compilation without Rhino.
  * Fixed phantom 'double click' bug.
  * Handle 'style' elements added and removed from the document when they are
    not at the root of the addition/removal.
  * Make CSS engine not throw an NPE for extension elements that don't expose
    an OverrideStyleProvider.
  * Fixes for text-on-a-path with glyphs who's geometry doesn't "start" at 0.
  * Various deadlock fixes.
  * Fixed bug where writing TIFF with JPEG compression fails.
  * Fixed significant performance bug in creating geometry (it was
    parsing the entire path for each entry in the path, O(N^2)).
  * Fixed SVGTextContent interfaces on FlowRoot elements.
  * Fixed NPE when reloading an SVG 1.2 document quickly.
  * Fixed bug where mousing over 'a' elements would result in exceptions.
  * Avoid NPEs when loading scripts from file: URIs.
  * Fixed flowText regression by rebasing off SVGOMTextPositioningElement.
  * radialGradient has a default value of "50%" for 'r' (thanks Tonny).
  * Fixed bug in key event dispatching when compiled under JDK 1.3.
  * Avoid precision errors in stringification of transform list values
    (thanks Nick).
  * Unmaximize Squiggle window if the "Automatically resize window when
    loading documents" option is turned on.
  * Fixed improper rendering of the batik70.svg sample under OS X.
  * Fixed synchronization bug in the SVG generator.

4. Misc

  * Code now compiles under Java 1.5 and 1.6.
  * Lots of code/javadoc cleanups.
  * New Forrest-based website for Batik.
  * New about box/splash screen.
  * Visual refresh for Squiggle's toolbar icons and Preferences window.

1.5.1 -> 1.6
------------

1. Bugzilla problem reports fixed: 

         28035, 28188, 28429, 28432, 28487, 29110, 29891, 30580, 30584, 
         30739, 30923, 30925, 30948, 31603, 32108, 32435, 32872, 32951, 
         33242, 33733, 33890, 33923, 33986, 34030, 34041, 34115


2. SVG 1.2 Features:

 * All SVG 1.2 elements/features have been moved from
   the Batik namespace into the SVG 1.2 namespace (except for
   flowText which remains, due to some loss of functionality
   with the current flowRoot and potential spec instability).
 * Implementation of SVG 1.2 flowRoot element (text flow into
   arbitrary regions with bidi support).
 * There is now an SVG12CSSEngine which has support for new
   properties in SVG 1.2
  

3. Improvements:

 * Massive performance improvement (>10X) for drawing on Mac OS X.
 * Document.getElementById now uses a Hashtable to speedup lookups.
 * JavaScript can now be used in an Applet out of the box.
 * Added support for 'font' shorthand property.
 * Added support for 'deselectAll' SVG DOM call.
 * getEncolsureList and getIntersectionList are implemented.
 * Added support for get[Screen]PixelUnitToMillimeterX/Y
 * SVG DOM getTotalLength, and getPointAtLength are now implemented.
 * Support for preserveAspectRatio DOM methods.
 * Squiggle browser now has a 'Save As' menu item.
   * Can toggle if xml:base should be added to saved SVG.
   * Can 'pretty print' saved XML for readability.
 * ExtensibleSVGDOMImplementation no longer exists,
   the SVGDOMImplementation is now extensible.  There is a
   new baseclass ExtensibleDOMImplementation that provides
   extension capabilities to any DOMImplementation subclass.
   As a result the name of the services meta-file has moved to:
    'org.apache.batik.dom.DomExtension' 
   from:
    'org.apache.batik.dom.svg.DomExtension'.  
   People with DOM extension will have to update the service file
   accordingly.
 * ExtensibleDOMImplementation now allows 'extension' elements in
   the SVG Namespace (they are always lower priority than 'buit ins').
 * When a new Document is loaded a new BridgeContext is created for it.
   As a result each document now get's it's own CSS engine.
 * ttf2svg now supports 'auto ranging' so it looks at the font
   to decide what to output.
 * DOM will now detect when the child passed to insert/remove
 * AbstractParentNode will now throw a DOM Hierarchy exception
   if you try to append a node as a child of it's self (PR 34030)
 * Anti-aliased clips (hqClip patch).  This can be disabled by setting 
   the transcoding hint to vector or print (so content doesn't get 
   rasterized). PR 29110.
 * Control of tiled image rendering based on rendering hint.
   Using this hint may greatly increase the amount of memory used.
 * The Marker interface now has a 'getCharIndex()' method to
   get the index of the character the Mark is associated with.
   node is not the child of the node insert/remove is called on.
 * DrawImage now defaults color model to sRGB_Unpre if the destination
   doesn't include alpha.
 * The pattern element now shares one rendering tree across all
   references.
 * Some improvements for Kerning in SVG Font (faster)
 * Arabic diacritics are now positioned relative to the right edge
   of the base glyph. Bug 28429
 * Basic DocumentType implementation, if present DOMUtilities will
   now emit a proper doctype specification
 * GVT Tree walker can now be given any node in the GVT tree and it
   walk just that subtree.
 * Many places now use getResource as Stream instead of Resource
   bundles where localization is not wanted/needed.
 * Tweaked UpdateManager repaint scheduling alg.
   It now tracks when the rendering goes 'out of date'
   with the document and runs runnables until it is out of
   them or MIN_REPAINT_TIME is exceeded.
 * Arc2D now translated to path with elliptical arc commands
   in SVGGraphics2D interface.
 * JSVGScrollPane now uses bbox of geometry if no viewBox is provided.
 * Refactored the text and flow bridges so make things cleaner.
 * Implemented textLength to return the value of
   getComputedTextLength when the textLength attribute is not
   set.
 * Potentially large optimization in BBox generation
 * The DOM tree viewer now has an option to hide text nodes that
   only contain whitespace.
 * The DOM tree viewer now includes the id of an element in the
   tree view if it has one.
 * Improvements to empty bounds handling, should decrease update areas.
 * GVT Render requests are now compressed.  This helps when doing
   live resize of large documents.
 * Some refactoring/restructuring of svggen.XmlWriter to potentially
   make it a pretty printing DOM serializer.
 * Latest pdf-transcoder from FOP which supports '-dpi' switch.
 * ScrollPane now should do a better job of fitting document w/o
   scrollbars
 * title and desc changes are now dynamically tracked.
 * ScriptFactorys now loaded via Services interface.
 * Made the JDK image loading Mediatracker static 
   (so others could use it easier).
 * ParsedURL now uses the current Batik version as it's default
   UserAgent
 * Rationalized Text Selection code.  Copy to clipboard is now done
   by JGVTComponent only if 'unix style' text selection is requested
   (default).

4. Bug Fixes:

 * Fixed all known memory leaks with loading images.
 * Changing documents in JSVGScrollPane now works properly.
 * Fixed a small memory leak when changing xlink:href on a
   use referencing a local element.
 * No longer uses Thread.interrupt.  On some JVM's this can confuse the
   class loader.
 * Adding elements in the 'null' namespace nolonger causes a NPE exception.
 * Fixed colorspace error in ColorMatrixRed.
 * Line element no longer has a fill associated with it.
 * Fixed bug #29416 - problem with empty quotes in CSS.
 * Fixed bug #34210 - element selectors are now case sensitive
 * Index out of bounds error fixed in linear gradient.
 * AOI problem in PrintTranscoder now fixed.
 * Null Pointer exception when PrintingTranscoding documents
   using SVG fonts is fixed (the BridgeContext was being
   disposed of too early).
 * PrintTranscoder now uses UserAgent to report exceptions
   rather than silently swallowing them.
 * Fixed some minor bugs in the GlyphLayout code for flow regions.
 * Very minor tweaks in JSVGScrollPane.
 * Applied patch 28188, fixed tooltip text & title text, thanks Glen
 * Applied patch 28487, fixed enabled/disabled state for
   toolbar buttons, thanks Glen
 * Fixed bug in text selection (programatically selected text wouldn't
   clear properly.
 * Fixed bug 23035 - Xlink attributes initialized with wrong values.
 * Fixed Bug 28785 - 'a' element not clickable with namespace prefix.
 * Bug 30584 - Concurrent Modification believed fixed (couldn't get
               test case to fail after fix).
 * Bug 29891 - Rasterizer problem if w/h == viewBox w/h
 * No more 'flicker' when panning a dynamic document.
 * CmapFormat4 now clips charCode 0->65534.
 * All Object.wait calls are now in while loops to prevent spurious wakeups.
 * DOM keypress events will now be generated for so called
   Java 'action' keyEvents (previously only keydown/up were
   generated).
 * Fixed a bug in the PNG Encoder for some 1,2 or 4 bit/pixel images.
 * Fixed bug in PackBits TIFF files with multiple IFD's
 * The 'debug' messages are now synced with GUI messages.  Also they
   are localizable.
 * Tweaked the way we generate paths to avoid a bug
   in the Mac OS X Java implemention where a close
   followed by a moveto didn't join the path properly.
 * Fixed width/height change onload bug in transcoders submitted
   by Simon Mieth.  Thanks!
 * Fixed a bug with CSS shorthand properties not being set by presentation
   attributes.
 * DOMImplementations now return empty documents if
   qualifiedName in createDocument is null (PR 32108)
 * Fixed 'flashing' with JSVGScrollPane.
 * Fixed PR: 30923.  Text children now respect display and
   visibility properties (including dynamic changes).
 * Fixed visibility update bug reported by George Armhold.
 * Fixed visibility bug on image elements.
 * getBBox should now work per the specification.
   a) fill="none" will still give bbox
   b) It is now the tight bounds of the geometry
   c) It does not include the stroke anymore.
   d) When called on 'undisplayed' elements returns null (not sure
      what the spec really says about this).
 * Modification of use element's width/height now works.
 * Fixed potential interrupted bug in the RunnableQueue class.
 * The document loader cache now uses soft references to avoid
   being a memory leak.
 * Improved the xml:space support.
 * textPath no longer supports 'position attributes' on it's self
 * textPath now supports progression position attributes provided
   by parent/child tspan/text nodes.
 * systemLanguage, requiredFeatures, requiredExtensions now all fail
   to match when given an empty string (as specified by the SVG
   specification). Bug #33242 (thanks Rick Graham).
 * Fixed potential synchronization issues in JavaScript intervals.
 * Small optimization in Event dispatch.
 * Removed debug print from SVGOMAnimatedPreserveAspectRatio.java
 * Synchronization fix in SoftReferenceCache.
 * SAXParser now includes 'ignorableWhitespace' when building the
   DOM tree.  I also tweaked the way text and CDATA sections are
   created.
 * UpdateManager is now always the 'source' of UpdateManagerEvents
   previously in some cases it was an inner class.
 * Fix for masks when using Alpha Premultiplied (Mac OS X).
 * Improved consistency of events from the RunnableQueue.
   Calling suspend/resumeExecution should now always generate
   a runHandler event (although the events may be "compressed"
   so suspend/resume may only generate one resume event).
 * Fixed bug in handling kerning elements with multiple glyph entries
   in g/u 1or2.
 * Removed use of 'getTagName' to use 'getLocalName' to avoid issues with
   prefixed svg elements.
 * Fixed a mistake in the generation of SVG fonts that include
   arabic forms (PR 33733)
 * Fixed handling of complex paint types (base types with
   fallbacks).
 * Fixed CSS SVG DOM interfaces for complex paint types
   (see bug 33923).  Added new test for same.
 * Text Selection events are now generated even when the
   done event is not over a graphics node.
 * The double '#' bug is gone in the SVG Pretty Printer. PR 33890
 * The worst of the 'extra spaces' problem with the pretty printer
   is solved. 
 * Fixed updates to SVG Font based text (PR 34041)
 * Bug fix in NumberParser (thanks dvholten)
 * Fixed bug 34115 - CSSStyleDecl doesn't collapse properties.
 * JSVGCanvas no longer holds cached viewBox so it responds
   properly when viewBox is updated in SVG content.

5. Misc:

  Documentation updates
  JavaDoc will now reference W3 and JDK docs
  Updated MAINTAIN file
  Splash/startup screen now embed font (a little simpler SVG as well).
  Some Rhino improvements.
  New Tests for bugs.

  Thanks to everyone on batik-dev/users for reporting problems and
  working on finding solutions.

1.5 -> 1.5.1
------------
1. Security:

   A script security issue was reported in the Batik Squiggle
   browser. Squiggle uses the Rhino scripting engine and some features
   of that engine can be leveraged by malicious scripts to gain access
   to otherwise protected resources (like the file system).

   The Batik team has worked with the Rhino team to fix the isssue
   that was reported and the Batik 1.5.1 patch release addresses the 
   issue.

2. Features:

   Rhino JavaScript Debugger now integrated with Squiggle.
   Better line numbers in error messages.
   Zachary DelProposto's Scroll pane class is added.  Allows for
      scrollbars on SVG documents.
   Cool solitaire examples in SVG.

3. Performance fixes:

   Parents who have children added/removed no longer invalidate
      their entire area.
   When a element is removed or CSS properties changed all next
      siblings are not repainted unless CSS rules change them.
   First event dispatch is much quicker than the past.
   Switched to Xerces 2.5.0 to improve performance of data protocol.
   Changes to paint properties on text is now _much_ faster.
   When using many objects from an external file the file will only
     be loaded/parsed once.
   Modifying x/y and some viewBox changes on SVG elements is now much
     faster.
   Better JPEG embedding in PDF.

4. Conformance:
   Batik now passes all non SMIL-Animation tests from the SVG Working
     Group beSuite.

   @font-face CSS property and font-face elements are now compliant
     with SVG specification.

   Changes to the 'class' attribute now update rendering tree
     correctly.

   selectSubString now implemented.

   Dynamic modification of display, and marker properties supported.
   SVG 'a' element respects 'preventDefault' on event object.
   Pattern element no longer applies object bounding box transform
      if viewBox is specified.

5. Bugs:

   Fullscreen mode (F11) works again.
   getExtentOfChar() returns bbox in text elements coordinate system.
   SVGPoint now supports matrixTransform in more cases
   clientX/Y now correct when target is a text element.
   Bugfix in parsing some TrueType fonts fixed.
   Now applies the 'all' media type to all content.
   Image transcoders default to 400x400 rather than issuing an error
     (consistent with viewer).
   The properties opacity/filter/masking/pointer-events now work
      on SVG elements.
   Clearing text selection now works
   Zero width/height image elements now work properly.
   BBox info is correct for zero width/height items.
   Fills/strokes text when text-rendering is set to geometricPrecision
      rather than using drawGlyphVector.
   getCssText() returns the relative version of URI's
   SVG Graphics2D outputs correct rendering hints for text.
   TextNodes returns the correct bounds.
   Proper text child set as 'target' of events.
   Events handled correctly for documents that reference the same
      image multiple times.
   Image are displayed as 'broken links' instead of being an error.
   FeColorMatrix now respects color-interpolation-filters property.
   elem.style.setProperty now works for shorthand properties.
   Fixed race condition in JSVGComponent.set[SVG]Document.
   Fixed several memory leaks dealing with use element.
   Glyph element's 'd' attribute uses inherited winding-rule.
   SVG 'style' element no longer generates multiple 'xml:space' attrs.
   SAXDocumentFactory now generates only one CData node for each
     CData section in source document.
   Memory leak with addEventListener and objects that ref event target fixed.
   Click events now allow a small amount of 'slop'.
   Fixed bug in rendering with Double Buffering.
   Clicking on the Canvas no longer clears the system clipboard.
   No longer recascades elements in use tree from foreign documents
   Now supports CSS properties with the max negative 32bit int value
   Now supports more JPEG image types.
   No longer throws a Class Cast exception when using named colors
     as the fallback for icc-colors
   clearTimeout, clearInterval no longer throw exceptions for null
     objects.
   clipPath now respects clip-rule when using 'use' element.

  Bug PRs: 12536, 23030, 23038, 23079, 23177, 24919, 25251, 25463


1.5beta5 -> 1.5
---------------

1. Features:

       - With JDK 1.4 text-rendering="optimizeSpeed" will render
         non-anti-aliased hinted text (for axially aligned text).

       - The pointer-events property is now handled correctly for elements
         without fill or stroke paint.

       - Improved stroke accuracy when shape-rendering="geometricPrecision"

       - JSVGComponent.setSVGDocument with 'null' now releases all
         heavy resources and makes the Canvas display only Background.

       - Many memory leaks fixed.
        
       - Transcoders and JSVGComponent will now accept any Document
         and convert to a Batik SVG Document if needed.

       - Script interpreters are loaded lazily.  This means that unless you
         use them (by having event attributes or script elements) you
         don't need any script engines.

   Performance Improvements:  

       - Image drawing is now 5-10x faster, for most raster images.

       - Significant performance improvement for patterns that are
         filtered or have filters applied to them (take Batik70 for
         example).
        
       - AbstractParentNode insertNode, removeNode, replaceNode are
         now much faster for Parents with many children.


   Library changes:

       - Updated to the Rhino 1.5R41 library

   Others:

       - Added control of floating point precision used in SVGGraphics2D
         (see the org.apache.batik.svggen.SVGGeneratorContext class)

       - Batik now almost never opens image URL's twice.

       - JSVGComponent/BridgeContext now has three levels of interactivity.
         STATIC, INTERACTIVE, DYNAMIC.  With increasing memory overhead
         and build time requirements.

       - Improved test environment with automated memory leak, performance,
         threading and interactivity tests. There are now over 1000 tests
         in the regard regression test suite.

       - Window.parseXML now returns a Document when no 'host'
         Document provided.

2. Bugs:

       See: http://nagoya.apache.org/bugzilla for details on each bug.

       - 6526 pointer-events does not work for fill="none" stroke="none" 
       - 6683 JSVGCanvas - Problem adding to JScrollPane 
       - 6951 see samples/tests/spec/paints/linearGradientLine.svg 
       - 8854 Fix feImage implementation according to latest SVG 1.0/1.1 c 
       - 9981 Improve Documentation on Scripting with Java 
       - 9994 text-decoration:underline is incorrect with tspan elements. 
       - 12013 Squiggle renders jagged curves/paths 12079 transcoder.wmf 
       - 13870 Language Code errors 
       - 14011 problem using rasterizer with the headless option 
       - 14673 not-so-pretty text rendering 
       - 14788 TranscoderInput(XMLReader reader) does not seem to be implem 
       - 14789 TranscoderInput(Document document) is incorrect 
       - 15348 SVGGraphics2D should not export scientific notation on CSS p 
       - 17168 SVGGraphics2D has package level protected on DOMGroupManager 
       - 17863 API to get Version info 
       - 17965 SVGGraphics2D adds 'font-size' attribute to 'line' and 'circ 
       - 18143 Removing attribute using Element.removeAttributeNode doesn't 
       - 18566 JSVGComponent packs its parent window 
       - 18640 Problem with setSVGDocument 
       - 18840 A few issues with the Batik source 
       - 18841 Problems with imports in Batik source 
       - 19017 error when attempting SVG to PDF conversion 
       - 19363 zero width on rect element causes error 
       - 19392 Bug in "org.apache.batik.dom.util.DOMUtilities.java" 
       - 19865 Exception rendering linked PNG file. 
       - 20147 JSVGCanvas doesn't function with JInternalFrame 
       - 20201 incorrect gAMA chunk for sRGB 
       - 20331 bug on batik SVG DOM implementation on the SVGPathSegArcAbs 
       - 20332 bug on batik SVG DOM implementation on the SVGPathSegCurveto 
       - 20811 A rect disabled because of zero width or height doesn't get 
       - 21125 XML comment cause css parser to abort parsing 
       - 21259 Drawing on an SVGGraphics2D and then using JPEGTranscoder pr 
       - 21352 XML comments inside script elements treated as script code 
       - 21358 Dynamic Update of content referenced by a <use> does not wor 
       - 21361 Bug on SVGPaint implementation 
       - 21362 Bug on CSSStyleDeclaration on URI reference 
       - 21374 Implement conformant Java Handler code 
       -       Viewer now properly displays w/h x/y locations in
               'viewbox' coordinates.
       -       Fixed dirty regions for nodes that change size then
               position in the rendering tree (where the new position
               has a different rendering transform).
       -       Fixed handling Fragment URL's with colon's in them.
       -       Batik no longer considers all @font-face rules a syntax error.
               Batik now just ignores them.  
       -       Fixed use elements in Documents with no base url.
       -       Runs of RTL Text now work with JDK 1.4.
               Nested embed and bidi-override properties should now work.
       -       Fixed bug setting viewBox on outermost SVG element as 
               described in:
       http://koala.ilog.fr/batik/mlists/batik-users/archives/msg03513.html
       -       JSVGComponent/Canvas now repaints when it changes size 
               (even when no viewBox).
       -       Modifying zero W/H rect, circle, ellipse now works properly.
       -       Fixed the handling of kerning attribute.
       -       JSVGComponent.setSVGDocument with a Document that has been 
               previously viewed in the Canvas now works
       -       'style' and 'script' elements no longer include the contents 
               of XML Comments (unless inside a CDATA block).
       -       Fixed a with absolute x/y attributes in non-text
               progression direction.
       -       Fixed JSVGComponent when it's working threads are 
	       interrupted.
       -       RhinoInterpreter once again binds the Global Object 
               as 'window'


3. Extensions:

       - Updated multiImage element to match WD.


4. Documentation:

       - Updated the FAQ with frequent questions on UpdateManager, DOM 
         updates and visual updates.

       - Updated the implementation status page and the home page.
   

1.5beta4b -> 1.5beta5
---------------------

1. Features:

   Library changes:

       - Xerces 2.3 is now the default parser. The parser can still be controlled
         through the org.apache.batik.util.XMLResourceDescriptor class.
       
         ** NOTE **

         While the general performance of the Xerces 2.3 parser seems equivalent
         to those of the Crimson parser used previously, there is a notable exception
         in the handling of long attributes. This makes a very visible difference 
         for SVG content that uses embeded images through the 'data:' protocol which
         can be extremely slow with Xerces 2.3. The bookOfKells.svgz example, which 
         used the data: protocol for rather long images has been removed from the 
         distribution as it does not load in a reasonable amount of time when using
         Xerces.

       - Rhino version has been updated from 1.5R3 to 1.5R4 allowing to fix 
         BR #11968 (DOM access functions should return ECMAScript strings). 

       - Removed SAX and DOM sources from the Batik project. Now use xml-apis.jar
         from xml-commons.

   Others:

       - currentScale/Translate now implemented (get and set).

       - Implemented getScreenCTM, fixed getCTM (SVGLocatable)

       - onzoom, onscroll, onresize events are now implemented.

       - Integrated pdf-transcoder from FOP.

       - Ctrl-K/L work in fullscreen mode.

       - initial support for SVGList ( provided for SVGPointList in 
         <polygon> and <polyline>, SVGPathSegList in <path>, SVGLengthList 
         in textual elements, SVGTransformList in graphical elements )

       - RFE #12735 : added support for dynamic modifications of 'xlink:href' in image element 

       - Now skips reading the SVG DTD unless validation 
         is turned on (improves load times for small docs).

       - glyph-orientation-horizontal now works.

       - linearGradient's can now be anti-aliased (radial gradient
         anti-aliasing also improved).

       - PNG Transcoder can now produce 1,2,4 & 8 bit indexed PNG's.

       - rasterizer can now be used in headless mode (JDK 1.4)

       - JPEG files now have X/YDensity set according to -dpi option.

       - Batik Docs now include a dependency graph for the jar files.

       - In ECMAScript, add the ability to pass an Object instead of a 
         Function to the Window.getURL() method in which case the 
         operationComplete method is called once the URL is retrieved.

       - SVGUserAgent now controls the default font-family

       - Added max-width and max-height support in the rasterizer infrastructure
         (contribution by Henri Ruini, Henri.Ruini@nokia.com)

       - Improvements to the WMFTranscoder by Luan O'Caroll

       - Added better error reporting for external scripts: errors now include the uri 
         of the external scripts in addition to the line number which is convenient 
         when an SVG file references multiple external scripts.
  
       - Modified the way SVG public identifiers are mapped to system identifiers. 
         The resource file now contains the mapping to the correct SVG DTD instead
         of only containing the allowed SVG 1.0 system identifiers for a hard 
         coded DTD. 
  
       - Added support for cursors.

Bug Fixes:

       - clientX/Y values in MouseEvents are now returned in screen
         pixels (it had been the viewBox coordinate system which was
         convenient but wrong and problematic for some cases).
        
       - Changed default extension for TIFF files to '.tif' from '.tiff'.

       - Fixed bugs in screen updating when the size/location of the
         root graphics node changed, and when transform on group
         changes after bounds on a child.

       - ToolTips no longer 'hang around' under JDK 1.4.1

       - The JSVGCanvas no longer steals focus and pops forward whenever
         the cursor passes over the canvas.

       - The JSVGCanvas will no longer thrown an exception if an SVG
         document is set prior to the component's size being set.

       - Text anchor for vertical text.

       - For corrupt/incomplete JPEG images now shows what it can.

       - Fixed bug with Indexed PNG's that have a bKGD chunk.

       - getRelatedTarget fixed for mouseout event.

       - glyph-orientation-horizontal/vertical no longer require a unit
         as per the SVG specification.

       - Fixes for 'jar' protocol.

       - Fixed compressed Tiff writing.

       - Missing Glyph's are now rendered correctly.

       - Text selection now works better with altGlyphs.

       - PrintTranscoder no longer scales 'real world' units wrong.

       - Fixed a bug involving the 'use' element with a width/height in
         an SVGFont referenced from external documents.

       - Collaboration with Philipe Converset (Qarbon) on an extension to define
         margins in filter regions. This is now supported in the Batik namespace.

2. Bugs:

        12389  Focus issue in Squiggle's URL text field
        12121  Cursor in URL field disappears
        12168  MarkerShapePainter delivers wrong PaintedArea
        12239  [PATCH] Moved some method local variables into class
        12954  add support for stroke-width:0
        15162  PNGTranscoder gives error message about TIFF transcoder
        17167  stroke-dashoffset does not support negative length
        13713  <use> element fails when using percentage values x/y coords
        17183  baseline-shift doesn't refer to parent "line-height"
        15063  Version-dependent code in package org.apache.batik.gvt.font
        12736  Allow non-element nodes to be added to g and svg elements.
  
       - Added Units resolutions to CSS rects

       - 'd' and 'points' can now be empty on <path>, <polygon> and 
         <polylines>

       - Now support the transform attribute on the <switch> element

       - Fixed class loader delegation bug in RhinoClassLoader
  
       - Patch from Keiron Liddle fixing PNGTranscoder KEY_INDEXED hint type 
         issue (was Boolean instead of Integer)

3. Extensions:

       - Binary distributions now include jar files that can display
         extensions.

       - The multImage element can now reference SVG images as well
          as raster images.

       - flowText now supports vertical-align.

       - flowText now uses 'flowRegion' and 'rect' to describe flow
         areas.

4. Documentation:

       - Status of Batik implementation of SVG DOM.         

       - Diagram showing the various Batik jar files and their relationships.


1.5beta4 -> 1.5beta4b
---------------------

This is a patch release fixing a bug on linking.

1.5beta3 -> 1.5beta4
--------------------

1. Features

  - Squiggle Browser:

       - the F11 key lets users enter the full screen mode or exit the 
         full screen mode if they are already in that mode.

       - the Browser can now handle XML files which contain a stylesheet
         declaration referencing an XSL transformation. If that transformation
         generates an SVG document, Squiggle will display it.

         By default, this feature is disabled because it requires that
         the user either uses the JDK 1.4 (or greater) version (which at
         this time is not a requirement for Batik) or that an XSL transformation
         engine (such as Xalan) be in the classpath.

         To enable the feature, uncomment the following line (i.e., remove the '#'):

         #org.apache.batik.apps.svgbrowser.XMLInputHandler  

         in the:

         resources/META-INF/services/org.apache.batik.apps.svgbrowser.SquiggleInputHandler

         file.

  - Scripting:

       - Added the ability to use an ECMAScript object providing an handleEvent 
         method for the listener argument of the addEventListener method on DOM objects 
         (previously only ECMAScript functions were accepted as parameter). [RFE9149] 

2. Bug Fixes

  - Images - bounds/hints now updated correctly when changed via script
  - Local refs now work with null Document URL.
  - Double buffered rendering now updates correctly on Mac OS X.
  - ParseXML no longer assumes doc is SVG.
  - Squiggle can now do Http authentication.
  - SVG Font bounds fix
  - Text layout fix (anchor and textLength)
  - Adding/Removing children no longer invalidates entire parent region.
  - handling of position attributes (x,y,dx,dy,rotate) on emtpy tspans has been fixed.
  - Fixed improper handling on <altGlyphItem>
  - Removed unnecessary font matching with <altGlyph> which can be found
  - 'stroke-dasharray' now works with units,
  - fixed bugs (CSS !important rules, DOM replaceChild on the root element,
    SVG paint parsing, CSS percentages).


1.5beta2 -> 1.5beta3
--------------------

1. Features:

  - Squiggle Browser:
       
       - now keeps a history of recently visited URIs

       - offers more control over the security features

  - Enhanced Security Support:
       
       - downloading of all external resources can now be 
         controlled through the UserAgent. This includes 
         scripting (as before) but also <image>, <use>, and
         all elements which may reference external resources
         (over 15).

       - users security policy now takes precedence over the 
         application policy. Users can set the java.security.policy
         system property when starting a Batik application and it 
         will take precedence.

  - Interactivity and Scripting Support Improvements:

       - filter, mask, gradients, clip, pattern and marker are now
         updated when the geometry of the referencing graphical 
         element is dynamically changed.

       - implementation of keyboard events (KeyEvents from DOM Level 2 
         Working Draft). In the near future, the batik team will 
         implement the TextEvents from the DOM Level 3 Events module.

       - Newly supported elements (can now be modified by scripts):
         <svg>, <image> and <text> element children (such as <tspan>);

      - Support for addition/removal/modification of stylesheets

  - Misc: 

       - external scripts can now be gzipped.

       - added getPixelUnitToMillimeter to UserAgent classes
         NOTE: the getPixelToMM and getPixelToMillimeter methods will be
               removed after 1.5b3 is released!

       - Improvements in the support for the <flowText> extension

      - Added support for attribute/id/adjacent selector and :first-child
        dynamic updates,

2. Bug Fixing:

  - #1075, #4834, #5233, #5347, #5806, #6642, #6683, #7053,#8330, #9740, 
    #9058, #9067, #9304, #9429, #9520, #9779, #9804, #9272, #9276, 
  - bug in feColorMatrix 
  - DOM: getElementByTagName, invalidateElementsByTagName, cloneNode(false)
  - mouseout bug on <tspan> fixed
  - File->Open dialog bug on JDK 1.4 fixed.
  - Memory leak fixes
  - ttf2svg now emits kern elements with the proper sign
  - MultiImage now respects xml:base
  - Fixed mouse location bug

3. Documentation Update:

  - Many additional links have been added to the Batik documentation
    as well as improvements in its structure and content, thanks to 
    the work of Robert Di Blasi.

1.5beta1 -> 1.5beta2
--------------------

** Note on the new security support **

- Scripts are now run in a sand-box by default for the Batik sample
  applications (the Squiggle SVG Browser and the Rasterizer). Users of
  the SVG toolkit who what to run scripts securely need to add
  security support in their application and may use the work done for
  Squiggle as an example 
  (e.g., see the org.apache.batik.util.ApplicationSecurityEnforcer class).
 
- There is a known limitiation: scripts loaded from a 'file:' url
  cannot access (i.e., load files) under the same root. This is not
  true for scripts loaded from a server (e.g., 'http:' urls).

1. Features:

  - Scripting support for styling properties:

      - Most of the CSS properties and style attributes can now 
        be changed dynad:/work/dev/svg/apache/builds/batik-1.5beta3/mically. See:
        http://xml.apache.org/batik/supportedProperties.html

      - Added support for style and class attributes dynamic updates


  - DOM support:

      - Implementation of SVGLocatable including SVGMatrix

      - All class members of type: SVGLength now works on most of the
        graphical elements (rect.x.baseVal.value)

      - Implementation of CSS DOM (getStyle, getComputedStyle...)


  - Events/Interactivity:
     - DOMFocusIn, DOMFocusOut and DOMActivate event types are not supported


  - Security:

     - Secure scripting is now in place. Scripts in ECMAScript or 
       Java code are run in a sand-box by default. The Batik browser
       (called Squiggle), has the following security options:
        . secure execution of scripts can be on or off.
        . scripts for a given language can be disabled/enabled.
        . scripts can be constrained to come from the same origin
          as the document referencing them.
       In the Squiggle SVG browser, the security options can be set 
       through the Preferences dialog, in the browser options panel.


  - Misc:

     - xml:base is now supported for the image element.

     - <flowText> extension element that flows text into multiple boxes.
       Trying to track the SVG 1.1 Working Group on this element.

     - getURL, parseXML supported in the Window object.


  - Testing:

     - Improved test infrastructure. There are new tests for handling
       automatic regression testing of scripting features and there are
       more tests (801 now) in the regard regression test suite.


2. Bug Fixing:

  - Fixed bugs with textPath w/ text-anchor, and textPath w/ multiple
    trailing tspan elements.

  - Work around for RenderingHints when Batik Jars loaded multiple times
    in the same JVM (through different class loaders).

  - Fixed a bug with X/Y Resolution for Tiff.

  - UIEvents are not dispatched properly on <text> children (<tspan>...)
  
  - <a> is now supported on <text> element children

1.1.1 -> 1.5beta1
-----------------

** Note on enhanced scripting support **

- Support for DOM Core and DOM Events (Mutation events and UI events)

- Limitations:
   - No support for dynamic modification of CSS properties.
     In particular, setAttributeNS of presentation attributes
     such as 'fill' or 'stroke' or modification of the 'style'
     attribute are not supported.

1. Features:
  - Enhanced scripting support: it is now possible to modify 
    SVG documents through scripting
  - Tiff Transcoder now takes FORCE_TRANSPARENT_WHITE hint.
  - Tiff Transcoder now writes resolution tags.
  - Implementation of SVG 1.1 solidColor element (in Batik extensions)
  - Implementation of multiImage element (a Batik extension).
  - Event compression mecanism (e.g., concatenates mouseMove events)

2. Bug Fixing

  - textLength now works in all cases except nested textLengths.
  - 'image' element now respects xml:base.
  - Renderer now reuses image buffers more often.
  - Speed improvements in cases with lots of tspans.
  - Filtered Raster Images now _much_ faster.
  - Things mostly work with current Mac OS X JVM (still some trouble with
    images/filters).
  - Applying a ICC Profile no longer messes up alpha channel in some cases.
  - PNG Transcoder now always writes sRGB chunk.
  - feImage image positioning fix
  - <use> bridge fix,
  - SVG generator <use> export fix
  - 
1.1 -> 1.1.1 Release
--------------------

1. Bug fixing:

    - dx/x and dy/y now work when combined.
    - clip-path now takes transforms into account
    - AbstractRable constructors problem fixed.
    - Put in workaround for worst of Mac OS X text problems (layout
      still isn't 100% correct in most cases).
    - Removed code that is no longer needed in GlyphLayout
    - Fixed the last text layout issues on Mac OS X (GlyphVector was
      returning the metrics for the first char for all chars in the vector.
      We now use the difference in defaultGlyphPositions to get the
      character advances).
    - Fixed JViewerFrame issue with large (width/height) documents.

2. Improvements
    - Text bounds computation is now much faster

1.1rc4 -> 1.1 Release
----------------------

1. Bug Fixing and Improvements

    - Code factorization in parsers.
    - Parsers bug fixing.
    - Added examples (maps, bookOfKells example, sunRise, mathMetal).
      Maps are a contribution of Andreas Neumann,
      MathMetal is a contribution of Christophe Held.
    - Added default xmlns and xmlns:xlink on root <svg> in generator
    - Integrated Ant task contribution from Henri Ruini
    - Improved error dialog (now includes details with the error stack trace).
    - Printing fix.
    - text-anchor fix.

1.1rc3 -> 1.1rc4 Release
------------------------

1. Features:

    - rewrite of rasterizer thanks to Henri Ruini's contribution
    - improved image handling strategies in the SVG generator thanks 
      to Paul Evenblij's contribution.
    - 'requiredFeatures', 'requiredExtensions' and 'systemLanguage' on 
      graphical elements
    - anchor on raster images and SVG images now works properly
    - anchor inside a SVG image now works properly
    - <a> inside <text> or <tspan> is now displayed
    - many tests added (transcoder...)
    - SVG viewer: added dialog box at start up time while loading classes
    - SVG viewer: limited number of files in history

2. Bug fixing:

    - memory leak fix
    - DOM core serialization
    - Parsing of CSS comments
    - SVG generator fixes (bug 4945 and 4389)
    - rasterizer fixes
    - text now works with JDK 1.4b3. Remaining issue with 
      BufferedImages.

3. Improvements:

    - general performance improvements in the build and rendering
      phases of the viewing component (for example float parsing 
      has been improved).

1.1rc2 -> 1.1rc3 Release
------------------------

1. Features:
   - Support for xml:base
   - TextSearch dialog is available (with a zoom option)
   - Full support for Alternate Stylesheets
   - Full support for CSS Media (including a way to define custom media)
   - Changed Text Selection UI. (selectable in the preference dialog)
     Added Text Selection API to GVT & swing packages
   - KeyStroke on the JSVGCanvas (for pan and zoom using keyboard)
   - SVG Font: The font lookup now takes into account namespaces
   - Provide a way to set the quality of an exported JPEG image
   - Printing now uses the CSS print medium
   - More complete handling of partial URLs
   - Two new extensions solidColor (a single color 'paint server' acts
     similar to the gradients and pattern fills/stroke), & paintSwitch
     similar to a switch element but children are paint servers instead
     of graphical elements.

2. Bug fixes:
   - Fix in gradient element boundary conditions
   - Fix and performance improvements in lighting filters
   - Fix in PNG encoding for BufferedImage' subimages.
   - feDisplacementMap fix (array out of bound exception)
   - font lookup now uses namespace properly
   - 'enable-background' now work on <svg> element
   - zero length on <rect>, <circle> and <ellipse> disable the rendering
   - linear gradient with x1=y1=x2=y2
   - printing now supports SVG URI fragment
   - Javadoc improvements and clean-up
   - Source clean-up
   - DOM: getElementsByTagNameNS now works properly
   - SVGDOM: huge cleanup of the implementation 
     (preparation for dynamic features)
   - CSS parser bug fix
   - Bridge: 'overflow' on SVG <image> should now work properly
   - GVT: cleanup the API - GraphicsNodeRenderContext is now obsolete
   

3. Testing

   - Reorganized tests (see samples/tests subdirectory)
   - Improved test reporting (i.e., better HTML report)
   - Simplified test infrastructure configuration files.
   - Lots of new tests (see samples/tests/structure/styling and 
     samples/tests/spec/structure for examples) and 
     samples/batikCandy.svg.


1.1rc1 -> 1.1rc2 Release
------------------------

1. Bug fixes:

  - 'preserveAspectRatio' now works properly on both raster and SVG images
  - bug fix with the 'clip' property on <symbol>, <image>, <marker>
  - thumbnail bug fix (sometimes the area of interest became hidden)
  - filename filter added to the file selection dialog in the svgbrowser when
     exporting as PNG and JPG
  - Infinite loop removed. Elliptical arc may cause an infinite loop if the end
  point of the arc was the same than current point in the <path> datas.
  - SVGGraphics2D can now generates SVG Font on demand

2. Improvements:

  - Added new custom filter extension (look at the samples/extensions/histogramNormalization.svg)
  - Added tooltips on <title> and <desc>.

1.0 -> 1.1rc1 Release
---------------------

1. Enhancements:
   a. HTTP headers fields (useragent/accepted...) now implemented.
   b. Anti-aliased gradients (when color-rendering set to quality).
   c. Small patterns are now much faster
   d. Ability to flush cached image data.
   e. full horizontal and vertical kerning now implemented for SVG fonts
   f. added support for orientation and lang <glyph> attributes
   g. improved support for Arabic text, includes handling of Arabic shaping 
      and ligatures for both SVG and system fonts
   h. improved text selection
   i. improved text rendering speed

2. Bug fixes:
   a. PNG files now use the proper gamma when reading/writing
   b. Off by one bug in image rendering fixed (background shows
      through on the left and top of image).
   c. Fixes in Base64 data handling (encoding/decoding)
   d. Absolute URL's shouldn't require document URI.
   e. color-interpolation-filters should now work :)
   f. fixed bug with font sizes < 1
   g. fixed problems in text chunk algorithm
   h. fixed bug with empty text nodes
   i. text decorations are now handled properly
   j. bidirectional text (including selection of it) is now handled properly
   k. fixed problem with alignment of vertical text
   l. fixed problem with em and ex unit conversion when font size 
      is not a float value (eg medium)

3. Misc:
   a. Now have automated tests for base64
   b. Now have automated tests for ParsedURL
   c. added some more test files for text and fonts

beta2 -> 1.0 Release
---------------------

1. Completed SVG Font Support.

2. Added vertical Text Support.

3. Major rework of the Batik documentation.

4. Added thumbnail

5. Bug fixes.

beta2rc9 -> beta2
-----------------

1. Contributions

   a. New contribution of a True Type Font to SVG Font converter that allows
      a set of characters from a True Type Font to be converted to the 
      SVG Font format. This contribution was made by David Schweinsberg
      of SteadyState (david@steadystate.co.uk). This contribution comes at
      the right time (i.e., as we just added SVG Font rendering suppor).

2. Improvements & new features

   a. SVG Font support. Initial revision of the soon complete support of 
      SVG Fonts. 

   b. New image encoder/decoder infrastructure. This allows a flexible
      handling of new raster image formats.

   c. Improved PNG encoder/decoder which now support the gamma chunk
      properly.

   d. Additional tests for SVGGraphics2D.

   e. Additional samples (mostly new SVG Font support examples).

beta2rc8 -> beta2rc9
--------------------

a. Polishing of existing features.
b. Bug fixes.
c. Improved test infrastructure.

beta2rc7 -> beta2rc8
--------------------

1. Improvements
  
   a. Full blown in-bound and out-bound linking support. See the 
      two new linking examples in the samples/test directory.

   b. Improved CSS error handling.

   c. New test infrastructure.

   d. Beanified SVG JComponent: see JSVGCanvas.

   e. Enhanced features in SVGGraphics2D to manage style properties.

   e. Improved error handling in SVGGraphics2D

2. Bug fixes

beta2rc6 -> beta2rc7
--------------------

1 - SVG Viewer Improvements

   a. Added support for double buffering (controllable by an option).

2. General

   a. Added line-number in error messages when parsing invalid 
      SVG document.
   b. Maintenance and Bug fixing. 
   c. JSVGCanvas is now a JavaBean which can be easily configured
      in IDE tools to add SVG viewing support in an application.
   d. Support for rendering hints such as text-rendering and 
      shape-rendering.

beta2rc5 -> beta2rc6
--------------------

1 - new implementation of the swing component and viewer.

  a. it is now possible to interrupt while loading, building or painting
  b. rotations are now applied on the image in real time. document is
     repainted when the mouse button is released.
  c. real time zoom on the image with SHIFT+BUTTON 2 Drag
  d. progressive rendering has been improved
  e. toggling debug traces on/off to see performance is now possible

2 - new implementation of the bridge package

  a. code cleanup
  b. percentages are now handled correctly (both for xml attributes and CSS
     properties - samples/tests/percentagesAndUnits.svg)
  c. error handling has been improved 
     (line number will be available soon)
  d. the building phase is now 30% faster
  e. bug fix with <pattern> (samples/tests/pattern*.svg)
  f. <marker> is now conformed to the current SVG spec (as of March 15)
  g. percentages are now handled correctly on the outermost SVG element (need
     to do the same for the ImageTranscoder)


3 - Transcoders. There are two new transcoders: 
   
  a. WMF -> SVG Transcoder. This is a contribution to the Batik project from
     Luan O'Caroll (Luan.O'Carroll@boimail.com). You can run the transcoder on 
     a set of Windows MetaFile (WMF) using the 
     org.apache.batik.transcoder.wmf.tosvg.WMFTranscoder application.

  b. Printing Transcoder. This implementation of the Transcoder interface will 
     print a set of SVG document to the default printer (see 
     org.apache.batik.transcoder.print.PrintTranscoder.

4 - Misc.
 
  a. support for System colors (see: samples/tests/systemColors.svg)

  b. Creating custom Graphics2D implementation is now easier. A generic part of
     the SVGGraphics2D has been moved into a new package: 
     org.apache.batik.ext.awt.g2d.
     This new package facilitates the creation of transcoders that will convert
     SVG documents to an arbitrary format by providing a Graphics2D implementation
     for that format that Batik can use to draw an SVG document (Batik can use 
     a Graphics2D to paint an SVG document).

NOTE: Known regressions.

  a. next, previous and thumbnail have been momentarily removed from the viewer.
  b. error messages are printed in the console and not in a dialog.
  c. the partial internal linking support has been removed until 
     a full implementation of linking can be done.
  d. printing has not been reintegrated into the new viewer.
  e. regressions may appear for scripting as it has been added in a hurry for
     this release. A clean pass will be done soon.
  f. text regression on tspan handling where dx/dy are temporarily not working.

beta2rc4 -> beta2rc5
---------------------

a. Text improvements.
b. Improved rendering (now uses tiling for faster panning).
c. Bug fixing.

beta2rc3 -> beta2rc4
--------------------

a. Handling of overflow property on <svg>
b. Filter and pattern optimizations
c. Color profile support
d. GVT optimizations

beta2rc2 -> beta2rc3
--------------------

a. Handling of the visibility property
b. Handling of references on gradients, patterns and filters
c. Baseline shift on text
d. Full support for markers.
e. Optimizations in filters and GVT.
f. Bug fixing (e.g., stroke's dash array)

beta2rc1 -> beta2rc2
--------------------

a. A lot of new text support (text length, letter and word spacing..)
b. Some support for markers (not complete yet, but should be complete
   shortly, probably by next week).
c. Some support for views (in linking). 
d. Cleaned up bridge code.
e. Improvements in the filtering code (there a some minor regressions
   which will soon be taken care of).

beta1 -> beta2rc1
-----------------

a Improved text support
b. Full filter implementation.
c. New Transcoder API.
d. Simplified package organization
xml-commons/java/external/LICENSE.dom-documentation.txt $Id: LICENSE.dom-documentation.txt 201084 2002-12-09 16:15:21Z vhardy $


This license came from: http://www.w3.org/Consortium/Legal/copyright-documents-19990405


W3C® DOCUMENT NOTICE AND LICENSE
Copyright © 1994-2001 World
Wide Web Consortium, <a href="http://www.w3.org/">World
Wide Web Consortium</a>, (<a href=
"http://www.lcs.mit.edu/">Massachusetts Institute of
Technology</a>, <a href="http://www.inria.fr/">Institut National de
Recherche en Informatique et en Automatique</a>, <a href=
"http://www.keio.ac.jp/">Keio University</a>). All Rights Reserved.
http://www.w3.org/Consortium/Legal/

Public documents on the W3C site are provided by the copyright
holders under the following license. The software or Document Type 
Definitions (DTDs) associated with W3C specifications are governed
by the Software Notice. By using and/or copying this document, or the
W3C document from which this statement is linked, you (the
licensee) agree that you have read, understood, and will comply
with the following terms and conditions:

Permission to use, copy, and distribute the contents of this
document, or the W3C document from which this statement is linked,
in any medium for any purpose and without fee or royalty is hereby
granted, provided that you include the following on ALL
copies of the document, or portions thereof, that you use:

A link or URL to the original W3C document.

The pre-existing copyright notice of the original author, or if
it doesn't exist, a notice of the form: "Copyright © [$date-of-document] World Wide Web
Consortium, (Massachusetts
Institute of Technology, Institut National de Recherche en Informatique et en
Automatique, Keio
University). All Rights Reserved.
http://www.w3.org/Consortium/Legal/" (Hypertext is preferred, but a
textual representation is permitted.)

If it exists, the STATUS of the W3C document.

When space permits, inclusion of the full text of this NOTICE 
should be provided. We request that authorship
attribution be provided in any software, documents, or other items
or products that you create pursuant to the implementation of the
contents of this document, or any portion thereof.

No right to create modifications or derivatives of W3C documents
is granted pursuant to this license. However, if additional 
requirements (documented in the Copyright
FAQ) are satisfied, the right to create modifications or
derivatives is sometimes granted by the W3C to individuals
complying with those requirements.

THIS DOCUMENT IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT
NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS
OF THE DOCUMENT ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE
IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY
PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT,
SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE
DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE CONTENTS
THEREOF.

The name and trademarks of copyright holders may NOT be used in
advertising or publicity pertaining to this document or its
contents without specific, written prior permission. Title to
copyright in this document will at all times remain with copyright
holders.

----------------------------------------------------------------------------
This formulation of W3C's notice and license became active on
April 05 1999 so as to account for the treatment of DTDs, schema's and 
bindings. See the older formulation for the policy prior to this date. 
Please see
our Copyright FAQ for common questions
about using materials from our site, including specific terms and
conditions for packages like libwww, Amaya, and Jigsaw. 
Other questions about this notice can be directed to site-policy@w3.org.

webmaster
(last updated by reagle on 1999/04/99.)
xml-commons/java/external/LICENSE.dom-software.txt $Id: LICENSE.dom-software.txt 201084 2002-12-09 16:15:21Z vhardy $


This license came from: http://www.w3.org/Consortium/Legal/copyright-software-19980720


W3C® SOFTWARE NOTICE AND LICENSE
Copyright © 1994-2001 World
Wide Web Consortium, <a href="http://www.w3.org/">World
Wide Web Consortium</a>, (<a href=
"http://www.lcs.mit.edu/">Massachusetts Institute of
Technology</a>, <a href="http://www.inria.fr/">Institut National de
Recherche en Informatique et en Automatique</a>, <a href=
"http://www.keio.ac.jp/">Keio University</a>). All Rights Reserved.
http://www.w3.org/Consortium/Legal/

This W3C work (including software, documents, or other related
items) is being provided by the copyright holders under the
following license. By obtaining, using and/or copying this work,
you (the licensee) agree that you have read, understood, and will
comply with the following terms and conditions:
Permission to use, copy, modify, and distribute this software
and its documentation, with or without modification,  for any
purpose and without fee or royalty is hereby granted, provided that
you include the following on ALL copies of the software and
documentation or portions thereof, including modifications, that
you make:

The full text of this NOTICE in a location viewable to users of
the redistributed or derivative work.

Any pre-existing intellectual property disclaimers, notices, or
terms and conditions. If none exist, a short notice of the
following form (hypertext is preferred, text is permitted) should
be used within the body of any redistributed or derivative code:
"Copyright © [$date-of-software] World Wide Web Consortium, (Massachusetts Institute of
Technology, Institut National de
Recherche en Informatique et en Automatique, Keio University). All Rights Reserved.
http://www.w3.org/Consortium/Legal/"

Notice of any changes or modifications to the W3C files,
including the date changes were made. (We recommend you provide 
URIs to the location from which the code is derived.)

THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND
COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE
USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD
PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT,
SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE
SOFTWARE OR DOCUMENTATION.

The name and trademarks of copyright holders may NOT be used in
advertising or publicity pertaining to the software without
specific, written prior permission. Title to copyright in this
software and any associated documentation will at all times remain
with copyright holders.
____________________________________
This formulation of W3C's notice and license became active on
August 14 1998 so as to improve compatibility with GPL. This
version ensures that W3C software licensing terms are no more
restrictive than GPL and consequently W3C software may be
distributed in GPL packages. See the older formulation for the
policy prior to this date. Please see our Copyright FAQ for common 
questions about using materials from
our site, including specific terms and conditions for packages like
libwww, Amaya, and Jigsaw. 
Other questions about this notice can be
directed to site-policy@w3.org.

webmaster
(last updated $Date: 2002-12-10 03:15:21 +1100 (Tue, 10 Dec 2002) $)
This distribution includes a binary distribution of Mozilla Rhino 1.6 release 5
plus one patch.

You can get the unpatched 1.6R5 release of Rhino from the following URL:

  ftp://ftp.mozilla.org/pub/mozilla.org/js/rhino1_6R5.zip

To obtain the source code for the 1.6R5 release of Rhino, issue the following
commands:

  cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot \
    co -D2006-11-20 mozilla/js/rhino

The patch is available here:

  https://bugzilla.mozilla.org/attachment.cgi?id=288467

which is attached to this bug:

  https://bugzilla.mozilla.org/show_bug.cgi?id=367627

Rhino is licensed under both the MPL (Mozilla Public License) 1.1 and the
GPL (GNU General Public License) 2.0, which are duplicated below.

The Rhino jar also includes four classes:
  org.mozilla.javascript.tools.debugger.downloaded.AbstractCellEditor.java
  org.mozilla.javascript.tools.debugger.downloaded.JTreeTable.java
  org.mozilla.javascript.tools.debugger.downloaded.TreeTableModel.java
  org.mozilla.javascript.tools.debugger.downloaded.TreeTableModelAdapter.java
Which come from:
  http://java.sun.com/products/jfc/tsc/articles/treetable2

Under the following license:

Code sample
License
Copyright 1994-2006 Sun Microsystems, Inc. All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
 

    * Redistribution of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    * Redistribution in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the
      distribution.

 
Neither the name of Sun Microsystems, Inc. or the names of
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
 
This software is provided "AS IS," without a warranty of any kind. ALL
EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 
You acknowledge that this software is not designed, licensed or
intended for use in the design, construction, operation or maintenance
of any nuclear facility.



==============================================================================

                          MOZILLA PUBLIC LICENSE
                                Version 1.1

                              ---------------

1. Definitions.

     1.0.1. "Commercial Use" means distribution or otherwise making the
     Covered Code available to a third party.

     1.1. "Contributor" means each entity that creates or contributes to
     the creation of Modifications.

     1.2. "Contributor Version" means the combination of the Original
     Code, prior Modifications used by a Contributor, and the Modifications
     made by that particular Contributor.

     1.3. "Covered Code" means the Original Code or Modifications or the
     combination of the Original Code and Modifications, in each case
     including portions thereof.

     1.4. "Electronic Distribution Mechanism" means a mechanism generally
     accepted in the software development community for the electronic
     transfer of data.

     1.5. "Executable" means Covered Code in any form other than Source
     Code.

     1.6. "Initial Developer" means the individual or entity identified
     as the Initial Developer in the Source Code notice required by Exhibit
     A.

     1.7. "Larger Work" means a work which combines Covered Code or
     portions thereof with code not governed by the terms of this License.

     1.8. "License" means this document.

     1.8.1. "Licensable" means having the right to grant, to the maximum
     extent possible, whether at the time of the initial grant or
     subsequently acquired, any and all of the rights conveyed herein.

     1.9. "Modifications" means any addition to or deletion from the
     substance or structure of either the Original Code or any previous
     Modifications. When Covered Code is released as a series of files, a
     Modification is:
          A. Any addition to or deletion from the contents of a file
          containing Original Code or previous Modifications.

          B. Any new file that contains any part of the Original Code or
          previous Modifications.

     1.10. "Original Code" means Source Code of computer software code
     which is described in the Source Code notice required by Exhibit A as
     Original Code, and which, at the time of its release under this
     License is not already Covered Code governed by this License.

     1.10.1. "Patent Claims" means any patent claim(s), now owned or
     hereafter acquired, including without limitation,  method, process,
     and apparatus claims, in any patent Licensable by grantor.

     1.11. "Source Code" means the preferred form of the Covered Code for
     making modifications to it, including all modules it contains, plus
     any associated interface definition files, scripts used to control
     compilation and installation of an Executable, or source code
     differential comparisons against either the Original Code or another
     well known, available Covered Code of the Contributor's choice. The
     Source Code can be in a compressed or archival form, provided the
     appropriate decompression or de-archiving software is widely available
     for no charge.

     1.12. "You" (or "Your")  means an individual or a legal entity
     exercising rights under, and complying with all of the terms of, this
     License or a future version of this License issued under Section 6.1.
     For legal entities, "You" includes any entity which controls, is
     controlled by, or is under common control with You. For purposes of
     this definition, "control" means (a) the power, direct or indirect,
     to cause the direction or management of such entity, whether by
     contract or otherwise, or (b) ownership of more than fifty percent
     (50%) of the outstanding shares or beneficial ownership of such
     entity.

2. Source Code License.

     2.1. The Initial Developer Grant.
     The Initial Developer hereby grants You a world-wide, royalty-free,
     non-exclusive license, subject to third party intellectual property
     claims:
          (a)  under intellectual property rights (other than patent or
          trademark) Licensable by Initial Developer to use, reproduce,
          modify, display, perform, sublicense and distribute the Original
          Code (or portions thereof) with or without Modifications, and/or
          as part of a Larger Work; and

          (b) under Patents Claims infringed by the making, using or
          selling of Original Code, to make, have made, use, practice,
          sell, and offer for sale, and/or otherwise dispose of the
          Original Code (or portions thereof).

          (c) the licenses granted in this Section 2.1(a) and (b) are
          effective on the date Initial Developer first distributes
          Original Code under the terms of this License.

          (d) Notwithstanding Section 2.1(b) above, no patent license is
          granted: 1) for code that You delete from the Original Code; 2)
          separate from the Original Code;  or 3) for infringements caused
          by: i) the modification of the Original Code or ii) the
          combination of the Original Code with other software or devices.

     2.2. Contributor Grant.
     Subject to third party intellectual property claims, each Contributor
     hereby grants You a world-wide, royalty-free, non-exclusive license

          (a)  under intellectual property rights (other than patent or
          trademark) Licensable by Contributor, to use, reproduce, modify,
          display, perform, sublicense and distribute the Modifications
          created by such Contributor (or portions thereof) either on an
          unmodified basis, with other Modifications, as Covered Code
          and/or as part of a Larger Work; and

          (b) under Patent Claims infringed by the making, using, or
          selling of  Modifications made by that Contributor either alone
          and/or in combination with its Contributor Version (or portions
          of such combination), to make, use, sell, offer for sale, have
          made, and/or otherwise dispose of: 1) Modifications made by that
          Contributor (or portions thereof); and 2) the combination of
          Modifications made by that Contributor with its Contributor
          Version (or portions of such combination).

          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
          effective on the date Contributor first makes Commercial Use of
          the Covered Code.

          (d)    Notwithstanding Section 2.2(b) above, no patent license is
          granted: 1) for any code that Contributor has deleted from the
          Contributor Version; 2)  separate from the Contributor Version;
          3)  for infringements caused by: i) third party modifications of
          Contributor Version or ii)  the combination of Modifications made
          by that Contributor with other software  (except as part of the
          Contributor Version) or other devices; or 4) under Patent Claims
          infringed by Covered Code in the absence of Modifications made by
          that Contributor.

3. Distribution Obligations.

     3.1. Application of License.
     The Modifications which You create or to which You contribute are
     governed by the terms of this License, including without limitation
     Section 2.2. The Source Code version of Covered Code may be
     distributed only under the terms of this License or a future version
     of this License released under Section 6.1, and You must include a
     copy of this License with every copy of the Source Code You
     distribute. You may not offer or impose any terms on any Source Code
     version that alters or restricts the applicable version of this
     License or the recipients' rights hereunder. However, You may include
     an additional document offering the additional rights described in
     Section 3.5.

     3.2. Availability of Source Code.
     Any Modification which You create or to which You contribute must be
     made available in Source Code form under the terms of this License
     either on the same media as an Executable version or via an accepted
     Electronic Distribution Mechanism to anyone to whom you made an
     Executable version available; and if made available via Electronic
     Distribution Mechanism, must remain available for at least twelve (12)
     months after the date it initially became available, or at least six
     (6) months after a subsequent version of that particular Modification
     has been made available to such recipients. You are responsible for
     ensuring that the Source Code version remains available even if the
     Electronic Distribution Mechanism is maintained by a third party.

     3.3. Description of Modifications.
     You must cause all Covered Code to which You contribute to contain a
     file documenting the changes You made to create that Covered Code and
     the date of any change. You must include a prominent statement that
     the Modification is derived, directly or indirectly, from Original
     Code provided by the Initial Developer and including the name of the
     Initial Developer in (a) the Source Code, and (b) in any notice in an
     Executable version or related documentation in which You describe the
     origin or ownership of the Covered Code.

     3.4. Intellectual Property Matters
          (a) Third Party Claims.
          If Contributor has knowledge that a license under a third party's
          intellectual property rights is required to exercise the rights
          granted by such Contributor under Sections 2.1 or 2.2,
          Contributor must include a text file with the Source Code
          distribution titled "LEGAL" which describes the claim and the
          party making the claim in sufficient detail that a recipient will
          know whom to contact. If Contributor obtains such knowledge after
          the Modification is made available as described in Section 3.2,
          Contributor shall promptly modify the LEGAL file in all copies
          Contributor makes available thereafter and shall take other steps
          (such as notifying appropriate mailing lists or newsgroups)
          reasonably calculated to inform those who received the Covered
          Code that new knowledge has been obtained.

          (b) Contributor APIs.
          If Contributor's Modifications include an application programming
          interface and Contributor has knowledge of patent licenses which
          are reasonably necessary to implement that API, Contributor must
          also include this information in the LEGAL file.

               (c)    Representations.
          Contributor represents that, except as disclosed pursuant to
          Section 3.4(a) above, Contributor believes that Contributor's
          Modifications are Contributor's original creation(s) and/or
          Contributor has sufficient rights to grant the rights conveyed by
          this License.

     3.5. Required Notices.
     You must duplicate the notice in Exhibit A in each file of the Source
     Code.  If it is not possible to put such notice in a particular Source
     Code file due to its structure, then You must include such notice in a
     location (such as a relevant directory) where a user would be likely
     to look for such a notice.  If You created one or more Modification(s)
     You may add your name as a Contributor to the notice described in
     Exhibit A.  You must also duplicate this License in any documentation
     for the Source Code where You describe recipients' rights or ownership
     rights relating to Covered Code.  You may choose to offer, and to
     charge a fee for, warranty, support, indemnity or liability
     obligations to one or more recipients of Covered Code. However, You
     may do so only on Your own behalf, and not on behalf of the Initial
     Developer or any Contributor. You must make it absolutely clear than
     any such warranty, support, indemnity or liability obligation is
     offered by You alone, and You hereby agree to indemnify the Initial
     Developer and every Contributor for any liability incurred by the
     Initial Developer or such Contributor as a result of warranty,
     support, indemnity or liability terms You offer.

     3.6. Distribution of Executable Versions.
     You may distribute Covered Code in Executable form only if the
     requirements of Section 3.1-3.5 have been met for that Covered Code,
     and if You include a notice stating that the Source Code version of
     the Covered Code is available under the terms of this License,
     including a description of how and where You have fulfilled the
     obligations of Section 3.2. The notice must be conspicuously included
     in any notice in an Executable version, related documentation or
     collateral in which You describe recipients' rights relating to the
     Covered Code. You may distribute the Executable version of Covered
     Code or ownership rights under a license of Your choice, which may
     contain terms different from this License, provided that You are in
     compliance with the terms of this License and that the license for the
     Executable version does not attempt to limit or alter the recipient's
     rights in the Source Code version from the rights set forth in this
     License. If You distribute the Executable version under a different
     license You must make it absolutely clear that any terms which differ
     from this License are offered by You alone, not by the Initial
     Developer or any Contributor. You hereby agree to indemnify the
     Initial Developer and every Contributor for any liability incurred by
     the Initial Developer or such Contributor as a result of any such
     terms You offer.

     3.7. Larger Works.
     You may create a Larger Work by combining Covered Code with other code
     not governed by the terms of this License and distribute the Larger
     Work as a single product. In such a case, You must make sure the
     requirements of this License are fulfilled for the Covered Code.

4. Inability to Comply Due to Statute or Regulation.

     If it is impossible for You to comply with any of the terms of this
     License with respect to some or all of the Covered Code due to
     statute, judicial order, or regulation then You must: (a) comply with
     the terms of this License to the maximum extent possible; and (b)
     describe the limitations and the code they affect. Such description
     must be included in the LEGAL file described in Section 3.4 and must
     be included with all distributions of the Source Code. Except to the
     extent prohibited by statute or regulation, such description must be
     sufficiently detailed for a recipient of ordinary skill to be able to
     understand it.

5. Application of this License.

     This License applies to code to which the Initial Developer has
     attached the notice in Exhibit A and to related Covered Code.

6. Versions of the License.

     6.1. New Versions.
     Netscape Communications Corporation ("Netscape") may publish revised
     and/or new versions of the License from time to time. Each version
     will be given a distinguishing version number.

     6.2. Effect of New Versions.
     Once Covered Code has been published under a particular version of the
     License, You may always continue to use it under the terms of that
     version. You may also choose to use such Covered Code under the terms
     of any subsequent version of the License published by Netscape. No one
     other than Netscape has the right to modify the terms applicable to
     Covered Code created under this License.

     6.3. Derivative Works.
     If You create or use a modified version of this License (which you may
     only do in order to apply it to code which is not already Covered Code
     governed by this License), You must (a) rename Your license so that
     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
     "MPL", "NPL" or any confusingly similar phrase do not appear in your
     license (except to note that your license differs from this License)
     and (b) otherwise make it clear that Your version of the license
     contains terms which differ from the Mozilla Public License and
     Netscape Public License. (Filling in the name of the Initial
     Developer, Original Code or Contributor in the notice described in
     Exhibit A shall not of themselves be deemed to be modifications of
     this License.)

7. DISCLAIMER OF WARRANTY.

     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.

8. TERMINATION.

     8.1.  This License and the rights granted hereunder will terminate
     automatically if You fail to comply with terms herein and fail to cure
     such breach within 30 days of becoming aware of the breach. All
     sublicenses to the Covered Code which are properly granted shall
     survive any termination of this License. Provisions which, by their
     nature, must remain in effect beyond the termination of this License
     shall survive.

     8.2.  If You initiate litigation by asserting a patent infringement
     claim (excluding declatory judgment actions) against Initial Developer
     or a Contributor (the Initial Developer or Contributor against whom
     You file such action is referred to as "Participant")  alleging that:

     (a)  such Participant's Contributor Version directly or indirectly
     infringes any patent, then any and all rights granted by such
     Participant to You under Sections 2.1 and/or 2.2 of this License
     shall, upon 60 days notice from Participant terminate prospectively,
     unless if within 60 days after receipt of notice You either: (i)
     agree in writing to pay Participant a mutually agreeable reasonable
     royalty for Your past and future use of Modifications made by such
     Participant, or (ii) withdraw Your litigation claim with respect to
     the Contributor Version against such Participant.  If within 60 days
     of notice, a reasonable royalty and payment arrangement are not
     mutually agreed upon in writing by the parties or the litigation claim
     is not withdrawn, the rights granted by Participant to You under
     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
     the 60 day notice period specified above.

     (b)  any software, hardware, or device, other than such Participant's
     Contributor Version, directly or indirectly infringes any patent, then
     any rights granted to You by such Participant under Sections 2.1(b)
     and 2.2(b) are revoked effective as of the date You first made, used,
     sold, distributed, or had made, Modifications made by that
     Participant.

     8.3.  If You assert a patent infringement claim against Participant
     alleging that such Participant's Contributor Version directly or
     indirectly infringes any patent where such claim is resolved (such as
     by license or settlement) prior to the initiation of patent
     infringement litigation, then the reasonable value of the licenses
     granted by such Participant under Sections 2.1 or 2.2 shall be taken
     into account in determining the amount or value of any payment or
     license.

     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
     all end user license agreements (excluding distributors and resellers)
     which have been validly granted by You or any distributor hereunder
     prior to termination shall survive termination.

9. LIMITATION OF LIABILITY.

     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.

10. U.S. GOVERNMENT END USERS.

     The Covered Code is a "commercial item," as that term is defined in
     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
     software" and "commercial computer software documentation," as such
     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
     all U.S. Government End Users acquire Covered Code with only those
     rights set forth herein.

11. MISCELLANEOUS.

     This License represents the complete agreement concerning subject
     matter hereof. If any provision of this License is held to be
     unenforceable, such provision shall be reformed only to the extent
     necessary to make it enforceable. This License shall be governed by
     California law provisions (except to the extent applicable law, if
     any, provides otherwise), excluding its conflict-of-law provisions.
     With respect to disputes in which at least one party is a citizen of,
     or an entity chartered or registered to do business in the United
     States of America, any litigation relating to this License shall be
     subject to the jurisdiction of the Federal Courts of the Northern
     District of California, with venue lying in Santa Clara County,
     California, with the losing party responsible for costs, including
     without limitation, court costs and reasonable attorneys' fees and
     expenses. The application of the United Nations Convention on
     Contracts for the International Sale of Goods is expressly excluded.
     Any law or regulation which provides that the language of a contract
     shall be construed against the drafter shall not apply to this
     License.

12. RESPONSIBILITY FOR CLAIMS.

     As between Initial Developer and the Contributors, each party is
     responsible for claims and damages arising, directly or indirectly,
     out of its utilization of rights under this License and You agree to
     work with Initial Developer and Contributors to distribute such
     responsibility on an equitable basis. Nothing herein is intended or
     shall be deemed to constitute any admission of liability.

13. MULTIPLE-LICENSED CODE.

     Initial Developer may designate portions of the Covered Code as
     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
     Developer permits you to utilize portions of the Covered Code under
     Your choice of the NPL or the alternative licenses, if any, specified
     by the Initial Developer in the file described in Exhibit A.

EXHIBIT A -Mozilla Public License.

     ``The contents of this file are subject to the Mozilla Public License
     Version 1.1 (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.mozilla.org/MPL/

     Software distributed under the License is distributed on an "AS IS"
     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
     License for the specific language governing rights and limitations
     under the License.

     The Original Code is ______________________________________.

     The Initial Developer of the Original Code is ________________________.
     Portions created by ______________________ are Copyright (C) ______
     _______________________. All Rights Reserved.

     Contributor(s): ______________________________________.

     Alternatively, the contents of this file may be used under the terms
     of the _____ license (the  "[___] License"), in which case the
     provisions of [______] License are applicable instead of those
     above.  If you wish to allow use of your version of this file only
     under the terms of the [____] License and not to allow others to use
     your version of this file under the MPL, indicate your decision by
     deleting  the provisions above and replace  them with the notice and
     other provisions required by the [___] License.  If you do not delete
     the provisions above, a recipient may use your version of this file
     under either the MPL or the [___] License."

     [NOTE: The text of this Exhibit A may differ slightly from the text of
     the notices in the Source Code files of the Original Code. You should
     use the text of this Exhibit A rather than the text found in the
     Original Code Source Code for Your Modifications.]

==============================================================================

		    GNU GENERAL PUBLIC LICENSE
		       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

		    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

			    NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

		     END OF TERMS AND CONDITIONS

	    How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

==============================================================================
The pdf-transcoder.jar file is licensed under the Apache License 2.0, which
can be found in the distribution root directory in the LICENSE file.
xml-commons/java/external/LICENSE.sax.txt $Id: LICENSE.sax.txt 201084 2002-12-09 16:15:21Z vhardy $


This license came from: http://www.megginson.com/SAX/copying.html
  However please note future versions of SAX may be covered 
  under http://saxproject.org/?selected=pd


This page is now out of date -- see the new SAX site at 
http://www.saxproject.org/ for more up-to-date
releases and other information. Please change your bookmarks.


SAX2 is Free!

I hereby abandon any property rights to SAX 2.0 (the Simple API for
XML), and release all of the SAX 2.0 source code, compiled code, and
documentation contained in this distribution into the Public Domain.
SAX comes with NO WARRANTY or guarantee of fitness for any
purpose.

David Megginson, david@megginson.com
2000-05-05
The xalan-2.6.0.jar file is licensed under the Apache License 2.0, which
can be found in the distribution root directory in the LICENSE file.
The xerces_2_5_0.jar file comes from the Apache Xerces project
(http://xmlapache.org/dist/xerces-j/), and is licensed under the
Apache Software License, Version 1.1, which is reproduced below.

/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights 
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:  
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written 
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
This distribution includes a binary distribution of Mozilla Rhino 1.6 release 5
plus one patch.

You can get the unpatched 1.6R5 release of Rhino from the following URL:

  ftp://ftp.mozilla.org/pub/mozilla.org/js/rhino1_6R5.zip

To obtain the source code for the 1.6R5 release of Rhino, issue the following
commands:

  cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot \
    co -D2006-11-20 mozilla/js/rhino

The patch is available here:

  https://bugzilla.mozilla.org/attachment.cgi?id=288467

which is attached to this bug:

  https://bugzilla.mozilla.org/show_bug.cgi?id=367627

Rhino is licensed under both the MPL (Mozilla Public License) 1.1 and the
GPL (GNU General Public License) 2.0, which are in the LICENSE.js.txt file.
The pdf-transcoder.jar file is built from the Apache FOP project
(http://xmlgraphics.apache.org/fop), version 0.94.

This is only needed if you want to transcode to PDF, otherwise it can
be removed.

The pdf-transcoder.jar file is licensed under the Apache License 2.0, which
can be found in the distribution root directory in the LICENSE file.
The xalan-2.6.0.jar file comes from the Apache Xalan project
(http://xml.apache.org/xalan-j/), and is licensed under the
Apache License 2.0, which can be found in the distribution root directory
in the LICENSE file.
The xerces_2_5_0.jar file comes from the Apache Xerces project
(http://xml.apache.org/dist/xerces-j/), and is licensed under the
Apache Software License, Version 1.1, which is in the
LICENSE.xerces_2_5_0.txt file.
This distribution includes xml-apis-ext.jar from the XML Commons External
1.3.04 binary distribution, which can also be obtained from:

  http://xml.apache.org/mirrors.cgi

Source code is available from the XML Commons web site:

  http://xml.apache.org/commons/

xml-apis-ext.jar contains:

  - SAC 1.3
  - SMIL Java bindings
  - SVG 1.1 Java bindings

SAC 1.3, the SMIL Java bindings and the SVG 1.1 Java bindings are licensed
under the W3C Software License.  Related documentation is licensed under the
W3C Document License.  See LICENSE.dom-software.txt and
LICENSE.dom-documentation.txt.
This distribution includes xml-apis.jar from the XML Commons External
1.3.04 binary distribution, which can also be obtained from:

  http://xml.apache.org/mirrors.cgi

Source code is available from the XML Commons web site:

  http://xml.apache.org/commons/

xml-apis.jar contains:

  - DOM Level 2 Events
  - DOM Level 2 HTML
  - DOM Level 2 Style
  - DOM Level 2 Traversal and Range
  - DOM Level 2 Views
  - DOM Level 3 Core
  - DOM Level 3 Load and Save
  - DOM Level 3 XPath
  - JAXP 1.3 (JSR 206)
  - SAX

All DOM code is licensed under the W3C Software License, and DOM documentation
under the W3C Document License.  See LICENSE.dom-software.txt and
LICENSE.dom-documentation.txt.

The JAXP 1.3 code is licensed under the Apache Software License 2.0, which is
in the LICENSE in the root directory of this distribution.

SAX is public domain.  See LICENSE.sax.txt.
#!/bin/bash
# -----------------------------------------------------------------------------
#
#   Licensed to the Apache Software Foundation (ASF) under one or more
#   contributor license agreements.  See the NOTICE file distributed with
#   this work for additional information regarding copyright ownership.
#   The ASF licenses this file to You 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.
#
# make-squiggle-app.sh
#
# Creates a Mac OS X application bundle for Squiggle, then opens a Finder
# window for the current directory so that the user can drag the icon
# into their desired installation location (probably /Applications).
#
# $Id$
# -----------------------------------------------------------------------------

trap 'echo Error creating application bundle.; exit 1' ERR

cd `dirname "$0"`/..

APP=Squiggle.app
CON=$APP/Contents
MAC=$CON/MacOS
RES=$CON/Resources

[ -e $APP ] && echo $APP already exists: please move it out of the way before running this script. && exit 1

echo Creating $APP in `pwd`...

mkdir $APP $CON $MAC $RES

cat >$CON/Info.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleExecutable</key>
  <string>Squiggle</string>
  <key>CFBundleVersion</key>
  <string>1.7+r608262</string>
  <key>CFBundleShortVersionString</key>
  <string>1.7+r608262</string>
  <key>CFBundleIconFile</key>
  <string>Squiggle.icns</string>
  <key>CFBundleIdentifier</key>
  <string>org.apache.batik</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundleName</key>
  <string>Squiggle</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>CFBundleSignature</key>
  <string>????</string>
  <key>NSHumanReadableCopyright</key>
  <string>Copyright Â© 2008 Apache Software Foundation. All Rights Reserved.</string>
</dict>
</plist>
EOF

cat >$MAC/Squiggle <<EOF
#!/bin/sh

JAVA=/usr/bin/java
[ -e "$JAVA_HOME/bin/java" ] && JAVA="$JAVA_HOME/bin/java"

"$JAVA" -Dcom.apple.mrj.application.apple.menu.about.name=Squiggle -jar "\`dirname \$0\`/../Resources/batik-squiggle.jar"
EOF
chmod a+x $MAC/Squiggle

cp -r *.jar lib extensions $RES/
mv $RES/lib/Squiggle.icns $RES/

echo Done.

open .
                                  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.
Apache Batik
Copyright 1999-2007 The Apache Software Foundation

This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

This software contains code from the World Wide Web Consortium (W3C) for the 
Document Object Model API (DOM API) and SVG Document Type Definition (DTD).

This software contains code from the International Organisation for
Standardization for the definition of character entities used in the software's
documentation.

This product includes images from the Tango Desktop Project
(http://tango.freedesktop.org/).

This product includes images from the Pasodoble Icon Theme
(http://www.jesusda.com/projects/pasodoble).

                  A  P  A  C  H  E     B  A  T  I  K

  What is it?
  -----------

  Batik is a Java based toolkit for applications which handle
  images in the Scalable Vector Graphics (SVG) format for
  various purposes, such as viewing, generation or
  manipulation. 

  The project's ambition is to give developers a set of core
  modules which can be used together or individually to
  support specific SVG solutions. Examples of modules are
  an SVG parser, an SVG generator and an SVG DOM
  implementation. Another ambition of the Batik project is to
  make it highly extensible (for example, Batik allows the
  developer to handle custom SVG tags). Even though the
  goal of the project is to provide a set of core modules, one
  of the deliveries is a full fledged SVG Viewer
  implementation which validates the various modules and
  their inter-operability. 

  In a nutshell, Batik provides building blocks that developers
  can assemble in various ways in their Java technology
  applications to generate, parse, view or convert SVG
  contents. For example, Batik contains a Swing component
  that can add SVG viewing capability to all Java technology
  applications. Batik can also be used to generate SVG on a
  client or on a server, and Batik can convert SVG content
  into other formats such as JPEG or PNG. Batik's goal is to
  make it easy for application developers to handle SVG
  content for various purposes, client-side or server-side.


  Where is it?
  ------------

  The home page for the Apache Batik project can be found in the Apache XML 
  Project web site (http://xmlgraphics.apache.org/batik/). There you also find 
  information on how to download the latest release as well as all the other 
  information you might need regarding this project.


  Requirements
  ------------

   o  A Java 1.3 or later compatible virtual machine for your operating system.

  Optional Libraries
  ------------------

  By default, Batik includes a scripting engine for ECMAScript. It is possible
  to add support for additional scripting languages (Python and TCL).
  
  See http://xmlgraphics.apache.org/batik/install.html#optionalComponents for
  details.
 
  Installation Instructions and Documentation
  -------------------------------------------

  Read the Install page at http://xmlgraphics.apache.org/batik/install.html
  for the installation instructions.

  Look for the most updated documentation on the Apache Batik web site under
  the Apache XML Graphics Project (http://xmlgraphics.apache.org/batik/).


  Licensing and legal issues
  --------------------------

  For legal and licensing issues, please read the LICENSE and NOTICE files.

  Thanks for using Apache Batik.

                                           The Apache XML Graphics Project
                                           http://xmlgraphics.apache.org/
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2015 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####
#print 'Content-type: text/html\n\nxxxxx'
import sys,cgi,os,datetime,cgitb, codecs
from time import time
from lib.configobj import ConfigObj
sys.path.append('modules')
from logintools import isloggedin
from lib.database import SQL


form = cgi.FieldStorage()

test = isloggedin("users/")
try:	admin = int(test[0]["admin"])
except:	pass

project = form.getvalue('project',"").decode("utf-8")
csv = form.getvalue('csv',None)

if test:
	cgitb.enable(logdir="dir")
else:
	#sys.exit()
	project="depexo"
	project="lingCorpus"
	admin=5
	csv=True



class Evaluation:
	def __init__(self,cats=0,deps=0,funcs=0,lemmas=0,nbwords=0, nbgovs=0):
		self.cats=cats
		self.deps=deps
		self.funcs=funcs
		self.lemmas=lemmas
		self.nbwords=nbwords
		self.nbgovs=nbgovs
		self.total=self.computeTotalEvaluation()

	def __add__(self, other):
		return Evaluation(self.cats+other.cats, self.deps+other.deps, self.funcs+other.funcs, self.lemmas+other.lemmas, self.nbwords+other.nbwords, self.nbgovs+other.nbgovs)

	def computeTotalEvaluation(self):
		if self.nbwords:
			#print "<br>computeTotalEvaluation",((float(self.cats)/self.nbwords)*sql.categoriesEval + (self.deps/self.nbwords)*sql.governorsEval + (self.funcs/self.nbwords)*sql.functionsEval + (self.lemmas/self.nbwords)*sql.lemmasEval ) / sql.evalTot * 100
			return ((float(self.cats)/self.nbwords)*sql.categoriesEval + (self.deps/self.nbgovs)*sql.governorsEval + (self.funcs/self.nbgovs)*sql.functionsEval + (self.lemmas/self.nbwords)*sql.lemmasEval ) / sql.evalTot * 100
		else:
			#print "<br>computeTotalEvaluation self.nbwords zero<br>"
			return 0
	def __str__(self):
		return "Evaluation: " + str(self.__dict__)

	def htmltable(self):
		out =  "<table class='whitableNarrow'>"
		out += "<tr><th>"+"</th><th>".join([str(x) for x in sorted(self.__dict__ )  ])+"</th></tr>"
		out += "<tr><td>"+"</td><td>".join([str(int(round(self.__dict__[x],0))) for x in sorted(self.__dict__ ) ])+"</td></tr>"
		out += "</table>"
		return out


def evaluateTree(usertree, teachertree):
	"""
	compares a usertree with the teacher's tree
	each identical cat and lemma adds a point
	each correct gov or func _set_ adds a point, so if a word has more than one governor, all the governors together make up one point
	returns an Evaluation object
	"""
	correctcats,correctdeps,correctfuncs,correctlemmas=(0,0.0,0.0,0)
	cc=sql.catName
	nbgovs = len([node for node in teachertree.keys() if "gov" in teachertree[node].keys()])
	for i in usertree:
		if i in teachertree:
			#print "_____",cc,teachertree[i]
			#print usertree[i]
			if teachertree[i][cc]==usertree[i].get(cc,None):correctcats+=1
			if teachertree[i].get("lemma","")==usertree[i].get("lemma"," "):correctlemmas+=1
			#print "<br>",usertree[i]["gov"]
			if "gov" in teachertree[i]:
				if "gov" in usertree[i]:
					t_gov, t_rel = teachertree[i]["gov"].items()[0]
					u_gov, u_rel = usertree[i]["gov"].items()[0]
					if t_gov == u_gov:
						correctdeps += 1
					if t_rel == u_rel:
						correctfuncs += 1
				else:
					pass

		else:
			pass
			#print "no teacher tree for",usertree[i]

	return Evaluation(correctcats,correctdeps,correctfuncs,correctlemmas,len(teachertree), nbgovs)

def attributeSentences(textid, username, userid):
	"""
	attributes sentences to students that didn't even open one of the texts.
	(users that don't have any tree in the whole project, will not be added)
	"""
	sql.getAllSentences(textid, username, userid, dbcrsr=(db,cursor)) # attributes sentences of the text to the user
	usertotevalu = Evaluation()
	for snr,sid,s,treeid in sql.getAllSentences(textid, username, userid, useradmindic.get(username,0), dbcrsr=(db,cursor)):
		# for each sentence attributed to the user from this text
		teachertree = teacherTrees.get(sid,sql.gettree(sid,teacherid)["tree"])
		tree = sql.gettree(sid,userid)["tree"]
		# adding the correct annotations item-wise:
		usertotevalu = usertotevalu + evaluateTree(tree, teachertree)
	return usertotevalu

def makeTable(texts,students,project,theader="",thstart="",thth="\t",thend="\n",trstart="",tdtd="\t",trend="\n",tfooter="", detailTable=False):
	"""
	draws the html table
	used for two cases:
		csv: detailTable = False
		html: detailTable = True
	"""

	out = theader
	out += thstart + thth.join(["student","login"]+[textname for textname,textid,nrtokens in sorted(texts)]+["total %"]) + thend


	for real,username,userid in sorted(students, key=lambda s: s[0].lower()):
		print real.encode("utf-8"),"<br>"
		usertotevalu = Evaluation()
		res=[]
		for textname,textid,nrtokens in sorted(texts):
			evalu = texts[(textname,textid,nrtokens)].get(userid,attributeSentences(textid, username, userid))
			# if a student didn't even open a text and thus doesn't have sentences attributed to her,
			# the function attributeSentences does the distribution.
			res += [evalu]
			#print evalu,"<br>"
			usertotevalu = usertotevalu + evalu

		outline = trstart+tdtd.join([real,username]+[ str(int(round(x.total,0)))+" {ht}".format(ht=x.htmltable() if (detailTable and x.nbwords>0)  else "") for x in res ] +[ ("<span style='font-weight: bold;'>" if detailTable else "") +str(round(usertotevalu.total,1))+("</span>" if detailTable else "") +" {ht}".format(ht=usertotevalu.htmltable() if (detailTable and usertotevalu.nbwords>0) else "" )])+trend
		#evalfile.write(outline)
		out += outline
	out += tfooter
	return out

def sampleStats(textid, dbcrsr):
	"""
	function for overview of annotation by students
	"""
	db,cursor=dbcrsr
	r = sql.getall(cursor, "sentences",["textid"], [textid])
	users=[]
	sentencelengths=[]
	modifiedtags=[]
	modifieddeps=[]
	allusers={}
	if r:
		for sentenceid,nr,sentence,textid in r: # for each sentence of the text:

			treeDict=dict([ (user, (treeid,uid,realname,timestamp)) for treeid,uid,user,realname,timestamp in sql.treesForSentence(sentenceid, dbcrsr=dbcrsr)])
			timeDict=dict([ (timestamp,user) for (user, (treeid,uid,realname,timestamp)) in treeDict.iteritems() ])

			lastuser=timeDict[sorted(timeDict,reverse=True)[0]]
			treeid,uid,realname,timestamp=treeDict[lastuser]
			if lastuser not in users: users+=[lastuser]

			parser=timeDict[sorted(timeDict,reverse=True)[-1]]
			ptreeid,puid,prealname,ptimestamp=treeDict[parser]

			for ts,user in timeDict.iteritems():
				if user!=parser:
					allusers[user]=allusers.get(user,0)+1

			modifiedtag = sql.nrDifferent(treeid, ptreeid, "tag", dbcrsr)
			modifiedtags +=[modifiedtag]
			modifieddep = sql.nrDifferent(treeid, ptreeid, None, dbcrsr)
			modifieddeps +=[modifieddep]

			sentencelength = sql.getnumber(cursor, "features", ["treeid","attr"], [treeid,"t"])
			#print "sentencelength",sentencelength
			sentencelengths+=[sentencelength]

		asentencelength=sum(sentencelengths)/float(len(sentencelengths))
		amodifiedtag=sum(modifiedtags)/float(len(modifiedtags))
		percentmodtags=round(sum(modifiedtags)/float(sum(sentencelengths))*100,2)
		amodifieddep=sum(modifieddeps)/float(len(modifieddeps))
		percentmoddeps=round(sum(modifieddeps)/float(sum(sentencelengths))*100,2)

		userinfo = ", ".join([u+":"+str(c) for u,c in allusers.iteritems()])

		return len(r), asentencelength, amodifiedtag, percentmodtags, amodifieddep, percentmoddeps, userinfo


userdir="users/"
RESERVEDNAMES = ['config', 'default', 'temp', 'emails', 'pending',"user_files_go_in_"]
useradmindic={}
for user in [entry[:-4] for entry in os.listdir(userdir) if os.path.isfile(userdir+entry) and entry[:-4] not in RESERVEDNAMES and not entry[0]=="."]:
	thisuserc = ConfigObj(userdir+user+'.ini')
	useradmindic[user]=int(thisuserc.get('admin',0))

sql=SQL(project)
project=project.encode("utf-8")
teacherTrees={} # sid -> teacher tree
students={} # real, username, userid -> None
texts={} # textname,textid,nrtokens -> {userid1 --> (correctcats,correctdeps,correctfuncs,correctlemmas,correctTotal), userid2->...}

teacherid=sql.userid(sql.teacher)
useradmindic[sql.teacher]=5 # treat teacher as admin

#print 'Content-type: text/html\n\n'

db,cursor=sql.open()

try:	mtime = os.path.getmtime(os.path.join("projects",project,"arborator.db.sqlite"))
except OSError: mtime = 0
last_modified_date = datetime.datetime.fromtimestamp(mtime)

with codecs.open(os.path.join("projects",project,"stats."+str(last_modified_date)+".csv"),'w','utf-8') as evalfile:

	evalfile.write("\t".join(["textname","nr sents", "asentencelength", "amodifiedtag", "percentmodtags", "amodifieddep", "percentmoddeps", "userinfo"])+"\n")

	textcounter=0

	for textname,textid,nrtokens in sorted( [(textname,textid,nrtokens) for textid,textname,nrtokens in list(sql.getall(cursor, "texts", None, None))]):
		# for each text
		texts[(textname,textid,nrtokens)]={}
		exotype, exotoknum = sql.getExo(textid,(db,cursor))

		if exotype:
			uids=[userid for userid, in sql.uidForText(textid,(db,cursor))] # some tree exists for uids
			for userid in uids:
				# for each userid for whom we got a tree
				#numtok=0
				#print cursor, "users",["rowid"],[userid]
				#print "!!!",cursor.description
				#allu=list()
				#print userid,allu
				for _,username,real in sql.getall(cursor, "users",["rowid"],[userid]): # only executes once
					#print "<br><br><br>",userid,username,real.encode("utf-8"),"<br>"

					students[real,username,userid]=None
					usertotevalu = Evaluation()
					for snr,sid,s,treeid in sql.getAllSentences(textid, username, userid, useradmindic.get(username,0), dbcrsr=(db,cursor)):

						# for each sentence attributed to the user from this text

						teachertree = teacherTrees.get(sid,sql.gettree(sid,teacherid, indb=db, incursor=cursor)["tree"])

						tree = sql.gettree(sid,userid, indb=db, incursor=cursor)["tree"]
						#print "it took",time()-ti
						# adding the correct annotations item-wise:
						#print "before eval"

						usertotevalu = usertotevalu + evaluateTree(tree, teachertree)
						#print "after eval"
						#print "__",snr,usertotevalu,"<br>" # s.encode("utf-8"),

					texts[(textname,textid,nrtokens)][userid]=usertotevalu
				print 'Dummy: '+username,len(texts[(textname,textid,nrtokens)])

		else: # normal annotation: make some stats
			print "Dummy: __________",textname, textcounter
			evalfile.write("\t".join([textname] + [unicode(info) for info in sampleStats(textid,(db,cursor))])+"\n")
			#,str(nrsentences),str(asentencelength),str(amodifiedtag),str(amodifieddep),userinfo]


		textcounter+=1


if not exotype:
	print "wrote stat file in project folder"
	sys.exit() # the rest is only if exo

if csv:
	print 'Content-type: text/csv'
	print "Content-Disposition:attachment;filename="+project+".csv\n";

	filecontent = makeTable(texts,students,project) #.encode("utf-8")
	#if not test:
		#print filecontent
	#else:
	try:
		mtime = os.path.getmtime(os.path.join("projects",project,"arborator.db.sqlite"))
	except OSError:
		mtime = 0
	last_modified_date = datetime.datetime.fromtimestamp(mtime)

	with codecs.open(os.path.join("projects",project,"evaluation."+str(last_modified_date)+".csv"),'w','utf-8') as evalfile:
		evalfile.write(filecontent)



else:
	print 'Content-type: text/html\n\n'
	if os.path.exists("projects/"+project+"/"+project+".png"):img="<img src='projects/"+project+"/"+project+".png' align='top' height='18'>"
	else: img=""
	print """<html>
		<head>
			<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
			<title>Arborator - {project} Project</title>
			<link media="screen" href="css/jquery-ui-1.8.18.custom.css" type="text/css" rel="stylesheet">
			<link href="css/arborator.css" rel="stylesheet" type="text/css">
		</head>
		<body id="body">
			<div id="center" class="center" style="width:100%">
				<div id="navigation" style="width:100%;margin:0px;border:0px;" class="arbortitle  ui-widget-header ui-helper-clearfix">
				<a href='.' style='position: fixed;left:1px;top:1px'><img src="images/arboratorNano.png" border="0"></a>
				<a href="project.cgi?project={project}" style='position: fixed;left:120px;top:5px;color:white;' title="project overview">{img} {project} Annotation Project</a>
				<div style='margin:5 auto;' id='sentinfo'>Student results</div>
			</div>
			<div id="projectbox" class="ui-widget ui-widget-content ui-corner-all box" style="text-align:-moz-center;">
		""".format(project=project,img=img)


	print makeTable(texts,students,project,theader="<table class='whitable'>",thstart="<tr><th>",thth="</th><th>",thend="</th></tr>",trstart="<tr><td>",tdtd="</td><td>",trend="</td></tr>",tfooter="</table>", detailTable=True).encode("utf-8")

	print "categories:",sql.categoriesEval,"%, "
	print "governors:",sql.governorsEval,"%, "
	print "functions:",sql.functionsEval,"%, "
	print "lemmas:",sql.lemmasEval,"%, "
	print "points in total:",sql.evalTot
	print '<br>Change these values in the <a href="configedit.cgi?project={project}">project configuration</a>.'.format(project=project)

	print """</div></body></html>"""


#if __name__ == "__main__":
#!/usr/bin/python
# -*- coding: utf-8 -*-

#import cgi

import sys,cgi,cgitb, os
cgitb.enable()
form = cgi.FieldStorage()
userdir = 'users/'
thisfile = os.environ.get('SCRIPT_NAME',".")
action = form.getvalue("action",None)
sys.path.append('modules')
from logintools import login
from logintools import isloggedin

action, userconfig = login(form, userdir, thisfile, action)
adminLevel = int(userconfig["admin"])

project = form.getvalue('project',"").decode("utf-8")

treeid = form.getvalue('treeid',None)
compare = form.getvalue('compare',None)

filename = form.getvalue('filename',"").decode("utf-8")

nr = int(form.getvalue('nr',1))
filetype = form.getvalue('filetype',None)

print 'Content-type: application/json\n\n'

#if project=="quickie":
	#if filetype == "conll14" or filetype == "conll10" or filetype == "conll4":
		#from lib.conll import conll2jsonTree
		#conll2jsonTree(filename,nr)
#else:
from lib.database import SQL
sql=SQL(project)

if compare:
	treeids={}
	for k in form.keys():
		if k.startswith("treeids"):
			treeids[int(k.split("[")[-1][:-1])]=form.getvalue(k)
	sql.compare(treeids)
else:
	sql.tree2json(treeid=treeid, adminLevel=adminLevel)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- width="6.86618in" height="4.73459in" -->
<svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:cc="http://web.resource.org/cc/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    xml:space="preserve"
    width="500px"
    height="500px"
    style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality"
    viewBox="-4202 0 6866 4734"
    id="svg2"
    
    onload="startup(evt)"
    
    sodipodi:version="0.32"
    inkscape:version="0.45"
    sodipodi:docname="arborator.svg"
    sodipodi:docbase="/home/kim/Desktop"
    inkscape:output_extension="org.inkscape.output.svg.inkscape"><sodipodi:namedview
										inkscape:window-height="694"
										inkscape:window-width="1012"
										inkscape:pageshadow="2"
										inkscape:pageopacity="0.0"
										guidetolerance="10.0"
										gridtolerance="10.0"
										objecttolerance="10.0"
										borderopacity="1.0"
										bordercolor="#666666"
										pagecolor="#ffffff"
										id="base"
										inkscape:zoom="0.964"
										inkscape:cx="250"
										inkscape:cy="249.48133"
										inkscape:window-x="6"
										inkscape:window-y="22"
										inkscape:current-layer="svg2" />
										

<script>
<![CDATA[

function startup(evt){

	blade=document.getElementById("path15");
	//ptiplace=document.getElementById("path11");
	step=20;
	size=80;
	animate(0);
	//blade.setAttribute("style","fill:#000000;")
	//+"stroke-width:"+((x%50)+100)+";");
	//ptiplace.setAttribute("style","stroke-dasharray:"+pdar+";");
	//var pdar = x+",62,"+(62-x)+",0";
}

function animate(x){
	
	if (x<size) var dar = x+","+size+","+(size-x)+",0";
	else var dar = 0 +"," + (x%size)+","+size+","+(size-x%size);
	blade.setAttribute("style","stroke-dasharray:"+dar+";")
	x=(x+step)%(2*size);
	window.setTimeout("animate("+x+")",40);
	
}
//]]>
</script>




	<defs
	     id="defs4">
		<style
		      type="text/css"
		      id="style6">
			
			.str0 {stroke:#7B4178;stroke-width:3}
			.str1 {stroke:#DD137B;stroke-width:56;stroke-dasharray:56 56}
			.str2 {stroke:#DD137B;stroke-width:125;stroke-dasharray:125 125}
			.fil2 {fill:none}
			.fil1 {fill:#654B6D}
			.fil0 {fill:#9AB086}
			.fil3 {fill:#DD137B}
			
		</style>
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
	</defs>
	<path
	     style="fill:#654b6d"
	     id="path13"
	     d="M -143,1363 L -3191,2392 C -3460,2196 -3628,1946 -3628,1711 C -3628,1005 -2339,430 -758,430 C 822,430 2111,1005 2111,1711 C 2111,2417 822,2992 -758,2992 C -1005,2992 -1263,2972 -1498,2947 L -143,1363 z "
	     class="fil1" /><path
				 style="fill:#dd137b;stroke:#dd137b;stroke-width:125;stroke-dasharray:125, 125"
				 id="path15"
				 d="M -1041,725 C -871,633 -559,587 -223,1547 C 112,2506 -332,2355 -626,2469 C -920,2583 -983,2759 -1218,1928 C -1453,1096 -1372,904 -1041,725 z "
				 class="fil3 str2" /><path
							  style="fill:#654b6d"
							  id="path17"
							  d="M -93,1313 L 1669,1620 C 2462,1758 872,2942 -708,2942 C -955,2942 -1213,2922 -1448,2897 L -93,1313 z "
							  class="fil1" /><g
									   id="g7121"
									   transform="matrix(1.0287277,0,0,0.9985667,166.73825,0.6895801)"><path
										class="fil1 str0"
										d="M -1952,3668 L -3221.8483,4275.4799 L -3470,4277 L -1605,3486 L -1952,3668 z "
										id="path9"
										style="fill:#654b6d;stroke:#7b4178;stroke-width:3"
										sodipodi:nodetypes="ccccc" /><path
										class="fil2 str1"
										d="M -2339,3389 C -2215,3341 -2045,3528 -1960,3799 L -2276,4176 C -2343,4206 -2330,4207 -2413,3947 C -2496,3688 -2462,3437 -2339,3389 z "
										id="path11"
										style="fill:none;stroke:#dd137b;stroke-width:56;stroke-dasharray:56, 56" /><path
										class="fil1 str0"
										d="M -1806,3589 L -1608.1517,3487.7802 L -2229.0895,4275.2601 L -2483,4276 L -1806,3589 z "
										id="path19"
										style="fill:#654b6d;stroke:#7b4178;stroke-width:3"
										sodipodi:nodetypes="ccccc" /></g><path
										style="fill:#654b6d"
										id="path21"
										d="M -1653,4274 L -1746,4274 L -1703,4091 L -1806,4091 L -1940,4274 L -2023,4274 L -1682,3808 L -1471,3808 C -1424,3808 -1397,3818 -1389,3838 C -1380,3860 -1395,3897 -1432,3948 C -1458,3983 -1484,4011 -1512,4032 C -1543,4055 -1575,4071 -1609,4079 L -1653,4274 z M -1517,3950 C -1501,3927 -1496,3911 -1502,3903 C -1508,3895 -1526,3890 -1553,3890 L -1658,3890 L -1746,4010 L -1641,4010 C -1613,4010 -1593,4008 -1580,4003 C -1558,3995 -1538,3977 -1517,3950 z "
										class="fil1" /><path
										style="fill:#654b6d"
										id="path23"
										d="M -1142,4145 C -1172,4186 -1201,4216 -1230,4235 C -1270,4261 -1317,4274 -1373,4274 L -1597,4274 L -1255,3808 L -1045,3808 C -997,3808 -968,3815 -956,3829 C -940,3848 -951,3884 -988,3936 C -1006,3960 -1026,3980 -1047,3996 C -1068,4012 -1095,4027 -1128,4041 C -1114,4053 -1108,4067 -1110,4083 C -1112,4098 -1123,4119 -1142,4145 z M -1087,3946 C -1059,3909 -1065,3890 -1104,3890 L -1231,3890 L -1311,4000 L -1186,4000 C -1167,4000 -1150,3996 -1136,3989 C -1118,3980 -1101,3966 -1087,3946 z M -1228,4138 C -1200,4100 -1205,4081 -1244,4081 L -1371,4081 L -1453,4193 L -1328,4193 C -1309,4193 -1292,4190 -1277,4182 C -1259,4173 -1243,4158 -1228,4138 z "
										class="fil1" /><path
										style="fill:#654b6d"
										id="path25"
										d="M -615,4041 C -679,4129 -747,4194 -819,4234 C -877,4268 -937,4284 -996,4284 C -1027,4284 -1051,4280 -1068,4272 C -1095,4260 -1107,4237 -1104,4205 C -1100,4165 -1072,4110 -1021,4040 C -972,3973 -923,3921 -874,3882 C -833,3851 -790,3828 -746,3814 C -713,3804 -678,3799 -642,3799 C -584,3799 -548,3812 -534,3837 C -523,3858 -526,3889 -545,3930 C -559,3959 -582,3996 -615,4041 z M -707,4042 C -662,3981 -641,3937 -643,3910 C -645,3888 -663,3877 -698,3877 C -733,3877 -771,3891 -811,3918 C -851,3945 -891,3986 -930,4040 C -968,4091 -990,4128 -996,4152 C -1006,4189 -987,4207 -940,4207 C -899,4207 -858,4192 -818,4161 C -783,4135 -746,4095 -707,4042 z "
										class="fil1" /><path
										style="fill:#654b6d"
										id="path27"
										d="M -357,4274 L -450,4274 L -406,4091 L -509,4091 L -643,4274 L -727,4274 L -385,3808 L -175,3808 C -128,3808 -100,3818 -92,3838 C -84,3860 -98,3897 -135,3948 C -161,3983 -187,4011 -215,4032 C -246,4055 -278,4071 -312,4079 L -357,4274 z M -221,3950 C -204,3927 -199,3911 -205,3903 C -212,3895 -229,3890 -256,3890 L -362,3890 L -450,4010 L -344,4010 C -316,4010 -296,4008 -283,4003 C -262,3995 -241,3977 -221,3950 z "
										class="fil1" /><path
										style="fill:#654b6d"
										id="path29"
										d="M 80,4274 L -13,4274 L 31,4162 L -148,4162 L -266,4274 L -354,4274 L 158,3808 L 247,3808 L 80,4274 z M 63,4081 L 136,3896 L -61,4081 L 63,4081 z "
										class="fil1" /><path
										style="fill:#654b6d"
										id="path31"
										d="M 624,3889 L 492,3889 L 210,4274 L 121,4274 L 404,3889 L 274,3889 L 332,3810 L 682,3810 L 624,3889 z "
										class="fil1" /><path
										style="fill:#654b6d"
										id="path33"
										d="M 937,4041 C 872,4129 804,4194 733,4234 C 674,4268 615,4284 556,4284 C 524,4284 500,4280 484,4272 C 457,4260 445,4237 448,4205 C 452,4165 479,4110 531,4040 C 580,3973 629,3921 678,3882 C 719,3851 761,3828 806,3814 C 839,3804 874,3799 910,3799 C 968,3799 1004,3812 1018,3837 C 1029,3858 1026,3889 1007,3930 C 993,3959 970,3996 937,4041 z M 845,4042 C 890,3981 911,3937 909,3910 C 907,3888 889,3877 853,3877 C 818,3877 781,3891 741,3918 C 701,3945 661,3986 621,4040 C 584,4091 562,4128 556,4152 C 546,4189 565,4207 612,4207 C 653,4207 694,4192 734,4161 C 769,4135 806,4095 845,4042 z "
										class="fil1" /><path
										style="fill:#654b6d"
										id="path35"
										d="M 1195,4274 L 1102,4274 L 1145,4091 L 1043,4091 L 909,4274 L 825,4274 L 1167,3808 L 1377,3808 C 1424,3808 1452,3818 1459,3838 C 1468,3860 1454,3897 1416,3948 C 1391,3983 1364,4011 1337,4032 C 1306,4055 1273,4071 1240,4079 L 1195,4274 z M 1331,3950 C 1348,3927 1353,3911 1346,3903 C 1340,3895 1323,3890 1295,3890 L 1190,3890 L 1102,4010 L 1208,4010 C 1236,4010 1256,4008 1269,4003 C 1290,3995 1311,3977 1331,3950 z "
										class="fil1" />
</svg>
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2008-2015 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

import cgitb, cgi, os, sys
sys.path.append('modules')
from logintools import isloggedin
from logintools import logout
from lib import config
thisfile = os.environ.get('SCRIPT_NAME',"index.cgi").split("/")[-1]
cgitb.enable()
form = cgi.FieldStorage()

thisfile = os.environ.get('SCRIPT_NAME',".")
userdir = 'users/'

logint = form.getvalue("login",None)
if logint == "logout":
	print logout(userdir) # printing cookie header. important!
	print "Content-Type: text/html\n" # blank line: end of headers
	print '<img src="images/arboratorNano.png" border="0"> Logging out...'
	print '''<script type="text/javascript">setTimeout('window.location.href=".";',1000)</script>'''
	sys.exit()
project = form.getvalue("project",None)
if project: 
	try:	project=project.decode("utf-8")
	except:	pass
if len(config.projects)==1:project=config.projects[0]

action = form.getvalue("action",None)
if action:
	action=action.decode("utf-8")
	if action.startswith("project_"):project=action[8:]
if project: action="project_"+project

logintest = isloggedin(userdir)
if logintest:
	userconfig = logintest[0]
#action, userconfig = login(form, userdir, thisfile, action)
	adminLevel, username, realname = int(userconfig["admin"]),userconfig["username"],userconfig["realname"]
else:
	#adminLevel, username, realname = 0,"guest","guest"
	adminLevel, username, realname = 0,None,None
print "Content-Type: text/html\n" # blank line: end of headers

projectconfig=None
try:	projectconfig = config.configProject(project) # read in the settings of the current project
except:	pass

######################   head    ###########################

print """
<html>
<head><title>Arborator</title>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">

<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />
<link href="css/arborator.css" rel="stylesheet" type="text/css">
<link rel="icon" type="image/png" href="favicon.ico">
<script type="text/javascript" src="script/jquery.js"></script>
<script type="text/javascript" src="script/raphael.js"></script>
<script type="text/javascript" src="script/jquery-ui-1.8.18.custom.min.js"></script>

<script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-11144858-1']);
  _gaq.push(['_setDomainName', '.ilpga.fr']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
  
var BrowserDetect = {
	init: function () {
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
	},
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1)
					return data[i].identity;
			}
			else if (dataProp)
				return data[i].identity;
		}
	},
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) return;
		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	},
	dataBrowser: [
		{
			string: navigator.userAgent,
			subString: "Chrome",
			identity: "Chrome"
		},
		{ 	string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari",
			versionSearch: "Version"
		},
		{
			prop: window.opera,
			identity: "Opera",
			versionSearch: "Version"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			   string: navigator.userAgent,
			   subString: "iPhone",
			   identity: "iPhone/iPod"
	    },
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	]

};
BrowserDetect.init();

</script></head>
"""

########################################### body #####################################

print """

<body id="body">

<div class="arbortitle fg-toolbar ui-widget-header ui-helper-clearfix">
	<a href='.' style='position: absolute;left:0px;'><img src="images/arboratorNano.png" border="0"></a>
	<span style='text-align:center;margin:0 auto;position: relative;top:0px;' id='sentinfo'>Welcome to the Arborator website!</span>
</div>
"""

if os.path.exists("sitemessage.html"):print open("sitemessage.html").read()

if project :
	print '<form method="post" action="project.cgi" name="enter" id="enter" class="nav" style="top:4px;right:220px;z-index: 9999;">'
	if logintest and logint != "logout" : buttonname="Enter"# enter
	else: buttonname="Login"
	print (u"""<input type="button" id="ent" name="ent" value="{buttonname}" style="width:80px;" class="fg-button ui-state-default ui-corner-all"  onClick="window.location.href='project.cgi?project={project}'">""".format(project=project.replace("'","\\'").replace('"','\\"'), buttonname=buttonname)).encode("utf-8")
	#print """<input type="button" id="visit" name="ent" value="Visit" style="width:80px;" class="fg-button ui-state-default ui-corner-all" onClick="window.location.href='visit.cgi'";">"""

	print "</form>"

print """
<div id="dialog" style="display: none;width:500px" title="Login">
</div>
"""

print """<div class='center'>
	<div class="ui-widget ui-widget-content ui-corner-all" style="font-size:23px;margin-top:37px;padding:10px">
	
	<div style="text-align:center;">
		<object height="300" width="500" data="images/arborator.svg" type="image/svg+xml"></object>
	
	
"""

print """<p style="text-align:center;">collaboratively annotating dependency corpora</p>
	<div class='ui-state-highlight ui-corner-all' style='padding: 1em; margin: 20px;clear: both;'><a href='q.cgi'><img src="images/q.png" border="0"><br/>Quick access without login:<br/>Simple graphical editor of CoNLL files</a></div>

	<div class="ui-state-highlight ui-corner-all" style="padding-bottom: 1em; margin: 20px;">"""
	
if project or len(config.projects)==1:
	try:
		projectconfig = config.configProject(project) # read in the settings of the current project
	except:
		print "some problem with the configuration of the project",project.encode("utf-8")
		
	finally:
		print "this page gives acces to the <b>",project.encode("utf-8"),"</b> annotation project.<br/>"
		print '<form method="post" action="project.cgi" name="enter" id="enter2">'
		if logintest and logint != "logout" : buttonname="Enter"# enter
		else: buttonname="Login"
		print (u"""C'mon in: Click on <input type="button" id="ent" name="ent" value="{buttonname}"  style="font:normal .8em Arial;padding-top: 2px;width:80px;" class="fg-button ui-state-default ui-corner-all"  onClick="window.location.href='project.cgi?project={project}'">""".format(project=project.replace("'","\\'").replace('"','\\"'),buttonname=buttonname)).encode("utf-8")
		print "</form>"
		if len(config.projects)>1: print "<p><small><a href='.?project='>For access to other annotation projects on this site click here</a></small></p>"
else:
	print '<div>or choose your annotation project:</div>'
	for project in sorted(config.projects):
		if os.path.exists(("projects/"+project+"/"+project+".png").encode("utf-8")):img=u"<img class='project_logo' src='projects/"+project+"/"+project+u".png' align='top'>"
		else: img=""
		print 	'<div class="ui-widget ui-widget-content ui-corner-all" style="float:left;margin:1.2em;"><p>',
		if logintest:
			print (u'<a href="project.cgi?project={project}">{img} {project}'.format(project=project, img=img)).encode("utf-8"),
		else:
			print (u'<a href="?project={project}">{img} {project}'.format(project=project, img=img)).encode("utf-8"),
		print '</a></p></div>'
		#print '<div style="clear: both;"></div>'
	
	print '<div style="clear: both;"></div>'


print "</div><div class='ui-state-highlight ui-corner-all' style='padding: 1em; margin: 20px;clear: both;'><a href='https://github.com/kimgerdes/arborator/wiki'>Short usage guide (wiki page)</a></div>"


#print """<br/><br/>

	#<table style="text-align:center;width:100%:background:none;"><tr><td>
	#<div class="ui-widget ui-widget-content ui-corner-all" ><p><a href='http://arborator.ilpga.fr'><img border="0" src="images/arboratorNano.png"><br>The Arborator website</a></p></div>
	#</td><td>
	#<div class="ui-widget ui-widget-content ui-corner-all" ><p>The French user and annotation guide:<br><a href='http://arborator.ilpga.fr/wiki' >Guide d'annotation</a></p></div>
	#</td><td>
	#<div class="ui-widget ui-widget-content ui-corner-all" ><p><a href='http://rhapsodie.ilpga.fr/'><img border="0" src="images/rhapsodie.png"><br>The Rhapsodie project's syntax site</a></p></div>

	
	#</td></tr></table>
	



#&nbsp;
#""" <img border='0' src='images/firefox.png' align='middle'> 

print '''</div></div><br/>
<div class='ui-widget ui-widget-content ui-corner-all' style='padding: 2em; margin-left: 199px; margin-right: 199px;clear: both;text-align:center;position: relative;'>
	<p style="font-size:.7em;margin:1;">
		<img border="0" src="images/arboratorNano.png" align='middle'>
Arborator is free software.
	</p><br><hr>
	<table style='margin-top:0;width:100%'><tr><td>
	<p style="font-size:.7em;margin:1;">
	<a href="http://www.gnu.org/licenses/agpl-3.0.html">
It is realeased under the 
	</p> <p style="font-size:.7em;margin:1;">
	
		<img src="images/Affero_General_Public_License_3.png" align='middle'>
licence
	</td>
	
	</tr>
	</table></a></p> <p style="font-size:.7em;margin:1;">   
It can <a href="http://rhapsodie.ilpga.fr/wiki/Arborator#site_administration_and_installation">easily be installed on any Apache server.</a>
	</p><p style="font-size:.7em;margin:1;">
This software relies on your browser's SVG capacity and is written principally for use in the 
	<img border="0" src="images/firefox.grey.png" align='middle'> 
Firefox browser.
	<br><br>
	<script>
		if (BrowserDetect.browser=="Firefox") document.write("You are using Firefox "+BrowserDetect.version+" on "+BrowserDetect.OS+". Everything should work fine.");
		else if (BrowserDetect.browser=="Chrome") document.write("You are using Chrome "+BrowserDetect.version+" on "+BrowserDetect.OS+". This Browser is not fully tested.");
		else document.write("You are using an unsupported browser ("+BrowserDetect.browser+" "+BrowserDetect.version+" on "+BrowserDetect.OS+"). Arborator is fully functional only on Firefox.");
	</script>
</p>
<br>
<div class="ui-widget ui-widget-content ui-corner-all"  style='
font:italic normal .8em/1em Times, serif;text-align:center;margin:0 auto;padding:1em;'>
	written by <a href='http://gerdes.fr'>Kim Gerdes</a><br/><br/>
	<a href='http://www.univ-paris3.fr/'>Sorbonne nouvelle</a>,
	<a href='http://ilpga.fr/'>ILPGA</a>,
	<a href='http://lpp.univ-paris3.fr/'>LPP</a>
	(<a href='http://lpp.univ-paris3.fr/'>CNRS</a>)
	
	<br><br>
	
	<a href="https://github.com/kimgerdes/arborator"><img style="position: absolute; top: 0; right: 0; border: 0;" src="images/forkme.png" alt="Fork me on GitHub"></a>

</div>
</div>



'''
#	<a href='http://english.cas.cn/'>Chinese Academy of Sciences</a>,
#	<a href='http://english.ia.cas.cn/'>Institute of Automation</a>,
#	<a href='http://nlpr-web.ia.ac.cn/'>NLPR</a>
#
#<td>
#</a></p> <p style="font-size:.7em;margin:1;">
#<a href="https://launchpad.net/arborator">
#downloadable on
#</p> <p style="font-size:.7em;margin:1;"> 
	#<img src="images/launchpad.png"  align='bottom'></p>
#</td>

print " <br><br>"

if username:
	
	print '<div class="arborfoot fg-toolbar ui-widget-header ui-helper-clearfix">'
	print "logged in as",username
	print '&nbsp;&nbsp;&nbsp;<a href="'+thisfile+'?login=logout">logout</a>'
	if username!="guest":
		
		print '&nbsp;&nbsp;&nbsp;<a href="'+thisfile+'?login=editaccount">edit the account '+username+'</a>'
	if adminLevel > 1: print '&nbsp;&nbsp;&nbsp;<a href="'+thisfile+'''?login=admin">User Administration</a>''' # &nbsp;&nbsp;&nbsp;<a href="admin.cgi">Corpus Administration</a>
	print "</div>"

	#if userconfig["username"] == "admin":print '<a href="'+thisfile+'?login=admin">Administration</a>'

print '</body></html>'
## DepEdit init ##
from depedit import DepEdit
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, codecs, json, colorsys, random, sys
from configobj import ConfigObj

try:	
	projects = [f.decode('utf-8') for f in os.listdir("projects")]
	projectsfolder=os.path.realpath("projects")
except:
	try:
		projects = [f.decode('utf-8') for f in os.listdir("../projects")]
		projectsfolder =  os.path.realpath("../projects")
	except:
		print "can't find projects folder"
		sys.exit()
verbose=False
#verbose=True



def configProject(projectName):
	""" read in config file"""
	if projectName==None:return
	filename=os.path.join(projectsfolder,unicode(projectName),u"project.cfg" ).encode("utf-8")
	if projectName not in projects: 
		print 'Content-type: text/plain\n\n',"error in projects:",type(projectName),"projectName:",[projectName]
		print projects
		return
		
	if os.path.exists(filename):
		try:
			config = ConfigObj(filename,encoding="UTF-8")
			#config.BOM=True
			if verbose : print "read", filename
						
		except Exception, e:
			if verbose : print "can't read config file:",filename,e
			return
	return readinContent(config,projectName)		

def readinContent(config,projectName, tmp=""):
	
	config.shownfeatures=[config["shown features"][i] for i in sorted(config["shown features"])]
	config.shownsentencefeatures=[config["shown sentencefeatures"][i] for i in sorted(config["shown sentencefeatures"])]
	
	config.functions=[]
	config.funcDic={}
	#try:
	#(os.getcwd(),"projects",projectName.encode("utf-8"),config["configuration"]["functionsfilename"].encode("utf-8") ) 
	fn=os.path.join(projectsfolder,projectName.encode("utf-8"),(config["configuration"]["functionsfilename"]+tmp).encode("utf-8") ) 
	f=codecs.open(fn ,"r","utf-8")
	for li in f: 
		if li.strip(): 
			if li[0]=='\ufeff':li=li[1:] # TODO: find out why this anti BOM shit is needed...
			par=li.strip().split("\t")
			if par[0][0]=="#":continue
			atts={}
			if len(par)==2: 
				a=par[1].strip().split(" #")[0] # not ideal: only removes comments that are preceded by space (to avoid confusion with hex values for colors)
				try:atts=json.loads(a)
				except:pass
			config.funcDic[par[0]]=atts
			config.functions+=[par[0]]
			#config.functions+=[li.strip()]
	f.close()
	#except Exception, e:
		#if verbose : print "can't read file:",fn,e
	#print type(config.functions)
	config.funcDic[config["configuration"]["erase"]]={}
	config.functions+=[config["configuration"]["erase"]]
	attStroke(config.funcDic)
	
	
	config.categories=[]
	config.catDic={}
	#config.categories={}
	#try:
	fn=os.path.join(projectsfolder,projectName.encode("utf-8"),(config["configuration"]["categoriesfilename"]+tmp).encode("utf-8") )  
	f=codecs.open(fn,"r","utf-8")
	for li in f:
		if li.strip(): 
			if li.strip()[0]==u'\ufeff':li=li[1:] # TODO: find out why this anti BOM shit is needed...
			par=li.strip().split("\t")
			if par[0][0]=="#":continue
			atts={}
			if len(par)==2:
				a=par[1].strip().split(" #")[0]
				atts=json.loads(a)
			#if len(par)==2: atts=par[1]
			config.catDic[par[0]]=atts
			config.categories+=[par[0]]
	f.close()
	#except Exception, e:
		#if verbose : print "_____can't read file:",fn,e

	attFill(config.catDic)
	
	return config

def checkConfigProject(projectName, filename=u"project.cfg",readin=True,tmp=""):	
	filename=unicode(os.path.join(projectsfolder,projectName,filename )).encode("utf-8")
	config = ConfigObj(filename,encoding="UTF-8")
	if readin:return readinContent(config,projectName, tmp)	
	else: return config
	
	
	
	
	

def simpleJsDefPrinter(functions, categories, shownfeatures=["t","tag","lemma"]):
	"""
	
	for viewer
	
	"""
	print """
	<script type="text/javascript">"""
	
	print ("shownfeatures = ['" + "','".join( shownfeatures ) +"'];").encode("utf-8")
	print "shownsentencefeatures=[];"
	functions+=["erase"]
	print ("functions = "+json.dumps(functions) +";").encode("utf-8")
	funcDic={}
	for f in functions:	funcDic[f]=None
	attStroke(funcDic)
	print ("funcDic = "+json.dumps(funcDic)+";").encode("utf-8")	
	print ("defaultfunction = '"+functions[0]+"';").encode("utf-8")
	print "categories = ",json.dumps(categories),";"
	catDic={}
	for c in categories:	catDic[c]=None
	attFill(catDic)
	print "catDic = ",json.dumps(catDic),";"
	print ("defaultcategory = '"+categories[0]+"';").encode("utf-8")
	print "categoryindex = '1';"
	print ("erase = 'erase';").encode("utf-8")
	print ("root = 'root';").encode("utf-8")
	print '</script>'	
	return functions,funcDic,categories,catDic
	
	
def attStroke(dic):
	for fu,co in list2colors([ f for f,c in dic.iteritems() if not c]).iteritems():
		dic[fu]=json.loads('{"stroke": "#'+co+'","stroke-width":"1"}')
	
def attFill(dic):
	for fu,co in list2colors([ f for f,c in dic.iteritems() if not c]).iteritems():
		dic[fu]=json.loads('{"fill": "#'+co+'"}')
	
#def firstProject():
	


#syntfunctions=[f.replace(","," ") for f in syntfunctionstext.split()]
#["suj","objd","obji","objloc","attrmesu","aux","comp","Ã©pi","appos","mod","prÃ©p","dÃ©t","attrsuj","attrobj","conj","coord","racine"]
#categories=["nom","nomp","conjsub","conjcoo","prÃ©p","pro","prori","adj","adv","parti","verbe","dÃ©t","num","interj","xxx"]
#categories=["nc","pnoun","conjsub","coo","prep","pro","prel","adj","adv","parti","verb","det","num","interj","xxx"]
#syntfunctionstext="""
 #suj, objd, compnom,
 #prÃ©p, comp, aux, coord, conj, 
 #dÃ©t, Ã©pi, Ã©pi-par, 
 #attr-suj, attr-obj,  comp-mesu, qsuj, qobj, obji, objobl, objloc, comp-man,
 #aj-attr, aj-loc, aj-temp, aj-man, aj,
  #app, app-par,
 #suj-disl, objd-disl, obji-disl, loc-disl, temp-disl, disl,
 #racine
#"""
#syntfunctionstext="""rac rÃ©g arg suj comp attr obj obl aj aux dÃ©t asso intro para"""
#syntfunctionstext="""root arg      subj  comp       pred     obj   iobj          mod   asso    aux det"""

##################################### functions: TODO: make work even for variable color scheme!!!

def jsDefPrinter(config):
	
	print """
	<script type="text/javascript">"""
	
	print ("shownfeatures = ['" + "','".join( config.shownfeatures ) +"'];").encode("utf-8")
	print ("shownsentencefeatures = ['" + "','".join( config.shownsentencefeatures ) +"'];").encode("utf-8")
	print ("functions = "+json.dumps(config.functions+[config["configuration"]["erase"]] ) +";").encode("utf-8")
	print ("funcDic = "+json.dumps(config.funcDic)+";").encode("utf-8")	
	
	print ("defaultcategory = '"+config["configuration"]["defaultcategory"]+"';").encode("utf-8")
	print "categoryindex = '"+config["configuration"]["categoryindex"]+"';"
	print ("erase = '"+config["configuration"]["erase"]+"';").encode("utf-8")
	print ("defaultfunction = '"+config["configuration"]["defaultfunction"]+"';").encode("utf-8")
	print ("root = '"+config["configuration"]["root"]+"';").encode("utf-8")

	print "categories = ",json.dumps(config.categories),";"
	print "catDic = ",json.dumps(config.catDic),";"
	print '</script>'
	#return fcolors,ccolors

def oldlist2colors(lis):
	
	if len(lis)>0:
		#colorstep=(256*256*256-200000)/len(lis)
		colorstep=(256*256*256)/(len(lis)+1)
		colors=[str(hex(i*colorstep))[2:].rjust(6,"0") for i in range(1,len(lis)+1)]
		fus = dict(zip(lis,colors))
		#fat = dict(zip(lis,[json.loads('{"stroke": "#'+c+'","stroke-width":"1"}') for c in colors])) 
		return fus
		#,fat
	return {}

def list2colors(lis):
	
	if len(lis)>0:
		colorstep=1.0/(len(lis))
		colors=[ '%02x%02x%02x' % tuple(255*c for c in colorsys.hls_to_rgb(i*colorstep, random.uniform(.3, .6),random.uniform(.4, 1.0))) for i in range(0,len(lis))]
		fus = dict(zip(lis,colors))
		return fus
	return {}
	
	
if __name__ == "__main__":	
	#c = configProject(projects[0])
	#c = configProject("Rhapsodie")
	#jsDefPrinter(c)
	print list2colors(["a","b","c"])

	
# configobj.py
# A config file reader/writer that supports nested sections in config files.
# Copyright (C) 2005-2008 Michael Foord, Nicola Larosa
# E-mail: fuzzyman AT voidspace DOT org DOT uk
#         nico AT tekNico DOT net

# ConfigObj 4
# http://www.voidspace.org.uk/python/configobj.html

# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# For information about bugfixes, updates and support, please join the
# ConfigObj mailing list:
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
# Comments, suggestions and bug reports welcome.

from __future__ import generators

import sys
INTP_VER = sys.version_info[:2]
if INTP_VER < (2, 2):
    raise RuntimeError("Python v.2.2 or later needed")

import os, re
compiler = None
try:
    import compiler
except ImportError:
    # for IronPython
    pass
from types import StringTypes
from warnings import warn
try:
    from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
except ImportError:
    # Python 2.2 does not have these
    # UTF-8
    BOM_UTF8 = '\xef\xbb\xbf'
    # UTF-16, little endian
    BOM_UTF16_LE = '\xff\xfe'
    # UTF-16, big endian
    BOM_UTF16_BE = '\xfe\xff'
    if sys.byteorder == 'little':
        # UTF-16, native endianness
        BOM_UTF16 = BOM_UTF16_LE
    else:
        # UTF-16, native endianness
        BOM_UTF16 = BOM_UTF16_BE

# A dictionary mapping BOM to
# the encoding to decode with, and what to set the
# encoding attribute to.
BOMS = {
    BOM_UTF8: ('utf_8', None),
    BOM_UTF16_BE: ('utf16_be', 'utf_16'),
    BOM_UTF16_LE: ('utf16_le', 'utf_16'),
    BOM_UTF16: ('utf_16', 'utf_16'),
    }
# All legal variants of the BOM codecs.
# TODO: the list of aliases is not meant to be exhaustive, is there a
#   better way ?
BOM_LIST = {
    'utf_16': 'utf_16',
    'u16': 'utf_16',
    'utf16': 'utf_16',
    'utf-16': 'utf_16',
    'utf16_be': 'utf16_be',
    'utf_16_be': 'utf16_be',
    'utf-16be': 'utf16_be',
    'utf16_le': 'utf16_le',
    'utf_16_le': 'utf16_le',
    'utf-16le': 'utf16_le',
    'utf_8': 'utf_8',
    'u8': 'utf_8',
    'utf': 'utf_8',
    'utf8': 'utf_8',
    'utf-8': 'utf_8',
    }

# Map of encodings to the BOM to write.
BOM_SET = {
    'utf_8': BOM_UTF8,
    'utf_16': BOM_UTF16,
    'utf16_be': BOM_UTF16_BE,
    'utf16_le': BOM_UTF16_LE,
    None: BOM_UTF8
    }


def match_utf8(encoding):
    return BOM_LIST.get(encoding.lower()) == 'utf_8'


# Quote strings used for writing values
squot = "'%s'"
dquot = '"%s"'
noquot = "%s"
wspace_plus = ' \r\t\n\v\t\'"'
tsquot = '"""%s"""'
tdquot = "'''%s'''"

try:
    enumerate
except NameError:
    def enumerate(obj):
        """enumerate for Python 2.2."""
        i = -1
        for item in obj:
            i += 1
            yield i, item

try:
    True, False
except NameError:
    True, False = 1, 0


__version__ = '4.5.3'

__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'

__docformat__ = "restructuredtext en"

__all__ = (
    '__version__',
    'DEFAULT_INDENT_TYPE',
    'DEFAULT_INTERPOLATION',
    'ConfigObjError',
    'NestingError',
    'ParseError',
    'DuplicateError',
    'ConfigspecError',
    'ConfigObj',
    'SimpleVal',
    'InterpolationError',
    'InterpolationLoopError',
    'MissingInterpolationOption',
    'RepeatSectionError',
    'ReloadError',
    'UnreprError',
    'UnknownType',
    '__docformat__',
    'flatten_errors',
)

DEFAULT_INTERPOLATION = 'configparser'
DEFAULT_INDENT_TYPE = '    '
MAX_INTERPOL_DEPTH = 10

OPTION_DEFAULTS = {
    'interpolation': True,
    'raise_errors': False,
    'list_values': True,
    'create_empty': False,
    'file_error': False,
    'configspec': None,
    'stringify': True,
    # option may be set to one of ('', ' ', '\t')
    'indent_type': None,
    'encoding': None,
    'default_encoding': None,
    'unrepr': False,
    'write_empty_values': False,
}



def getObj(s):
    s = "a=" + s
    if compiler is None:
        raise ImportError('compiler module not available')
    p = compiler.parse(s)
    return p.getChildren()[1].getChildren()[0].getChildren()[1]


class UnknownType(Exception):
    pass


class Builder(object):
    
    def build(self, o):
        m = getattr(self, 'build_' + o.__class__.__name__, None)
        if m is None:
            raise UnknownType(o.__class__.__name__)
        return m(o)
    
    def build_List(self, o):
        return map(self.build, o.getChildren())
    
    def build_Const(self, o):
        return o.value
    
    def build_Dict(self, o):
        d = {}
        i = iter(map(self.build, o.getChildren()))
        for el in i:
            d[el] = i.next()
        return d
    
    def build_Tuple(self, o):
        return tuple(self.build_List(o))
    
    def build_Name(self, o):
        if o.name == 'None':
            return None
        if o.name == 'True':
            return True
        if o.name == 'False':
            return False
        
        # An undefined Name
        raise UnknownType('Undefined Name')
    
    def build_Add(self, o):
        real, imag = map(self.build_Const, o.getChildren())
        try:
            real = float(real)
        except TypeError:
            raise UnknownType('Add')
        if not isinstance(imag, complex) or imag.real != 0.0:
            raise UnknownType('Add')
        return real+imag
    
    def build_Getattr(self, o):
        parent = self.build(o.expr)
        return getattr(parent, o.attrname)
    
    def build_UnarySub(self, o):
        return -self.build_Const(o.getChildren()[0])
    
    def build_UnaryAdd(self, o):
        return self.build_Const(o.getChildren()[0])


_builder = Builder()


def unrepr(s):
    if not s:
        return s
    return _builder.build(getObj(s))



class ConfigObjError(SyntaxError):
    """
    This is the base class for all errors that ConfigObj raises.
    It is a subclass of SyntaxError.
    """
    def __init__(self, message='', line_number=None, line=''):
        self.line = line
        self.line_number = line_number
        self.message = message
        SyntaxError.__init__(self, message)


class NestingError(ConfigObjError):
    """
    This error indicates a level of nesting that doesn't match.
    """


class ParseError(ConfigObjError):
    """
    This error indicates that a line is badly written.
    It is neither a valid ``key = value`` line,
    nor a valid section marker line.
    """


class ReloadError(IOError):
    """
    A 'reload' operation failed.
    This exception is a subclass of ``IOError``.
    """
    def __init__(self):
        IOError.__init__(self, 'reload failed, filename is not set.')


class DuplicateError(ConfigObjError):
    """
    The keyword or section specified already exists.
    """


class ConfigspecError(ConfigObjError):
    """
    An error occured whilst parsing a configspec.
    """


class InterpolationError(ConfigObjError):
    """Base class for the two interpolation errors."""


class InterpolationLoopError(InterpolationError):
    """Maximum interpolation depth exceeded in string interpolation."""

    def __init__(self, option):
        InterpolationError.__init__(
            self,
            'interpolation loop detected in value "%s".' % option)


class RepeatSectionError(ConfigObjError):
    """
    This error indicates additional sections in a section with a
    ``__many__`` (repeated) section.
    """


class MissingInterpolationOption(InterpolationError):
    """A value specified for interpolation was missing."""

    def __init__(self, option):
        InterpolationError.__init__(
            self,
            'missing option "%s" in interpolation.' % option)


class UnreprError(ConfigObjError):
    """An error parsing in unrepr mode."""



class InterpolationEngine(object):
    """
    A helper class to help perform string interpolation.

    This class is an abstract base class; its descendants perform
    the actual work.
    """

    # compiled regexp to use in self.interpolate()
    _KEYCRE = re.compile(r"%\(([^)]*)\)s")

    def __init__(self, section):
        # the Section instance that "owns" this engine
        self.section = section


    def interpolate(self, key, value):
        def recursive_interpolate(key, value, section, backtrail):
            """The function that does the actual work.

            ``value``: the string we're trying to interpolate.
            ``section``: the section in which that string was found
            ``backtrail``: a dict to keep track of where we've been,
            to detect and prevent infinite recursion loops

            This is similar to a depth-first-search algorithm.
            """
            # Have we been here already?
            if backtrail.has_key((key, section.name)):
                # Yes - infinite loop detected
                raise InterpolationLoopError(key)
            # Place a marker on our backtrail so we won't come back here again
            backtrail[(key, section.name)] = 1

            # Now start the actual work
            match = self._KEYCRE.search(value)
            while match:
                # The actual parsing of the match is implementation-dependent,
                # so delegate to our helper function
                k, v, s = self._parse_match(match)
                if k is None:
                    # That's the signal that no further interpolation is needed
                    replacement = v
                else:
                    # Further interpolation may be needed to obtain final value
                    replacement = recursive_interpolate(k, v, s, backtrail)
                # Replace the matched string with its final value
                start, end = match.span()
                value = ''.join((value[:start], replacement, value[end:]))
                new_search_start = start + len(replacement)
                # Pick up the next interpolation key, if any, for next time
                # through the while loop
                match = self._KEYCRE.search(value, new_search_start)

            # Now safe to come back here again; remove marker from backtrail
            del backtrail[(key, section.name)]

            return value

        # Back in interpolate(), all we have to do is kick off the recursive
        # function with appropriate starting values
        value = recursive_interpolate(key, value, self.section, {})
        return value


    def _fetch(self, key):
        """Helper function to fetch values from owning section.

        Returns a 2-tuple: the value, and the section where it was found.
        """
        # switch off interpolation before we try and fetch anything !
        save_interp = self.section.main.interpolation
        self.section.main.interpolation = False

        # Start at section that "owns" this InterpolationEngine
        current_section = self.section
        while True:
            # try the current section first
            val = current_section.get(key)
            if val is not None:
                break
            # try "DEFAULT" next
            val = current_section.get('DEFAULT', {}).get(key)
            if val is not None:
                break
            # move up to parent and try again
            # top-level's parent is itself
            if current_section.parent is current_section:
                # reached top level, time to give up
                break
            current_section = current_section.parent

        # restore interpolation to previous value before returning
        self.section.main.interpolation = save_interp
        if val is None:
            raise MissingInterpolationOption(key)
        return val, current_section


    def _parse_match(self, match):
        """Implementation-dependent helper function.

        Will be passed a match object corresponding to the interpolation
        key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
        key in the appropriate config file section (using the ``_fetch()``
        helper function) and return a 3-tuple: (key, value, section)

        ``key`` is the name of the key we're looking for
        ``value`` is the value found for that key
        ``section`` is a reference to the section where it was found

        ``key`` and ``section`` should be None if no further
        interpolation should be performed on the resulting value
        (e.g., if we interpolated "$$" and returned "$").
        """
        raise NotImplementedError()
    


class ConfigParserInterpolation(InterpolationEngine):
    """Behaves like ConfigParser."""
    _KEYCRE = re.compile(r"%\(([^)]*)\)s")

    def _parse_match(self, match):
        key = match.group(1)
        value, section = self._fetch(key)
        return key, value, section



class TemplateInterpolation(InterpolationEngine):
    """Behaves like string.Template."""
    _delimiter = '$'
    _KEYCRE = re.compile(r"""
        \$(?:
          (?P<escaped>\$)              |   # Two $ signs
          (?P<named>[_a-z][_a-z0-9]*)  |   # $name format
          {(?P<braced>[^}]*)}              # ${name} format
        )
        """, re.IGNORECASE | re.VERBOSE)

    def _parse_match(self, match):
        # Valid name (in or out of braces): fetch value from section
        key = match.group('named') or match.group('braced')
        if key is not None:
            value, section = self._fetch(key)
            return key, value, section
        # Escaped delimiter (e.g., $$): return single delimiter
        if match.group('escaped') is not None:
            # Return None for key and section to indicate it's time to stop
            return None, self._delimiter, None
        # Anything else: ignore completely, just return it unchanged
        return None, match.group(), None


interpolation_engines = {
    'configparser': ConfigParserInterpolation,
    'template': TemplateInterpolation,
}



class Section(dict):
    """
    A dictionary-like object that represents a section in a config file.
    
    It does string interpolation if the 'interpolation' attribute
    of the 'main' object is set to True.
    
    Interpolation is tried first from this object, then from the 'DEFAULT'
    section of this object, next from the parent and its 'DEFAULT' section,
    and so on until the main object is reached.
    
    A Section will behave like an ordered dictionary - following the
    order of the ``scalars`` and ``sections`` attributes.
    You can use this to change the order of members.
    
    Iteration follows the order: scalars, then sections.
    """

    def __init__(self, parent, depth, main, indict=None, name=None):
        """
        * parent is the section above
        * depth is the depth level of this section
        * main is the main ConfigObj
        * indict is a dictionary to initialise the section with
        """
        if indict is None:
            indict = {}
        dict.__init__(self)
        # used for nesting level *and* interpolation
        self.parent = parent
        # used for the interpolation attribute
        self.main = main
        # level of nesting depth of this Section
        self.depth = depth
        # purely for information
        self.name = name
        #
        self._initialise()
        # we do this explicitly so that __setitem__ is used properly
        # (rather than just passing to ``dict.__init__``)
        for entry, value in indict.iteritems():
            self[entry] = value
            
            
    def _initialise(self):
        # the sequence of scalar values in this Section
        self.scalars = []
        # the sequence of sections in this Section
        self.sections = []
        # for comments :-)
        self.comments = {}
        self.inline_comments = {}
        # for the configspec
        self.configspec = {}
        self._order = []
        self._configspec_comments = {}
        self._configspec_inline_comments = {}
        self._cs_section_comments = {}
        self._cs_section_inline_comments = {}
        # for defaults
        self.defaults = []
        self.default_values = {}


    def _interpolate(self, key, value):
        try:
            # do we already have an interpolation engine?
            engine = self._interpolation_engine
        except AttributeError:
            # not yet: first time running _interpolate(), so pick the engine
            name = self.main.interpolation
            if name == True:  # note that "if name:" would be incorrect here
                # backwards-compatibility: interpolation=True means use default
                name = DEFAULT_INTERPOLATION
            name = name.lower()  # so that "Template", "template", etc. all work
            class_ = interpolation_engines.get(name, None)
            if class_ is None:
                # invalid value for self.main.interpolation
                self.main.interpolation = False
                return value
            else:
                # save reference to engine so we don't have to do this again
                engine = self._interpolation_engine = class_(self)
        # let the engine do the actual work
        return engine.interpolate(key, value)


    def __getitem__(self, key):
        """Fetch the item and do string interpolation."""
        val = dict.__getitem__(self, key)
        if self.main.interpolation and isinstance(val, StringTypes):
            return self._interpolate(key, val)
        return val


    def __setitem__(self, key, value, unrepr=False):
        """
        Correctly set a value.
        
        Making dictionary values Section instances.
        (We have to special case 'Section' instances - which are also dicts)
        
        Keys must be strings.
        Values need only be strings (or lists of strings) if
        ``main.stringify`` is set.
        
        `unrepr`` must be set when setting a value to a dictionary, without
        creating a new sub-section.
        """
        if not isinstance(key, StringTypes):
            raise ValueError('The key "%s" is not a string.' % key)
        
        # add the comment
        if not self.comments.has_key(key):
            self.comments[key] = []
            self.inline_comments[key] = ''
        # remove the entry from defaults
        if key in self.defaults:
            self.defaults.remove(key)
        #
        if isinstance(value, Section):
            if not self.has_key(key):
                self.sections.append(key)
            dict.__setitem__(self, key, value)
        elif isinstance(value, dict) and not unrepr:
            # First create the new depth level,
            # then create the section
            if not self.has_key(key):
                self.sections.append(key)
            new_depth = self.depth + 1
            dict.__setitem__(
                self,
                key,
                Section(
                    self,
                    new_depth,
                    self.main,
                    indict=value,
                    name=key))
        else:
            if not self.has_key(key):
                self.scalars.append(key)
            if not self.main.stringify:
                if isinstance(value, StringTypes):
                    pass
                elif isinstance(value, (list, tuple)):
                    for entry in value:
                        if not isinstance(entry, StringTypes):
                            raise TypeError('Value is not a string "%s".' % entry)
                else:
                    raise TypeError('Value is not a string "%s".' % value)
            dict.__setitem__(self, key, value)


    def __delitem__(self, key):
        """Remove items from the sequence when deleting."""
        dict. __delitem__(self, key)
        if key in self.scalars:
            self.scalars.remove(key)
        else:
            self.sections.remove(key)
        del self.comments[key]
        del self.inline_comments[key]


    def get(self, key, default=None):
        """A version of ``get`` that doesn't bypass string interpolation."""
        try:
            return self[key]
        except KeyError:
            return default


    def update(self, indict):
        """
        A version of update that uses our ``__setitem__``.
        """
        for entry in indict:
            self[entry] = indict[entry]


    def pop(self, key, *args):
        """
        'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
        If key is not found, d is returned if given, otherwise KeyError is raised'
        """
        val = dict.pop(self, key, *args)
        if key in self.scalars:
            del self.comments[key]
            del self.inline_comments[key]
            self.scalars.remove(key)
        elif key in self.sections:
            del self.comments[key]
            del self.inline_comments[key]
            self.sections.remove(key)
        if self.main.interpolation and isinstance(val, StringTypes):
            return self._interpolate(key, val)
        return val


    def popitem(self):
        """Pops the first (key,val)"""
        sequence = (self.scalars + self.sections)
        if not sequence:
            raise KeyError(": 'popitem(): dictionary is empty'")
        key = sequence[0]
        val =  self[key]
        del self[key]
        return key, val


    def clear(self):
        """
        A version of clear that also affects scalars/sections
        Also clears comments and configspec.
        
        Leaves other attributes alone :
            depth/main/parent are not affected
        """
        dict.clear(self)
        self.scalars = []
        self.sections = []
        self.comments = {}
        self.inline_comments = {}
        self.configspec = {}


    def setdefault(self, key, default=None):
        """A version of setdefault that sets sequence if appropriate."""
        try:
            return self[key]
        except KeyError:
            self[key] = default
            return self[key]


    def items(self):
        """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
        return zip((self.scalars + self.sections), self.values())


    def keys(self):
        """D.keys() -> list of D's keys"""
        return (self.scalars + self.sections)


    def values(self):
        """D.values() -> list of D's values"""
        return [self[key] for key in (self.scalars + self.sections)]


    def iteritems(self):
        """D.iteritems() -> an iterator over the (key, value) items of D"""
        return iter(self.items())


    def iterkeys(self):
        """D.iterkeys() -> an iterator over the keys of D"""
        return iter((self.scalars + self.sections))

    __iter__ = iterkeys


    def itervalues(self):
        """D.itervalues() -> an iterator over the values of D"""
        return iter(self.values())


    def __repr__(self):
        """x.__repr__() <==> repr(x)"""
        return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
            for key in (self.scalars + self.sections)])

    __str__ = __repr__
    __str__.__doc__ = "x.__str__() <==> str(x)"


    # Extra methods - not in a normal dictionary

    def dict(self):
        """
        Return a deepcopy of self as a dictionary.
        
        All members that are ``Section`` instances are recursively turned to
        ordinary dictionaries - by calling their ``dict`` method.
        
        >>> n = a.dict()
        >>> n == a
        1
        >>> n is a
        0
        """
        newdict = {}
        for entry in self:
            this_entry = self[entry]
            if isinstance(this_entry, Section):
                this_entry = this_entry.dict()
            elif isinstance(this_entry, list):
                # create a copy rather than a reference
                this_entry = list(this_entry)
            elif isinstance(this_entry, tuple):
                # create a copy rather than a reference
                this_entry = tuple(this_entry)
            newdict[entry] = this_entry
        return newdict


    def merge(self, indict):
        """
        A recursive update - useful for merging config files.
        
        >>> a = '''[section1]
        ...     option1 = True
        ...     [[subsection]]
        ...     more_options = False
        ...     # end of file'''.splitlines()
        >>> b = '''# File is user.ini
        ...     [section1]
        ...     option1 = False
        ...     # end of file'''.splitlines()
        >>> c1 = ConfigObj(b)
        >>> c2 = ConfigObj(a)
        >>> c2.merge(c1)
        >>> c2
        {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
        """
        for key, val in indict.items():
            if (key in self and isinstance(self[key], dict) and
                                isinstance(val, dict)):
                self[key].merge(val)
            else:   
                self[key] = val


    def rename(self, oldkey, newkey):
        """
        Change a keyname to another, without changing position in sequence.
        
        Implemented so that transformations can be made on keys,
        as well as on values. (used by encode and decode)
        
        Also renames comments.
        """
        if oldkey in self.scalars:
            the_list = self.scalars
        elif oldkey in self.sections:
            the_list = self.sections
        else:
            raise KeyError('Key "%s" not found.' % oldkey)
        pos = the_list.index(oldkey)
        #
        val = self[oldkey]
        dict.__delitem__(self, oldkey)
        dict.__setitem__(self, newkey, val)
        the_list.remove(oldkey)
        the_list.insert(pos, newkey)
        comm = self.comments[oldkey]
        inline_comment = self.inline_comments[oldkey]
        del self.comments[oldkey]
        del self.inline_comments[oldkey]
        self.comments[newkey] = comm
        self.inline_comments[newkey] = inline_comment


    def walk(self, function, raise_errors=True,
            call_on_sections=False, **keywargs):
        """
        Walk every member and call a function on the keyword and value.
        
        Return a dictionary of the return values
        
        If the function raises an exception, raise the errror
        unless ``raise_errors=False``, in which case set the return value to
        ``False``.
        
        Any unrecognised keyword arguments you pass to walk, will be pased on
        to the function you pass in.
        
        Note: if ``call_on_sections`` is ``True`` then - on encountering a
        subsection, *first* the function is called for the *whole* subsection,
        and then recurses into it's members. This means your function must be
        able to handle strings, dictionaries and lists. This allows you
        to change the key of subsections as well as for ordinary members. The
        return value when called on the whole subsection has to be discarded.
        
        See  the encode and decode methods for examples, including functions.
        
        .. caution::
        
            You can use ``walk`` to transform the names of members of a section
            but you mustn't add or delete members.
        
        >>> config = '''[XXXXsection]
        ... XXXXkey = XXXXvalue'''.splitlines()
        >>> cfg = ConfigObj(config)
        >>> cfg
        {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
        >>> def transform(section, key):
        ...     val = section[key]
        ...     newkey = key.replace('XXXX', 'CLIENT1')
        ...     section.rename(key, newkey)
        ...     if isinstance(val, (tuple, list, dict)):
        ...         pass
        ...     else:
        ...         val = val.replace('XXXX', 'CLIENT1')
        ...         section[newkey] = val
        >>> cfg.walk(transform, call_on_sections=True)
        {'CLIENT1section': {'CLIENT1key': None}}
        >>> cfg
        {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
        """
        out = {}
        # scalars first
        for i in range(len(self.scalars)):
            entry = self.scalars[i]
            try:
                val = function(self, entry, **keywargs)
                # bound again in case name has changed
                entry = self.scalars[i]
                out[entry] = val
            except Exception:
                if raise_errors:
                    raise
                else:
                    entry = self.scalars[i]
                    out[entry] = False
        # then sections
        for i in range(len(self.sections)):
            entry = self.sections[i]
            if call_on_sections:
                try:
                    function(self, entry, **keywargs)
                except Exception:
                    if raise_errors:
                        raise
                    else:
                        entry = self.sections[i]
                        out[entry] = False
                # bound again in case name has changed
                entry = self.sections[i]
            # previous result is discarded
            out[entry] = self[entry].walk(
                function,
                raise_errors=raise_errors,
                call_on_sections=call_on_sections,
                **keywargs)
        return out


    def decode(self, encoding):
        """
        Decode all strings and values to unicode, using the specified encoding.
        
        Works with subsections and list values.
        
        Uses the ``walk`` method.
        
        Testing ``encode`` and ``decode``.
        >>> m = ConfigObj(a)
        >>> m.decode('ascii')
        >>> def testuni(val):
        ...     for entry in val:
        ...         if not isinstance(entry, unicode):
        ...             print >> sys.stderr, type(entry)
        ...             raise AssertionError, 'decode failed.'
        ...         if isinstance(val[entry], dict):
        ...             testuni(val[entry])
        ...         elif not isinstance(val[entry], unicode):
        ...             raise AssertionError, 'decode failed.'
        >>> testuni(m)
        >>> m.encode('ascii')
        >>> a == m
        1
        """
        warn('use of ``decode`` is deprecated.', DeprecationWarning)
        def decode(section, key, encoding=encoding, warn=True):
            """ """
            val = section[key]
            if isinstance(val, (list, tuple)):
                newval = []
                for entry in val:
                    newval.append(entry.decode(encoding))
            elif isinstance(val, dict):
                newval = val
            else:
                newval = val.decode(encoding)
            newkey = key.decode(encoding)
            section.rename(key, newkey)
            section[newkey] = newval
        # using ``call_on_sections`` allows us to modify section names
        self.walk(decode, call_on_sections=True)


    def encode(self, encoding):
        """
        Encode all strings and values from unicode,
        using the specified encoding.
        
        Works with subsections and list values.
        Uses the ``walk`` method.
        """
        warn('use of ``encode`` is deprecated.', DeprecationWarning)
        def encode(section, key, encoding=encoding):
            """ """
            val = section[key]
            if isinstance(val, (list, tuple)):
                newval = []
                for entry in val:
                    newval.append(entry.encode(encoding))
            elif isinstance(val, dict):
                newval = val
            else:
                newval = val.encode(encoding)
            newkey = key.encode(encoding)
            section.rename(key, newkey)
            section[newkey] = newval
        self.walk(encode, call_on_sections=True)


    def istrue(self, key):
        """A deprecated version of ``as_bool``."""
        warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
                'instead.', DeprecationWarning)
        return self.as_bool(key)


    def as_bool(self, key):
        """
        Accepts a key as input. The corresponding value must be a string or
        the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
        retain compatibility with Python 2.2.
        
        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns 
        ``True``.
        
        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns 
        ``False``.
        
        ``as_bool`` is not case sensitive.
        
        Any other input will raise a ``ValueError``.
        
        >>> a = ConfigObj()
        >>> a['a'] = 'fish'
        >>> a.as_bool('a')
        Traceback (most recent call last):
        ValueError: Value "fish" is neither True nor False
        >>> a['b'] = 'True'
        >>> a.as_bool('b')
        1
        >>> a['b'] = 'off'
        >>> a.as_bool('b')
        0
        """
        val = self[key]
        if val == True:
            return True
        elif val == False:
            return False
        else:
            try:
                if not isinstance(val, StringTypes):
                    # TODO: Why do we raise a KeyError here?
                    raise KeyError()
                else:
                    return self.main._bools[val.lower()]
            except KeyError:
                raise ValueError('Value "%s" is neither True nor False' % val)


    def as_int(self, key):
        """
        A convenience method which coerces the specified value to an integer.
        
        If the value is an invalid literal for ``int``, a ``ValueError`` will
        be raised.
        
        >>> a = ConfigObj()
        >>> a['a'] = 'fish'
        >>> a.as_int('a')
        Traceback (most recent call last):
        ValueError: invalid literal for int(): fish
        >>> a['b'] = '1'
        >>> a.as_int('b')
        1
        >>> a['b'] = '3.2'
        >>> a.as_int('b')
        Traceback (most recent call last):
        ValueError: invalid literal for int(): 3.2
        """
        return int(self[key])


    def as_float(self, key):
        """
        A convenience method which coerces the specified value to a float.
        
        If the value is an invalid literal for ``float``, a ``ValueError`` will
        be raised.
        
        >>> a = ConfigObj()
        >>> a['a'] = 'fish'
        >>> a.as_float('a')
        Traceback (most recent call last):
        ValueError: invalid literal for float(): fish
        >>> a['b'] = '1'
        >>> a.as_float('b')
        1.0
        >>> a['b'] = '3.2'
        >>> a.as_float('b')
        3.2000000000000002
        """
        return float(self[key])


    def restore_default(self, key):
        """
        Restore (and return) default value for the specified key.
        
        This method will only work for a ConfigObj that was created
        with a configspec and has been validated.
        
        If there is no default value for this key, ``KeyError`` is raised.
        """
        default = self.default_values[key]
        dict.__setitem__(self, key, default)
        if key not in self.defaults:
            self.defaults.append(key)
        return default

    
    def restore_defaults(self):
        """
        Recursively restore default values to all members
        that have them.
        
        This method will only work for a ConfigObj that was created
        with a configspec and has been validated.
        
        It doesn't delete or modify entries without default values.
        """
        for key in self.default_values:
            self.restore_default(key)
            
        for section in self.sections:
            self[section].restore_defaults()


class ConfigObj(Section):
    """An object to read, create, and write config files."""

    _keyword = re.compile(r'''^ # line start
        (\s*)                   # indentation
        (                       # keyword
            (?:".*?")|          # double quotes
            (?:'.*?')|          # single quotes
            (?:[^'"=].*?)       # no quotes
        )
        \s*=\s*                 # divider
        (.*)                    # value (including list values and comments)
        $   # line end
        ''',
        re.VERBOSE)

    _sectionmarker = re.compile(r'''^
        (\s*)                     # 1: indentation
        ((?:\[\s*)+)              # 2: section marker open
        (                         # 3: section name open
            (?:"\s*\S.*?\s*")|    # at least one non-space with double quotes
            (?:'\s*\S.*?\s*')|    # at least one non-space with single quotes
            (?:[^'"\s].*?)        # at least one non-space unquoted
        )                         # section name close
        ((?:\s*\])+)              # 4: section marker close
        \s*(\#.*)?                # 5: optional comment
        $''',
        re.VERBOSE)

    # this regexp pulls list values out as a single string
    # or single values and comments
    # FIXME: this regex adds a '' to the end of comma terminated lists
    #   workaround in ``_handle_value``
    _valueexp = re.compile(r'''^
        (?:
            (?:
                (
                    (?:
                        (?:
                            (?:".*?")|              # double quotes
                            (?:'.*?')|              # single quotes
                            (?:[^'",\#][^,\#]*?)    # unquoted
                        )
                        \s*,\s*                     # comma
                    )*      # match all list items ending in a comma (if any)
                )
                (
                    (?:".*?")|                      # double quotes
                    (?:'.*?')|                      # single quotes
                    (?:[^'",\#\s][^,]*?)|           # unquoted
                    (?:(?<!,))                      # Empty value
                )?          # last item in a list - or string value
            )|
            (,)             # alternatively a single comma - empty list
        )
        \s*(\#.*)?          # optional comment
        $''',
        re.VERBOSE)

    # use findall to get the members of a list value
    _listvalueexp = re.compile(r'''
        (
            (?:".*?")|          # double quotes
            (?:'.*?')|          # single quotes
            (?:[^'",\#].*?)       # unquoted
        )
        \s*,\s*                 # comma
        ''',
        re.VERBOSE)

    # this regexp is used for the value
    # when lists are switched off
    _nolistvalue = re.compile(r'''^
        (
            (?:".*?")|          # double quotes
            (?:'.*?')|          # single quotes
            (?:[^'"\#].*?)|     # unquoted
            (?:)                # Empty value
        )
        \s*(\#.*)?              # optional comment
        $''',
        re.VERBOSE)

    # regexes for finding triple quoted values on one line
    _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
    _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
    _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
    _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')

    _triple_quote = {
        "'''": (_single_line_single, _multi_line_single),
        '"""': (_single_line_double, _multi_line_double),
    }

    # Used by the ``istrue`` Section method
    _bools = {
        'yes': True, 'no': False,
        'on': True, 'off': False,
        '1': True, '0': False,
        'true': True, 'false': False,
        }


    def __init__(self, infile=None, options=None, **kwargs):
        """
        Parse a config file or create a config file object.
        
        ``ConfigObj(infile=None, options=None, **kwargs)``
        """
        # init the superclass
        Section.__init__(self, self, 0, self)
        
        if infile is None:
            infile = []
        if options is None:
            options = {}
        else:
            options = dict(options)
            
        # keyword arguments take precedence over an options dictionary
        options.update(kwargs)
        
        defaults = OPTION_DEFAULTS.copy()
        # TODO: check the values too.
        for entry in options:
            if entry not in defaults:
                raise TypeError('Unrecognised option "%s".' % entry)
        
        # Add any explicit options to the defaults
        defaults.update(options)
        self._initialise(defaults)
        configspec = defaults['configspec']
        self._original_configspec = configspec
        self._load(infile, configspec)
        
        
    def _load(self, infile, configspec):
        if isinstance(infile, StringTypes):
            self.filename = infile
            if os.path.isfile(infile):
                h = open(infile, 'rb')
                infile = h.read() or []
                h.close()
            elif self.file_error:
                # raise an error if the file doesn't exist
                raise IOError('Config file not found: "%s".' % self.filename)
            else:
                # file doesn't already exist
                if self.create_empty:
                    # this is a good test that the filename specified
                    # isn't impossible - like on a non-existent device
                    h = open(infile, 'w')
                    h.write('')
                    h.close()
                infile = []
                
        elif isinstance(infile, (list, tuple)):
            infile = list(infile)
            
        elif isinstance(infile, dict):
            # initialise self
            # the Section class handles creating subsections
            if isinstance(infile, ConfigObj):
                # get a copy of our ConfigObj
                infile = infile.dict()
                
            for entry in infile:
                self[entry] = infile[entry]
            del self._errors
            
            if configspec is not None:
                self._handle_configspec(configspec)
            else:
                self.configspec = None
            return
        
        elif hasattr(infile, 'read'):
            # This supports file like objects
            infile = infile.read() or []
            # needs splitting into lines - but needs doing *after* decoding
            # in case it's not an 8 bit encoding
        else:
            raise TypeError('infile must be a filename, file like object, or list of lines.')
        
        if infile:
            # don't do it for the empty ConfigObj
            infile = self._handle_bom(infile)
            # infile is now *always* a list
            #
            # Set the newlines attribute (first line ending it finds)
            # and strip trailing '\n' or '\r' from lines
            for line in infile:
                if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
                    continue
                for end in ('\r\n', '\n', '\r'):
                    if line.endswith(end):
                        self.newlines = end
                        break
                break

            infile = [line.rstrip('\r\n') for line in infile]
            
        self._parse(infile)
        # if we had any errors, now is the time to raise them
        if self._errors:
            info = "at line %s." % self._errors[0].line_number
            if len(self._errors) > 1:
                msg = "Parsing failed with several errors.\nFirst error %s" % info
                error = ConfigObjError(msg)
            else:
                error = self._errors[0]
            # set the errors attribute; it's a list of tuples:
            # (error_type, message, line_number)
            error.errors = self._errors
            # set the config attribute
            error.config = self
            raise error
        # delete private attributes
        del self._errors
        
        if configspec is None:
            self.configspec = None
        else:
            self._handle_configspec(configspec)
    
    
    def _initialise(self, options=None):
        if options is None:
            options = OPTION_DEFAULTS
            
        # initialise a few variables
        self.filename = None
        self._errors = []
        self.raise_errors = options['raise_errors']
        self.interpolation = options['interpolation']
        self.list_values = options['list_values']
        self.create_empty = options['create_empty']
        self.file_error = options['file_error']
        self.stringify = options['stringify']
        self.indent_type = options['indent_type']
        self.encoding = options['encoding']
        self.default_encoding = options['default_encoding']
        self.BOM = False
        self.newlines = None
        self.write_empty_values = options['write_empty_values']
        self.unrepr = options['unrepr']
        
        self.initial_comment = []
        self.final_comment = []
        self.configspec = {}
        
        # Clear section attributes as well
        Section._initialise(self)
        
        
    def __repr__(self):
        return ('ConfigObj({%s})' % 
                ', '.join([('%s: %s' % (repr(key), repr(self[key]))) 
                for key in (self.scalars + self.sections)]))
    
    
    def _handle_bom(self, infile):
        """
        Handle any BOM, and decode if necessary.
        
        If an encoding is specified, that *must* be used - but the BOM should
        still be removed (and the BOM attribute set).
        
        (If the encoding is wrongly specified, then a BOM for an alternative
        encoding won't be discovered or removed.)
        
        If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
        removed. The BOM attribute will be set. UTF16 will be decoded to
        unicode.
        
        NOTE: This method must not be called with an empty ``infile``.
        
        Specifying the *wrong* encoding is likely to cause a
        ``UnicodeDecodeError``.
        
        ``infile`` must always be returned as a list of lines, but may be
        passed in as a single string.
        """
        if ((self.encoding is not None) and
            (self.encoding.lower() not in BOM_LIST)):
            # No need to check for a BOM
            # the encoding specified doesn't have one
            # just decode
            return self._decode(infile, self.encoding)
        
        if isinstance(infile, (list, tuple)):
            line = infile[0]
        else:
            line = infile
        if self.encoding is not None:
            # encoding explicitly supplied
            # And it could have an associated BOM
            # TODO: if encoding is just UTF16 - we ought to check for both
            # TODO: big endian and little endian versions.
            enc = BOM_LIST[self.encoding.lower()]
            if enc == 'utf_16':
                # For UTF16 we try big endian and little endian
                for BOM, (encoding, final_encoding) in BOMS.items():
                    if not final_encoding:
                        # skip UTF8
                        continue
                    if infile.startswith(BOM):
                        ### BOM discovered
                        ##self.BOM = True
                        # Don't need to remove BOM
                        return self._decode(infile, encoding)
                    
                # If we get this far, will *probably* raise a DecodeError
                # As it doesn't appear to start with a BOM
                return self._decode(infile, self.encoding)
            
            # Must be UTF8
            BOM = BOM_SET[enc]
            if not line.startswith(BOM):
                return self._decode(infile, self.encoding)
            
            newline = line[len(BOM):]
            
            # BOM removed
            if isinstance(infile, (list, tuple)):
                infile[0] = newline
            else:
                infile = newline
            self.BOM = True
            return self._decode(infile, self.encoding)
        
        # No encoding specified - so we need to check for UTF8/UTF16
        for BOM, (encoding, final_encoding) in BOMS.items():
            if not line.startswith(BOM):
                continue
            else:
                # BOM discovered
                self.encoding = final_encoding
                if not final_encoding:
                    self.BOM = True
                    # UTF8
                    # remove BOM
                    newline = line[len(BOM):]
                    if isinstance(infile, (list, tuple)):
                        infile[0] = newline
                    else:
                        infile = newline
                    # UTF8 - don't decode
                    if isinstance(infile, StringTypes):
                        return infile.splitlines(True)
                    else:
                        return infile
                # UTF16 - have to decode
                return self._decode(infile, encoding)
            
        # No BOM discovered and no encoding specified, just return
        if isinstance(infile, StringTypes):
            # infile read from a file will be a single string
            return infile.splitlines(True)
        return infile


    def _a_to_u(self, aString):
        """Decode ASCII strings to unicode if a self.encoding is specified."""
        if self.encoding:
            return aString.decode('ascii')
        else:
            return aString


    def _decode(self, infile, encoding):
        """
        Decode infile to unicode. Using the specified encoding.
        
        if is a string, it also needs converting to a list.
        """
        if isinstance(infile, StringTypes):
            # can't be unicode
            # NOTE: Could raise a ``UnicodeDecodeError``
            return infile.decode(encoding).splitlines(True)
        for i, line in enumerate(infile):
            if not isinstance(line, unicode):
                # NOTE: The isinstance test here handles mixed lists of unicode/string
                # NOTE: But the decode will break on any non-string values
                # NOTE: Or could raise a ``UnicodeDecodeError``
                infile[i] = line.decode(encoding)
        return infile


    def _decode_element(self, line):
        """Decode element to unicode if necessary."""
        if not self.encoding:
            return line
        if isinstance(line, str) and self.default_encoding:
            return line.decode(self.default_encoding)
        return line


    def _str(self, value):
        """
        Used by ``stringify`` within validate, to turn non-string values
        into strings.
        """
        if not isinstance(value, StringTypes):
            return str(value)
        else:
            return value


    def _parse(self, infile):
        """Actually parse the config file."""
        temp_list_values = self.list_values
        if self.unrepr:
            self.list_values = False
            
        comment_list = []
        done_start = False
        this_section = self
        maxline = len(infile) - 1
        cur_index = -1
        reset_comment = False
        
        while cur_index < maxline:
            if reset_comment:
                comment_list = []
            cur_index += 1
            line = infile[cur_index]
            sline = line.strip()
            # do we have anything on the line ?
            if not sline or sline.startswith('#'):
                reset_comment = False
                comment_list.append(line)
                continue
            
            if not done_start:
                # preserve initial comment
                self.initial_comment = comment_list
                comment_list = []
                done_start = True
                
            reset_comment = True
            # first we check if it's a section marker
            mat = self._sectionmarker.match(line)
            if mat is not None:
                # is a section line
                (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
                if indent and (self.indent_type is None):
                    self.indent_type = indent
                cur_depth = sect_open.count('[')
                if cur_depth != sect_close.count(']'):
                    self._handle_error("Cannot compute the section depth at line %s.",
                                       NestingError, infile, cur_index)
                    continue
                
                if cur_depth < this_section.depth:
                    # the new section is dropping back to a previous level
                    try:
                        parent = self._match_depth(this_section,
                                                   cur_depth).parent
                    except SyntaxError:
                        self._handle_error("Cannot compute nesting level at line %s.",
                                           NestingError, infile, cur_index)
                        continue
                elif cur_depth == this_section.depth:
                    # the new section is a sibling of the current section
                    parent = this_section.parent
                elif cur_depth == this_section.depth + 1:
                    # the new section is a child the current section
                    parent = this_section
                else:
                    self._handle_error("Section too nested at line %s.",
                                       NestingError, infile, cur_index)
                    
                sect_name = self._unquote(sect_name)
                if parent.has_key(sect_name):
                    self._handle_error('Duplicate section name at line %s.',
                                       DuplicateError, infile, cur_index)
                    continue
                
                # create the new section
                this_section = Section(
                    parent,
                    cur_depth,
                    self,
                    name=sect_name)
                parent[sect_name] = this_section
                parent.inline_comments[sect_name] = comment
                parent.comments[sect_name] = comment_list
                continue
            #
            # it's not a section marker,
            # so it should be a valid ``key = value`` line
            mat = self._keyword.match(line)
            if mat is None:
                # it neither matched as a keyword
                # or a section marker
                self._handle_error(
                    'Invalid line at line "%s".',
                    ParseError, infile, cur_index)
            else:
                # is a keyword value
                # value will include any inline comment
                (indent, key, value) = mat.groups()
                if indent and (self.indent_type is None):
                    self.indent_type = indent
                # check for a multiline value
                if value[:3] in ['"""', "'''"]:
                    try:
                        (value, comment, cur_index) = self._multiline(
                            value, infile, cur_index, maxline)
                    except SyntaxError:
                        self._handle_error(
                            'Parse error in value at line %s.',
                            ParseError, infile, cur_index)
                        continue
                    else:
                        if self.unrepr:
                            comment = ''
                            try:
                                value = unrepr(value)
                            except Exception, e:
                                if type(e) == UnknownType:
                                    msg = 'Unknown name or type in value at line %s.'
                                else:
                                    msg = 'Parse error in value at line %s.'
                                self._handle_error(msg, UnreprError, infile,
                                    cur_index)
                                continue
                else:
                    if self.unrepr:
                        comment = ''
                        try:
                            value = unrepr(value)
                        except Exception, e:
                            if isinstance(e, UnknownType):
                                msg = 'Unknown name or type in value at line %s.'
                            else:
                                msg = 'Parse error in value at line %s.'
                            self._handle_error(msg, UnreprError, infile,
                                cur_index)
                            continue
                    else:
                        # extract comment and lists
                        try:
                            (value, comment) = self._handle_value(value)
                        except SyntaxError:
                            self._handle_error(
                                'Parse error in value at line %s.',
                                ParseError, infile, cur_index)
                            continue
                #
                key = self._unquote(key)
                if this_section.has_key(key):
                    self._handle_error(
                        'Duplicate keyword name at line %s.',
                        DuplicateError, infile, cur_index)
                    continue
                # add the key.
                # we set unrepr because if we have got this far we will never
                # be creating a new section
                this_section.__setitem__(key, value, unrepr=True)
                this_section.inline_comments[key] = comment
                this_section.comments[key] = comment_list
                continue
        #
        if self.indent_type is None:
            # no indentation used, set the type accordingly
            self.indent_type = ''

        # preserve the final comment
        if not self and not self.initial_comment:
            self.initial_comment = comment_list
        elif not reset_comment:
            self.final_comment = comment_list
        self.list_values = temp_list_values


    def _match_depth(self, sect, depth):
        """
        Given a section and a depth level, walk back through the sections
        parents to see if the depth level matches a previous section.
        
        Return a reference to the right section,
        or raise a SyntaxError.
        """
        while depth < sect.depth:
            if sect is sect.parent:
                # we've reached the top level already
                raise SyntaxError()
            sect = sect.parent
        if sect.depth == depth:
            return sect
        # shouldn't get here
        raise SyntaxError()


    def _handle_error(self, text, ErrorClass, infile, cur_index):
        """
        Handle an error according to the error settings.
        
        Either raise the error or store it.
        The error will have occured at ``cur_index``
        """
        line = infile[cur_index]
        cur_index += 1
        message = text % cur_index
        error = ErrorClass(message, cur_index, line)
        if self.raise_errors:
            # raise the error - parsing stops here
            raise error
        # store the error
        # reraise when parsing has finished
        self._errors.append(error)


    def _unquote(self, value):
        """Return an unquoted version of a value"""
        if (value[0] == value[-1]) and (value[0] in ('"', "'")):
            value = value[1:-1]
        return value


    def _quote(self, value, multiline=True):
        """
        Return a safely quoted version of a value.
        
        Raise a ConfigObjError if the value cannot be safely quoted.
        If multiline is ``True`` (default) then use triple quotes
        if necessary.
        
        Don't quote values that don't need it.
        Recursively quote members of a list and return a comma joined list.
        Multiline is ``False`` for lists.
        Obey list syntax for empty and single member lists.
        
        If ``list_values=False`` then the value is only quoted if it contains
        a ``\n`` (is multiline) or '#'.
        
        If ``write_empty_values`` is set, and the value is an empty string, it
        won't be quoted.
        """
        if multiline and self.write_empty_values and value == '':
            # Only if multiline is set, so that it is used for values not
            # keys, and not values that are part of a list
            return ''
        
        if multiline and isinstance(value, (list, tuple)):
            if not value:
                return ','
            elif len(value) == 1:
                return self._quote(value[0], multiline=False) + ','
            return ', '.join([self._quote(val, multiline=False)
                for val in value])
        if not isinstance(value, StringTypes):
            if self.stringify:
                value = str(value)
            else:
                raise TypeError('Value "%s" is not a string.' % value)

        if not value:
            return '""'
        
        no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
        need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
        hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
        check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
        
        if check_for_single:
            if not self.list_values:
                # we don't quote if ``list_values=False``
                quot = noquot
            # for normal values either single or double quotes will do
            elif '\n' in value:
                # will only happen if multiline is off - e.g. '\n' in key
                raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
            elif ((value[0] not in wspace_plus) and
                    (value[-1] not in wspace_plus) and
                    (',' not in value)):
                quot = noquot
            else:
                quot = self._get_single_quote(value)
        else:
            # if value has '\n' or "'" *and* '"', it will need triple quotes
            quot = self._get_triple_quote(value)
        
        if quot == noquot and '#' in value and self.list_values:
            quot = self._get_single_quote(value)
                
        return quot % value
    
    
    def _get_single_quote(self, value):
        if ("'" in value) and ('"' in value):
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
        elif '"' in value:
            quot = squot
        else:
            quot = dquot
        return quot
    
    
    def _get_triple_quote(self, value):
        if (value.find('"""') != -1) and (value.find("'''") != -1):
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
        if value.find('"""') == -1:
            quot = tdquot
        else:
            quot = tsquot 
        return quot


    def _handle_value(self, value):
        """
        Given a value string, unquote, remove comment,
        handle lists. (including empty and single member lists)
        """
        # do we look for lists in values ?
        if not self.list_values:
            mat = self._nolistvalue.match(value)
            if mat is None:
                raise SyntaxError()
            # NOTE: we don't unquote here
            return mat.groups()
        #
        mat = self._valueexp.match(value)
        if mat is None:
            # the value is badly constructed, probably badly quoted,
            # or an invalid list
            raise SyntaxError()
        (list_values, single, empty_list, comment) = mat.groups()
        if (list_values == '') and (single is None):
            # change this if you want to accept empty values
            raise SyntaxError()
        # NOTE: note there is no error handling from here if the regex
        # is wrong: then incorrect values will slip through
        if empty_list is not None:
            # the single comma - meaning an empty list
            return ([], comment)
        if single is not None:
            # handle empty values
            if list_values and not single:
                # FIXME: the '' is a workaround because our regex now matches
                #   '' at the end of a list if it has a trailing comma
                single = None
            else:
                single = single or '""'
                single = self._unquote(single)
        if list_values == '':
            # not a list value
            return (single, comment)
        the_list = self._listvalueexp.findall(list_values)
        the_list = [self._unquote(val) for val in the_list]
        if single is not None:
            the_list += [single]
        return (the_list, comment)


    def _multiline(self, value, infile, cur_index, maxline):
        """Extract the value, where we are in a multiline situation."""
        quot = value[:3]
        newvalue = value[3:]
        single_line = self._triple_quote[quot][0]
        multi_line = self._triple_quote[quot][1]
        mat = single_line.match(value)
        if mat is not None:
            retval = list(mat.groups())
            retval.append(cur_index)
            return retval
        elif newvalue.find(quot) != -1:
            # somehow the triple quote is missing
            raise SyntaxError()
        #
        while cur_index < maxline:
            cur_index += 1
            newvalue += '\n'
            line = infile[cur_index]
            if line.find(quot) == -1:
                newvalue += line
            else:
                # end of multiline, process it
                break
        else:
            # we've got to the end of the config, oops...
            raise SyntaxError()
        mat = multi_line.match(line)
        if mat is None:
            # a badly formed line
            raise SyntaxError()
        (value, comment) = mat.groups()
        return (newvalue + value, comment, cur_index)


    def _handle_configspec(self, configspec):
        """Parse the configspec."""
        # FIXME: Should we check that the configspec was created with the 
        #        correct settings ? (i.e. ``list_values=False``)
        if not isinstance(configspec, ConfigObj):
            try:
                configspec = ConfigObj(configspec,
                                       raise_errors=True,
                                       file_error=True,
                                       list_values=False)
            except ConfigObjError, e:
                # FIXME: Should these errors have a reference
                #        to the already parsed ConfigObj ?
                raise ConfigspecError('Parsing configspec failed: %s' % e)
            except IOError, e:
                raise IOError('Reading configspec failed: %s' % e)
        
        self._set_configspec_value(configspec, self)


    def _set_configspec_value(self, configspec, section):
        """Used to recursively set configspec values."""
        if '__many__' in configspec.sections:
            section.configspec['__many__'] = configspec['__many__']
            if len(configspec.sections) > 1:
                # FIXME: can we supply any useful information here ?
                raise RepeatSectionError()
            
        if hasattr(configspec, 'initial_comment'):
            section._configspec_initial_comment = configspec.initial_comment
            section._configspec_final_comment = configspec.final_comment
            section._configspec_encoding = configspec.encoding
            section._configspec_BOM = configspec.BOM
            section._configspec_newlines = configspec.newlines
            section._configspec_indent_type = configspec.indent_type
            
        for entry in configspec.scalars:
            section._configspec_comments[entry] = configspec.comments[entry]
            section._configspec_inline_comments[entry] = configspec.inline_comments[entry]
            section.configspec[entry] = configspec[entry]
            section._order.append(entry)
            
        for entry in configspec.sections:
            if entry == '__many__':
                continue
            
            section._cs_section_comments[entry] = configspec.comments[entry]
            section._cs_section_inline_comments[entry] = configspec.inline_comments[entry]
            if not section.has_key(entry):
                section[entry] = {}
            self._set_configspec_value(configspec[entry], section[entry])


    def _handle_repeat(self, section, configspec):
        """Dynamically assign configspec for repeated section."""
        try:
            section_keys = configspec.sections
            scalar_keys = configspec.scalars
        except AttributeError:
            section_keys = [entry for entry in configspec 
                                if isinstance(configspec[entry], dict)]
            scalar_keys = [entry for entry in configspec 
                                if not isinstance(configspec[entry], dict)]
            
        if '__many__' in section_keys and len(section_keys) > 1:
            # FIXME: can we supply any useful information here ?
            raise RepeatSectionError()
        
        scalars = {}
        sections = {}
        for entry in scalar_keys:
            val = configspec[entry]
            scalars[entry] = val
        for entry in section_keys:
            val = configspec[entry]
            if entry == '__many__':
                scalars[entry] = val
                continue
            sections[entry] = val
            
        section.configspec = scalars
        for entry in sections:
            if not section.has_key(entry):
                section[entry] = {}
            self._handle_repeat(section[entry], sections[entry])


    def _write_line(self, indent_string, entry, this_entry, comment):
        """Write an individual line, for the write method"""
        # NOTE: the calls to self._quote here handles non-StringType values.
        if not self.unrepr:
            val = self._decode_element(self._quote(this_entry))
        else:
            val = repr(this_entry)
        return '%s%s%s%s%s' % (indent_string,
                               self._decode_element(self._quote(entry, multiline=False)),
                               self._a_to_u(' = '),
                               val,
                               self._decode_element(comment))


    def _write_marker(self, indent_string, depth, entry, comment):
        """Write a section marker line"""
        return '%s%s%s%s%s' % (indent_string,
                               self._a_to_u('[' * depth),
                               self._quote(self._decode_element(entry), multiline=False),
                               self._a_to_u(']' * depth),
                               self._decode_element(comment))


    def _handle_comment(self, comment):
        """Deal with a comment."""
        if not comment:
            return ''
        start = self.indent_type
        if not comment.startswith('#'):
            start += self._a_to_u(' # ')
        return (start + comment)


    # Public methods

    def write(self, outfile=None, section=None):
        """
        Write the current ConfigObj as a file
        
        tekNico: FIXME: use StringIO instead of real files
        
        >>> filename = a.filename
        >>> a.filename = 'test.ini'
        >>> a.write()
        >>> a.filename = filename
        >>> a == ConfigObj('test.ini', raise_errors=True)
        1
        """
        if self.indent_type is None:
            # this can be true if initialised from a dictionary
            self.indent_type = DEFAULT_INDENT_TYPE
            
        out = []
        cs = self._a_to_u('#')
        csp = self._a_to_u('# ')
        if section is None:
            int_val = self.interpolation
            self.interpolation = False
            section = self
            for line in self.initial_comment:
                line = self._decode_element(line)
                stripped_line = line.strip()
                if stripped_line and not stripped_line.startswith(cs):
                    line = csp + line
                out.append(line)
                
        indent_string = self.indent_type * section.depth
        for entry in (section.scalars + section.sections):
            if entry in section.defaults:
                # don't write out default values
                continue
            for comment_line in section.comments[entry]:
                comment_line = self._decode_element(comment_line.lstrip())
                if comment_line and not comment_line.startswith(cs):
                    comment_line = csp + comment_line
                out.append(indent_string + comment_line)
            this_entry = section[entry]
            comment = self._handle_comment(section.inline_comments[entry])
            
            if isinstance(this_entry, dict):
                # a section
                out.append(self._write_marker(
                    indent_string,
                    this_entry.depth,
                    entry,
                    comment))
                out.extend(self.write(section=this_entry))
            else:
                out.append(self._write_line(
                    indent_string,
                    entry,
                    this_entry,
                    comment))
                
        if section is self:
            for line in self.final_comment:
                line = self._decode_element(line)
                stripped_line = line.strip()
                if stripped_line and not stripped_line.startswith(cs):
                    line = csp + line
                out.append(line)
            self.interpolation = int_val
            
        if section is not self:
            return out
        
        if (self.filename is None) and (outfile is None):
            # output a list of lines
            # might need to encode
            # NOTE: This will *screw* UTF16, each line will start with the BOM
            if self.encoding:
                out = [l.encode(self.encoding) for l in out]
            if (self.BOM and ((self.encoding is None) or
                (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
                # Add the UTF8 BOM
                if not out:
                    out.append('')
                out[0] = BOM_UTF8 + out[0]
            return out
        
        # Turn the list to a string, joined with correct newlines
        newline = self.newlines or os.linesep
        output = self._a_to_u(newline).join(out)
        if self.encoding:
            output = output.encode(self.encoding)
        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
            # Add the UTF8 BOM
            output = BOM_UTF8 + output
            
        if not output.endswith(newline):
            output += newline
        if outfile is not None:
            outfile.write(output)
        else:
            h = open(self.filename, 'wb')
            h.write(output)
            h.close()


    def validate(self, validator, preserve_errors=False, copy=False,
                 section=None):
        """
        Test the ConfigObj against a configspec.
        
        It uses the ``validator`` object from *validate.py*.
        
        To run ``validate`` on the current ConfigObj, call: ::
        
            test = config.validate(validator)
        
        (Normally having previously passed in the configspec when the ConfigObj
        was created - you can dynamically assign a dictionary of checks to the
        ``configspec`` attribute of a section though).
        
        It returns ``True`` if everything passes, or a dictionary of
        pass/fails (True/False). If every member of a subsection passes, it
        will just have the value ``True``. (It also returns ``False`` if all
        members fail).
        
        In addition, it converts the values from strings to their native
        types if their checks pass (and ``stringify`` is set).
        
        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
        of a marking a fail with a ``False``, it will preserve the actual
        exception object. This can contain info about the reason for failure.
        For example the ``VdtValueTooSmallError`` indicates that the value
        supplied was too small. If a value (or section) is missing it will
        still be marked as ``False``.
        
        You must have the validate module to use ``preserve_errors=True``.
        
        You can then use the ``flatten_errors`` function to turn your nested
        results dictionary into a flattened list of failures - useful for
        displaying meaningful error messages.
        """
        if section is None:
            if self.configspec is None:
                raise ValueError('No configspec supplied.')
            if preserve_errors:
                # We do this once to remove a top level dependency on the validate module
                # Which makes importing configobj faster
                from validate import VdtMissingValue
                self._vdtMissingValue = VdtMissingValue
            section = self
        #
        spec_section = section.configspec
        if copy and hasattr(section, '_configspec_initial_comment'):
            section.initial_comment = section._configspec_initial_comment
            section.final_comment = section._configspec_final_comment
            section.encoding = section._configspec_encoding
            section.BOM = section._configspec_BOM
            section.newlines = section._configspec_newlines
            section.indent_type = section._configspec_indent_type
            
        if '__many__' in section.configspec:
            many = spec_section['__many__']
            # dynamically assign the configspecs
            # for the sections below
            for entry in section.sections:
                self._handle_repeat(section[entry], many)
        #
        out = {}
        ret_true = True
        ret_false = True
        order = [k for k in section._order if k in spec_section]
        order += [k for k in spec_section if k not in order]
        for entry in order:
            if entry == '__many__':
                continue
            if (not entry in section.scalars) or (entry in section.defaults):
                # missing entries
                # or entries from defaults
                missing = True
                val = None
                if copy and not entry in section.scalars:
                    # copy comments
                    section.comments[entry] = (
                        section._configspec_comments.get(entry, []))
                    section.inline_comments[entry] = (
                        section._configspec_inline_comments.get(entry, ''))
                #
            else:
                missing = False
                val = section[entry]
            try:
                check = validator.check(spec_section[entry],
                                        val,
                                        missing=missing
                                        )
            except validator.baseErrorClass, e:
                if not preserve_errors or isinstance(e, self._vdtMissingValue):
                    out[entry] = False
                else:
                    # preserve the error
                    out[entry] = e
                    ret_false = False
                ret_true = False
            else:
                try: 
                    section.default_values.pop(entry, None)
                except AttributeError: 
                    # For Python 2.2 compatibility
                    try:
                        del section.default_values[entry]
                    except KeyError:
                        pass
                    
                if hasattr(validator, 'get_default_value'):
                    try: 
                        section.default_values[entry] = validator.get_default_value(spec_section[entry])
                    except KeyError:
                        # No default
                        pass
                    
                ret_false = False
                out[entry] = True
                if self.stringify or missing:
                    # if we are doing type conversion
                    # or the value is a supplied default
                    if not self.stringify:
                        if isinstance(check, (list, tuple)):
                            # preserve lists
                            check = [self._str(item) for item in check]
                        elif missing and check is None:
                            # convert the None from a default to a ''
                            check = ''
                        else:
                            check = self._str(check)
                    if (check != val) or missing:
                        section[entry] = check
                if not copy and missing and entry not in section.defaults:
                    section.defaults.append(entry)
        # Missing sections will have been created as empty ones when the
        # configspec was read.
        for entry in section.sections:
            # FIXME: this means DEFAULT is not copied in copy mode
            if section is self and entry == 'DEFAULT':
                continue
            if copy:
                section.comments[entry] = section._cs_section_comments.get(entry, [])
                section.inline_comments[entry] = section._cs_section_inline_comments.get(entry, '')
            check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
            out[entry] = check
            if check == False:
                ret_true = False
            elif check == True:
                ret_false = False
            else:
                ret_true = False
                ret_false = False
        #
        if ret_true:
            return True
        elif ret_false:
            return False
        return out


    def reset(self):
        """Clear ConfigObj instance and restore to 'freshly created' state."""
        self.clear()
        self._initialise()
        # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
        #        requires an empty dictionary
        self.configspec = None
        # Just to be sure ;-)
        self._original_configspec = None
        
        
    def reload(self):
        """
        Reload a ConfigObj from file.
        
        This method raises a ``ReloadError`` if the ConfigObj doesn't have
        a filename attribute pointing to a file.
        """
        if not isinstance(self.filename, StringTypes):
            raise ReloadError()

        filename = self.filename
        current_options = {}
        for entry in OPTION_DEFAULTS:
            if entry == 'configspec':
                continue
            current_options[entry] = getattr(self, entry)
            
        configspec = self._original_configspec
        current_options['configspec'] = configspec
            
        self.clear()
        self._initialise(current_options)
        self._load(filename, configspec)
        


class SimpleVal(object):
    """
    A simple validator.
    Can be used to check that all members expected are present.
    
    To use it, provide a configspec with all your members in (the value given
    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
    members are present, or a dictionary with True/False meaning
    present/missing. (Whole missing sections will be replaced with ``False``)
    """
    
    def __init__(self):
        self.baseErrorClass = ConfigObjError
    
    def check(self, check, member, missing=False):
        """A dummy check method, always returns the value unchanged."""
        if missing:
            raise self.baseErrorClass()
        return member


# Check / processing functions for options
def flatten_errors(cfg, res, levels=None, results=None):
    """
    An example function that will turn a nested dictionary of results
    (as returned by ``ConfigObj.validate``) into a flat list.
    
    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
    dictionary returned by ``validate``.
    
    (This is a recursive function, so you shouldn't use the ``levels`` or
    ``results`` arguments - they are used by the function.
    
    Returns a list of keys that failed. Each member of the list is a tuple :
    ::
    
        ([list of sections...], key, result)
    
    If ``validate`` was called with ``preserve_errors=False`` (the default)
    then ``result`` will always be ``False``.

    *list of sections* is a flattened list of sections that the key was found
    in.
    
    If the section was missing then key will be ``None``.
    
    If the value (or section) was missing then ``result`` will be ``False``.
    
    If ``validate`` was called with ``preserve_errors=True`` and a value
    was present, but failed the check, then ``result`` will be the exception
    object returned. You can use this as a string that describes the failure.
    
    For example *The value "3" is of the wrong type*.
    
    >>> import validate
    >>> vtor = validate.Validator()
    >>> my_ini = '''
    ...     option1 = True
    ...     [section1]
    ...     option1 = True
    ...     [section2]
    ...     another_option = Probably
    ...     [section3]
    ...     another_option = True
    ...     [[section3b]]
    ...     value = 3
    ...     value2 = a
    ...     value3 = 11
    ...     '''
    >>> my_cfg = '''
    ...     option1 = boolean()
    ...     option2 = boolean()
    ...     option3 = boolean(default=Bad_value)
    ...     [section1]
    ...     option1 = boolean()
    ...     option2 = boolean()
    ...     option3 = boolean(default=Bad_value)
    ...     [section2]
    ...     another_option = boolean()
    ...     [section3]
    ...     another_option = boolean()
    ...     [[section3b]]
    ...     value = integer
    ...     value2 = integer
    ...     value3 = integer(0, 10)
    ...         [[[section3b-sub]]]
    ...         value = string
    ...     [section4]
    ...     another_option = boolean()
    ...     '''
    >>> cs = my_cfg.split('\\n')
    >>> ini = my_ini.split('\\n')
    >>> cfg = ConfigObj(ini, configspec=cs)
    >>> res = cfg.validate(vtor, preserve_errors=True)
    >>> errors = []
    >>> for entry in flatten_errors(cfg, res):
    ...     section_list, key, error = entry
    ...     section_list.insert(0, '[root]')
    ...     if key is not None:
    ...        section_list.append(key)
    ...     else:
    ...         section_list.append('[missing]')
    ...     section_string = ', '.join(section_list)
    ...     errors.append((section_string, ' = ', error))
    >>> errors.sort()
    >>> for entry in errors:
    ...     print entry[0], entry[1], (entry[2] or 0)
    [root], option2  =  0
    [root], option3  =  the value "Bad_value" is of the wrong type.
    [root], section1, option2  =  0
    [root], section1, option3  =  the value "Bad_value" is of the wrong type.
    [root], section2, another_option  =  the value "Probably" is of the wrong type.
    [root], section3, section3b, section3b-sub, [missing]  =  0
    [root], section3, section3b, value2  =  the value "a" is of the wrong type.
    [root], section3, section3b, value3  =  the value "11" is too big.
    [root], section4, [missing]  =  0
    """
    if levels is None:
        # first time called
        levels = []
        results = []
    if res is True:
        return results
    if res is False:
        results.append((levels[:], None, False))
        if levels:
            levels.pop()
        return results
    for (key, val) in res.items():
        if val == True:
            continue
        if isinstance(cfg.get(key), dict):
            # Go down one level
            levels.append(key)
            flatten_errors(cfg[key], val, levels, results)
            continue
        results.append((levels[:], key, val))
    #
    # Go up one level
    if levels:
        levels.pop()
    #
    return results


"""*A programming language is a medium of expression.* - Paul Graham"""
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2017 Kim Gerdes
# kim AT gerdes. fr
# http://arborator.ilpga.fr/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 3 of the GNU Affero General Public License (the "License")
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of of version 3 of the GNU Affero General Public License
# from http://www.gnu.org/licenses/agpl-3.0.html 
####

import codecs, collections, re
#debug=False
debug=True


class Tree(dict):
	"""
	just a dictionary that maps nodenumber->{"t":"qsdf", ...}
	moreover: 
		sentencefeatures is a dictionary with sentence wide information, eg "comments":"comment line content"
			there is a special key: _comments used for comments that are not of the form x = yyy, they are stored as such
		words is not necessarily a list of tokens: it contains the actual correctly spelled words, ie. the hyphen (1-2) lines
	"""
	def __init__(self, *args, **kwargs):
		self.update(*args, **kwargs)
		self.sentencefeatures={}
		self.words=[]

	def __getitem__(self, key):
		val = dict.__getitem__(self, key)
		#print 'GET', key
		return val

	def __setitem__(self, key, val):
		#print 'SET', key, val
		dict.__setitem__(self, key, val)

	def __repr__(self):
		#dictrepr = dict.__repr__(self)
		#return '%s(%s)' % (type(self).__name__, dictrepr)
		return u"\n".join([u"Tree: "+self.sentence(),self.conllu()])
		#[f+": "+v for f,v in self.sentencefeatures.iteritems()]+[str(i)+u": "+self[i].get("t",u"")+"\t"+str(self[i]) for i in self]).encode("utf-8")

	def update(self, *args, **kwargs):
		#print 'update', args, kwargs
		for k, v in dict(*args, **kwargs).iteritems():
			self[k] = v
	
	def sentence(self):
		if self.words==[]:
			self.words = [self[i].get("t","") for i in sorted(self)]
		return u" ".join(self.words)
	
	def conllu(self):
		treestring = ""
		for stftkey in sorted(self.sentencefeatures):
			if stftkey=="_comments":
				treestring+="# "+self.sentencefeatures[stftkey]
			else:
				treestring+="# "+stftkey+" = "+self.sentencefeatures[stftkey]+"\n"
		for i in sorted(self.keys()):
			node = self[i]                        
			govs=node.get("gov",{})
			govk = sorted(govs.keys())
			if govk:
				gk,gv = str(govk[0]),govs.get(govk[0],"_")
			else:
				gk,gv = "_","_"
			treestring+="\t".join([
				str(i), 
				node.get("t","_"), 
				node.get("lemma",""), 
				node.get("tag","_"), 
				node.get("tag2","_"),
				"|".join( [ a+"="+v for a,v in node.iteritems() if a not in ["t","lemma","lemma2","tag","tag2","xpos","egov","misc","id","index","gov"] and v!='_']) or "_", 
				gk,
				gv,
				"|".join( [ str(g)+":"+govs.get(g,"_") for g in govk[1:] ]) or "_", 
				node.get("misc","_")]) + "\n"
		return treestring

def update(d, u):
	for k, v in u.iteritems():
		if isinstance(v, collections.Mapping):
			r = update(d.get(k, {}), v)
			d[k] = r
		else:
			d[k] = u[k]
	return d


def conll2tree(conllstring):
	""" 
	takes the conll string (or malt) representation of a single tree and creates a Tree (dictionary) for it
	"""
	tree=Tree()
	nr=1
	skipuntil=0 # only used to get the right "words" sequence, doesn't touch the actual tokens
	for line in conllstring.split('\n'):
		#print line
		if line.strip():
			if line.strip()[0]=="#": # comment of conllu
				if "=" in line:
					tree.sentencefeatures[line.split("=")[0].strip()[1:].strip()]="=".join(line.split("=")[1:]).strip()
				else:
					tree.sentencefeatures["_comments"]=tree.sentencefeatures.get("_comments","")+line.strip()[1:]+"\n"
				continue
			
			cells = line.split('\t')
			nrCells = len(cells)
			
			if nrCells in [4,10,12,14]:
				
				if nrCells == 4: # malt!
					t, tag, head, rel = cells
					if head=="_": head=-1
					else:head = int(head)
					newf={'id':nr,'t': t, 'tag': tag,'gov':{head: rel}}
					tree[nr]=update(tree.get(nr,{}), newf)
					nr+=1

				elif nrCells == 10: # standard conll 10 or conllu
					nr, t, lemma , tag, xpos, features, head, rel, edeps, misc = cells
					if "-" in nr: 
						try:	skipuntil=int(nr.split("-")[-1])
						except:	skipuntil=float(nr.split("-")[-1])
						tree.words+=[t]
						continue
					try:	nr = int(nr)
					except:	nr = float(nr) # handling the 3.1 format for "emtpy nodes"
					if head.strip()=="_": head=-1
					else:
						try:	head = int(head)
						except:	head = float(head)
					egov={}
					if ":" in edeps: # the enhanced graph is used
						egov=dict([(gf.split(":")[0],gf.split(":")[-1]) for gf in edeps.split("|")])					
					
					newf={'id':nr,'t': t,'lemma': lemma, 'tag': tag, 'xpos': xpos, 'gov':{head: rel}, 'egov':egov, 'misc': misc}
					if "=" in features:
						mf=dict([(av.split("=")[0],av.split("=")[-1]) for av in features.split("|")])
						newf=update(mf,newf)
					
					tree[nr]=update(tree.get(nr,{}), newf)
					if nr>skipuntil: tree.words+=[t]
				
				elif nrCells == 12: # naija
					nr, t, lemma , tag, xpos, features, head, rel, edeps, misc, startali, endali = cells
					if "-" in nr: 
						try:	skipuntil=int(nr.split("-")[-1])
						except:	skipuntil=float(nr.split("-")[-1])
						tree.words+=[t]
						continue
					try:	nr = int(nr)
					except:	nr = float(nr) # handling the 3.1 format for "emtpy nodes"
					if head.strip()=="_": head=-1
					else:
						try:	head = int(head)
						except:	head = float(head)
					egov={}
					if ":" in edeps: # the enhanced graph is used
						egov=dict([(gf.split(":")[0],gf.split(":")[-1]) for gf in edeps.split("|")])					
					
					newf={'id':nr,'t': t,'lemma': lemma, 'tag': tag, 'xpos': xpos, 'gov':{head: rel}, 'egov':egov, 'misc': misc, 'startali':startali, 'endali':endali}
					if "=" in features:
						mf=dict([(av.split("=")[0],av.split("=")[-1]) for av in features.split("|")])
						newf=update(mf,newf)
					
					tree[nr]=update(tree.get(nr,{}), newf)
					if nr>skipuntil: tree.words+=[t]
				
				elif nrCells == 14:
					#mate:
					#6, inscriptions, _, inscription, _, N, _, pl|masc, -1, 4, _, dep, _, _
					nr, t, lemma, lemma2, tag, xpos, morph, morph2, head, head2, rel, rel2, _, _ = cells
					nr = int(nr)
					if head.strip()=="_": head=-1
					else:head = int(head)
					if head2.strip()=="_": head2=-1
					else:head2 = int(head2)
					if lemma=="_" and lemma2!="_":lemma=lemma2
					if tag=="_" and xpos!="_":tag=xpos
					if morph=="_" and morph2!="_":morph=morph2
					if rel=="_" and rel2!="_":
						rel=rel2
						head=head2
					newf={'id':nr,'t': t,'lemma': lemma,'lemma2': lemma2, 'tag': tag, 'xpos': xpos, 'morph': morph, 'morph2': morph2, 'gov':{head: rel}, 'egov':{head2: rel2} }
					tree[nr]=update(tree.get(nr,{}), newf)
					
			elif debug:
				print "strange conll:",nrCells,"columns!",line
	
	return tree


def conllFile2trees(path, encoding="utf-8"):
	"""
	file with path -> list of trees
	
	important function!	
	called from enterConll, treebankfiles, and uploadConll in treebankfiles.cgi
	
	"""
	trees=[]
	with codecs.open(path,"r",encoding) as f:
		conlltext=""
		for li in f:
			li=li.strip()
			if li: 	conlltext+=li+"\n"
			else: # emptyline, sentence is finished
				tree=conll2tree(conlltext)
				trees+=[tree]
				del tree
				conlltext=""
		f.close()
		if conlltext.strip(): # last tree may not be followed by empty line
			tree=conll2tree(conlltext)
			trees+=[tree]
		return trees


def trees2conllFile(trees, outfile, columns="u"): # changed default from 10 to u!
	"""
	exports a list of treedics into outfile
	used after tree transformations...
	in conll14 format, the lemma position contains a duplicated t if lemma is not available
	"""
        with codecs.open(outfile,"w","utf-8") as f:
		for tree in trees:
			if columns=="u": # conllu format
				treestring = tree.conllu()
			else:
				
				treestring = ""
				for stftkey in sorted(tree.sentencefeatures):
					if stftkey=="_comments":
						treestring+=tree.sentencefeatures[stftkey]
					else:
						treestring+=stftkey+" = "+tree.sentencefeatures[stftkey]				
				for i in sorted(tree.keys()):					
					node = tree[i] 
					gov = node.get("gov",{}).items()
					govid = -1
					func = "_"
					if gov:
						for govid,func in gov:
							if columns==10:
								treestring+="\t".join([str(i), node.get("t","_"), node.get("lemma",""), node.get("tag","_"), node.get("xpos","_"), "_", str(govid),func,"_","_"])+"\n"
							elif columns==14:
								lemma = node.get("lemma","_")
								if lemma == "_": lemma = node.get("t","_")
								treestring+="\t".join([str(i), node.get("t","_"), lemma, lemma or node.get("t","_"), node.get("tag","_"), node.get("tag","_"), node.get("morph","_"), node.get("morph","_"), str(govid),str(govid),func,func,"_","_"])+"\n"
					else:
						if columns==10:
							treestring+="\t".join([str(i), node.get("t","_"), node.get("lemma",""), node.get("tag","_"), node.get("xpos","_"), "_", str(govid),func,"_","_"])+"\n"
							
						elif columns==14:
							lemma = node.get("lemma","_")
							if lemma == "_": lemma = node.get("t","_")
							treestring+="\t".join([str(i), node.get("t","_"), lemma, lemma, node.get("tag","_"), node.get("tag","_"), node.get("morph","_"), node.get("morph","_"), str(govid),str(govid),func,func,"_","_"])+"\n"
			f.write(treestring+"\n")


def sentences2emptyConllFile(infile, outfile):
	"""
	transforms a list of sentences into conll format without trees
	"""
	inf=codecs.open(infile,"r","utf-8")
	outf=codecs.open(outfile,"w","utf-8")
	counter=0
	for line in inf:
		line=line.strip()
		if line:
			for i,word in enumerate(line.split()):
				outf.write("\t".join([str(i+1),word,word,"_","_","_","-1","","_","_"])+"\n")
				## nr, t, lemma , tag, xpos, _, head, rel, _, _ = cells
			outf.write("\n")
			counter+=1
	inf.close()
	outf.close()
	print counter, "sentences"


def textFiles2emptyConllFiles(infolder, outfolder):
	import glob, os
	sentenceSplit=re.compile(ur"(\s*\n+\s*|(?<!\s[A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ])[\?\!ï¼\!\.ãï¼â¦â¦]+\s+|\s+\|\s+|[ï¼ãï¼â¦â¦]+)(?!\d)", re.M+re.U)
	resentence=re.compile(ur"[\?\!ï¼\!\.ãï¼â¦â¦]+", re.M+re.U)
	retokenize=re.compile("(\W)",re.U+re.I)
	redoublespace=re.compile("(\s+)",re.U+re.I)
	renumber=re.compile("(\d) \. (\d)",re.U+re.I)
	rewhite=re.compile("\w",re.U+re.I)
	try:os.mkdir(outfolder)
	except:print "folder exists"
	for infile in glob.glob(os.path.join(infolder, '*.*')):
		print  infile
		outfile=codecs.open(outfolder+"/"+infile.split("/")[-1],"w","utf-8")
		for line in codecs.open(infile,"r","utf-8"):
			line=line.strip()
			for s in sentenceSplit.split(line):
				s=s.strip()
				if resentence.match(s):
					outfile.write(str(count)+"\t"+s+"\n")
					outfile.write("\n")
				else:
					count=1
					s = retokenize.sub(r" \1 ",s)
					s = redoublespace.sub(r" ",s)
					s = renumber.sub(r"\1.\2",s)
					for token in s.split():
						if token.strip()==u'\ufeff':continue # TODO: find out why this anti BOM shit is needed...
						outfile.write(str(count)+"\t"+token+"\n")
						count +=1




if __name__ == "__main__":
	pass
	ts = conllFile2trees("test.conll")
	print len(ts)
	print ts[0]
	
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2011 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

import os
from sqlite3 import connect, OperationalError
from optparse import OptionParser

	
def makeDataBase(dbpath):
	db = connect(dbpath)
	cursor = db.cursor()
	
	cursor.execute('''create table IF NOT EXISTS texts
	(textname TEXT UNIQUE, nrtokens INTEGER)''')
	cursor.execute('''create table IF NOT EXISTS users
	(user TEXT UNIQUE, realname TEXT)''')
	
	cursor.execute('''create table IF NOT EXISTS sentences
	(nr INTEGER, sentence TEXT, textid INTEGER)''')
	try:
		cursor.execute('''create VIRTUAL table IF NOT EXISTS sentencesearch USING fts4
		(nr INTEGER, sentence TEXT, textid INTEGER)''')
	except OperationalError: # Compatibility for sqlite without fts4
		cursor.execute('''create table IF NOT EXISTS sentencesearch
		(nr INTEGER, sentence TEXT, textid INTEGER)''')

	#annotype: 0=annoatator, 1=validator
	cursor.execute('''create table IF NOT EXISTS trees
	(sentenceid INTEGER, userid INTEGER, annotype INTEGER, status TEXT, comment TEXT, timestamp FLOAT, primary key (sentenceid, userid, annotype) )''') 
	cursor.execute('''create table IF NOT EXISTS features
	(treeid INTEGER, nr INTEGER, attr TEXT, value TEXT, primary key (treeid,nr,attr) )''')
	
	cursor.execute('''create table IF NOT EXISTS sentencefeatures
	(sentenceid INTEGER, attr TEXT, value TEXT )''')
	
	cursor.execute('''create table IF NOT EXISTS links
	(treeid INTEGER, depid INTEGER, govid INTEGER, function TEXT )''') 
	
	#todos has the following structure: userid INTEGER, textid INTEGER, type TEXT, status TEXT, comment TEXT
	#type means: 0=simple annotator, 1=validator
	#status means: 0:todo, 1: ok, ... fixed in the parameters of the project
	cursor.execute('''create table IF NOT EXISTS todos
	(userid INTEGER, textid INTEGER, type TEXT, status TEXT, comment TEXT, primary key (userid, textid) )''') 
	cursor.execute('''create table IF NOT EXISTS exos
	(textid INTEGER NOT NULL PRIMARY KEY, type INTEGER, exotoknum INTEGER, status TEXT, comment TEXT, FOREIGN KEY (textid) REFERENCES texts(rowid))''')
	cursor.execute('''create table IF NOT EXISTS exousersentence
	(textid INTEGER NOT NULL, userid INTEGER NOT NULL, sentenceid INTEGER NOT NULL, 
	UNIQUE (textid, userid, sentenceid),
	FOREIGN KEY (textid) REFERENCES texts(rowid),
	FOREIGN KEY (userid) REFERENCES users(rowid),
	FOREIGN KEY (sentenceid) REFERENCES sentences(rowid)
	)''')
	db.commit()
	db.close()
	
if __name__ == "__main__":

	usage = "usage: %prog [options] newProjectName\n important: make all folders world readable!"
	parser = OptionParser(usage)
	parser.add_option("-v", "--verbose",default=False,
			help="print status messages to stdout",
			action="store_true", dest="verbose")
			
	(options, args) = parser.parse_args()
	verbose = options.verbose
	if verbose: 
		print "options:",options,args
	if len(args)<1:	
		options.test=True
		print "please indicate a name for the new project"
	else:
		project=args[0]
		
		if os.path.exists ("../projects/"+project):
			print "ok"
			print "creating the database for the project",project
			dbpath="../projects/"+project+"/arborator.db.sqlite"
			makeDataBase(dbpath)
			os.chmod(dbpath,0777)
			
		else:
			print "please create the", project,"folder in the 'projects' folder."
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2017 Kim Gerdes
# kim AT gerdes. fr
# http://arborator.ilpga.fr/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 3 of the GNU Affero General Public License (the "License")
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of of version 3 of the GNU Affero General Public License
# from http://www.gnu.org/licenses/agpl-3.0.html 
# For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

from datetime import datetime
from time     import mktime, time, asctime, localtime
from sqlite3   import connect, OperationalError
import sys, os, re, json
from configobj import ConfigObj
import codecs
import config
import rhapsoxml
from conll import Tree

debug=False
#debug=True

if debug:import traceback

class SQL:
	def __init__(self, project):
		#,config.projects[0].encode('utf-8')
		if not project:
			try:project = config.projects[0]
			except:print "something went seriously wrong: can't find any projects"
		self.projectconfig = config.configProject(project) # read in the settings of the current project
		if os.path.exists("projects"):
			projectsfolder =  os.path.realpath("projects")
		elif os.path.exists("../projects"):
			projectsfolder =  os.path.realpath("../projects")
		else:
			print "can't find database",project
		self.dbpath = os.path.join(projectsfolder,project,self.projectconfig["configuration"]["db"]).encode('utf-8')
		self.exportpath = os.path.join(projectsfolder,project,self.projectconfig["configuration"]["exportfolder"])+"/"
		
		self.visibleToEveryone = dict([(v,None) for v in self.projectconfig["visible to everyone"].values()])
		
		
		#self.baseAnnotatorName = self.projectconfig["configuration"]["baseAnnotatorName"]
		
		self.importAnnotatorName = self.projectconfig["configuration"]["importAnnotatorName"]
		
		self.exoBaseAnnotatorName = self.projectconfig["configuration"].get("exoBaseAnnotatorName",None) # self.baseAnnotatorName
		self.teacher = self.projectconfig["configuration"].get("teacher","admin")
		
		self.governorsEval=int(self.projectconfig["evalutation scores"]["governors"])
		self.functionsEval=int(self.projectconfig["evalutation scores"]["functions"])
		self.categoriesEval=int(self.projectconfig["evalutation scores"]["categories"])
		self.lemmasEval=int(self.projectconfig["evalutation scores"]["lemmas"])
		self.evalTot=float(self.governorsEval+self.functionsEval+self.categoriesEval+self.lemmasEval)
		
		
		self.newTree = self.projectconfig["configuration"]["newTree"]
		self.tokenName = self.projectconfig["shown features"]["0"]
		self.catName = self.projectconfig["shown features"][self.projectconfig["configuration"]["categoryindex"]]
		self.treeorder = self.projectconfig["configuration"]["treeorder"]
		self.showTreesOfValidatedTexts = None
		if self.projectconfig["configuration"]["showTreesOfValidatedTexts"] in self.projectconfig["text status"]:
			self.showTreesOfValidatedTexts = self.projectconfig["configuration"]["showTreesOfValidatedTexts"]
		self.allVisibleForNonAnnotators=int(self.projectconfig["configuration"].get("allVisibleForNonAnnotators","0"))
		self.validatorsCanModifyTokens=int(self.projectconfig["configuration"].get("validatorsCanModifyTokens","0"))
		self.validatorsCanConnect=int(self.projectconfig["configuration"].get("validatorsCanConnect","0"))
		self.usersCanModifyTokens=int(self.projectconfig["configuration"].get("usersCanModifyTokens","0"))
		self.sentencefeaturesAlways=int(self.projectconfig["configuration"].get("sentencefeaturesAlways","0"))
		self.mate = self.projectconfig["configuration"].get("mate",None) # self.baseAnnotatorName

		
		self.editable=self.projectconfig["configuration"]["editable"]
		
		self.governorsVal=int(self.projectconfig["evalutation scores"]["governors"])
		self.functionsVal=int(self.projectconfig["evalutation scores"]["functions"])
		self.categoriesVal=int(self.projectconfig["evalutation scores"]["categories"])
		self.lemmasVal=int(self.projectconfig["evalutation scores"]["lemmas"])
		self.evalTot=float(self.governorsVal+self.functionsVal+self.categoriesVal+self.lemmasVal)
		
		self.exotypes=["no exercise","no feedback","percentage","graphical feedback","teacher visible"] # TODO: get this into configuration. important: only if index >1 the user gets the check button, see the main function in editor.cgi
		
	
	######################### central functions

	def open(self):
		
		db = connect(self.dbpath, check_same_thread = False)
		
		cursor = db.cursor()
		cursor.execute("PRAGMA journal_mode=WAL;")
		
		return db, cursor
	
	
	def enter(self, cursor, table, columns, values, computeId=True):
		"""
		general function to add data into the database
		"""
		#open("logger.txt","a").write("enter "+str(columns)+"\n")
		#print "INSERT OR IGNORE INTO "+table+" (" + ",".join(columns)+ ") VALUES (" + ",".join(len(values)*"?")+ ") ;"
		#print values
		cursor.execute("INSERT OR IGNORE INTO "+table+" (" + ",".join(columns)+ ") VALUES (" + ",".join(len(values)*"?")+ ") ;", values)
		if computeId:
			cursor.execute("select rowid from "+table+"  where "+ " and ".join( [c+"=?" for c in  columns ] ) + ";",values)
			id=cursor.fetchone()
			if id:
				id,=id
				return int(id)
			else:
				return None
			
			
		else: return None
	
	
	def getall(self, cursor, table, columns=[], values=[], tries=0, orderby=""):
		"""
		general function to obtain data from the database
		"""
		# TODO: check if this retry loop is really useful...
		
		if tries<2:
			try:
				if cursor:
					#print "select rowid,* from "+table+"  where "+ " and ".join( [c+"=?" for c in  columns ] ) + ";",values
					
					if columns:
						command="select rowid,* from "+table+" where "+ " and ".join( [c+"=?" for c in  columns ] ) + " "+orderby+ ";"
						if debug: print command, values
						cursor.execute(command,values)
						a = cursor.fetchall()
						#db.close()
						#print type(a)
						#print a
						return a
					else: cursor.execute("select rowid,* from "+table+ " "+orderby+ ";")
					a = cursor.fetchall()
					#db.close()
					return a
				else:
					db,cursor=self.open()
					if columns: 
						cursor.execute("select rowid,* from "+table+"  where "+ " and ".join( [c+"=?" for c in  columns ] ) + " "+orderby + ";",values)
						#print command
						a = cursor.fetchall()
						db.close()
						return a
					else: cursor.execute("select rowid,* from "+table+ " "+orderby+ ";")
					a = cursor.fetchall()	
					db.close()
					return a
					
			except Exception, e:
				from sqlite3 import OperationalError
				if isinstance(e, OperationalError) and str(e)in["attempt to write a readonly database","unable to open database file"]:
					print "Error! Make your database and the path leading there from the arborator base modifiable by the apache user!"
					sys.exit("something's wrong")
					
				else:
					if debug:print traceback.format_exc()
					#print msg
					return self.getall(cursor, table, columns, values, tries+1)


		else:
			print "problem with database. try to refresh the page!"


	
	
	def clean(self, cursor, table, columns, values):
		cursor.execute("delete from "+table+"  where "+ " and ".join( [c+"=?" for c in  columns ] ) + ";",values)
	
	
	
	def getUniqueId(self, cursor, table, columns, values):
		if columns: cursor.execute("select rowid,* from "+table+"  where "+ " and ".join( [c+"=?" for c in  columns ] ) + ";",values)
		else: cursor.execute("select rowid,* from "+table+ ";")
		a = cursor.fetchone()
		if a: return int(a[0])
		else: return None


	def getnumber(self, cursor, table, columns, values):
		if cursor:
			if columns: cursor.execute("select count(*) from "+table+"  where "+ " and ".join( [c+"=?" for c in  columns ] ) + ";",values)
			else: cursor.execute("select count(*) from "+table+ ";")
			return cursor.fetchone()[0]
		else:
			db,cursor=self.open()
			if columns: cursor.execute("select count(*) from "+table+"  where "+ " and ".join( [c+"=?" for c in  columns ] ) + ";",values)
			else: cursor.execute("select count(*) from "+table+ ";")
			a = cursor.fetchone()[0]
			db.close()
			return a


	
	
	######################### enter data

	
	
	def enterTree(self, cursor, nodedic, sentenceid, userid, notenter=[], intokenname=None, tokensChanged=False):
		"""
		takes the 
			db cursor, 
			nodedic, 
			sentenceid, 
			userid, 
			t (the name of the feature that serves as the token = invariable by the annotators)
		drops tree into database
		returns word counter

		called in two cases:
		- a new tree is entered whose tokens are not in the base yet
		- a tree is saved: a treeid already exists, all the features are erased first
		"""
		#debug start:
		#import sys
		#currentFuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name
		#open("logger.txt","a").write("enterTree "+str( [cursor, nodedic, sentenceid, userid, notenter, intokenname, tokensChanged,"daddy:",currentFuncName(1)])+"\n")
		#print nodedic, sentenceid, userid, intokenname
		#debug end
		
		if not intokenname:  intokenname=self.tokenName

		wcounter=0
		timestamp = mktime( datetime.now().timetuple() ) # contrary: asctime(localtime(timestamp))


		treeid=self.getUniqueId(cursor, "trees", ["sentenceid","userid","annotype"],[sentenceid, userid, 0])
		if treeid:
			#open("logger.txt","a").write("enterTree2 treeid existed\n")
			self.clean(cursor, "features",["treeid"],(treeid,))
			self.clean(cursor, "links",["treeid"],(treeid,))
			cursor.execute("update trees set timestamp=? where sentenceid=? and userid=? and annotype=?;",(timestamp, sentenceid, userid, 0,))

		else:
			#open("logger.txt","a").write("enterTree2 treeid didn't exist\n")
			treeid=self.enter(cursor, "trees",["sentenceid","userid","annotype","status","timestamp"],(sentenceid,userid,0,"0",timestamp),True) # createIfNotExists
			self.clean(cursor, "features",["treeid"],(treeid,)) # erase all existing features for this treeid, should be a useless line. just in case...
			self.clean(cursor, "links",["treeid"],(treeid,))


		sent=""

		for i in sorted(nodedic.keys()): # for every token

			tok=nodedic[i].get(intokenname,"")
			sent+=tok+" "
			if tokensChanged:
				cursor.execute("update features set value=? where features.attr=? and features.nr=? and features.treeid=(select trees.rowid from trees where trees.sentenceid =?)",(tok, self.tokenName, i, sentenceid,))

			wcounter+=1
			# enter attributes for token
			for attr in nodedic[i]: # i is now used as the nr value in the database's feature table
				if attr not in notenter:
					val=nodedic[i][attr]
					if type(val).__name__=='list':
						# not nice: values tabseparated. but not used for the moment
						#for v in val: # doesn't work because database doesn't allow multiple features of same attr
						self.enter(cursor, "features",["treeid","nr","attr","value"],(treeid,i,attr,"\t".join(val),), computeId=False)
					elif type(val).__name__=='dict': # should only happen for attr="gov"
						for gov in val:
							self.enter(cursor, "links",["treeid","depid","govid","function"],(treeid,i,gov,val[gov],), computeId=False)
					else: # atomic value
						#if attr!=t:
						if attr==intokenname: attr=self.tokenName # change the token name in the nodedic to the project's token name
						self.enter(cursor, "features",["treeid","nr","attr","value"],(treeid,i,attr,val,), computeId=False)
		if tokensChanged:
			sent=" ".join([a for a, in cursor.execute("select value from features where attr='t' and treeid in (select trees.rowid from trees, users where trees.sentenceid =? and users.user=? and users.rowid=trees.userid) ORDER BY nr ;",(sentenceid,self.importAnnotatorName,)).fetchall()])

			cursor.execute("UPDATE sentences SET sentence=? WHERE rowid=? ",(sent,sentenceid,))
			cursor.execute("UPDATE sentencesearch SET sentence=? WHERE rowid=? ",(sent,sentenceid,))
		return wcounter, sent.strip(), treeid



	def saveTree(self, sentenceid, userid, words, tokensChanged=False):
		""" 
		js to sqlite
		called from save.cgi
		gets a special format tree: words (ids are still strings etc)
		calls enterTree for entering
		returns the new tree id
		"""
		db,cursor=self.open()


		nodedic={}
		for i in words: # for each word
			nodedic[int(i)]={} # initialize the nodedic
			for k in words[i]:
				if k=="features": # features, except index and gov, are under the
					for a in words[i][k]:
						nodedic[int(i)][a]=words[i][k][a]
				else:
					nodedic[int(i)][k]=words[i][k]
		#open("logger.txt","a").write("saveTree "+str(nodedic)+"\ntokensChanged: "+str(tokensChanged)+"\n")

		wcounter, sent, treeid = self.enterTree(cursor, nodedic, sentenceid, userid,tokensChanged=True)
		
		#open("logger.txt","a").write("___return to saveTree. nodedic: "+str(nodedic)+"\ntokensChanged: "+str(tokensChanged)+"\n treeid: "+str(treeid)+"\n")
		if tokensChanged:
			for (nnr,ty) in tokensChanged:
				if ty==1:self.duplicateNode(nnr,sentenceid, cursor)
				elif ty==-1:self.eraseNode(nnr,sentenceid, cursor)

			sent=" ".join([a for a, in cursor.execute("select value from features where attr='t' and treeid in (select trees.rowid from trees, users where trees.sentenceid =? and users.user=? and users.rowid=trees.userid) ORDER BY nr ;",(sentenceid,self.importAnnotatorName,)).fetchall()])

			cursor.execute("UPDATE sentences SET sentence=? WHERE rowid=? ",(sent,sentenceid,))
			cursor.execute("UPDATE sentencesearch SET sentence=? WHERE rowid=? ",(sent,sentenceid,))
		db.commit()
		db.close()
		#open("logger.txt","a").write("checking back: gettree: "+str(self.gettree(sid=None,uid=None,treeid=treeid, username=None, indb=None, incursor=None))+"\n")
		
		if tokensChanged:
			return treeid,self.gettree(None,None,treeid), sent
		return treeid, None, None



	def setExo(self, tid, exochoice, exotoknum):
		"""
		called from project.cgi
		"""
		
		#print "___",self.exotypes.index(exochoice)
		db,cursor=self.open() 
		cursor.execute('INSERT or REPLACE INTO exos(textid, type, exotoknum) VALUES(?, ?, ?);', (tid, self.exotypes.index(exochoice), int(exotoknum), ))
		db.commit()
		db.close()



	def addtext2user(self,textid,user, validator):
		"""
		called from project.cgi
		"""
		database,cursor=self.open()
		uid=self.userid(user)
		self.clean(cursor, "todos",["userid","textid"],(uid,textid,))
		# status 0 corresponds to "todo":
		self.enter(cursor, "todos", ["userid","textid","type","status"], [uid,textid,validator,0], computeId=False)



		database.commit()
		database.close()

	
	def statustree(self, sid, snr, visibletreeid, userid, username, textid, admin):
		"""
		called from statusChange.cgi
		
		since the user can change the status we know that she is annotator or validator
		usually, the user changes her own tree's status
		if she doesn't have a tree, we copy the visible tree and give it the new status
		
		"""
		answerdic={}
		status="no tree"
		db,cursor=self.open()
		
		for _,_,_,_,status,comment,_ in self.getall(cursor, "trees",["sentenceid","userid"], [sid,userid]):
			if not status:status=0
			status=int(status)+1
			if str(status) not in self.projectconfig["tree status"]: status=0
		if status=="no tree": # copy visible tree
			status=1
			nodedic = self.gettree(None,None,visibletreeid)["tree"]
			#print "jjj",nodedic
			t=self.tokenName
			tds = self.gettodostatus(userid,textid)
			if tds<1:tds=0 # tds=1 means this is a validator
			wcounter, sent, treeid = self.enterTree(cursor, nodedic, sid, userid, intokenname=t)
			db.commit()
			links=self.links2AllTrees(sid,snr,username,admin,validator=tds)
			answerdic["links"]=links[0]
		cursor.execute("UPDATE trees SET status=? WHERE sentenceid=? AND userid=?",(status,sid,userid,))
		db.commit()
		db.close()
		answerdic["status"]=self.projectconfig["tree status"].get(str(status),"ooo")
		return answerdic
		
	
	def statustext(self, textid, userid):
		"""
		called from statusChange.cgi
		"""
		answerdic={}
		db,cursor=self.open()
		for _,_,_,ty,status,comment in self.getall(cursor, "todos",["textid","userid"], [textid,userid]):
			status=int(status)+1
			if str(status) not in self.projectconfig["text status"]: status=0
		try:
			self.clean(cursor, "todos", ["userid","textid"], [userid,textid])
			self.enter(cursor, "todos", ["userid","textid","type","status","comment"], [userid,textid,ty,status,comment], computeId=False)
			db.commit()
		except: print "database when problem changing status"
		db.close()
		answerdic["status"]=self.projectconfig["text status"].get(str(status),"ooo")
		return answerdic
	



	######################### getting data out to the interface




	def gettree(self,sid=None,uid=None,treeid=None, username=None, indb=None, incursor=None, adminLevel=0):
		"""
		gets tree if
		- treeid is known
		or
		- uid and sid is known
		or
		- username and sid is known

		returns the dictionary of the tree
		"""
		if indb:
			db=indb
			cursor=incursor
		else:
			db,cursor=self.open()
		#print sid,uid,treeid
		if treeid:

			dbexe = db.execute("""select features.nr, trees.rowid, trees.sentenceid, features.attr, features.value, users.user
					from trees, features, users
					where trees.rowid=features.treeid and trees.rowid=? and trees.userid=users.rowid;""",(treeid,))
		elif sid:


			if uid:
				dbexe = db.execute("""select features.nr, trees.rowid, trees.sentenceid, features.attr, features.value, users.user
						from trees, features, users
						where trees.rowid=features.treeid and trees.userid=? and trees.sentenceid=? and trees.userid=users.rowid;""",(uid, sid,))
			else:
				dbexe = db.execute("""select features.nr, trees.rowid, trees.sentenceid, features.attr, features.value, users.user
						from trees, features, users
						where trees.rowid=features.treeid and trees.userid=users.rowid and users.user=? and trees.sentenceid=?;""",(username, sid,))

		else:
			print "problem gettree",sid,uid,treeid
			sys.exit()
		nodedic={}
		t=self.tokenName
		for nr,treeid,sid,attr,value,username, in dbexe:
			nodedic[nr]=nodedic.get(nr,{})
			#nodedic[nr][t]=token
			nodedic[nr][attr]=value
			
		cursor.execute("""select * from links where links.treeid=?;""",(treeid,))	
		for treeid,depid,govid,function, in cursor.fetchall():	
			govdic=nodedic[depid].get("gov",{})
			#nodedic[nr][attr]=value
			#for _,_,_,govid,function in self.getall(cursor, "links", ["treeid", "depid"], [treeid, nr]):
			govdic[govid]=function
			nodedic[depid]["gov"]=govdic
		
		dic={}
		if not self.sentencefeaturesAlways: # if sentencefeaturesAlways no need to send it again whith the individual tree
			for _,sid,a,v in self.getall(cursor, "sentencefeatures", ["sentenceid"], [sid]):
				dic[a]=v
		dic["tree"]=nodedic ## maybe test whether "tree" is already an attribute?

		# exo stuff:
		
		try:
			cursor.execute("""select type from exos, sentences where exos.textid=sentences.textid and sentences.rowid = ?;""",(sid,))
			exotype,=cursor.fetchone()
		except:
			exotype = 0
		
		if username==self.teacher: # this can happen in two cases: user is actually teacher, or the teacher's tree is needed for comparison		
			if exotype>0 and adminLevel==0: # this is a student asking for the teacher's tree --> the tree cannot be saved
				dic["teacher"]=1 # the teacher feature is checked in javascript: arborator.edit.js
		else:# to avoid recursion
			
			if exotype>1:
				dic["goodtree"]=self.gettree(sid=sid, username=self.teacher, indb=db, incursor=cursor, adminLevel=adminLevel)["tree"]



		if not indb: db.close()
		return dic


	def treesForSentence(self,sid, dbcrsr=None):
		"""
		treeid,uid,user,realname,timestamp for a sentenceid
		"""
		if dbcrsr:	db,cursor=dbcrsr
		else:		db,cursor=self.open() 
		cursor.execute("""select trees.rowid, trees.userid, users.user, users.realname, trees.timestamp
					from trees, users
					where users.rowid=trees.userid and trees.sentenceid=?;""",(sid,))
		result = list(cursor.fetchall())
		if not dbcrsr: db.close()
		return result

	def treesForText(self,tid):
		"""
		gives all the realnames of all trees for a given textid
		called from project.cgi
		"""
		#importannoname=self.importAnnotatorName
		database,cursor=self.open()
		cursor.execute("""select distinct users.realname
					from trees, users, sentences
					where users.rowid=trees.userid and trees.sentenceid=sentences.rowid and sentences.textid=?;""",(tid,))
		#importannoname and users.user!=? 
		return cursor.fetchall()


	def uidForText(self,tid, dbcrsr=None):
		"""
		gives all the uids of all existing trees for a given textid
		called from getEvaluation.cgi
		"""
		#importannoname=self.self.importAnnotatorName
		if dbcrsr:	db,cursor=dbcrsr
		else:		db,cursor=self.open() 
		cursor.execute("""select distinct users.rowid
					from trees, users,sentences
					where users.rowid=trees.userid and trees.sentenceid=sentences.rowid and sentences.textid=?;""",(tid,))
		result = list(cursor.fetchall())
		if not dbcrsr: db.close()
		return result

	def treeNrsForUserAndText(self,uid,textid):
		"""
		called from project.cgi
		used for the all assignments table
		"""
		database,cursor=self.open()
		cursor.execute("""select sentences.rowid, sentences.nr, trees.timestamp, trees.rowid from trees, sentences where trees.userid=? and trees.sentenceid=sentences.rowid and sentences.textid=?;""",(uid,textid,))
		return cursor.fetchall()


	def tree2json(self, sid=None,uid=None,treeid=None, adminLevel=0):
		"""
		called from getTree.cgi
		"""
		if sid:		nodedic = self.gettree(sid,uid, adminLevel=adminLevel)
		elif treeid:	nodedic = self.gettree(None,None,treeid, adminLevel=adminLevel)
		print json.dumps(nodedic, sort_keys=True)

	
	def getAllSentences(self, textid, username, userid, adminLevel=0, dbcrsr=None):
		"""
		lists the sentences for a given text and user
		
		this is easy if it's 
			the admin user
			or no exercice 
			or the exotoknum is set to 0 (all)
		in other cases, this function 
			attributes the necessary sentences to the user
			and stores it in the exousersentence table!!!
		
		returns an ordered list of (snr,sid,s,tid) :
			sentence number
			sentence id
			sentence
			treeid
		
		called from main function in editor
		
		"""
		if dbcrsr:
			db,cursor=dbcrsr
		else:
			db,cursor=self.open() 
		try:
			cursor.execute("""select type, exotoknum from exos where exos.textid=?;""",(textid,))
			exotype,exotoknum, = cursor.fetchone()
		except:
			exotype, exotoknum = 0, 0
		
		
		if adminLevel or exotype==0 or exotoknum==0: # user sees all sentences
			if not dbcrsr:db.close()
			return sorted([(snr,sid,s,tid) for (sid,snr,s,tid) in self.getall(None, "sentences",["textid"],[textid])])
		else:	# normal user that needs to see only some subset of the sentences
			cursor.execute("""select sentences.rowid, sentences.nr, sentences.sentence, sentences.textid from exousersentence, users, sentences where exousersentence.textid=? and exousersentence.userid=users.rowid and users.user=? and exousersentence.sentenceid=sentences.rowid;""",(textid,username))
			a = cursor.fetchall()
			if a:	# user already has sentence attribution 
				ret = sorted([(snr,sid,s,tid) for (sid,snr,s,tid) in a])
				
			else:	# user still needs sentence attribution
				cursor.execute("""select sentences.rowid from sentences where sentences.textid=? ;""",(textid, ))
				sid2numUsers = dict.fromkeys([sid for sid, in cursor.fetchall()], 0)
				cursor.execute("""select * from exousersentence where exousersentence.textid=?;""",(textid, ))
				for tid,uid,sid in cursor.fetchall():
					sid2numUsers[sid]=sid2numUsers.get(sid,0)+1
				thisuserstoknum=0
				exousersentence=[]
				for sid in sorted(sid2numUsers, key=sid2numUsers.get):
					# for all sentence ids from low nb of users to many:
					if thisuserstoknum>exotoknum:
						break
					else:
						exousersentence+=[(textid, userid, sid) ]
						cursor.execute("""select count(*) from features, sentences, trees, users where users.user=? and users.rowid=trees.userid and trees.rowid=features.treeid and trees.sentenceid=sentences.rowid and sentences.rowid=? and features.attr=?;""",(self.importAnnotatorName,  sid, self.tokenName, ))
						number=cursor.fetchone()[0]
						thisuserstoknum+=number
				
				cursor.executemany("INSERT INTO exousersentence (textid, userid, sentenceid) VALUES (?,?,?)", exousersentence)
				
				cursor.execute("""select sentences.rowid, sentences.nr, sentences.sentence, sentences.textid from exousersentence, users, sentences where exousersentence.textid=? and exousersentence.userid=users.rowid and users.user=? and exousersentence.sentenceid=sentences.rowid;""",(textid,username))
				a = cursor.fetchall()
				ret = sorted([(snr,sid,s,tid) for (sid,snr,s,tid) in a])
				db.commit()
			if not dbcrsr:db.close()
			return ret
				
	
	
	def links2AllTrees(self,sid,snr,username,adminLevel, todo=-1, validvalid=[],validator=False, addEmptyUser=None):
		"""
		for a given sentence id, gives the html with links to all the trees of the sentence
		validvalid: list of userids that should be shown because they have validated the text. if none is given, the validvalid is computed
		addEmptyUser: in case of "teacher visible" exercises, the user itself needs to be automatically provided with an empty tree
		"""
		
		if self.showTreesOfValidatedTexts and not validvalid:
			validvalid=self.validvalid(None, sid)			
				
		if adminLevel or validator or (self.allVisibleForNonAnnotators and todo==-1):
			treeDict=dict([ (user, (treeid,uid,realname,timestamp)) for treeid,uid,user,realname,timestamp in self.treesForSentence(sid)])
		else: # common mortal:
			treeDict=dict([ (user, (treeid,uid,realname,timestamp)) for treeid,uid,user,realname,timestamp in self.treesForSentence(sid) if user in self.visibleToEveryone or user==username or uid in validvalid])
		
		if not treeDict or (addEmptyUser and addEmptyUser not in treeDict and username not in treeDict):
			# could happen in case of exo:
			# in case user hasn't created own tree, she should see tree of self.exoBaseAnnotatorName
			# in case tree of self.exoBaseAnnotatorName doesn't exist = tree of self.importAnnotatorName without deps and cats
			userid = self.userid(username)
			exoBaseAnnoId = self.userid(self.exoBaseAnnotatorName)
			#print "<br>____exoBaseAnnoId",exoBaseAnnoId,self.exoBaseAnnotatorName
			#open("logger.txt","a").write("___links2AllTrees____"+str(addEmptyUser and addEmptyUser not in treeDict)+str([username not in treeDict, treeDict,username])+"~~~~~~~\n"+str([ (user, (treeid,uid,realname,timestamp)) for treeid,uid,user,realname,timestamp in self.treesForSentence(sid)])+"\n")
			
			db,cursor=self.open()
			nodedic = self.gettree(sid=sid,username=self.exoBaseAnnotatorName,indb=db,incursor=cursor, adminLevel=adminLevel)["tree"]
			#print "<br>____",nodedic
			if nodedic: # found tree of self.exoBaseAnnotatorName, save it as user's tree
				if username not in treeDict:
					self.enterTree(cursor, nodedic, sid, userid)	
			else:	# no tree exists yet for self.exoBaseAnnotatorName, let's create it, 
				# and save it under exoBaseAnnoId if applicable, and always under the userid
				nodedic = self.gettree(sid=sid,username=self.importAnnotatorName,indb=db,incursor=cursor, adminLevel=adminLevel)["tree"]
				if exoBaseAnnoId: self.enterTree(cursor, nodedic, sid, exoBaseAnnoId, notenter=["gov",self.catName,"tag2"])
				wcounter, sent, newtreeid = self.enterTree(cursor, nodedic, sid, userid, notenter=["gov",self.catName,"tag2"])
				#wcounter, sent, treeid = self.enterTree(cursor, nodedic, sid, userid, t)
			db.commit()
			db.close()
			treeDict[username]=(newtreeid, userid, username,0)
			
		# putting these trees in order:
		goodlist=[]
		if self.treeorder=="newestHuman":
			inhumane=[self.importAnnotatorName,self.exoBaseAnnotatorName,self.teacher]
			# order by timestamp
			timeCouples=[ (timestamp,user) for (user, (treeid,uid,realname,timestamp)) in treeDict.iteritems() if user not in inhumane]
			for ts,u in sorted(timeCouples,reverse=True): 
				goodlist+=[(u,treeDict[u])]
			for user in inhumane:
				if user in treeDict:
					goodlist+=[(user,treeDict[user])]
		elif self.treeorder=="newest":
			# order by timestamp
			timeCouples=[ (timestamp,user) for (user, (treeid,uid,realname,timestamp)) in treeDict.iteritems() ]
			for ts,u in sorted(timeCouples,reverse=True): 
				goodlist+=[(u,treeDict[u])]
		elif self.treeorder=="user":
			# user first, then baseAnnotator, then others by alphabet
			bestusers=list(set([username,self.importAnnotatorName]))
			for u in bestusers:
				if u in treeDict:goodlist+=[(u,treeDict[u])]
			for u in sorted(treeDict,key=unicode.lower):
				if (u,treeDict[u]) not in goodlist: goodlist+=[(u,treeDict[u])]
		elif self.treeorder=="importAnnotator":
			# baseAnnotator first, then user, then others by alphabet
			bestusers=[self.importAnnotatorName,username]
			for u in bestusers:
				if u in treeDict:goodlist+=[(u,treeDict[u])]
			for u in sorted(treeDict,key=unicode.lower):
				if (u,treeDict[u]) not in goodlist: goodlist+=[(u,treeDict[u])]
		else:
			# default: order by alphabet
			for u in sorted(treeDict,key=unicode.lower):
				goodlist+=[(u,treeDict[u])]
		
		#print "<br>____",adminLevel,sid,treeDict,"===============",goodlist, not goodlist, addEmptyUser, "self.visibleToEveryone",self.visibleToEveryone,"uid in validvalid",uid in validvalid,uid, validvalid,"self.catName",self.catName
		
		html=""
		if len(goodlist)>0:
			if adminLevel  :
				html += " ".join([u'''<a class='othertree' title='{title}' treeid='{treeid}' treecreator='{u}' nr='{snr}'>{u}</a><sup> <a onclick="eraseTree({snr},{treeid},'{u}');">x</a></sup>'''.format(treeid=treeid,u=u,snr=snr,title=unicode(realname)+" "+asctime(localtime(timestamp))) for (u,(treeid,uid,realname,timestamp)) in goodlist])
			else:
				html = " ".join([u'''<a class='othertree' treeid='{treeid}' title='{title}' treecreator='{u}' nr='{snr}'>{u}</a>'''.format(treeid=treeid,u=u,snr=snr,title=unicode(realname)+" "+asctime(localtime(timestamp))) for (u,(treeid,uid,realname,timestamp)) in goodlist])
		
		if len(goodlist)>1:
			objs="{"+",".join(['''"{u}":"{treeid}"'''.format(treeid=treeid,u=u) for (u,(treeid,uid,realname,timestamp)) in goodlist])+"}"
			colors=json.dumps(config.list2colors([u for (u,(treeid,uid,realname,timestamp)) in goodlist]))
			html+="""<img class="compare" src="images/compare.png" border="0" align="bottom" onclick='compare({o},{snr},{colors})' id="compa{snr}"  nr='{snr}'>""".format(o=objs, snr=snr, colors=colors[:-1]+',"ok":"cccccc"}')
		try:firsttreeid=goodlist[0][1][0]
		except:firsttreeid=0
		
		if self.sentencefeaturesAlways:
			dic={}
			db,cursor=self.open()
			for _,sid,a,v in self.getall(cursor, "sentencefeatures", ["sentenceid"], [sid]):
				if a in self.projectconfig.shownsentencefeatures:
					dic[a]=v
			db.close()
			sentenceinfo = "<br/>".join(dic.values())
		else:
			sentenceinfo = ""
				
				
		return html,firsttreeid, sentenceinfo
	
	

	def getNumberTexts(self):
		"""
		returns number of texts in the table
		"""
		db,cursor=self.open()
		cursor.execute("""select count(*) from texts""")
		number=cursor.fetchone()[0]
		db.close()
		return number


	def getNumberSentences(self):
		"""
		returns number of sentences in the table
		"""
		db,cursor=self.open()
		cursor.execute("""select count(*) from sentences""")
		number=cursor.fetchone()[0]
		db.close()
		return number
        
        def getNumberValidatedSentences(self):
                """
		returns number of validated sentences in the table
		"""
		db,cursor=self.open()
		cursor.execute("""select count(*) from (select distinct sentences.nr, sentences.sentence from sentences, trees, texts, todos where trees.sentenceid=sentences.rowid and sentences.textid=texts.rowid and todos.textid=texts.rowid and( trees.status=1 or todos.status=1)  );""")
		number=cursor.fetchone()[0]
		db.close()
		return number
            
            
        

	def getNumberTreesPerUserAndText(self, uid, tid):
		"""
		returns number of trees the user has saved
		called from project.cgi
		"""
		db,cursor=self.open()
		cursor.execute("""select count(*) from trees, sentences where sentences.textid=? and trees.userid=? and sentences.rowid=trees.sentenceid;""",(tid,uid,))
		number=cursor.fetchone()[0]
		db.close()
		return number


	def getNumberTokensPerText(self, tid, recompute=False):
		"""
		returns number of tokens
		"""
		db,cursor=self.open()

		for _,_,number in self.getall(cursor, "texts",["rowid"], [tid]):
			break
		try:
			if recompute: raise bloedError
			number=int(number)
			db.close()
			return number
		except:
			# try with the official importAnnotatorName
			cursor.execute("""select count(*) from features, sentences, trees, users where users.user=? and users.rowid=trees.userid and trees.rowid=features.treeid and trees.sentenceid=sentences.rowid and sentences.textid=? and features.attr=?;""",(self.importAnnotatorName,  tid, self.tokenName, ))

			number=cursor.fetchone()[0]
			if number==0: # try the first user
				cursor.execute("""select count(*) from features, sentences, trees, users where users.rowid=1 and users.rowid=trees.userid and trees.rowid=features.treeid and trees.sentenceid=sentences.rowid and sentences.textid=? and features.attr=?;""",(tid, self.tokenName, ))

				number=cursor.fetchone()[0]
			
			
			cursor.execute('update texts set nrtokens=? where rowid=?', (number,tid,))

			db.commit()
			db.close()
			return number
                    
        def getNumberValidatedTokens(self):
                """
		returns number of tokens of all validated sentences in the table
		"""
		db,cursor=self.open()
				
		cursor.execute("""select count(*) from (select distinct features.nr, trees.sentenceid from features, sentences, trees, texts, todos where features.attr=? and features.treeid=trees.rowid and trees.sentenceid=sentences.rowid and sentences.textid=texts.rowid and todos.textid=texts.rowid and ( trees.status=1 or todos.status=1)   );""",self.tokenName )
		number=cursor.fetchone()[0]
		db.close()
		return number
	
	def gettreestatus(self,sid,userid):
		"""
		called from editor and quick
		"""
		
		status="no tree"
		db,cursor=self.open()
		
		for _,_,_,_,status,comment,_ in self.getall(cursor, "trees",["sentenceid","userid"], [sid,userid]):
			status=status
			if status==None:status=0
		db.close()
		if str(status) in self.projectconfig["text status"]: return self.projectconfig["text status"][str(status)]
		else: return str(status)
	
	
	def validvalid(self, textid, sid=None):
		"""
		gets a list of validators for a given text that have given their ok
		can also retrieve the list if given only a sentence id - maybe not a good idea?
		"""
		
			
		if self.showTreesOfValidatedTexts:
			database,cursor=self.open()
			if sid:
				cursor.execute("""select users.rowid from users, todos, sentences where users.rowid=todos.userid and todos.type=1 and todos.status=? and todos.textid=sentences.textid and sentences.rowid=?;""",(self.showTreesOfValidatedTexts,sid,))
			else:
				
				cursor.execute("""select users.rowid from users, todos where users.rowid=todos.userid and todos.type=? and todos.status=1 and todos.textid=? ;""",(self.showTreesOfValidatedTexts,textid,))
			return [i for i, in cursor.fetchall()]
		else:
			return []
		
	
	
	def gettodostatus(self,userid,textid):
		"""
		-1: no todo
		0: simple annotation
		1: validation
		"""
		ty=-1
		db,cursor=self.open()
		for _,_,_,ty,status,comment in self.getall(cursor, "todos",["textid","userid"], [textid,userid]):
			ty=ty
		db.close()
		return ty

	
		
	def usersLastSeen(self, seconds=600, userdir="users/"):
		db,cursor=self.open()
		users={}
		now=[]
		for uid,username,realname in self.getall(cursor,"users"):
			user = ConfigObj(userdir+username+'.ini')
			users[username]=(float(user.get("lastused",0)),realname)
			if mktime( datetime.now().timetuple() ) - float(user.get("lastused",0)) < seconds: 
				now+=[(username,realname,user.get("email",""))]
		
		return users,now



	def userid(self,username,realname=None):
		# get the userid for username
		db,cursor=self.open()
		if realname:
			uid = self.enter(cursor, "users", ["user","realname"], [username, realname])
			if not uid and self.getUniqueId(cursor, "users", ["user"],[username]):
				self.clean(cursor, "users",["user"],(username,))
				try:uid = self.enter(cursor, "users", ["user","realname"], [username, realname])
				except:uid = self.enter(cursor, "users", ["user","realname"], [username.decode("utf-8"), realname.decode("utf-8")])
			db.commit()

		else: uid = self.enter(cursor, "users", ["user"], [username]) # should only be called if realname is already entered, because realname will be None...
		db.close()
		return uid


	def realname(self,user,crsr=None):
		if crsr:
			cursor=crsr
		else:
			database,cursor=self.open()
			
		cursor.execute('select realname from users where user=?', (user,))
		 
		rn=cursor.fetchone()
		try:	realna, = rn
		except:	realna=None
		if not realna:
			if os.path.exists("users/"+user+".ini"):
				config = ConfigObj("users/"+user+".ini")
				realna = config["realname"].decode("utf-8")
			elif user in ["parser","mate","guest","gold"]:
				realna=user
			else: 
				realna = "anonymous"
			#print "realna,user",realna,user
			cursor.execute('update users set realname=? where user=?', (realna,user,))
			
		if not crsr:
			database.commit()
			database.close()
			
		return realna
		
		
	def getExo(self, tid, dbcrsr=None):
		"""
		called from editor.cgi and project.cgi
		"""
		
		if dbcrsr:
			db,cursor=dbcrsr
		else:
			db,cursor=self.open() 
		
		
		try:
			cursor.execute("""select type, exotoknum from exos where textid=? ;""",(tid,))
			exotype,exotoknum, =cursor.fetchone()
		except: # downward compatiblity for databases that don't have the table yet. 
			cursor.execute('''create table IF NOT EXISTS exos (textid INTEGER, type INTEGER, exotoknum INTEGER, status TEXT, comment TEXT)''')
			exotype = None
		if not exotype: exotype, exotoknum = 0, 0
		if not dbcrsr: db.close()
		return exotype, exotoknum
		
		
	
	
	
	
	
	######################### tree comparison and user evaluation
	



	def compare(self, treeids):
		"""
		for graphical diff between trees
		"""
		trees={}

		cc=self.catName # cat name: cat or tag

		comparedic={}
		for treeid in treeids:
			thisnodedic=self.gettree(None,None,treeid)["tree"] # TODO: turn this loop into a unique SQL query to make it faster!
			trees[treeids[treeid]] = thisnodedic

		comparedic=thisnodedic
		for i in thisnodedic:
			comparedic[i]["cgov"]=comparedic[i].get("cgov",{})
			good=True
			for tu in trees:
				t = trees[tu]

				comparedic[i]["cgov"][tu]=t[i].get("gov",{}).items()
				comparedic[i]["agov"]=comparedic[i].get("agov",{})
				for g,f in t[i].get("gov",{}).items():
					comparedic[i]["agov"][str(g)+"___"+f]=comparedic[i]["agov"].get(str(g)+"___"+f,[])+[tu]
				#cat compare
				comparedic[i]["ccat"]=comparedic[i].get("ccat",{})
				comparedic[i]["ccat"][tu]=t[i].get(cc,"undefined")
				comparedic[i]["acat"]=comparedic[i].get("acat",{})
				comparedic[i]["acat"][t[i].get(cc,"undefined")]=comparedic[i]["acat"].get(t[i].get(cc,"undefined"),[])+[tu]
		for i in comparedic:
			comparedic[i]["gov"]={}
			for (gf),ul in comparedic[i]["agov"].iteritems():
				g,f = gf.split("___")
				g=int(g)

				if len(ul)==len(treeids):
					comparedic[i]["gov"][g]= [(f,["ok"])]
				else:
					comparedic[i]["gov"][g]=comparedic[i]["gov"].get(g,[])+[(f,ul)]

			comparedic[i][cc]=[]
			for (c),ul in comparedic[i]["acat"].iteritems():


				if len(ul)==len(treeids):
					comparedic[i][cc]= [(c,["ok"])]
				else:
					comparedic[i][cc]=comparedic[i].get(cc,[])+[(c,ul)]

		for i in comparedic:
			del comparedic[i]["agov"]
			del comparedic[i]["cgov"]
			del comparedic[i]["acat"]
			del comparedic[i]["ccat"]


		dic={}


		# TODO: this just uses the last treeid. all compared trees should have the same sentenceid. this is not tested.
		if not self.sentencefeaturesAlways: # if sentencefeaturesAlways no need to send it again whith the individual tree
			db,cursor=self.open()
			for at,va, in cursor.execute("""select sentencefeatures.attr, sentencefeatures.value
						from trees, sentences,sentencefeatures
						where sentencefeatures.sentenceid=sentences.rowid and trees.sentenceid=sentences.rowid and trees.rowid=?;""",(treeid,)).fetchall():
				dic[at]=va
			db.close()
		dic["tree"]=comparedic ## maybe test whether "tree" is already an attribute?

		print json.dumps(dic, sort_keys=True)



		

	#def evaluateUser(self,uid, tid=None, consolidateLine=True):
		#"""
		#if tid (textid) is given: only this text
		#else: all assignment for the user
		
		#moved to getEvaluation
		#"""

		#print "----------",uid,tid,consolidateLine

		#out=""

		#textids=[]
		#treeidlists=[]
		#totaltokens=[]
		#titles=[]
		#if tid:
			#textids+=[tid]
			#tid,t,nrtokens = self.getall(None, "texts",["rowid"],[tid])[0]

			#treenrs=self.treeNrsForUserAndText(uid,tid)
			#trees=sorted([(int(n),asctime(localtime(o))) for (sid,n,o,trid,) in treenrs])
			#treeidlists+=[ [ (trid,sid) for (sid,n,o,trid,) in treenrs] ]

			#totaltokens+=[nrtokens]
			#titles+=[t]
		#else:
			#for _,uid,tid,todotype,status,comment in self.getall(None, "todos",["userid"], [uid]): # for all the assignments (texts) for the given user

				#textids+=[tid]
				#tid,t,nrtokens = self.getall(None, "texts",["rowid"],[tid])[0]

				#treenrs=self.treeNrsForUserAndText(uid,tid)
				#trees=sorted([(int(n),asctime(localtime(o))) for (sid,n,o,trid,) in treenrs])
				#treeidlists+=[ [ (trid,sid) for (sid,n,o,trid,) in treenrs] ]

				#totaltokens+=[nrtokens]
				#titles+=[t]

		#teacher=self.projectconfig["configuration"].get("teacher",None)
		#if teacher:	teacherid=self.userid(self.teacher)

		#out+= "<table><tr><td>text</td><td>correctcats</td><td>correctdeps</td><td>correctfuncs</td><td>correctlemmas</td><th>tokens</th><th>score/100</th></tr>"

		##print teacherid
		#tottottok=0
		#goods=[0,0,0,0]
		#db,cursor=self.open()

		#a= self.getall(cursor, "users",["rowid"], [uid])
		#if a :
			#uid,user,realname = a[0]
		#out+=" --- ".join([str(uid),user,unicode(realname)])
		#print uid, user, realname

		#for textid,treeids,title,tottok in zip(textids,treeidlists,titles,totaltokens): # for each text assigned to the user

			#gcats,gdeps,gfuncs,glemmas=0,0,0,0
			#for treeid, sid in treeids:
				#utree = self.gettree(None,None,treeid)["tree"]
				#ttree = self.gettree(sid,teacherid,None)["tree"]

				#gc,gd,gf,gl=self.evaluateTree(utree, ttree) # TODO: what's going on
				#gcats+=gc
				#gdeps+=gd
				#gfuncs+=gf
				#glemmas+=gl
			#score=self.computeTotalEvaluation(gcats,gdeps,gfuncs,glemmas,	float(tottok))
			#out+= "<tr><th>{title}</th><td>{gcats}</td><td>{gdeps}</td><td>{gfuncs}</td><td>{glemmas}</td><th>{tottok}</th><th>{score:.{digits}f}</th></tr>".format(title=title,gcats=gcats,gdeps=gdeps,gfuncs=gfuncs,glemmas=glemmas,tottok=tottok,score=score,digits=2)
			#correctcats,correctdeps,correctfuncs,correctlemmas=goods
			#goods[0]+=gcats
			#goods[1]+=gdeps
			#goods[2]+=gfuncs
			#goods[3]+=glemmas
			#tottottok+=tottok
		#gcats,gdeps,gfuncs,glemmas=goods
		#score=self.computeTotalEvaluation(gcats,gdeps,gfuncs,glemmas,	float(tottottok))
		#out+= "<tr><th>=</th><td>{gcats}</td><td>{gdeps}</td><td>{gfuncs}</td><td>{glemmas}</td><th>{tottok}</th><th style='font-weight: bold;'>{score:.{digits}f}</th></tr>".format(title=title,gcats=gcats,gdeps=gdeps,gfuncs=gfuncs,glemmas=glemmas,tottok=tottottok,score=score,digits=2)
		#out+= "</td></tr></table>"
		#if consolidateLine:
			#import unicodedata
			#out=''.join(c for c in unicodedata.normalize('NFD', unicode(realname)) if unicodedata.category(c) != 'Mn').replace(" ","-")+"\t"+str(score)

		#return out
	
	def nrDifferent(self, atid, btid, attribute, dbcrsr):
		"""
		counts the number of differnt attributes "attribute" of the tree id atid and the tree btid
		"""
		db,cursor=dbcrsr
		if attribute:
			cursor.execute('select count(*) from (select nr,attr,value from features where treeid=? and attr=?)  t1, (select nr,attr,value from features where treeid=? and attr=?)  t2 where t1.nr=t2.nr and t1.value<>t2.value;', (atid,attribute,btid,attribute,))
		else: # link compare
			cursor.execute('select count(distinct  t1.depid ) from  (select depid,govid,function from links where treeid=? )  t1, (select depid,govid,function from links where  treeid=? )  t2 where t1.depid=t2.depid and (t1.govid<>t2.govid or t1.function<>t2.function );', (atid,btid,))
			
		return cursor.fetchone()[0]

	
				
	
	
	######################### node modification



	def duplicateNode(self,nnr,sentenceid, cursor):
		"""
		called from save function
		"""
		cursor.execute("update features set nr=-nr where nr>=? and features.treeid in (select trees.rowid from trees where trees.sentenceid =?)",(nnr,sentenceid,))
		cursor.execute("update features set nr=1-nr where nr<0 and features.treeid in (select trees.rowid from trees where trees.sentenceid =?)",(sentenceid,))
		cursor.execute("insert into features (treeid, nr,attr,value) select treeid,nr-1,attr,value from features where nr=? and features.treeid in (select trees.rowid from trees where trees.sentenceid =?)",(nnr+1,sentenceid,))

		cursor.execute("update links set depid=depid+1 where depid>=? and links.treeid in (select trees.rowid from trees where trees.sentenceid =?)",(nnr,sentenceid,))
		cursor.execute("update links set govid=govid+1 where govid>=? and links.treeid  in (select trees.rowid from trees where trees.sentenceid =?)",(nnr,sentenceid,))

		cursor.execute("insert into links (treeid, govid, depid, function) select treeid, govid-1, depid, function from links where govid=? and links.treeid in (select trees.rowid from trees where trees.sentenceid =?)",(nnr+1,sentenceid,))
		cursor.execute("insert into links (treeid, govid, depid, function) select treeid, govid, depid-1, function from links where depid=? and links.treeid in (select trees.rowid from trees where trees.sentenceid =?)",(nnr+1,sentenceid,))


	def eraseNode(self,nnr,sentenceid, cursor):
		"""
		called from save function
		"""
		cursor.execute("delete from features where nr=? and treeid in (select trees.rowid from trees where trees.sentenceid =?);",(nnr,sentenceid,))
		cursor.execute("delete from links where govid=? and treeid in (select trees.rowid from trees where trees.sentenceid =?);",(nnr,sentenceid,))
		cursor.execute("delete from links where depid=? and treeid in (select trees.rowid from trees where trees.sentenceid =?);",(nnr,sentenceid,))
		cursor.execute("update features set nr=-nr where nr>=? and features.treeid in (select trees.rowid from trees where trees.sentenceid =?)",(nnr,sentenceid,))
		cursor.execute("update features set nr=-1-nr where nr<0 and features.treeid in (select trees.rowid from trees where trees.sentenceid =?)",(sentenceid,))
		cursor.execute("update links set depid=depid-1 where depid>=? and links.treeid in (select trees.rowid from trees where trees.sentenceid =?)",(nnr,sentenceid,))
		cursor.execute("update links set govid=govid-1 where govid>=? and links.treeid  in (select trees.rowid from trees where trees.sentenceid =?)",(nnr,sentenceid,))


	def eraseTree(self, treeid, sentencenr, username, adminLevel):
		"""
		called from eraseTree.cgi
		"""
		db,cursor=self.open()
		t = self.getall(cursor, "trees", ["rowid"], [treeid])

		if t: 	treeid,sid,uid,annoty,sta,comm,ti = t[0]
		else:
			print "error: no sentence id found for the treeid",treeid
			sys.exit()

		if len(self.treesForSentence(sid))==1: # don't completely erase the last tree, because we need to preserve the tokens
			cursor.execute('UPDATE features SET value="" WHERE treeid=? AND attr!=?;',(treeid,self.tokenName,))
			cursor.execute("DELETE from links WHERE treeid=?;",(treeid,))
			importAnnotatorID=self.getUniqueId(cursor, "users", ["user"],[self.importAnnotatorName])
			cursor.execute("UPDATE trees SET userid=? WHERE rowid=?;",(importAnnotatorID,treeid,))


		else:
			self.clean(cursor, "features", ["treeid"], [treeid])
			self.clean(cursor, "links", ["treeid"], [treeid])
			self.clean(cursor, "trees", ["rowid"], [treeid])
		db.commit()

		try:
			return self.links2AllTrees(sid,sentencenr,username,adminLevel,validator=0)[0] # return just the html with the new list of trees for the sentence
		except:
			return "" # needed if last tree was erased


	def connectRight(self, sid1, sentencenr1):
		"""
		called from connect.cgi
		"""
		db,cursor=self.open()

		t = self.getall(cursor, "sentences", ["rowid"], [sid1])
		if t: 	sid1,nr1,sentence1,textid = t[0]
		else:
			print "error: no sentence id found for the sentence id",sid1
			sys.exit()

		if int(nr1)!=int(sentencenr1):
			print "error: nr!=sentencnr",nr1,sentencenr1
			sys.exit()

		t = self.getall(cursor, "sentences", ["nr","textid"], [nr1+1,textid])
		if t: 	sid2,nr2,sentence2,textid = t[0]
		else:
			print "error: no next sentence found",nr1+1
			sys.exit()

		parserid=self.getUniqueId(cursor, "users", ["user"],[self.importAnnotatorName])

		userid2coupleTrees={}
		uidTotid1=dict((uid,(tid,ts)) for tid, uid, _, _, ts in self.treesForSentence(sid1))
		uidTotid2=dict((uid,(tid,ts)) for tid, uid, _, _, ts in self.treesForSentence(sid2))

		#for tid, uid, user, _, ts in self.treesForSentence(sid1):
			#print "___",uid, user
		#for tid, uid, user, _, ts in self.treesForSentence(sid2):
			#print "___",uid, user


		userid2coupleTrees=dict((uid,(uidTotid1.get(uid,(self.getUniqueId(cursor,"trees",["sentenceid","userid"],[sid1,parserid]) ,0)) ,uidTotid2.get(uid,(self.getUniqueId(cursor,"trees",["sentenceid","userid"],[sid2,parserid]) ,0))))
		  for uid in set(uidTotid1.keys()+uidTotid2.keys()))


		tree1already=[]

		for uid,((treeid1,ts1),(treeid2,ts2)) in userid2coupleTrees.iteritems():
			#print "\noooooo",uid
			tree1 = self.gettree(treeid=treeid1, indb=db,incursor=cursor)
			tree2 = self.gettree(treeid=treeid2, indb=db,incursor=cursor)

			#print "tree1",tree1
			#print "len",len(tree1["tree"])
			#print "tree2",tree2
			#print "len",len(tree2["tree"])

			m = max(tree1["tree"].keys())
			#print m
			for i,node in tree2["tree"].iteritems():
				node["gov"]=dict(((j+m if j else 0),f) for j,f in node.get("gov",{}).iteritems())
				if treeid1 not in tree1already: tree1["tree"][i+m]=node

			if treeid1 not in tree1already:
				if "markupIU" in tree1 and "markupIU" in tree2:
					tree1["markupIU"]=tree1["markupIU"]+" "+tree2["markupIU"]

			wcounter, sent, treeid = self.enterTree(cursor, tree1["tree"], sid1, uid,tokensChanged=True)

			tree1already+=[treeid1]

		cursor.execute("UPDATE sentences SET sentence=? WHERE rowid=? ",(sent,sid1,))
		cursor.execute("UPDATE sentencesearch SET sentence=? WHERE rowid=? ",(sent,sid1,))
		if "markupIU" in tree1:
			cursor.execute("UPDATE sentencefeatures SET value=? WHERE sentenceid=? and attr=?",(tree1["markupIU"],sid1,"markupIU",))# TODO:generalize this to any sentencefeature

		for uid,((treeid1,ts1),(treeid2,ts2)) in userid2coupleTrees.iteritems():
			self.clean(cursor, "features", ["treeid"], [treeid2])
			self.clean(cursor, "links", ["treeid"], [treeid2])
			self.clean(cursor, "trees", ["rowid"], [treeid2])

		self.clean(cursor, "sentences",["rowid"],(sid2,)) # erase all existing sentences for this textid
		self.clean(cursor, "sentencesearch",["rowid"],(sid2,)) # erase all existing sentences for this textid
		cursor.execute("UPDATE sentences SET nr=nr-1 WHERE nr>? and textid=?",(nr1,textid,))

		db.commit()
		db.close()


	def splitSentence(self, sid, sentencenr, toknr):
		"""
		called from connect.cgi
		"""
		db,cursor=self.open()
		toknr=int(toknr)
		sentencenr=int(sentencenr)
		t = self.getall(cursor, "sentences", ["rowid"], [sid])
		if t: 	sid,snr,sentence,textid = t[0]
		else:
			print "error: no sentence id found for the sentence id",sid
			sys.exit()

		if int(snr)!=sentencenr:
			print "error: nr!=sentencnr",snr,sentencenr
			sys.exit()

		t = self.getall(cursor, "trees", ["sentenceid"], [sid])
		if t: 	tid,_,uid,_,_,_,_ = t[0]
		else:
			print "error: no tree found found. sentenceid:",sid
			sys.exit()

		if toknr+1 not in self.gettree(treeid=tid, indb=db,incursor=cursor)["tree"]:
			print "error: no next token found after token nr:",toknr
			sys.exit()

		#parserid=self.getUniqueId(cursor, "users", ["user"],[self.baseAnnotatorName])

		uidToTrees={}

		for tid,_,uid,annotype,status,comment,timestamp in t:

			tree = self.gettree(treeid=tid, indb=db,incursor=cursor)
			if "markupIU" in tree:
				markup1,markup2=[],tree["markupIU"].split()
			s1,s2=[],[]

			newtree={}
			for i,node in sorted(tree["tree"].items()):
				if i>toknr:
					s2+=[node[self.tokenName]]
					node["gov"]=dict(((j-toknr if j>toknr else 0),f) for j,f in node.get("gov",{}).iteritems())
					newtree[i-toknr]=node
					del tree["tree"][i]
				else:
					node["gov"]=dict(((0 if j>toknr else j),f) for j,f in node.get("gov",{}).iteritems())
					s1+=[node[self.tokenName]]
					if "markupIU" in tree:
						while markup2:
							if node[self.tokenName]==markup2[0]:
								markup1+=[markup2.pop(0)]
								#print "good",markup1
								break
							markup1+=[markup2.pop(0)]	
			
			uidToTrees[uid]=newtree
			
			wcounter, sent, treeid = self.enterTree(cursor, tree["tree"], sid, uid,tokensChanged=True)
			
		if "markupIU" in tree:
			mu1= " ".join(markup1)
			mu2= " ".join(markup2)	
			
		se1= " ".join(s1)
		se2= " ".join(s2)
		
		cursor.execute("UPDATE sentences SET nr=nr+1 WHERE nr>? and textid=?",(sentencenr,textid,)) 
		cursor.execute("UPDATE sentences SET sentence=? WHERE rowid=? ",(se1,sid,))
		cursor.execute("UPDATE sentencesearch SET sentence=? WHERE rowid=? ",(se1,sid,))
		
		if "markupIU" in tree:
			cursor.execute("UPDATE sentencefeatures SET value=? WHERE sentenceid=? and attr=?",(mu1,sid,"markupIU",))# TODO:generalize this to any sentencefeature
		
		newsid=self.enter(cursor, "sentences",["nr","sentence","textid"],(sentencenr+1,se2,textid),computeId=True)
		
		if "markupIU" in tree:
			self.enter(cursor, "sentencefeatures",["sentenceid","attr","value"],(newsid,"markupIU",mu2) )
		
		for uid,newtree in uidToTrees.iteritems():
			wcounter, sent, treeid = self.enterTree(cursor, newtree, newsid, uid,tokensChanged=True)
		
		db.commit()
		db.close()
		
		
		return
		
		


	######################### export

	def exportAnnotations(self, textid, textname, exportType):
		"""
		conll and xml export
		called from project.cgi
		for export of complete sets of annotations
		
		exportType: todosconll, todosxml, lastconll, lastxml, allconll, allxml
		"""
		db,cursor=self.open()

		scounter, fcounter, users=0,0,[]
		doublegovs=False
		renolet=re.compile("\W",re.U+re.I)
		basefilename=renolet.sub(".",textname.strip().replace(" ","_").replace("?",""))
		if not os.path.exists(self.exportpath.encode("utf-8")): 
			os.makedirs(self.exportpath.encode("utf-8"))
			os.chmod(self.exportpath.encode("utf-8"),0777)
			with codecs.open(".htaccess","w","utf-8") as htfile:
				htfile.write("Options Indexes\n")

		#importannoname=self.importAnnotatorName
		parserid=self.getUniqueId(cursor, "users", ["user"],[self.importAnnotatorName])


		if exportType in ["todosconll", "todosxml"]: 	# only todos are exported, __complete__ conll or xml files are created, non existing trees are filled up with parser trees

			for _,uid,_,annotype,status,comment in self.getall(cursor, "todos",["textid"], [textid]): # for each assignment of the text to someone:
				a= self.getall(cursor, "users",["rowid"], [uid])
				if a :
					uid,user,realname = a[0]
					users+=[user]
					filename = self.exportpath+basefilename + "."+user
					if annotype: filename += ".valid."
					if exportType=="todosconll":
						filename+=".complete.conll10"
					else:
						alltext="\n"
						doc,tokens,lexemes, dependencies, text = rhapsoxml.baseXml()
						filename+=".complete.rhaps.xml"

					outf=codecs.open(filename.encode("utf-8"),"w","utf-8") #open the file to write in

					for sid, snr, sentence, _ in self.getall(cursor, "sentences", ["textid"], [textid]): # for each sentence of the text

						if annotype: # try to get a validated tree
							t = self.getall(cursor, "trees", ["sentenceid", "userid", "annotype"], [sid, uid, 1])
							if t: treeid,sid,uid,annoty,sta,comm,ti = t[0]
							else: t = self.getall(cursor, "trees", ["sentenceid", "userid" ], [sid, uid])
						else: # if not the normal tree by the user
							t = self.getall(cursor, "trees", ["sentenceid", "userid" ], [sid, uid])
						if t: # the tree exists annoted by the user.
							treeid,sid,uid,annoty,sta,comm,ti = t[0]
						else:# the user did not touch the tree assigned to him or her.
							t = self.getall(cursor, "trees", ["sentenceid", "userid" ], [sid, parserid])
							if t:
								treeid,sid,parserid,annoty,sta,comm,ti, = t[0]
							else:
								print "error: no parsed tree found for sentenceid", sid,"!!! parserid:",parserid,"<br/>\n"
								#1/0
								continue

						if exportType=="todosconll":
							doublegovs=self.conllSentenceExport(sid, cursor,treeid,outf)
						else:
							alltext+=self.xmlSentenceAdd(sid, cursor,treeid,doc,tokens, lexemes,  dependencies)
						scounter+=1
					if exportType=="todosxml":
						text.appendChild(doc.createTextNode(alltext))
						xml= doc.toprettyxml(indent="     ")
						outf.write(xml)
					outf.close()
					try:	os.chmod(filename,0666)
					except:	pass
					fcounter+=1

		elif exportType in ["lastconll", "lastxml"]: 	# for every sentence, the most recent tree is exported

			r = self.getall(cursor, "sentences",["textid"], [textid])
			users=[]
			if r:
				self.exportpath+u"Ã©"
				basefilename+u"Ã©"
				filename = self.exportpath+basefilename + u".most.recent"
				if exportType=="lastconll":
					filename+=".trees.conll10"
				else:
					alltext="\n"
					doc,tokens,lexemes, dependencies, text = rhapsoxml.baseXml()
					filename+=".trees.rhaps.xml"
				outf=codecs.open(filename.encode("utf-8"),"w","utf-8")

				for sentenceid,nr,sentence,textid in r: # for each sentence of the text:


					treeDict=dict([ (user, (treeid,uid,realname,timestamp)) for treeid,uid,user,realname,timestamp in self.treesForSentence(sentenceid)])
					# order by timestamp
					timeDict=dict([ (timestamp,user) for (user, (treeid,uid,realname,timestamp)) in treeDict.iteritems() ])

					lastuser=timeDict[sorted(timeDict,reverse=True)[0]]
					treeid,uid,realname,timestamp=treeDict[lastuser]
					if lastuser not in users: users+=[lastuser]
					if exportType=="lastconll":
						doublegovs=self.conllSentenceExport(sentenceid, cursor, treeid, outf)
					else:
						alltext+=self.xmlSentenceAdd(sentenceid, cursor, treeid, doc, tokens, lexemes,  dependencies)
					scounter+=1

				if exportType=="lastxml":
					text.appendChild(doc.createTextNode(alltext))
					xml= doc.toprettyxml(indent="     ")
					outf.write(xml)
				outf.close()
				try:	os.chmod(filename,0666)
				except:	pass
				fcounter+=1

			users=["most recent modification (including trees from "+", ".join(users)+")"]

		elif exportType in ["allconll","allxml"]: # all __trees__ of all users are exported
			r = self.getall(cursor, "sentences",["textid"], [textid])
			if r:
				uid2stid={}
				for sentenceid,nr,sentence,textid in r: # for each sentence of the text:
					rr=self.getall(cursor, "trees",["sentenceid"], [sentenceid])
					if rr:
						for treeid,sid,uid,ty,sta,comm,ti in rr: # for each tree on the sentence:
							uid2stid[(uid,ty)]=uid2stid.get((uid,ty),[])+ [  (nr,sid,treeid)] # keep it in memory
				for uid,ty in uid2stid:
					a= self.getall(cursor, "users",["rowid"], [uid])
					if a :
						uid,user,realname = a[0]
						
						if ty: user = user+"-valid"
						users+=[user]
						filename = self.exportpath+basefilename + "."+user
						if exportType=="allconll":
							filename+=".trees.conll10"
						else:
							alltext="\n"
							doc,tokens,lexemes, dependencies, text = rhapsoxml.baseXml()
							filename+=".trees.rhaps.xml"
						outf=codecs.open(filename.encode("utf-8"),"w","utf-8")
						for nr,sid,treeid in sorted(uid2stid[(uid,ty)]):
							if exportType=="allconll":
								doublegovs=self.conllSentenceExport(sid, cursor, treeid, outf)
							else:
								alltext+=self.xmlSentenceAdd(sid, cursor, treeid, doc, tokens, lexemes,  dependencies)
							scounter+=1
					if exportType=="allxml":
						text.appendChild(doc.createTextNode(alltext))
						xml= doc.toprettyxml(indent="     ")
						outf.write(xml)
					outf.close()
					try:	os.chmod(filename,0666)
					except:	pass
					fcounter+=1

		return fcounter,users,scounter,doublegovs
	
	


	def conllSentenceExport(self, sid, cursor, treeid, outf):
		"""
		called for each sentence in the conll export procss
		if outf=None, also used from outside tools, like trees2train.py
		"""
		doublegovs=False

		tree = Tree()
		for nr,at,va, in cursor.execute("""select features.nr, features.attr, features.value
					from trees, features
					where trees.rowid=features.treeid and trees.rowid=?;""",(treeid,)).fetchall():
			tree[nr]=tree.get(nr,{})
			tree[nr][at]=va
		for nr in sorted(tree): # for each token:
			g = self.getall(cursor, "links", ["treeid", "depid"], [treeid, nr])
			gd = dict([(govid,function) for _,trid,depid,govid,function in g if govid>=0]) #trid==treeid and
			tree[nr]["gov"]=gd
			if len(gd)>1:doublegovs=True
		for at,va, in cursor.execute("""select sentencefeatures.attr, sentencefeatures.value
						from trees, sentences,sentencefeatures
						where sentencefeatures.sentenceid=sentences.rowid and trees.sentenceid=sentences.rowid and trees.rowid=?;""",(treeid,)).fetchall():
			tree.sentencefeatures[at]=va
		
		if outf:
			outf.write(tree.conllu()+"\n")
			return doublegovs
		else:
			return tree
		
		



	def xmlSentenceAdd(self, sid, cursor,treeid,doc,tokens, lexemes,  dependencies):
		"""
		called for each sentence in the xml export procss

		"""
		_,_,sentence,_=self.getall(cursor, "sentences",["rowid"], [sid])[0]

		d={}
		for nr,at,va, in cursor.execute("""select features.nr, features.attr, features.value
					from trees, features
					where trees.rowid=features.treeid and trees.rowid=?;""",(treeid,)).fetchall():
			d[nr]=d.get(nr,{})
			d[nr][at]=va
		for nr in sorted(d):
			g = self.getall(cursor, "links", ["treeid", "depid"], [treeid, nr])
			gd = dict([(govid,function) for _,trid,depid,govid,function in g if govid>=0])
			d[nr]["gov"]=gd

		rhapsoxml.addFeat2doc(d, doc, self.tokenName,"lemma", tokens, lexemes, dependencies)
		return sentence+"\n"

	
	def exportAll(self, exportType): 
		for t,tid in sorted( [(t,tid) for tid,t,nrtokens in self.getall(None, "texts", None, None)]):
			fc,users,sc,doublegovs=self.exportAnnotations(tid, t, exportType )



	######################### search
	
	
	def snippetSearch(self, query):
		"""
		3 separate cases for speed reasons:
		
		1. mixed query of words and features
		2. only features
		3. only words
		
		called from project.cgi
		"""
		#TODO: security issue here: query is not escaped!!!!
		
		db,cursor=self.open()
		query=query.replace("'","''")
		words,featuresearch=[],[]
		for word in query.split():
			if ":" in word:
				both=word.split(":")
				featuresearch+=[ ( both[0],":".join(both[1:])) ]
			else:
				words+=[word]
		
		if featuresearch:
			featurequery = " "
			features, features2 = " ", " "
			links, links2 = " ", " "
			for attr, value in featuresearch:
				if attr=="func" or attr=="function":
					featurequery+= " and links.function='{value}' and links.treeid=trees.rowid ".format(value=value)
					links=", links "
					links2=" and links.treeid=trees.rowid "
					
				else:
					featurequery+= " and features.attr='{attr}' and features.value='{value}'".format(attr=attr,value=value)
					features=", features "
					features2=" and features.treeid=trees.rowid " 
					
			#print featurequery	
			if words:
				query=" ".join(words)
				
				
				try:
					rc=cursor.execute("SELECT texts.textname, sentencesearch.rowid, sentencesearch.nr, sentencesearch.textid, snippet(sentencesearch) FROM sentencesearch, texts, features, trees, sentences, links  WHERE sentencesearch.sentence MATCH '{query}' AND sentencesearch.textid = texts.rowid and sentences.nr=sentencesearch.nr and features.treeid=trees.rowid and trees.sentenceid=sentences.rowid and sentences.textid=texts.rowid  {featurequery} ;	".format(query=query,featurequery=featurequery))
					#print "SELECT texts.textname, sentencesearch.rowid, sentencesearch.nr, sentencesearch.textid, snippet(sentencesearch) FROM sentencesearch, texts, features, trees, sentences  WHERE sentencesearch.sentence MATCH '{query}' AND sentencesearch.textid = texts.rowid and sentences.nr=sentencesearch.nr and features.treeid=trees.rowid and trees.sentenceid=sentences.rowid and sentences.textid=texts.rowid  {featurequery} ;	".format(query=query,featurequery=featurequery)
				except OperationalError:
					# compatiblity for sqlite without fts module compilation
					rc=cursor.execute("SELECT texts.textname, sentencesearch.rowid, sentencesearch.nr, sentencesearch.textid, sentencesearch.sentence FROM sentencesearch, texts, features, trees, sentences, links  WHERE sentencesearch.sentence like '%{query}%' AND sentencesearch.textid = texts.rowid and sentences.nr=sentencesearch.nr and features.treeid=trees.rowid and trees.sentenceid=sentences.rowid and sentences.textid=texts.rowid  {featurequery} ;	".format(query=query,featurequery=featurequery))
					#print "SELECT texts.textname, sentencesearch.rowid, sentencesearch.nr, sentencesearch.textid, sentencesearch.sentence FROM sentencesearch, texts, features, trees, sentences  WHERE sentencesearch.sentence like '%{query}%' AND sentencesearch.textid = texts.rowid and sentences.nr=sentencesearch.nr and features.treeid=trees.rowid and trees.sentenceid=sentences.rowid and sentences.textid=texts.rowid  {featurequery} ;	".format(query=query,featurequery=featurequery)
			else:
				
				#rc=cursor.execute("SELECT texts.textname, sentences.rowid, sentences.nr, sentences.textid, '-' FROM texts {otherfeatures}, trees, sentences {links}  WHERE sentences.textid = texts.rowid and features.treeid=trees.rowid and trees.sentenceid=sentences.rowid and sentences.textid=texts.rowid  {featurequery} ;".format(features=features, links=links, featurequery=featurequery))
				q="SELECT texts.textname, sentences.rowid, sentences.nr, sentences.textid, '-' FROM texts, sentences {features} {links}, (SELECT sentences.rowid as sentenceid,trees.rowid as rowid,trees.userid,MAX(trees.timestamp) as max_date FROM sentences,trees WHERE trees.sentenceid=sentences.rowid GROUP BY sentences.rowid)trees WHERE sentences.textid = texts.rowid {features2} {links2} and trees.sentenceid=sentences.rowid and sentences.textid=texts.rowid  {featurequery} ;".format(features=features, features2=features2, links=links, links2=links2, featurequery=featurequery)
				#print q
				print "results only on last modified trees <br>"
				rc=cursor.execute(q)
				
		
		else:
			try:
				rc=cursor.execute("SELECT texts.textname, sentencesearch.rowid, sentencesearch.nr, sentencesearch.textid, snippet(sentencesearch) FROM sentencesearch, texts WHERE sentencesearch.sentence MATCH '{query}' AND sentencesearch.textid = texts.rowid;".format(query=query))
			except OperationalError:
				rc=cursor.execute("SELECT texts.textname, sentencesearch.rowid, sentencesearch.nr, sentencesearch.textid, sentencesearch.sentence FROM sentencesearch, texts WHERE sentencesearch.sentence like '%{query}%' AND sentencesearch.textid = texts.rowid;".format(query=query))
		 
		html=""
		counter=0
		lastsid=None
		for tname, sid, snr, textid,r, in rc:
			
			
			
			if sid!=lastsid: # ugly hack to avoid the sqlite fts4 bug that doesn't allow "select distinct" searches
				counter+=1
				html+= "<tr><td>"+str(counter)+"</td><td><b><a style='cursor:pointer;' onclick=edit('"+str(textid)+"','"+str(snr)+"')>"+ tname +"</a></b></td>"
				html+= "<td><a  style='cursor:pointer;' onclick=edit('"+str(textid)+"','"+str(snr)+"')>"+str(snr)+"</a></td><td><a  style='cursor:pointer;' onclick=edit('"+str(textid)+"','"+str(snr)+"')>"+r+"</a></td></tr>\n"
				lastsid=sid
			#
		db.close()
		if html:return "<table class='whitable'> <thead><tr><th>nr.</th><th>text</th><th>sentence</th><th>snippet</th></tr></thead>\n"+html+"</table>"





	######################### maintenance functions
	
	def cleanDatabase(self,filename=None):
		"""
		called from treebankfiles
		
		allows to remove a text from the database for a given text name
		deletes all possible remaining non-related data
		TODO: replace this by a version of removeText
		"""
		text="cleanDatabase <br/>\n"
		db,cursor=self.open()
		
		if filename:
			rc=cursor.execute("DELETE FROM texts WHERE textname = ?;",(filename,)).rowcount
			if rc:text+= "deleted " + str( rc)+ " texts, <br/>"
		
		rc=cursor.execute("DELETE FROM sentences WHERE NOT EXISTS (SELECT NULL FROM texts WHERE texts.rowid = sentences.textid)").rowcount
		if rc:text+= "deleted " + str( rc)+ " sentences, <br/>"
		rc=cursor.execute("DELETE FROM sentencefeatures WHERE NOT EXISTS (SELECT NULL FROM sentences WHERE sentences.rowid = sentencefeatures.sentenceid)").rowcount
		if rc:text+= "deleted " + str( rc)+ " sentences, <br/>"
		rc=cursor.execute("DELETE FROM sentencesearch WHERE NOT EXISTS (SELECT NULL FROM texts WHERE texts.rowid = sentencesearch.textid)").rowcount
		if rc:text+= "deleted " + str( rc)+ " sentencesearch, <br/>"
		rc=cursor.execute("DELETE FROM todos WHERE NOT EXISTS (SELECT NULL FROM texts WHERE texts.rowid = todos.textid)").rowcount
		if rc:text+= "deleted " + str( rc)+ " todos, <br/>"
		rc=cursor.execute("DELETE FROM todos WHERE NOT EXISTS (SELECT NULL FROM users WHERE users.rowid = todos.userid)").rowcount
		if rc:text+= "deleted " + str( rc)+ " todos, <br/>"
		
		rc=cursor.execute("DELETE FROM trees WHERE NOT EXISTS (SELECT NULL FROM sentences WHERE sentences.rowid = trees.sentenceid)").rowcount
		if rc:text+= "deleted " + str( rc)+ " trees, <br/>"
		rc=cursor.execute("DELETE FROM trees WHERE NOT EXISTS (SELECT NULL FROM users WHERE users.rowid = trees.userid)").rowcount
		if rc:text+= "deleted " + str( rc)+ " trees, <br/>"
		
		rc=cursor.execute("DELETE FROM features WHERE NOT EXISTS (SELECT NULL FROM trees WHERE trees.rowid = features.treeid)").rowcount
		if rc:text+= "deleted " + str( rc)+ " features, <br/>"
		rc=cursor.execute("DELETE FROM links WHERE NOT EXISTS (SELECT NULL FROM trees WHERE trees.rowid = links.treeid)").rowcount
		if rc:text+= "deleted " + str( rc)+ " features, <br/>"
		
		db.commit()
		db.close()
		return text


	


	def removetextfromuser(self,textid,uid):
		"""
		called from project.cgi
		"""
		database,cursor=self.open()
		self.clean(cursor, "todos",["userid","textid"],(uid,textid,))
		database.commit()
		database.close()



	def removeText(self, tid):
		"""
		called from project.cgi
		"""
		
		db,cursor=self.open()
		scounter=0
		for sid,snr, s,t, in self.getall(cursor, "sentences",["textid"],[tid]):
			for treeid,sid,uid,annoty,sta,comm,ti, in self.getall(cursor, "trees",["sentenceid"],[sid]):
				#for treeid,_,uid, in self.getall(cursor, "trees",["sentenceid"],[sid]):
					self.clean(cursor, "features", ["treeid"], [treeid])
					self.clean(cursor, "trees", ["rowid"], [treeid])
			#self.clean(cursor, "tokens", ["sentenceid"], [sid])
			scounter+=1
		self.clean(cursor, "sentences",["textid"],(tid,)) # erase all existing sentences for this textid
		self.clean(cursor, "sentencesearch",["textid"],(tid,)) # erase all existing sentences for this textid
		self.clean(cursor, "texts",["rowid"],(tid,)) # erase all existing texts for this textid
		self.clean(cursor, "todos",["textid"],(tid,)) # erase all existing todos for this textid

		db.commit()
		return scounter

	
	
	def correctSentenceLength(self,gooduserid=2):
		"""
		probably no longer needed
		before: correction in case of bad nb of tokens
		TODO: remove function
		"""
		db,cursor=self.open() 
		for textid,tname,nbt in self.getall(cursor, "texts",[], []):
			print "__________",textid,tname
			#textid=self.getUniqueId(cursor, "texts", ["textname"],["Amelie_PreudHomme"])
			#print textid
			for sid,snr,sentence,_ in self.getall(cursor, "sentences",["textid"], [textid]):
				
				goodnrtoks=0
				tidToToknr,tidToUid={},{}
				for tid,sidd,uid,aty,sta,com,tim in self.getall(cursor, "trees",["sentenceid"], [sid]):
					
					nt = self.getnumber(cursor, "features", ["treeid","attr"], [tid,"t"])
					#print tid,uid,nt
					if uid==gooduserid:	goodnrtoks=nt
					tidToToknr[tid]=nt
					tidToUid[tid]=uid
				for tid in tidToToknr:
					if tidToToknr[tid]>goodnrtoks:
						print "__sentence",snr
						print "problem",tid,uid,tidToToknr[tid]
						for _,user,real in self.getall(cursor, "users",["rowid"], [tidToUid[tid]]):
							print user,real
						rc=cursor.execute("delete from features where treeid=? and nr>?;",(tid,goodnrtoks)).rowcount
						print "deleted",rc,"features"
						rc=cursor.execute("delete from links where treeid=? and (depid>? or govid>?);",(tid,goodnrtoks,goodnrtoks)).rowcount
						print "deleted",rc,"links"
	
		db.commit()	
		db.close()
	
	def removeStudentTrees(self):
		
		db,cursor=self.open()
		q="""select trees.rowid as treeid, sentences.nr as sentencenr, users.user as username 
			from trees, sentences, users 
			where users.rowid = trees.userid and trees.sentenceid=sentences.rowid;"""
		rc=cursor.execute(q)
		for treeid, sentencenr, username in rc:
			print treeid, sentencenr, username
			if username not in ["prof","admin"]:
				self.eraseTree(treeid, sentencenr, username, 5)
				print "erased"
		
		q="""delete from users where users.user != 'admin' and users.user != 'prof';"""
		rc=cursor.execute(q)
		
		db.commit()	
		db.close()
		
	
if __name__ == "__main__":
	print "bonjour"
	sql=SQL("SyntaxeAlOuest")
	sql.removeStudentTrees()
	
	#sql=SQL("Rhapsodie")
	#sql=SQL("lingCorpus")
	#sql=SQL("HongKongTVMandarin")
	
	#sql.exportAnnotations(14, "UD-mandarinParsed.0", "allconll")
	#sql.exportAnnotations(6, "Rhaps.gold", "lastconll")
	
	#sql.exportAnnotations(1, "x", "lastconll")
	#print sql.snippetSearch("func:disflink")
	#sql.exportAll("lastconll")
	#print sql.gettree(treeid=3947)
	
	#sql.correctSentenceLength()
	sql.cleanDatabase()
	
	#sentenceid=41
	#userid=5
	#words=	{"1":{"index":1,"gov":{},"features":{"t":"euh","cat":"I","lemma":"euh"}},"2":{"index":2,"gov":{"0":"root"},"features":{"t":"eh ben","cat":"I","lemma":"eh bien"}},"3":{"index":3,"gov":{"4":"sub"},"features":{"t":"on","cat":"Cl","lemma":"cln"}},"4":{"index":4,"gov":{"0":"root"},"features":{"t":"a","cat":"V","lemma":"avoir"}},"5":{"index":5,"gov":{"4":"ad"},"features":{"t":"toujours","cat":"Adv","lemma":"toujours"}},"6":{"index":6,"gov":{"7":"det"},"features":{"t":"un","cat":"D","lemma":"un"}},"7":{"index":7,"gov":{"4":"ad"},"features":{"t":"peu","cat":"N","lemma":"peu"}},"8":{"index":8,"gov":{"4":"obj"},"features":{"t":"ceux","cat":"Pro","lemma":"celui"}},"9":{"index":9,"gov":{"8":"dep"},"features":{"t":u"lÃ ","cat":"Adv","lemma":u"lÃ "}},"10":{"index":10,"gov":{"11":"sub"},"features":{"t":"qui","cat":"Qu","lemma":"qui"}},"11":{"index":11,"gov":{"8":"dep"},"features":{"t":"vieillissent","cat":"V","lemma":"vieillir"}}}

	#sql.saveTree(sentenceid,userid,words)
	#sql.compare({41: 'parser', 1355: 'wxcv', 1357: 'admin'})
	#sql.exportAnnotations( 30, "M016.XML", "todosxml")
	#print sql.validvalid(None, 84)
	#print sql.cleanDatabase()
	print "ok"
## DepEdit init ##
from depedit import DepEdit
#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
DepEdit - A simple configurable tool for manipulating dependency trees

Input: CoNLL10 or CoNLLU (10 columns, tab-delimited, blank line between sentences, comments with pound sign #)

Author: Amir Zeldes
"""

from __future__ import print_function
import argparse
import re
from io import open as io_open
from copy import copy, deepcopy
import sys, os
from collections import defaultdict
from glob import glob
from six import iteritems

__version__ = "2.1.1"


def escape(string,symbol_to_mask,border_marker):
	inside = False
	output = ""
	# Scan the string looking for border or symbol to mask
	for char in string:
		if char == border_marker:
			inside = not inside
		if char == symbol_to_mask and inside:
			output += "%%%%%"
		else:
			output += char
	return output


class ParsedToken:
	def __init__(self, tok_id, text, lemma, pos, cpos, morph, head, func, head2, func2, num, child_funcs, position, is_super_tok=False):
		self.id = tok_id
		self.text = text
		self.pos = pos
		self.cpos = cpos
		self.lemma = lemma
		self.morph = morph
		self.head = head
		self.func = func
		self.head2 = head2
		self.func2 = func2
		self.num = num
		self.child_funcs = child_funcs
		self.position = position
		self.is_super_tok = is_super_tok

	def __repr__(self):
		return str(self.text) + " (" + str(self.pos) + "/" + str(self.lemma) + ") " + "<-" + str(self.func)


class Sentence:

	def __init__(self,sentence_string=""):
		self.sentence_string = sentence_string
		self.length = 0
		self.annotations = {}

	def print_annos(self):
		out_string = ""
		for (key,val) in iteritems(self.annotations):
			out_string += "# " + key + "=" + val + "\n"
		return out_string


class Transformation:

	def parse_transformation(self, transformation_text):
		if transformation_text.count("\t") < 2:
			return None
		else:
			split_trans = transformation_text.split("\t")
			definition_string = split_trans[0]
			relation_string = split_trans[1]
			action_string = split_trans[2]
			relation_string = self.normalize_shorthand(relation_string)
			action_string = self.normalize_shorthand(action_string)
			definition_string = escape(definition_string,";","/")
			definitions_list = definition_string.split(";")
			escaped_definitions = []
			for _def in definitions_list:
				escaped_definitions.append(_def.replace("%%%%%",";"))
			definitions = []
			for def_index, esc_string in enumerate(escaped_definitions):
				definitions.append(DefinitionMatcher(esc_string,def_index + 1))
			relations = relation_string.split(";")
			actions = action_string.strip().split(";")
			aliased_actions = []
			for action in actions:
				aliased_actions.append(self.handle_aliases(action))
			return [definitions, relations, aliased_actions]

	@staticmethod
	def handle_aliases(orig_action):
		orig_action = orig_action.replace(":form=",":text=")
		orig_action = orig_action.replace(":upostag=", ":pos=")
		orig_action = orig_action.replace(":xpostag=",":cpos=")
		orig_action = orig_action.replace(":feats=",":morph=")
		orig_action = orig_action.replace(":deprel=",":func=")
		orig_action = orig_action.replace(":deps=",":head2=")
		orig_action = orig_action.replace(":misc=",":func2=")
		return orig_action

	@staticmethod
	def normalize_shorthand(criterion_string):
		criterion_string = criterion_string.replace('.*', '.1,1000')
		temp = ""
		while temp != criterion_string:
			temp = criterion_string
			criterion_string = re.sub(r'(#[0-9]+)(>|\.(?:[0-9]+(?:,[0-9]+)?)?)(#[0-9]+)(>|\.(?:[0-9]+(?:,[0-9]+)?)?)', r'\1\2\3;\3\4', criterion_string)
		return criterion_string

	def __init__(self, transformation_text, line):
		instructions = self.parse_transformation(transformation_text)
		if instructions is None:
			message = "Depedit says: error in configuration file\n"
			message += "Malformed instruction on line " + str(line) + " (instruction lines must contain exactly two tabs)\n"
			sys.stderr.write(message)
			sys.exit()
		self.definitions = instructions[0]
		self.relations = instructions[1]
		self.actions = instructions[2]
		self.line = line

	def validate(self):
		report = ""
		for definition in self.definitions:
			node = definition.def_text
			node = escape(node,"&","/")
			criteria = node.split("&")
			criteria = (_crit.replace("%%%%%","&") for _crit in criteria)
			for criterion in criteria:
				if re.match("(text|pos|cpos|lemma|morph|func|head|func2|head2|num|form|upostag|xpostag|feats|deprel|deps|misc)!?=/[^/=]*/",criterion) is None:
					if re.match(r"position!?=/(first|last|mid)/",criterion) is None:
						report+= "Invalid node definition in column 1: " + criterion
		for relation in self.relations:
			if relation == "none" and len(self.relations) == 1:
				if len(self.definitions) > 1:
					report += "Column 2 setting 'none' invalid with more than one definition in column 1"
			elif relation == "none":
				report += "Setting 'none' invalid in column 2 when multiple relations are defined"
			else:
				criteria = relation.split(";")
				for criterion in criteria:
					criterion = criterion.strip()
					#if not re.match(r"#[0-9]+((>|\.([0-9]+(,[0-9]+)?)?)#[0-9]+)+",criterion):
					if not re.match(r"(#[0-9]+((>|\.([0-9]+(,[0-9]+)?)?)#[0-9]+)+|#[0-9]+:(text|pos|cpos|lemma|morph|func|head|func2|head2|num|form|upostag|xpostag|feats|deprel|deps|misc)==#[0-9]+)", criterion):
						report += "Column 2 relation setting invalid criterion: " + criterion + "."
		for action in self.actions:
			commands = action.split(";")
			for command in commands:
				if not re.match(r"(#[0-9]+>#[0-9]+|#[0-9]+:(func|lemma|text|pos|cpos|morph|head|head2|func2|num|form|upostag|xpostag|feats|deprel|deps|misc)=[^;]*)$",command):  # Node action
					if not re.match(r"#S:[A-Za-z_]+=[A-Za-z_]+$|last$", command):  # Sentence annotation action or quit
						report += "Column 3 invalid action definition: " + command + " and the action was " + action
		return report


class DefinitionMatcher:

	def __init__(self, def_text, def_index):
		self.def_text = escape(def_text,"&","/")
		self.def_index = def_index
		self.groups = []
		self.defs = []

		def_items = self.def_text.split("&")
		for def_item in def_items:
			def_item = def_item.replace("%%%%%","&")
			criterion = def_item.split("=",1)[0]
			if criterion[-1] == "!":
				negative_criterion = True
				criterion = criterion[:-1]
			else:
				negative_criterion = False

			def_value = def_item.split("=",1)[1][1:-1]

			# Ensure regex is anchored
			if def_value[0] != "^":
				def_value = "^" + def_value
			if def_value[-1] != "$":
				def_value += "$"
			self.defs.append(Definition(criterion,def_value,negative_criterion))

	def __repr__(self):
		return "#" + str(self.def_index) + ": " + self.def_text

	def match(self, token):
		potential_groups = []
		for def_item in self.defs:
			tok_value = getattr(token,def_item.criterion)
			match_obj = def_item.match_func(def_item,tok_value)

			if match_obj is None:
				return False
			elif not match_obj:
				return False
			elif match_obj is True:
				pass
			elif match_obj is not None:
				if len(match_obj.groups()) > 0:
					potential_groups.append(match_obj.groups())
		self.groups = potential_groups
		return True


class Definition:

	def __init__(self, criterion, value, negative=False):
		# Handle conllu criterion aliases:
		if criterion == "form":
			criterion = "text"
		elif criterion == "upostag":
			criterion = "pos"
		elif criterion == "xpostag":
			criterion = "cpos"
		elif criterion == "feats":
			criterion = "morph"
		elif criterion == "deprel":
			criterion = "func"
		elif criterion == "deps":
			criterion = "head2"
		elif criterion == "misc":
			criterion = "func2"

		self.criterion = criterion
		self.value = value
		self.match_type = ""
		self.compiled_re = None
		self.negative = negative
		self.set_match_type()

	def set_match_type(self):
		value = self.value[1:-1]

		if self.value == "^.*$" and not self.negative:
			self.match_func = self.return_true
		elif re.escape(value) == value: # No regex operators within  expression
			if self.negative:
				self.match_func = self.return_exact_negative
			else:
				self.match_func = self.return_exact
			self.value = value
		else:  # regex
			self.compiled_re = re.compile(self.value)
			if self.negative:
				self.match_func = self.return_regex_negative
			else:
				self.match_func = self.return_regex

	@staticmethod
	def return_exact(definition,test_val):
		return test_val == definition.value

	@staticmethod
	def return_exact_negative(definition,test_val):
		return test_val != definition.value

	@staticmethod
	def return_regex(definition,test_val):
		return definition.compiled_re.search(test_val)

	@staticmethod
	def return_regex_negative(definition,test_val):
		return definition.compiled_re.search(test_val) is None

	@staticmethod
	def return_true(definition,test_val):
		return True


class Match:

	def __init__(self, def_index, token, groups):
		self.def_index = def_index
		self.token = token
		self.groups = groups

	def __repr__(self):
		return "#" + str(self.def_index) + ": " + self.token.__repr__


class DepEdit():

	def __init__(self, config_file=""):
		self.transformations = []
		self.user_transformation_counter = 0
		if not config_file == "":
			self.read_config_file(config_file)

	def read_config_file(self, config_file, clear_transformations = False):
		"""
		Function to read configuration file. Can be invoked after initialization.

		:param config_file: The file to read instructions from.
		:param clear_transformations: Whether to discard previous instructions.
		:return: void
		"""

		if clear_transformations:
			self.transformations = []
			self.user_transformation_counter = 0
		line_num = 0
		if isinstance(config_file, str):
			if sys.version_info[0] < 3:
				config_file = open(config_file).readlines()
			else:
				config_file = open(config_file,encoding="utf8").readlines()
		for instruction in config_file:
			line_num += 1
			if len(instruction)>0 and not instruction.startswith(";") and not instruction.startswith("#") and not instruction.strip() =="":
				self.transformations.append(Transformation(instruction, line_num))

		trans_report = ""
		for transformation in self.transformations:
			temp_report = transformation.validate()
			if temp_report != "":
				trans_report += "On line " + str(transformation.line) + ": " + temp_report +"\n"
		if len(trans_report) > 0:
			trans_report = "Depedit says: error in configuration file\n\n" + trans_report
			sys.stderr.write(trans_report)
			sys.exit()

	def process_sentence(self, conll_tokens, tokoffset, supertok_offset, transformations):
		for transformation in transformations:
			node_matches = defaultdict(list)
			for def_matcher in transformation.definitions:
				for token in conll_tokens[tokoffset+supertok_offset+1:]:
					if not token.is_super_tok:
						if def_matcher.match(token):
							node_matches[def_matcher.def_index].append(Match(def_matcher.def_index, token, def_matcher.groups))
			result_sets = []
			for relation in transformation.relations:
				found = self.matches_relation(node_matches, relation, result_sets)
				if not found:
					result_sets = []
			result_sets = self.merge_sets(result_sets,len(transformation.definitions),len(transformation.relations))
			self.add_groups(result_sets)
			if len(result_sets) > 0:
				for action in transformation.actions:
					retval = self.execute_action(result_sets, action)
					if retval == "last": # Explicit instruction to cease processing
						return self.serialize_output_tree(conll_tokens[tokoffset + supertok_offset + 1:], tokoffset)
		return self.serialize_output_tree(conll_tokens[tokoffset + supertok_offset + 1:], tokoffset)

	def matches_relation(self, node_matches, relation, result_sets):
		if len(relation) == 0:
			return
		elif "==" in relation:
			m = re.search(r':(.+)==', relation)
			operator = m.group()
			field = m.group(1)
		elif "." in relation:
			if re.match(r'.*\.[0-9]', relation):
				m = re.match(r'.*\.[0-9]*,?[0-9]*#', relation)
				operator = m.group()
				operator = operator[operator.find("."):operator.rfind("#")]
			else:
				operator = "."
		elif ">" in relation:
			operator = ">"

		matches = defaultdict(list)

		hits=0
		if relation == "none": # Unary operation on one node
			node1 = 1
			for matcher1 in node_matches[node1]:
				tok1 = matcher1.token
				hits += 1
				result = {}
				matches[node1].append(tok1)
				result[node1] = tok1
				result["rel"] = relation
				result["matchers"] = [matcher1]
				result_sets.append(result)
		elif "==" in relation:
			node1 = relation.split(operator)[0]
			node2 = relation.split(operator)[1]
			node1=int(node1.replace("#", ""))
			node2=int(node2.replace("#", ""))

			for matcher1 in node_matches[node1]:
				tok1 = matcher1.token
				for matcher2 in node_matches[node2]:
					tok2 = matcher2.token
					if self.test_relation(tok1, tok2, field):
						result_sets.append({node1: tok1, node2: tok2, "rel": relation, "matchers": [matcher1, matcher2] })
						matches[node1].append(tok1)
						matches[node2].append(tok2)
						hits += 1

			for option in [node1,node2]:
				matchers_to_remove = []
				for matcher in node_matches[option]:
					if matcher.token not in matches[option]:
						matchers_to_remove.append(matcher)
				for matcher in matchers_to_remove:
					node_matches[option].remove(matcher)
		else:
			node1 = relation.split(operator)[0]
			node2 = relation.split(operator)[1]

			node1=int(node1.replace("#", ""))
			node2=int(node2.replace("#", ""))
			for matcher1 in node_matches[node1]:
				tok1 = matcher1.token
				for matcher2 in node_matches[node2]:
					tok2 = matcher2.token
					if self.test_relation(tok1, tok2, operator):
						result_sets.append({node1: tok1, node2: tok2, "rel": relation, "matchers": [matcher1, matcher2] })
						matches[node1].append(tok1)
						matches[node2].append(tok2)
						hits += 1

			for option in [node1,node2]:
				matchers_to_remove = []
				for matcher in node_matches[option]:
					if matcher.token not in matches[option]:
						matchers_to_remove.append(matcher)
				for matcher in matchers_to_remove:
					node_matches[option].remove(matcher)

		if hits == 0:  # No solutions found for this relation
			return False
		else:
			return True

	@staticmethod
	def test_relation(node1,node2,operator):
		if operator == ".":
			if int(float(node2.id)) == int(float(node1.id))+1:
				return True
			else:
				return False
		elif operator == ">":
			if int(float(node2.head)) == int(float(node1.id)):
				return True
			else:
				return False
		elif "." in operator:
			m = re.match(r'\.([0-9]+)(,[0-9]+)?',operator)
			if len(m.groups()) > 1:
				min_dist = int(m.group(1))
				if not m.group(2) is None:
					max_dist = int(m.group(2).replace(",",""))
				else:
					max_dist = min_dist
				if max_dist >= int(float(node2.id)) - int(float(node1.id)) >= min_dist:
					return True
				else:
					return False
			else:
				dist = int(m.group(1))
				if int(float(node2.id)) - int(float(node1.id)) == dist:
					return True
				else:
					return False
		else:
			val1 = getattr(node1,operator)
			val2 = getattr(node2,operator)
			return val1 == val2


	def merge_sets(self, sets, node_count, rel_count):

		solutions = []
		bins = []
		for set_to_merge in sets:
			new_set = {}
			new_set["rels"] = []
			new_set["matchers"] = []
			for key in set_to_merge:
				if key == "rel":
					new_set["rels"].append(set_to_merge[key])
				elif key == "matchers":
					new_set["matchers"] += set_to_merge[key]
				else:
					new_set[key] = set_to_merge[key]

			for my_bin in copy(bins):
				if self.bins_compatible(new_set, my_bin):
					candidate = self.merge_bins(new_set, my_bin)
					bins.append(copy(candidate))
			bins.append(copy(new_set))

		for my_bin in bins:
			if len(my_bin) == node_count + 2:
				if len(my_bin["rels"]) == rel_count:  # All required relations have been fulfilled
					solutions.append(my_bin)
				else:  # Some node pair has multiple relations, check that all are fulfilled
					for set_to_merge in sets:
						if set_to_merge["rel"] not in my_bin["rels"]: # This relation was missing
							node_ids = list((key) for key in set_to_merge if isinstance(key,int))
							# Check that the missing rel connects nodes that are already in my_bin
							ids_are_in_bin = list((nid in my_bin) for nid in node_ids)
							if all(ids_are_in_bin):
								nodes_are_identical = list((set_to_merge[nid] == my_bin[nid]) for nid in node_ids)
								if all(nodes_are_identical):
									my_bin["rels"].append(set_to_merge["rel"])
									if len(my_bin["rels"]) == rel_count:  # Check if we now have all required relations
										solutions.append(my_bin)

		merged_bins = []
		for solution in solutions:
			self.merge_solutions(solution,merged_bins,rel_count)
		self.prune_merged_bins(merged_bins,rel_count)
		return merged_bins


	@staticmethod
	def merge_solutions(solution, merged, rel_count):
		merges_to_add = []
		if solution not in merged:
			merged.append(solution)
		if len(solution["rels"]) == rel_count:
			# This solution is completed
			pass
		else:
			# This is an incomplete solution, try to merge it into the merged solutions list
			for candidate in merged:
				if candidate != solution:
					for key in solution:
						if key != "rels" and key !="matchers":
							if key in candidate:  # This is a position, e.g. #1, that is also in the candidate in merged
								if solution[key] == candidate[key]:  # Check that they are compatible, e.g. #1 is the same token
									rels_in_candidate = (rel in candidate["rels"] for rel in solution["rels"])
									if not all(rels_in_candidate):
										rels = solution["rels"]+candidate["rels"]
										matchers = []
										for matcher in solution["matchers"]:
											matchers.append(matcher)
										for matcher in candidate["matchers"]:
											if matcher not in matchers:
												matchers.append(matcher)
										merged_solution = copy(solution)
										merged_solution.update(candidate)
										merged_solution["rels"] = rels
										merged_solution["matchers"] = matchers
										merges_to_add.append(merged_solution)
		if len(merges_to_add)>0:
			for merge_to_add in merges_to_add:
				merged.append(merge_to_add)
		solution["rels"].sort()


	@staticmethod
	def bins_compatible(bin1, bin2):
		overlap = False
		non_overlap = False
		for key in bin1:
			if key in bin2:
				if bin1[key] == bin2[key]:
					overlap = True
			if key not in bin2:
				non_overlap = True
		if overlap and non_overlap:
			return True
		else:
			return False

	@staticmethod
	def merge_bins(bin1, bin2):
		"""
		Merge bins we know are compatible, e.g. bin1 has #1+#2 and bin2 has #2+#3

		:param bin1: a bin dictionary mapping indices to tokens, a list of relations 'rels' and matcher objects 'matchers'
		:param bin2: a bin dictionary mapping indices to tokens, a list of relations 'rels' and matcher objects 'matchers'
		:return: the merged bin with data from both input bins
		"""
		for matcher in bin1["matchers"]:
			skip = False
			for matcher2 in bin2["matchers"]:
				if matcher2.def_index == matcher.def_index:
					skip = True
			if not skip:
				bin2["matchers"].append(matcher)
		for key in bin1:
			if key != "rels":
				if key not in bin2:
					out_bin = copy(bin2)
					out_bin[key]= bin1[key]
					for rel in bin1["rels"]:
						new_rel = deepcopy(rel)
						out_bin["rels"] = bin2["rels"]+[new_rel]
					return out_bin


	@staticmethod
	def prune_merged_bins(merged_bins,rel_count):
		"""
		Deletes bins with too few relationships matched after merging is complete

		:param merged_bins: candidates for bins representing complete related chains of nodes
		:param rel_count: how many relations the current transformation has - any bins with less will be discarded now
		:return: void
		"""
		bins_to_delete = []
		for merged_bin in merged_bins:
			if len(merged_bin["rels"]) < rel_count:
				bins_to_delete.append(merged_bin)
		for bin_to_delete in bins_to_delete:
			merged_bins.remove(bin_to_delete)

	@staticmethod
	def add_groups(result_sets):
		for result in result_sets:
			groups = []
			sorted_matchers = sorted(result["matchers"], key=lambda x: x.def_index)
			for matcher in sorted_matchers:
				if len(matcher.groups) > 0:
					for group in matcher.groups:
						groups.append(group[0])
			result["groups"] = groups[:]

	@staticmethod
	def execute_action(result_sets, action_list):
		actions = action_list.split(";")
		for result in result_sets:
			if len(result) > 0:
				for action in actions:
					if action == "last":
						return "last"
					elif ":" in action:  # Unary instruction
						if action.startswith("#S:"):  # Sentence annotation instruction
							key_val = action.split(":")[1]
							key, val = key_val.split("=",1)
							result[1].sentence.annotations[key] = val
						else: # node instruction
							node_position = int(action[1:action.find(":")])
							property = action[action.find(":")+1:action.find("=")]
							value = action[action.find("=")+1:].strip()
							group_num_match = re.search(r"(\$[0-9]+[LU]?)",value)
							if group_num_match is not None:
								no_dollar = group_num_match.groups(0)[0][1:]
								case = ""
								if no_dollar[-1] == "U":
									case = "upper"
									no_dollar = no_dollar[0:-1]
								elif no_dollar[-1] == "L":
									case = "lower"
									no_dollar = no_dollar[0:-1]
								group_num = int(no_dollar)
								try:
									group_value = result["groups"][group_num - 1]
									if case == "lower":
										group_value = group_value.lower()
									elif case == "upper":
										group_value = group_value.upper()
								except IndexError:
									sys.stderr.write("The action '" + action + "' refers to a missing regex bracket group '$" + str(group_num) + "'\n")
									sys.exit()
								value = re.sub(r"\$[0-9]+[LR]?",group_value,value)
							setattr(result[node_position],property,value)
					else:  # Binary instruction
						if ">" in action:  # Head relation
							operator = ">"
							node1 = int(action.split(operator)[0].replace("#", ""))
							node2 = int(action.split(operator)[1].replace("#", ""))
							tok1 = result[node1]
							tok2 = result[node2]
							if tok1 != tok2:
								tok2.head = tok1.id

	def add_transformation(self, *args, **kwargs):
		"""
		Flexible function for adding transformations to an imported DepEdit object, rather than reading them from a configuration file

		:param args: a string specifying a transformation line (three specs separated by two tabs), or a tuples of such strings
		:param kwargs: alternatively, a dictionary supplying three lists for the keys: 'nodes', 'rels', 'actions'
		:return: void
		"""

		if len(args)>0:  # Single string transformation(s)

			if not isinstance(args,list): # Make list out of input tuple
				args = list(arg for arg in args)
				if isinstance(args[0], list):  # Flatten list in case user provided a list
					args = args[0]
			for transformation_string in args:
				try:
					self.user_transformation_counter += 1
					user_line_number = "u" + str(self.user_transformation_counter)
					new_transformation = Transformation(transformation_string,user_line_number)
					self.transformations.append(new_transformation)
				except:
					raise IOError("Invalid transformation - must be a string")
		elif "nodes" in kwargs and "rels" in kwargs and "actions" in kwargs:
			if not (isinstance(kwargs["nodes"], list) and (isinstance(kwargs["nodes"], list)) and (isinstance(kwargs["nodes"], list))):
				raise IOError("Invalid transformation - must supply three lists in values for keys: nodes, rels, actions")
			else:
				node_clause = ";".join(kwargs["nodes"])
				rel_clause = ";".join(kwargs["rels"])
				actions_clause = ";".join(kwargs["actions"])
				transformation_string = "\t".join([node_clause,rel_clause,actions_clause])

				self.user_transformation_counter += 1
				user_line_number = "u" + str(self.user_transformation_counter)
				new_transformation = Transformation(transformation_string, user_line_number)
				self.transformations.append(new_transformation)

	def serialize_output_tree(self,tokens, tokoffset):
		output_tree = ""
		for tok in tokens:
			if tok.is_super_tok:
				tok_head_string = tok.head
				tok_id = tok.id
			elif tok.head == "0":
				tok_head_string = "0"
				tok_id = str(float(tok.id) - tokoffset)
			else:
				tok_head_string = str(float(tok.head)-tokoffset)
				tok_id = str(float(tok.id)-tokoffset)
			# Only keep decimal ID component for non-0 ellipsis IDs, e.g. 10.1 - those tokens have normal head '_'
			tok_id = tok_id.replace(".0","")
			tok_head_string = tok_head_string.replace(".0","")
			if "." in tok_id:
				tok_head_string = "_"
			if self.input_mode == "8col":
				output_tree += tok_id+"\t"+tok.text+"\t"+tok.lemma+"\t"+tok.pos+"\t"+tok.cpos+"\t"+tok.morph+\
								"\t"+tok_head_string+"\t"+tok.func+"\n"
			else:
				output_tree += tok_id+"\t"+tok.text+"\t"+tok.lemma+"\t"+tok.pos+"\t"+tok.cpos+"\t"+tok.morph+\
								"\t"+tok_head_string+"\t"+tok.func+"\t"+tok.head2+"\t"+tok.func2+"\n"
		return output_tree

	def make_sent_id(self, sent_id):
		return "# sent_id = " + self.docname + "-" + str(sent_id) + "\n"

	def run_depedit(self, infile, filename="file", sent_id=False, docname=False):
		children = defaultdict(list)
		child_funcs = defaultdict(list)
		conll_tokens = []
		self.input_mode = "10col"
		self.docname = filename
		tokoffset = 0
		sent_num = 0
		supertok_offset = 0
		sentlength = 0
		supertok_length = 0

		conll_tokens.append(0)
		my_output = ""
		sentence_string = ""
		current_sentence = Sentence()

		# Check if DepEdit has been fed an unsplit string programmatically
		if isinstance(infile,str):
			infile = infile.split("\n")

		for myline in infile:
			if sentlength > 0 and "\t" not in myline:
				current_sentence.length = sentlength
				conll_tokens[-1].position = "last"
				transformed = self.process_sentence(conll_tokens, tokoffset, supertok_offset, self.transformations)
				transformed = current_sentence.print_annos() + transformed
				sent_num += 1
				if sent_id:
					my_output += self.make_sent_id(sent_num)
				my_output += transformed
				sentence_string = ""
				current_sentence = Sentence()
				if sentlength > 0:
					tokoffset += sentlength
					supertok_offset += supertok_length
				sentlength = 0
				supertok_length = 0
			if myline.startswith("#"):  # Preserve comment lines
					my_output += myline.strip() + "\n"
			elif myline.strip() == "":
					my_output += "\n"
			elif myline.find("\t") > 0:  # Only process lines that contain tabs (i.e. conll tokens)
				sentence_string += myline
				cols = myline.split("\t")
				if "-" in cols[0]: # potential conllu super-token, just preserve
					super_tok = True
					tok_id = cols[0]
					head_id = cols[6]
				else:
					super_tok = False
					tok_id = str(float(cols[0]) + tokoffset)
					if cols[6] == "_":
						sys.stderr.write("DepEdit WARN: head not set for token " + tok_id + " in " + filename + "\n")
						head_id = str(0 + tokoffset)
					else:
						head_id = str(float(cols[6]) + tokoffset)
				if len(cols) > 8:
					# Collect token from line; note that head2 is parsed as a string, which is often "_" for monoplanar trees
					this_tok = ParsedToken(tok_id, cols[1], cols[2], cols[3], cols[4], cols[5],head_id, cols[7].strip(), cols[8], cols[9].strip(), cols[0], [], "mid",super_tok)
				else:  # Attempt to read as 8 column Malt input
					this_tok = ParsedToken(tok_id,cols[1],cols[2],cols[3],cols[4],cols[5],head_id,cols[7].strip(),cols[6],cols[7].strip(),cols[0],[], "mid",super_tok)
					self.input_mode = "8col"
				if cols[0] == "1" and not super_tok:
					this_tok.position = "first"
				this_tok.sentence = current_sentence
				conll_tokens.append(this_tok)
				if not super_tok:
					sentlength += 1
					children[str(float(head_id) + tokoffset)].append(tok_id)
					child_funcs[(float(head_id) + tokoffset)].append(cols[7])
				else:
					supertok_length += 1

		if sentlength > 0:  # Possible final sentence without trailing new line
			current_sentence.length = sentlength
			conll_tokens[-1].position = "last"
			transformed = self.process_sentence(conll_tokens, tokoffset, supertok_offset, self.transformations)
			transformed = current_sentence.print_annos() + transformed
			sent_num += 1
			if sent_id:
				my_output += self.make_sent_id(sent_num)
			my_output += transformed

		if docname:
			newdoc = '# newdoc id = ' + self.docname + "\n"
			my_output = newdoc + my_output

		return my_output

if __name__ == "__main__":
	depedit_version = "DepEdit V" + __version__
	parser = argparse.ArgumentParser()
	parser.add_argument('file',action="store",help="Input single file name or glob pattern to process a batch (e.g. *.conll10)")
	parser.add_argument('-c', '--config', action="store", dest="config", default="config.ini", help="Configuration file defining transformation")
	parser.add_argument('-d', '--docname', action="store_true", dest="docname", help="Begin output with # newdoc id =...")
	parser.add_argument('-s', '--sent_id', action="store_true", dest="sent_id", help="Add running sentence ID comments")
	group = parser.add_argument_group('Batch mode options')
	group.add_argument('-o', '--outdir', action="store", dest="outdir", default="", help="Output directory in batch mode")
	group.add_argument('-e', '--extension', action="store", dest="extension", default="", help="Extension for output files in batch mode")
	group.add_argument('-i', '--infix', action="store", dest="infix", default=".depedit", help="Infix to denote edited files in batch mode (default: .depedit)")
	parser.add_argument('--version', action='version', version=depedit_version)
	options = parser.parse_args()

	if options.extension.startswith("."):  # Ensure user specified extension does not include leading '.'
		options.extension = options.extension[1:]

	try:
		config_file = io_open(options.config, encoding="utf8")
	except IOError:
		sys.stderr.write("\nConfiguration file not found (specify with -c or use the default 'config.ini')\n")
		sys.exit()
	depedit = DepEdit(config_file)

	if sys.platform == "win32":  # Print \n new lines in Windows
		import os, msvcrt
		msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

	files = glob(options.file)
	for filename in files:
		infile = io_open(filename, encoding="utf8")
		basename = os.path.basename(filename)
		if options.docname or options.sent_id:
			docname = basename[:basename.rfind(".")]
		else:
			docname = filename
		output_trees = depedit.run_depedit(infile,docname, sent_id=options.sent_id, docname=options.docname)
		if len(files) == 1:
			# Single file being processed, just print to STDOUT
			if sys.version_info[0] < 3:
				print(output_trees.encode("utf-8"))
			else:
				print(output_trees)
		else:
			# Multiple files, add '.depedit' or other infix from options before extension and write to file
			if options.outdir != "":
				if not options.outdir.endswith(os.sep):
					options.outdir += os.sep
			outname = options.outdir + basename
			if "." in filename:
				extension = outname[outname.rfind(".")+1:]
				if options.extension != "":
					extension = options.extension
				outname = outname[:outname.rfind(".")]
				outname += options.infix + "." + extension
			else:
				if options.extension == "":
					outname += options.infix
				else:
					outname += options.infix + "." + options.extension
			if sys.version_info[0] < 3:
				with open(outname,'wb') as f:
					f.write(output_trees.encode("utf-8"))
			else:
				with open(outname,'w',encoding="utf8") as f:
					f.write(output_trees.encode("utf-8"))

# -*- coding: utf-8 -*-
"""
This is a simple Flask app that uses Authomatic to log users in with Facebook Twitter and OpenID.
"""

from flask import Flask, render_template, request, make_response
from authomatic.adapters import WerkzeugAdapter
from authomatic import Authomatic

from flaskconfig import CONFIG

app = Flask(__name__)

# Instantiate Authomatic.
authomatic = Authomatic(CONFIG, 'your secret string', report_errors=False)


@app.route('/')
def index():
    """
    Home handler
    """
    
    return render_template('index.html')


@app.route('/login/<provider_name>/', methods=['GET', 'POST'])
def login(provider_name):
    """
    Login handler, must accept both GET and POST to be able to use OpenID.
    """
    
    # We need response object for the WerkzeugAdapter.
    response = make_response()
    
    # Log the user in, pass it the adapter and the provider name.
    result = authomatic.login(WerkzeugAdapter(request, response), provider_name)
    
    # If there is no LoginResult object, the login procedure is still pending.
    if result:
        if result.user:
            # We need to update the user to get more info.
            result.user.update()
        
        # The rest happens inside the template.
        return render_template('login.html', result=result)
    
    # Don't forget to return the response.
    return response


# Run the app.
if __name__ == '__main__':
    app.run(host="0.0.0.0",debug=True, port=2830) 
#!/usr/bin/python
# -*- coding: utf-8 -*-


from xml.dom import minidom
from xml.dom.ext import PrettyPrint
from random import choice, shuffle
import subprocess,re
from config import *
#functions=["suj","objd","obji","objloc","attrmesu","Ã©pi","appos","mod","prÃ©p","dÃ©t","attrsuj","attrobj","racine"]

punct=u",;:!?./Â§*+=(){}[]"

debug=False
#debug==True

#sentence=u"L'Ã®le de Bermeja est une ancienne Ã®le inhabitÃ©e du golfe du Mexique, portÃ©e disparue en 1997.".strip()
#outfilename="sentence.xml"



def makexmls(text, randomdep, split=True):
	filelist=[]
	#print"oooooooooooo",split
	if split:
		text=insertAtSentenceBorders(text,"(([\?\!](?![\?\!])|((?<!\s[A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ])\.\s)|\s\|)\s*)", 
		r"\1\n","Mr Ph Dr Mme Ms".split())
		for sentence in text.split("\n") :
			#print "sentence",sentence.encode('utf-8')
			sentence=sentence.strip()
			if not sentence or sentence=="":continue
			f = ("cache/"+sentence[:50]+".xml").encode("utf-8")
			filelist+=[f]
			makexml(sentence,f,randomdep)
	else: # only one sentence (text is presplit)
		#print "*************"
		f = ("cache/"+text[:50]+".xml").encode("utf-8")
		filelist+=[f]
		makexml(text,f,randomdep)
	return filelist
		
		
		


def makexml(sentence,outfilename,randomdep):
	
	outfile=open(outfilename,"w")

	doc=minidom.Document()

	sentEl=doc.createElement("sentence")
	text=doc.createElement("text")
	text.appendChild(doc.createTextNode(sentence))
	sentEl.appendChild(text)
	doc.appendChild(sentEl)

	wordtier=doc.createElement("tier")
	wordtier.setAttribute("type","words")
	sentEl.appendChild(wordtier)

	token=""
	start,id=0,0

	def makeTokenNode(token,start,end,id):
		#print "***"+token.encode("utf-8")
		if token.lower()=="des":
			id=makeTokenNode("de",start,end,id)
			token="les"
		if token.lower()=="du":
			id=makeTokenNode("de",start,end,id)
			token="le"
		if token.lower()=="au":
			id=makeTokenNode(u"Ã ",start,end,id)
			token="le"
		if token.lower()=="aux":
			id=makeTokenNode(u"Ã ",start,end,id)
			token="les"
		if token.lower()=="duquel":
			id=makeTokenNode(u"de",start,end,id)
			token="lequel"
		if token.lower()=="auquel":
			id=makeTokenNode(u"Ã ",start,end,id)
			token="lequel"
		if token.lower()=="auxquels":
			id=makeTokenNode(u"Ã ",start,end,id)
			token="lesquels"
		if token.lower()=="auxquelles":
			id=makeTokenNode(u"Ã ",start,end,id)
			token="lesquelles"
		#print token,punct
		try:
			if token in punct:  #.decode("utf-8")
				tokenEl=doc.createElement("punct")
				tokenEl.appendChild(doc.createTextNode(token))
				tokenEl.setAttribute("start",str(start))
				tokenEl.setAttribute("end",str(end))
				#tokenEl.setAttribute("id",str(id))
				wordtier.appendChild(tokenEl)
				return id
			else:
				tokenEl=doc.createElement("word")
				tokenEl.appendChild(doc.createTextNode(token))
				tokenEl.setAttribute("start",str(start))
				tokenEl.setAttribute("end",str(end))
				tokenEl.setAttribute("id",str(id))
				#lemma=doc.createElement("lemma")
				#lemma.appendChild(doc.createTextNode(token))
				#tokenEl.appendChild(lemma)
				wordtier.appendChild(tokenEl)
				id+=1
				return id
		except:
			print "problem",token
		

	for i,ll in enumerate(sentence+" "):
		if (ll==" " or ll=="'" or ll=="," or ll=="."):
			ii=i
		
			if token=="":
				start=ii+1
				continue
			
			if ll=="'" :
				token+=ll
				ii+=1
			
			id = makeTokenNode(token,start,ii-1,id)
			
			
			if ll in punct: id = makeTokenNode(ll,ii,ii,id)
			
			token=""
			if ll=="'" :ii-=1
			start=ii+1
			
		else:	token+=ll

	ids=[]
	for n in doc.getElementsByTagName("word"):
		ids+=[n.getAttribute("id")]

	if debug:	print ids
	shuffle(ids)

	for i,n in enumerate(doc.getElementsByTagName("word")):
		if randomdep:
			govel=doc.createElement("gov")
			govel.setAttribute("id",ids[i])
			govel.setAttribute("func",choice(syntfunctions))
			n.appendChild(govel)
		#n.setAttribute("govid",ids[i])
		#n.setAttribute("func",choice(functions))
		#print unicode(n.childNodes[0].nodeValue).encode("utf-8"),"===",
		
		#s=unicode(n.childNodes[0].nodeValue).encode("utf-8")
		s=n.childNodes[0].nodeValue #).encode("utf-8")
		
		args=["./buildlexicon -d . -p lemmm consult"]
		p = subprocess.Popen(args ,stdout=subprocess.PIPE, shell=True,stdin=subprocess.PIPE, stderr=subprocess.PIPE)
		(stdoutdata, stderrdata) = p.communicate(s.encode("utf-8")+"\n")
		#print s,(stdoutdata, stderrdata),p
		if stdoutdata.strip()!="":le = stdoutdata.decode("utf-8").strip().split()[0]
		if le=="?":le=s
		#lemma=doc.createElement("lemma")
		#lemma.appendChild(doc.createTextNode(le))
		#n.appendChild(lemma)
		
		features=doc.createElement("features")
		features.setAttribute("cat","nom")
		features.setAttribute("lemma",le)
		n.appendChild(features)
		

		
	#print "_____________"	
		
		

	outfile.write(doc.toprettyxml())
	outfile.close()

def insertAtSentenceBorders(text,border,insert,abreviationList):

	abreviationsregex = re.compile("("+ "|".join(abreviationList) +")\.",re.UNICODE+re.M+re.IGNORECASE)
	borderRegex=re.compile(border,re.UNICODE+re.M+re.IGNORECASE)
	#replaceRegex=re.compile(replace,re.UNICODE+re.M+re.IGNORECASE)

	text = abreviationsregex.sub(r"\1__ABR__",text) # substitute abreviation+period => abreviation+__ABR__
	text = borderRegex.sub(insert,text) # substitute period ==> period + new line symbol
	text = re.sub("__ABR__",".",text) # substitute __ABR__ ==> period
	text = re.sub(r"[Â  \t\r\f\v\n]*\n+[Â  \t\r\f\v\n]*","\n",text,re.UNICODE+re.M) # strange: different spaces???
	return text

	#print "test"
	#doc = minidom.parse(outfilename)
	#for n in doc.getElementsByTagName("word"):print unicode(n.childNodes[0].nodeValue).encode("utf-8")
	#print doc.getElementsByTagName("ponct")
	#print doc.getElementsByTagName("text")[0].childNodes[0].nodeValue
	#PrettyPrint(doc.getElementsByTagName("text")[0])





if __name__ == "__main__":
	print "bonjour"	
	
	makexml("Nous mangions qsdf","qsdf.xml",False)
	
	#text=u"""
	#L'Ã®le de Bermeja est une ancienne Ã®le inhabitÃ©e du golfe du Mexique, portÃ©e disparue en 1997. Diverses cartes et documents historiques la situaient Ã  plus de 100 kilomÃ¨tres au nord de l'Ãtat de Yucatan, sous dÃ©pendance mexicaine.

	#Sa localisation est actuellement indÃ©terminÃ©e, tout comme son existence. Elle est donnÃ©e pour disparue, situation qui affecte la souverainetÃ© du Mexique en faveur des Ãtats-Unis, dans une rÃ©gion stratÃ©gique du Golfe du mÃªme nom, dans laquelle on prÃ©sume l'existence de rÃ©serves pÃ©troliÃ¨res.
	#Histoire [modifier]

	#Bermeja est apparue pour la premiÃ¨re fois sur les cartes comme un simple point dans la mer. Son existence fut attestÃ©e dÃ¨s 1864 sur la Carta EtnogrÃ¡fica de MÃ©xico (carte ethnographique du Mexique), ainsi que par l'ouvrage Islas mexicanas, publiÃ© par le secrÃ©tariat Ã  l'Ãducation publique, qui la situait (p. 110) Ã  22Â°33â²N 91Â°22â²W / 22.55, -91.367

	#Plusieurs agences fÃ©dÃ©rales des Ãtats-Unis, dont la CIA, ont rapportÃ© son existence [1].

	#Sa prÃ©sence gÃ©ographique s'est maintenue jusqu'en 1946 dans un ouvrage Ã©ditÃ© par le gouvernement mexicain. Mais Ã  la fin des annÃ©es 1990, aux environs de ce rÃ©cif corallien, les compagnies pÃ©troliÃ¨res dÃ©couvrent un gisement pÃ©trolier qui serait l'un des plus important au monde. Et alors que le Mexique nÃ©gociait avec les Ãtats-Unis un accord pour dÃ©limiter la frontiÃ¨re maritime entre les deux pays, Bermeja cessa d'Ãªtre visible.

	#En 1997 le ministÃ¨re mexicain de la Marine dÃ©pÃªcha un navire ocÃ©anographique pour vÃ©rifier l'existence de cette Ã®le, mais Nestor Yee Amador, le capitaine (promu depuis amiral) du navire, chargÃ© de la recherche rapporta qu'il n'avait rien trouvÃ©. L'inspection, rÃ©alisÃ©e le 5 septembre Ã  7 h, aux coordonnÃ©es indiquÃ©es plus haut par balayage hydroacoustique dans un cadre de recherche d'une superficie de 322,5 milles nautiques carrÃ©s donna des rÃ©sultats nÃ©gatifs.

	#Le 9 juin 2000, l'accord Clinton-Zedillo ne la mentionne pas; ce qui donne aux Ãtats-Unis 40% de la zone pÃ©troliÃ¨re dÃ©couverte prÃ©cÃ©demment dans cette zone.

	#En 2008, des sÃ©nateurs du Parti d'action nationale ont demandÃ© au gouvernement des Â« explications Â» sur la disparition de l'Ã®le. Selon l'un d'eux, Alberto Coppola, cette disparition aurait Ã©tÃ© provoquÃ©e (L'Ã®le aurait tout simplement Ã©tÃ© dynamitÃ©e[2]), et l'Ã®le serait toujours prÃ©sente Ã  quarante ou cinquante mÃ¨tres sous la surface de la mer[3].
	#Importance stratÃ©gique [modifier]

	#L'existence de l'Ã®le de Bermeja attribuait au Mexique un plus grand espace maritime, Ã©tendu sur environ cent milles vers le nord, que celui qui rÃ©sulta du traitÃ© Clinton-Zedillo, par lequel le Mexique et les Ãtats-Unis dÃ©finirent la frontiÃ¨re maritime dans le golfe du Mexique lors d'une cÃ©rÃ©monie cÃ©lÃ©brÃ©e Ã  Washington le 9 juin 2000.
	#Notes, sources et rÃ©fÃ©rences [modifier]

	#1. En se basant sur les ouvrages mexicains.
	#2. (fr) Mexique - A la recherche de l'Ã®le perdue, Le Point, 11 dÃ©cembre 2008, p. 24. [archive]
	#3. (fr) Bermeja, l'Ã®le disparue au large du Mexique, Le Figaro, 2 dÃ©cembre 2008, p. 7. [archive]


	#"""

	#paragraphs=text.split("\n")
	#c=0
	#for p in paragraphs:
		#if p.strip()=="" or p.strip()[-1] not in ".!?)" : continue
		#for s in p.split("."):
			#s=s.strip()
			#if s!="":
				#sentence=s+"."
				##outfilename="xml/sentence."+sentence[:30].decode("utf-8").encode('ascii', 'ignore')+".xml"
				#outfilename="xml/sentence."+str(c)+".xml"
				#c+=1
				#print "______________________________\n\n"
				#print sentence.encode("utf-8"),outfilename
				#print "______________________________\n\n"
				#makexml(sentence,outfilename)
import codecs,random,os,glob,conll, shutil

def rhapsToStandardConll():
	with codecs.open("Rhapsodie.tok.simpl","r","utf-8") as infile, codecs.open("Rhapsodie.tok.simpl.conll","w","utf-8") as outfile:
		for line in infile:
			line=line.strip()
			lili = line.split("\t")
			print len(lili)
			if len(lili)==1: outfile.write("\n")
			elif len(lili)==8:
				outfile.write(lili[0]+"\t"+lili[1]+"\t"+lili[2]+"\t"+lili[2]+"\t"+lili[3]+"\t"+lili[3]+"\t"+"_\t_\t"+lili[6]+"\t"+lili[6]+"\t"+lili[7]+"\t"+lili[7]+"\t"+"_\t_\t"+"\n")
			else:
				outfile.write(line+"\n")

def treeToConll14Text(tree):
	outtext=""
	for i in sorted(tree.keys()):
		node = tree[i]
		gov = node.get("gov",{}).items()
		govid = -1
		func = "_"
		
		if gov:
			for govid,func in gov:
				if govid!=-1:
					outtext+="\t".join([str(i),node.get("t","_"), node.get("lemma",""), node.get("lemma",""), node.get("tag","_"), node.get("tag","_"), "_","_", str(govid), str(govid),func,func,"_","_"])+"\n"
		else:
			outtext+="\t".join([str(i),node.get("t","_"), node.get("lemma",""), node.get("lemma",""), node.get("tag","_"), node.get("tag","_"), "_","_", str(govid), str(govid),func,func,"_","_"])+"\n"
	return outtext

def treeToEmptyConll14Text(tree, lemma=True):
	outtext=""
	for i in sorted(tree.keys()):
		node = tree[i]
		lem="_"
		if not lemma: lem=node.get("t","_")
		outtext+="\t".join([str(i),node.get("t","_"), node.get("t","_"), lem]+["_"]*10)+"\n"
	return outtext


def makeTrainTestSets(infolder,pattern="*conll",train="train",test="test",empty="emptytest",testsize=10, lemma=True):
	tottec,tottrc,toks=0,0,0
	with codecs.open(os.path.join(infolder, test),"w","utf-8") as testf, codecs.open(os.path.join(infolder, empty),"w","utf-8") as emptyf, codecs.open(os.path.join(infolder, train),"w","utf-8") as trainf:
		for infilename in glob.glob(os.path.join(infolder, pattern)):
			print "newconvert: looking at",infilename
			
			tec,trc=0,0
			allsentences=conll.conllFile2trees(infilename)
			print len(allsentences),"sentences"
			testselection=random.sample(range(len(allsentences)),len(allsentences)*testsize/100)
			
			for i,s in enumerate(allsentences):
				toks+=len(s)
				if i in testselection:
					testf.write(treeToConll14Text(s)+"\n")
					emptyf.write(treeToEmptyConll14Text(s,lemma)+"\n")
					tec+=1
				else:
					trainf.write(treeToConll14Text(s)+"\n")
					trc+=1
						
			print "testing with",tec,"sentences. training with",trc,"sentences"
			tottec+=tec
			tottrc+=trc
			if not lemma: shutil.copyfile(os.path.join(infolder, empty), os.path.join(infolder, empty)+"_lem")
	print "tottec,tottrc,toks=",tottec,tottrc,toks
				
def tagFuncConfig(infolder,pattern="*conll"):
	tags,funcs={},{}
	for infilename in glob.glob(os.path.join(infolder, pattern)):
		print infilename
		allsentences=conll.conllFile2trees(infilename)
		for tree in allsentences:
			for i in tree.keys():
				node = tree[i]
				gov = node.get("gov",{}).items()
				tag=node.get("tag",None)
				if tag: tags[tag]=None
				for govid,func in gov:
					 funcs[func]=None
	for tag in sorted(tags):
		print tag, '{"fill": "#69399d"}'
	for func in sorted(funcs):
		print func, '{"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}'
		
		
#makeTrainTestSets("canons")	
#tagFuncConfig("canons")
#makeTrainTestSets("newcanons/punct_prev_clause")
#makeTrainTestSets("newcanons/punct_root_p0")

"""
AD {"fill": "#69399d"}
AS {"fill": "#69399d"}
BA {"fill": "#69399d"}
CC {"fill": "#69399d"}
CD {"fill": "#69399d"}
CS {"fill": "#69399d"}
DEC {"fill": "#69399d"}
DEG {"fill": "#69399d"}
DT {"fill": "#69399d"}
ETC {"fill": "#69399d"}
IJ {"fill": "#69399d"}
JJ {"fill": "#69399d"}
LB {"fill": "#69399d"}
LC {"fill": "#69399d"}
M {"fill": "#69399d"}
MSP {"fill": "#69399d"}
NN {"fill": "#69399d"}
NR {"fill": "#69399d"}
NT {"fill": "#69399d"}
OD {"fill": "#69399d"}
P {"fill": "#69399d"}
PN {"fill": "#69399d"}
PU {"fill": "#69399d"}
SB {"fill": "#69399d"}
SP {"fill": "#69399d"}
VA {"fill": "#69399d"}
VC {"fill": "#69399d"}
VE {"fill": "#69399d"}
VV {"fill": "#69399d"}
det {"fill": "#69399d"}
CJTN {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
_ {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
advmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
amod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
asp {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
assm {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
assmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
attr {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
ba {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
cc {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
ccomp {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
clf {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
conj {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
cpm {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
csubj {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
dep {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
det {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
dobj {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
etc {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
exd {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
iobj {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
lccomp {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
lmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
lobj {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
loc {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
mmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
neg {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
nn {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
npadvmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
nsubj {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
nsubjpass {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
nummod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
obl {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
ordmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
pass {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
pccomp {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
plmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
pobj {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
prep {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
prtmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
range {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
rcmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
rcomp {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
root {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
tmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
top {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
vmod {"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
"""

if __name__ == "__main__":
	path="mate/fr/"
	try:
		os.mkdir(path)
	except OSError:
		pass

	makeTrainTestSets(path,pattern="trainingSet.2016-07-05.conll14",train="train",test="test",empty="emptytest",testsize=10)
# -*- coding: utf-8 -*-
#!/usr/bin/python
import argparse, datetime, glob, os
from mate import parsing, createNonExistingFolders
from retokenisation import retokeniser
from conll import makeEmpty

memory="40G"

def parseFile(infile, lemodel, tagmodel, parsemodel, folderpref="mate/parses/"):
	timestamp=datetime.datetime.now().strftime('%Y-%m-%d_%H:%M')
	if folderpref: prelimfolder=folderpref+"_prelim/"
	else: prelimfolder=folderpref+timestamp+"_prelim/"
	parsefile = parsing(infile=infile, lemodel=lemodel, tagmodel=tagmodel,parsemodel=parsemodel , outfolder=prelimfolder, memory=memory) # , depparse=False
	#parsefile="mate/parses/2016-09-22_01:18/Louise_Liotard_F_85_et_Jeanne_Mallet_F_75_SO-2-one-word-per-line.conll14_parse"
	print "retokenizing..."
	newname=retokeniser(parsefile, addtoout="_retok")
	print "retokenization done"
	if folderpref: outfolder=folderpref+"/"
	else: outfolder=folderpref+timestamp+"/"
	createNonExistingFolders(outfolder)
	emptyname=makeEmpty(newname, outfolder=outfolder)
	parsefile = parsing(infile=emptyname, lemodel=modeldir+args.get("lemmodel",None), tagmodel=modeldir+args.get("tagmodel",None), parsemodel=modeldir+args.get("parsemodel",None), outfolder=outfolder, memory="40G")


if __name__ == "__main__":
	
	parser = argparse.ArgumentParser(description='wrapper for mate parser with orfeo dictionaries')
	
	parser.add_argument('-ci','--conllinfile', help='file to be parsed', type=lambda s: unicode(s, 'utf8'), required=False)
	parser.add_argument('-cf','--conllfilter', help='files to be parsed', type=lambda s: unicode(s, 'utf8'), required=False)
	parser.add_argument('-md','--modeldir', help='folder containing the models', type=lambda s: unicode(s, 'utf8'), required=True)
	parser.add_argument('-lm','--lemmodel', help='lemmatizing model', type=lambda s: unicode(s, 'utf8'), required=False, nargs='?', default="LemModel")
	parser.add_argument('-tm','--tagmodel', help='tagging model', type=lambda s: unicode(s, 'utf8'), required=False, nargs='?', default="TagModel")
	parser.add_argument('-pm','--parsemodel', help='parsing model', type=lambda s: unicode(s, 'utf8'), required=False, nargs='?', default="ParseModel")

	args = vars(parser.parse_args())
	modeldir=args.get("modeldir",".")
	infile=args.get("conllinfile",None)
	conllfilter=args.get("conllfilter",None)
	lemodel=modeldir+args.get("lemmodel",None)
	tagmodel=modeldir+args.get("tagmodel",None)
	parsemodel=modeldir+args.get("parsemodel",None)
	if infile:	
		parseFile(infile, lemodel, tagmodel, parsemodel)
	elif conllfilter:
		timestamp=datetime.datetime.now().strftime('%Y-%m-%d_%H:%M')
		for infile in glob.glob(conllfilter):
			head, tail = os.path.split(infile) # put the parse output next to the infile
			parseFile(infile, lemodel, tagmodel, parsemodel, folderpref=head+"/"+timestamp)
	
	
	# python parser.py -ci mate/AParser/echantillon/Louise_Liotard_F_85_et_Jeanne_Mallet_F_75_SO-2-one-word-per-line.conll14 -lm mate/AParser/LemModel -tm mate/AParser/TagModel -pm mate/AParser/ParseModel 
	# python parser.py -ci mate/AParser/echantillon/Louise_Liotard_F_85_et_Jeanne_Mallet_F_75_SO-2-one-word-per-line.conll14 -md mate/AParser/
	# python parser.py -ci mate/AParser/echantillon/Louise_Liotard_F_85_et_Jeanne_Mallet_F_75_SO-2-one-word-per-line.conll14 -md mate/AParser/
	# python parser.py -cf "mate/AParser/echantillon/*.conll14" -md mate/AParser/
	# python parser.py -cf "mate/AParser/tcof/*.conll14" -md mate/AParser/
	
	
	
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2012 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

from xml.dom import minidom
from collections import OrderedDict	
import argparse
import difflib, codecs, glob, os

try:from nltk import Tree
except:pass
try:from treebankfiles import rhapsodie2Sentences
except:pass


#verbose=False
#verbose=True

verbose=False

def baseXml():
	############# basics
	doc=minidom.Document()
	sampleEl=doc.createElement("sample")
	sampleEl.setAttribute("style","Rhapsodie")
	doc.appendChild(sampleEl)
	text=doc.createElement("text")
	sampleEl.appendChild(text)
	pivot=doc.createElement("words")
	pivot.setAttribute("type","pivot")
	pivot.appendChild(doc.createTextNode("pivot"))
	sampleEl.appendChild(pivot)
	tokens=doc.createElement("words")
	tokens.setAttribute("type","tokens")
	#tokens.appendChild(doc.createTextNode("ici les tokens"))
	sampleEl.appendChild(tokens)
	lexemes=doc.createElement("words")
	lexemes.setAttribute("type","lexemes")
	tagset=doc.createElement("tagset")
	tagset.setAttribute("tag","cat")
	tagset.setAttribute("name","cat")
	lexemes.appendChild(tagset)
	sampleEl.appendChild(lexemes)
	dependencies=doc.createElement("dependencies")
	dependencies.setAttribute("type","syntax")
	tagset=doc.createElement("tagset")
	tagset.setAttribute("tag","func")
	tagset.setAttribute("name","syntacticfunctions")
	dependencies.appendChild(tagset)
	sampleEl.appendChild(dependencies)
	return doc,tokens,lexemes,dependencies,text
	
	

		
def addFeat2doc(feat, doc, tokenname, lemmaname, tokens, lexemes, dependencies, exportTokenName="orthotext"): # 
	"""
	is called for each dependency tree (rectional unit)
	called from 
	- export.cgi
	- xmlSentenceAdd in database.py
	
	"""
	# TODO: get rid of this orthotext stuff and replace by: the tokenname
	toks=[]
	
	#for i in feat:
		#print feat[i][tokenname].encode("utf-8"),"___",i, feat[i],"<br>"
		
	
	dnum,fnum,tnum,lnum=0,0,0,0
	for nn in dependencies.childNodes:
		if nn.nodeName=="dependency": 
			ddnum=int(nn.getAttribute("id")[3:])
			if ddnum==0: dnum+=1
			else: dnum=ddnum
			for fu in nn.childNodes:
				if fu.nodeName=="link":fnum=int(nn.getAttribute("id")[3:])
	for nn in tokens.childNodes:
		if nn.nodeName=="word": tnum=int(nn.getAttribute("id")[3:])
	for nn in lexemes.childNodes:
		if nn.nodeName=="word": lnum=int(nn.getAttribute("id")[3:])
			
			
			
	xdep = doc.createElement("dependency")	
	dependencies.appendChild(xdep)
	xdep.setAttribute("id","dep"+str(dnum))
	
	for i in feat: toks+=[feat[i][tokenname]]
	line=" ".join(toks)
	
	xdep.appendChild(doc.createComment(line))
	lexemes.appendChild(doc.createComment(line))
	
	count=0
	############ add lexemes
	for i in feat:
		count+=1
		word = feat[i]
		word["lexid"]="lex"+str(lnum+count)
		
	count=0	
	#lastokenid=None
	tokid=""
	
	for i in feat:
		count+=1

		word = feat[i]
		#print "___",word,"<br>"
		########### making token nodes
		#if word["tokenid"]==lastokenid:
			#tnum-=1
		#else:
		xtok=doc.createElement("word")
		tokens.appendChild(xtok)
		xtok.setAttribute("text",word.get(tokenname,"!undefined!"))
		
		xtref=doc.createElement("ref")
		xtok.appendChild(xtref)
		xtref.setAttribute("idref","pivX")
		tokid="tok"+str(tnum+count)
		xtok.setAttribute("id",tokid)	
			#lastokenid=word["tokenid"]
		#print word,"<br>"
		############ making lexeme nodes
		xword=doc.createElement("word")
		lexemes.appendChild(xword)
		xword.setAttribute(exportTokenName,word.get(tokenname,"!undefined!"))
		xword.setAttribute(lemmaname,word.get(lemmaname,"!undefined!"))
		xword.setAttribute("id","lex"+str(lnum+count))		
		
		xref=doc.createElement("ref")
		xword.appendChild(xref)
		xref.setAttribute("idref",tokid)
		
		xfeatures=doc.createElement("features")
		xword.appendChild(xfeatures)
		for attr in sorted(word.keys()):
			if attr == "gov":
				for g in word.get("gov",{}):
					
					xlink = doc.createElement("link")
					xdep.appendChild(xlink)
					fnum+=1
					xlink.setAttribute("id","func"+str(fnum))
				
					
				#g = word["gov"].keys()[0]
					#if  g >= 0 : 
						#if word["gov"].keys()[0] in feat:
							#xlink.setAttribute("govid",feat[g]["lexid"])
					if g in feat:
						xlink.setAttribute("govid",feat[g]["lexid"])
					xlink.setAttribute("depid",str(word["lexid"]))
					xlink.setAttribute("func",str(word["gov"][g]))
			elif attr not in [tokenname,lemmaname,"cluster","tokenid","idnum"]:
				xfeatures.setAttribute(attr,unicode(word[attr]))
	return line
	#lnum+=count
	#tnum+=count
	


	
	
	
	
	
	####################### alignment
	
	

	

def brandNewXml():
	############# basics
	doc=minidom.Document()
	sampleEl=doc.createElement("sample")
	sampleEl.setAttribute("style","Rhapsodie")
	doc.appendChild(sampleEl)
	
	return doc
	
	
def addinfototree(tree,deletePara=True):
	rootindeces=[]
		
		
	for i,node in tree.items(): # getting some basic additional features: children and govcats
		tree[i]["children"]=tree[i].get("children",[])
		tree[i]["govcats"]={}
		
		if "junc" in node["gov"].values() and len(node["gov"].values())>1:
			for j,func in node["gov"].items():
				#if deletePara and func.startswith("para"):
					#del node["gov"][j]
				if func!="junc":
					del node["gov"][j]
		
		for j,func in node["gov"].items():
			#print func,func.endswith("_inherited")
			if func.endswith("_inherited"):
				# if the node has other govs than the inherited, ignore the inherited link
				if [dep for g,dep in node["gov"].iteritems() if not dep.endswith("_inherited")]:
					continue
			if func=="attention":
				# if the node has other govs than the attention, ignore the attention link
				if [dep for g,dep in node["gov"].iteritems() if dep!="attention"]:
					continue
			if j in tree: # in tree: has governor
				tree[j]["children"]=tree[j].get("children",[])+[i]
				tree[i]["govcats"][tree[j]["cat"]]=tree[i]["govcats"].get(tree[j]["cat"],[])+[ (j,func) ]
			else: # not in tree: roots
				if [dep for g,dep in node["gov"].iteritems() if not dep.endswith("_inherited") and g!=j and dep!="attention"]:
					continue # we have other governors than the root node and inherited govs
				rootindeces+=[i]
	return rootindeces

def makeConstNode(i,node,constDic, rootindeces,tree):
	if node["cat"] in constDic:
		
		if node["cat"]=="V":
			if node.get("mode","")=="infinitive":
				ntree=Tree("(VP (V "+node["orthotext"]+"))")
			elif "V" in node["govcats"]:
				j,func=node["govcats"]["V"][0]
				if func.startswith("para"): # copy governor's const
					gov=tree[j]
					if "const" not in gov:
						makeConstNode(j,gov,constDic, rootindeces,tree)	
					if unicode(gov["const"])[1]=="V": # governor's const is VP
						ntree=Tree("(VP (V "+node["orthotext"]+"))")
					else:	# governor's const is S
						ntree=Tree("(S (V "+node["orthotext"]+"))")
				else: # verb depends on verb
					ntree=Tree("(VP (V "+node["orthotext"]+"))")			
			else:
				ntree=Tree("(S (V "+node["orthotext"]+"))")
		else:
			ntree=constDic[node["cat"]].copy(True)
			ntree.insert(0,Tree("("+node["cat"]+" "+node["orthotext"]+")"))
	else:
		if node["children"] or i in rootindeces or node["cat"] in node["govcats"]:
			ntree=Tree("("+node["cat"]+"P ("+node["cat"]+" "+node["orthotext"]+"))")
		else:
			ntree=Tree("("+node["cat"]+" "+node["orthotext"]+")")
			
	ntree.depindex=i
	ntree.function=unicode(node["gov"].items()[0][1])
	
	node["const"]=ntree
	for t in list(ntree.subtrees(lambda t: t.height() == 2)):
		t.depindex=i
		t.lexid=node["lexid"]
	
def phraseStructure(xmlfile, doc=None):
	
	
	if verbose:print "making phrase structure"
	
	if not doc:	doc=brandNewXml()
	sample=doc.getElementsByTagName("sample")[0]
	phraseStrus=doc.createElement("constrees")
	phraseStrus.setAttribute("type","phraseStructure")
	sample.appendChild(phraseStrus)
	
	
	constDic={"V":Tree("(S)"),"N":Tree("(NP)"),"I":Tree("(IP)"),"Adj":Tree("(AP)"),"Pre":Tree("(PP)"),"CS":Tree("(CP)") ,"Pro":Tree("(NP)")   }
	#left in automatic mode: D "Adv":Tree("(AdvP)"), "Cl":Tree("(NP)"),
	sentences, fs = rhapsodie2Sentences(xmlfile)
	
	phrasestructurecounter=0
	#print sentences[0]
	
	allConstTrees=[]
	#print len(sentences)		
	for treeindex,tree in enumerate(sentences): # [:33] [19:20]
		
		
		if verbose:
			print "_____________",treeindex

		
		rootindeces=addinfototree(tree)
		
		
		
		
		
		for i,node in tree.items(): # creating the actual phrase structure
			#if node.get("mode",""): print "mode",node.get("mode","")
			#print i, node
			
			makeConstNode(i,node,constDic,rootindeces,tree)
			

			
				
			#print "____",unicode(ntree)
	
		alreadyconst=[]
		for i,node in tree.items(): # putting the const trees for each node together
			children=sorted(node["children"])

			for j,chi in enumerate(children):
				
				if chi in alreadyconst:
					continue
				
				if chi<i:
					node["const"].insert(j,tree[chi]["const"])
					alreadyconst+=[chi]
				elif chi>i: # necessary to kick out autochildren
					node["const"].insert(len(node["const"]),tree[chi]["const"])
					alreadyconst+=[chi]
			#print "===",unicode(node["const"])
				
	
		#for i,n in tree.items():
			#print i,n["orthotext"],n
			
		nodesshown=[]	
		for i in rootindeces: # output
			if i in nodesshown:continue
			ctree=tree[i]["const"]
			allConstTrees+=[ctree]
			
			#for t in ctree.subtrees(lambda t: t.height() == 2):
				#print unicode(t)
			
			nodesshown += [t.depindex for t in list(ctree.subtrees(lambda t: t.height() == 2))]
			
			#for pro in ctree.productions():
				#print pro
			
			phraseStru=doc.createElement("constree")
			phraseStru.setAttribute("id","phrasestruct"+str(phrasestructurecounter))
			phraseStru.appendChild(doc.createComment(" ".join(list(ctree.leaves()))))
			phraseStrus.appendChild(phraseStru)
			
			root=doc.createElement("const")
			root.setAttribute("ctype","phrase")
			root.setAttribute("cat",unicode(ctree.node))			
			root.setAttribute("func","root")
			phraseStru.appendChild(root)
			
			for child in ctree:
				traverse(child,doc,root)
			#print "vvv",doc.toprettyxml()
			#ctree.draw()
			phrasestructurecounter+=1

			#print 
			print "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii",treeindex,i
			
			if (treeindex,i)>=(118,0):
				if ctree.height()>3:
					
					print "drawn"
					print "height",ctree.height()
					ctree.draw()

		#print doc.toprettyxml()
	return allConstTrees		
			
def traverse(t,doc, lastconst, plexid=None):
	#print unicode(doc.toprettyxml())[:333],"uuu",plexid
	try:
		t.node
	except AttributeError:
		#print unicode(t)
		
		const=doc.createElement("const")
		const.setAttribute("ctype","lexeme")
		#const.setAttribute("cat","lexeme")
		#const.setAttribute("shape","empty")
		#const.setAttribute("localreg","empty")
		#const.setAttribute("function","?")
		const.setAttribute("func","head")
		const.setAttribute("idref",plexid)
		lastconst.appendChild(const)
	else:
		# Now we know that t.node is defined
		#print '(', unicode(t.node),t.function
		const=doc.createElement("const")
		const.setAttribute("ctype","phrase")
		const.setAttribute("cat",unicode(t.node))
		const.setAttribute("shape","empty")
		const.setAttribute("localreg","empty")
		
		try: const.setAttribute("func",t.function)
		except: const.setAttribute("func","head")		
		
		lastconst.appendChild(const)
		try: 	plexid=t.lexid
		except: 	plexid=None
		if verbose : print "lexid",unicode(t),plexid
		for child in t:
			traverse(child,doc,const,plexid)
		#print ')',	
	
	

def joinRead(xmlfilename):
	data=open(xmlfilename,"r").read()
	#print data[:100]
	lines=[li.strip() for li in data.split('\n')]
	data=(lines[0]+'\n<!DOCTYPE word [<!ATTLIST word id ID #IMPLIED>]>\n'+"\n".join(lines[1:])).replace("\n","")
	#print data,1/0
	#doc = minidom.parseString('<!DOCTYPE word [<!ATTLIST word id ID #IMPLIED>]>'+data)
	#doc = minidom.parseString('<!DOCTYPE div [<!ATTLIST div id ID #IMPLIED>]><div><div id="foo">FOO word</div><div id="bar">BAR word</div></div>')
	#print data[:100]
	doc = minidom.parseString(data)
	#doc=minidom.parse(xmlfilename)
	pivs, tokens, lexemes, deps, nodedics = OrderedDict(),OrderedDict(),OrderedDict(),OrderedDict(),OrderedDict()
	wtypedic={"pivot":pivs,"tokens":tokens,"lexemes":lexemes}
	dtypedic={"syntax":deps}
	
	# all dictionaries look like this: u'lex565': (<DOM Element: word at 0xb617e2cc>, 566)
	for wordgroup in doc.getElementsByTagName("words"):# looking at all the wordgroups
		currentdic = wtypedic.get(wordgroup.getAttribute("type"),None) # if they are mentioned in the wtypedic, read them in
		if currentdic=={}:
			#order=0
			for w in wordgroup.getElementsByTagName("word"):
				#order+=1
				currentdic[w.getAttribute("id")]=w
				#print type(w),w.toprettyxml(indent="     ")
	# each of the pivs, tokens, lexemes, ... sends: lexid ==> the xmldoc node 
	return {"pivs":pivs, "tokens":tokens, "lexemes":lexemes},doc,[l.getAttribute("orthotext") for lexid,l in lexemes.items()],[lexid for lexid,l in lexemes.items()]

	
def align(old, new, olddoc,newdoc, olid, newlexid, oldlex,newlex, newtokens, tokcounter,oldidtoktoidpiv):
	
	#if oldlex!=newlex:
	#print newlexid,olid,"=========== aligning",oldlex,"==>",newlex
	
	
	
	
	#print new["lexemes"][newlexid].toprettyxml()
	
	oldxlex=old["lexemes"][olid] # xml node
	oldtokid=oldxlex.getElementsByTagName("ref")[0].getAttribute("idref") # id
	#print oldtokid
	oldxtok=old["tokens"][oldtokid] # xml node

	#xpivrefs=oldxtok.getElementsByTagName("ref") # list of xml nodes: pivot references in old token
	pivrefs=oldidtoktoidpiv.get(oldtokid,[]) # list of xml nodes: pivot references in old token
	#print "uu",oldxtok.toprettyxml()
	#for xpr in xpivrefs:
		#print xpr.toprettyxml()
	#print "oldxtok",oldxtok.toprettyxml()
	
	
	#print oldlexes[o1:o2][i],newlex
	#print "oldlexids",oldlexids[o1:o2][i]
	#print "newlexid",newlexid
	#print oldxlex.getAttribute("start"),oldxlex.getAttribute("end")
	#print type(new["lexemes"][newlexids[n1:n2][i]]),1/0
	new["lexemes"][newlexid].setAttribute("start",oldxlex.getAttribute("start")) # copy start/end attributes
	new["lexemes"][newlexid].setAttribute("end",oldxlex.getAttribute("end"))
	
	
	#print "--"
	
	#print new["lexemes"][newlexids[n1:n2][i]].toprettyxml(indent="     ")
	#1/0
	tokstr=[]
	
	for j,tok in enumerate(newlex.split()): # for each token (separated by space)
		
		#create good and new toks:
		xtok=newdoc.createElement("word") # create new token element
		xtok.setAttribute("id","tok"+str(tokcounter))
		#if len(xpivrefs)>j: # if there is a ref to the pivots (some nodes have no pivot (esperluette))
		if len(pivrefs)>j: # if there is a ref to the pivots (some nodes have no pivot (esperluette))
		
			#xtok.setAttribute("start",olddoc.getElementById(xpivrefs[j].getAttribute("idref")).getAttribute("xmin"))
			xtok.setAttribute("start",olddoc.getElementById(pivrefs[j]).getAttribute("xmin"))
			xtok.setAttribute("end",olddoc.getElementById(pivrefs[j]).getAttribute("xmax"))
			#xtok.setAttribute("orthotext",oldxtok.getAttribute("orthotext")) # problem: sometimes not separated: eg "pas du tout"
			xtok.setAttribute("orthotext",tok)
			tokstr+=[tok]
			#print "oooo",xpivrefs[j].getAttribute("idref"),olddoc.getElementById(xpivrefs[j].getAttribute("idref"))
			xref=newdoc.createElement("ref") # create a ref child node
			xref.setAttribute("idref",pivrefs[j]) # copy the ref to the pivot
			xtok.appendChild(xref) # hang them in
			
			#oldxtok.removeChild(xpivrefs[j])
		newtokens.appendChild(xtok)
		#print "xtok",xtok.toprettyxml()
		
		xref=newdoc.createElement("ref") # create a ref child node
		xref.setAttribute("idref","tok"+str(tokcounter)) # put the ref to the new token
		new["lexemes"][newlexid].appendChild(xref) # hang them in
		new["lexemes"][newlexid].getElementsByTagName("features")[0].setAttribute("token"," ".join(tokstr))
		tokcounter+=1
	oldidtoktoidpiv[oldtokid]=pivrefs[len(newlex.split()):] # fucking difficult: kick out the already used pivots...
	#print new["lexemes"][newlexid].toprettyxml()
	#if oldlex==u"est-Ã -dire":1/0
	return tokcounter

	
def correctfeatures(e, lexidtonode):
	features = e.getElementsByTagName("features")[0]
	try: node = lexidtonode[features.getAttribute("lexid")]
	except: return # happens for crazy nodes like lex191 that don't appear in any tree
	cat = features.getAttribute("cat")
	orthotext = e.getAttribute("orthotext")
	token = features.getAttribute("token") # the crazy token feature
	
	lemma = e.getAttribute("lemma")
	#ajouter mode="indicative" et tense="present" pour tous les V qui n'ont pas de trait mode instanciÃ©
	if cat=="V" and not features.hasAttribute("mode"):
		features.setAttribute("mode","indicative")
		features.setAttribute("tense","present")
		
	#faire mode="infinitive" pour tous les V dont la forme se termine par er, ir, oir
	if cat=="V" and orthotext[-2:]=="er" or orthotext[-2:]=="ir" or orthotext[-3:]=="oir" or orthotext==u"rÃ©pondre" or orthotext=="dire" or orthotext=="reprendre" or orthotext=="prendre" or orthotext=="conduire" :	
		features.setAttribute("mode","infinitive")
	
	#faire mode="infinitive" pour tous les V dont le lemme et la forme sont identique et se termine par re
	if cat=="V" and lemma==orthotext and lemma[-2:]=="re":
		features.setAttribute("mode","infinitive")
	
	#changer mode= "gerundive" en mode="present_participle"
	if cat=="V" and features.getAttribute("mode")=="gerundive":
		features.setAttribute("mode","present_participle")
	
	#print e.toprettyxml()
	
	# faire mode="past_participle" et supprimer le trait tense pour tous les V qui dÃ©pendent d'un lemme="avoir" ou lemme="Ãªtre" par une relation pred
	# kim: changÃ© en : si verbe et dÃ©pend d'un verbe par pred
	if cat=="V" and "V" in node["govcats"] and "pred" in node["gov"].values():
		if features.hasAttribute("tense") : features.removeAttribute("tense")
		features.setAttribute("mode","present_participle")
	
	#faire mode="past_participle" et supprimer le trait tense pour tous les V dont la forme se termine par Ã©, i, u, Ã©e, Ã©s, Ã©es (on fait pas ie, ies, is parce que ca peut Ã©tre d'autres formes)
	if cat=="V" and (orthotext[-1:]==u"Ã©" or orthotext[-1:]==u"i" or orthotext[-1:]==u"u" or orthotext[-2:]==u"Ã©e" or orthotext[-2:]==u"Ã©s" or orthotext[-3:]==u"Ã©es"):
		if features.hasAttribute("tense") : features.removeAttribute("tense")
		features.setAttribute("mode","past_participle")
	
	#faire mode="present_participle" et supprimer le trait tense pour tous les V dont la forme se termine par ant
	if cat=="V" and (orthotext[-3:]==u"ant"):
		if features.hasAttribute("tense") : features.removeAttribute("tense")
		features.setAttribute("mode","present_participle")
	
	
	#2) il y a aussi qq lemmes Ã  changer
	#faire lemme="Ã§a" quand forme="Ã§a"
	if orthotext==u"Ã§a":
		e.setAttribute("lemma",u"Ã§a")
		
	#et faire forme="n'" quand token="n'" (IMPORTANT)
	if orthotext==u"Ã§a":
		e.setAttribute("lemma",u"Ã§a")
	
	
	#faire forme="qu' " quand token="qu' "
	if token=="qu' " or token=="qu'":
		e.setAttribute("orthotext","qu' ")
	
	
	
	if cat=="V" and features.getAttribute("mode")=="conditional":
		features.setAttribute("mode","indicative")
		features.setAttribute("tense","conditional")
	
	#Pour les V il faudrait le faire une fois maintenant et une fois que tout aura Ã©tÃ© corrigÃ© (avant l'export) ????
	

	#3) supprimer les traits qui servent Ã  rien :
	dumbfeatures="""
	token
	que
	aux
	aux_req
	attention
	attentionFeature
	Vadj
	countable
	def
	dem
	det
	poss
	numberposs
	index
	wh
	control
	extraction
	neg
	sat
	enum
	hum
	nb
	sat
	time
	inv
	width
	features
	pcas
	real
	position""".strip().split()
	for df in dumbfeatures:
		if features.hasAttribute(df) : features.removeAttribute(df)
		
	#si catâ V supprimer trait mode
	if cat!="V" :
		if features.hasAttribute("mode") : features.removeAttribute("mode")
	#pour les V
	else:
		#si modeâ past_particple|present_participle supprimer trait gender
		if features.getAttribute("mode")!="past_particple":
			if features.hasAttribute("gender") : features.removeAttribute("gender")
		#si modeâ  indicative supprimer trait tense, person
		if features.getAttribute("mode")!="indicative":
			if features.hasAttribute("tense") : features.removeAttribute("tense")
			if features.hasAttribute("person") : features.removeAttribute("person")
		#si mode= infinitive supprimer trait numberposs
		if features.getAttribute("mode")=="infinitive":
			if features.hasAttribute("numberposs") : features.removeAttribute("numberposs")
	

	if cat=="CS" or cat=="I":
		if features.hasAttribute("gender") : features.removeAttribute("gender")
		if features.hasAttribute("number") : features.removeAttribute("number")
		if features.hasAttribute("person") : features.removeAttribute("person")
		if features.hasAttribute("tense") : features.removeAttribute("tense")
	if cat!="V":
		if features.hasAttribute("tense") : features.removeAttribute("tense")
		
	
	if lemma and lemma[0]=="-": lemma=lemma[1:]
	
	if cat=="Adj" and not features.hasAttribute("gender"): features.setAttribute("gender","masc/fem")




	
	
def joinRhapsodies(goodfilename,oldfilename,newcombinedname):
	from treebankfiles import rhapsodie2Sentences
	if verbose: 
		print "reading the files"
		print "oldfilename",oldfilename
		print "goodfilename",goodfilename
	old,olddoc,oldlexes,oldlexids=joinRead(oldfilename) # contains time
	dumbolddoc = minidom.parse(oldfilename) # only for markup
	
	new,newdoc,newlexes,newlexids=joinRead(goodfilename)# good for deps!
	if verbose: print "finished reading the files"
	lexidtonode={}
	sentences, fs = rhapsodie2Sentences(goodfilename)
	for s in sentences:
		rootindeces=addinfototree(s)
		for i,node in s.items():
			lexidtonode[node["lexid"]]=node	
	
	#for i in sorted(lexidtonode): print i,lexidtonode[i]
	brandnewdoc=brandNewXml()
	newtokens=brandnewdoc.createElement("words")
	newtokens.setAttribute("type","tokens")
	
	
	newoldlexid,oldnewlexid={},{}
	s = difflib.SequenceMatcher(None, newlexes, oldlexes)
	
	tokcounter=0
	
	
	for e in new["lexemes"].values():
		for refchild in e.getElementsByTagName('ref'): # old refs out
			e.removeChild(refchild)
		correctfeatures(e,lexidtonode)
	
	oldidtoktoidpiv={}
	for oid,oxtok in old["tokens"].items():
		
		for xr in oxtok.getElementsByTagName("ref"):
			oldidtoktoidpiv[oid]=oldidtoktoidpiv.get(oid,[])+[xr.getAttribute("idref")]
			
	#print oldidtoktoidpiv
	
	
	for info,n1,n2,o1,o2 in s.get_opcodes():
		if verbose:print "\n_____________",info,"_________________ new:",n1,n2,"old:",o1,o2
		if info=="equal":
			
			for i,olid in enumerate(oldlexids[o1:o2]):
				newlexid=newlexids[n1:n2][i] #id
				newlex=newlexes[n1:n2][i] # str
				oldlex=oldlexes[o1:o2][i]
				
				newoldlexid[newlexid]=olid # put in dico
				oldnewlexid[olid]=newlexid # put in dico
				
				tokcounter=align(old, new, olddoc,newdoc, olid, newlexid, oldlex,newlex, newtokens, tokcounter, oldidtoktoidpiv)
				
				
		elif info=="replace" or info=="insert" or info=="delete":
			
			totn=float(len(" ".join(newlexes[n1:n2]))) # complete length of the new lexemes
			toto=float(len(" ".join(oldlexes[o1:o2]))) # complete length of the old lexemes
			
			#print "matching",toto,totn
			if verbose: print "newlexes",", ".join(newlexes[n1:n2])
			#print newlexids[n1:n2]
			if verbose: print "oldlexes",", ".join(oldlexes[o1:o2])
			#print oldlexids[o1:o2]
			
			lastlen=0
			nperc=[] # contains the fractions of each lexemes (0 < x < 1=
			for t in newlexes[n1:n2]:
				nperc+=[(len(t)+lastlen)/totn]
				lastlen+=len(t)+1
			lastlen=0
			operc=[] # contains the fractions of each lexemes (0 < x < 1=
			for t in oldlexes[o1:o2]:
				operc+=[(len(t)+lastlen)/toto]
				lastlen+=len(t)+1
			
			#print "nperc",nperc
			#print "operc",operc
			coperc=operc[:] # copy of operc
			#print 
			#print [len(t)/toto for t in newlexes[n1:n2]]
			#print [len(t)/totn for t in oldlexes[o1:o2]]
			aligndic={} # hashtable that sends the corresponding fractions of new lexemes to the old ones
			lasto=None
			for i,p in enumerate(nperc):
				#print "p",p
				while operc and operc[0]<=p:
					#print "operc[0]<=p",operc[0],p
					lasto=operc.pop(0)
					aligndic[p]=aligndic.get(p,[])+[lasto]
					#print aligndic
				
				if operc:
					#print "operc[0]>p",operc[0],p,lasto
					
					#print abs(operc[0]-p),abs(operc[0]-nperc[i+1])
					if abs(operc[0]-p)<abs(operc[0]-nperc[i+1]):
						#print "next one still better comes to me!"
						lasto=operc.pop(0)
						aligndic[p]=aligndic.get(p,[])+[lasto]
					
					#print lasto,abs(lasto-p),abs(operc[0]-p)
					if lasto in aligndic.get(p,[]): continue # case i just found smaller ones to aligndic to
					#print "got to aligndic up to the next one"
					aligndic[p]=aligndic.get(p,[])+[operc[0]]
					#elif abs(lasto-p)<abs(operc[0]-p):
						#print "got to aligndic down to the last one"
						#aligndic[p]=aligndic.get(p,[])+[lasto]
					
			if verbose:print aligndic
			for p,opli in sorted(aligndic.items()):
				for op in opli:
					
					
					
					olid=oldlexids[o1:o2][coperc.index(op)]
					newlex=newlexes[n1:n2][nperc.index(p)]
					oldlex=oldlexes[o1:o2][coperc.index(op)]
					newlexid=newlexids[n1:n2][nperc.index(p)] #id
					
					newoldlexid[newlexid]=olid # put in dico
					oldnewlexid[olid]=newlexid # put in dico
					#print "aligning",newlex,oldlex
					tokcounter=align(old, new, olddoc,newdoc, olid, newlexid, oldlex,newlex, newtokens, tokcounter, oldidtoktoidpiv)
					
				
			
		else: 
			print info,n1,n2,o1,o2
			1/0
			
				
		#print "_______________"
		
		
	if verbose:print "_________________________"
	
	
	
	for c in olddoc.getElementsByTagName("const"):
		if c.getAttribute("ctype")=="lexeme":
			#print c.getAttribute("idref")
			if c.getAttribute("idref") in oldnewlexid:
				
				c.setAttribute( "idref", oldnewlexid[c.getAttribute("idref")] )
			else:
				c.parentNode.removeChild(c)
	
	
	
	
	
	#sample=brandnewdoc.getElementsByTagName("sample")[0]
	#for e in  sample.getElementsByTagName("words"):
		#print e.getAttribute("type")
	#print brandnewdoc.getElementsByTagName("words")
	#print
	sample=brandnewdoc.getElementsByTagName("sample")[0] # the principal node
	
	
	
	
	for e in newdoc.getElementsByTagName("words"):# looking at all the wordgroups
		if e.getAttribute("type")=="lexemes":
			nxlexemes = e
			break
	for e in newdoc.getElementsByTagName("dependencies"):# looking at all the wordgroups
		if e.getAttribute("type")=="syntax":
			nxdependencies = e
			break		
	for e in olddoc.getElementsByTagName("words"):# looking at all the wordgroups
		if e.getAttribute("type")=="pivot":
			nxpivot = e
			break
	for e in olddoc.getElementsByTagName("constrees"):# looking at all the wordgroups
		if e.getAttribute("type")=="pile_tree":
			nxpiles = e
		#if e.getAttribute("type")=="topology_tree":
		################################################### !!!!!!!!!!!!!!!!!!!!!!!
		if e.getAttribute("type")=="macrosyntax_tree":
			nxtopos = e
					
	bxlexemes = brandnewdoc.importNode(nxlexemes,  True)
	bnxdependencies = brandnewdoc.importNode(nxdependencies,  True)
	
	#markup = (olddoc.getElementsByTagName("markup_text")[0]).firstChild.data.split
	xtext=brandnewdoc.importNode(dumbolddoc.getElementsByTagName("markup_text")[0],True)
	
	# putting it all together:
	sample.appendChild(xtext)
	sample.appendChild(nxpivot)
	sample.appendChild(newtokens)
	sample.appendChild(bxlexemes)
	sample.appendChild(bnxdependencies)
	sample.appendChild(nxpiles)
	sample.appendChild(nxtopos)
	
	#allCats,allFuncs,catdeps,depcats=freqs(goodfilename,allCats,allFuncs,catdeps,depcats)
	#1/0
	allConstTrees=phraseStructure(goodfilename, brandnewdoc)
	#print sample.toprettyxml()
	
	xmlrhaps=codecs.open(newcombinedname, "w","utf-8")
	#text.appendChild(doc.createTextNode(alltext))
	#PrettyPrint(doc,xmlrhaps)
	texte= brandnewdoc.toprettyxml() #indent="     "
	xmlrhaps.write(texte)
	
	xmlrhaps.close()
	return allConstTrees
	
	

def idioticdepnumbers(infile,outfile):
	print infile,outfile
	inf=codecs.open(infile, "r","utf-8")
	outf=codecs.open(outfile, "w","utf-8")
	depcount=0
	for line in inf:
		if '<dependency id="dep' in line:
			#print "ooooooooooooo",line
			line=line.replace("dep0","dep"+str(depcount))
			line=line.replace("dep1","dep"+str(depcount))
			depcount+=1
		outf.write(line)
	inf.close()
	outf.close()




def freqs(xmlfile,freqsdics):
	from treebankfiles import rhapsodie2Sentences
	print "freqs"
	
	doc=brandNewXml()
	sample=doc.getElementsByTagName("sample")[0]
	phraseStrus=doc.createElement("constrees")
	phraseStrus.setAttribute("type","phraseStructure")
	sample.appendChild(phraseStrus)
	
	sentences, fs = rhapsodie2Sentences(xmlfile)
	
	filecode=xmlfile.split("Rhap")[1].split("Synt")[0][1:-1]
	
	
	
	
	for treeindex,tree in enumerate(sentences):
		#rootindeces=addinfototree(tree, False)
		
		
		
		for i,node in tree.items():
			
			freqsdics[0][node["cat"]]=freqsdics[0].get(node["cat"],[])+[(filecode,treeindex)]
			for f in node["gov"].values():
				freqsdics[1][f]=freqsdics[1].get(f,[])+[(filecode,treeindex)]
			for gi,f in node["gov"].iteritems():
				if gi:
					freqsdics[2][ "-".join([tree[gi]["cat"],f ]) ]=freqsdics[2].get( "-".join([tree[gi]["cat"],f ]), [])+[(filecode,treeindex)]
					freqsdics[4][ "-".join([tree[gi]["cat"],f, node["cat"]]) ]=freqsdics[4].get( "-".join([tree[gi]["cat"],f , node["cat"]]), [])+[(filecode,treeindex)]
				freqsdics[3][ "-".join([f,node["cat"]]) ]=freqsdics[3].get( "-".join([f,node["cat"]]) , [])+[(filecode,treeindex)]
				
	return freqsdics

def freqsout(freqsdics,freqtypes):
	
	files=[codecs.open(fn+".csv","w","utf-8") for fn in freqtypes]
	
		
	for i,dic in enumerate(freqsdics):
			for l,c in sorted([ (len(freqsdics[i][cc]),cc) for cc in freqsdics[i]]): # , reverse=True
				#print c,l,",".join()
				files[i].write("\t".join((c,str(l)))+"\n")
				files[i].write(", ".join([fil+":"+str(nb) for fil, nb in freqsdics[i][c]])+"\n")
				
	print "frequencies written to"," ".join(freqtypes)	
	

def conll2phrase(conllinfilename, phrasestructureoutname):
	pass

	
if __name__ == "__main__":
	
	parser = argparse.ArgumentParser(description='Combine Rhapsodie XML files.')
	parser.add_argument('oldfile', type=str,  help='The XML file before manual correction in the Arborator, containing the correct pivots and constituent trees')
	parser.add_argument('goodfile', type=str,  help='The XML file exported from the Arborator, containing the correct lexemes and dependencies')
	parser.add_argument('outfile', type=str,  help='The XML file that will be written, containing the combination and correction of the two other files')
	parser.add_argument('--verbose', '-v', action='count')
	parser.add_argument('--conll2phrase', '-c', action='store_true')
	parser.add_argument('--idiot', '-i', action='store_true')
	parser.add_argument('--graphs', '-g', action='store_true', help='Show all created phrase structure trees graphically')
	parser.add_argument('--freqs', '-f', action='store_true', help='Create statistics over the syntactic analyses ')
	parser.add_argument('--dir', '-d', action='store_true', help='use whole folders instead of files')
	
	args=parser.parse_args()
	verbose=args.verbose
	
	freqtypes=["allCats","allFuncs","catdeps","depcats","catdepcats"]
	
	freqsdics=[{} for t in freqtypes]
	
	if args.conll2phrase:
		print args
	elif args.idiot:
		idioticdepnumbers(args.oldfile,args.goodfile)
	else:
		if args.dir:
			for oldfile in glob.glob(os.path.join(args.oldfile, '*.*')):
				print "************************************************************* handling",os.path.basename(oldfile)[5:10]
				#Rhap-M0002-Synt.xml
				goodfile=os.path.join(args.goodfile, "Rhap."+os.path.basename(oldfile)[5:10]+".Synt.xml.most.recent.trees.rhaps.xml")
				outfile=os.path.join(args.outfile, os.path.basename(oldfile)[5:10]+".cool.xml")
				#print oldfile,goodfile,outfile
				allConstTrees=joinRhapsodies(goodfile,oldfile,outfile)
				idioticdepnumbers(outfile,os.path.join(args.outfile, os.path.basename(oldfile)[5:10]+".absolutely.cool.xml"))
				print "result written to",outfile
				if args.freqs:
					freqsdics=freqs(goodfile, freqsdics)
					
						
					
					
				if args.graphs:
					for i,tree in enumerate(allConstTrees):
						
						if i<95:continue
						print i
						tree.draw()
		else:	# single file	
			
			if args.freqs:
				freqsdics=freqs(args.goodfile,freqsdics)
			allConstTrees=joinRhapsodies(args.goodfile,args.oldfile,args.outfile)
			print "result written to",args.outfile
			
			if args.graphs:
				for i,tree in enumerate(allConstTrees):
					
					#if i<95:continue
					print i
					tree.draw()
				
	if args.freqs:			
		freqsout( freqsdics,freqtypes )			
				
				
# conllu format
1	faut	falloir	V	_	_	0	root	_	must
2	que	que	C	_	_	4	mark	_	that
3	tu	tu	Pron	_	_	4	nsubj	_	you
4	fasses	faire	V	_	_	1	ccomp	_	make
5	envie	envie	N	_	_	4	dobj	_	desire
6-7	aux	aux	P+D	_	_	_	_	_	to_the
6	Ã 	Ã 	P	_	_	8	case	_	to
7	les	le	D	_	_	8	det	_	the
8	enfants	enfant	N	_	_	4	iobj	5:nmod	kids
9	de	de	P	_	_	10	mark	_	to
10	faire	faire	V	_	_	5	ccomp	_	make
11-12	du	du	P+D	_	_	_	_	_	of_the
11	de	de	P	_	_	13	case	_	of
12	le	le	D	_	_	13	det	_	the
13	bruit	bruit	N	_	_	10	dobj	_	noise

# conllu format using SpaceAfter=No
1	Can	can	AUX	MD	VerbForm=Fin	3	aux	_	_
2	you	you	PRON	PRP	Case=Nom|Person=2|PronType=Prs	3	nsubj	_	_
3	use	use	VERB	VB	VerbForm=Inf	0	root	_	_
4	the	the	DET	DT	Definite=Def|PronType=Art	10	det	_	_
5	'	'	PUNCT	``	_	6	punct	_	SpaceAfter=No
6	find	find	VERB	VB	VerbForm=Inf	10	compound	_	_
7	my	my	PRON	PRP$	Number=Sing|Person=1|Poss=Yes|PronType=Prs	8	nmod:poss	_	_
8	phone	phone	NOUN	NN	Number=Sing	6	dobj	_	SpaceAfter=No
9	'	'	PUNCT	''	_	6	punct	_	_
10	feature	feature	NOUN	NN	Number=Sing	3	dobj	_	_
11	to	to	PART	TO	_	12	mark	_	_
12	track	track	VERB	VB	VerbForm=Inf	3	advcl	_	_
13	someone	someone	NOUN	NN	Number=Sing	16	nmod:poss	_	_
14	else	else	ADJ	JJ	Degree=Pos	13	amod	_	SpaceAfter=No
15	's	's	PART	POS	_	13	case	_	_
16	phone	phone	NOUN	NN	Number=Sing	12	dobj	_	SpaceAfter=No
17	?	?	PUNCT	.	_	3	punct	_	_

# I wrote the letter with a quill.
1	Ð¯	ja	PRON	_	Case=Nom|Number=Sing|Person=1|PronType=Prs	2	nsubj	_	I
2	Ð½Ð°Ð¿Ð¸ÑÐ°Ð»	napisat'	VERB	_	Gender=Masc|Number=Sing|VerbForm=Part|Voice=Act	0	root	_	wrote
3	Ð¿Ð¸ÑÑÐ¼Ð¾	pis'mo	NOUN	_	Case=Acc|Gender=Neut|Number=Sing	2	dobj	_	the-letter
4	Ð¿ÐµÑÐ¾Ð¼	pero	NOUN	_	Case=Ins|Gender=Neut|Number=Sing	2	nmod	_	with-a-quill 
#!/usr/bin/python
# -*- coding: utf-8 -*-

#######################################################################################
# this file contains loads of useful functions
# - for importing files containing treebanks (parse files) into the arborator
# - and also for correction during or after the import process

#simple usage, for example, to import a group of mate parse files into the database:
	#readinallmates("test_mini_db","corpus/mate/",filepattern="*_Parse")
#this will import all files in corpus/mate that fit the pattern into the database creating a new text under the name of the file
#the filename will be abreviated: see the readinallmates function itself
		
#######################################################################################


debug=False
#debug=1
#debug=2
#debug=3

maxlength=5000 # maxlength of words per sentence, longer ones are ignored

logfile=None
#logfile="log.treebankfiles.txt"


# problem of token names:
# import token name: how the token is named in the xml file: defined here, will also be used for the internal storage of read in conll files
# export token name: how the token feature is encoded in the database: defined in the project's config file

tokenname="t"


# central functions:
# rhapsodie2Sentences: reads in rhapsodie file format into tree internal representation
# correctRhapsodie: corrects a tree list following correction dictionaries
# checkSentence: corrects an individual tree
# enterSentences: puts a tree list into the database





#import cgi
from xml.dom import minidom
#from xml.dom.ext import PrettyPrint
from datetime import date
from time     import mktime, time, asctime, localtime
from sqlite3   import connect
from os       import environ
from sys      import exit
import sys, os, re, json
import codecs,urllib
import collections
import traceback, copy
from glob import glob


try: 	import jellyfish
except: pass
try:	from nltk.featstruct import unify
except:	pass

import database
import config
import conll

# erase old logfile
if logfile:
	lf=codecs.open(logfile,"w","utf-8")
	lf.close()

#def simpleEnterFolder(sql, annotatorName):
	#for filename in glob(os.path.join(conlldirpath, filepattern)):
	
def simpleEnterSentences(sql, trees, dbtextname, annotatorName, eraseAllAnnos, sentencefeatures={}, preserveSampleWithSameName=False):
	"""
	takes a list of trees (nodedics)
	puts them into the database
	if preserveSampleWithSameName: add _ to names to create new name of sample
	"""
	ti = time()
	db,cursor=sql.open()
	if preserveSampleWithSameName:
		while sql.getUniqueId(cursor, "texts", ["textname"],[dbtextname])!=None:
			dbtextname+="_"
	textid = sql.enter(cursor, "texts",["textname"],(dbtextname,))
	print "textid",textid
	if eraseAllAnnos: 
		db.close()
		sql.removeText(textid)
		db,cursor=sql.open()
		textid = sql.enter(cursor, "texts",["textname"],(dbtextname,))
		print "textid",textid
	if debug: 
		print "found",len(trees),"trees to enter"
		print "entering an annotation by",annotatorName, "into annotations of textid",textid
			
	userid = sql.enter(cursor, "users",["user"],(annotatorName,))
	sql.realname(annotatorName, cursor)
	if not userid:
		print "the user is not in the database"
		return
	
	scounter,wcounter=0,0

	for i,tree in enumerate(trees): # for every sentence
		scounter+=1
		sentence=" ".join([tree[j].get("t","") for j in sorted(tree)])
		#cursor.execute("select rowid from sentences,texts  where nr=? and sentence=?;",
		sentenceid = sql.getUniqueId(cursor, "sentences", ["nr","sentence","textid"], [scounter,sentence,textid])
		#print "found sentenceid",sentenceid
		if not sentenceid:
			sentenceid = sql.enter(cursor, "sentences",["nr","sentence","textid"],(scounter,sentence,textid,))
			#print "made sentenceid",sentenceid
		sql.enter(cursor, "sentencesearch",["nr","sentence","textid"],(scounter,sentence,textid,))
		if sentencefeatures == True:
			for a,v in tree.sentencefeatures.iteritems():
				sql.enter(cursor, "sentencefeatures",["sentenceid","attr","value"],(sentenceid,a,v,))
		elif sentencefeatures:	
			for a,v in sentencefeatures[i].iteritems():
				sql.enter(cursor, "sentencefeatures",["sentenceid","attr","value"],(sentenceid,a,v,))
		#print tree
		ws, sent, treeid = sql.enterTree(cursor, tree, sentenceid, userid)
		#print "ws, sent, treeid",ws, sent, treeid
		wcounter+=ws
		#if debug:print "----",scounter,"of",len(tree),sent.encode("utf-8")," - it took",time()-ti,"seconds up to now for",wcounter, "words - ", float(wcounter)/(time()-ti),'words per second.'
	
	db.commit()
	db.close()	
	
	if debug:print "it took",time()-ti,"seconds"
	return textid
	
def enterSentences(sql, cursor, sentences, filename, textid, annotatorName, eraseAllAnnos, notenter=["order"], sentencefeatures={}, defaultAnnotatorName="parser", tokname=None):
	"""
	takes a list of nodedics (sentences)
	
	puts them into the database
	
	"""
	#print "found",len(sentences),"sentences to enter"
	
	if debug: print "entering",filename,"as annotation by",annotatorName, "into annotations of textid",textid
	
	if not tokname:tokname=tokenname
	
	shortname = filename.split("/")[-1]
	
	userid = sql.enter(cursor, "users",["user"],(annotatorName,))
	sql.realname(annotatorName, cursor)
	if not userid:
		print "the user is not in the database"
		return
	
	
	scounter,wcounter=0,0
	ti = time()
	if eraseAllAnnos:
		# TODO : remove trees completely with all features!
		sql.clean(cursor, "sentences",["textid"],(textid,)) # erase all existing sentences for this textid
		sql.clean(cursor, "sentencesearch",["textid"],(textid,)) # erase all existing sentences for this textid
		
		#{0: {'deps': [4]}, 1: {'head': 2, 'word': u'``', 'deps': [], 'lemma': u'--', 'tag': u'$(', 'rel': u'-PUNCT-', 'id': 1}, 2: {'head': 4, 'word': u'Ross', 'deps': [1, 3], 'lemma': u'--', 'tag': u'NE', 'rel': u'SUBJ', 'id': 2}, 3: {'head': 2, 'word': u'Perot', 'deps': [], 'lemma': u'--', 'tag': u'NE', 'rel': u'APP', 'id': 3}, 4: {'head': 0, 'word': u'w\xe4re', 'deps': [2, 5, 8], 'lemma': u'--', 'tag': u'VAFIN', 'rel': u'ROOT', 'id': 4}, 5: {'head': 4, 'word': u'vielleicht', 'deps': [], 'lemma': u'--', 'tag': u'ADV', 'rel': u'ADV', 'id': 5}, 6: {'head': 8, 'word': u'ein', 'deps': [], 'lemma': u'--', 'tag': u'ART', 'rel': u'DET', 'id': 6}, 7: {'head': 8, 'word': u'pr\xe4chtiger', 'deps': [], 'lemma': u'--', 'tag': u'ADJA', 'rel': u'ATTR', 'id': 7}, 8: {'head': 4, 'word': u'Diktator', 'deps': [6, 7, 9], 'lemma': u'--', 'tag': u'NN', 'rel': u'PRED', 'id': 8}, 9: {'head': 8, 'word': u"''", 'deps': [], 'lemma': u'--', 'tag': u'$(', 'rel': u'-PUNCT-', 'id': 9}}
		
		
		#for snode in sentences:
			#print snode

		for i,tree in enumerate(sentences): # for every sentence
			#print "ooooooooooooooooo",len(tree)
			
			if len(tree)<maxlength:
				scounter+=1
				
				sentence=" ".join([tree[j].get(tokname,"") for j in sorted(tree)])
				sentenceid = sql.enter(cursor, "sentences",["nr","sentence","textid"],(scounter,sentence,textid,))
				sql.enter(cursor, "sentencesearch",["nr","sentence","textid"],(scounter,sentence,textid,))
				#print sentencefeatures
				if tree.sentencefeatures:
					for a,v in tree.sentencefeatures.iteritems():
						sql.enter(cursor, "sentencefeatures",["sentenceid","attr","value"],(sentenceid,a,v,))
				
				# enter first tree for sentencewcounter, sent, treeid
				ws, sent, treeid = sql.enterTree(cursor, tree, sentenceid, userid, notenter=notenter, intokenname=tokname)
				wcounter+=ws
				
				if debug:print "----",scounter,"of",len(sentences),sent.encode("utf-8")," - it took",time()-ti,"seconds up to now for",wcounter, "words - ", float(wcounter)/(time()-ti),'words per second.'
				#' expected time remaining', float(scounter)/(time()-ti)*(len(sentences)-scounter)
			else:
				print "ooooooh"
				if logfile:
					f=codecs.open("toolong.txt","a","utf-8")
					f.write("\t".join([filename, str(len(nodes)),unicode(tree)])+"\n")
					f.close()
				
					f=codecs.open(logfile,"a","utf-8")
					f.write("tooooooooooooooooolong: \n"+ filename+"\n"+unicode(tree)+"\n\n\n")
					f.close()

	else: # not erasing, adding
		#print "enterSentences qqq"
		
		r = sql.getall(cursor, "sentences",["textid"], [textid])

		mini=0
		
		for tree in sentences: # for every sentence
			if len(tree)<maxlength:
				
				
				#print tree
				
				distas=[]
				
				scounter+=1
				inssentence=" ".join([tree[j].get(tokname,"") for j in sorted(tree)])
				entered=False
				for sentenceid,nr,sentence,textid in r[mini:]: 
					#print "found:",sentence
					#print "looking for:",inssentence
					#print len(tree),len(sentence.split()),len(tree)==len(sentence.split())
					if sentence==inssentence:
						mini=nr
						origdic=getorigdic(sql,cursor,sentenceid,defaultAnnotatorName)
						#origdic=sql.gettree(sid=sentenceid,username=defaultAnnotatorName)["tree"]
						if len(origdic)==len(tree):
							if debug: print "standard entry",inssentence
							ws, sent, treeid = sql.enterTree(cursor, tree, sentenceid, userid, notenter=notenter, intokenname=tokname)
						else:
							if debug: print "try fuzzy entry",inssentence
							ws, sent, treeid = fuzzyEnterSentence(tree,origdic,sql, cursor, filename, textid, annotatorName, scounter,sentence, sentenceid, inssentence, userid, notenter=notenter)
						wcounter+=ws
						if debug :
							if sent:print "---",sent.encode("utf-8"),"sentence"
							else:
								print "ooooooh"
								if logfile:
									
									f=codecs.open(logfile,"a","utf-8")
									f.write("no sentence: \n"+ filename+"\n"+unicode(tree)+"\n\n\n")
									f.close()
								
						entered=True
						break
					else: 
						distas+=[(jellyfish.levenshtein_distance(unicode(sentence).encode("utf-8"), unicode(inssentence).encode("utf-8")), (tree, sentenceid, sentence))]
					
				if not entered: 
					print "couldn't find '"+inssentence+"' in", shortname,"! Haven't yet entered the annotation"
					if distas:
						tree, sentenceid, sentence = sorted(distas)[0][1] # get best sentence
						print "will try to enter instead\n",inssentence,"\ninto\n",sentence
						origdic=getorigdic(sql,cursor,sentenceid,defaultAnnotatorName)
						ws, sent, treeid = fuzzyEnterSentence(tree,origdic,sql, cursor, filename, textid, annotatorName, scounter,sentence, sentenceid, inssentence, userid, notenter=notenter)
						wcounter+=ws
					else:
						print "no data under that name:",shortname
						#enterSentences(sql, cursor, sentences, filename, textid, annotatorName, eraseAllAnnos=True, notenter=notenter, sentencefeatures=sentencefeatures, defaultAnnotatorName=defaultAnnotatorName, tokname=tokname)
						
						
			
			else:
				print "ooooooh"
				if logfile:
					f=codecs.open("toolong.txt","a","utf-8")
					f.write("\t".join([filename, str(len(nodes)),unicode(tree)])+"\n")
					f.close()
				
					f=codecs.open(logfile,"a","utf-8")
					f.write("tooooooooooooooooolong: \n"+ filename+"\n"+unicode(tree)+"\n\n\n")
					f.close()
			
			
	#db.commit()
	if debug:print "it took",time()-ti,"seconds"

def getorigdic(sql,cursor,sentenceid,defaultAnnotatorName):
	sqltokenname=sql.projectconfig["shown features"]["0"]	
	
	dbexe = cursor.execute("""select features.nr, trees.rowid, trees.sentenceid, features.attr, features.value 
						from trees, features, users
						where trees.rowid=features.treeid and trees.userid=users.rowid and users.user=? and trees.sentenceid=?;""",(defaultAnnotatorName, sentenceid,))
	
	tree={}
	for nr,treeid,sid,attr,value, in dbexe.fetchall():
		#print nr,treeid,sid,attr,value
		tree[nr]=tree.get(nr,{})
			
		if attr==sqltokenname:
			tree[nr][tokenname]=value
		else:
			tree[nr][attr]=value
		govdic={}
		for _,_,_,govid,function in sql.getall(cursor, "links", ["treeid", "depid"], [treeid, nr]):
			govdic[govid]=function
		tree[nr]["gov"]=govdic
	#print "888888888888888888",tree
	#dic={}
	#for _,sid,a,v in self.getall(cursor, "sentencefeatures", ["sentenceid"], [sid]):
		#dic[a]=v
	#dic["tree"]=tree ## maybe test whether "tree" is already an attribute?
	
	
	#origdic=sql.gettree(sid=sentenceid,username=defaultAnnotatorName)["tree"]
	#for i in origdic:
		#origdic[i][tokenname]=origdic[i][sqltokenname]
		#del origdic[i][sqltokenname]
	return tree
	
def fuzzyEnterSentence(tree,origdic, sql, cursor, filename, textid, annotatorName, scounter,sentence, sentenceid, inssentence, userid, notenter):
	ws, sent, treeid =0,None,None
	# t=sql.projectconfig["shown features"]["0"]
	restree = correcttokens(copy.deepcopy(tree),origdic)
	print "end strict stuff"
	if not restree: 
		print "\n\nthis didn't work out. we try again, less strict"
		restree = correcttokens(copy.deepcopy(tree),origdic, strict=False)
	if not restree:
		print "ooooooh"
		if logfile:
			f=codecs.open(logfile,"a","utf-8")
			f.write("\t".join([filename,str(textid), annotatorName, str(scounter),sentence])+"\n\n\n")
			f.close()
		
	if len(origdic)==len(restree):
		print "will really enter ",inssentence,"into",sentence,"!!!!\n"
		#if sentence.startswith("alors j' emprunte la rue de Strasbourg"):
			#for i,d in  restree.iteritems():print i,d 
			#for i,d in  origdic.iteritems():print i,d 
			#print sentence
			#1/0
			
		ws, sent, treeid = sql.enterTree(cursor, restree, sentenceid, userid, newTokens=False, notenter=notenter, intokenname=tokenname)
	else: 
		print "could not enter!!!!!"
	return ws, sent, treeid 

	
def correcttokens(tree, origdic, strict=True):
	"""
	tree is checked
	origdic is the parser's annotation whose tokens are relevant
	strict: first trying to match exactly the parser's tokens
	"""
	if debug: 
		print "\n=================== start correcttokens tree"
		if strict:print "strict!!!"
		else: print "not strict!!!!"
		for i,d in  tree.iteritems():print i,d[tokenname],d 
		print "with origdic"
		for i,d in  origdic.iteritems():print i,d[tokenname],d 
	
	newtree={}
	try:
		for nr in sorted(origdic):
			#print origdic[nr]
			#print nr, origdic.get(nr,{tokenname:"___________verystrange"})[tokenname],tree.get(nr,{tokenname:"___________verystrange"})[tokenname]
			
			if nr in tree and tree[nr][tokenname]==origdic[nr][tokenname]: # found perfect matching token, copying everything to the new node
				#print "ok"
				newtree[nr]=tree[nr]
			else:
				#print "___problem"
				i=0
				intdic,normal2ins={},{}
				
				smalltoks=tree[nr][tokenname].encode("utf-8").split()
				if strict:
					
					while tree[nr][tokenname].startswith(" ".join([origdic[ii][tokenname] for ii in range(nr,nr+i+1)])): # making the insertion dict if words fit on one another:
						intdic["i"+str(i+1)]=origdic[nr+i]
						normal2ins[nr+i]="i"+str(i+1)
						i+=1
				else: 
					while i<len(smalltoks) and jellyfish.levenshtein_distance(    origdic[nr+i][tokenname].encode("utf-8"), smalltoks[i]   ) / len ( smalltoks[i] ) <= .5:
						intdic["i"+str(i+1)]=origdic[nr+i]
						normal2ins[nr+i]="i"+str(i)
						i+=1
				#print intdic
				
				if len(intdic)>1: # we found a token that has to be separated
					for ind in intdic: # copy good internal governors
						newgov={}
						for govi in intdic[ind]["gov"]:
							if govi in normal2ins: newgov[normal2ins[govi]]=intdic[ind]["gov"][govi]
						intdic[ind]["gov"]=newgov
					intdic["i1"]["gov"]={"i0":"root"} 
					intdic["i"+str(i)]["child"]=True
					#print intdic
					newtree=integrate(tree,nr,intdic)
					return correcttokens(newtree, origdic, strict) # start over because indeces changed
					
				else: # just wrong token for example ce != c'
					#print "changed token from",tree[nr][t],"to",origdic[nr][t]
					newtree[nr]=tree[nr]
					newtree[nr][tokenname]=origdic[nr][tokenname]
	except Exception, err:
		if debug:print traceback.format_exc()
		if logfile:
			f=codecs.open(logfile,"a","utf-8")
			f.write(traceback.format_exc()+"\n\n\n")
			f.close()
		#1/0
		return {}
	return newtree	
	
	
def enterConll(dbname,filename,annotatorName=None, eraseAllAnnos=False, dbtextname=None, corrdic={"tag":"cat","t":"orthotext"}): 
	""" 
	TODO: think about when to erase all tokens and features of existing annotations. maybe put warnings and confirm?
	"""
	sql = database.SQL(dbname)
	db,cursor=sql.open()
	sentences=conll.conllFile2trees(filename,corrdic)
	
	if dbtextname:
		textid = sql.enter(cursor, "texts",["textname"],(dbtextname,))
	else:
		#M016.XML.Paola.trees.conll10
		if filename[-14:-2]=='.trees.conll':
			dbtextname=".".join(filename.split("/")[-1].split(".")[:-3])
			
			# TODO: remove:
			dbtextname= "Rhap-"+dbtextname[:2] + "0" + dbtextname[2:4]+"-Synt.xml"
			#print dbtextname
			
			textid = sql.enter(cursor, "texts",["textname"],(dbtextname,))
			#print textid
			if not annotatorName: annotatorName=filename.split(".")[-3]
		else:
			dbtextname=filename.split("/")[-1]
			textid = sql.enter(cursor, "texts",["textname"],(dbtextname,))
			
	enterSentences(sql,cursor,sentences,filename, textid,annotatorName,eraseAllAnnos )
	db.commit()
	db.close()
	return len(sentences)
	
	
def uploadConll(sql, filename, annotatorName=None, eraseAllAnnos=True): 
	""" 
	important function, called from project.cgi!!!
	
	"""

	db,cursor=sql.open()
	sentences=conll.conllFile2trees(filename)
	#print sentences
	if not annotatorName: annotatorName=sql.importAnnotatorName
	
	dbtextname = filename.split("/")[-1].decode("utf-8")
	for exten in ".conll10 .conllu .conll14 .malt .tab .txt.conll_parse .txt.conll_parse.orig".split():
		if dbtextname.endswith(exten):
			dbtextname=dbtextname[:-len(exten)]
			break
	
	#if dbtextname.endswith("") or dbtextname.endswith("") or  dbtextname.endswith("") or dbtextname.endswith("") or dbtextname.endswith("") or dbtextname.endswith("") :
		#dbtextname=".".join(dbtextname.split(".")[:-1])
	
	textid = sql.enter(cursor, "texts",["textname"],(dbtextname,))
	
			
	enterSentences(sql, cursor, sentences, filename, textid, annotatorName, eraseAllAnnos, tokname="t" )
	db.commit()
	db.close()
	return len(sentences)


def enterDegraded(project, filenameGood, filenameDegraded=None, goodAnnotatorName="prof", degradedAnnotatorName="parser", eraseAllAnnos=True): 
	"""
	if filenameDegraded is not given, the files have to be named xxx.orig and xxx.deg
	"""
	if not filenameDegraded: 
		filenameDegraded=filenameGood[:-len(".orig")]+".deg"
		
	dbtextname = filenameGood.split("/")[-1] #.decode("utf-8")
	if dbtextname.endswith(".orig"): dbtextname=dbtextname[:-len(".orig")]
	for exten in ".conll10 .conll14 .malt .tab .txt.conll_parse .txt.conll_parse.orig".split():
		if dbtextname.endswith(exten):
			dbtextname=dbtextname[:-len(exten)]
			break
		
	sql=database.SQL(project)
	print filenameGood
	trees=conll.conllFile2trees(filenameGood)
	
	simpleEnterSentences(sql, trees, dbtextname, annotatorName=goodAnnotatorName, eraseAllAnnos=True)
	
	#db,cursor=sql.open()
	#textid = sql.enter(cursor, "texts",["textname"],(dbtextname,))
	#enterSentences(sql, cursor, trees, dbtextname, textid, goodAnnotatorName, eraseAllAnnos=True)
	#db.commit()
	#db.close()
	
	trees=conll.conllFile2trees(filenameDegraded)
	textid=simpleEnterSentences(sql, trees, dbtextname, annotatorName=degradedAnnotatorName, eraseAllAnnos=False)
	print "==",sql.getNumberTokensPerText(textid, recompute=True)
	
	
		
		
		
def update(d, u):
		""" used in rhapsodie2Sentences"""
		
		for k, v in u.iteritems():
			if isinstance(v, collections.Mapping):
				r = update(d.get(k, {}), v)
				d[k] = r
			else:
				d[k] = u[k]
		return d
	
	
def rhapsodie2Sentences(filename):
		
		doc = minidom.parse(filename)
		
		pivs, tokens, lexemes, deps, nodedics = {},{},{},{},{}
		wtypedic={"pivot":pivs,"tokens":tokens,"lexemes":lexemes}
		dtypedic={"syntax":deps}
		
		# all dictionaries look like this: u'lex565': (<DOM Element: word at 0xb617e2cc>, 566)
		for wordgroup in doc.getElementsByTagName("words"):# looking at all the wordgroups
			currentdic = wtypedic.get(wordgroup.getAttribute("type"),None) # if they are mentioned in the wtypedic, read them in
			if currentdic=={}:
				order=0
				for w in wordgroup.getElementsByTagName("word"):
					order+=1
					currentdic[w.getAttribute("id")]=w,order
		
		depcounter = 0
		sentences,sentencefeatures=[],[]
		for depgroup in doc.getElementsByTagName("dependencies"):
			currentdic = dtypedic.get(depgroup.getAttribute("type"),None)
			if currentdic=={}: # i found the syntax depgroup
				for dependency in depgroup.getElementsByTagName("dependency"): # for each dependency = one sentence
					
					lexiddic = {}
					nodes={}
					sentencefeature={}
					for i in range(dependency.attributes.length):
						a=dependency.attributes.item(i)
						sentencefeature[a.name]=a.value
					
					for link in dependency.getElementsByTagName("link"): 
					# this takes all the links and all the depids mentioned here and adds them as words. unlinked words are not taken into account. no testing on govids either...
						l,order=lexemes.get(link.getAttribute("depid"),None)
						if l: # for each dep node of the link:
						# TODO: check if 'order' still used anywhere!!!
							d={'lexid': link.getAttribute("depid"), tokenname: l.getAttribute("orthotext"), 
								'lemma': l.getAttribute("lemma"), 'order':order, 
								'gov':{ link.getAttribute("govid"): link.getAttribute("func")}
								}
														
							for fs in l.getElementsByTagName("features"):
								for i in range(fs.attributes.length):
									a=fs.attributes.item(i)
									d[a.name]=a.value
							
							#if (not d[tokenname]) and l.getAttribute("text"): d[tokenname] = l.getAttribute("text")
							#if (not d[tokenname]) and d.get(tokenname,None): d[tokenname] = d.get(tokenname,None)
							if debug and (not d[tokenname]):
								print "no token!!!!",filename
								print d
								for fs in l.getElementsByTagName("features"):
									print fs
									for i in range(fs.attributes.length):
										a=fs.attributes.item(i)
										d[a.name]=a.value
										print a,a.name,a.value
								1/0
							nodes[order]=update(nodes.get(order,{}),d)
							depcounter+=1
							
						else:
							print "error! lexeme",l,"does not exist. Error in XML file."
							1/0
					if len(nodes)<maxlength:
						nodedic={}
						lexidid = {}
						for i,(order,d) in enumerate(sorted(nodes.items())):
							nodedic[i+1]=d
							lexidid[d["lexid"]]=i+1
						for i in nodedic:
							newgov={}
							for g,f in nodedic[i].get('gov',{}).iteritems():
								if g: newgov[lexidid.get(g)]=f
								else:newgov[0]=f
							nodedic[i]['gov']=newgov
						sentences+=[nodedic]
						sentencefeatures+=[sentencefeature]
					else:
						print sentencefeature
						print "ooooooh"
						if logfile:
							f=codecs.open("toolong.txt","a","utf-8")
							f.write("\t".join([filename, str(len(nodes)),sentencefeature["markupIU"]])+"\n")
							f.close()
						
							f=codecs.open(logfile,"a","utf-8")
							f.write("toolong: \n"+ filename+"\n\n\n"+str(len(nodes))+"\t"+sentencefeature["markupIU"]+"\n\n\n")
							f.close()
						
						#1/0
						
					

		print len(sentences),"sentences with",depcounter,"dependencies"
		print "_________________________"
		
		return sentences,sentencefeatures
		
#def enterConll(dbname,filename,annotatorName=None, eraseAllAnnos=False, dbtextname=None): 
def enterRhapsodie(sql,inputfilename,annotatorName, eraseAllAnnos,  clean=True, corrlist=False):		
		
		print "______________________________________________________________reading",inputfilename
		if logfile:
			f=codecs.open(logfile,"a","utf-8")
			f.write("______________________________________________________________reading"+ inputfilename+"\n"+annotatorName+"\n\n\n")
			f.close()
			
		if clean: sql.cleanDatabase(inputfilename)
		
		db,cursor=sql.open()
		
		if corrlist: 	sentences, sentencefeatures=correctRhapsodie(inputfilename, corrlist)
		else:		sentences, sentencefeatures=rhapsodie2Sentences(inputfilename)
		
		#print sentences
		#print sentencefeatures
		
		#1/0
		
		annotatorName=sql.projectconfig["configuration"]["baseAnnotatorName"]
		dbtextname=inputfilename.split("/")[-1]
		textid = sql.enter(cursor, "texts",["textname"],(dbtextname,))
		print "______________",len(sentences)," sentences ready to enter into database. textname:",dbtextname,"textid",textid
		enterSentences(sql,cursor,sentences,inputfilename, textid,annotatorName,eraseAllAnnos, sentencefeatures=sentencefeatures )

		db.commit()
		db.close()
		return len(sentences)
	
	
	
def correcttree2nodedic(tree,catname="cat"):
		""" 
		takes the conll string representation of a single tree and creates a dictionary for it
		here special format: spacelemmas
		"""
	
		nodedic={}
		for line in tree.split('\n'):
			line=line.strip()
			
			#print "___",line
			if line:
				cells = line.split('\t')
				nrCells = len(cells)
				if nrCells in [6,7]:
					if nrCells == 6:
						nr, t, lemma, tag, head, rel = cells
						#nr,head = int(nr), int(head)
						if int(head)>=0:nodedic['i'+nr]={'id':'i'+nr,tokenname: t,'lemma': lemma, catname: tag, 'gov':{'i'+head: rel} }
						else:nodedic['i'+nr]={'id':'i'+nr,tokenname: t,'lemma': lemma, catname: tag, 'gov':{} }
					elif nrCells == 7:
						nr, t, lemma, tag, head, rel, child = cells
						
						#nr,head = int(nr), int(head)
						#if lemma2!="_": lemma=lemma2 # TODO: find out what the difference between these columns is!!!!!!
						if int(head)>=0:nodedic['i'+nr]={'id':'i'+nr,tokenname: t,'lemma': lemma, catname: tag, 'gov':{'i'+head: rel} }
						else:nodedic['i'+nr]={'id':'i'+nr,tokenname: t,'lemma': lemma, catname: tag, 'gov':{} }
						
						
						if child.strip()=="-":
							nodedic['i'+nr]["child"]=True
						else:
							print child,cells,"--- strange line:"
							print line
				else:
					print "strange line"
					print line
					print "in:"
					print tree
			
					
		return nodedic
	
	
	
	
	####################### functions for the batch correction of Rhapsodie
	
def glue(sentencedic,nodenum,direction):
		"""
		glues the nodenum (t and lemma) to the neighboring node in the direction
		ignores govs etc du nodenum
		gives back new sentencedic
		
		"""
		
		if debug>2:print "================== glueing"
		newsentencedic={}
		try:
			intchi=nodenum+direction
			if direction<0:
				sentencedic[intchi][tokenname]
				sentencedic[nodenum][tokenname]
				sentencedic[intchi][tokenname]=sentencedic[intchi][tokenname]+sentencedic[nodenum][tokenname]
				sentencedic[intchi]['lemma']=sentencedic[intchi]['lemma']+sentencedic[nodenum]['lemma']
			else:
				sentencedic[intchi][tokenname]=sentencedic[nodenum][tokenname]+sentencedic[intchi][tokenname]
				sentencedic[intchi]['lemma']=sentencedic[nodenum]['lemma']+sentencedic[intchi]['lemma']
				
			
			
			l = [0]+sorted(sentencedic)[:nodenum-1]+sorted(sentencedic)[nodenum:]
			if debug>1:print "lllll",l
			
			
			
			# correct wrong gov links
			for i in sorted(sentencedic):
				if nodenum in sentencedic[i]['gov'].keys():
					sentencedic[i]['gov'][intchi]=sentencedic[i]['gov'][nodenum]
					del sentencedic[i]['gov'][nodenum]		
			
			
			#print 'lllll',l
			# bring all in a new list with new indeces based on l
			
			for newi,oldi in enumerate(l):
				if newi:# not 0
					newsentencedic[newi]=sentencedic[oldi]
					govdic={}
					for govi,dep in sentencedic[oldi]['gov'].iteritems():
						
						govdic[l.index(govi)]=dep
					newsentencedic[newi]['gov']=govdic
		
		except Exception, err:
			print traceback.format_exc()
			print nodenum
			print direction
			print "oldoldold((((((((((((("
			for i in sorted(sentencedic):
				print i,sentencedic[i]
			print ")))))))))))))))"
			print "newnewnew((((((((((((("
			for i in sorted(newsentencedic):
				print i,newsentencedic[i]
			print ")))))))))))))))"
			print traceback.format_exc()
			if logfile:
				f=codecs.open(logfile,"a","utf-8")
				f.write(traceback.format_exc()+str(nodenum)+"\n" +str(intchi)+"\n" +  "\n\n\n"+unicode(sentencedic)+"\n\n\n"+unicode(newsentencedic)+"\n\n\n")
				f.close()
			return sentencedic
			#raise ShitError
		
		#del sentencedic[nodenum]

		if debug>1:
			print "((((((((((((("
			for i in sorted(newsentencedic):
				print i,newsentencedic[i]
			print ")))))))))))))))"
			
		return newsentencedic
	
	
	
def integrate(sentencedic,nodenum,intdic):
		"""
		integrates the intdic into sentencedic at position nodenum
		an empty intdic kicks out the node in a proper manner (other dependencies are still ok)
		
		gives back new sentencedic
		
		"""
		intgov,intchi=None,None
		newsentencedic={}
		if debug>2: 
			print "================== integrating"
			for i in sorted(intdic):
				print i,intdic[i]
			print "into ((((((((((((("
			for i in sorted(sentencedic):
				print i,sentencedic[i]
			print ")))))))))))))))"
		try:
			l = [0]+sorted(sentencedic)[:nodenum-1]+sorted(intdic)+sorted(sentencedic)[nodenum:]
			if debug>2: print "lllll",l
			
			
			for i in sorted(intdic):
			
				sentencedic[i]=sentencedic[nodenum].copy()
				#intdic[i][tokenname]
				sentencedic[i][tokenname]=intdic[i][tokenname]
				sentencedic[i]['lemma']=intdic[i]['lemma']
				sentencedic[i]['cat']=intdic[i]['cat']
				sentencedic[i]['gov']=intdic[i].get('gov',{})
				
				
				if 'i0' in intdic[i].get('gov',{}).keys(): # found the gov: the one that has a root link
					sentencedic[i]['gov']=sentencedic[nodenum]['gov']
					intgov=i
				if "child" in intdic[i]: # found the child bearer
					intchi = i
			if not intgov:# if no governor is indicated, i guess it's the first word
				intgov="i1"
				if intgov in sentencedic: sentencedic[intgov]['gov']=sentencedic[nodenum]['gov']
			if not intchi: intchi=intgov
			
			# correct wrong gov links
			for i in sorted(sentencedic):
				if nodenum in sentencedic[i]['gov'].keys():
					sentencedic[i]['gov'][intchi]=sentencedic[i]['gov'][nodenum]
					del sentencedic[i]['gov'][nodenum]		
			
			
			#print 'lllll',l
			# bring all in a new list with new indeces based on l
			newsentencedic={}
			for newi,oldi in enumerate(l):
				if newi:# not 0
					newsentencedic[newi]=sentencedic[oldi]
					govdic={}
					for govi,dep in sentencedic[oldi]['gov'].iteritems():
						if govi>=0 and govi in l:govdic[l.index(govi)]=dep
					newsentencedic[newi]['gov']=govdic
		except Exception, err:
			print traceback.format_exc()
			print "oldoldold((((((((((((("
			for i in sorted(sentencedic):
				print i,sentencedic[i]
			print ")))))))))))))))"
			print "newnewnew((((((((((((("
			for i in sorted(newsentencedic):
				print i,newsentencedic[i]
			print ")))))))))))))))"
			print "intdic((((((((((((("
			for i in sorted(intdic):
				print i,intdic[i]
			print ")))))))))))))))"
			print nodenum
			print intdic
			
			if logfile:
				f=codecs.open(logfile,"a","utf-8")
				f.write(traceback.format_exc()+"\n\n\n"+unicode(sentencedic)+"\n\n\n"+unicode(newsentencedic)+"\n\n\n")
				f.close()

			raise ShitError
		#del sentencedic[nodenum]

		if debug>2:
			print "result:((((((((((((("
			for i in sorted(newsentencedic):
				print i,newsentencedic[i]
			print ")))))))))))))))"
			
		return newsentencedic
	
def checkSentence(iii,s,corrlist,rec=0):
	"""
	iii: index of sentence in the sentence list
	s: sentence in nodedic format
	rec: recursion level
	"""
	#print "_____________________________________________"
	#if rec>10:
		#print s,rec
		#1/0
	if debug: print "checking sentence",iii,"rec",rec,"len(s)",len(s)
	
	
	# TODO: kick out:
	if rec<1:
		#print "____________________________"
		#for i in s:	print i,s[i][tokenname], s[i]
		#print "____________________________"
		
		
		for i in s:
			#print s
			if "gov" in s[i]:
				for g in s[i]["gov"]:
					if s[i]["gov"][g].endswith("_invisible"):
						s[i]["gov"][g]=s[i]["gov"][g].replace("_invisible","_inherited")
	
	
	if rec<3:
				
		
		for i in sorted(s):
			
			if debug>1:
				try:
					print "checking:",i,s[i][tokenname]
					
					#s[i]
					#print s[i]["lemma"],s[i]["lemma"] in corrdic,i in s
					#print corrdic
				except:
					print "index",i,"is gone"
				#if s[i]['cat']=="unknown" and s[i][tokenname]=="d'":
				#if s[i][tokenname]==u"Ã©crivant":
					#print "********************************************************"
					#for i in sorted(s):
						#print s[i]
					##print s
					#1/0
			#print i,s[i]		
			
			
			if i in s:
				for matchdic,insdic in corrlist:
					if unify(s[i],matchdic):
						
						
						#( s[i]["lemma"] in corrdic or s[i][tokenname] in corrdic):
						if debug>1:
							print "Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨"
							#if s[i]["lemma"] in corrdic : print corrdic[s[i]["lemma"]]
							#else:print corrdic[s[i][tokenname]]
							print s[i]
							print "matched oooooooooooooooooooooooooooooooo"
							print matchdic
							print "Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨Ã¨"
						#if s[i]["lemma"] in corrdic : insdic=corrdic[s[i]["lemma"]]
						#else:insdic=corrdic[s[i][tokenname]]
						if isinstance(insdic,int):# glueing. in this case insdic contains the direction of the token glueing
							s = glue(copy.deepcopy(s),i,insdic)
							s = checkSentence(iii,copy.deepcopy(s),corrlist,rec+1)
						else:
							s = integrate(copy.deepcopy(s),i,insdic)
							if len(insdic)>1:
								s=checkSentence(iii,copy.deepcopy(s),corrlist,rec+1)
			#elif i in s and  ( s[i]["lemma"] in gluedic or s[i][tokenname] in gluedic) :
				#if debug>1:
					#print "gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
					#if s[i]["lemma"] in gluedic :print gluedic[s[i]["lemma"]]
					#else:print gluedic[s[i][tokenname]]
					#print "gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
				#if s[i]["lemma"] in gluedic : insdic=gluedic[s[i]["lemma"]]
				#else:insdic=gluedic[s[i][tokenname]]
				#news = glue(s,i,insdic)
				#s=checkSentence(iii,news,corrlist,rec+1)
			
			
	return s			


def correctionDics(correctionfilename):
	
	if debug: print "reading the correction file",correctionfilename
	corrf=codecs.open(correctionfilename,"r","utf-8")
			
	corrlist=[]
	#currtoken=None
	currdic={}
	currconlltree=""
	
	for line in corrf:
		line=line.split("#")[0] # comments
		#if (line.strip()+" ")[0]!="#":
		if line.strip():
			#if "#" in line: 
			if currdic: # second or further line of a block
				if line.startswith("<-"):
					corrlist+=[(currdic,-1)]
					currconlltree=""
					currdic={}
				elif line.startswith("->"):
					#gluedic[currtoken]=1
					corrlist+=[(currdic,1)]
					currconlltree=""
					currdic={}
				else:
					currconlltree+=line
			else: # first line of a block
				trucs=line.split("\t")
				if len(trucs)>3:
					_,currtoken,currlemma,currcat=trucs[:4] # [0].strip()
					currdic={}
					for (a,v) in [(tokenname,currtoken),("lemma",currlemma),("cat",currcat)]:
						v=v.strip()
						if v: currdic[a]=v
					
				else:
					print "strange line:",line
					currconlltree=""
					currdic={}	
		else: # empty line
			if currconlltree.strip():
				corrlist+=[(currdic,correcttree2nodedic(currconlltree))]
				#corrdic[currtoken]=correcttree2nodedic(currconlltree)			
			
			currconlltree=""
			currdic={}		
	if debug:
		for matchdic,insdic in corrlist:
			print matchdic
			if isinstance(insdic, int):
				print insdic
			else:
			
				for i in sorted(insdic):
					print i,insdic[i]
			print "_______________"
	
			
	return corrlist



def correctRhapsodie(rhapsodiexmlfilename,corrlist):
	"""
	
	
	"""
	sentences, sentencefeatures = rhapsodie2Sentences(rhapsodiexmlfilename)
	
	newsentences = [checkSentence(i,s,corrlist) for i,s in enumerate(sentences)]
	
	#TODO: get rid of this:
	for sf in sentencefeatures:
		for atr in sf:
			if atr=="markupIU":
				sf["markupIU"]=sf["markupIU"].replace(u"ESPERLUETTE",u"&")
	
	#newsentences = [checkSentence(i,s,corrlist,gluelist) for i,s in enumerate([sentences[10]])]
	#newsentences=[sentences[10]]
	print "end:correctRhapsodie_______________",rhapsodiexmlfilename,"\n\n"
	
	return newsentences, sentencefeatures


def findspacelexemes(filename,spacelemmas):
		
		#db,cursor=self.open()
		doc = minidom.parse(filename)
		
		pivs, tokens, lexemes, deps, nodedics = {},{},{},{},{}
		wtypedic={"pivot":pivs,"tokens":tokens,"lexemes":lexemes}
		dtypedic={"syntax":deps}
		
		# all dictionaries look like this: u'lex565': (<DOM Element: word at 0xb617e2cc>, 566)
		for wordgroup in doc.getElementsByTagName("words"):# looking at all the wordgroups
			currentdic = wtypedic.get(wordgroup.getAttribute("type"),None) # if they are mentioned in the wtypedic, read them in
			if currentdic=={}:
				order=0
				for w in wordgroup.getElementsByTagName("word"):
					order+=1
					currentdic[w.getAttribute("id")]=w,order
		
		depcounter = 0
		sentences=[]
		for depgroup in doc.getElementsByTagName("dependencies"):
			currentdic = dtypedic.get(depgroup.getAttribute("type"),None)
			if currentdic=={}: # i found the syntax depgroup
				for dependency in depgroup.getElementsByTagName("dependency"): # for each dependency = one sentence
					
					lexiddic = {}
					nodes={}
					
					for link in dependency.getElementsByTagName("link"): 
					# this takes all the links and all the depids mentioned here and adds them as words. unlinked words are not taken into account. no testing on govids either...
						l,order=lexemes.get(link.getAttribute("depid"),None)
						if l: # for each dep node of the link:
														
							#print "lex::::",l,order
							#print l
							d={'lexid': link.getAttribute("depid"), tokenname: l.getAttribute("text"), 'lemma': l.getAttribute("lemma"), 'order':order, 
							'gov':{ link.getAttribute("govid"): link.getAttribute("func")}
							}
							
							#print d,d["lemma"]," " in d["lemma"] 
							
							
							
							#print spacelemmas
							
							for fs in l.getElementsByTagName("features"):
								for i in range(fs.attributes.length):
									a=fs.attributes.item(i)
									
									d[a.name]=a.value
							#if not d[tokenname] and d[tokenname]: d[tokenname] = d[tokenname]
							#print d[tokenname]
							
							if " " in d["lemma"] or "'" in d["lemma"] or "-" in d["lemma"] or "_" in d["lemma"] or " " in d[tokenname] or "'" in d[tokenname] or "-" in d[tokenname] or "_" in d[tokenname]:
								spacelemmas[d["lemma"]]=( d[tokenname],  spacelemmas.get(d["lemma"],("",0," "))[1]+1,     spacelemmas.get(d["lemma"],("",0," "))[2]+" "+filename.split("/")[-1])
								# spacelemmas has 3 elements: token, counter, file list
								
								
								#for a in fs.attributes:
									#d[a.name]=a.value
							#if nodes.get(order,{}):
								#print nodes
								#print d
								#print self.update(nodes.get(order,{}),d)
								#print "________________________________________________________"
							nodes[order]=update(nodes.get(order,{}),d)
							
						else:
							print "error! lexeme",l,"does not exist. Error in XML file."
							1/0
					
					
					
					
					
					
					nodedic={}
					lexidid = {}
					for i,(order,d) in enumerate(sorted(nodes.items())):
						nodedic[i+1]=d
						lexidid[d["lexid"]]=i+1
					for i in nodedic:
						newgov={}
						for g,f in nodedic[i].get('gov',{}).iteritems():
							if g: newgov[lexidid.get(g)]=f
							else:newgov[0]=f
						nodedic[i]['gov']=newgov

					
					#for a,v in  nodedic.iteritems():
						#print a,v["lemma"]
						#print v
					sentences+=[nodedic]
					depcounter+=1
					

		print "_____",depcounter,"dependencies"
		print "_________________________"
		print len(sentences),"sentences"				
		
		
		return spacelemmas

	
	
###################### useful::::::::::::
	
	
def findrhapsodiespacelemmas(path):
	sql = database.SQL("Rhapsodie")	
	#print sql.cleanDatabase("output_D211.xml")
	spacelemmas={}
	for infile in os.listdir(path):
		print "current file is: " + infile
		if unicode(infile).lower().endswith(".xml"):
			spacelemmas=findspacelexemes(path+infile,spacelemmas)
			# spacelemma has 3 elements: token, counter, file list

	outf=codecs.open("spacelemmas.txt","w","utf-8")
	for k in spacelemmas:
		outf.write(k+"\t"+unicode(spacelemmas[k][0])+"\t"+str(spacelemmas[k][1])+"\t"+spacelemmas[k][2]+"\n")
	outf.close()
	


def readinallrhapsodie(projectname,rhapsodiexmlfilepath,repair=False,pattern=None):
	"""
	all rhapsodiexml files found in the rhapsodiexmlfilepath are read into the projectname database via the enterRhapsodie function
	if a repair file is given, it will be used
	"""
	sql = database.SQL(projectname)
	annotatorName=sql.projectconfig["configuration"]["baseAnnotatorName"]
	eraseAllAnnos=True
	#eraseAllAnnos=False
	if repair:	corrlist=correctionDics(repair)
	else: 		corrlist=False
	
	if pattern:
		for i,infile in enumerate(glob(os.path.join(rhapsodiexmlfilepath, pattern))):
			enterRhapsodie(sql,infile,annotatorName, eraseAllAnnos,clean=True,corrlist=corrlist)
	
	else:
		files=os.listdir(rhapsodiexmlfilepath)
		for i,infile in enumerate(files):
			#print "________________________________________current file is: " + rhapsodiexmlfilepath+infile
			print "file",i+1,"of",len(files)
			if infile.lower().endswith(".xml") :
				enterRhapsodie(sql,rhapsodiexmlfilepath+infile,annotatorName, eraseAllAnnos,clean=True,corrlist=corrlist)
	print "readinallrhapsodie ok\n\n"
		
	
def readinsinglerhapsodie(projectname,rhapsodiexmlfile,eraseAllAnnos=False,repair=False):
	sql = database.SQL(projectname)
	annotatorName=sql.projectconfig["configuration"]["baseAnnotatorName"]
	eraseAllAnnos=True
	if repair:corrlist=correctionDics(repair)
	else: corrlist=False
	enterRhapsodie(sql,rhapsodiexmlfile,annotatorName, eraseAllAnnos,clean=True,corrlist=corrlist)
	print "ok"

	
	
		
		
		
		
		
		
		
		
		
		
		

def readinallmates(projectname,conlldirpath,filepattern="*.trees.conll14",eraseAllAnnos=True, steps=1000, importAnnotatorName=None, preserveSampleWithSameName=False):
	
	
	sql = database.SQL(projectname)
	#db,cursor=sql.open()
	
	for filename in glob(os.path.join(conlldirpath, filepattern)):
		print "entering",filename
		allsentences=conll.conllFile2trees(filename)
		
		for i in range(len(allsentences)/steps+1):
			 
			sentences=allsentences[steps*i:steps*(i+1)]
			
			#print sentences
			if importAnnotatorName:	annotatorName=importAnnotatorName
			else: annotatorName=sql.importAnnotatorName
			
			dbtextname = filename.split("/")[-1].decode("utf-8").replace("'","")
			if dbtextname.endswith(".conll07") or dbtextname.endswith(".conll10") or dbtextname.endswith(".conll14") or  dbtextname.endswith(".malt") or dbtextname.endswith(".tab") or dbtextname.endswith(".orfeo") or dbtextname.endswith(".conll_parse"):
				dbtextname=".".join(dbtextname.split(".")[:-1])
			if dbtextname.endswith(".trees"):
				dbtextname=".".join(dbtextname.split(".")[:-1])
				
			if dbtextname.endswith("-one-word-per-line.conll14_Parse"): # ChloÃ© ! Ã  adapter si besoin ou encore mieux gÃ©nÃ©raliser de maniÃ¨re Ã  ce que tout ce qui est devant le premier point suffit pour identifier un texte
				dbtextname=dbtextname[:-len("-one-word-per-line.conll14_Parse")]
			
			
				
				
			if steps<len(allsentences):
				dbtextname+=".no"+str(i)
				print dbtextname
			#textid = sql.enter(cursor, "texts",["textname"],(dbtextname,))
			
			#enterSentences(sql,cursor,sentences,filename, textid,annotatorName,eraseAllAnnos, tokname="t" )
			simpleEnterSentences(sql, sentences, dbtextname, annotatorName, eraseAllAnnos=True, sentencefeatures=True, preserveSampleWithSameName=preserveSampleWithSameName)
			
	#db.commit()
	#db.close()
	
	
	
	
	

def readinallconll(projectname,conllfilepath,useFileInfo=True,eraseAllAnnos=False):
	"""
	all conll10 files found in the conllfilepath are read into the projectname database
	"""
	sql = database.SQL(projectname)
	annotatorName=sql.projectconfig["configuration"]["importAnnotatorName"]
	#print sql.cleanDatabase("output_D211.xml")
	files=os.listdir(conllfilepath)
	for i,infile in enumerate(files):
		print "\n\n________________________________________current file is: " + conllfilepath+infile,"file",i+1,"of",len(files)
		if infile.lower().endswith(".conll10")  or infile.lower().endswith("-dependanaly.txt") or infile.lower().endswith(".trees.conll14"):
			#enterConll(dbname,filename,annotatorName=None, eraseAllAnnos=False, dbtextname=None): 
			if useFileInfo:
				enterConll(projectname,conllfilepath+infile, eraseAllAnnos)
			else: # todo: correct:
				enterConll(projectname,conllfilepath+infile,annotatorName, eraseAllAnnos)
			print "ok"
	
	
def readinsingleconll(projectname,conllfile,useFileInfo=True,eraseAllAnnos=False,checkExtension=True):
	"""
	all conll10 files found in the conllfilepath are read into the projectname database
	"""
	sql = database.SQL(projectname)
	annotatorName=sql.projectconfig["configuration"]["importAnnotatorName"]
	#print sql.cleanDatabase("output_D211.xml")

	print "________________________________________current file is: " + conllfile
	if conllfile.lower().endswith(".conll10") or not checkExtension:
		#enterConll(dbname,filename,annotatorName=None, eraseAllAnnos=False, dbtextname=None): 
		if useFileInfo:
			enterConll(projectname, conllfile, eraseAllAnnos)
		else: # todo: correct:
			enterConll(projectname,conllfile,annotatorName, eraseAllAnnos)
		print "ok"
	else:
		print "skipped",conllfile


def writeConfigs(project,allcats,allfuncs):
	with codecs.open("projects/"+project+"/categories.config","w","utf-8") as catfile:
		with codecs.open("projects/"+project+"/functions.config","w","utf-8") as funcfile:
			for cat in sorted(allcats):
				catfile.write(cat+'	{"fill": "#69399d"}\n')
			for func in sorted(allfuncs):
				funcfile.write(func+'	{"stroke": "#000","stroke-width":"1","stroke-dasharray": ""}\n')
			funcfile.write('attention	{"stroke": "#DD137B","stroke-width":"1","stroke-dasharray": "- "}\n')

					
def computeDifference(tree):
	"""
	for a conll 14 tree with gold + parse results (lemma + lemma2, tag + tag2, ...)
	gives back scores: (l, t, g, f, len(tree) )
	l: number of correct lemmas
	t: number of correct tags
	g: number of correct govs
	l: number of correct function labels
	len(tree)
	"""
	l,t,g,f=0.0,0.0,0.0,0.0
	for i in tree:
		node=tree[i]
		if node["lemma"]==node["lemma2"]:	l+=1
		if node["tag"]==node["tag2"]:		t+=1
		if node["gov"].keys()[0]==node["gov2"].keys()[0]:
			g+=1
			if node["gov"].values()[0]==node["gov2"].values()[0]:
				f+=1
	#length=len(tree)
	#return length,l/length,t/length,g/length,f/length
	return l,t,g,f,len(tree)
		
		
		
						
def readInTestResults(projectname,conllfilename,gold="gold",parser="parser"):
	"""
	reads in the result of the mate transition parser into visually comparable results
	the result file is a conll 14 files with double annotation (lemma + lemma2, ...)
	"""
	print "________________________________________current file is: " + conllfilename
	shortname = conllfilename.split("/")[-1]
	if projectname: # really modifying the database
		
		sql = database.SQL(projectname)
		db,cursor=sql.open()
		textid = sql.enter(cursor, "texts",["textname"],(shortname,))
		goldid = sql.enter(cursor, "users",["user","realname"],(gold,gold,))
		parserid = sql.enter(cursor, "users",["user","realname"],(parser,parser,))
		
		
		sents={} # score of correct functions -> list of trees having that score. allows to sort by annotation quality. the list of trees are triples: ( goldtree,parsetree,info )
		goldsentences,parsesentences,sentencefeatures=[],[],[]
		l,t,g,f,length=0.0,0.0,0.0,0.0,0
		allcats,allfuncs={},{}
		
		sentences=conll.conllFile2trees(conllfilename)
		#print len(sentences)
		for tree in sentences:
			tl,tt,tg,tf,tlength=computeDifference(tree)
			#"lemma precision:"+str(round(tl/tlength,2))+
			info = " LAS:"+str(round(tf/tlength,3))+" UAS:"+str(round(tg/tlength,3))+" pos precision:"+str(round(tt/tlength,3))+u" nÂ° tokens:"+str(tlength)
			
			
			goldtree,parsetree={},{}
			for i in tree:
				node=tree[i]
				goldtree[i]= {"id":node["id"],"t":node["t"],"lemma":node["lemma"],"tag":node["tag"],"morph":node["morph"],"gov":node["gov"]}
				parsetree[i]={"id":node["id"],"t":node["t"],"lemma":node["lemma2"],"tag":node["tag2"],"morph":node["morph2"],"gov":node["gov2"]}
				allcats[node["tag"]]=None
				allcats[node["tag2"]]=None
				allfuncs[node["gov"].values()[0]]=None
				allfuncs[node["gov2"].values()[0]]=None
				
			sents[tf/tlength]=sents.get(tf/tlength,[])+[ ( goldtree,parsetree,info ) ]
			(l,t,g,f,length)=map(sum,zip((l,t,g,f,length),(tl,tt,tg,tf,tlength) ))
		#	
		globalinfo = "lemma precision:"+str(round(l/length,2))+" pos precision:"+str(round(t/length,2))+" UAS:"+str(round(g/length,2))+" LAS:"+str(round(f/length,2))+u" nÂ° tokens:"+str(length)+u" nÂ° sentences:"+str(len(sentences))	
		print globalinfo
		treec=0
		for f in sorted(sents):
			for ( goldtree,parsetree,info ) in sents[f]:
				#print f, treec
				#print f, goldtree
				goldsentences+=[goldtree]
				parsesentences+=[parsetree]
				sentencefeatures+=[{"info":info}]
				treec+=1
		print "entering",len(parsesentences),"sentences"
		enterSentences(sql,cursor,goldsentences,shortname, textid,gold,eraseAllAnnos=True, sentencefeatures=sentencefeatures,tokname="t" )
		enterSentences(sql,cursor,parsesentences,shortname, textid,parser,eraseAllAnnos=False, defaultAnnotatorName="gold", tokname="t" )
		db.commit()
		db.close()
		
		writeConfigs(projectname,allcats,allfuncs)
		
		
	else:
		#only testing
		sentences=conll.conllFile2trees(conllfilename)
		l,t,g,f,length=0.0,0.0,0.0,0.0,0
		
		for s in sentences:
			(l,t,g,f,length)=map(sum,zip((l,t,g,f,length),computeDifference(s)))
			
		print "____"
		print "lemma precision:",l/length,"tag precision:",t/length,"UAS:",g/length,"LAS:",f/length
		print "nÂ° tokens:",length,"nÂ° sentences:",len(sentences)
		#print computeDifference(sentences[1])
		print sentences[1]
		
def corrigerNumerotation(arbre):
	indexcorrection = {0:0} # ancien indice --> nouvel indice
	problem = False
	for compteur, ind in enumerate(sorted(arbre.keys())):
		indexcorrection[ind]=compteur+1
		if compteur+1 != ind:
			problem = True
	if problem:
		arbrecorrige = conll.Tree()
		for i, node in arbre.iteritems():
			#print"before", node["gov"]
			node["id"]=indexcorrection[i]
			newgov={}
			for gi,f in node["gov"].iteritems():
				newgov[indexcorrection[gi]]=f
			node["gov"]=newgov
			#print "after",node["gov"]
			arbrecorrige[indexcorrection[i]]=node
		return arbrecorrige
	return arbre
	
	
def unclutter(conllfilename):
	"""
	separate trees that cross branches or join the same conll package
	"""
	trees=conll.conllFile2trees(conllfilename)
	print len(trees),"sentences read"
	newtrees = []
	for tree in trees:
		rootindices = [i for i in tree if tree[i].get('gov',{}).keys()[0] not in tree]
		if len(rootindices)>1:
			ptrees={}
			ordreptrees=[]
			for i in tree:
				root=i
				while tree[root].get('gov',{}).keys()[0] in tree:
					root = tree[root].get('gov',{}).keys()[0]
				if root not in ordreptrees: ordreptrees+=[root]
				ptrees[root]=ptrees.get(root,[]) + [i]
			for r in ordreptrees:
				ptree=conll.Tree()
				for nr,jj in enumerate(ptrees[r]):
					ptree[jj]=tree[jj].copy()
				ptree = corrigerNumerotation(ptree)
				newtrees+=[tree]
		
		else:
			newtrees+=[tree]
	
		
	conll.trees2conllFile(newtrees, conllfilename+".uncluttered")
	print "written",len(newtrees),"sentences into",conllfilename+".uncluttered"

					
	
#readAlignColumns("WAZK_11_M_Chiagozie's-Life-Story_TRANS.eaf.csv")

if __name__ == "__main__":
	print "bonjour"
	#unclutter("../projects/Naija/export/IBA_07_D.conllu")
	#unclutter("27_JD_CP_100226.lastHumanTrees.conll")
	#unclutter("test-split-root")
	
	
	#readinsinglerhapsodie("Rhapsodie","/home/kim/Dropbox/Rhapsodie/rhapsodie-kimsPostcomputing/cool/D0001.absolutely.cool.xml")
	
	#readinallconll("Rhapsodie","projects/Rhapsodie/verylastexport/",useFileInfo=True,eraseAllAnnos=False)
	
	#readinsingleconll("Rhapsodie","projects/Rhapsodie/lastexport/M014.XML.cricriben.trees.conll10",useFileInfo=True,eraseAllAnnos=False)
	
	#sql = SQL("Rhapsodie")	
	#sql = SQL("berlin")	
	#sql = SQL("xinying")
	#print sql.cleanDatabase("output_D211.xml")
	
	#readinallconll("xinying","corpus/conll/conll/")
	
	#readinsingleconll("Occitan","corpus/occitanConll/Boece.conll10",useFileInfo=False,eraseAllAnnos=True)
	#sql.correctRhapsodie("corpus/xml/M201.XML","spacelemmas5.txt")
	#enterConll("Rhapsodie","corpus/conll/M016.XML.Sy.trees.conll10")
	#findrhapsodiespacelemmas("/home/kim/Dropbox/Rhapsodie/syntaxe-julie/xml_ok/")
	#readinsingleconll("Rhapsodie","projects/Rhapsodie/lastexport/D020.XML.Sy.trees.conll10",useFileInfo=True,eraseAllAnnos=False)
	
	#,annotatorName="Sy", eraseAllAnnos=False, dbtextname="M016.XML")
	#print sql.cleanDatabase()
	
	#sql.enterRhapsodie("corpus/xml/output_D211.xml")
	#sql.enterRhapsodie("corpus/xml/output_M024.xml")
	
	#sql.enterConll("corpus/conll/conll/4.csv.110.conll10.semantics.conll10")
	
	#readinallconll("lingCorpus","/home/kim/Documents/newmate/testimportdeps/")
	#readinsingleconll("lingCorpus","/home/kim/Documents/newmate/testimportdeps/Shi_Yu-dependanaly.txt",useFileInfo=False,eraseAllAnnos=True,checkExtension=False)
	
	
	
	#readinallmates("lingCorpus","/home/gerdes/arborator/corpus/coursLingCorpus/newparses/",filepattern="*Fum*")
	#readinallmates("linguistiqueCorpus-Echantillons-2017","/home/gerdes/arborator/corpus/coursLingCorpus/2017parses3/",filepattern="*", preserveSampleWithSameName=True)
	#readinallmates("decoda","/home/gerdes/arborator/corpus/decoda/",filepattern="decoda.test*")
	
	#readinallmates("lingCorpus","/home/gerdes/arborator/corpus/2015lingCorpus/",filepattern="*")
	#readinallmates("test_mini_db","corpus/mate/",filepattern="*_Parse")
	#readinallmates("platinum","corpus/platinum/",filepattern="*")
	#readinallmates("Platinum","../projects/Platinum/exportcorrected/",filepattern="*",steps=10000,importAnnotatorName="gold")
	#readinallmates("lingCorpus2016","../tools/parses/",filepattern="*conll_parse",steps=10000,importAnnotatorName="parser")
	#readinallmates("Naija","../tools/parses/",filepattern="*_parse",steps=10000,importAnnotatorName="parser")
	#readinallmates("ExoNaija","../corpus/conll/",filepattern="Exercice*conllu",steps=10000)
	#readinallmates("Uppsala","../corpus/Uppsala/",filepattern="*conllu",steps=10000,importAnnotatorName="parser")
	readinallmates("NaijaSUD","../corpus/",filepattern="WAZK_11_M_Chiagozie's-Life-Story_TRANS.eaf.csv",steps=10000,importAnnotatorName="parser")
		
	#readInTestResults(None,"/home/kim/Documents/newmate/canons/result-chin-canon-S2a-40-0.25-0.1-2-2-ht4-hm4-kk0-1")
	#readInTestResults("canons","/home/kim/Documents/newmate/canons/result-chin-canon-S2a-40-0.25-0.1-2-2-ht4-hm4-kk0-1")
	
	
	
	
	#enterDegraded("linguistiqueCorpus-Exo","/home/kim/Dropbox/programmation/arborator/trunk/projects/SyntaxeAlOuest/export/1a.orig")
	#enterDegraded("SyntaxeAlOuest","corpus/conll/2c.txt.conll_parse.orig","corpus/conll/2c.txt.conll_parse.deg")
	
	#from database import SQL
	#trees=conll.conllFile2trees("../tools/annotatedCorpora.conllu")
	#simpleEnterSentences(SQL("Naija"), trees, "Naija1", "prof", eraseAllAnnos=True)
	#print "ok"
	
	
	
 
#! usr/bin/python
#! coding: utf-8

import conll, datetime, codecs, time, sys, os, glob, tqdm
from database import SQL
from pprint import pprint

"""
TODO :
- documenter scripts
- mettre en place le cron
"""

#ti = time.time()-86400




def exportUniqueSentences(project, mode="lasttree",pattern=False):
	"""
	exports one tree per sentences: the first time the sentence is found, the newest tree
	"""
	sql = SQL(project)
	db,cursor=sql.open()
	sentences={} # toks -> tree
	outdir=os.path.join("..","projects",project,"export")
	try: os.mkdir(outdir)	
	except OSError: pass
	outfile=os.path.join(outdir,"allSentences.conll")
	if pattern:
		command="""select trees.rowid,userid,max(timestamp) from trees, sentences, texts where texts.rowid=sentences.textid and sentences.rowid=trees.sentenceid
		and textname like "{pattern}"
		group by sentenceid order by trees.rowid;""".format(pattern=pattern)
	else:
		command="""select trees.rowid,userid,max(timestamp) from trees, sentences, texts where texts.rowid=sentences.textid and sentences.rowid=trees.sentenceid
		group by sentenceid order by trees.rowid;"""
	for i,(treeid,userid,timestamp,) in enumerate(cursor.execute(command).fetchall()):
		tree=sql.gettree(treeid=treeid, indb=db, incursor=cursor)["tree"]
		toks=tuple(tree[i]["t"] for i in tree)
		print "___",i, "\r",
		if toks not in sentences:
			sentences[toks]=tree
	print "writing file with",len(sentences),"sentences..."
	conll.trees2conllFile([sentences[toks] for toks in sorted(sentences)], outfile=outfile, columns=10)
	return outfile


def exportGoodTexts(project, lastHuman=False, onlyValidated=True, pattern=False):
	"""
	TODO :
	- ajouter parametre p/selectionner Texte
	ex : "UD_ZH_[number]"
	"""
	outfiles=[]
	sql = SQL(project)
	db,cursor=sql.open()
	goodTexts={}
	if onlyValidated:onlyValidated="and todos.status=1"
	else: onlyValidated=""
	# take all texts where a validator has validated
	if pattern: command="select distinct * from texts, todos, users where texts.rowid=todos.textid and users.rowid=todos.userid and texts.textname {pattern};".format(pattern=pattern) # like 'UD_ZH%'
	else:
		command="select distinct * from texts, todos, users where texts.rowid=todos.textid and todos.type=1 {onlyValidated} and users.rowid=todos.userid;".format(onlyValidated=onlyValidated)

	for row in cursor.execute(command):
		textname, nrtokens, userid, textid, validator, status, comment, user, realname = row


		goodTexts[textid]=(textname, userid, user)
		print "i'll take",textname,"validated by",user,"with", nrtokens, "tokens"
	sentenceValidationInValidatedText(cursor, sql, db)
	outdir=os.path.join("..","projects",project,"export")
	try: os.mkdir(outdir)
	except OSError: pass

	for textid,(textname, userid, user) in goodTexts.iteritems():
		textname=textname.replace("-one-word-per-line.conll14_Parse","")

		if lastHuman: outfile=os.path.join(outdir,textname+".lastHuman.conll")
		else: outfile=os.path.join(outdir,"validated."+textname+"."+user+".conll")
		print "doing",textname,textid
		trees=[]

		if lastHuman:
			snr2all={}
			for row in cursor.execute("""
			select sentences.nr as snr, trees.rowid as treeid, users.user, trees.timestamp
			from sentences, trees, users
			where sentences.textid=?
			and sentences.rowid=trees.sentenceid
			and users.rowid = trees.userid; """,(textid,)):
				snr,treeid,user,timestamp=row
				snr2all[snr]=snr2all.get(snr,[])+[(timestamp,user,treeid)]
			lastpourc=-1
			for c,snr in enumerate(sorted(snr2all)):
				pourc = int(float(c)/len(snr2all)*100)
				if pourc!=lastpourc: print "___{pourc}%___\r".format(pourc=pourc),

				lastusersnotparser=sorted([(timestamp,user,treeid) for (timestamp,user,treeid) in snr2all[snr] if user not in ["parser","mate"]])
				if len(lastusersnotparser) > 0:
					time, u, tid = lastusersnotparser[-1] # last tree by human
				else:
					time, u, tid = sorted(snr2all[snr])[-1] # last tree by whoever
				#print "je prends l'arbre de",u
				trees+=[sql.gettree(treeid=treeid, indb=db, incursor=cursor)["tree"]]

		else:

			for (treeid,sentencenr,) in cursor.execute("select trees.rowid, sentences.nr from texts, trees, sentences where texts.rowid=? and trees.userid=? and trees.sentenceid = sentences.rowid and sentences.textid=texts.rowid order by sentences.nr;",(textid,userid,)).fetchall():
				#print "ooo",sentencenr,"\r",
				print "nr",sentencenr,"_____\r",
				trees+=[sql.gettree(treeid=treeid, indb=db, incursor=cursor)["tree"]]

		print "exporting",len(trees),"trees into",outfile
		outfiles+=[outfile]
		conll.trees2conllFile(trees, outfile, columns=10)
	return outfiles

def getTreesForSents(sents, trees, annotators, parserid, cursor, db, sql):
	"""
	for each entry in sents,
	adds new trees to trees and annotators
	"""
	for i,(sid,nr,sentence,teid) in enumerate(sents):
		print "\r",i,
		if parserid>=0:
			tr=list(cursor.execute("select trees.rowid,* from trees, users where sentenceid=? and userid<>? and userid=users.rowid order by timestamp DESC limit 1;",(sid,parserid)).fetchall())
		else:
			tr=list(cursor.execute("select trees.rowid,* from trees, users where sentenceid=? and userid=users.rowid order by timestamp DESC limit 1;",(sid,)).fetchall())
		#print trees
		if len(tr):
			treeid, sidd, usid, annotype, status, comment, ts, user, realname=tr[0]
			tree=sql.gettree(treeid=treeid, indb=db, incursor=cursor)["tree"]
			#print tree
			trees+=[conll.Tree(tree)]
			annotators[user]=annotators.get(user,0)+1
		#print sentence,trees


def lastTreeForAllSamples(project, onlyHuman=True, combine=False):
	outdir=os.path.join("..","projects",project,"export")
	try: os.mkdir(outdir)
	except OSError: pass
	sql = SQL(project)
	db,cursor=sql.open()
	if onlyHuman:
		parserid=0
		for pid, in cursor.execute("select rowid from users where user='parser';"): parserid=pid
	else:
		parserid=-1
	sents=list(cursor.execute("select rowid, * from sentences;").fetchall())
	print "todo:",len(sents),"sentences"

	annotators={}

	if combine:
		trees=[]
		getTreesForSents(sents, trees, annotators, parserid, cursor, db, sql)
		outfile=os.path.join(outdir,project+".lastHumanTreeForAllSamples.conll")
		conll.trees2conllFile(trees, outfile=outfile, columns=10)
		print "wrote",outfile

	else:
		for tid,textname,nrtokens in list(cursor.execute("select rowid, * from texts;")):
			print tid, textname, nrtokens
			sents=list(cursor.execute("select rowid, * from sentences where textid=?;",(tid,)).fetchall())
			trees=[]
			getTreesForSents(sents, trees, annotators, parserid, cursor, db, sql)
			if textname.endswith(".conll_parse"): textname=textname[:len(".conll_parse")]
			outfile=os.path.join(outdir,textname+".lastHumanTrees.conll")
			conll.trees2conllFile(trees, outfile=outfile, columns=10)
			print "wrote",outfile
	print annotators


def getSpecificTrees(sql, db, cursor, nrutids, annotatorIds):
	trees=[]
	for nr in sorted(nrutids): # for each sentence
		tree=None
		for aid in annotatorIds: # for each interesting annotator id
			if aid in nrutids[nr]:
				#tree=)
				tree=conll.Tree(sql.gettree(treeid=nrutids[nr][aid], indb=db, incursor=cursor)["tree"])
				trees+=[tree]
				#print "atree:",tree
				break
		if not tree:
			print "problem: no tree for nr",nr,"type",type(nr)
			print "annotatorIds",annotatorIds
			return []
			#raise Exception('no tree', nr)
	return trees


def exportConllByAnnotators(project, annotators=["prof","Sy","parser"], fileExtension=".conllu"):
	"""
	exports complete project
	for every sentence, trees of annotators in given order.
	if no tree: throw error

	"""
	outfiles=[]
	sql = SQL(project)
	db,cursor=sql.open()
	goodTexts={}
	outdir=os.path.join("..","projects",project,"export")
	try: os.mkdir(outdir)
	except OSError: pass
	try:	annotatorIds=tuple(a for (a,) in [list(cursor.execute("select rowid from users where user =?;",(annotator,) ))[0] for annotator in annotators])
	except:
		print "some required annotator IDs are not in the database"
		return
	print annotators, annotatorIds

	for textid, textname, nrtokens in list(cursor.execute("select rowid, * from texts;" )):  # for each text
		print "doing",textname,"with", nrtokens, "tokens"
		nrutids={}
		for nr,userid,treeid in list(cursor.execute("select nr,userid,trees.rowid as treeid from trees, sentences where sentenceid=sentences.rowid and userid in {annotatorIds} and  textid = ? order by nr;".format(annotatorIds=annotatorIds if len(annotatorIds)>1 else '('+str(annotatorIds[0])+')'),(textid,))):
			nrutids[nr]=nrutids.get(nr,{})
			nrutids[nr][userid]=treeid
		trees=getSpecificTrees(sql, db, cursor, nrutids, annotatorIds)
		if trees:
			if textname.endswith(".conll"): textname=textname[:-len(".conll")]
			if textname.endswith(".conllu"): textname=textname[:-len(".conllu")]
			outfile=os.path.join(outdir,textname+fileExtension)
			conll.trees2conllFile(trees, outfile=outfile, columns=10)
			print len(trees),"trees"
			outfiles+=[outfile]
		else:
			print "skipped",textname
	return outfiles

lemmacorrection=dict( [(a.split(":")[0],a.split(":")[1]) for a in u"""
c' est-Ã -dire que:c'est-Ã -dire que
est-ce-que:est-ce que
quelques-un:quelques-uns
Ax-~~:Ax~
festiv-Ã©tÃ©:Festiv-Ã©tÃ©
t-il:il
ouvre-moi:ouvrir
trouve-moi:trouver
rappelle-moi:rappeler
dÃ©brouille-toi:dÃ©brouiller
met-il:mettre
es-tu:Ãªtre
doit-on:devoir
moment-lÃ :moment
gars-lÃ :gars
jour-lÃ :jour
bruit-lÃ :bruit
mois-lÃ :mois
festival-lÃ :festival
cÃ´tÃ©-lÃ :cÃ´tÃ©
jeunes-lÃ :jeunes
niveau-lÃ :niveau
celui-lÃ :celui
lui-mÃªme:lui
sapeurs-pompier:sapeur-pompier
proposition-lÃ :proposition
petit-Ã -petit:petit Ã  petit
groupe-lÃ :groupe
titre-lÃ :titre
stylo-lÃ :stylo
pays-lÃ :pays
sens-lÃ :sens
faut-il:falloir
passe-moi:passer
appelez-le:appeler

""".strip().split("\n")])

def correctLemmas(tree):
	for iii in tree:
		tree[iii]["tag2"]="_"
		if tree[iii]["lemma"] in lemmacorrection:
			tree[iii]["lemma"]=lemmacorrection[tree[iii]["lemma"]]


def fusionForgottenTrees(project="Platinum",fusdir="../projects/OrfeoGold2016/platinum/*", annotators=["admin"]):
	"""
	takes trees from project ordered by annotators. if they exist fuse them into the fusdir
	result has the extension "cool.conll"
	,"Sy","Marion"
	"""

	#print lemmacorrection
	sys.path.insert(0, '../tools')
	import difflib
	outfiles=[]
	sql = SQL(project)
	db,cursor=sql.open()
	goodTexts={}
	outdir=os.path.join("..","projects",project,"exportcool")
	try: os.mkdir(outdir)
	except OSError: pass
	for annotator in annotators:
		print [list(cursor.execute("select rowid from users where user =?;",(annotator,) ))]
	annotatorIds=tuple(a for (a,) in [list(cursor.execute("select rowid from users where user =?;",(annotator,) ))[0] for annotator in annotators])
	print annotators, annotatorIds

	for textid, textname, nrtokens in list(cursor.execute("select rowid, * from texts;" )):  # for each text
		print "\n__________________________doing",textname,"with", nrtokens, "tokens"
		nrutids={}
		for nr,userid,treeid in list(cursor.execute("select nr,userid,trees.rowid as treeid from trees, sentences where sentenceid=sentences.rowid and userid in {annotatorIds} and  textid = ? order by nr;".format(annotatorIds=annotatorIds),(textid,))):
			nrutids[nr]=nrutids.get(nr,{})
			nrutids[nr][userid]=treeid
		trees={}
		for nr in sorted(nrutids): # for each sentence
			tree=None
			for aid in annotatorIds: # for each interesting annotator id
				if aid in nrutids[nr]:
					tree=sql.gettree(treeid=nrutids[nr][aid], indb=db, incursor=cursor)["tree"]
					trees[nr]=tree
					#print "atree:",tree
					break
			#if not tree:
				#print "problem: no tree for nr",nr,"type",type(nr)
				#print "annotatorIds",annotatorIds
				#raise Exception('no tree', nr)
		#print trees
		print len(trees),"trees from", project
		print textname,textname.split(".")[0]
		btextname=os.path.basename(textname).split(".")[0]
		if btextname.endswith("-one-word-per-line"): btextname=btextname[:-len("-one-word-per-line")]
		#print glob.glob(fusdir),[os.path.basename(fi).split(".")[0] for fi in glob.glob(fusdir)]
		cooltrees=[]
		ptrees,ftrees=0,0
		for fi in glob.glob(fusdir):
			if btextname == os.path.basename(fi).split(".")[0] :
				print "yes",btextname
				fustrees=conll.conllFile2trees(fi)
				print len(fustrees),"ftrees",fi
				for nr,ftree in enumerate(fustrees):
					if nr+1 in trees:
						#print "added tree",nr+1,"from database"
						#ptree=platinum(trees[nr+1])
						ptree=trees[nr+1]
						for iii in ptree:
							ptree[iii]["tag2"]="_"
							if ptree[iii]["lemma"] in lemmacorrection:
								ptree[iii]["lemma"]=lemmacorrection[ptree[iii]["lemma"]]
						cooltrees+=[ptree]
						#print nr+1,"tree from",project#,tree
						ptrees+=1
						if ftree.sentence() != u" ".join([ptree[i].get("t","") for i in sorted(ptree)]):
							print "\n_________",nr+1
							print ftree.sentence()
							print u" ".join([ptree[i].get("t","") for i in sorted(ptree)])
							#for l in difflib.context_diff(ftree.sentence() ,u" ".join([ptree[i].get("t","") for i in sorted(ptree)])):print l

						#print "dbtree",platinum(trees[nr+1])
					else:
						for iii in ftree:
							ftree[iii]["tag2"]="_"
							if ftree[iii]["lemma"] in lemmacorrection:
								ftree[iii]["lemma"]=lemmacorrection[ftree[iii]["lemma"]]
						#print nr+1,"tree from",fusdir#,tree
						ftrees+=1
						cooltrees+=[ftree]
						#print "added tree",nr+1,"from fustrees",fi
				outfile=os.path.join(outdir,textname+".cool.conll")
				conll.trees2conllFile(cooltrees, outfile=outfile, columns=10)
				print "wrote",outfile
				print ptrees,"ptrees, ",ftrees,"ftrees"
				break
		if len(cooltrees)==0:print "nothing for",btextname
		outfiles+=[outfile]
		#qsdf
	return outfiles

def createNonExistingFolders(path):
	print "createNonExistingFolders",path
	head, tail = os.path.split(path)
	if head=="":
		if tail and not os.path.exists(tail): os.makedirs(tail)
	else:
		createNonExistingFolders(head)
	if head and not os.path.exists(head): os.makedirs(head)

def addArbitraryPuncs(infolder,outfolder):
	createNonExistingFolders(outfolder)
	for conllinfile in glob.glob(os.path.join(infolder, '*')):
		if os.path.isfile(conllinfile):
			print conllinfile
			trees=conll.conllFile2trees(conllinfile)
			for i,tree in enumerate(trees):
				m=max(tree)
				splitcode=".,!?;:()"
				p=splitcode[i%len(splitcode)]
				tree[m+1]={u'tag': u'PUNC', u'lemma': p, u't': p, 'gov': {0: u'punc'}}
			conll.trees2conllFile(trees, os.path.join(outfolder,os.path.basename(conllinfile)), columns=14)

def splitSentenceFile(infile,splitcode="abc"):
	"""
	splits the lines of a file in different files with the extensions given as letters in splitcode
	careful: "a"=append mode -> next run it adds to the existing file!
	"""
	with codecs.open(infile,"r","utf-8") as f:
		for c,li in enumerate(f):
			print c, li, c%len(splitcode)
			with codecs.open(infile.split(".")[0]+"."+splitcode[c%len(splitcode)],"a","utf-8") as out:
				out.write(li)


def databaseQuery(cursor,table="all", values=[]):

	if table == "all":
		command="select distinct trees.rowid, texts.textname, users.user, sentences.nr, trees.* from texts, trees, sentences, users where trees.status = 1 and trees.sentenceid = sentences.rowid and sentences.textid=texts.rowid and trees.userid=users.rowid ;"
	elif table == "validator":
		command="select distinct trees.rowid, texts.textname, users.user, sentences.nr, trees.* from texts, trees, sentences, users, todos where trees.status = 1 and trees.sentenceid = sentences.rowid and sentences.textid=texts.rowid and trees.userid=users.rowid and todos.userid=users.rowid and todos.type=1 and todos.textid=texts.rowid;"

	elif table=="duplicate":
		command="""
		select sentences.rowid as sid, trees.rowid as treeid, users.user as annotator, todos.userid as validatorid, trees.timestamp
		from sentences, todos, trees, users where
		todos.status=1 and todos.type=1 -- we want texts that are marked ok (status=1) by validators (type=1) not just vulgar annotators (type=0)
		and sentences.textid=todos.textid
		and sentences.rowid=trees.sentenceid
		and users.rowid = trees.userid
		and sentences.rowid not in  (select sentenceid from trees,  (select sentences.rowid as y from sentences, todos  where todos.status=1 and todos.type=1 and sentences.textid=todos.textid) as x  where x.y = trees.sentenceid and trees.status = 1);

		"""


	cursor.execute(command, values)
	a = cursor.fetchall()
	return a

def sentenceValidationInValidatedText(cursor, sql, db):
	print "sentenceValidationInValidatedText: consolidating the database by creating trees for validators that don't have all the trees for texts they have marked as ok"
	sids2all={}
	answer=databaseQuery(cursor, table="duplicate")
	for nr, (sid, treeid, user, validid, timestamp) in enumerate(answer):
		sids2all[sid]=sids2all.get(sid, [])+[(treeid, user, validid, timestamp)]
	lastpourc=-1
	for c,sid in enumerate(sids2all):
		pourc = int(float(c)/len(sids2all)*100)
		if pourc!=lastpourc: sys.stdout.write( "{pourc}%\r".format(pourc=pourc) )
		sys.stdout.flush()
		#print "sid",sid
		usernotparser=sorted([(treeid, user, validid, timestamp) for (treeid, user, validid, timestamp) in sids2all[sid] if user != "parser"])
		if len(usernotparser) > 0:
			treeid2get, _, vid, _=usernotparser[-1]
		else:
			treeid2get, _, vid,_ =sids2all[sid][0]
		#print treeid2get
		dic=sql.gettree(None,None,treeid2get, indb=db,incursor=cursor) # dic -> get tree == on rÃ©cupÃ¨re l'arbre
		if dic and dic["tree"]:
			sentencetree=dic["tree"]
			#print sentencetree
			wcounter, sent, newtreeid = sql.enterTree(cursor, sentencetree, sid, vid, intokenname="t")
			#print "tree number", treeid, "has been copied. New number =", newtreeid, "New annotator :", userid
			cursor.execute("UPDATE trees SET status=1 WHERE sentenceid=? AND userid=?",(sid,vid,))


def checkTree(sentencetree):
	for i, node in sentencetree.iteritems():
		if "gov" in sentencetree[i].keys():
			for gi in node["gov"]:
				if gi==i:
					return False, "self", i
				if gi > len(sentencetree):
					return False, "weird link", i
			if -1 in node["gov"]:
				return False, "nogov", i
		else:
			return False, "nogov", i
	return True, "ok", 0

def corrigerNumerotation(arbre):
	indexcorrection = {0:0} # ancien indice --> nouvel indice
	problem = False
	for compteur, ind in enumerate(sorted(arbre.keys())):
		indexcorrection[ind]=compteur+1
		if compteur+1 != ind:
			problem = True
	if problem:
		arbrecorrige = {}
		for i, node in arbre.iteritems():
			#print"before", node["gov"]
			node["id"]=indexcorrection[i]
			newgov={}
			for gi,f in node["gov"].iteritems():
				newgov[indexcorrection[gi]]=f
			node["gov"]=newgov
			#print "after",node["gov"]
			arbrecorrige[indexcorrection[i]]=node
		return arbrecorrige
	return arbre




def getValidatedTrees(project, folder, whoseTrees="validator"):
	sql = SQL(project)
	db,cursor=sql.open()
	sentenceValidationInValidatedText(cursor, sql, db)
	#on rÃ©cupÃ¨re les nouveaux arbres
	b = databaseQuery(cursor,table=whoseTrees)
	print len(b), u"trees to extract"
	sids2all={}
	trees=[]
	error_trees=[]
	textnames={}
	for nr, (treeid,textname,user, snr ,sid,uid,annotype,status,comment,timestamp) in enumerate(b):
		# TODO: remove:
		#if textname.startswith("mandarinParsed"):continue
		sids2all[sid]=sids2all.get(sid, [])+[(timestamp, textname, user, snr, treeid)]
		textnames[textname]=None
	#print len(sids2all)
	print u"trees extracted from the samples",", ".join(sorted(textnames))
	lastpourc=-1
	for c,sid in enumerate(sids2all):
		pourc = int(float(c)/len(sids2all)*100)
		if pourc!=lastpourc: sys.stdout.write( "{pourc}%\r".format(pourc=pourc) )
		sys.stdout.flush()

		snr, treeid2get=sorted(sids2all[sid])[0][-2:]
		#print treeid2get, type(treeid2get)
		#lknlk
		dic=sql.gettree(None,None,treeid2get, indb=db,incursor=cursor) # dic -> get tree
		#if treeid2get==9669:
			#print 9669,dic

		if dic:
			sentencetree=dic["tree"]
			sentencetree=corrigerNumerotation(sentencetree)
			trees.append(sentencetree)
			#print " ".join(node["t"] for i,node in sentencetree.iteritems())
			if checkTree(sentencetree)[0] == False:
				if checkTree(sentencetree)[1] == "self":
					error_trees+=["\t".join([textname, str(snr), user, "node "+str(checkTree(sentencetree)[2])+" points to itself"])]
				else:
					error_trees+=["\t".join([textname, str(snr), user, "no gov at node "+str(checkTree(sentencetree)[2])])]
				trees.remove(sentencetree)
				#print "nr arbres",len(trees)
		lastpourc=pourc
	print len(error_trees), "arbre(s) avec erreurs."
	if len(error_trees) > 0:
		print "\t".join(["Texte", "num phrase", "correcteur", "cause"])
		for x in sorted(list(set(error_trees))):
			print x
		f=codecs.open(folder+"logs/log_erreurs."+datetime.datetime.now().strftime('%Y-%m-%d')+".tsv", "w", "utf-8")
		f.write("\t".join(["Texte", "num phrase", "correcteur", "cause"])+'\n')
		for e in error_trees:
			f.write(e+'\n')
		f.close()
		print "Erreurs dans", f.name
	print len(trees), "arbres restants pour entrainement"
	#Creation d'un fichier log
	db.commit()
	db.close()
	return trees

def printTree(project,treeid):
	sql = SQL(project)
	db,cursor=sql.open()
	dic=sql.gettree(None,None,treeid, indb=db,incursor=cursor) # dic -> get tree == on rÃ©cupÃ¨re l'arbre
	#print dic
	if dic and dic["tree"]:
		sentencetree=dic["tree"]
		#sentencetree=corrigerNumerotation(sentencetree)
		for i in sorted(sentencetree):
			print i, sentencetree[i]

def split(conllfile,maxi):
	trees=conll.conllFile2trees(conllfile)
	for j,ts  in enumerate([trees[i:i+maxi] for i in range(0, len(trees), maxi)]):
		conll.trees2conllFile(ts,conllfile+str(j))


if __name__ == "__main__":
	ti = time.time()
	#project="OrfeoGold2016"
	#project="hongkongtvmandarin.2016-08-23_15:29"
	#printTree(project,9669)
	#trees = getValidatedTrees("test_mini_db_backup")
	#conll.trees2conllFile(trees, "./mate/test_200816.conll")
	#print "it took", (time.time()-ti)/60, "minutes."
	#pprint(trees2copy)
	#exportGoodTexts("OrfeoGold2016")

	#exportGoodTexts("OrfeoGold2016", lastHuman=True, onlyValidated=False)
	#addArbitraryPuncs("../projects/Platinum/exportcorrected/","../projects/Platinum/punc/")
	#splitSentenceFile("exo1sents.txt")

	#exportGoodTexts("HongKongTVMandarin", lastHuman=True, onlyValidated=False, pattern="UD_ZH%")
	#print exportConllByAnnotators("SyntaxeAlOuest2016", annotators=["prof"])
	# print exportConllByAnnotators("linguistiqueCorpus-Exo", annotators=["prof"])
	#print exportConllByAnnotators("OrfeoGold2016", annotators=["Sy","Marion","parser"])

	#split("../projects/OrfeoGold2016/platinum/Rhaps.gold",1000)
	#print len(conll.conllFile2trees("../projects/Platinum/exportcool/Rhaps"))
	#fusionForgottenTrees()

	#exportGoodTexts("Platinum", lastHuman=True, onlyValidated=False)
	#addArbitraryPuncs("../projects/Platinum/export","../projects/Platinum/punc/")
	#print exportConllByAnnotators("Platinum", annotators=["Sy","admin","gold","parser"])
	#exportUniqueSentences("Platinum", pattern="ftb%")
	#exportUniqueSentences("Platinum", pattern="ftb-3-vadvv%")
	#exportUniqueSentences("templingCorpus2016")
	#lastHumanTreeForAllSamples("Platinum")
	#lastTreeForAllSamples("templingCorpus2016", onlyHuman=False, combine=True)
	lastTreeForAllSamples("Rhapsodie", onlyHuman=False, combine=False)
#!/usr/bin/python
# -*- coding: utf-8 -*-

#######################################################################################
# this file contains functions to update the trees of a specific user, in particular for the update when a new parse model has been trained and applied

#simple usage, for example, to import a group of mate parse files into the database:
	#updateParseResult("test_mini_db", "corpus/mate/", filepattern="*_Parse")
#this will import all files in corpus/mate that fit the pattern into the database overwriting the corresponding user's tree in the corresponding sample
		
#######################################################################################

import sys, os, re, json, codecs
from time import time
from glob import glob

import database
import conll


debug=False

	
def enterNewAnnotation(sql, db, cursor, sentences, textid, annotatorName="parser", notenter=["order"], sentencefeatures={}, tokname="t"):
	"""
	takes a list of nodedics (sentences) and puts them into the database erasing an existing annotation
	
	sql, db, cursor of open database
	sentences: nodedics to enter
	textid: textid of the sample to be modified
	annotatorName: name to be modified (TODO: check what happens if annotatorName not yet present in the db)
	notenter: features to skip
	sentencefeatures: features per sentence (to be added below the sentence, for example parser information)
	
	"""
	print "entering",len(sentences),"annotations by", annotatorName,"into textid",textid
	
	
	
	userid = sql.enter(cursor, "users",["user"],(annotatorName,))
	sql.realname(annotatorName, cursor)
	if not userid:
		print "the user is not in the database"
		return
	
	
	scounter,wcounter=0,0
	ti = time()

	r = sql.getall(cursor, "sentences",["textid"], [textid])
	if len(r) != len(sentences):
		print "something fishy here:", len(sentences), "sentences to enter onto",len(r),"existing sentences"
		return
	
	for nodedic, (sentenceid,nr,sentence,textid) in zip(sentences,r): # for every sentence
		if debug: print "_____",sentenceid, nr, sentence, textid
		inssentence=" ".join([nodedic[j].get(tokname,"") for j in sorted(nodedic)])
		
		if sentence.replace(" ","") != inssentence.replace(" ",""):
			print "something fishy here:", inssentence, "analysis to be put on sentence",sentence
			return
		
		# remove everything of the existing tree
		cursor.execute("delete from features where treeid in (select trees.rowid from trees where trees.sentenceid =? and trees.userid=?);",(sentenceid,userid,))
		cursor.execute("delete from links    where treeid in (select trees.rowid from trees where trees.sentenceid =? and trees.userid=?);",(sentenceid,userid,))
		cursor.execute("delete from trees    where trees.sentenceid =? and trees.userid=?;",(sentenceid,userid,))
		
		# put the new tree in
		ws, sent, treeid = sql.enterTree(cursor, nodedic, sentenceid, userid, notenter=notenter, intokenname=tokname)
		
		if sentencefeatures:
			for a,v in sentencefeatures[i].iteritems():
				
				cursor.execute("UPDATE sentencefeatures SET value=? WHERE sentenceid=? and attr=?",(v,sentenceid,a,))# TODO: check what happens if new sentencefeature attribute
				#sql.enter(cursor, "sentencefeatures",["sentenceid","attr","value"],(sentenceid,a,v,))
			
			
	db.commit()
	print "this sample took",time()-ti,"seconds to enter"
	
	
	
	
def updateParseResult(projectname, conlldirpath, filepattern="*.trees.conll14", annotatorName="parser", removeToGetDB="-one-word-per-line.conll14_parse"):
	sql = database.SQL(projectname)
	db,cursor=sql.open()
	print  "updateTrees:",glob(os.path.join(conlldirpath, filepattern))
	for filename in glob(os.path.join(conlldirpath, filepattern)):
		print "entering",filename
		sentences=conll.conllFile2trees(filename)
		dbtextname = os.path.basename(filename)[:-len(removeToGetDB)]
		textid = sql.enter(cursor, "texts",["textname"],(dbtextname,))
		
		if not textid:
			print "couldn't find the database named",textid
			return
		enterNewAnnotation(sql, db,cursor, sentences, textid, annotatorName=annotatorName)







	
	

if __name__ == "__main__":
	print "bonjour"
	updateParseResult("OrfeoGold2016", "mate/parses/2016-07-13/", filepattern="*_Parse")
	print "ok"
	
	
	
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2011 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####



#import time, re, sha, Cookie,  sys,codecs
import os, cgitb, cgi,time, sys, glob
sys.path.append('modules')
from logintools import login
from logintools import isloggedin
from logintools import logout
from lib import config, database 


verbose=False
######################### functions

##############################################"




########################## default values

##########################

cgitb.enable()
form = cgi.FieldStorage()

#print "Content-Type: text/html\n" # blank line: end of headers

#thisscript = os.environ.get('SCRIPT_NAME', '')

thisfile = os.environ.get('SCRIPT_NAME',".")
#.split("/")[-1]

action = None
userdir = 'users/'
test = isloggedin("users/")
logint = form.getvalue("login",None)
if logint == "logout":
	#cookieheader = 
	print logout(userdir)
	#print "Content-Type: text/html\n" # blank line: end of headers
	#print ")))))))))))))))))))"
	print '<script type="text/javascript">window.location.href=".";</script>'
#else: cookieheader="rien"



#adminLevel,username = 0,"guest"


project = form.getvalue("project",None)

#project="Rhapsodie"
projectconfig=None
try:
	projectconfig = config.configProject(project) # read in the settings of the current project
except:
	pass
if not projectconfig:
	print "Content-Type: text/html\n" # blank line: end of headers
	print "something went seriously wrong: can't read the configuration of the project",project
	#print "Content-Type: text/html\n" # blank line: end of headers
	print '<script type="text/javascript">window.location.href=".";</script>'
	sys.exit("something's wrong")



action, userconfig = login(form, userdir, thisfile+'?project='+project, action)
adminLevel, username, realname = int(userconfig["admin"]),userconfig["username"],userconfig["realname"]

#cachedir = projectconfig["configuration"]["cacheFolder"]+"/"

sql=database.SQL(project)
userid = sql.userid(username, realname)
#realname = sql.realname(username)


######################   head    ###########################
#print "Content-Type: text/html\n" # blank line: end of headers


print """
<html>
	<head><title>Arborator - {project} Project</title>
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
	<script type="text/javascript" src="script/jquery.js"></script>
	<script type="text/javascript" src="script/raphael.js"></script>
	<script type="text/javascript" src="script/jquery-ui-1.8.18.custom.min.js"></script>
	<script type="text/javascript" src="script/jquery.fileupload-ui.js"></script>
	<script type="text/javascript" src="script/jquery.fileupload.js"></script>
	<link href="css/arborator.css" rel="stylesheet" type="text/css">
	<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />
	<link rel="stylesheet" type="text/css" href="css/jquery.fileupload-ui.css" media="screen" />""".format(project=project)
print """
	<script type="text/javascript">	
	edit = function(textid) {
		$("#textid").attr("value", textid );
		$("#username").attr("value", "moi" );
		$("#editorform").submit();
	}
	</script>
	<script src="script/upload.js"></script>
</head>
"""

#


########################################### body #####################################

print """<body id="body">
		<div id="navigation" style="width:100%;margin:0px;border:0px;" class="arbortitle  ui-widget-header ui-helper-clearfix">
		<a href='.' style='position: fixed;left:1px;top:1px'><img src="images/arboratorNano.png" border="0"></a>
			<span style='margin:5 auto;position: relative;top:5px;' id='sentinfo'>{project} Project</span>
		</div>
		<div id="center" class="center" style="width:100%;font-size:.8em">
""".format(project=project)


################################ reactions after clicks/uploads
if login:
	
	print "ooooooooooooooooooooooooooooooooooooooo o"*55
	print '<script type="text/javascript">window.location.href="project.cgi?project={project}";</script>'.format(project=project)
else:
	print "uuuuuuuuuuu"*55
print '</body></html>'










<head><title> Arborator - Login Page</title>
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
	<LINK REL="SHORTCUT ICON" href="http://elizia.net/images/favicon.ico">	

<link href="css/arborator.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />


</head>
<body id="body">
<div class="header">
<br> <br> 
			<a href="."><img src="images/arboratoraNano.png" alt="Arborator" title="Arborator" border=0px/></a>
	
	<div class="arbortitle ui-widget-header ui-helper-clearfix" style="width:100%;margin:0px;border:0px;">
	Arborator User Admin Page
	</div>
			
</div>  
<br><br>
	<div id="shadow-container"><div class="shadow1"><div class="shadow2"><div class="shadow3"><div class="container">


<p>From this screen you can edit a few of the settings in the main config file. You can also change some of the settings for the <em>default user</em>. These values are transferred to every new account.</p>

<table>
<tr><td class="login"> 
<h2>Config File Values</h2>
<p>This is a description of all the config file values that you can edit from this page.</p>
<p>
<dl>
<dt>newloginlink</dt>
<dd>Setting this to 'Yes' or 'No' determines whether users will be able to create new logins or not. It also determines whether the <em>'Create New Login'</em> appears on the login screen.</dd>
<dt>adminmail</dt>
<dd>This is the email address that reports of new login confirmations are sent to. Setting this to '' switches this off.</dd>
<dt>email_subject</dt>
<dd>This is the Subject Line of the emails sent to new users.</dd>
<dt>email_message</dt>
<dd>This is the body of the message sent to new users, along with the link to confirm the account.</dd>
</dl>
</p>


<h2>Default User Values</h2>
<p>This is a description of all the default user values that you can edit from this page.</p>
<p>
<dl>
<dt>max-age</dt>
<dd>This is the maximum age of the cookie we use, in seconds. Setting it to zero usually means (slightly browser dependent) the cookie will only endure for that browser session. Common values are 3600=1 hour, 86400=1 day, 604800=1 week. The cookie is reset after every new page access - so this is the maximum time in between visits that the cookie will last. After that, the user will have to login again.</dd>
<dt>editable</dt>
<dd>This is whether the user is allowed to change their password, email address etc.</dd>
</dl>
</p>

</td><td class="login" width="50%">

<h2>Edit the Values</h2>

    <form action="**thisscript**" method="post">
    <input type="hidden" name="action" value="**action**">
    <input type="hidden" name="login" value="admin">
    <input type="hidden" name="admin" value="doeditconfig">
    <table border="1" summary="Edit Config File and Default User"  class="table">
    <tr> <th colspan="2">Config File Values</th></tr>
    <tr><td class="login" align="center"> <strong>New Login Link : </strong></td><td class="login" align="center"> <input type="checkbox" name="loginlink" **loginlink**>Allow New Logins ?</td></tr>
    <tr><td class="login" align="center"> <strong>Admin Email : </strong></td><td class="login" align="center"> <input type="text" name="adminmail" value="**adminmail**"></td></tr>
    <tr><td class="login" align="center"> <strong>Email Subject : </strong></td><td class="login" align="center"> <input type="text" name="emailsubject" value="**email subject**"></td></tr>
    <tr><td class="login" align="center"> <strong>Email Message : </strong></td><td class="login" align="center"  style="width:300px; height:290px;"> <textarea name="emailmsg">**email message**</textarea></td></tr>

    <tr><td class="login" align="center"> &nbsp;</td><td class="login" align="center"> &nbsp;</td></tr>
    <tr> <th colspan="2">Default User Values</th></tr>
    <tr><td class="login" align="center"> <strong>Cookie max-age : </strong></td><td class="login" align="center"> <input type="text" name="maxage" value="**maxage**"></td></tr>
    <tr><td class="login" align="center"> <strong>Accounts Editable : </strong></td><td class="login" align="center"> <input type="checkbox" name="editable" **editable** >Can New Users Edit <br>Account Details ?</td></tr>
    <tr><td class="login" align="center"> &nbsp;</td><td class="login" align="center"> &nbsp;</td></tr>
    <tr><td class="login" align="center"> <input type="reset"></td><td class="login" align="center"> <input type="submit" value="Submit Changes"></td></tr>

    </table>
    </form>


</td></tr>
</table>
</div></div></div></div></div>
<p>From this screen you can edit a few of the settings in the main config file. You can also change some of the settings for the <em>default user</em>. These values are transferred to every new account.</p>

<table>
<tr><td> 
<h2>Config File Values</h2>
<p>This is a description of all the config file values that you can edit from this page.</p>
<p>
<dl>
<dt>newloginlink</dt>
<dd>Setting this to 'Yes' or 'No' determines whether users will be able to create new logins or not. It also determines whether the <em>'Create New Login'</em> appears on the login screen.</dd>
<dt>adminmail</dt>
<dd>This is the email address that reports of new login confirmations are sent to. Setting this to '' switches this off.</dd>
<dt>email_subject</dt>
<dd>This is the Subject Line of the emails sent to new users.</dd>
<dt>email_message</dt>
<dd>This is the body of the message sent to new users, along with the link to confirm the account.</dd>
</dl>
</p>


<h2>Default User Values</h2>
<p>This is a description of all the default user values that you can edit from this page.</p>
<p>
<dl>
<dt>max-age</dt>
<dd>This is the maximum age of the cookie we use, in seconds. Setting it to zero usually means (slightly browser dependent) the cookie will only endure for that browser session. Common values are 3600=1 hour, 86400=1 day, 604800=1 week. The cookie is reset after every new page access - so this is the maximum time in between visits that the cookie will last. After that, the user will have to login again.</dd>
<dt>editable</dt>
<dd>This is whether the user is allowed to change their password, email address etc.</dd>
</dl>
</p>

</td><td class="login"width="50%">

<h2>Edit the Values</h2>

    <form action="**thisscript**" method="post">
    <input type="hidden" name="action" value="**action**">
    <input type="hidden" name="login" value="admin">
    <input type="hidden" name="admin" value="doeditconfig">
    <table border="1" summary="Edit Config File and Default User"  class="table">
    <tr> <th colspan="2">Config File Values</th></tr>
    <tr><td class="login"align="center"> <strong>New Login Link : </strong></td><td class="login"align="center"> <input type="checkbox" name="loginlink" **loginlink**>Allow New Logins ?</td></tr>
    <tr><td class="login"align="center"> <strong>Admin Email : </strong></td><td class="login"align="center"> <input type="text" name="adminmail" value="**adminmail**"></td></tr>
    <tr><td class="login"align="center"> <strong>Email Subject : </strong></td><td class="login"align="center"> <input type="text" name="emailsubject" value="**email subject**"></td></tr>
    <tr><td class="login"align="center"> <strong>Email Message : </strong></td><td class="login"align="center"> <textarea name="emailmsg" style="width:300px; height:290px;">**email message**</textarea></td></tr>

    <tr><td class="login"align="center"> &nbsp;</td><td class="login"align="center"> &nbsp;</td></tr>
    <tr> <th colspan="2">Default User Values</th></tr>
    <tr><td class="login"align="center"> <strong>Cookie max-age : </strong></td><td class="login"align="center"> <input type="text" name="maxage" value="**maxage**"></td></tr>
    <tr><td class="login"align="center"> <strong>Accounts Editable : </strong></td><td class="login"align="center"> <input type="checkbox" name="editable" **editable** >Can New Users Edit <br>Account Details ?</td></tr>
    <tr><td class="login"align="center"> &nbsp;</td><td class="login"align="center"> &nbsp;</td></tr>
    <tr><td class="login"align="center"> <input type="reset"></td><td class="login"align="center"> <input type="submit" value="Submit Changes"></td></tr>

    </table>
    </form>


</td></tr>
</table>
<h2>Delete or Edit Users</h2>

<p>Accounts are shown in alphabetical order. You cannot edit or delete yourself, or the main administrator. To edit yourself use the normal 'edit account' option.</p>

**account table**
<h2>Create or Invite New Users</h2>

<p>If you select 'Invite User' then an email will be sent to the address you supply, asking the user to confirm the new login (and telling them their initial password). If you select 'Create New User' then the new login will be created immediately (and no email sent). Admin level should be a number between 0 and 3. For normal users the value should be 0.</p>

<div style="margin:auto;max-width:50%">
    <form action="**thisscript**" method="post" class="table">
        <table summary="Form to create a new login." border="1" >
            <tr><td class="login" align="center"><strong>Create or Invite : </strong></td> <td class="login" align="center"> 
            <input type="radio" name="create" **create1** value="create">Create New User<br >
            <input type="radio" name="create" **create2** value="invite">Invite User            
            </td></tr>
            <tr><td class="login" align="center"><strong>Real Name : </strong></td><td class="login"><input name="realname" type="text" value="**realname**"></td></tr>
            <tr><td class="login" align="center"><strong>Login Name : </strong></td><td class="login"><input name="username" type="text" value="**username**"></td></tr>
            <tr><td class="login" align="center"><strong>Email Address : </strong></td><td class="login"><input name="email" type="text" value="**email**"></td></tr>
            <tr><td class="login" align="center"><strong>Admin Level : </strong></td><td class="login"><input name="adminlev" type="text" value="**adminlev**"></td></tr>            
            <tr><td class="login" align="center"><strong>Initial Password : </strong></td><td class="login"><input name="pass1" type="text" value="**pass1**">
            <tr><td class="login" align="center"><strong>Confirm Password : </strong></td><td class="login"><input name="pass2" type="text" value="**pass2**">
            <input type="hidden" name="login" value="admin">
            <input type="hidden" name="action" value="**action**">
            <input type="hidden" name="admin" value="doinvite">
            </td></tr>
            
            <tr><td class="login">&nbsp;</td><td class="login" align="center"><input type="submit" value="Submit"></td></tr>
        </table>
    </form>
</div>
<br>

<div style="margin:auto;max-width:50%">
    <table summary="Admin Menu" border="0">
        <tr>
        <td class="login">To create or invite new users </td> 
        <td class="login"><strong><a href="**invite**">Click Here - New Users</a></strong>.</td></tr>
        
        <tr>
        <td class="login">To edit or delete users </td> 
        <td class="login"><strong><a href="**edit users**">Click Here - Edit Users</a></strong>.</td></tr>
        
        <tr>
        <td class="login">To edit the main config file and default user </td> 
        <td class="login"><strong><a href="**edit config**">Click Here - Edit Config</a></strong>.</td></tr>
    </table>
</div>
<head><title> arborator - Login Page</title>
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
	<LINK REL="SHORTCUT ICON" href="http://elizia.net/images/favicon.ico">	

<link href="css/arborator.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />

</head>
<body id="body">
<div class="header">
<br> <br> 
				<a href="."><img src="images/arboratorNano.png" alt="arborator" title="arborator" border=0px/></a>

	<div class="arbortitle ui-widget-header ui-helper-clearfix" style="width:100%;margin:0px;border:0px;">
	arborator User Admin Page
	</div>
			
</div>  
<br><br>
<div id="shadow-container"><div class="shadow1"><div class="shadow2"><div class="shadow3"><div class="container">




<div style="margin:auto;max-width:50%">
<h1 class="title">Account Administration</h1>
    <table border="1" summary="Links Back" width="100%">
        <tr><td class="login" align="center"><a href="**this script**">Return to the Main Script</a></td>
        <td class="login" align="center"><a href="**admin menu**">Go to the Admin Menu</a></td></tr>
    </table>
</div>

<br><!- message --> 

<hr width="50%">

**admin**
</div></div></div></div></div>
</body>
</html>
<head><title> Arborator - Login Page</title>
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
	<LINK REL="SHORTCUT ICON" href="http://elizia.net/images/favicon.ico">	

<link href="css/arborator.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />


</head>
<body id="body">
<div class="header">
<br> <br> 
	<a href="."><img src="images/arboratorNano.png" alt="arborator" title="arborator" border=0px/></a>		
	<div class="arbortitle ui-widget-header ui-helper-clearfix" style="width:100%;margin:0px;border:0px;">
	Arborator Account Admin Page
	
	</div>
			
</div>  
<br><br>
	<div id="shadow-container"><div class="shadow1"><div class="shadow2"><div class="shadow3"><div class="container">


<h1 class="title">Edit Your Account Details</h1>
<h2 class="subtitle" >Welcome</h2>
<br>
<!-- **message** -->

    <p>This account was created on **created on**.<p>
    
    <p>You have logged in **num logins** times.</p>
    
    <p>You have accessed **num used** protected pages.</p>
    
    <p>It was last used **last used**.</p>
   
    <p>In order to change your password or your email address you will need to confirm your current password. Your password must be at least five characters long. I haven't written out a privacy policy, but I promise never to give your email address to anyone else.</p>

    <p>To return to the main script, <a href="**this script**">Click Here</a>.</p>

    <p>**edit account form**</p>
</div></div></div></div></div></div>
</body>
</html>
<div id="editaccount"> 
    <form action="**script**" method="post">
        <table summary="Form to edit account details." border="1">
            <tr><td class="login">Real Name :</td><td class="login"><input name="realname" type="text" value="**realname**"></td></tr>
            <tr><td colspan="2">&nbsp</td></tr>
            <tr><td class="login">Email Address :</td><td class="login"><input name="email" type="text" value="**email**"></td></tr>
            <tr><td class="login">Current Password :</td><td class="login"><input name="pass0" type="password">
            <tr><td class="login">New Password :</td><td class="login"><input name="pass1" type="password">
            <tr><td class="login">Confirm New Pass :</td><td class="login"><input name="pass2" type="password">
            <input type="hidden" name="login" value="doeditaccountnojs">
            <!-- **action** -->
            </td></tr>
            
            <tr><td class="login">&nbsp;</td><td class="login"><input type="submit" value="Submit"></td></tr>
        </table>
    </form>
</div>
<head><title> Arborator - Login Page</title>
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
	<LINK REL="SHORTCUT ICON" href="http://elizia.net/images/favicon.ico">	

<link href="css/arborator.css" rel="stylesheet" type="text/css">

</head><body id="body">
<div class="header">
<br> 
			Arborator Login Page
			<div class="headerright">
			<a href="."><img src="images/arboratorNano.png" alt="arborator" title="arborator" border=0px/></a>
			</div>
			
	</div>  <br>  
	<div id="shadow-container"><div class="shadow1"><div class="shadow2"><div class="shadow3"><div class="container">



<h1 class="title">New Login Successfully Created</h1>
<p>An email has been sent to the address you provided. This will contain a link to activate the account. Once you have done that, you will be able to use your name and password to login.</p>

<p><em>Thank you</em></p>

<p>To return to the main script - <a href="**this script**">Click Here</a>.</p>
</div></div></div></div></div>
</body>
</html>
<div id="loginform"> 
    <form action="**script**" method="post">
        <table summary="Form to get login details." border="0" style="width:100%">
            <tr><td class="login" >Login Name:</td><td class="login" ><input name="username" type="text"></td></tr>
            <tr><td class="login" >Password:</td><td class="login" ><input name="pass" type="password">
            <input type="hidden" name="login" value="loginnojs">
            <input id="project" type="hidden" name="project" value="xxx">
            <!-- **action** -->
            </td></tr>
            <tr><td class="login"  colspan=2><input type="submit" value="Login" class="fg-button ui-state-default ui-corner-all"></td></tr>
        </table>
    </form>
</div>
 <head><title>arborator Project Login Page</title>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8"> 
<link href="css/arborator.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />
<script type="text/javascript" src="script/jquery.js"></script>

</head>

<body>


<br>
<br>

<div class="ui-widget-overlay" style=" z-index: 1001;"></div>
<div class="center" style="width: 420px; ">
<div style="display: block;  overflow: hidden; z-index: 1002; outline: 0px none; height: auto; width: 420px; " class="ui-dialog ui-widget ui-widget-content ui-corner-all " tabindex="-1" role="dialog" aria-labelledby="ui-dialog-title-dialog"><div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix" unselectable="on" style="-moz-user-select: none;"><a href='.' ><span class="ui-dialog-title" id="ui-dialog-title-dialog" unselectable="on" style="-moz-user-select: none;">Login</span><img src="images/arboratorNano.png" border="0"></a></div><div id="dialog" class="ui-dialog-content ui-widget-content">

<br>


**login form**


<script type="text/javascript">
$("#project").attr("value",window.location.search.substr(9))
</script>

<!-- <strong>There is a guest login. Username <em>guest</em> and password <em>pass1</em>.</strong> -->
<p>Alternatively <a href="**new login link**" style="color:#DD137B;">click on this link</a> to create a new login.</p>


</div>


<div id="createlogin"> 


    <form action="**script**" method="post">
        <table summary="Form to create a new login." border="1">
            <tr><td>Real name:<br> <span style="font-size:12px">Nom rÃ©el (PrÃ©nom et nom) </span><br> <span style="font-size:12px">çå®å§å </span></td><td><input name="realname" type="text" value="**realname**"></td></tr>
            <tr><td>User name:<br>  <span style="font-size:12px">Nom dâutilisateur</span><br>  <span style="font-size:12px">ç¨æ·å</span></td><td><input name="username" type="text" value="**username**"></td></tr>
            <tr><td>Email address:<br>  <span style="font-size:12px">Adresse mÃ¨l </span><br>  <span style="font-size:12px">çµå­é®ä»¶å°å </span></td><td><input name="email" type="text" value="**email**"></td></tr>
            <tr><td>Password:<br>  <span style="font-size:12px">Mot de passe </span><br>  <span style="font-size:12px">å¯ç  </span></td><td><input name="pass1" type="password">
            <tr><td>Password confirmation:<br> <span style="font-size:12px">Confirmation du mot de passe </span><br> <span style="font-size:12px">åæ¬¡ç¡®è®¤å¯ç  </span></td><td><input name="pass2" type="password">
            <input type="hidden" name="login" value="donewloginnojs">
            <!-- **action** -->
            </td></tr>
            
            <tr><td>&nbsp;</td><td><input type="submit" value="submit  (soumettre - æäº¤)"></td></tr>
        </table>
    </form>
</div>
<html>
<head><title>Arborator</title>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">

<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />
<link href="./css/arborator.css" rel="stylesheet" type="text/css">


</head><body id="body">


<div class="arbortitle fg-toolbar ui-widget-header ui-helper-clearfix">
	<a href='.' style='position: absolute;left:0px;'><img src="images/arboratorNano.png" border="0"></a>
	<span style='text-align:center;margin:0 auto;position: relative;top:0px;' id='sentinfo'>Welcome!</span>
	
</div>
	<div class="ui-widget ui-widget-content ui-corner-all" style="margin:55 auto;padding: 10px; width: 650px; top:155px;font-size=14px; ">



<h1 class="title">Create a New Arborator Login Account</h1>

<!-- **message** -->

<div style="font-size:13px">
    Please enter your details below. Once you have successfully completed the form an email will be sent to you asking you to verify the account. This is done by using the link sent in the email. When you have done this you will be able to use your account to log in.  The link you are sent will be valid for thirty days. 

    I haven't written out a privacy policy, but I promise never to give your email address to anyone else.
    <br><br>
    Your password must be at least five characters long. The login name must be unique (we'll soon tell you if it isn't) - but your 'Real Name' can be whatever you want, but if you want us to be able to evaluate your work, please give your complete name: first name and family name. This is the name we'll welcome you with. You will be able to change your 'Real Name' later, but you're stuck with whatever you choose as your login name.
</div>





<div class="ui-state-highlight ui-corner-all" style="padding: 0pt 0.7em; margin-top: 20px;">
    <p>**new login form**</p>
  </div>  
    
<h3 class="subtitle" >CrÃ©ez un nouveau compte de connection Arborator</h3>
<div style="font-size:11px">
	Veuillez entrer vos donnÃ©es ci-dessus. Une fois envoyÃ©, vous allez recevoir un mÃ¨l pour vÃ©rifier votre compte. La vÃ©rification se fait grÃ¢ce Ã  un lien dans le mÃ¨l. AprÃ¨s vÃ©rification, vous pouvez utiliser votre compte pour vous connecter. 
	Le lien reste valable pendant 30 jours.

	Je n'ai pas Ã©crit de convention de confidentialitÃ© mais je promets de jamais distribuer votre adresse mÃ¨l.
	<br>
	Votre mot de passe doit avoir au moins cinq caractÃ¨res. Le nom d'utilisateur doit Ãªtre unique (on vous dira dans quelques instants s'il ne l'est pas) mais vous Ãªtes libre de choisir ce que vous voulez comme "nom rÃ©el". Si vous souhaitez que votre travail d'annotation soit pris en compte, mettez votre nom complet : prÃ©nom et nom. Nous allons vous souhaiter la bienvenue avec ce nom. Vous pouvez changer votre "nom rÃ©el", mais vous allez garder votre nom d'utilisateur.
</div>
<h3 class="subtitle" >åå»ºä¸ä¸ªæ°çArboratorç»å½å¸æ·</h3>
<div style="font-size:11px">
	è¯·å¨ä¸é¢è¾å¥æ¨çè¯¦ç»ä¿¡æ¯ã ä¸æ¦æ¨é¡ºå©å°å¡«å®è¡¨æ ¼ï¼æ¨å°ä¼æ¶å°ä¸å°ç¡®è®¤æ¨è´¦æ·ççµå­é®ä»¶ãæ¨å¯ä»¥éè¿é®ä»¶ä¸­çé¾æ¥æ¿æ´»æ¨çè´¦æ·ãæ¿æ´»è´¦æ·ä»¥åï¼æ¨å¯ä»¥ä½¿ç¨æ¨çè´¦æ·ç»å¥ãé¾æ¥çæææä¸º30å¤©ã æä»¬è¿æ²¡æä¸ä¸ªå·ä½çéç§æ¡ä¾ï¼ä½æä»¬æ¿è¯ºç»ä¸ä¼å°æ¨çé®ç®±å°åæ³é²ç»ä»»ä½äººã
	<br>
å¯ç å¿é¡»è³å°åå«äºä¸ªå­ç¬¦ãæ³¨åè´¦æ·åä¸è½éå¤ï¼æä»¬ä¼å¾å¿«åè¯æ¨ï¼æ¨ç³è¯·çè´¦æ·åæ¯å¦å·²æäººä½¿ç¨ï¼ï¼ä½æ¯æ¨çâçå®å§åâå¯ä»¥éæå¡«åãå¦ææ¨å¸ææä»¬è½å¤å¯¹æ¨çå·¥ä½è¿è¡è¯ä¼°ï¼è¯·ç»åºæ¨çå®æ´å§åï¼åæ¬æ¨çå§æ°ååå­ãæä»¬å°ä»¥è¿ä¸ªåå­ç§°å¼æ¨ãä¹åï¼æ¨è½å¤ä¿®æ¹æ¨çâçå®å§åâï¼ä½æ¯ä¸è½ä¿®æ¹æ¨çè´¦æ·åã

</div>

    
</div></div></div></div></div>
&nbsp;
</body>
</html>
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2016 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####


import cgi, cgitb, codecs, time, subprocess, sys, os
cgitb.enable()
form = cgi.FieldStorage()

project = form.getvalue('project','').decode("utf-8") # graph / transition
parserType = form.getvalue('parserType','').decode("utf-8") # graph / transition
whoseTrees = form.getvalue('whoseTrees','').decode("utf-8") # all / validators only
evaluationPercent = form.getvalue('evaluationPercent','').decode("utf-8") # from 1 to 100
additionnalLexicon = form.getvalue('additionnalLexicon','').decode("utf-8") # infile / None
resultAnnotator = form.getvalue('resultAnnotator','').decode("utf-8") # parser / mate / enterName

#parserType= u"graph"
#whoseTrees = u"all"
#evaluationPercent = u"10"
#additionnalLexicon = u"None"
#resultAnnotator = u"parser"

command = u'python mate.py {project} {parserType} {whoseTrees} {evaluationPercent} {additionnalLexicon} {resultAnnotator} '.format(project=project, parserType=u"--parserType "+parserType if parserType else "", whoseTrees=u"--whoseTrees "+whoseTrees if whoseTrees else "", evaluationPercent=u"--evaluationPercent "+evaluationPercent if evaluationPercent else "", additionnalLexicon=u"--additionnalLexicon "+additionnalLexicon if additionnalLexicon else "", resultAnnotator=u"--resultAnnotator "+resultAnnotator if resultAnnotator else "")
print 'Content-type: text/plain\n\n',
print command
#-testRun
p = subprocess.Popen(command, shell=True)

#de=newmate.doIt(project=u"OrfeoGold2016", lang="graph", evaluationPercent=10, additionnalLexicon=None, resultAnnotator="mate")
#output_trees=de.run_depedit(infile)

#print output_trees.encode("utf-8")

# ``__init__.py``
# The package file for the Pythonutils module
# http://www.voidspace.org.uk/python/pythonutils.html

# Copyright Michael Foord and Nicola Larosa, 2004 & 2005.
# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# For information about bugfixes, updates and support, please join the
# Pythonutils mailing list.
# http://groups.google.com/group/pythonutils/
# Comments, suggestions and bug reports welcome.
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# E-mail fuzzyman@voidspace.org.uk

# this wrapper imports all the pythonutils modules
# *except* cgiutils and validate

"""
This makes most of the pythonutils objects available from the ``pythonutils``
namespace.

It doesn't import cgiutils or validate.
"""

from listquote import *
from configobj import *
from pathutils import *
from standout import *
from urlpath import *
from odict import *

__version__ = '0.2.5'

"""
CHANGELOG

2005/09/02
----------

Added ``__version__``

"""
# Version 0.3.5
# 2005/11/26

# Copyright Michael Foord 2004 & 2005
# cgiutils.py
# Functions and constants useful for working with CGIs

# http://www.voidspace.org.uk/python/modules.shtml

# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# For information about bugfixes, updates and support, please join the Pythonutils mailing list.
# http://groups.google.com/group/pythonutils/
# Comments, suggestions and bug reports welcome.
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# E-mail fuzzyman@voidspace.org.uk

import os
import sys

__version__ = '0.3.5'

__all__ = (
    'serverline',
    'SENDMAIL',
    'validchars',
    'alphanums',
    'getrequest',
    'getform',
    'getall',
    'isblank',
    'formencode',
    'formdecode',
    'mailme',
    'sendmailme',
    'createhtmlmail',
    'environdata',
    'validemail',
    'cgiprint',
    'ucgiprint',
    'replace',
    'error',
    'makeindexline',
    'istrue',
    'randomstring',
    'blacklisted',
    '__version__',
    )

serverline = "Content-Type: text/html"

# A common location of sendmail on servers
SENDMAIL = "/usr/sbin/sendmail"
validchars = 'abcdefghijklmnopqrstuvwxyz0123456789!-_*'
alphanums = 'abcdefghijklmnopqrstuvwxyz0123456789'

#######################################################
# Some functions for dealing with CGI forms (instances of FieldStorage)

def getrequest(valuelist=None, nolist=False):
    """
    Initialise the ``FieldStorage`` and return the specified list of values as 
    a dictionary.
    
    If you don't specify a list of values, then *all* values will be returned.
    
    If you set ``nolist`` to ``True`` then any parameters supplied as lists 
    will only have their first entry returned.
    """
    import cgi
    form = cgi.FieldStorage()
    if valuelist is not None:
        return getform(valuelist, form, nolist=nolist)
    else:
        return getall(form, nolist=nolist)

def getform(valuelist, theform, notpresent='', nolist=False):
    """
    This function, given a CGI form, extracts the data from it, based on
    valuelist passed in. Any non-present values are set to '' - although this 
    can be changed.
    
    It also takes a keyword argument 'nolist'. If this is True list values only 
    return their first value.
    
    Returns a dictionary.
    """
    data = {}
    for field in valuelist:
        if not theform.has_key(field):
            data[field] = notpresent
        else:
            if not isinstance(theform[field], list):
                data[field] = theform[field].value
            else:
                if not nolist:
                    # allows for list type values
                    data[field] = [x.value for x in theform[field]]
                else:
                    # just fetch the first item
                    data[field] = theform[field][0].value
    return data

def getall(theform, nolist=False):
    """
    Passed a form (FieldStorage instance) return all the values.
    This doesn't take into account file uploads.
    
    Also accepts the 'nolist' keyword argument as ``getform``.
    
    Returns a dictionary.
    """
    data = {}
    for field in theform.keys():
        if not isinstance(theform[field], list):
            data[field] = theform[field].value
        else:
            if not nolist:
                # allows for list type values
                data[field] = [x.value for x in theform[field]]
            else:
                # just fetch the first item
                data[field] = theform[field][0].value
    return data

def isblank(indict):
    """
    Passed an indict of values it checks if any of the values are set.
    
    Returns ``True`` if every member of the indict is empty (evaluates as False).
    
    I use it on a form processed with getform to tell if my CGI has been 
    activated without any values.
    """
    return not [val for val in indict.values() if val]

def formencode(theform):
    """
    A version that turns a cgi form into a single string.
    It only handles single and list values, not multipart.
    This allows the contents of a form requested to be encoded into a single value as part of another request.
    """
    from urllib import urlencode, quote_plus
    return quote_plus(urlencode(getall(theform)))

def formdecode(thestring):
    """Decode a single string back into a form like dictionary."""
    from cgi import parse_qs
    from urllib import unquote_plus 
    return parse_qs(unquote_plus(thestring), True)


#############################################################
# Functions for handling emails
#
# Use mailme for sending email - specify a path to sendmail *or* a host, port etc (optionally username)


def mailme(to_email, msg, email_subject=None, from_email=None,
            host='localhost', port=25, username=None, password=None,
            html=True, sendmail=None):
    """
    This function will send an email using ``sendmail`` or ``smtplib``, depending 
    on what parameters you pass it.
    
    If you want to use ``sendmail`` to send the email then set 
    ``sendmail='/path/to/sendmail'``. (The ``SENDMAIL`` value from Constants_ often 
    works).
    
    If you aren't using sendmail then you will need to set ``host`` and ``port`` to 
    the correct values. If your server requires authentication then you'll need to 
    supply the correct ``username`` and ``password``. 
    
    ``to_email`` can be a single email address, *or* a list of addresses.
    
    ``mailme`` *assumes* you are sending an html email created by 
    ``createhtmlmail``. If this isn't the case then set ``html=False``.
    
    Some servers won't let you send a message without supplying a ``from_email``.
    """
    if sendmail is not None:
        # use sendmailme if specified
        return sendmailme(to_email, msg, email_subject, from_email, 
                            html, sendmail)
    if not isinstance(to_email, list):
        # if we have a single email then change it into a list
        to_email = [to_email]
    #
    import smtplib
    #
    head = "To: %s\r\n" % ','.join(to_email) 
    if from_email is not None:
        head += ('From: %s\r\n' % from_email)
    # subject is in the body of an html email
    if not html and email_subject is not None:
        head += ("Subject: %s\r\n\r\n" % email_subject)
    msg = head + msg
    #
    server = smtplib.SMTP(host, port)
    if username:
        server.login(username, password)
    server.sendmail(from_email, to_email, msg)
    server.quit()

 
def sendmailme(to_email, msg, email_subject=None, from_email=None, 
                html=True, sendmail=SENDMAIL):
    """
    Quick and dirty, pipe a message to sendmail. Can only work on UNIX type systems 
    with sendmail.
    
    Will need the path to sendmail - defaults to the 'SENDMAIL' constant.
    
    ``to_email`` can be a single email address, *or* a list of addresses.
    
    *Assumes* you are sending an html email created by ``createhtmlmail``. If this 
    isn't the case then set ``html=False``.
    """
    if not isinstance(to_email, list):
        to_email = [to_email]
    o = os.popen("%s -t" %  sendmail,"w")
    o.write("To: %s\r\n" %  ','.join(to_email))
    if from_email:
        o.write("From: %s\r\n" %  from_email)
    if not html and email_subject:
        o.write("Subject: %s\r\n" %  email_subject)
    o.write("\r\n")
    o.write("%s\r\n" % msg)
    o.close()

def createhtmlmail(subject, html, text=None):
    """
    Create a mime-message that will render as HTML or text as appropriate.
    If no text is supplied we use htmllib to guess a text rendering. 
    (so html needs to be well formed) 
    
    Adapted from recipe 13.5 from Python Cookbook 2
    """
    import MimeWriter, mimetools, StringIO
    if text is None:
        # produce an approximate text from the HTML input
        import htmllib
        import formatter
        textout = StringIO.StringIO()
        formtext = formatter.AbstractFormatter(formatter.DumbWriter(textout))
        parser = htmllib.HTMLParser(formtext)
        parser.feed(html)
        parser.close()
        text = textout.getvalue()
        del textout, formtext, parser
    out = StringIO.StringIO()       # output buffer for our message
    htmlin = StringIO.StringIO(html)  # input buffer for the HTML
    txtin = StringIO.StringIO(text)   # input buffer for the plain text
    writer = MimeWriter.MimeWriter(out)
    # Set up some basic headers. Place subject here because smtplib.sendmail
    # expects it to be in the message, as relevant RFCs prescribe.
    writer.addheader("Subject", subject)
    writer.addheader("MIME-Version", "1.0")
    # Start the multipart section of the message. Multipart/alternative seems
    # to work better on some MUAs than multipart/mixed.
    writer.startmultipartbody("alternative")
    writer.flushheaders()
    # the plain-text section: just copied through, assuming iso-8859-1  # XXXX always true ?
    subpart = writer.nextpart()
    pout = subpart.startbody("text/plain", [("charset", 'iso-8859-l')]) 
    pout.write(txtin.read())
    txtin.close()
    # the HTML subpart of the message: quoted-printable, just in case
    subpart = writer.nextpart()
    subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
    pout = subpart.startbody("text/html", [("charset", 'us-ascii')])
    mimetools.encode(htmlin, pout, 'quoted-printable')
    htmlin.close()
    # You're done; close your writer and return the message as a string
    writer.lastpart()
    msg = out.getvalue()
    out.close()
    return msg    

def environdata():
    """Returns some data about the CGI environment, in a way that can be mailed."""
    ENVIRONLIST = [ 'REQUEST_URI','HTTP_USER_AGENT','REMOTE_ADDR','HTTP_FROM','REMOTE_HOST','REMOTE_PORT','SERVER_SOFTWARE','HTTP_REFERER','REMOTE_IDENT','REMOTE_USER','QUERY_STRING','DATE_LOCAL' ]  # XXX put this in template ??
    environs = []
    environs.append("\n\n---------------------------------------\n")
    for x in ENVIRONLIST:
        if os.environ.has_key(x):
            environs.append("%s: %s\n" % (x, os.environ[x]))
    environs.append("---------------------------------------\n")
    return ''.join(environs)

def validemail(email):
    """
    A quick function to do a basic email validation.
    Returns False or the email address.
    """
    if ' ' in email:
        return False
    dot = email.rfind('.')
    at = email.find('@')
    if dot == -1 or at < 1 or at > dot:
        return False
    return email

##########################################################

def error(errorval=''):
    """The generic error function."""
    print serverline
    print
    print '''<html><head><title>An Error Has Occurred</title>
    <body><center>
    <h1>Very Sorry</h1>
    <h2>An Error Has Occurred</h2>'''
    if errorval:
        print '<h3>%s</h3>' % errorval
    print '''<script type="text/javascript">setTimeout(function() {window.location.href=".";},3000);</script>'''
    print '</center></body></html>'
    sys.exit()
    
#########################################################

def makeindexline(url, startpage, total, numonpage=10, pagesonscreen=5):
    """
    Make a menu line for a given number of inputs, with a certain number per page.
    Will look something like : ::
    
        First  Previous  22 23 24 25 26 27 28 29 30 31 32  Next  Last
    
    Each number or word will be a link to the relevant page.
    
    url should be in the format : ``'<a href="script.py?startpage=%s">%s</a>'`` - 
    it will have the two ``%s`` values filled in by the function.
    
    The url will automatically be put between ``<strong></strong>`` tags. Your 
    script needs to accepts a parameter ``start`` telling it which page to display.
    
    ``startpage`` is the page actually being viewed - which won't be a link.
    
    ``total`` is the number of total inputs.
    
    ``numonpage`` is the number of inputs per page - this tells makeindexline how 
    many pages to divide the total into.
    
    The links shown will be some before startpage and some after. The amount of 
    pages links are shown for is ``pagesonscreen``. (The actual total number shown 
    will be *2 \* pagesonscreen + 1*).
    
    The indexes generated are *a bit* like the ones created by google. Unlike 
    google however, next and previous jump you into the *middle* of the next set of 
    links. i.e. If you are on page 27 next will take you to 33 and previous to 21. 
    (assuming pagesonscreen is 5). This makes it possible to jump more quickly 
    through a lot of links. Also - the current page will always be in the center of 
    the index. (So you never *need* Next just to get to the next page).
    """
    b = '<strong>%s</strong>'
    url = b % url
    outlist = []
    last = ''
    next = ''
    numpages = total//numonpage
    if total%numonpage:
        numpages += 1
    if startpage - pagesonscreen > 1:
        outlist.append(url % (1, 'First'))
        outlist.append('&nbsp;')
        outlist.append(url % (startpage-pagesonscreen-1, 'Previous'))
        outlist.append('&nbsp;')
    index = max(startpage - pagesonscreen, 1)
    end = min(startpage+pagesonscreen, numpages)
    while index <= end:
        if index == startpage:
            outlist.append(b % startpage)
        else:
            outlist.append(url % (index, index))
        index += 1
    outlist.append('&nbsp;')
    if (startpage+pagesonscreen) < numpages:
        outlist.append(url % (startpage+pagesonscreen+1, 'Next'))
        outlist.append('&nbsp;')
        outlist.append(url % (numpages, 'Last'))

    return '&nbsp;'.join(outlist)    

######################################

def istrue(value):
    """
    Accepts a string as input.
    
    If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns 
    ``True``.
    
    If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns 
    ``False``.
    
    ``istrue`` is not case sensitive.
    
    Any other input will raise a ``KeyError``. 
    """
    return {
        'yes': True, 'no': False,
        'on': True, 'off': False, 
        '1': True, '0': False,
        'true': True, 'false': False,
        }[value.lower()]

def randomstring(length):
    """
    Return a random string of length 'length'.
    
    The string is comprised only of numbers and lowercase letters.
    """ 
    import random
    outstring = []
    while length > 0:
        length -= 1
        outstring.append(alphanums[int(random.random()*36)])
    return ''.join(outstring)

##################################

def cgiprint(inline='', unbuff=False, line_end='\r\n'):
    """
    Print to the ``stdout``.
    
    Set ``unbuff=True`` to flush the buffer after every write.
    
    It prints the inline you send it, followed by the ``line_end``. By default this 
    is ``\r\n`` - which is the standard specified by the RFC for http headers.
    """
    sys.stdout.write(inline)
    sys.stdout.write(line_end)
    if unbuff:
        sys.stdout.flush()

def ucgiprint(inline='', unbuff=False, encoding='UTF-8', line_end='\r\n'):
    """
    A unicode version of ``cgiprint``. It allows you to store everything in your 
    script as unicode and just do your encoding in one place.
    
    Print to the ``stdout``.
    
    Set ``unbuff=True`` to flush the buffer after every write.
    
    It prints the inline you send it, followed by the ``line_end``. By default this 
    is ``\r\n`` - which is the standard specified by the RFC for http headers.
    
    ``inline`` should be a unicode string.
    
    ``encoding`` is the encoding used to encode ``inline`` to a byte-string. It 
    defaults to ``UTF-8``, set it to ``None`` if you pass in ``inline`` as a byte 
    string rather than a unicode string.
    """
    if encoding:
        inline = inline.encode(encoding)
        # don't need to encode the line endings
    sys.stdout.write(inline)
    sys.stdout.write(line_end)
    if unbuff:
        sys.stdout.flush()

def replace(instring, indict):
    """
    This function provides a simple but effective template system for your html 
    pages. Effectively it is a convenient way of doing multiple replaces in a 
    single string.
    
    Takes a string and a dictionary of replacements. 
    
    This function goes through the string and replaces every occurrence of every 
    dicitionary key with it's value.
    
    ``indict`` can also be a list of tuples instead of a dictionary (or anything 
    accepted by the dict function).
    """
    indict = dict(indict)
    if len(indict) > 40:
        regex = re.compile("(%s)" % "|".join(map(re.escape, indict.keys())))
        # For each match, look-up corresponding value in dictionary
        return regex.sub(lambda mo: indict[mo.string[mo.start():mo.end()]],
                                                                    instring)
    for key in indict:
        instring = instring.replace(key, indict[key])
    return instring

############################

def blacklisted(ip, DNSBL_HOST='sbl-xbl.spamhaus.org'):
    """
    Returns ``True`` if ip address is a blacklisted IP (i.e. from a spammer).
    
    ip can also be a domain name - this raises ``socket.gaierror`` if the ip is
    a domain name that cannot be resolved.
    
    The DNS blacklist host (``DNSBL_HOST``) defaults to *sbl-xbl.spamhaus.org*.
    
    Other ones you could use include :
    
    - 'relays.ordb.org'
    - 'dns.rfc-ignorant.org'
    - 'postmaster.rfc-ignorant.org'
    - 'http.dnsbl.sorbs.net'
    - 'misc.dnsbl.sorbs.net'
    - 'spam.dnsbl.sorbs.net'
    - 'bl.spamcop.net'
    
    Useful for vetting user added information posted to web applications.
    
    - 'relays.ordb.org'
    - 'dns.rfc-ignorant.org'
    - 'postmaster.rfc-ignorant.org'
    - 'http.dnsbl.sorbs.net'
    - 'misc.dnsbl.sorbs.net'
    - 'spam.dnsbl.sorbs.net'
    - 'bl.spamcop.net'
    
    .. note::
    
        Another, possibly more effective, way of coping with spam input to web
        applications is to use the `Akismet Web Service <http://akismet.com>`_.
        
        For this you can use the
        `Python Akismet API Interface <http://www.voidspace.org.uk/python/modules.shtml#akismet>`_.
    """
    # turn '1.2.3.4' into '4.3.2.1.sbl-xbl.spamhaus.org'
    import socket
    # convert domain name to IP
    # raises an error if domain name can't be resolved
    ip = socket.gethostbyname(ip)
    iplist = ip.split('.')
    iplist.reverse()
    ip = '%s.%s' % ('.'.join(iplist), DNSBL_HOST)
    try:
        socket.gethostbyname(ip)
        return True
    except socket.gaierror:
        return False

############################

if __name__ == '__main__':
    print 'No tests yet - sorry'

"""
TODO/ISSUES
===========

The indexes generated by makeindexline use next to jump 10 pages. This is
different to what people will expect if they are used to the 'Google' type
index lines.

createhtmlmail assumes iso-8859-1 input encoding for the html

email functions to support 'cc' and 'bcc'

Need doctests

Changelog
=========

2005/11/26      Version 0.3.5
-----------------------------

Add the ``blacklisted`` function.

Added ``__version__``


2005/10/29      Version 0.3.4
-----------------------------

Shortened ``isblank``.


2005/09/21      Version 0.3.3
-----------------------------

Fixed bug in ``getall``.

Fixed bug in ``getrequest``.


2005/08/27      Version 0.3.2
-----------------------------

Large dictionary replaces use a regex approach.


2005/08/20      Version 0.3.1
-----------------------------

Improved istrue function.

Added __all__.

Various other code/doc improvements.


2005/04/07      Version 0.3.0
-----------------------------

Changed the email functions, this may break things (but it's better this way)

Added createhtmlemail, removed loginmailme

mailme is now a wrapper for sendmailme, mailme, *and* the old loginmailme


2005/03/20      Version 0.2.0
-----------------------------

Added ucgiprint and replace.


2005/02/18      Version 0.1.0
-----------------------------

The first numbered version.
"""
# configobj.py
# A config file reader/writer that supports nested sections in config files.
# Copyright (C) 2005 Michael Foord, Nicola Larosa
# E-mail: fuzzyman AT voidspace DOT org DOT uk
#         nico AT tekNico DOT net

# ConfigObj 4

# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# For information about bugfixes, updates and support, please join the
# ConfigObj mailing list:
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
# Comments, suggestions and bug reports welcome.

"""
    >>> z = ConfigObj()
    >>> z['a'] = 'a'
    >>> z['sect'] = {
    ...    'subsect': {
    ...         'a': 'fish',
    ...         'b': 'wobble',
    ...     },
    ...     'member': 'value',
    ... }
    >>> x = ConfigObj(z.write())
    >>> z == x
    1
"""

import sys
INTP_VER = sys.version_info[:2]
if INTP_VER < (2, 2):
    raise RuntimeError("Python v.2.2 or later needed")

import os, re
from types import StringTypes

try:
    from validate import VdtMissingValue
except ImportError:
    VdtMissingValue = None

# the UTF8 BOM - from codecs module
BOM_UTF8 = '\xef\xbb\xbf'

__version__ = '4.1.0'

__revision__ = '$Id: configobj.py 147 2005-11-08 12:08:49Z fuzzyman $'

__docformat__ = "restructuredtext en"

__all__ = (
    '__version__',
    'BOM_UTF8',
    'DEFAULT_INDENT_TYPE',
    'NUM_INDENT_SPACES',
    'MAX_INTERPOL_DEPTH',
    'ConfigObjError',
    'NestingError',
    'ParseError',
    'DuplicateError',
    'ConfigspecError',
    'ConfigObj',
    'SimpleVal',
    'InterpolationError',
    'InterpolationDepthError',
    'MissingInterpolationOption',
    'RepeatSectionError',
    '__docformat__',
    'flatten_errors',
)

DEFAULT_INDENT_TYPE = ' '
NUM_INDENT_SPACES = 4
MAX_INTERPOL_DEPTH = 10

OPTION_DEFAULTS = {
    'interpolation': True,
    'raise_errors': False,
    'list_values': True,
    'create_empty': False,
    'file_error': False,
    'configspec': None,
    'stringify': True,
    # option may be set to one of ('', ' ', '\t')
    'indent_type': None,
}

class ConfigObjError(SyntaxError):
    """
    This is the base class for all errors that ConfigObj raises.
    It is a subclass of SyntaxError.
    
    >>> raise ConfigObjError
    Traceback (most recent call last):
    ConfigObjError
    """
    def __init__(self, message='', line_number=None, line=''):
        self.line = line
        self.line_number = line_number
        self.message = message
        SyntaxError.__init__(self, message)

class NestingError(ConfigObjError):
    """
    This error indicates a level of nesting that doesn't match.
    
    >>> raise NestingError
    Traceback (most recent call last):
    NestingError
    """

class ParseError(ConfigObjError):
    """
    This error indicates that a line is badly written.
    It is neither a valid ``key = value`` line,
    nor a valid section marker line.
    
    >>> raise ParseError
    Traceback (most recent call last):
    ParseError
    """

class DuplicateError(ConfigObjError):
    """
    The keyword or section specified already exists.
    
    >>> raise DuplicateError
    Traceback (most recent call last):
    DuplicateError
    """

class ConfigspecError(ConfigObjError):
    """
    An error occured whilst parsing a configspec.
    
    >>> raise ConfigspecError
    Traceback (most recent call last):
    ConfigspecError
    """

class InterpolationError(ConfigObjError):
    """Base class for the two interpolation errors."""

class InterpolationDepthError(InterpolationError):
    """Maximum interpolation depth exceeded in string interpolation."""

    def __init__(self, option):
        """
        >>> raise InterpolationDepthError('yoda')
        Traceback (most recent call last):
        InterpolationDepthError: max interpolation depth exceeded in value "yoda".
        """
        InterpolationError.__init__(
            self,
            'max interpolation depth exceeded in value "%s".' % option)

class RepeatSectionError(ConfigObjError):
    """
    This error indicates additional sections in a section with a
    ``__many__`` (repeated) section.
    
    >>> raise RepeatSectionError
    Traceback (most recent call last):
    RepeatSectionError
    """

class MissingInterpolationOption(InterpolationError):
    """A value specified for interpolation was missing."""

    def __init__(self, option):
        """
        >>> raise MissingInterpolationOption('yoda')
        Traceback (most recent call last):
        MissingInterpolationOption: missing option "yoda" in interpolation.
        """
        InterpolationError.__init__(
            self,
            'missing option "%s" in interpolation.' % option)

class Section(dict):
    """
    A dictionary-like object that represents a section in a config file.
    
    It does string interpolation if the 'interpolate' attribute
    of the 'main' object is set to True.
    
    Interpolation is tried first from the 'DEFAULT' section of this object,
    next from the 'DEFAULT' section of the parent, lastly the main object.
    
    A Section will behave like an ordered dictionary - following the
    order of the ``scalars`` and ``sections`` attributes.
    You can use this to change the order of members.
    
    Iteration follows the order: scalars, then sections.
    """

    _KEYCRE = re.compile(r"%\(([^)]*)\)s|.")

    def __init__(self, parent, depth, main, indict=None, name=None):
        """
        * parent is the section above
        * depth is the depth level of this section
        * main is the main ConfigObj
        * indict is a dictionary to initialise the section with
        """
        if indict is None:
            indict = {}
        dict.__init__(self)
        # used for nesting level *and* interpolation
        self.parent = parent
        # used for the interpolation attribute
        self.main = main
        # level of nesting depth of this Section
        self.depth = depth
        # the sequence of scalar values in this Section
        self.scalars = []
        # the sequence of sections in this Section
        self.sections = []
        # purely for information
        self.name = name
        # for comments :-)
        self.comments = {}
        self.inline_comments = {}
        # for the configspec
        self.configspec = {}
        # for defaults
        self.defaults = []
        #
        # we do this explicitly so that __setitem__ is used properly
        # (rather than just passing to ``dict.__init__``)
        for entry in indict:
            self[entry] = indict[entry]

    def _interpolate(self, value):
        """Nicked from ConfigParser."""
        depth = MAX_INTERPOL_DEPTH
        # loop through this until it's done
        while depth:
            depth -= 1
            if value.find("%(") != -1:
                value = self._KEYCRE.sub(self._interpolation_replace, value)
            else:
                break
        else:
            raise InterpolationDepthError(value)
        return value

    def _interpolation_replace(self, match):
        """ """
        s = match.group(1)
        if s is None:
            return match.group()
        else:
            # switch off interpolation before we try and fetch anything !
            self.main.interpolation = False
            # try the 'DEFAULT' member of *this section* first
            val = self.get('DEFAULT', {}).get(s)
            # try the 'DEFAULT' member of the *parent section* next
            if val is None:
                val = self.parent.get('DEFAULT', {}).get(s)
            # last, try the 'DEFAULT' member of the *main section*
            if val is None:
                val = self.main.get('DEFAULT', {}).get(s)
            self.main.interpolation = True
            if val is None:
                raise MissingInterpolationOption(s)
            return val

    def __getitem__(self, key):
        """Fetch the item and do string interpolation."""
        val = dict.__getitem__(self, key)
        if self.main.interpolation and isinstance(val, StringTypes):
            return self._interpolate(val)
        return val

    def __setitem__(self, key, value):
        """
        Correctly set a value.
        
        Making dictionary values Section instances.
        (We have to special case 'Section' instances - which are also dicts)
        
        Keys must be strings.
        Values need only be strings (or lists of strings) if
        ``main.stringify`` is set.
        """
        if not isinstance(key, StringTypes):
            raise ValueError, 'The key "%s" is not a string.' % key
        # add the comment
        if not self.comments.has_key(key):
            self.comments[key] = []
            self.inline_comments[key] = ''
        # remove the entry from defaults
        if key in self.defaults:
            self.defaults.remove(key)
        #
        if isinstance(value, Section):
            if not self.has_key(key):
                self.sections.append(key)
            dict.__setitem__(self, key, value)
        elif isinstance(value, dict):
            # First create the new depth level,
            # then create the section
            if not self.has_key(key):
                self.sections.append(key)
            new_depth = self.depth + 1
            dict.__setitem__(
                self,
                key,
                Section(
                    self,
                    new_depth,
                    self.main,
                    indict=value,
                    name=key))
        else:
            if not self.has_key(key):
                self.scalars.append(key)
            if not self.main.stringify:
                if isinstance(value, StringTypes):
                    pass
                elif isinstance(value, (list, tuple)):
                    for entry in value:
                        if not isinstance(entry, StringTypes):
                            raise TypeError, (
                                'Value is not a string "%s".' % entry)
                else:
                    raise TypeError, 'Value is not a string "%s".' % value
            dict.__setitem__(self, key, value)

    def __delitem__(self, key):
        """Remove items from the sequence when deleting."""
        dict. __delitem__(self, key)
        if key in self.scalars:
            self.scalars.remove(key)
        else:
            self.sections.remove(key)
        del self.comments[key]
        del self.inline_comments[key]

    def get(self, key, default=None):
        """A version of ``get`` that doesn't bypass string interpolation."""
        try:
            return self[key]
        except KeyError:
            return default

    def update(self, indict):
        """
        A version of update that uses our ``__setitem__``.
        """
        for entry in indict:
            self[entry] = indict[entry]


    def pop(self, key, *args):
        """ """
        val = dict.pop(self, key, *args)
        if key in self.scalars:
            del self.comments[key]
            del self.inline_comments[key]
            self.scalars.remove(key)
        elif key in self.sections:
            del self.comments[key]
            del self.inline_comments[key]
            self.sections.remove(key)
        if self.main.interpolation and isinstance(val, StringTypes):
            return self._interpolate(val)
        return val

    def popitem(self):
        """Pops the first (key,val)"""
        sequence = (self.scalars + self.sections)
        if not sequence:
            raise KeyError, ": 'popitem(): dictionary is empty'"
        key = sequence[0]
        val =  self[key]
        del self[key]
        return key, val

    def clear(self):
        """
        A version of clear that also affects scalars/sections
        Also clears comments and configspec.
        
        Leaves other attributes alone :
            depth/main/parent are not affected
        """
        dict.clear(self)
        self.scalars = []
        self.sections = []
        self.comments = {}
        self.inline_comments = {}
        self.configspec = {}

    def setdefault(self, key, default=None):
        """A version of setdefault that sets sequence if appropriate."""
        try:
            return self[key]
        except KeyError:
            self[key] = default
            return self[key]

    def items(self):
        """ """
        return zip((self.scalars + self.sections), self.values())

    def keys(self):
        """ """
        return (self.scalars + self.sections)

    def values(self):
        """ """
        return [self[key] for key in (self.scalars + self.sections)]

    def iteritems(self):
        """ """
        return iter(self.items())

    def iterkeys(self):
        """ """
        return iter((self.scalars + self.sections))

    __iter__ = iterkeys

    def itervalues(self):
        """ """
        return iter(self.values())

    def __repr__(self):
        return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
            for key in (self.scalars + self.sections)])

    __str__ = __repr__

    # Extra methods - not in a normal dictionary

    def dict(self):
        """
        Return a deepcopy of self as a dictionary.
        
        All members that are ``Section`` instances are recursively turned to
        ordinary dictionaries - by calling their ``dict`` method.
        
        >>> n = a.dict()
        >>> n == a
        1
        >>> n is a
        0
        """
        newdict = {}
        for entry in self:
            this_entry = self[entry]
            if isinstance(this_entry, Section):
                this_entry = this_entry.dict()
            elif isinstance(this_entry, (list, tuple)):
                # create a copy rather than a reference
                this_entry = list(this_entry)
            newdict[entry] = this_entry
        return newdict

    def merge(self, indict):
        """
        A recursive update - useful for merging config files.
        
        >>> a = '''[section1]
        ...     option1 = True
        ...     [[subsection]]
        ...     more_options = False
        ...     # end of file'''.splitlines()
        >>> b = '''# File is user.ini
        ...     [section1]
        ...     option1 = False
        ...     # end of file'''.splitlines()
        >>> c1 = ConfigObj(b)
        >>> c2 = ConfigObj(a)
        >>> c2.merge(c1)
        >>> c2
        {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
        """
        for key, val in indict.items():
            if (key in self and isinstance(self[key], dict) and
                                isinstance(val, dict)):
                self[key].merge(val)
            else:   
                self[key] = val

    def rename(self, oldkey, newkey):
        """
        Change a keyname to another, without changing position in sequence.
        
        Implemented so that transformations can be made on keys,
        as well as on values. (used by encode and decode)
        
        Also renames comments.
        """
        if oldkey in self.scalars:
            the_list = self.scalars
        elif oldkey in self.sections:
            the_list = self.sections
        else:
            raise KeyError, 'Key "%s" not found.' % oldkey
        pos = the_list.index(oldkey)
        #
        val = self[oldkey]
        dict.__delitem__(self, oldkey)
        dict.__setitem__(self, newkey, val)
        the_list.remove(oldkey)
        the_list.insert(pos, newkey)
        comm = self.comments[oldkey]
        inline_comment = self.inline_comments[oldkey]
        del self.comments[oldkey]
        del self.inline_comments[oldkey]
        self.comments[newkey] = comm
        self.inline_comments[newkey] = inline_comment

    def walk(self, function, raise_errors=True,
            call_on_sections=False, **keywargs):
        """
        Walk every member and call a function on the keyword and value.
        
        Return a dictionary of the return values
        
        If the function raises an exception, raise the errror
        unless ``raise_errors=False``, in which case set the return value to
        ``False``.
        
        Any unrecognised keyword arguments you pass to walk, will be pased on
        to the function you pass in.
        
        Note: if ``call_on_sections`` is ``True`` then - on encountering a
        subsection, *first* the function is called for the *whole* subsection,
        and then recurses into it's members. This means your function must be
        able to handle strings, dictionaries and lists. This allows you
        to change the key of subsections as well as for ordinary members. The
        return value when called on the whole subsection has to be discarded.
        
        See  the encode and decode methods for examples, including functions.
        
        .. caution::
        
            You can use ``walk`` to transform the names of members of a section
            but you mustn't add or delete members.
        
        >>> config = '''[XXXXsection]
        ... XXXXkey = XXXXvalue'''.splitlines()
        >>> cfg = ConfigObj(config)
        >>> cfg
        {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
        >>> def transform(section, key):
        ...     val = section[key]
        ...     newkey = key.replace('XXXX', 'CLIENT1')
        ...     section.rename(key, newkey)
        ...     if isinstance(val, (tuple, list, dict)):
        ...         pass
        ...     else:
        ...         val = val.replace('XXXX', 'CLIENT1')
        ...         section[newkey] = val
        >>> cfg.walk(transform, call_on_sections=True)
        {'CLIENT1section': {'CLIENT1key': None}}
        >>> cfg
        {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
        """
        out = {}
        # scalars first
        for i in range(len(self.scalars)):
            entry = self.scalars[i]
            try:
                val = function(self, entry, **keywargs)
                # bound again in case name has changed
                entry = self.scalars[i]
                out[entry] = val
            except Exception:
                if raise_errors:
                    raise
                else:
                    entry = self.scalars[i]
                    out[entry] = False
        # then sections
        for i in range(len(self.sections)):
            entry = self.sections[i]
            if call_on_sections:
                try:
                    function(self, entry, **keywargs)
                except Exception:
                    if raise_errors:
                        raise
                    else:
                        entry = self.sections[i]
                        out[entry] = False
                # bound again in case name has changed
                entry = self.sections[i]
            # previous result is discarded
            out[entry] = self[entry].walk(
                function,
                raise_errors=raise_errors,
                call_on_sections=call_on_sections,
                **keywargs)
        return out

    def decode(self, encoding):
        """
        Decode all strings and values to unicode, using the specified encoding.
        
        Works with subsections and list values.
        
        Uses the ``walk`` method.
        
        Testing ``encode`` and ``decode``.
        >>> m = ConfigObj(a)
        >>> m.decode('ascii')
        >>> def testuni(val):
        ...     for entry in val:
        ...         if not isinstance(entry, unicode):
        ...             print >> sys.stderr, type(entry)
        ...             raise AssertionError, 'decode failed.'
        ...         if isinstance(val[entry], dict):
        ...             testuni(val[entry])
        ...         elif not isinstance(val[entry], unicode):
        ...             raise AssertionError, 'decode failed.'
        >>> testuni(m)
        >>> m.encode('ascii')
        >>> a == m
        1
        """
        def decode(section, key, encoding=encoding):
            """ """
            val = section[key]
            if isinstance(val, (list, tuple)):
                newval = []
                for entry in val:
                    newval.append(entry.decode(encoding))
            elif isinstance(val, dict):
                newval = val
            else:
                newval = val.decode(encoding)
            newkey = key.decode(encoding)
            section.rename(key, newkey)
            section[newkey] = newval
        # using ``call_on_sections`` allows us to modify section names
        self.walk(decode, call_on_sections=True)

    def encode(self, encoding):
        """
        Encode all strings and values from unicode,
        using the specified encoding.
        
        Works with subsections and list values.
        Uses the ``walk`` method.
        """
        def encode(section, key, encoding=encoding):
            """ """
            val = section[key]
            if isinstance(val, (list, tuple)):
                newval = []
                for entry in val:
                    newval.append(entry.encode(encoding))
            elif isinstance(val, dict):
                newval = val
            else:
                newval = val.encode(encoding)
            newkey = key.encode(encoding)
            section.rename(key, newkey)
            section[newkey] = newval
        self.walk(encode, call_on_sections=True)

    def istrue(self, key):
        """
        Accepts a key as input. The corresponding value must be a string or
        the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
        retain compatibility with Python 2.2.
        
        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns 
        ``True``.
        
        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns 
        ``False``.
        
        ``istrue`` is not case sensitive.
        
        Any other input will raise a ``ValueError``. 
        """
        val = self[key]
        if val == True:
            return True
        elif val == False:
            return False
        else:
            try:
                if not isinstance(val, StringTypes):
                    raise KeyError
                else:
                    return self.main._bools[val.lower()]
            except KeyError:
                raise ValueError('Value "%s" is neither True nor False' % val)

class ConfigObj(Section):
    """
    An object to read, create, and write config files.
    
    Testing with duplicate keys and sections.
    
    >>> c = '''
    ... [hello]
    ... member = value
    ... [hello again]
    ... member = value
    ... [ "hello" ]
    ... member = value
    ... '''
    >>> ConfigObj(c.split('\\n'), raise_errors = True)
    Traceback (most recent call last):
    DuplicateError: Duplicate section name at line 5.
    
    >>> d = '''
    ... [hello]
    ... member = value
    ... [hello again]
    ... member1 = value
    ... member2 = value
    ... 'member1' = value
    ... [ "and again" ]
    ... member = value
    ... '''
    >>> ConfigObj(d.split('\\n'), raise_errors = True)
    Traceback (most recent call last):
    DuplicateError: Duplicate keyword name at line 6.
    """

    _keyword = re.compile(r'''^ # line start
        (\s*)                   # indentation
        (                       # keyword
            (?:".*?")|          # double quotes
            (?:'.*?')|          # single quotes
            (?:[^'"=].*?)       # no quotes
        )
        \s*=\s*                 # divider
        (.*)                    # value (including list values and comments)
        $   # line end
        ''',
        re.VERBOSE)

    _sectionmarker = re.compile(r'''^
        (\s*)                     # 1: indentation
        ((?:\[\s*)+)              # 2: section marker open
        (                         # 3: section name open
            (?:"\s*\S.*?\s*")|    # at least one non-space with double quotes
            (?:'\s*\S.*?\s*')|    # at least one non-space with single quotes
            (?:[^'"\s].*?)        # at least one non-space unquoted
        )                         # section name close
        ((?:\s*\])+)              # 4: section marker close
        \s*(\#.*)?                # 5: optional comment
        $''',
        re.VERBOSE)

    # this regexp pulls list values out as a single string
    # or single values and comments
    _valueexp = re.compile(r'''^
        (?:
            (?:
                (
                    (?:
                        (?:
                            (?:".*?")|              # double quotes
                            (?:'.*?')|              # single quotes
                            (?:[^'",\#][^,\#]*?)       # unquoted
                        )
                        \s*,\s*                     # comma
                    )*      # match all list items ending in a comma (if any)
                )
                (
                    (?:".*?")|                      # double quotes
                    (?:'.*?')|                      # single quotes
                    (?:[^'",\#\s][^,]*?)             # unquoted
                )?          # last item in a list - or string value
            )|
            (,)             # alternatively a single comma - empty list
        )
        \s*(\#.*)?          # optional comment
        $''',
        re.VERBOSE)

    # use findall to get the members of a list value
    _listvalueexp = re.compile(r'''
        (
            (?:".*?")|          # double quotes
            (?:'.*?')|          # single quotes
            (?:[^'",\#].*?)       # unquoted
        )
        \s*,\s*                 # comma
        ''',
        re.VERBOSE)

    # this regexp is used for the value
    # when lists are switched off
    _nolistvalue = re.compile(r'''^
        (
            (?:".*?")|          # double quotes
            (?:'.*?')|          # single quotes
            (?:[^'"\#].*?)      # unquoted
        )
        \s*(\#.*)?              # optional comment
        $''',
        re.VERBOSE)

    # regexes for finding triple quoted values on one line
    _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
    _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
    _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
    _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')

    _triple_quote = {
        "'''": (_single_line_single, _multi_line_single),
        '"""': (_single_line_double, _multi_line_double),
    }

    # Used by the ``istrue`` Section method
    _bools = {
        'yes': True, 'no': False,
        'on': True, 'off': False,
        '1': True, '0': False,
        'true': True, 'false': False,
        }

    def __init__(self, infile=None, options=None, **kwargs):
        """
        Parse or create a config file object.
        
        ``ConfigObj(infile=None, options=None, **kwargs)``
        """
        if infile is None:
            infile = []
        if options is None:
            options = {}
        # keyword arguments take precedence over an options dictionary
        options.update(kwargs)
        # init the superclass
        Section.__init__(self, self, 0, self)
        #
        defaults = OPTION_DEFAULTS.copy()
        for entry in options.keys():
            if entry not in defaults.keys():
                raise TypeError, 'Unrecognised option "%s".' % entry
        # TODO: check the values too
        # add the explicit options to the defaults
        defaults.update(options)
        #
        # initialise a few variables
        self._errors = []
        self.raise_errors = defaults['raise_errors']
        self.interpolation = defaults['interpolation']
        self.list_values = defaults['list_values']
        self.create_empty = defaults['create_empty']
        self.file_error = defaults['file_error']
        self.stringify = defaults['stringify']
        self.indent_type = defaults['indent_type']
        # used by the write method
        self.BOM = None
        #
        self.initial_comment = []
        self.final_comment = []
        #
        if isinstance(infile, StringTypes):
            self.filename = os.path.abspath(infile)
            if os.path.isfile(self.filename):
                infile = open(self.filename).readlines()
            elif self.file_error:
                # raise an error if the file doesn't exist
                raise IOError, 'Config file not found: "%s".' % self.filename
            else:
                # file doesn't already exist
                if self.create_empty:
                    # this is a good test that the filename specified
                    # isn't impossible - like on a non existent device
                    h = open(self.filename, 'w')
                    h.write('')
                    h.close()
                infile = []
        elif isinstance(infile, (list, tuple)):
            self.filename = None
        elif isinstance(infile, dict):
            # initialise self
            # the Section class handles creating subsections
            if isinstance(infile, ConfigObj):
                # get a copy of our ConfigObj
                infile = infile.dict()
            for entry in infile:
                self[entry] = infile[entry]
            self.filename = None
            del self._errors
            if defaults['configspec'] is not None:
                self._handle_configspec(defaults['configspec'])
            else:
                self.configspec = None
            return
        elif hasattr(infile, 'seek'):
            # this supports StringIO instances and even file objects
            self.filename = infile
            infile.seek(0)
            infile = infile.readlines()
            self.filename.seek(0)
        else:
            raise TypeError, ('infile must be a filename,'
                ' StringIO instance, or a file as a list.')
        #
        # strip trailing '\n' from lines
        infile = [line.rstrip('\n') for line in infile]
        #
        # remove the UTF8 BOM if it is there
        # FIXME: support other BOM
        if infile and infile[0].startswith(BOM_UTF8):
            infile[0] = infile[0][3:]
            self.BOM = BOM_UTF8
        else:
            self.BOM = None
        #
        self._parse(infile)
        # if we had any errors, now is the time to raise them
        if self._errors:
            error = ConfigObjError("Parsing failed.")
            # set the errors attribute; it's a list of tuples:
            # (error_type, message, line_number)
            error.errors = self._errors
            # set the config attribute
            error.config = self
            raise error
        # delete private attributes
        del self._errors
        #
        if defaults['configspec'] is None:
            self.configspec = None
        else:
            self._handle_configspec(defaults['configspec'])

    def _parse(self, infile):
        """
        Actually parse the config file
        
        Testing Interpolation
        
        >>> c = ConfigObj()
        >>> c['DEFAULT'] = {
        ...     'b': 'goodbye',
        ...     'userdir': 'c:\\\\home',
        ...     'c': '%(d)s',
        ...     'd': '%(c)s'
        ... }
        >>> c['section'] = {
        ...     'a': '%(datadir)s\\\\some path\\\\file.py',
        ...     'b': '%(userdir)s\\\\some path\\\\file.py',
        ...     'c': 'Yo %(a)s',
        ...     'd': '%(not_here)s',
        ...     'e': '%(c)s',
        ... }
        >>> c['section']['DEFAULT'] = {
        ...     'datadir': 'c:\\\\silly_test',
        ...     'a': 'hello - %(b)s',
        ... }
        >>> c['section']['a'] == 'c:\\\\silly_test\\\\some path\\\\file.py'
        1
        >>> c['section']['b'] == 'c:\\\\home\\\\some path\\\\file.py'
        1
        >>> c['section']['c'] == 'Yo hello - goodbye'
        1
        
        Switching Interpolation Off
        
        >>> c.interpolation = False
        >>> c['section']['a'] == '%(datadir)s\\\\some path\\\\file.py'
        1
        >>> c['section']['b'] == '%(userdir)s\\\\some path\\\\file.py'
        1
        >>> c['section']['c'] == 'Yo %(a)s'
        1
        
        Testing the interpolation errors.
        
        >>> c.interpolation = True
        >>> c['section']['d']
        Traceback (most recent call last):
        MissingInterpolationOption: missing option "not_here" in interpolation.
        >>> c['section']['e']
        Traceback (most recent call last):
        InterpolationDepthError: max interpolation depth exceeded in value "%(c)s".
        
        Testing our quoting.
        
        >>> i._quote('\"""\'\'\'')
        Traceback (most recent call last):
        SyntaxError: EOF while scanning triple-quoted string
        >>> try:
        ...     i._quote('\\n', multiline=False)
        ... except ConfigObjError, e:
        ...    e.msg
        'Value "\\n" cannot be safely quoted.'
        >>> k._quote(' "\' ', multiline=False)
        Traceback (most recent call last):
        SyntaxError: EOL while scanning single-quoted string
        
        Testing with "stringify" off.
        >>> c.stringify = False
        >>> c['test'] = 1
        Traceback (most recent call last):
        TypeError: Value is not a string "1".
        """
        comment_list = []
        done_start = False
        this_section = self
        maxline = len(infile) - 1
        cur_index = -1
        reset_comment = False
        while cur_index < maxline:
            if reset_comment:
                comment_list = []
            cur_index += 1
            line = infile[cur_index]
            sline = line.strip()
            # do we have anything on the line ?
            if not sline or sline.startswith('#'):
                reset_comment = False
                comment_list.append(line)
                continue
            if not done_start:
                # preserve initial comment
                self.initial_comment = comment_list
                comment_list = []
                done_start = True
            reset_comment = True
            # first we check if it's a section marker
            mat = self._sectionmarker.match(line)
##            print >> sys.stderr, sline, mat
            if mat is not None:
                # is a section line
                (indent, sect_open, sect_name, sect_close, comment) = (
                    mat.groups())
                if indent and (self.indent_type is None):
                    self.indent_type = indent[0]
                cur_depth = sect_open.count('[')
                if cur_depth != sect_close.count(']'):
                    self._handle_error(
                        "Cannot compute the section depth at line %s.",
                        NestingError, infile, cur_index)
                    continue
                if cur_depth < this_section.depth:
                    # the new section is dropping back to a previous level
                    try:
                        parent = self._match_depth(
                            this_section,
                            cur_depth).parent
                    except SyntaxError:
                        self._handle_error(
                            "Cannot compute nesting level at line %s.",
                            NestingError, infile, cur_index)
                        continue
                elif cur_depth == this_section.depth:
                    # the new section is a sibling of the current section
                    parent = this_section.parent
                elif cur_depth == this_section.depth + 1:
                    # the new section is a child the current section
                    parent = this_section
                else:
                    self._handle_error(
                        "Section too nested at line %s.",
                        NestingError, infile, cur_index)
                #
                sect_name = self._unquote(sect_name)
                if parent.has_key(sect_name):
##                    print >> sys.stderr, sect_name
                    self._handle_error(
                        'Duplicate section name at line %s.',
                        DuplicateError, infile, cur_index)
                    continue
                # create the new section
                this_section = Section(
                    parent,
                    cur_depth,
                    self,
                    name=sect_name)
                parent[sect_name] = this_section
                parent.inline_comments[sect_name] = comment
                parent.comments[sect_name] = comment_list
##                print >> sys.stderr, parent[sect_name] is this_section
                continue
            #
            # it's not a section marker,
            # so it should be a valid ``key = value`` line
            mat = self._keyword.match(line)
##            print >> sys.stderr, sline, mat
            if mat is not None:
                # is a keyword value
                # value will include any inline comment
                (indent, key, value) = mat.groups()
                if indent and (self.indent_type is None):
                    self.indent_type = indent[0]
                # check for a multiline value
                if value[:3] in ['"""', "'''"]:
                    try:
                        (value, comment, cur_index) = self._multiline(
                            value, infile, cur_index, maxline)
                    except SyntaxError:
                        self._handle_error(
                            'Parse error in value at line %s.',
                            ParseError, infile, cur_index)
                        continue
                else:
                    # extract comment and lists
                    try:
                        (value, comment) = self._handle_value(value)
                    except SyntaxError:
                        self._handle_error(
                            'Parse error in value at line %s.',
                            ParseError, infile, cur_index)
                        continue
                #
##                print >> sys.stderr, sline
                key = self._unquote(key)
                if this_section.has_key(key):
                    self._handle_error(
                        'Duplicate keyword name at line %s.',
                        DuplicateError, infile, cur_index)
                    continue
                # add the key
##                print >> sys.stderr, this_section.name
                this_section[key] = value
                this_section.inline_comments[key] = comment
                this_section.comments[key] = comment_list
##                print >> sys.stderr, key, this_section[key]
##                if this_section.name is not None:
##                    print >> sys.stderr, this_section
##                    print >> sys.stderr, this_section.parent
##                    print >> sys.stderr, this_section.parent[this_section.name]
                continue
            #
            # it neither matched as a keyword
            # or a section marker
            self._handle_error(
                'Invalid line at line "%s".',
                ParseError, infile, cur_index)
        if self.indent_type is None:
            # no indentation used, set the type accordingly
            self.indent_type = ''
        # preserve the final comment
        if not self and not self.initial_comment:
            self.initial_comment = comment_list
        else:
            self.final_comment = comment_list

    def _match_depth(self, sect, depth):
        """
        Given a section and a depth level, walk back through the sections
        parents to see if the depth level matches a previous section.
        
        Return a reference to the right section,
        or raise a SyntaxError.
        """
        while depth < sect.depth:
            if sect is sect.parent:
                # we've reached the top level already
                raise SyntaxError
            sect = sect.parent
        if sect.depth == depth:
            return sect
        # shouldn't get here
        raise SyntaxError

    def _handle_error(self, text, ErrorClass, infile, cur_index):
        """
        Handle an error according to the error settings.
        
        Either raise the error or store it.
        The error will have occured at ``cur_index``
        """
        line = infile[cur_index]
        message = text % cur_index
        error = ErrorClass(message, cur_index, line)
        if self.raise_errors:
            # raise the error - parsing stops here
            raise error
        # store the error
        # reraise when parsing has finished
        self._errors.append(error)

    def _unquote(self, value):
        """Return an unquoted version of a value"""
        if (value[0] == value[-1]) and (value[0] in ('"', "'")):
            value = value[1:-1]
        return value

    def _quote(self, value, multiline=True):
        """
        Return a safely quoted version of a value.
        
        Raise a ConfigObjError if the value cannot be safely quoted.
        If multiline is ``True`` (default) then use triple quotes
        if necessary.
        
        Don't quote values that don't need it.
        Recursively quote members of a list and return a comma joined list.
        Multiline is ``False`` for lists.
        Obey list syntax for empty and single member lists.
        
        If ``list_values=False`` then the value is only quoted if it contains
        a ``\n`` (is multiline).
        """
        if isinstance(value, (list, tuple)):
            if not value:
                return ','
            elif len(value) == 1:
                return self._quote(value[0], multiline=False) + ','
            return ', '.join([self._quote(val, multiline=False)
                for val in value])
        if not isinstance(value, StringTypes):
            if self.stringify:
                value = str(value)
            else:
                raise TypeError, 'Value "%s" is not a string.' % value
        squot = "'%s'"
        dquot = '"%s"'
        noquot = "%s"
        wspace_plus = ' \r\t\n\v\t\'"'
        tsquot = '"""%s"""'
        tdquot = "'''%s'''"
        if not value:
            return '""'
        if (not self.list_values and '\n' not in value) or not (multiline and
                ((("'" in value) and ('"' in value)) or ('\n' in value))):
            if not self.list_values:
                # we don't quote if ``list_values=False``
                quot = noquot
            # for normal values either single or double quotes will do
            elif '\n' in value:
                # will only happen if multiline is off - e.g. '\n' in key
                raise ConfigObjError, ('Value "%s" cannot be safely quoted.' %
                    value)
            elif ((value[0] not in wspace_plus) and
                    (value[-1] not in wspace_plus) and
                    (',' not in value)):
                quot = noquot
            else:
                if ("'" in value) and ('"' in value):
                    raise ConfigObjError, (
                        'Value "%s" cannot be safely quoted.' % value)
                elif '"' in value:
                    quot = squot
                else:
                    quot = dquot
        else:
            # if value has '\n' or "'" *and* '"', it will need triple quotes
            if (value.find('"""') != -1) and (value.find("'''") != -1):
                raise ConfigObjError, (
                    'Value "%s" cannot be safely quoted.' % value)
            if value.find('"""') == -1:
                quot = tdquot
            else:
                quot = tsquot
        return quot % value

    def _handle_value(self, value):
        """
        Given a value string, unquote, remove comment,
        handle lists. (including empty and single member lists)
        
        Testing list values.
        
        >>> testconfig3 = '''
        ... a = ,
        ... b = test,
        ... c = test1, test2   , test3
        ... d = test1, test2, test3,
        ... '''
        >>> d = ConfigObj(testconfig3.split('\\n'), raise_errors=True)
        >>> d['a'] == []
        1
        >>> d['b'] == ['test']
        1
        >>> d['c'] == ['test1', 'test2', 'test3']
        1
        >>> d['d'] == ['test1', 'test2', 'test3']
        1
        
        Testing with list values off.
        
        >>> e = ConfigObj(
        ...     testconfig3.split('\\n'),
        ...     raise_errors=True,
        ...     list_values=False)
        >>> e['a'] == ','
        1
        >>> e['b'] == 'test,'
        1
        >>> e['c'] == 'test1, test2   , test3'
        1
        >>> e['d'] == 'test1, test2, test3,'
        1
        
        Testing creating from a dictionary.
        
        >>> f = {
        ...     'key1': 'val1',
        ...     'key2': 'val2',
        ...     'section 1': {
        ...         'key1': 'val1',
        ...         'key2': 'val2',
        ...         'section 1b': {
        ...             'key1': 'val1',
        ...             'key2': 'val2',
        ...         },
        ...     },
        ...     'section 2': {
        ...         'key1': 'val1',
        ...         'key2': 'val2',
        ...         'section 2b': {
        ...             'key1': 'val1',
        ...             'key2': 'val2',
        ...         },
        ...     },
        ...      'key3': 'val3',
        ... }
        >>> g = ConfigObj(f)
        >>> f == g
        1
        
        Testing we correctly detect badly built list values (4 of them).
        
        >>> testconfig4 = '''
        ... config = 3,4,,
        ... test = 3,,4
        ... fish = ,,
        ... dummy = ,,hello, goodbye
        ... '''
        >>> try:
        ...     ConfigObj(testconfig4.split('\\n'))
        ... except ConfigObjError, e:
        ...     len(e.errors)
        4
        
        Testing we correctly detect badly quoted values (4 of them).
        
        >>> testconfig5 = '''
        ... config = "hello   # comment
        ... test = 'goodbye
        ... fish = 'goodbye   # comment
        ... dummy = "hello again
        ... '''
        >>> try:
        ...     ConfigObj(testconfig5.split('\\n'))
        ... except ConfigObjError, e:
        ...     len(e.errors)
        4
        """
        # do we look for lists in values ?
        if not self.list_values:
            mat = self._nolistvalue.match(value)
            if mat is None:
                raise SyntaxError
            (value, comment) = mat.groups()
            # NOTE: we don't unquote here
            return (value, comment)
        mat = self._valueexp.match(value)
        if mat is None:
            # the value is badly constructed, probably badly quoted,
            # or an invalid list
            raise SyntaxError
        (list_values, single, empty_list, comment) = mat.groups()
        if (list_values == '') and (single is None):
            # change this if you want to accept empty values
            raise SyntaxError
        # NOTE: note there is no error handling from here if the regex
        # is wrong: then incorrect values will slip through
        if empty_list is not None:
            # the single comma - meaning an empty list
            return ([], comment)
        if single is not None:
            single = self._unquote(single)
        if list_values == '':
            # not a list value
            return (single, comment)
        the_list = self._listvalueexp.findall(list_values)
        the_list = [self._unquote(val) for val in the_list]
        if single is not None:
            the_list += [single]
        return (the_list, comment)

    def _multiline(self, value, infile, cur_index, maxline):
        """
        Extract the value, where we are in a multiline situation
        
        Testing multiline values.
        
        >>> i == {
        ...     'name4': ' another single line value ',
        ...     'multi section': {
        ...         'name4': '\\n        Well, this is a\\n        multiline '
        ...             'value\\n        ',
        ...         'name2': '\\n        Well, this is a\\n        multiline '
        ...             'value\\n        ',
        ...         'name3': '\\n        Well, this is a\\n        multiline '
        ...             'value\\n        ',
        ...         'name1': '\\n        Well, this is a\\n        multiline '
        ...             'value\\n        ',
        ...     },
        ...     'name2': ' another single line value ',
        ...     'name3': ' a single line value ',
        ...     'name1': ' a single line value ',
        ... }
        1
        """
        quot = value[:3]
        newvalue = value[3:]
        single_line = self._triple_quote[quot][0]
        multi_line = self._triple_quote[quot][1]
        mat = single_line.match(value)
        if mat is not None:
            retval = list(mat.groups())
            retval.append(cur_index)
            return retval
        elif newvalue.find(quot) != -1:
            # somehow the triple quote is missing
            raise SyntaxError
        #
        while cur_index < maxline:
            cur_index += 1
            newvalue += '\n'
            line = infile[cur_index]
            if line.find(quot) == -1:
                newvalue += line
            else:
                # end of multiline, process it
                break
        else:
            # we've got to the end of the config, oops...
            raise SyntaxError
        mat = multi_line.match(line)
        if mat is None:
            # a badly formed line
            raise SyntaxError
        (value, comment) = mat.groups()
        return (newvalue + value, comment, cur_index)

    def _handle_configspec(self, configspec):
        """Parse the configspec."""
        try:
            configspec = ConfigObj(
                configspec,
                raise_errors=True,
                file_error=True,
                list_values=False)
        except ConfigObjError, e:
            # FIXME: Should these errors have a reference
            # to the already parsed ConfigObj ?
            raise ConfigspecError('Parsing configspec failed: %s' % e)
        except IOError, e:
            raise IOError('Reading configspec failed: %s' % e)
        self._set_configspec_value(configspec, self)

    def _set_configspec_value(self, configspec, section):
        """Used to recursively set configspec values."""
        if '__many__' in configspec.sections:
            section.configspec['__many__'] = configspec['__many__']
            if len(configspec.sections) > 1:
                # FIXME: can we supply any useful information here ?
                raise RepeatSectionError
        for entry in configspec.scalars:
            section.configspec[entry] = configspec[entry]
        for entry in configspec.sections:
            if entry == '__many__':
                continue
            if not section.has_key(entry):
                section[entry] = {}
            self._set_configspec_value(configspec[entry], section[entry])

    def _handle_repeat(self, section, configspec):
        """Dynamically assign configspec for repeated section."""
        try:
            section_keys = configspec.sections
            scalar_keys = configspec.scalars
        except AttributeError:
            section_keys = [entry for entry in configspec 
                                if isinstance(configspec[entry], dict)]
            scalar_keys = [entry for entry in configspec 
                                if not isinstance(configspec[entry], dict)]
        if '__many__' in section_keys and len(section_keys) > 1:
            # FIXME: can we supply any useful information here ?
            raise RepeatSectionError
        scalars = {}
        sections = {}
        for entry in scalar_keys:
            val = configspec[entry]
            scalars[entry] = val
        for entry in section_keys:
            val = configspec[entry]
            if entry == '__many__':
                scalars[entry] = val
                continue
            sections[entry] = val
        #
        section.configspec = scalars
        for entry in sections:
            if not section.has_key(entry):
                section[entry] = {}
            self._handle_repeat(section[entry], sections[entry])

    def _write_line(self, indent_string, entry, this_entry, comment):
        """Write an individual line, for the write method"""
        return '%s%s = %s%s' % (
            indent_string,
            self._quote(entry, multiline=False),
            self._quote(this_entry),
            comment)

    def _write_marker(self, indent_string, depth, entry, comment):
        """Write a section marker line"""
        return '%s%s%s%s%s' % (
            indent_string,
            '[' * depth,
            self._quote(entry, multiline=False),
            ']' * depth,
            comment)

    def _handle_comment(self, comment):
        """
        Deal with a comment.
        
        >>> filename = a.filename
        >>> a.filename = None
        >>> values = a.write()
        >>> index = 0
        >>> while index < 23:
        ...     index += 1
        ...     line = values[index-1]
        ...     assert line.endswith('# comment ' + str(index))
        >>> a.filename = filename
        
        >>> start_comment = ['# Initial Comment', '', '#']
        >>> end_comment = ['', '#', '# Final Comment']
        >>> newconfig = start_comment + testconfig1.split('\\n') + end_comment
        >>> nc = ConfigObj(newconfig)
        >>> nc.initial_comment
        ['# Initial Comment', '', '#']
        >>> nc.final_comment
        ['', '#', '# Final Comment']
        >>> nc.initial_comment == start_comment
        1
        >>> nc.final_comment == end_comment
        1
        """
        if not comment:
            return ''
        if self.indent_type == '\t':
            start = '\t'
        else:
            start = ' ' * NUM_INDENT_SPACES
        if not comment.startswith('#'):
            start += '# '
        return (start + comment)

    def _compute_indent_string(self, depth):
        """
        Compute the indent string, according to current indent_type and depth
        """
        if self.indent_type == '':
            # no indentation at all
            return ''
        if self.indent_type == '\t':
            return '\t' * depth
        if self.indent_type == ' ':
            return ' ' * NUM_INDENT_SPACES * depth
        raise SyntaxError

    # Public methods

    def write(self, section=None):
        """
        Write the current ConfigObj as a file
        
        tekNico: FIXME: use StringIO instead of real files
        
        >>> filename = a.filename
        >>> a.filename = 'test.ini'
        >>> a.write()
        >>> a.filename = filename
        >>> a == ConfigObj('test.ini', raise_errors=True)
        1
        >>> os.remove('test.ini')
        >>> b.filename = 'test.ini'
        >>> b.write()
        >>> b == ConfigObj('test.ini', raise_errors=True)
        1
        >>> os.remove('test.ini')
        >>> i.filename = 'test.ini'
        >>> i.write()
        >>> i == ConfigObj('test.ini', raise_errors=True)
        1
        >>> os.remove('test.ini')
        >>> a = ConfigObj()
        >>> a['DEFAULT'] = {'a' : 'fish'}
        >>> a['a'] = '%(a)s'
        >>> a.write()
        ['a = %(a)s', '[DEFAULT]', 'a = fish']
        """
        int_val = 'test'
        if self.indent_type is None:
            # this can be true if initialised from a dictionary
            self.indent_type = DEFAULT_INDENT_TYPE
        #
        out = []
        return_list = True
        if section is None:
            int_val = self.interpolation
            self.interpolation = False
            section = self
            return_list = False
            for line in self.initial_comment:
                stripped_line = line.strip()
                if stripped_line and not stripped_line.startswith('#'):
                    line = '# ' + line
                out.append(line)
        #
        indent_string = self._compute_indent_string(section.depth)
        for entry in (section.scalars + section.sections):
            if entry in section.defaults:
                # don't write out default values
                continue
            for comment_line in section.comments[entry]:
                comment_line = comment_line.lstrip()
                if comment_line and not comment_line.startswith('#'):
                    comment_line = '#' + comment_line
                out.append(indent_string + comment_line)
            this_entry = section[entry]
            comment = self._handle_comment(section.inline_comments[entry])
            #
            if isinstance(this_entry, dict):
                # a section
                out.append(self._write_marker(
                    indent_string,
                    this_entry.depth,
                    entry,
                    comment))
                out.extend(self.write(this_entry))
            else:
                out.append(self._write_line(
                    indent_string,
                    entry,
                    this_entry,
                    comment))
        #
        if not return_list:
            for line in self.final_comment:
                stripped_line = line.strip()
                if stripped_line and not stripped_line.startswith('#'):
                    line = '# ' + line
                out.append(line)
        #
        if int_val != 'test':
            self.interpolation = int_val
        #
        if (return_list) or (self.filename is None):
            return out
        #
        if isinstance(self.filename, StringTypes):
            h = open(self.filename, 'w')
            h.write(self.BOM or '')
            h.write('\n'.join(out))
            h.close()
        else:
            self.filename.seek(0)
            self.filename.write(self.BOM or '')
            self.filename.write('\n'.join(out))
            # if we have a stored file object (or StringIO)
            # we *don't* close it

    def validate(self, validator, preserve_errors=False, section=None):
        """
        Test the ConfigObj against a configspec.
        
        It uses the ``validator`` object from *validate.py*.
        
        To run ``validate`` on the current ConfigObj, call: ::
        
            test = config.validate(validator)
        
        (Normally having previously passed in the configspec when the ConfigObj
        was created - you can dynamically assign a dictionary of checks to the
        ``configspec`` attribute of a section though).
        
        It returns ``True`` if everything passes, or a dictionary of
        pass/fails (True/False). If every member of a subsection passes, it
        will just have the value ``True``. (It also returns ``False`` if all
        members fail).
        
        In addition, it converts the values from strings to their native
        types if their checks pass (and ``stringify`` is set).
        
        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
        of a marking a fail with a ``False``, it will preserve the actual
        exception object. This can contain info about the reason for failure.
        For example the ``VdtValueTooSmallError`` indeicates that the value
        supplied was too small. If a value (or section) is missing it will
        still be marked as ``False``.
        
        You must have the validate module to use ``preserve_errors=True``.
        
        You can then use the ``flatten_errors`` function to turn your nested
        results dictionary into a flattened list of failures - useful for
        displaying meaningful error messages.
        
        >>> try:
        ...     from validate import Validator
        ... except ImportError:
        ...     print >> sys.stderr, 'Cannot import the Validator object, skipping the related tests'
        ... else:
        ...     config = '''
        ...     test1=40
        ...     test2=hello
        ...     test3=3
        ...     test4=5.0
        ...     [section]
        ...         test1=40
        ...         test2=hello
        ...         test3=3
        ...         test4=5.0
        ...         [[sub section]]
        ...             test1=40
        ...             test2=hello
        ...             test3=3
        ...             test4=5.0
        ... '''.split('\\n')
        ...     configspec = '''
        ...     test1= integer(30,50)
        ...     test2= string
        ...     test3=integer
        ...     test4=float(6.0)
        ...     [section ]
        ...         test1=integer(30,50)
        ...         test2=string
        ...         test3=integer
        ...         test4=float(6.0)
        ...         [[sub section]]
        ...             test1=integer(30,50)
        ...             test2=string
        ...             test3=integer
        ...             test4=float(6.0)
        ...     '''.split('\\n')
        ...     val = Validator()
        ...     c1 = ConfigObj(config, configspec=configspec)
        ...     test = c1.validate(val)
        ...     test == {
        ...         'test1': True,
        ...         'test2': True,
        ...         'test3': True,
        ...         'test4': False,
        ...         'section': {
        ...             'test1': True,
        ...             'test2': True,
        ...             'test3': True,
        ...             'test4': False,
        ...             'sub section': {
        ...                 'test1': True,
        ...                 'test2': True,
        ...                 'test3': True,
        ...                 'test4': False,
        ...             },
        ...         },
        ...     }
        1
        >>> val.check(c1.configspec['test4'], c1['test4'])
        Traceback (most recent call last):
        VdtValueTooSmallError: the value "5.0" is too small.
        
        >>> val_test_config = '''
        ...     key = 0
        ...     key2 = 1.1
        ...     [section]
        ...     key = some text
        ...     key2 = 1.1, 3.0, 17, 6.8
        ...         [[sub-section]]
        ...         key = option1
        ...         key2 = True'''.split('\\n')
        >>> val_test_configspec = '''
        ...     key = integer
        ...     key2 = float
        ...     [section]
        ...     key = string
        ...     key2 = float_list(4)
        ...        [[sub-section]]
        ...        key = option(option1, option2)
        ...        key2 = boolean'''.split('\\n')
        >>> val_test = ConfigObj(val_test_config, configspec=val_test_configspec)
        >>> val_test.validate(val)
        1
        >>> val_test['key'] = 'text not a digit'
        >>> val_res = val_test.validate(val)
        >>> val_res == {'key2': True, 'section': True, 'key': False}
        1
        >>> configspec = '''
        ...     test1=integer(30,50, default=40)
        ...     test2=string(default="hello")
        ...     test3=integer(default=3)
        ...     test4=float(6.0, default=6.0)
        ...     [section ]
        ...         test1=integer(30,50, default=40)
        ...         test2=string(default="hello")
        ...         test3=integer(default=3)
        ...         test4=float(6.0, default=6.0)
        ...         [[sub section]]
        ...             test1=integer(30,50, default=40)
        ...             test2=string(default="hello")
        ...             test3=integer(default=3)
        ...             test4=float(6.0, default=6.0)
        ...     '''.split('\\n')
        >>> default_test = ConfigObj(['test1=30'], configspec=configspec)
        >>> default_test
        {'test1': '30', 'section': {'sub section': {}}}
        >>> default_test.validate(val)
        1
        >>> default_test == {
        ...     'test1': 30,
        ...     'test2': 'hello',
        ...     'test3': 3,
        ...     'test4': 6.0,
        ...     'section': {
        ...         'test1': 40,
        ...         'test2': 'hello',
        ...         'test3': 3,
        ...         'test4': 6.0,
        ...         'sub section': {
        ...             'test1': 40,
        ...             'test3': 3,
        ...             'test2': 'hello',
        ...             'test4': 6.0,
        ...         },
        ...     },
        ... }
        1
        
        Now testing with repeated sections : BIG TEST
        
        >>> repeated_1 = '''
        ... [dogs]
        ...     [[__many__]] # spec for a dog
        ...         fleas = boolean(default=True)
        ...         tail = option(long, short, default=long)
        ...         name = string(default=rover)
        ...         [[[__many__]]]  # spec for a puppy
        ...             name = string(default="son of rover")
        ...             age = float(default=0.0)
        ... [cats]
        ...     [[__many__]] # spec for a cat
        ...         fleas = boolean(default=True)
        ...         tail = option(long, short, default=short)
        ...         name = string(default=pussy)
        ...         [[[__many__]]] # spec for a kitten
        ...             name = string(default="son of pussy")
        ...             age = float(default=0.0)
        ...         '''.split('\\n')
        >>> repeated_2 = '''
        ... [dogs]
        ... 
        ...     # blank dogs with puppies
        ...     # should be filled in by the configspec
        ...     [[dog1]]
        ...         [[[puppy1]]]
        ...         [[[puppy2]]]
        ...         [[[puppy3]]]
        ...     [[dog2]]
        ...         [[[puppy1]]]
        ...         [[[puppy2]]]
        ...         [[[puppy3]]]
        ...     [[dog3]]
        ...         [[[puppy1]]]
        ...         [[[puppy2]]]
        ...         [[[puppy3]]]
        ... [cats]
        ... 
        ...     # blank cats with kittens
        ...     # should be filled in by the configspec
        ...     [[cat1]]
        ...         [[[kitten1]]]
        ...         [[[kitten2]]]
        ...         [[[kitten3]]]
        ...     [[cat2]]
        ...         [[[kitten1]]]
        ...         [[[kitten2]]]
        ...         [[[kitten3]]]
        ...     [[cat3]]
        ...         [[[kitten1]]]
        ...         [[[kitten2]]]
        ...         [[[kitten3]]]
        ... '''.split('\\n')
        >>> repeated_3 = '''
        ... [dogs]
        ... 
        ...     [[dog1]]
        ...     [[dog2]]
        ...     [[dog3]]
        ... [cats]
        ... 
        ...     [[cat1]]
        ...     [[cat2]]
        ...     [[cat3]]
        ... '''.split('\\n')
        >>> repeated_4 = '''
        ... [__many__]
        ... 
        ...     name = string(default=Michael)
        ...     age = float(default=0.0)
        ...     sex = option(m, f, default=m)
        ... '''.split('\\n')
        >>> repeated_5 = '''
        ... [cats]
        ... [[__many__]]
        ...     fleas = boolean(default=True)
        ...     tail = option(long, short, default=short)
        ...     name = string(default=pussy)
        ...     [[[description]]]
        ...         height = float(default=3.3)
        ...         weight = float(default=6)
        ...         [[[[coat]]]]
        ...             fur = option(black, grey, brown, "tortoise shell", default=black)
        ...             condition = integer(0,10, default=5)
        ... '''.split('\\n')
        >>> from validate import Validator
        >>> val= Validator()
        >>> repeater = ConfigObj(repeated_2, configspec=repeated_1)
        >>> repeater.validate(val)
        1
        >>> repeater == {
        ...     'dogs': {
        ...         'dog1': {
        ...             'fleas': True,
        ...             'tail': 'long',
        ...             'name': 'rover',
        ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
        ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
        ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
        ...         },
        ...         'dog2': {
        ...             'fleas': True,
        ...             'tail': 'long',
        ...             'name': 'rover',
        ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
        ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
        ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
        ...         },
        ...         'dog3': {
        ...             'fleas': True,
        ...             'tail': 'long',
        ...             'name': 'rover',
        ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
        ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
        ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
        ...         },
        ...     },
        ...     'cats': {
        ...         'cat1': {
        ...             'fleas': True,
        ...             'tail': 'short',
        ...             'name': 'pussy',
        ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
        ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
        ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
        ...         },
        ...         'cat2': {
        ...             'fleas': True,
        ...             'tail': 'short',
        ...             'name': 'pussy',
        ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
        ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
        ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
        ...         },
        ...         'cat3': {
        ...             'fleas': True,
        ...             'tail': 'short',
        ...             'name': 'pussy',
        ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
        ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
        ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
        ...         },
        ...     },
        ... }
        1
        >>> repeater = ConfigObj(repeated_3, configspec=repeated_1)
        >>> repeater.validate(val)
        1
        >>> repeater == {
        ...     'cats': {
        ...         'cat1': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
        ...         'cat2': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
        ...         'cat3': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
        ...     },
        ...     'dogs': {
        ...         'dog1': {'fleas': True, 'tail': 'long', 'name': 'rover'},
        ...         'dog2': {'fleas': True, 'tail': 'long', 'name': 'rover'},
        ...         'dog3': {'fleas': True, 'tail': 'long', 'name': 'rover'},
        ...     },
        ... }
        1
        >>> repeater = ConfigObj(configspec=repeated_4)
        >>> repeater['Michael'] = {}
        >>> repeater.validate(val)
        1
        >>> repeater == {
        ...     'Michael': {'age': 0.0, 'name': 'Michael', 'sex': 'm'},
        ... }
        1
        >>> repeater = ConfigObj(repeated_3, configspec=repeated_5)
        >>> repeater == {
        ...     'dogs': {'dog1': {}, 'dog2': {}, 'dog3': {}},
        ...     'cats': {'cat1': {}, 'cat2': {}, 'cat3': {}},
        ... }
        1
        >>> repeater.validate(val)
        1
        >>> repeater == {
        ...     'dogs': {'dog1': {}, 'dog2': {}, 'dog3': {}},
        ...     'cats': {
        ...         'cat1': {
        ...             'fleas': True,
        ...             'tail': 'short',
        ...             'name': 'pussy',
        ...             'description': {
        ...                 'weight': 6.0,
        ...                 'height': 3.2999999999999998,
        ...                 'coat': {'fur': 'black', 'condition': 5},
        ...             },
        ...         },
        ...         'cat2': {
        ...             'fleas': True,
        ...             'tail': 'short',
        ...             'name': 'pussy',
        ...             'description': {
        ...                 'weight': 6.0,
        ...                 'height': 3.2999999999999998,
        ...                 'coat': {'fur': 'black', 'condition': 5},
        ...             },
        ...         },
        ...         'cat3': {
        ...             'fleas': True,
        ...             'tail': 'short',
        ...             'name': 'pussy',
        ...             'description': {
        ...                 'weight': 6.0,
        ...                 'height': 3.2999999999999998,
        ...                 'coat': {'fur': 'black', 'condition': 5},
        ...             },
        ...         },
        ...     },
        ... }
        1
        
        Test that interpolation is preserved for validated string values.
        Also check that interpolation works in configspecs.
        >>> t = ConfigObj()
        >>> t['DEFAULT'] = {}
        >>> t['DEFAULT']['test'] = 'a'
        >>> t['test'] = '%(test)s'
        >>> t['test']
        'a'
        >>> v = Validator()
        >>> t.configspec = {'test': 'string'}
        >>> t.validate(v)
        1
        >>> t.interpolation = False
        >>> t
        {'test': '%(test)s', 'DEFAULT': {'test': 'a'}}
        >>> specs = [
        ...    'interpolated string  = string(default="fuzzy-%(man)s")',
        ...    '[DEFAULT]',
        ...    'man = wuzzy',
        ...    ]
        >>> c = ConfigObj(configspec=specs)
        >>> c.validate(v)
        1
        >>> c['interpolated string']
        'fuzzy-wuzzy'
        
        FIXME: Above tests will fail if we couldn't import Validator (the ones
        that don't raise errors will produce different output and still fail as
        tests)
        """
        if section is None:
            if self.configspec is None:
                raise ValueError, 'No configspec supplied.'
            if preserve_errors:
                if VdtMissingValue is None:
                    raise ImportError('Missing validate module.')
            section = self
        #
        spec_section = section.configspec
        if '__many__' in section.configspec:
            many = spec_section['__many__']
            # dynamically assign the configspecs
            # for the sections below
            for entry in section.sections:
                self._handle_repeat(section[entry], many)
        #
        out = {}
        ret_true = True
        ret_false = True
        for entry in spec_section:
            if entry == '__many__':
                continue
            if (not entry in section.scalars) or (entry in section.defaults):
                # missing entries
                # or entries from defaults
                missing = True
                val = None
            else:
                missing = False
                val = section[entry]
            try:
                check = validator.check(spec_section[entry],
                                        val,
                                        missing=missing
                                        )
            except validator.baseErrorClass, e:
                if not preserve_errors or isinstance(e, VdtMissingValue):
                    out[entry] = False
                else:
                    # preserve the error
                    out[entry] = e
                    ret_false = False
                ret_true = False
            else:
                ret_false = False
                out[entry] = True
                if self.stringify or missing:
                    # if we are doing type conversion
                    # or the value is a supplied default
                    if not self.stringify:
                        if isinstance(check, (list, tuple)):
                            # preserve lists
                            check = [str(item) for item in check]
                        elif missing and check is None:
                            # convert the None from a default to a ''
                            check = ''
                        else:
                            check = str(check)
                    if (check != val) or missing:
                        section[entry] = check
                if missing and entry not in section.defaults:
                    section.defaults.append(entry)
        #
        # FIXME: Will this miss missing sections ?
        for entry in section.sections:
            if section is self and entry == 'DEFAULT':
                continue
            check = self.validate(validator, preserve_errors=preserve_errors,
                section=section[entry])
            out[entry] = check
            if check == False:
                ret_true = False
            elif check == True:
                ret_false = False
            else:
                ret_true = False
                ret_false = False
        #
        if ret_true:
            return True
        elif ret_false:
            return False
        else:
            return out

class SimpleVal(object):
    """
    A simple validator.
    Can be used to check that all members expected are present.
    
    To use it, provide a configspec with all your members in (the value given
    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
    members are present, or a dictionary with True/False meaning
    present/missing. (Whole missing sections will be replaced with ``False``)
    
    >>> val = SimpleVal()
    >>> config = '''
    ... test1=40
    ... test2=hello
    ... test3=3
    ... test4=5.0
    ... [section]
    ... test1=40
    ... test2=hello
    ... test3=3
    ... test4=5.0
    ...     [[sub section]]
    ...     test1=40
    ...     test2=hello
    ...     test3=3
    ...     test4=5.0
    ... '''.split('\\n')
    >>> configspec = '''
    ... test1=''
    ... test2=''
    ... test3=''
    ... test4=''
    ... [section]
    ... test1=''
    ... test2=''
    ... test3=''
    ... test4=''
    ...     [[sub section]]
    ...     test1=''
    ...     test2=''
    ...     test3=''
    ...     test4=''
    ... '''.split('\\n')
    >>> o = ConfigObj(config, configspec=configspec)
    >>> o.validate(val)
    1
    >>> o = ConfigObj(configspec=configspec)
    >>> o.validate(val)
    0
    """
    
    def __init__(self):
        self.baseErrorClass = ConfigObjError
    
    def check(self, check, member, missing=False):
        """A dummy check method, always returns the value unchanged."""
        if missing:
            raise self.baseErrorClass
        return member

# Check / processing functions for options
def flatten_errors(cfg, res, levels=None, results=None):
    """
    An example function that will turn a nested dictionary of results
    (as returned by ``ConfigObj.validate``) into a flat list.
    
    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
    dictionary returned by ``validate``.
    
    (This is a recursive function, so you shouldn't use the ``levels`` or
    ``results`` arguments - they are used by the function.
    
    Returns a list of keys that failed. Each member of the list is a tuple :
    ::
    
        ([list of sections...], key, result)
    
    If ``validate`` was called with ``preserve_errors=False`` (the default)
    then ``result`` will always be ``False``.

    *list of sections* is a flattened list of sections that the key was found
    in.
    
    If the section was missing then key will be ``None``.
    
    If the value (or section) was missing then ``result`` will be ``False``.
    
    If ``validate`` was called with ``preserve_errors=True`` and a value
    was present, but failed the check, then ``result`` will be the exception
    object returned. You can use this as a string that describes the failure.
    
    For example *The value "3" is of the wrong type*.
    
    # FIXME: is the ordering of the output arbitrary ?
    >>> import validate
    >>> vtor = validate.Validator()
    >>> my_ini = '''
    ...     option1 = True
    ...     [section1]
    ...     option1 = True
    ...     [section2]
    ...     another_option = Probably
    ...     [section3]
    ...     another_option = True
    ...     [[section3b]]
    ...     value = 3
    ...     value2 = a
    ...     value3 = 11
    ...     '''
    >>> my_cfg = '''
    ...     option1 = boolean()
    ...     option2 = boolean()
    ...     option3 = boolean(default=Bad_value)
    ...     [section1]
    ...     option1 = boolean()
    ...     option2 = boolean()
    ...     option3 = boolean(default=Bad_value)
    ...     [section2]
    ...     another_option = boolean()
    ...     [section3]
    ...     another_option = boolean()
    ...     [[section3b]]
    ...     value = integer
    ...     value2 = integer
    ...     value3 = integer(0, 10)
    ...         [[[section3b-sub]]]
    ...         value = string
    ...     [section4]
    ...     another_option = boolean()
    ...     '''
    >>> cs = my_cfg.split('\\n')
    >>> ini = my_ini.split('\\n')
    >>> cfg = ConfigObj(ini, configspec=cs)
    >>> res = cfg.validate(vtor, preserve_errors=True)
    >>> errors = []
    >>> for entry in flatten_errors(cfg, res):
    ...     section_list, key, error = entry
    ...     section_list.insert(0, '[root]')
    ...     if key is not None:
    ...        section_list.append(key)
    ...     else:
    ...         section_list.append('[missing]')
    ...     section_string = ', '.join(section_list)
    ...     errors.append((section_string, ' = ', error))
    >>> errors.sort()
    >>> for entry in errors:
    ...     print entry[0], entry[1], (entry[2] or 0)
    [root], option2  =  0
    [root], option3  =  the value "Bad_value" is of the wrong type.
    [root], section1, option2  =  0
    [root], section1, option3  =  the value "Bad_value" is of the wrong type.
    [root], section2, another_option  =  the value "Probably" is of the wrong type.
    [root], section3, section3b, section3b-sub, [missing]  =  0
    [root], section3, section3b, value2  =  the value "a" is of the wrong type.
    [root], section3, section3b, value3  =  the value "11" is too big.
    [root], section4, [missing]  =  0
    """
    if levels is None:
        # first time called
        levels = []
        results = []
    if res is True:
        return results
    if res is False:
        results.append((levels[:], None, False))
        if levels:
            levels.pop()
        return results
    for (key, val) in res.items():
        if val == True:
            continue
        if isinstance(cfg.get(key), dict):
            # Go down one level
            levels.append(key)
            flatten_errors(cfg[key], val, levels, results)
            continue
        results.append((levels[:], key, val))
    #
    # Go up one level
    if levels:
        levels.pop()
    #
    return results


# FIXME: test error code for badly built multiline values
# FIXME: test handling of StringIO
# FIXME: test interpolation with writing

def _doctest():
    """
    Dummy function to hold some of the doctests.
    
    >>> a.depth
    0
    >>> a == {
    ...     'key2': 'val',
    ...     'key1': 'val',
    ...     'lev1c': {
    ...         'lev2c': {
    ...             'lev3c': {
    ...                 'key1': 'val',
    ...             },
    ...         },
    ...     },
    ...     'lev1b': {
    ...         'key2': 'val',
    ...         'key1': 'val',
    ...         'lev2ba': {
    ...             'key1': 'val',
    ...         },
    ...         'lev2bb': {
    ...             'key1': 'val',
    ...         },
    ...     },
    ...     'lev1a': {
    ...         'key2': 'val',
    ...         'key1': 'val',
    ...     },
    ... }
    1
    >>> b.depth
    0
    >>> b == {
    ...     'key3': 'val3',
    ...     'key2': 'val2',
    ...     'key1': 'val1',
    ...     'section 1': {
    ...         'keys11': 'val1',
    ...         'keys13': 'val3',
    ...         'keys12': 'val2',
    ...     },
    ...     'section 2': {
    ...         'section 2 sub 1': {
    ...             'fish': '3',
    ...     },
    ...     'keys21': 'val1',
    ...     'keys22': 'val2',
    ...     'keys23': 'val3',
    ...     },
    ... }
    1
    >>> t = '''
    ... 'a' = b # !"$%^&*(),::;'@~#= 33
    ... "b" = b #= 6, 33
    ... ''' .split('\\n')
    >>> t2 = ConfigObj(t)
    >>> assert t2 == {'a': 'b', 'b': 'b'}
    >>> t2.inline_comments['b'] = ''
    >>> del t2['a']
    >>> assert t2.write() == ['','b = b', '']
    
    # Test ``list_values=False`` stuff
    >>> c = '''
    ...     key1 = no quotes
    ...     key2 = 'single quotes'
    ...     key3 = "double quotes"
    ...     key4 = "list", 'with', several, "quotes"
    ...     '''
    >>> cfg = ConfigObj(c.splitlines(), list_values=False)
    >>> cfg == {'key1': 'no quotes', 'key2': "'single quotes'", 
    ... 'key3': '"double quotes"', 
    ... 'key4': '"list", \\'with\\', several, "quotes"'
    ... }
    1
    >>> cfg = ConfigObj(list_values=False)
    >>> cfg['key1'] = 'Multiline\\nValue'
    >>> cfg['key2'] = '''"Value" with 'quotes' !'''
    >>> cfg.write()
    ["key1 = '''Multiline\\nValue'''", 'key2 = "Value" with \\'quotes\\' !']
    >>> cfg.list_values = True
    >>> cfg.write() == ["key1 = '''Multiline\\nValue'''",
    ... 'key2 = \\'\\'\\'"Value" with \\'quotes\\' !\\'\\'\\'']
    1
    
    Test flatten_errors:
    
    >>> from validate import Validator, VdtValueTooSmallError
    >>> config = '''
    ...     test1=40
    ...     test2=hello
    ...     test3=3
    ...     test4=5.0
    ...     [section]
    ...         test1=40
    ...         test2=hello
    ...         test3=3
    ...         test4=5.0
    ...         [[sub section]]
    ...             test1=40
    ...             test2=hello
    ...             test3=3
    ...             test4=5.0
    ... '''.split('\\n')
    >>> configspec = '''
    ...     test1= integer(30,50)
    ...     test2= string
    ...     test3=integer
    ...     test4=float(6.0)
    ...     [section ]
    ...         test1=integer(30,50)
    ...         test2=string
    ...         test3=integer
    ...         test4=float(6.0)
    ...         [[sub section]]
    ...             test1=integer(30,50)
    ...             test2=string
    ...             test3=integer
    ...             test4=float(6.0)
    ...     '''.split('\\n')
    >>> val = Validator()
    >>> c1 = ConfigObj(config, configspec=configspec)
    >>> res = c1.validate(val)
    >>> flatten_errors(c1, res) == [([], 'test4', False), (['section', 
    ...     'sub section'], 'test4', False), (['section'], 'test4', False)]
    True
    >>> res = c1.validate(val, preserve_errors=True)
    >>> check = flatten_errors(c1, res)
    >>> check[0][:2]
    ([], 'test4')
    >>> check[1][:2]
    (['section', 'sub section'], 'test4')
    >>> check[2][:2]
    (['section'], 'test4')
    >>> for entry in check:
    ...     isinstance(entry[2], VdtValueTooSmallError)
    ...     print str(entry[2])
    True
    the value "5.0" is too small.
    True
    the value "5.0" is too small.
    True
    the value "5.0" is too small.
    """

if __name__ == '__main__':
    # run the code tests in doctest format
    #
    testconfig1 = """\
    key1= val    # comment 1
    key2= val    # comment 2
    # comment 3
    [lev1a]     # comment 4
    key1= val    # comment 5
    key2= val    # comment 6
    # comment 7
    [lev1b]    # comment 8
    key1= val    # comment 9
    key2= val    # comment 10
    # comment 11
        [[lev2ba]]    # comment 12
        key1= val    # comment 13
        # comment 14
        [[lev2bb]]    # comment 15
        key1= val    # comment 16
    # comment 17
    [lev1c]    # comment 18
    # comment 19
        [[lev2c]]    # comment 20
        # comment 21
            [[[lev3c]]]    # comment 22
            key1 = val    # comment 23"""
    #
    testconfig2 = """\
                        key1 = 'val1'
                        key2 =   "val2"
                        key3 = val3
                        ["section 1"] # comment
                        keys11 = val1
                        keys12 = val2
                        keys13 = val3
                        [section 2]
                        keys21 = val1
                        keys22 = val2
                        keys23 = val3
                        
                            [['section 2 sub 1']]
                            fish = 3
    """
    #
    testconfig6 = '''
    name1 = """ a single line value """ # comment
    name2 = \''' another single line value \''' # comment
    name3 = """ a single line value """
    name4 = \''' another single line value \'''
        [ "multi section" ]
        name1 = """
        Well, this is a
        multiline value
        """
        name2 = \'''
        Well, this is a
        multiline value
        \'''
        name3 = """
        Well, this is a
        multiline value
        """     # a comment
        name4 = \'''
        Well, this is a
        multiline value
        \'''  # I guess this is a comment too
    '''
    #
    import doctest
    m = sys.modules.get('__main__')
    globs = m.__dict__.copy()
    a = ConfigObj(testconfig1.split('\n'), raise_errors=True)
    b = ConfigObj(testconfig2.split('\n'), raise_errors=True)
    i = ConfigObj(testconfig6.split('\n'), raise_errors=True)
    globs.update({
        'INTP_VER': INTP_VER,
        'a': a,
        'b': b,
        'i': i,
    })
    doctest.testmod(m, globs=globs)

"""
    BUGS
    ====
    
    None known.
    
    TODO
    ====
    
    A method to optionally remove uniform indentation from multiline values.
    (do as an example of using ``walk`` - along with string-escape)
    
    Should the results dictionary from validate be an ordered dictionary if
    `odict <http://www.voidspace.org.uk/python/odict.html>`_ is available ?
    
    Implement a better ``__repr__`` ? (``ConfigObj({})``)
    
    Implement some of the sequence methods (which include slicing) from the
    newer ``odict`` ?
    
    INCOMPATIBLE CHANGES
    ====================
    
    (I have removed a lot of needless complications - this list is probably not
    conclusive, many option/attribute/method names have changed)
    
    Case sensitive
    
    The only valid divider is '='
    
    We've removed line continuations with '\'
    
    No recursive lists in values
    
    No empty section
    
    No distinction between flatfiles and non flatfiles
    
    Change in list syntax - use commas to indicate list, not parentheses
    (square brackets and parentheses are no longer recognised as lists)
    
    ';' is no longer valid for comments and no multiline comments
    
    No attribute access
    
    We don't allow empty values - have to use '' or ""
    
    In ConfigObj 3 - setting a non-flatfile member to ``None`` would
    initialise it as an empty section.
    
    The escape entities '&mjf-lf;' and '&mjf-quot;' have gone
    replaced by triple quote, multiple line values.
    
    The ``newline``, ``force_return``, and ``default`` options have gone
    
    The ``encoding`` and ``backup_encoding`` methods have gone - replaced
    with the ``encode`` and ``decode`` methods.
    
    ``fileerror`` and ``createempty`` options have become ``file_error`` and
    ``create_empty``
    
    Partial configspecs (for specifying the order members should be written
    out and which should be present) have gone. The configspec is no longer
    used to specify order for the ``write`` method.
    
    Exceeding the maximum depth of recursion in string interpolation now
    raises an error ``InterpolationDepthError``.
    
    Specifying a value for interpolation which doesn't exist now raises an
    error ``MissingInterpolationOption`` (instead of merely being ignored).
    
    The ``writein`` method has been removed.
    
    The comments attribute is now a list (``inline_comments`` equates to the
    old comments attribute)
    
    ISSUES
    ======
    
    ``validate`` doesn't report *extra* values or sections.
    
    You can't have a keyword with the same name as a section (in the same
    section). They are both dictionary keys - so they would overlap.
    
    ConfigObj doesn't quote and unquote values if ``list_values=False``.
    This means that leading or trailing whitespace in values will be lost when
    writing. (Unless you manually quote).
    
    Interpolation checks first the 'DEFAULT' subsection of the current
    section, next it checks the 'DEFAULT' section of the parent section,
    last it checks the 'DEFAULT' section of the main section.
    
    Logically a 'DEFAULT' section should apply to all subsections of the *same
    parent* - this means that checking the 'DEFAULT' subsection in the
    *current section* is not necessarily logical ?
    
    In order to simplify unicode support (which is possibly of limited value
    in a config file) I have removed automatic support and added the
    ``encode`` and ``decode methods, which can be used to transform keys and
    entries. Because the regex looks for specific values on inital parsing
    (i.e. the quotes and the equals signs) it can only read ascii compatible
    encodings. For unicode use ``UTF8``, which is ASCII compatible.
    
    Does it matter that we don't support the ':' divider, which is supported
    by ``ConfigParser`` ?
    
    The regular expression correctly removes the value -
    ``"'hello', 'goodbye'"`` and then unquote just removes the front and
    back quotes (called from ``_handle_value``). What should we do ??
    (*ought* to raise exception because it's an invalid value if lists are
    off *sigh*. This is not what you want if you want to do your own list
    processing - would be *better* in this case not to unquote.)
    
    String interpolation and validation don't play well together. When
    validation changes type it sets the value. This will correctly fetch the
    value using interpolation - but then overwrite the interpolation reference.
    If the value is unchanged by validation (it's a string) - but other types
    will be.
    
    List Value Syntax
    =================
    
    List values allow you to specify multiple values for a keyword. This
    maps to a list as the resulting Python object when parsed.
    
    The syntax for lists is easy. A list is a comma separated set of values.
    If these values contain quotes, the hash mark, or commas, then the values
    can be surrounded by quotes. e.g. : ::
    
        keyword = value1, 'value 2', "value 3"
    
    If a value needs to be a list, but only has one member, then you indicate
    this with a trailing comma. e.g. : ::
    
        keyword = "single value",
    
    If a value needs to be a list, but it has no members, then you indicate
    this with a single comma. e.g. : ::
    
        keyword = ,     # an empty list
    
    Using triple quotes it will be possible for single values to contain
    newlines and *both* single quotes and double quotes. Triple quotes aren't
    allowed in list values. This means that the members of list values can't
    contain carriage returns (or line feeds :-) or both quote values.
      
    CHANGELOG
    =========
    
    2005/12/14
    ----------
    
    Validation no longer done on the 'DEFAULT' section (only in the root
    level). This allows interpolation in configspecs.
    
    Change in validation syntax implemented in validate 0.2.1
    
    4.1.0
    
    2005/12/10
    ----------
    
    Added ``merge``, a recursive update.
    
    Added ``preserve_errors`` to ``validate`` and the ``flatten_errors``
    example function.
    
    Thanks to Matthew Brett for suggestions and helping me iron out bugs.
    
    Fixed bug where a config file is *all* comment, the comment will now be
    ``initial_comment`` rather than ``final_comment``.
    
    2005/12/02
    ----------
    
    Fixed bug in ``create_empty``. Thanks to Paul Jimenez for the report.
    
    2005/11/04
    ----------
    
    Fixed bug in ``Section.walk`` when transforming names as well as values.
    
    Added the ``istrue`` method. (Fetches the boolean equivalent of a string
    value).
    
    Fixed ``list_values=False`` - they are now only quoted/unquoted if they
    are multiline values.
    
    List values are written as ``item, item`` rather than ``item,item``.
    
    4.0.1
    
    2005/10/09
    ----------
    
    Fixed typo in ``write`` method. (Testing for the wrong value when resetting
    ``interpolation``).

    4.0.0 Final
    
    2005/09/16
    ----------
    
    Fixed bug in ``setdefault`` - creating a new section *wouldn't* return
    a reference to the new section.
    
    2005/09/09
    ----------
    
    Removed ``PositionError``.
    
    Allowed quotes around keys as documented.
    
    Fixed bug with commas in comments. (matched as a list value)
    
    Beta 5
    
    2005/09/07
    ----------
    
    Fixed bug in initialising ConfigObj from a ConfigObj.
    
    Changed the mailing list address.
    
    Beta 4
    
    2005/09/03
    ----------
    
    Fixed bug in ``Section.__delitem__`` oops.
    
    2005/08/28
    ----------
    
    Interpolation is switched off before writing out files.
    
    Fixed bug in handling ``StringIO`` instances. (Thanks to report from
    "Gustavo Niemeyer" <gustavo@niemeyer.net>)
    
    Moved the doctests from the ``__init__`` method to a separate function.
    (For the sake of IDE calltips).
    
    Beta 3
    
    2005/08/26
    ----------
    
    String values unchanged by validation *aren't* reset. This preserves
    interpolation in string values.
    
    2005/08/18
    ----------
    
    None from a default is turned to '' if stringify is off - because setting 
    a value to None raises an error.
    
    Version 4.0.0-beta2
    
    2005/08/16
    ----------
    
    By Nicola Larosa
    
    Actually added the RepeatSectionError class ;-)
    
    2005/08/15
    ----------
    
    If ``stringify`` is off - list values are preserved by the ``validate``
    method. (Bugfix)
    
    2005/08/14
    ----------
    
    By Michael Foord
    
    Fixed ``simpleVal``.
    
    Added ``RepeatSectionError`` error if you have additional sections in a
    section with a ``__many__`` (repeated) section.
    
    By Nicola Larosa
    
    Reworked the ConfigObj._parse, _handle_error and _multiline methods:
    mutated the self._infile, self._index and self._maxline attributes into
    local variables and method parameters
    
    Reshaped the ConfigObj._multiline method to better reflect its semantics
    
    Changed the "default_test" test in ConfigObj.validate to check the fix for
    the bug in validate.Validator.check
    
    2005/08/13
    ----------
    
    By Nicola Larosa
    
    Updated comments at top
    
    2005/08/11
    ----------
    
    By Michael Foord
    
    Implemented repeated sections.
    
    By Nicola Larosa
    
    Added test for interpreter version: raises RuntimeError if earlier than
    2.2
    
    2005/08/10
    ----------
   
    By Michael Foord
     
    Implemented default values in configspecs.
    
    By Nicola Larosa
    
    Fixed naked except: clause in validate that was silencing the fact
    that Python2.2 does not have dict.pop
    
    2005/08/08
    ----------
    
    By Michael Foord
    
    Bug fix causing error if file didn't exist.
    
    2005/08/07
    ----------
    
    By Nicola Larosa
    
    Adjusted doctests for Python 2.2.3 compatibility
    
    2005/08/04
    ----------
    
    By Michael Foord
    
    Added the inline_comments attribute
    
    We now preserve and rewrite all comments in the config file
    
    configspec is now a section attribute
    
    The validate method changes values in place
    
    Added InterpolationError
    
    The errors now have line number, line, and message attributes. This
    simplifies error handling
    
    Added __docformat__
    
    2005/08/03
    ----------
    
    By Michael Foord
    
    Fixed bug in Section.pop (now doesn't raise KeyError if a default value
    is specified)
    
    Replaced ``basestring`` with ``types.StringTypes``
    
    Removed the ``writein`` method
    
    Added __version__
    
    2005/07/29
    ----------
    
    By Nicola Larosa
    
    Indentation in config file is not significant anymore, subsections are
    designated by repeating square brackets
    
    Adapted all tests and docs to the new format
    
    2005/07/28
    ----------
    
    By Nicola Larosa
    
    Added more tests
    
    2005/07/23
    ----------
    
    By Nicola Larosa
    
    Reformatted final docstring in ReST format, indented it for easier folding
    
    Code tests converted to doctest format, and scattered them around
    in various docstrings
    
    Walk method rewritten using scalars and sections attributes
    
    2005/07/22
    ----------
    
    By Nicola Larosa
    
    Changed Validator and SimpleVal "test" methods to "check"
    
    More code cleanup
    
    2005/07/21
    ----------
    
    Changed Section.sequence to Section.scalars and Section.sections
    
    Added Section.configspec
    
    Sections in the root section now have no extra indentation
    
    Comments now better supported in Section and preserved by ConfigObj
    
    Comments also written out
    
    Implemented initial_comment and final_comment
    
    A scalar value after a section will now raise an error
    
    2005/07/20
    ----------
    
    Fixed a couple of bugs
    
    Can now pass a tuple instead of a list
    
    Simplified dict and walk methods
    
    Added __str__ to Section
    
    2005/07/10
    ----------
    
    By Nicola Larosa
    
    More code cleanup
    
    2005/07/08
    ----------
    
    The stringify option implemented. On by default.
    
    2005/07/07
    ----------
    
    Renamed private attributes with a single underscore prefix.
    
    Changes to interpolation - exceeding recursion depth, or specifying a
    missing value, now raise errors.
    
    Changes for Python 2.2 compatibility. (changed boolean tests - removed
    ``is True`` and ``is False``)
    
    Added test for duplicate section and member (and fixed bug)
    
    2005/07/06
    ----------
    
    By Nicola Larosa
    
    Code cleanup
    
    2005/07/02
    ----------
    
    Version 0.1.0
    
    Now properly handles values including comments and lists.
    
    Better error handling.
    
    String interpolation.
    
    Some options implemented.
    
    You can pass a Section a dictionary to initialise it.
    
    Setting a Section member to a dictionary will create a Section instance.
    
    2005/06/26
    ----------
    
    Version 0.0.1
    
    Experimental reader.
    
    A reasonably elegant implementation - a basic reader in 160 lines of code.
    
    *A programming language is a medium of expression.* - Paul Graham
"""

# -*- coding: utf-8 -*-
#13-09-04
# v1.1.5

# **build 3**

# dataenc.py
# password encoding and comparing module
# The strength of the SHA hashing module - in an ascii safe, timestamped format.

# uses a binary to ascii encoding
# and will timestamp the encodings as well.
# (Binary watermarking of data).

# used in a CGI called 'custbase' - to check logins are correct
# (For the CGI to function it needs the users password (or it's SH5 hash) to be encoded into
# each page as a hidden form field. This exposes the encrypted password in the HTML source of each page.
# This module provides functions to interleave a timestamp *into* the hash.
# Even if the encoded 'timestamped hash' is extracted from the HTML source, the CGI can tell
# that the password has expired).


# Contains functions to :

# do binary to ascii encoding using a TABLE mapping (and ascii to binary)
# binary interleave - to disperse one set of binary data into another (e.g. as a 'watermark' or date/time stamp)
# to extract the watermark again 
# convert a decimal value to base 64 digits, and base 64 digits back to 8 bit digits..
# Creating and retrieving a timestamp from the current time/date
# functions for testing and setting bits in a byte (or larger value)
# (including a bitwise operator object that comes from the python cookbook
# and is no longer used here, but included for reference).

# Wrapping all that together to return an ascii encoded, date stamped SHA hash of a string


# Copyright Michael Foord 2004
# dataenc.py
# Functions for encoding and interleaving data.

# http://www.voidspace.org.uk/python/modules.shtml

# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/documents/BSD-LICENSE.txt

# For information about bugfixes, updates and support, please join the Pythonutils mailing list.
# http://voidspace.org.uk/mailman/listinfo/pythonutils_voidspace.org.uk
# Comments, suggestions and bug reports welcome.
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# E-mail fuzzyman@voidspace.org.uk


"""
DOCS for dataenc as a module

When run it should go through a few basic tests - see the function test()

This module provides low-level functions to interleave two bits of data into each other and separate them.
It will also encode this binary data to and from ascii - for inclusion in HTML, cookies or email transmission.

It also provides high level functions to use these functions for time stamping passwords and password hashes,
and also to check that a 'time-stamped hash' is both valid and unexpired.

The check_pass function is interesting. Given an encoded and timestamped hash it compares it with the hash (using SD5) of a password.
If it matches *and* is unexpired (you set the time limit) it returns a new encoded time stamp of the hash with the current time.
I use this for secure, time limited, logins over CGI. (Could be stored in a cookie as well).
(On the first login you will need to compare the password with the stored hash and use that to generate a time stamped hash to include in the page returned.
Thereafter you can just use the check_pass function and include the time-stamped hash in a hidden form field for every action.)

The binary data is interleaved on a 'bitwise' basis - every byte is mangled.

--

CONSTANTS 

The main constant defined in dataenc.py is :

TABLE = '_-0123456789' + \
         'abcdefghijklmnopqrstuvwxyz'+ \
         'NOPQRSTUVWXYZABCDEFGHIJKLM'
TABLE should be exactly 64 printable characters long... or we'll all die horribly
Obviously the same TABLE should be used for decoding as for encoding....
note - changing the order of the TABLE here can be used to change the mapping.
Versions 1.1.2+ of TABLE uses only characters that are safe to pass in URLs
(e.g. using the GET method for passing FORM data)

OLD_TABLE is the previous encoding map used for versions of dataenc.py previous to 1.1.2
See the table_dec function for how to decode data encoded with that map.

PSYCOIN = 1
This decides if we attempt to import psyco or not (the specialising compiler). Set to 0 to not import.
If we attempt but fail to import psyco then this value will be set to 0.

DATEIN = 1
As above but for the dateutils and time module.
We need to import dateutils for the expired and pass_enc functions (amongst others) to work fully.


FUNCTIONS

Following are the docstrings extracted from the public functions :

pass_enc(instring, indict = {}, **keywargs)
    Returns an ascii version of an SHA hash or a string, with the date/time stamped into it.
    e.g. For ascii safe storing of password hashes.

    It also accepts the following keyword args (or a dictionary conatining the following keys).
    (Keywords shown - with default values).
    
    lower = False, sha_hash = False, daynumber = None, timestamp = None, endleave = False

    Setting lower to True makes instring lowercase before hashing/encoding.

    If sha_hash is set to True then instead of the actual string passed in being encoded, it's SHA hash
    is encoded. (In either case the string can contain any binary data).

    If a daynumber is passed in then the daynumber will be encoded into the returned string.
    (daynumber is an integer representing the 'Julian day number' of a date - see the dateutils module).
    This can be used as a 'datestamp' for the generated code and you can detect anyone reusing old codes this way.
    If 'daynumber' is set to True then today's daynumber will automatically be used.
    (dateutils module required - otherwise it will be ignored).

    Max allowed value for daynumber is 16777215 (9th May 41222)
    (so daynumber can be any integer from 1 to 16777215 that you want to 'watermark' the hash with
    could be used as a session ID for a CGI for example).

    If a timestamp is passed in it should either be timestamp = True meanining use 'now'.
    Or it should be a tuple (HOUR, MINUTES).
    HOUR should be an integer 0-23
    MINUTES should be an integer 0-59

    The time and date stamp is *binary* interleaved, before encoding, into the data.

    If endleave is set to True then the timestamp is interleaved more securely. Shouldn't be necessary in practise
    because the stamp is so short and we subsequently encode using table_enc.
    If the string is long this will slow down the process - because we interleave twice.

    
pass_dec(incode)
    Given a string encoded by pass_enc - it returns it decoded.
    It also extracts the datestamp and returns that.
    The return is :
    (instring, daynumber, timestamp)


expired(daynumber, timestamp, validity)
    Given the length of time a password is valid for, it checks if a daynumber/timestamp tuple is
    still valid.
    validity should be an integer tuple (DAYS, HOURS, MINUTES).
    Returns True for valid or False for invalid. 
    Needs the dateutils module to get the current daynumber.

unexpired is an alias for expired - because it makes for better tests.
(The return results from the expired function are logically the wrong way round, expired returns True if the timestamp is *not* expired..)
 
 
check_pass(inhash, pswdhash, EXPIRE)
    Given the hash (possibly from a webpage or cookie) it checks that it is still valid and matches the password it is supposed to have.
    If so it returns a new hash - with the current time stamped into it.
    EXPIRE is a validity tuple to test for (see expired function)
    e.g. (0, 1, 0) means the supplied hash should be no older than 1 hour
    
    If the hash is expired it returns -1.
    If the pass is invalid or doesn't match the supplied pswdhash it returns False.
    This is a high level function that can do all your password checking and 'time-stamped hash' generation after initial login.


makestamp(daynumber, timestamp)
    Receives a Julian daynumber (integer 1 to 16777215) and an (HOUR, MINUTES) tuple timestamp.
    Returns a 5 digit string of binary characters that represent that date/time.
    Can receive None for either or both of these arguments.

    The function 'daycount' in dateutils will turn a date into a daynumber.


dec_datestamp(datestamp)
    Given a 5 character datestamp made by makestamp, it returns it as the tuple :
    (daynumber, timestamp).
    daynumber and timestamp can either be None *or*
    daynumber is an integer between 1 and 16777215
    timestamp is  (HOUR, MINUTES)

    The function 'counttodate' in dateutils will turn a daynumber back into a date.
    

sixbit(invalue)
    Given a value in it returns a list representing the base 64 version of that number.
    Each value in the list is an integer from 0-63...
    The first member of the list is the most significant figure... down to the remainder.
    Should only be used for positive values.


sixtoeight(intuple)
    Given four base 64 (6-bit) digits... it returns three 8 bit digits that represent
    the same value.
    If length of intuple != 4, or any digits are > 63, it returns None.

    **NOTE**
    Not quite the reverse of the sixbit function.
    

table_enc(instring, table=TABLE)
    The actual function that performs TABLE encoding.
    It takes instring in three character chunks (three 8 bit values)
    and turns it into 4 6 bit characters.
    Each of these 6 bit characters maps to a character in TABLE.
    If the length of instring is not divisible by three it is padded with Null bytes.
    The number of Null bytes to remove is then encoded as a semi-random character at the start of the string.
    You can pass in an alternative 64 character string to do the encoding with if you want.


table_dec(instring, table=TABLE)
    The function that performs TABLE decoding.
    Given a TABLE encoded string it returns the original binary data - as a string.
    If the data it's given is invalid (not data encoded by table_enc) it returns None
    (definition of invalid : not consisting of characters in the TABLE or length not len(instring) % 4 = 1).
    You can pass in an alternative 64 character string to do the decoding with if you want.
    

return_now()
    Returns the time now.
    As (HOUR, MINUTES).
    

binleave(data1, data2, endleave = False)
    Given two strings of binary data it interleaves data1 into data2 on a bitwise basis
    and returns a single string combining both. (not just the bytes interleaved).
    The returned string will be 4 bytes or so longer than the two strings passed in.
    Use bin_unleave to return the two strings again.
    Even if both strings passed in are ascii - the result will contain non-ascii characters.
    To keep ascii-safe you must subsequently encode with table_enc.

    Max length for the smallest data string (one string can be of unlimited size) is about 16meg
    (increasing this would be easy if anyone needed it - but would be very slow anyway).

    If either string is empty (or the smallest string greater than 16meg) - we return None.
    The first 4 characters of the string returned 'define' the interleave. (actually the size of the watermark)
    For added safety you could remove this and send seperately.

    Version 1.0.0 used a bf (bitfield) object from the python cookbook. Version 1.1.0 uses the binary and & and or |
    operations and is about 2.5 times faster. On my AMD 3000, leaving and unleaving two 20k files took 1.8 seconds.
    (instead of 4.5 previously - with Psyco enabled this improved to 0.4 seconds.....)

    Interleaving a file with a watermark of pretty much any size makes it unreadable - this is because *every* byte is changed.
    (Except perhaps a few at the end - see the endleave keyword). However it shouldn't be relied on if you need
    a really secure method of encryption. For many purposes it will be sufficient however.
    
    In practise any file not an exact multiple of the size of the watermark will have a chunk at the end that is untouched.
    To get round this you can set endleave = True.. which then releaves the end data back into itself.
    (and therefore takes twice as long - it shouldn't be necessary where you have a short watermark.)

    data2 ought to be the smaller string - or they will be swapped round internally.
    This could cause you to get them back in an unexpected order from binunleave.    


binunleave(data)
    Given a chunk of data woven by binleave - it returns the two seperate pieces of data.
    

For the binary operations of binleave and binunleave, version 1.0.0 used a bf (bitfield) object from
the python cookbook.

class bf(object)
    the bf(object) from activestate python cookbook - by Sebastien Keim - Many Thanks
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/113799

Version 1.1.0 replaced these with specific binary AND & and OR | operations that are about 2.5 times faster.
They are 'inline' in the functions for speed (avoiding function calls) but are available separately as well.

def bittest(value, bitindex)
    This function returns the setting of any bit from a value.
    bitindex starts at 0.

def bitset(value, bitindex, bit)
    Sets a bit, specified by bitindex, in in 'value' to 'bit'.
    bit should be 1 or 0



There are also the 'private functions' which actually contain the substance of binleave and binunleave,
You are welcome to 'browse' them - but you shouldn't need to use them directly.


Any comments, suggestions and bug reports welcome.

Regards,

Fuzzy

michael AT foord DOT me DOT uk


"""

#import sha hashlib.sha1
import hashlib
from random import random

DATEIN = 1
if DATEIN:
    try:                # try to import the dateutils and time module
        from time import strftime
        from dateutils import daycount, returndate          # ,counttodate          # counttodate returns a daynumber as a date
    except:
        DATEIN = 0

# If PSYCOON is set to 0 then we won't try and import the psyco module
# IF importing fails, PSYCOON is set to 0
PSYCOON = 1
if PSYCOON:
    try:
        import psyco
        psyco.full()
        from psyco.classes import *
        try:
            psyco.cannotcompile(re.compile)         # psyco hinders rather than helps regular expression compilation
        except NameError:
            pass
    except:
        PSYCOON = 0

# note - changing the order of the TABLE here can be used to change the mapping.
TABLE = '_-0123456789' + \
         'abcdefghijklmnopqrstuvwxyz'+ \
         'NOPQRSTUVWXYZABCDEFGHIJKLM'
# table should be exactly 64 printable characters long... or we'll all die horribly
# Obviously the same TABLE should be used for decoding as for encoding....
# This version of TABLE (v1.1.2) uses only characters that are safe to pass in URLs
# (e.g. using the GET method for passing FORM data)


OLD_TABLE = '!$%^&*()_-+=' + \
         'abcdefghijklmnopqrstuvwxyz'+ \
         'NOPQRSTUVWXYZABCDEFGHIJKLM'
# OLD_TABLE is the old encoding. If anyone has stuff encoded with this then it can be decoded using :
# data = table_dec(encodedstring, OLD_TABLE)


def pass_enc(instring, indict=None, **keywargs):
    """Returns an ascii version of an SHA hash or a string, with the date/time stamped into it.
    e.g. For ascii safe storing of password hashes.

    It also accepts the following keyword args (or a dictionary conatining the following keys).
    (Keywords shown - with default values).
    
    lower = False, sha_hash = False, daynumber = None, timestamp = None, endleave = False

    Setting lower to True makes instring lowercase before hashing/encoding.

    If sha_hash is set to True then instead of the actual string passed in being encoded, it's SHA hash
    is encoded. (In either case the string can contain any binary data).

    If a daynumber is passed in then the daynumber will be encoded into the returned string.
    (daynumber is an integer representing the 'Julian day number' of a date - see the dateutils module).
    This can be used as a 'datestamp' for the generated code and you can detect anyone reusing old codes this way.
    If 'daynumber' is set to True then today's daynumber will automatically be used.
    (dateutils module required - otherwise it will be ignored).

    Max allowed value for daynumber is 16777215 (9th May 41222)
    (so daynumber can be any integer from 1 to 16777215 that you want to 'watermark' the hash with
    could be used as a session ID for a CGI for example).

    If a timestamp is passed in it should either be timestamp = True meanining use 'now'.
    Or it should be a tuple (HOUR, MINUTES).
    HOUR should be an integer 0-23
    MINUTES should be an integer 0-59

    The time and date stamp is *binary* interleaved, before encoding, into the data.

    If endleave is set to True then the timestamp is interleaved more securely. Shouldn't be necessary in practise
    because the stamp is so short and we subsequently encode using table_enc.
    If the string is long this will slow down the process - because we interleave twice.
    """
    if indict == None: indict = {}
    arglist = {'lower' : False, 'sha_hash' : False, 'daynumber' : None, 'timestamp' : None, 'endleave' : False}

    if not indict and keywargs:         # if keyword passed in instead of a dictionary - we use that
        indict = keywargs
    for keyword in arglist:             # any keywords not specified we use the default
        if not indict.has_key(keyword):
            indict[keyword] = arglist[keyword]
            
    if indict['lower']:     # keyword lower :-)
        instring = instring.lower()
    if indict['sha_hash']:
        instring =  hashlib.sha1(instring).digest()

    if indict['daynumber'] == True:
        if not DATEIN:
            indict['daynumber'] = None
        else:
            a,b,c = returndate()
            indict['daynumber'] = daycount(a,b,c)       # set the daycount to today
    if indict['timestamp']== True:
        if not DATEIN:
            indict['timestamp'] = None
        else:
            indict['timestamp'] = return_now()          # set the time to now.

    datestamp = makestamp(indict['daynumber'], indict['timestamp'])
    if len(instring) == len(datestamp): instring = instring + '&mjf-end;'               # otherwise we can't tell which is which when we unleave them later :-)
    outdata = binleave(instring, datestamp, indict['endleave'])
    return table_enc(outdata)         # do the encoding of the actual string



def pass_dec(incode):
    """Given a string encoded by pass_enc - it returns it decoded.
    It also extracts the datestamp and returns that.
    The return is :
    (instring, daynumber, timestamp)
    """
    binary = table_dec(incode)
    out1, out2 = binunleave(binary)
    if len(out1) == 5:
        datestamp = out1
        if out2.endswith('&mjf-end;'):
            out2 = out2[:-9]
        instring = out2
    else:
        datestamp = out2
        if out1.endswith('&mjf-end;'):
            out1 = out1[:-9]
        instring = out1
    daynumber, timestamp = dec_datestamp(datestamp)
    return instring, daynumber, timestamp



def expired(daynumber, timestamp, validity):
    """Given the length of time a password is valid for, it checks if a daynumber/timestamp tuple is
    still valid.
    validity should be an integer tuple (DAYS, HOURS, MINUTES).
    Returns True for valid or False for invalid. 
    Needs the dateutils module to get the current daynumber.

>>> a, b, c = returndate()
>>> today = daycount(a, b, c)
>>> h, m = return_now()
>>> expired(today, (h, m-2), (0,0,1))
False
>>> expired(today, (h, m-2), (0,0,10))
True
>>> expired(today, (h-2, m-2), (0,1,10))
False
>>> expired(today-1, (h-2, m-2), (1,1,10))
False
>>> expired(today-1, (h-2, m-2), (2,1,10))
True
>>> 
    """
    if not DATEIN:
        raise ImportError("Need the dateutils module to use the 'expired' function.")

    h1, m1 = timestamp
# h1, m1 are the hours and minutes of the timestamp
    d2, h2, m2 = validity
# validity is how long the timestamp is valid for

    a, b, c = returndate()
    today = daycount(a, b, c)
# today is number representing the julian day number of today
    h, m = return_now()
# h, m are the hours and minutes of time now

    h1 = h1 + h2
    m1 = m1 + m2
    daynumber = daynumber + d2
# so we need to test if today, h, m are greater than daynumber, h1, m1
# But first we need to adjust because we might currently have hours above 23 and minutes above 59
    while m1 > 59:
        h1 += 1
        m1 -= 60
    while h1 > 23:
        daynumber += 1
        h1 -= 24
    daynumber += d2
    if today > daynumber:
        return False
    if today < daynumber:
        return True
    if h > h1:                  # same day
        return False
    if h < h1:
        return True
    if m > m1:                  # same hour
        return False
    else:
        return True

unexpired = expired             # Technically unexpired is a better name since this function returns True if the timestamp is unexpired.

def makestamp(daynumber, timestamp):
    """Receives a Julian daynumber (integer 1 to 16777215) and an (HOUR, MINUTES) tuple timestamp.
    Returns a 5 digit string of binary characters that represent that date/time.
    Can receive None for either or both of these arguments.

    The function 'daycount' in dateutils will turn a date into a daynumber.
    """
    if not daynumber:
        datestamp = chr(0)*3
    else:
        day1 = daynumber//65536
        daynumber = daynumber % 65536
        day2 = daynumber//256
        daynumber = daynumber%256
        datestamp = chr(day1) + chr(day2) + chr(daynumber)
    if not timestamp:
        datestamp = datestamp + chr(255)*2
    else:
        datestamp = datestamp + chr(timestamp[0]) + chr(timestamp[1])
    return datestamp


def dec_datestamp(datestamp):
    """Given a 5 character datestamp made by makestamp, it returns it as the tuple :
    (daynumber, timestamp).
    daynumber and timestamp can either be None *or*
    daynumber is an integer between 1 and 16777215
    timestamp is  (HOUR, MINUTES)

    The function 'counttodate' in dateutils will turn a daynumber back into a date."""
    daynumber = datestamp[:3]
    timechars = datestamp[3:]
    daynumber = ord(daynumber[0])*65536 + ord(daynumber[1])*256 + ord(daynumber[2])
    if daynumber == 0: daynumber = None
    if ord(timechars[0]) == 255:
        timestamp = None
    else:
        timestamp = (ord(timechars[0]), ord(timechars[1]))
    return daynumber, timestamp



def sixbit(invalue):
    """Given a value in it returns a list representing the base 64 version of that number.
    Each value in the list is an integer from 0-63...
    The first member of the list is the most significant figure... down to the remainder.
    Should only be used for positive values.
    """
    if invalue < 1:     # special case !
        return [0]
    power = -1
    outlist = []
    test = 0 
    while test <= invalue:
        power += 1
        test = pow(64,power)
        
    while power:
        power -= 1
        outlist.append(int(invalue//pow(64,power)))
        invalue = invalue % pow(64,power)
    return outlist

def sixtoeight(intuple):
    """Given four base 64 (6-bit) digits... it returns three 8 bit digits that represent
    the same value.
    If length of intuple != 4, or any digits are > 63, it returns None.

    **NOTE**
    Not quite the reverse of the sixbit function."""
    if len(intuple) != 4: return None
    for entry in intuple:
        if entry > 63:
            return None
    value = intuple[3] + intuple[2]*64 + intuple[1]*4096 + intuple[0]*262144
    val1 = value//65536
    value = value % 65536
    val2 = value//256
    value = value % 256
    return val1, val2, value


def table_enc(instring, table=None):
    """The actual function that performs TABLE encoding.
    It takes instring in three character chunks (three 8 bit values)
    and turns it into 4 6 bit characters.
    Each of these 6 bit characters maps to a character in TABLE.
    If the length of instring is not divisible by three it is padded with Null bytes.
    The number of Null bytes to remove is then encoded as a semi-random character at the start of the string.
    You can pass in an alternative 64 character string to do the encoding with if you want.
    """
    if table == None: table = TABLE
    out = []
    test = len(instring) % 3
    if test: instring = instring + chr(0)*(3-test)          # make sure the length of instring is divisible by 3
#    print test,'  ', len(instring) % 3
    while instring:
        chunk = instring[:3]
        instring = instring[3:]
        value = 65536 * ord(chunk[0]) + 256 * ord(chunk[1]) + ord(chunk[2])
        newdat = sixbit(value)
        while len(newdat) < 4:
            newdat.insert(0, 0)
        for char in newdat:
            out.append(table[char])
    if not test:
        out.insert(0, table[int(random()*21)])                # if we added 0 extra characters we add a character from 0 to 20
    elif test == 1:
        out.insert(0, table[int(random()*21)+21])                # if we added 1 extra characters we add a character from 21 to 41
    elif test == 2:
        out.insert(0, table[int(random()*22)+42])                 # if we added 1 extra characters we add a character from 42 to 63
    return ''.join(out)

def table_dec(instring, table=None):
    """The function that performs TABLE decoding.
    Given a TABLE encoded string it returns the original binary data - as a string.
    If the data it's given is invalid (not data encoded by table_enc) it returns None
    (definition of invalid : not consisting of characters in the TABLE or length not len(instring) % 4 = 1).
    You can pass in an alternative 64 character string to do the decoding with if you want.
    """
    if table == None: table = TABLE
    out = []
    rem_test = table.find(instring[0])      # remove the length data at the end
    if rem_test == -1: return None
    instring = instring[1:]
    if len(instring)%4 != 0: return None        # check the length is now divisible by 4
    while instring:
        chunk = instring[:4]
        instring = instring[4:]
        newchunk = []
        for char in chunk:
            test = table.find(char)
            if test == -1: return None
            newchunk.append(test)
        newchars = sixtoeight(newchunk)
        if not newchars: return None
        for char in newchars:
            out.append(chr(char))
    if rem_test > 41:
        out = out[:-1]
    elif rem_test > 20:
        out = out[:-2]
    return ''.join(out)

def return_now():
    """Returns the time now.
    As (HOUR, MINUTES)."""
    return int(strftime('%I')), int(strftime('%M'))

def check_pass(inhash, pswdhash, EXPIRE):
    """Given the hash (possibly from a webpage) it checks that it is still valid and matches the password it is supposed
    to have.
    If so it returns the new hash.
    If expired it returns -1.
    If the pass is invalid it returns False."""
    try:
        instring, daynumber, timestamp = pass_dec(inhash)         # of course a fake or mangled password will cause an exception here
        if not table_dec(pswdhash) == instring:
            return False
        if not unexpired(daynumber, timestamp, EXPIRE):            # this tests if the hash is still valid and is the password hash the same as the password hash encoded in the page ?
            return -1
        else:
            return pass_enc(instring, daynumber = True, timestamp = True)       # generate a new hash, with the current time
    except:
        return False

def binleave(data1, data2, endleave = False):
    """Given two strings of binary data it interleaves data1 into data2 on a bitwise basis
    and returns a single string combining both. (bits interleaved not just the bytes).
    The returned string will be 4 bytes or so longer than the two strings passed in.
    Use bin_unleave to return the two strings again.
    Even if both strings passed in are ascii - the result will contain non-ascii characters.
    To keep ascii-safe you must subsequently encode with table_enc.

    Max length for the smallest data string (one string can be of unlimited size) is about 16meg
    (increasing this would be easy if anyone needed it - but would be very slow anyway).

    If either string is empty (or the smallest string greater than 16meg) - we return None.
    The first 4 characters of the string returned 'define' the interleave. (actually the size of the watermark)
    For added safety you could remove this and send seperately.

    Version 1.0.0 used a bf (bitfield) object from the python cookbook. Version 1.1.0 uses the binary and & and or |
    operations and is about 2.5 times faster. On my AMD 3000, leaving and unleaving two 20k files took 1.8 seconds.
    (instead of 4.5 previously - with Psyco enabled this improved to 0.4 seconds.....)

    Interleaving a file with a watermark of pretty much any size makes it unreadable - this is because *every* byte is changed.
    (Except perhaps a few at the end - see the endleave keyword). However it shouldn't be relied on if you need
    a really secure method of encryption. For many purposes it will be sufficient however.
    
    In practise any file not an exact multiple of the size of the watermark will have a chunk at the end that is untouched.
    To get round this you can set endleave = True.. which then releaves the end data back into itself.
    (and therefore takes twice as long - it shouldn't be necessary where you have a short watermark.)

    data2 ought to be the smaller string - or they will be swapped round internally.
    This could cause you to get them back in an unexpected order from binunleave.    
    """
    header, out, data1 = internalfunc(data1,data2)
#    print len(data1), len(out), len(header)
    header = chr(int(random()*128)) + header            # making it a 4 byte header
    if endleave and data1 and len(data1) < 65536:
        header, out, data1 = internalfunc(header + out, data1)
        header = chr(int(random()*128)+ 128) + header
    return header + out + data1

def binunleave(data):
    """Given a chunk of data woven by binleave - it returns the two seperate pieces of data."""
    header = data[0]
    data = data[1:]
    data1, data2 = internalfunc2(data)
    if ord(header) > 127:
#        print len(data1)
        data = data2 + data1
        data = data[1:]
        data1, data2 = internalfunc2(data)
    return data1, data2

######################

# binleave and binunleave used to make extensive use of a python objectcalled bf() (bitfield)
# There are still many places this could be useful, but I now use binary operations inline.
# Included for reference is the bf object and the binary operations as functions.

class bf(object):
    """the bf(object) from activestate python cookbook - by Sebastien Keim - Many Thanks
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/113799"""
    def __init__(self,value=0):
        self._d = value

    def __getitem__(self, index):
        return (self._d >> index) & 1 

    def __setitem__(self,index,value):
        value    = (value&1L)<<index
        mask     = (1L)<<index
        self._d  = (self._d & ~mask) | value

    def __getslice__(self, start, end):
        mask = 2L**(end - start) -1
        return (self._d >> start) & mask

    def __setslice__(self, start, end, value):
        mask = 2L**(end - start) -1
        value = (value & mask) << start
        mask = mask << start
        self._d = (self._d & ~mask) | value
        return (self._d >> start) & mask

    def __int__(self):
        return self._d


def bittest(value, bitindex):
    """This function returns the setting of any bit from a value.
    bitindex starts at 0.
    """
    return (value&(1<<bitindex))>>bitindex

def bitset(value, bitindex, bit):
    """Sets a bit, specified by bitindex, in 'value' to 'bit'.
    bit should be 1 or 0
    bitindex starts at 0.
    """
    bit    = (bit&1L)<<bitindex
    mask     = (1L)<<bitindex
    return (value & ~mask) | bit                # set that bit of value to 0 with an & operation and then or it with the 'bit'

    

##################################################################

# private functions used by the above public functions

def internalfunc(data1, data2):
    """Used by binleave.
    This function interleaves data2 into data1 a little chunk at a time."""
    if len(data2) > len(data1):         # make sure that data1 always has the longer string, making data2 the watermark
        dummy = data1
        data1 = data2
        data2 = dummy
    if not data2 or not data1: return None      # check for empty data
    length = len(data2)
    if length >= pow(2,24): return None         # if the strings are oversized
    multiple = len(data1)//length       # this is how often we should interleave bits
    if multiple > 65535: multiple = 65535   # in practise we'll set to max 65535
    header1 = length//65536
    header3 = length % 65536
    header2 = header3//256
    header3 = header3 % 256
    header = chr(header1) + chr(header2) + chr(header3)      # these are the 3 bytes we will put at the start of the string
# so - to encode one byte of data2 (the watermark) we need multiple bytes of data1
    data1 = [ord(char) for char in list(data1)]
    startpos = 0
    data2 = [ord(char) for char in list(data2)]
    BINLIST=[1,2,4,8,16,32,64,128]
    out = []
    bitlen = multiple*8 + 8     # the total number of bits we'll have
#    print bitlen, multiple
    while data2:
        chunklist = data1[startpos:startpos + multiple]
        startpos = startpos + multiple
        heapobj = 0
        mainobj = data2.pop(0)
        charobj = chunklist.pop(0)
        bitindex = 0
        mainindex = 0
        heapindex = 0
        charindex = 0
        while mainindex < bitlen:
    #        print  mainindex, heapindex, charindex, bitindex
            if heapindex == 8:       #    if we've got all 8 bit's
                out.append(chr(heapobj))
                heapobj = 0
                heapindex = 0
            if not mainindex%(multiple+1):            # we've got to a point where we should nick another bit from the byte
                if mainobj&BINLIST[bitindex]:       # if the bit at binindex is set
                    heapobj = heapobj|BINLIST[heapindex] # set the bit at heapindex
                heapindex += 1
                bitindex += 1
                mainindex += 1
                continue
            if charindex == 7 and chunklist:                   # we've used up the current character from the chunk
                if charobj&BINLIST[charindex]:
                    heapobj = heapobj|BINLIST[heapindex]
                charobj = chunklist.pop(0)
                charindex = 0
                heapindex += 1
                mainindex += 1 
                continue
            if charobj&BINLIST[charindex]:
                heapobj = heapobj|BINLIST[heapindex]
            heapindex += 1
            charindex += 1
            mainindex += 1
            
        if heapindex == 8:       #    if we've got all 8 bit's.. but the loop has ended...
            out.append(chr(heapobj))

    return header, ''.join(out), ''.join([chr(char) for char in data1[startpos:]])

def internalfunc2(data):
    """Used by binunleave.
    This function extracts data that has been interleaved using binleave."""
    lenstr = data[:3]           # extract the length of the watermark
    data = list(data[3:])
    length2 = ord(lenstr[0])*65536 + ord(lenstr[1])*256 + ord(lenstr[2])       # length of watermark
    length1 = len(data) - length2                                               # overall length
    multiple = length1//length2 + 1
    if multiple > 65536: multiple = 65536   # in practise we'll set to max 65535 + 1
    bitlen = multiple*8
#    print len(data), length1, length2, multiple
    out1 = []
    out = []
    index = 0
    BINLIST=[1,2,4,8,16,32,64,128]
#    print len(chunk)    
    while index < length2:
        index += 1
        chunk = data[:multiple]
        data = data[multiple:]
        chunklist = [ord(char) for char in chunk]       # turn chunk into a list of it's values
        heapobj = 0
        outbyte = 0
        charobj = chunklist.pop(0)
        bitindex = 0
        mainindex = 0
        heapindex = 0
        charindex = 0
        while mainindex < bitlen:
    #        print  mainindex, heapindex, charindex, bitindex
            if heapindex == 8:       #    if we've got all 8 bit's
                out.append(chr(heapobj))
                heapobj = 0
                heapindex = 0    
            if not mainindex%multiple:            # we've got to a point where we should add another bit to the byte
                if charobj&BINLIST[charindex]:
                    outbyte = outbyte|BINLIST[bitindex]
                if not charindex == 7:
                    charindex += 1
                else:
                    charobj = chunklist.pop(0)
                    charindex = 0                
                bitindex += 1
                mainindex += 1
                continue
            if charindex == 7 and chunklist:                   # we've used up the current character from the chunk
                if charobj&BINLIST[charindex]:
                    heapobj = heapobj|BINLIST[heapindex]
                charobj = chunklist.pop(0)
                charindex = 0
                heapindex += 1
                mainindex += 1 
                continue
            if charobj&BINLIST[charindex]:
                heapobj = heapobj|BINLIST[heapindex]
            heapindex += 1
            charindex += 1
            mainindex += 1
        if heapindex == 8:       #    if we've got all 8 bit's.. but the loop has ended...
            out.append(chr(heapobj))
        out1.append(chr(outbyte))

    return ''.join(out1), ''.join(out+data)

def test():                     # the test suite
    from time import clock
    from os.path import exists
    print 'Printing the TABLE : '
    index = 0
    while index < len(TABLE):
        print TABLE[index], TABLE.find(TABLE[index])
        index +=1

    print '\nEnter test password to encode using table_enc :\n(Hit enter to continue past this)\n'
    while True:
        dummy = raw_input('>>...')
        if not dummy: break
        test =  table_enc(dummy)
        test2 = table_dec(test)
        print test
        print 'length  : ', len(test), '    modulo 4 of length - 1  : ', (len(test)-1) % 4
        print 'Decoded : ', test2
        print 'Length dec : ', len(test2)

    print '\nEnter password - to timestamp and then encode :\n(Hit enter to continue past this)\n'
    while True:
        instring = raw_input('>>...')
        if not instring:
            break
        code = pass_enc(instring, sha_hash=False, daynumber=True, timestamp=True)
        print code
        print pass_dec(code)


    print '\n\nTesting interleaving a 1000 byte random string with a 1500 byte random string :'
    print
    print 'Overall length of combined string : ',
    a=0
    b=''
    c = ''
    while a < 1000:
        a += 1
        b = b + chr(int(random()*256))
        c = c + chr(int(random()*256))
    while a < 1500:
        a += 1
        c = c + chr(int(random()*256))
    d = clock()
    test = binleave(c, b, True)
    print  len(test)
    a1, a2 = binunleave(test)
    print 'Time taken (including print statements ;-) ', str(clock()-d)[:6], ' seconds'
    print 'Test for equality of extracted data against original :'
    print a1 == b
    print a2 == c


# If you give it two test files 'test1.zip' and 'test2.zip' it will interleave the two files,
# unleave them again and write out the first file as 'test4.zip'
# It prints how long it takes and you can verify that the returned file is undamaged.
    
    if exists('test1.zip') and exists('test2.zip'):
        print
        print "Reading 'test1.zip' and 'test2.zip'"
        print "Interleaving them together and writing the combined file out as 'test3.zip'"
        print "Then unleaving them and writing 'test1.zip' back out as 'test4.zip'",
        print " to confirm it is unchanged by the process"
        a = file('test1.zip','rb')
        b = a.read()
        a.close()
        a = file('test2.zip','rb')
        c  = a.read()
        a.close()
        d = clock()
        test = binleave(c,b, True)

        print len(test)
        a = file('test3.zip','wb')
        a.write(test)
        a.close()
        a1, a2 = binunleave(test)
        print str(clock()-d)[:6]
        a = file('test4.zip','wb')
        a.write(a1)
        a.close()
    else:
        print
        print 'Unable to perform final test.'
        print "We need two files to use for the test : 'test1.zip' and 'test2.zip'"
        print "We then interleave them together, and write the combined file out as 'test3.zip'"
        print "Then we unleave them again, and write 'test1.zip' back out as 'test4.zip'",
        print "(So we can confirm that it's unchanged by the process.)"

    



if __name__ == '__main__':

# the start of making dataenc an application - but I don't think it will be used :-)
# just runs the test suite instead

### this is executed if dataenc is run from the commandline
##
### first we get the arguments we were called with using optparse
##
### minimum arguments :
### input file
### output file
##
### default :
### if three file arguments are given the two are interelaved and saved as the third file
### so long as the third file doesn't already exist.
##
### if two filenames are given it reads the first file and datestamps it
### saves as the second file (assuming it doesn't exist)
##
### if one filename is given it assumes it is a n interleaved file to extract
##
### options :
### overwrite output file             - default OFF
### encode or decode                  - default is encode (specifying three files forces encode)
### table_enc on or off               - default is OFF
### specify a TABLE file              - default is to use inbuilt
### datestamp/interleave on or off    - default is ON (datestamping)
### endleaving on or off              - default is OFF
### header file                       - default is to use the header in the file when decoding, and to leave it in the file when encoding
### (If a header file is specified the 3 byte header from binary interleaving will be saved seperately).
##
### **special**
### config file - *all* values are read from the config file
##
##    from optparse import OptionParser
##
##    parser = OptionParser()
##    parser.add_option("-q", "--quiet",
##                      action="store_false", dest="quiet", default = False,
##                      help="Set a verbosity level of 0, print no messages.")
##    
##    parser.add_option("--test",
##                      action="store_true", dest="test", default=False,
##                      help="Run the tests, all other options ignored. Verbosity of tests is 9.")
##    
##    parser.add_option("-v", "--verbose", type = 'int', dest="verbose", default=9,
##                      help="Set the verbosity level. Should be an integer from 0 to 9,"+\
##                      "9 means the most verbose and 0 means don't ouput any messages. Default is 9.")
##    
##    parser.add_option("-d", "--decode",
##                      action="store_true", dest="decode", default = False,
##                      help="Set to decode rather than encode. Default is encode.")
##    
##    parser.add_option("-t", "--table",
##                      action="store_true", dest="table", default = False,
##                      help="Encode or decode files using the TABLE. (ASCII  to binary or binary to ASCII).")
##    
##    parser.add_option("-T","--TABLE", dest="table_file", 
##                  help="Specify a 64 character file to use as the TABLE for encoding/decoding.")
##    
##    parser.add_option("-o", "--off",
##                      action="store_false", dest="datestamp", default = True,
##                      help="Switches datestamping OFF. Default is ON.")
##
##    parser.add_option("-e", "--end",
##                      action="store_false", dest="end", default = True,
##                      help="Switches endleaving ON. default is OFF.")
##    
##    parser.add_option("-H","--header", dest="header_file", default = False,
##                  help="Specify a separate file to use as the header file when binary encoding/decoding.")
##
##    parser.add_option("-c","--config", dest="config_file", default = False,
##                  help="Specify a config file to read *all* the other options from.")
##
##
##
##    options, args = parser.parse_args()
###    print args
##
##
### next import StandOut which allows us to set variable levels of verbosity
##    try:
##        from standout import StandOut
##        stout = StandOut()
##    except:
##        print 'dataenc uses the standout module to handle varying levels of verbosity'
##        print 'Without it, all messages will be printed.'
##        class dummy:                # a dummy object that we can twiddle if StandOut isn't available
##            def __init__(self):
##                self.priority = 0
##                self.verbosity = 0
##            def close(self):
##                pass
##        stout = dummy()
##    
##    defaults = { 'header_file' : False, 'datestamp' : True
##    if options.config_file:          # a configfile, the settings here override all the others
##        try:
##            from configobj import ConfigObj
##        except ImportError:
##            print "Without the ConfigObj module I can't import a config file."
##            print 'See http://www.voidspace.org.uk/atlantibots/pythonutils.html'
##            raise
##        config = ConfigObj(options.config_file, fileerror=True)
##        
##
##    if options.verbose:
##        stout.verbosity = 10 - options.verbose          # a higher verbosity level here, actually means quiter
##    else:
##        stout.verbosity = 0                             # except for 0, which means silent
##    if options.quiet:                                   # if the quiet option is explicitly set
##        stout.verbosity = 0
##    stout.priority = 2
##    print 'Welcome to dataenc - the data encoding and interleaving program by Fuzzyman'
##    print 'See http://www.voidspace.org.uk/atlantibots/pythonutils.html'
##    print 'Written in Python.'
##    stout.priority = 3
##    if not psycoin:
##        print 'Having the Psyco module installed (Python Specialising compiler) would vastly speed up dataenc.'
##    if not DATEIN:
##        print 'Some of the datestamping features are only available when the dateutils module is available.'
##    stout.priority = 5
##
##        
##    if options.test:
    test()


    



"""

BUGS
No more known bugs... yet.
I'm sure they'll surface.

ISSUES
binleave and bin_unleave are still quite slow.
For stamping small password hashes with a date stamp it's fast enough - for weaving larger files together it's *too slow*.
Also for weaving similar sized files together we may be better with a pattern of 2 bits of water mark per 3 bits of string.
(or a 3 to 4 or 5 to 7 etc..)
Currrently it will only work with 1 bit of watermark per 1 or 2 or 3 or 4 etc bits of main string. (Exact multiples)
Again, for small watermarks this works fine - and as that is all I'm using it for I'm not inclined to change it.
The logic would be simple - just fiddly.


TODO :
Might make it a simple application - so it can be used from the command line for encoding, decoding
timestamping and combining files.....
Could replace use of the BINLIST and the if test with a single inline statement with more << >> in binleave and binunleave
Could move the binleave and binuleave into C


CHANGELOG
13-09-04        Version 1.1.5
Increased speed in table_enc and table_dec.

30-08-04        Version 1.1.4
Slight docs improvement.
Slight speed improvement in binleave and binunleave.

22-08-04        Version 1.1.3
Added the unexpired alias and the check_pass function.
Changed license text.
Minor preemptive bugfix in some default values.

11-04-04        Version 1.1.2
Added the expired function for testing validity of timestamps.
Changed the TABLE to be URL safe for passing in forms using the 'GET' method.
Added OLD_TABLE with the old encoding, and gave table_dec and table_enc the ability to receive an explicit TABLE.

07-04-04        Version 1.1.1
Improved the tests a bit.
Corrected a bug that affected large files or large files with small watermarks.

05-04-04        Version 1.1.0
Replaced the bf object with much faster bitwise logical operations. It is now about 2.5 times faster.
With Psyco enabled it becomes 11 times faster than the first version....
Added the bit setting and testing operations as functions.

03-04-04        Version 1.0.0
Initial testing is a success.


"""
# 01-02-04
#v1.0.2

#
# Date Utils
# By Fuzzyman see www.voidspace.org.uk/atlantibots/pythonutils.html
# Written for the Victory Day program for Jesus Fellowship Church 
# www.jesus.org.uk

# These are various functions for dealing with dates (including leap years and so on)
# Useful especially for situations where you have to arrange appointments.
# (e.g. second Tuesday of the month etc...)

# None of these functions are designed to handle BC dates.........
# They will also only work with dates from the Gregorian (modern) calender.
# They usually assume that given dates are *possible* dates.
# (Although there is a function to explicitly check a date).

# Help and inspiration was taken from :
# http://users.aol.com/s6sj7gt/mikecal.htm and 
# http://mathforum.org/library/drmath/view/62338.html

# If you have any bug reports or suggestions please contact me.
# If you would like to be notified of bug fixes / updates then please contact me.

# E-mail fuzzyman AT atlantibots DOT org DOT uk (or michael AT foord DOT me DOT uk )
# Code maintained at http://www.voidspace.org.uk/atlantibots/pythonutils.html

# Copyright Michael Foord
# Not for use in commercial projects without permission.
# If you use them in a non-commercial project then please credit me and include a link back.
# If you release the project non-commercially then let me know (and include this message with my code !)

# No warranty express or implied for the accuracy, fitness to purpose or otherwise for this code....
# Use at your own risk !!!

from time import localtime

##############################

# First set up some useful values

monthslower = [ 'january', 'february', 'march', 'april', 'may', 'june', 'july',
          'august', 'september', 'october', 'november', 'december' ]

dayslower =[ 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday',
        'saturday' ]

monthdict = { 'january' : 31, 'february' : 28, 'march' : 31, 'april' : 30, 'may' : 31,
              'june' : 30, 'july' : 31, 'august' : 31, 'september' : 30, 'october' : 31,
              'november' : 30, 'december' : 31 }

monthdictleap = { 'january' : 31, 'february' : 29, 'march' : 31, 'april' : 30, 'may' : 31,
              'june' : 30, 'july' : 31, 'august' : 31, 'september' : 30, 'october' : 31,
              'november' : 30, 'december' : 31 }

monthlist = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]

monthlistleap = [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]

days =[ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
        'Saturday' ]

months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July',
          'August', 'September', 'October', 'November', 'December' ]

#############################

# Next the functions

"""
There are various useful 'constants' defined in dateutils :

monthslower, dayslower = lowercase lists of the days and months
monthdict, monthdictleap = dictionaries keyed by month - value is the number of days in the month (monthdictleap is for a leap year)
monthlist, monthlistleap = a list of the number of days in the month (monthlistleap is for a leapyear)
days, months = capitalised lists of the days and months
dateformcon = a dictionary with the standard config settings for the formatted date function.

The Following functions are defined in dateutils :

(Some of the functions depend on each other - so it's better to import the ones you want rather than cut and paste :-)

realdate(day, month, year):
    Returns true if the supplied date is a possible date
    and False if it isn't :-) (Note - it only tests that the *year* is greater than zero).

isleapyear(year):
    Given a year as an integer (e.g. 2004) it returns True if the year is a leap year,
    and False if it isn't.

daysinmonth(year, month):
    Given a year and a month it returns how many days are in that month.
    
datetoday(day, month, year):
    Passed in a date, in integers, it returns the day of the week.
    Output is expressed as an integer from 0-6.
    0 is Sunday, 1 is Monday.......
    
datestringtoints(datestring):
    Passed in a datestring - in the form 'yyyymmdd'
    (e.g. 20040122 being 22nd January 2004) -
    it returns an integer tuple ( yyyy, mm, dd ).
    If the datestring is of the wrong length it returns None.
    (It assumes a four figure year).

intstodatestring(day, month, year):
    Given three integers for day, month and year
    it returns a datestring 'yyyymmdd' (for easy storage).
    
returndate():
    Returns the local date using the localtime function
    from the time module.
    Returns integers - ( yyyy, mm, dd ).

nearestday(day, month, year, dayofweek = 2, afteronly = 0):
    Given a date as three integers (year, month and day) it returns the nearest
    date that is 'dayofweek'. (dayofweek should be an integer from 0 - 6. 0 is Sunday, 1 Monday etc..)
    If afteronly is set to 1 then it finds the nearest date of that day, on or *after* the specified.
    Returns integers - ( yyyy, mm, dd ).
    dayofweek defaults to Tuesday (2) and afteronly defaults to 0 as they are the defaults I'm using  for the Victory Day program this is written for.
    This is used for : e.g find the nearest Tuesday to a given date, or find the nearest Tuesday *after* a given date !

addnumdays(day, month, year, modifier):
    Given a date as three integers (year, month and day) and a number of days to add or subtract
    to that date (the integer modifier, positive or negative value) - it returns the correct date
    as a tuple of integers - ( yyyy, mm, dd ).   

incdate(day, month, year):
    Given a date it adds one day to the date and returns the new date.

decdate(day, month, year):
    Given a date it subtracts one day from the date and returns the new date.

adddate(day1, month1, year1, day2, month2, year2):
    Given a date as three integers (year1, month1 and day1) and another number of days (day2), months (month2)
    and years (year2) to add to that date (or subtract from it) - it returns the new date as a tuple of integers - ( yyyy, mm, dd ).
    Note :
    Feb 28th + 1 month = March 31st
    Feb 29th  + 1 month = March 31st
    January 29th to 31st + 1 month = feb 28th/29th
    August 31st + 1 month = September 30th
    We add the years together, then the months, then correct for the 'end of month' (e.g. we change Sep 31st to Sep 30th)
    Finally we add any extra days on.    

daycount(year, month, day)
    This is an implementation of the Julian Day system. This 
    is a continuous count of days from January 1, 4713 B.C.
    Given a date in in integers it returns an integer value for the date
    This represents it's Julian Day number as above.
    This only works for dates represented using the the Gregorian
    calendar which was adopted in the US/UK on Oct. 15, 1582 - but
    at different times elsewhere (so historical dates may not be in this system....).

counttodate(daycount)
    Given the number for a date using the Julian Day System,
    it returns that date as integer tuple (year, month, day).

daysbetween(day1, month1, year1, day2, month2, year2)
    Given two dates it returns the number of days between them.
    If date1 is earlier than date2 then the result will be positive.

def dayfinish(day)
    Takes an integer day and returns the correct finish for it
    1 = 'st', 2 = 'nd', 3 = 'rd', 4-10 = 'th' etc....

def formatteddate(day, month, year, configdict = {}, **configs)
    Given a date in in integers, it returns the date as a nicely formatted string :
    e.g. 24th January 1997 or 2nd February 1948
    configs accepts the following keywords :
    dayofweek, addzero, addcom, fullstop, monthfirst
    e.g. print formatteddate(12, 8, 1974, dayofweek=1, addzero=0, addcom=1, fullstop=1, monthfirst=0)
         Monday 12th August, 1974.
    If dayofweek is set to 1 then the day of the week will also be printed :
    e.g. Monday 24th January 1997
    If addzero is set to 1 then days 1-9 will have an additional zero :
    e.g. 02nd February 1948
    If addcom is set to 1 then there will be a comma between the month and the year :
    e.g. 24th January, 1997
    If fullstop is set to 1 then there will be a fullstop after the year :
    e.g. 24th January 1997.
    If monthfirst is set to 1 then then the month will be put before the day :
    e.g. January 24th 1997
    If the year is set to zero then it will be missed off.
    (and the dayofweek will be treated as 0 in this case as well).
    There is a dictionary called dateformcon defined in the dateutils module with all the config values
    defined and some good  standard settings :-)
    This dictionary can be passed in instead of the individual settings.
    """

#############################


def realdate(day, month, year):
    """Returns true if the supplied date is a possible date
    and False if it isn't :-) (Note - it *only* tests that the year is greater than zero)."""
    if  month > 12 or year < 1 or day < 1 or month < 1:
        return False
    elif  month == 2:      # if it's february we need to know if it's a leap year
        if isleapyear(year):
            numdays = 29
        else:
            numdays = 28
    else:
        numdays = monthlist[ month-1 ]        # -1 because in the list January is 0
    if day > numdays:
        return False
    else:
        return True


def isleapyear(year):
    """Given a year as an integer (e.g. 2004) it returns True if the year is a leap year,
    and False if it isn't."""
    if year%4 != 0:
        return False
    elif year%100 !=0:
        return True
    elif year%400 == 0:
        return True
    else:
        return False


def daysinmonth(year, month):
    """Given a year and a month it returns how many days are in that month."""
    if month == 2:      # if it's february we need to know if it's a leap year
        if isleapyear(year):
            numdays = 29
        else:
            numdays = 28
    else:
        numdays = monthlist[ month-1 ]        # -1 because in the list January is 0
    return numdays


def datetoday(day, month, year):
    """Passed in a date, in integers, it returns the day of the week.
    Output is expressed as an integer from 0-6.
    0 is Sunday, 1 is Monday....... """
    # dayofweek would have been a better name for this function :-(
    d = day
    m = month
    y = year
    if m < 3:
        z = y-1
    else:
        z = y
    dayofweek = ( 23*m//9 + d + 4 + y + z//4 - z//100 + z//400 )
    if m >= 3:
        dayofweek -= 2
    dayofweek = dayofweek%7
    return dayofweek

def datestringtoints(datestring):
    """Passed in a datestring - in the form 'yyyymmdd'
    (e.g. 20040122 being 22nd January 2004) -
    it returns an integer tuple ( yyyy, mm, dd ).
    If the datestring is of the wrong length it returns None.
    (It assumes a four figure year)."""
    if len(datestring) != 8:        # badly formed datestring
        return None
    return (int(datestring[:4]), int(datestring[4:6]), int(datestring[6:8]))

def intstodatestring(day, month, year):
    """Given three integers for day, month and year
    it returns a datestring 'yyyymmdd' (for easy storage)."""
    y = str(year)
    while len(y) < 4:
        y = '0' + y
    m = str(month)
    d = str(day)
    if len(m) < 2:
        m = '0' + m
    if len(d) < 2:
        d = '0' + d
    return y+m+d


def returndate():
    """Returns the local date using the localtime function
    from the time module.
    Returns integers - ( yyyy, mm, dd )."""
    try:      # because this function doesn't work on some platforms
        datetuple = localtime()
    except:
        return (2004, 1, 31)
    return ( datetuple[0], datetuple[1], datetuple[2] )

def nearestday(day, month, year, dayofweek = 2, afteronly = 0):
    """Given a date as three integers (year, month and day) it returns the nearest
    date that is 'dayofweek'. (dayofweek should be an integer from 0 - 6. 0 is Sunday, 1 Monday etc..)
    If afteronly is set to 1 then it finds the nearest date of that day, on or *after* the specified.
    Returns integers - ( yyyy, mm, dd ).
    dayofweek defaults to Tuesday (2) and afteronly defaults to 0 as they are the defaults I'm using  for the Victory Day program this is written for.
    This is used for : e.g find the nearest Tuesday to a given date, or find the nearest Tuesday *after* a given date !"""
    thisday = datetoday(day, month, year)
    if thisday == dayofweek:
        return (year, month, day)
    
    if thisday < dayofweek:     # this 'if else test' tells us the number of days between the two days of the week
        forward = dayofweek - thisday
        backward = 7 - forward
    else:
        backward = thisday - dayofweek
        forward = 7 - backward
    if afteronly or forward < backward:
        difference = forward
    else:
        difference = -backward
        
    return addnumdays(day, month, year, difference)
        

def addnumdays(day, month, year, modifier):
    """Given a date as three integers (year, month and day) and a number of days to add or subtract
    to that date (the integer modifier, positive or negative value) - it returns the correct date
    as a tuple of integers - ( yyyy, mm, dd )."""
    if modifier > 0:    # damn - different rules for negative modifiers and hard to make generic
        if month == 2 and isleapyear(year) and day == 29:     # special case
            modifier -= 1
            month = 3
            day = 1
        while modifier >= 365:      # add any years on
            if month <= 2 and isleapyear(year) or month > 2  and isleapyear(year+1):
                numdays = 366
            else:
                numdays = 365            
            if modifier >= numdays:
                year += 1
                modifier -= numdays
            else:
                break
                
        while modifier >= 28:   #add any full months on     
            if month == 2:      # if it's february we need to know if it's a leap year
                if isleapyear(year):
                    numdays = 29
                else:
                    numdays = 28
            else:
                numdays = monthlist[ month-1 ]        # -1 because in the list January is 0
            if modifier >= numdays:
                modifier -= numdays
                if month != 12:
                    month += 1
                else:
                    month = 1
                    year += 1
            else:
                break
# now we need to correct if the new 'day' value is greater than the number of days in the new month...... 
            if month == 2:      # if it's february we need to know if it's a leap year
                if isleapyear(year):
                    numdays = 29
                else:
                    numdays = 28
            else:
                numdays = monthlist[ month-1 ]        # -1 because in the list January is 0
            if day > numdays:
                if month != 12:
                    month += 1
                else:
                    month = 1
                    year += 1
                day = day - numdays

        while modifier > 0:
            year, month, day = incdate(day, month, year)
            modifier -= 1

    elif modifier < 0:   # we have to subtract days
        modifier = -modifier        # easier to deal with positive numbers :-)
        if month == 2 and isleapyear(year) and day == 29:     # special case
            modifier -= 1
            day = 28
        while modifier >= 365:      # take any years off
            if month > 2 and isleapyear(year) or month <= 2 and isleapyear(year-1):
                numdays = 366
            else:
                numdays = 365            
            if modifier >= numdays:
                year -= 1
                modifier -= numdays
            else:
                break
            
        while modifier >= 28:   # subtract any full months on
            if month == 2:
                if isleapyear(year):
                    numdays = 29
                else:
                    numdays = 28
            else:
                numdays = monthlist[month-1]
            adjuster = numdays - day        # how many days before the end of the month is it
            if day > numdays:
                modifier -= numdays
                if month != 1:
                    month -=1
                else:
                    month = 12
                    year -= 1
                if month == 2:
                    if isleapyear(year):
                        numdays = 29
                    else:
                        numdays = 28
                else:
                    numdays = monthlist[month-1]
                day = numdays - adjuster        # if we've gone back a whole month it's now the smae numebr of days before the end of the month
            else:
                break
            
        while modifier > 0:
            year, month, day = decdate(day, month, year)
            modifier -= 1         

    return ( year, month, day )
    

def incdate(day, month, year):
    """Given a date it adds one day to the date and returns the new date."""
    if month == 2:      # if it's february we need to know if it's a leap year
        if isleapyear(year):
            numdays = 29
        else:
            numdays = 28
    else:
        numdays = monthlist[ month-1 ]        # -1 because in the list January is 0
    if day < numdays:
        day += 1
    else:       # of course, here day should equal numdays or the date is invalid :-)
        if month == 12:
            month = 1
            year +=1
            day = 1
        else:
            month += 1
            day = 1
    return ( year, month, day )

def decdate(day, month, year):
    """Given a date it subtracts one day from the date and returns the new date."""
    if day > 1:
        day -= 1
    elif month == 1:        # 1st January
        year -=1
        day = 31
        month = 12
    elif month == 3:        # 1st March
        if isleapyear(year):
            day = 29
        else:
            day = 28
        month = 2
    else:
        day = monthlist[ month-2 ]
        month -= 1
    return ( year, month, day )

def adddate(day1, month1, year1, day2, month2, year2):
    """Given a date as three integers (year1, month1 and day1) and another number of days (day2), months (month2)
    and years (year2) to add to that date (or subtract from it) - it returns the new date as a tuple of integers - ( yyyy, mm, dd ).
    Note :
    Feb 28th + 1 month = March 31st
    Feb 29th  + 1 month = March 31st
    January 29th to 31st + 1 month = feb 28th/29th
    August 31st + 1 month = September 30th
    We add the years together, then the months, then correct for the 'end of month' (e.g. we change Sep 31st to Sep 30th)
    Finally we add any extra days on."""
    year = year1 + year2
    month = month1 + month2
    while month < 1:
        year -= 1
        month += 12
    while month > 12:
        year += 1
        month -=12
    numdays = daysinmonth(year, month)
    if day1 > numdays:
        day1 = numdays
    if day2 < 0:
        day2 = -day2
        thisfunc = decdate
    else:
        thisfunc = incdate
    while day2 > 0:
        year, month, day1 = thisfunc(day1, month, year)
        day2 -= 1
    return year, month, day1

def daycount(year, month, day):
    """"This is an implementation of the Julian Day system. This 
    is a continuous count of days from January 1, 4713 B.C.
    Given a date in in integers it returns an integer value for the date
    This represents it's Julian Day number as above.
    This only works for dates represented using the the Gregorian
    calendar which was adopted in the US/UK on Oct. 15, 1582 - but
    at different times elsewhere (so historical dates may not be in this system....)."""
    if month < 3:
        year = year - 1
        month = month + 13
    else:
        month = month + 1
    A = int(year/100)
    B = 2 - A + int(A/4)
    return int(365.25*year) + int(30.6001*month) + B + day + 1720995

def counttodate(daycount):
    """Given the number for a date using the Julian Day System,
    it returns that date as integer tuple (year, month, day)."""
# note - slow and badly implemented... but fast enough :-)
    daycount = daycount - 2453030
    return addnumdays(25, 1, 2004, daycount)

def daysbetween(day1, month1, year1, day2, month2, year2):
    """Given two dates it returns the number of days between them.
    If date1 is earlier than date2 then the result will be positive."""
    return daycount(year2, month2, day2) - daycount(year1, month1, day1)

def dayfinish(day):
    """Takes an integer day and returns the correct finish for it
    1 = 'st', 2 = 'nd', 3 = 'rd', 4-10 = 'th' etc...."""
    if day > 3 and day < 21:
        return 'th'     # special cases
    daystr = str(day)
    if len(daystr) > 1:
        daystr = daystr[-1]
    if daystr == '1':
        return 'st'
    elif daystr == '2':
        return 'nd'
    elif daystr == '3':
        return 'rd'
    else:
        return 'th'

def formatteddate(day, month, year, configdict = {}, **configs):
    """Given a date in in integers, it returns the date as a nicely formatted string :
    e.g. 24th January 1997 or 2nd February 1948
    configs accepts the following keywords :
    dayofweek, addzero, addcom, fullstop, monthfirst
    e.g. print formatteddate(12, 8, 1974, dayofweek=1, addzero=0, addcom=1, fullstop=1, monthfirst=0)
         Monday 12th August, 1974.
    If dayofweek is set to 1 then the day of the week will also be printed :
    e.g. Monday 24th January 1997
    If addzero is set to 1 then days 1-9 will have an additional zero :
    e.g. 02nd February 1948
    If addcom is set to 1 then there will be a comma between the month and the year :
    e.g. 24th January, 1997
    If fullstop is set to 1 then there will be a fullstop after the year :
    e.g. 24th January 1997.
    If monthfirst is set to 1 then then the month will be put before the day :
    e.g. January 24th 1997
    If the year is set to zero then it will be missed off.
    (and the dayofweek will be treated as 0 in this case as well).
    There is a dictionary called dateformcon defined in the dateutils module with all the config values
    defined and some good  standard settings :-)
    This dictionary can be passed in instead of the individual settings.
    """
    keywordlist = ['dayofweek', 'addzero', 'addcom', 'fullstop', 'monthfirst']
    if configdict != {} and isinstance(configdict, dict):
        configs = configdict
    for member in keywordlist:
        if not configs.has_key(member):
            configs[member] = 0
    outstring = ''

    if configs['dayofweek'] and year:
        outstring = days[datetoday(day, month, year)] +' '
    if day < 10 and configs['addzero']:
        daystr = '0' + str(day)
    else:
        daystr = str(day)
    if not configs['monthfirst']:
        outstring += daystr + dayfinish(day) + ' ' + months[month-1]
    else:
        outstring += months[month-1] + ' ' + daystr + dayfinish(day) 
    if configs['addcom'] and year:
        outstring += ','
    if year:
        outstring += ' ' + str(year)
    if configs['fullstop']:
        outstring += '.'
    return outstring
    

dateformcon = { 'dayofweek' : 1, 'addzero' : 0, 'addcom' : 1, 'fullstop' : 1, 'monthfirst' : 0 }

############################################################

if __name__ == "__main__":
    print returndate()
    year, month, day = returndate()
    test = daycount(year, month, day)
    print test
    print counttodate(test)
    while True:
        x = raw_input("Enter Year of date (Enter to quit) >> ")
        if x=='':
            break
        y = raw_input("Enter Month >> ")
        z = raw_input("Enter Day >> ")
        test = daycount(int(x), int(y), int(z))
        print test
        print counttodate(test)


    
    print realdate(32, 1, 2004)
    while True:
        x = raw_input("Enter Modifier (0 to quit) >> ")
        if x=='0':
            break
        print addnumdays(31, 3, 2004, -int(x) )

    while True:
        x = raw_input("Enter Day of Week 0-6 (7 to quit) >> ")
        if x=='7':
            break
        print nearestday(24, 1, 2004, int(x))

    while True:
        x = raw_input("Enter Years to Add (Enter to quit) >> ")
        if x=='':
            break
        y = raw_input("Enter Months to Add >> ")
        z = raw_input("Enter Days To Add >> ")
        print adddate(24, 1, 2004, int(z), int(y), int(x))
        year, month , day = adddate(24, 1, 2004, int(z), int(y), int(x))
        print "The nearest Tuesday after that date is ", nearestday(day, month, year)


"""

Versionlog

01-02-04        Version 1.0.2
Corrected bug in intstodatestring.
Created lowercase day list and capitalised month list.
Added formatteddate and dayfinish function.
Put a try: except: catch in returndate - mainly so I can test on the pocketpc.

"""
#  2005/08/28
# v1.4.0
# listquote.py

# Lists 'n' Quotes
# Handling lists and quoted strings
# Can be used for parsing/creating lists - or lines in a CSV file
# And also quoting or unquoting elements.

# Homepage : http://www.voidspace.org.uk/python/modules.shtml

# Copyright Michael Foord, 2004 & 2005.
# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# For information about bugfixes, updates and support, please join the Pythonutils mailing list.
# http://groups.google.com/group/pythonutils/
# Comments, suggestions and bug reports welcome.
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# E-mail fuzzyman@voidspace.org.uk

"""
Having written modules to handle turning a string representation of a list back 
into a list (including nested lists) and also a very simple CSV parser, I 
realised I needed a more solid set of functions for handling lists (comma 
delimited lines) and quoting/unquoting elements of lists.

The test stuff provides useful examples of how the functions work.
"""

# Pre-2.3 workaround for basestring.
try:
    basestring
except NameError:
    basestring = (str, unicode)

import re
inquotes = re.compile(r'''\s*(".*?"|'.*?')(.*)''')
badchars = re.compile(r'''^[^'," \[\]\(\)#]+$''')
##commented_line = re.compile(r'''\s*([^#]*)\s*(#.*)''')
paramfinder = re.compile(r'''(?:'.*?')|(?:".*?")|(?:[^'",\s][^,]*)''')
unquoted = re.compile(r'''
    ([^\#,"'\(\)\[\]][^\#,\]\)]*)  # value
    \s*                         # whitespace - XXX not caught
    ([\#,\)\]].*)?                  # rest of the line
    $''', re.VERBOSE)

__all__ = [
    'elem_quote',
    'unquote',
    'ListQuoteError',
    'QuoteError',
    'UnQuoteError',
    'BadLineError',
    'CommentError',
    'quote_escape',
    'quote_unescape',
    'simplelist',
    'LineParser',
    'lineparse',
    'csvread',
    'csvwrite',
    'list_stringify',
    'makelist'
    ]

class ListQuoteError(SyntaxError):
    """Base class for errors raised by the listquote module."""

class QuoteError(ListQuoteError):
    """This value can't be quoted."""

class UnQuoteError(ListQuoteError):
    """The value is badly quoted."""

class BadLineError(ListQuoteError):
    """A line is badly built."""

class CommentError(BadLineError):
    """A line contains a disallowed comment."""

class CSVError(ListQuoteError):
    """The CSV File contained errors."""

#################################################################
# functions for quoting and unquoting

def elem_quote(member, nonquote=True, stringify=False, encoding=None):
    """
    Simple method to add the most appropriate quote to an element - either single 
    quotes or double quotes.
    
    If member contains ``\n`` a ``QuoteError`` is raised - multiline values 
    can't be quoted by elem_quote.
    
    If ``nonquote`` is set to ``True`` (the default), then if member contains none 
    of ``'," []()#;`` then it isn't quoted at all.
    
    If member contains both single quotes *and* double quotes then all double 
    quotes (``"``) will be escaped as ``&mjf-quot;`` and member will then be quoted 
    with double quotes.
    
    If ``stringify`` is set to ``True`` (the default is ``False``) then non string 
    (unicode or byte-string) values will be first converted to strings using the 
    ``str`` function. Otherwise elem_quote raises a ``TypeError``.
    
    If ``encoding`` is not ``None`` and member is a byte string, then it will be 
    decoded into unicode using this encoding.
    
    >>> elem_quote('hello')
    'hello'
    >>> elem_quote('hello', nonquote=False)
    '"hello"'
    >>> elem_quote('"hello"')
    '\\'"hello"\\''
    >>> elem_quote(3)
    Traceback (most recent call last):
    TypeError: Can only quote strings. "3"
    >>> elem_quote(3, stringify=True)
    '3'
    >>> elem_quote('hello', encoding='ascii')
    u'hello'
    >>> elem_quote('\\n')
    Traceback (most recent call last):
    QuoteError: Multiline values can't be quoted.
    "
    "
    """
    if not isinstance(member, basestring):
        if stringify:
            member = str(member)
        else:
            # FIXME: is this the appropriate error message ?
            raise TypeError('Can only quote strings. "%s"' % str(member))
    if encoding and isinstance(member, str):
        # from string to unicode
        member = unicode(member, encoding)
    if '\n' in member:
        raise QuoteError('Multiline values can\'t be quoted.\n"%s"' % str(member))
    #
    if nonquote and badchars.match(member) is not None:
        return member
    # this ordering of tests determines which quote character will be used in 
    # preference - here we have \" first...
    elif member.find('"') == -1:
        return '"%s"' % member
    # but we will use either... which may not suit some people
    elif member.find("'") == -1:
        return "'%s'" % member
    else:
        raise QuoteError('Value can\'t be quoted : "%s"' % member)

def unquote(inline, fullquote=True, retain=False): 
    """
    Unquote a value.
    
    If the value isn't quoted it returns the value.
    
    If the value is badly quoted it raises ``UnQuoteError``.
    
    If retain is ``True`` (default is ``False``) then the quotes are left 
    around the value (but leading or trailing whitespace will have been 
    removed).
    
    If fullquote is ``False`` (default is ``True``) then unquote will only 
    unquote the first part of the ``inline``. If there is anything after the 
    quoted element, this will be returned as well (instead of raising an 
    error).
    
    In this case the return value is ``(value, rest)``.
    
    >>> unquote('hello')
    'hello'
    >>> unquote('"hello"')
    'hello'
    >>> unquote('"hello')
    Traceback (most recent call last):
    UnQuoteError: Value is badly quoted: ""hello"
    >>> unquote('"hello" fish')
    Traceback (most recent call last):
    UnQuoteError: Value is badly quoted: ""hello" fish"
    >>> unquote("'hello'", retain=True)
    "'hello'"
    >>> unquote('"hello" fish', fullquote=False)
    ('hello', ' fish')
    """
    mat = inquotes.match(inline)
    if mat is None:
        if inline.strip()[0] not in '\'\"': # not quoted
            return inline
        else:
            # badly quoted
            raise UnQuoteError('Value is badly quoted: "%s"' % inline) 
    quoted, rest = mat.groups()
    if fullquote and rest.strip():
        # badly quoted
        raise UnQuoteError('Value is badly quoted: "%s"' % inline)
    if not retain:
        quoted = quoted[1:-1]
    if not fullquote:
        return quoted, rest
    else:
        return quoted

def quote_escape(value, lf='&mjf-lf;', quot='&mjf-quot;'):
    """
    Escape a string so that it can safely be quoted. You should use this if the 
    value to be quoted *may* contain line-feeds or both single quotes and double 
    quotes.
    
    If the value contains ``\n`` then it will be escaped using ``lf``. By 
    default this is ``&mjf-lf;``.
    
    If the value contains single quotes *and* double quotes, then all double 
    quotes will be escaped using ``quot``. By default this is ``&mjf-quot;``.
    
    >>> quote_escape('hello')
    'hello'
    >>> quote_escape('hello\\n')
    'hello&mjf-lf;'
    >>> quote_escape('hello"')
    'hello"'
    >>> quote_escape('hello"\\'')
    "hello&mjf-quot;'"
    >>> quote_escape('hello"\\'\\n', '&fish;', '&wobble;')
    "hello&wobble;'&fish;"
    """
    if '\n' in value:
        value = value.replace('\n', lf)
    if '\'' in value and '\"' in value:
        value = value.replace('"', quot)
    return value

def quote_unescape(value, lf='&mjf-lf;', quot='&mjf-quot;'):
    """
    Unescape a string escaped by ``quote_escape``.
    
    If it was escaped using anything other than the defaults for ``lf`` and 
    ``quot`` you must pass them to this function.
    
    >>> quote_unescape("hello&wobble;'&fish;",  '&fish;', '&wobble;')
    'hello"\\'\\n'
    >>> quote_unescape('hello')
    'hello'
    >>> quote_unescape('hello&mjf-lf;')
    'hello\\n'
    >>> quote_unescape("'hello'")
    "'hello'"
    >>> quote_unescape('hello"')
    'hello"'
    >>> quote_unescape("hello&mjf-quot;'")
    'hello"\\''
    >>> quote_unescape("hello&wobble;'&fish;",  '&fish;', '&wobble;')
    'hello"\\'\\n'
    """
    return value.replace(lf, '\n').replace(quot, '"')

def simplelist(inline):
    """
    Parse a string to a list.
        
    A simple regex that extracts quoted items from a list.
    
    It retains quotes around elements. (So unquote each element)
    
    >>> simplelist('''hello, goodbye, 'title', "name", "I can't"''')
    ['hello', 'goodbye', "'title'", '"name"', '"I can\\'t"']
    
    FIXME:  This doesn't work fully (allows some badly formed lists):
    e.g.
    >>> simplelist('hello, fish, "wobble" bottom hooray')
    ['hello', 'fish', '"wobble"', 'bottom hooray']
    """
    return paramfinder.findall(inline)

##############################################
# LineParser - a multi purpose line parser
# handles lines with comma seperated values on it, followed by a comment
# correctly handles quoting
# *and* can handle nested lists - marked between '[...]' or '(...)'
# See the docstring for how this works 
# by default it returns a (list, comment) tuple !
# There are several keyword arguments that control how LineParser works.

class LineParser(object):
    """An object to parse nested lists from strings."""
    
    liststart = { '[' : ']', '(' : ')' }
    quotes = ['\'', '"']
    
    def __init__(self, options=None, **keywargs):
        """Initialise the LineParser."""
        self.reset(options, **keywargs)
    
    def reset(self, options=None, **keywargs):
        """Reset the parser with the specified options."""
        if options is None:
            options = {}
        options.update(keywargs)
        #
        defaults = { 
                    'recursive': True,
                    'comment': True,
                    'retain': False,
                    'force_list': False,
                    'csv': False
                    }
        defaults.update(options)
        if defaults['csv']:
            defaults.update({
                        'recursive': False, 
                        'force_list': True, 
                        'comment': False,
                        })
        # check all the options are valid
        for entry in defaults.keys():
            if entry not in ['comment',
                            'retain',
                            'csv',
                            'recursive',
                            'force_list']:
                raise TypeError, ("'%s' is an invalid keyword argument for "
                                    "this function" % entry)
        #
        self.recursive = defaults['recursive']
        self.comment = defaults['comment']
        self.retain = defaults['retain']
        self.force_list = defaults['force_list']

    def feed(self, inline, endchar=None):
        """
        Parse a single line (or fragment).
        
        Uses the options set in the parser object.
        
        Can parse lists - including nested lists. (If ``recursive`` is 
        ``False`` then nested lists will cause a ``BadLineError``).
        
        Return value depends on options.
        
        If ``comment`` is ``False`` it returns ``outvalue``
        
        If ``comment`` is ``True`` it returns ``(outvalue, comment)``. (Even if
        comment is just ``''``).
        
        If ``force_list`` is ``False`` then ``outvalue`` may be a list or a
        single item.
        
        If ``force_list`` is ``True`` then ``outvalue`` will always be a list -
        even if it has just one member.
        
        List syntax :
        
        * Comma separated lines ``a, b, c, d``
        * Lists can optionally be between square or ordinary brackets
            - ``[a, b, c, d]``
            - ``(a, b, c, d)``
        * Nested lists *must* be between brackets -  ``a, [a, b, c, d], c``
        * A single element list can be shown by a trailing quote - ``a,``
        * An empty list is shown by ``()`` or ``[]``
        
        Elements can be quoted with single or double quotes (but can't contain
        both).
        
        The line can optionally end with a comment (preeded by a '#').
        This depends on the ``comment`` attribute.
        
        If the line is badly built then this method will raise one of : ::
        
            CommentError, BadLineError, UnQuoteError
        
        Using the ``csv`` option is the same as setting : ::
        
                        'recursive': False
                        'force_list': True
                        'comment': False
        """
        # preserve the original line
        # for error messages
        if endchar is None:
            self.origline = inline
        inline = inline.lstrip()
        #
        outlist = []
        comma_needed = False
        found_comma = False
        while inline:
            # NOTE: this sort of operation would be quicker
            # with lists - but then can't use regexes
            thischar = inline[0]
            if thischar == '#':
                # reached a comment
                # end of the line...
                break
            #
            if thischar == endchar:
                return outlist, inline[1:]
            #
            if comma_needed:
                if thischar == ',':
                    inline = inline[1:].lstrip()
                    comma_needed = False
                    found_comma = True
                    continue
                raise BadLineError('Line is badly built :\n%s' % self.origline)
            #
            try:
                # the character that marks the end of the list
                listend = self.liststart[thischar]
            except KeyError:
                pass
            else:
                if not self.recursive and endchar is not None:
                    raise BadLineError('Line is badly built :\n%s' % self.origline)
                newlist, inline = self.feed(inline[1:], endchar=listend)
                outlist.append(newlist)
                inline = inline.lstrip()
                comma_needed = True
                continue
            #
            if thischar in self.quotes:
                # this might raise an error
                # FIXME: trap the error and raise a more appropriate one ?
                element, inline = unquote(inline, fullquote=False, 
                                                    retain=self.retain)
                inline = inline.lstrip()
                outlist.append(element)
                comma_needed = True
                continue
            #
            # must be an unquoted element
            mat = unquoted.match(inline)
            if mat is not None:
                # FIXME: if the regex was better we wouldn't need an rstrip
                element = mat.group(1).rstrip()
                # group 2 will be ``None`` if we reach the end of the line
                inline = mat.group(2) or ''
                outlist.append(element)
                comma_needed = True
                continue
            # or it's a badly built line
            raise BadLineError('Line is badly built :\n%s' % self.origline)
        #
        # if we've been called recursively
        # we shouldn't have got this far
        if endchar is not None:
            raise BadLineError('Line is badly built :\n%s' % self.origline)
        #
        if not found_comma:
            # if we didn't find a comma
            # the value could be a nested list
            if outlist:
                outlist = outlist[0]
            else:
                outlist = ''
        if self.force_list and not isinstance(outlist, list):
            if outlist:
                outlist = [outlist]
            else:
                outlist = []
        if not self.comment:
            if inline:
                raise CommentError('Comment not allowed :\n%s' % self.origline)
            return outlist
        return outlist, inline

def lineparse(inline, options=None, **keywargs):
    """
    A compatibility function that mimics the old lineparse.
    
    Also more convenient for single line use.
    
    Note: It still uses the new ``LineParser`` - and so takes the same
    keyword arguments as that.
    
    >>> lineparse('''"hello", 'goodbye', "I can't do that", 'You "can" !' # a comment''')
    (['hello', 'goodbye', "I can't do that", 'You "can" !'], '# a comment')
    >>> lineparse('''"hello", 'goodbye', "I can't do that", 'You "can" !' # a comment''', comment=False)
    Traceback (most recent call last):
    CommentError: Comment not allowed :
    "hello", 'goodbye', "I can't do that", 'You "can" !' # a comment
    >>> lineparse('''"hello", 'goodbye', "I can't do that", 'You "can" !' # a comment''', recursive=False)
    (['hello', 'goodbye', "I can't do that", 'You "can" !'], '# a comment')
    >>> lineparse('''"hello", 'goodbye', "I can't do that", 'You "can" !' # a comment''', csv=True)
    Traceback (most recent call last):
    CommentError: Comment not allowed :
    "hello", 'goodbye', "I can't do that", 'You "can" !' # a comment
    >>> lineparse('''"hello", 'goodbye', "I can't do that", 'You "can" !' ''', comment=False)
    ['hello', 'goodbye', "I can't do that", 'You "can" !']
    >>> lineparse('')
    ('', '')
    >>> lineparse('', force_list=True)
    ([], '')
    >>> lineparse('[]')
    ([], '')
    >>> lineparse('()')
    ([], '')
    >>> lineparse('()', force_list=True)
    ([], '')
    >>> lineparse('1,')
    (['1'], '')
    >>> lineparse('"Yo"')
    ('Yo', '')
    >>> lineparse('"Yo"', force_list=True)
    (['Yo'], '')
    >>> lineparse('''h, i, j, (h, i, ['hello', "f"], [], ([]),), k''')
    (['h', 'i', 'j', ['h', 'i', ['hello', 'f'], [], [[]]], 'k'], '')
    >>> lineparse('''h, i, j, (h, i, ['hello', "f"], [], ([]),), k''', recursive=False)
    Traceback (most recent call last):
    BadLineError: Line is badly built :
    h, i, j, (h, i, ['hello', "f"], [], ([]),), k
    >>> lineparse('fish#dog')
    ('fish', '#dog')
    >>> lineparse('"fish"#dog')
    ('fish', '#dog')
    >>> lineparse('(((())))')
    ([[[[]]]], '')
    >>> lineparse('((((,))))')
    Traceback (most recent call last):
    BadLineError: Line is badly built :
    ((((,))))
    >>> lineparse('hi, ()')
    (['hi', []], '')
    >>> lineparse('"hello", "",')
    (['hello', ''], '')
    >>> lineparse('"hello", ,')
    Traceback (most recent call last):
    BadLineError: Line is badly built :
    "hello", ,
    >>> lineparse('"hello", ["hi", ""], ""')
    (['hello', ['hi', ''], ''], '')
    >>> lineparse('''"member 1", "member 2", ["nest 1", ("nest 2", 'nest 2b', ['nest 3', 'value'], nest 2c), nest1b]''')
    (['member 1', 'member 2', ['nest 1', ['nest 2', 'nest 2b', ['nest 3', 'value'], 'nest 2c'], 'nest1b']], '')
    >>> lineparse('''"member 1", "member 2", ["nest 1", ("nest 2", 'nest 2b', ['nest 3', 'value'], nest 2c), nest1b]]''')
    Traceback (most recent call last):
    BadLineError: Line is badly built :
    "member 1", "member 2", ["nest 1", ("nest 2", 'nest 2b', ['nest 3', 'value'], nest 2c), nest1b]]
    """
    p = LineParser(options, **keywargs)
    return p.feed(inline)

############################################################################
# a couple of functions to help build lists

def list_stringify(inlist):
    """
    Recursively rebuilds a list - making sure all the members are strings.
    
    Can take any iterable or a sequence as the argument and always
    returns a list.
    
    Useful before writing out lists.
    
    Used by makelist if stringify is set.
    
    Uses the ``str`` function for stringification.
    
    Every element will be a string or a unicode object.
    
    Doesn't handle decoding strings into unicode objects (or vice-versa).
    
    >>> list_stringify([2, 2, 2, 2, (3, 3, 2.9)])
    ['2', '2', '2', '2', ['3', '3', '2.9']]
    >>> list_stringify(None)
    Traceback (most recent call last):
    TypeError: iteration over non-sequence
    >>> list_stringify([])
    []
    
    FIXME: can receive any iterable - e.g. a sequence
    >>> list_stringify('')
    []
    >>> list_stringify('Hello There')
    ['H', 'e', 'l', 'l', 'o', ' ', 'T', 'h', 'e', 'r', 'e']
    """
    outlist = []
    for item in inlist:
        if not isinstance(item, (tuple, list)):
            if not isinstance(item, basestring):
                item = str(item)
        else:
            item = list_stringify(item)
        outlist.append(item)
    return outlist


def makelist(inlist, listchar='', stringify=False, escape=False, encoding=None):
    """
    Given a list - turn it into a string that represents that list. (Suitable
    for parsing by ``LineParser``).
    
    listchar should be ``'['``, ``'('`` or ``''``. This is the type of bracket
    used to enclose the list. (``''`` meaning no bracket of course).
    
    If you have nested lists and listchar is ``''``, makelist will
    automatically use ``'['`` for the nested lists.
    
    If stringify is ``True`` (default is ``False``) makelist will stringify the
    inlist first (using ``list_stringify``).
    
    If ``escape`` is ``True`` (default is ``False``) makelist will call
    ``quote_escape`` on each element before passing them to ``elem_quote`` to
    be quoted.
    
    If encoding keyword is not ``None``, all strings are decoded to unicode
    with the specified encoding. Each item will then be a unicode object
    instead of a string.
    
    >>> makelist([])
    '[]'
    >>> makelist(['a', 'b', 'I can\\'t do it', 'Yes you "can" !'])
    'a, b, "I can\\'t do it", \\'Yes you "can" !\\''
    >>> makelist([3, 4, 5, [6, 7, 8]], stringify=True)
    '3, 4, 5, [6, 7, 8]'
    >>> makelist([3, 4, 5, [6, 7, 8]])
    Traceback (most recent call last):
    TypeError: Can only quote strings. "3"
    >>> makelist(['a', 'b', 'c', ('d', 'e'), ('f', 'g')], listchar='(')
    '(a, b, c, (d, e), (f, g))'
    >>> makelist(['hi\\n', 'Quote "heck\\''], escape=True)
    'hi&mjf-lf;, "Quote &mjf-quot;heck\\'"'
    >>> makelist(['a', 'b', 'c', ('d', 'e'), ('f', 'g')], encoding='UTF8')
    u'a, b, c, [d, e], [f, g]'
    """
    if stringify:
        inlist = list_stringify(inlist)
    listdict = {'[' : '[%s]', '(' : '(%s)', '' : '%s'}
    outline = []
    # this makes '[' the default for empty or single value lists
    if len(inlist) < 2:
        listchar = listchar or '['
    for item in inlist:
        if not isinstance(item, (list, tuple)):
            if escape:
                item = quote_escape(item)
            outline.append(elem_quote(item, encoding=encoding))
        else:
            # recursive for nested lists
            outline.append(makelist(item, listchar or '[', 
                                        stringify, escape, encoding))
    return listdict[listchar] % (', '.join(outline))

############################################################################
# CSV functions
# csvread, csvwrite

def csvread(infile):
    """
    Given an infile as an iterable, return the CSV as a list of lists.
    
    infile can be an open file object or a list of lines.
    
    If any of the lines are badly built then a ``CSVError`` will be raised.
    This has a ``csv`` attribute - which is a reference to the parsed CSV.
    Every line that couldn't be parsed will have ``[]`` for it's entry.
    
    The error *also* has an ``errors`` attribute. This is a list of all the
    errors raised. Error in this will have an ``index`` attribute, which is the
    line number, and a ``line`` attribute - which is the actual line that
    caused the error.
    
    Example of usage :
    
    .. raw:: html
    
        {+coloring}
        
        handle = open(filename)
        # remove the trailing '\n' from each line
        the_file = [line.rstrip('\n') for line in handle.readlines()]
        csv = csvread(the_file)
        
        {-coloring}
    
    >>> a = '''"object 1", 'object 2', object 3
    ...     test 1 , "test 2" ,'test 3'
    ...     'obj 1',obj 2,"obj 3"'''
    >>> csvread(a.splitlines())
    [['object 1', 'object 2', 'object 3'], ['test 1', 'test 2', 'test 3'], ['obj 1', 'obj 2', 'obj 3']]
    >>> csvread(['object 1,'])
    [['object 1']]
    >>> try:
    ...     csvread(['object 1, "hello', 'object 1, # a comment in a csv ?'])
    ... except CSVError, e:
    ...     for entry in e.errors:
    ...         print entry.index, entry
    0 Value is badly quoted: ""hello"
    1 Comment not allowed :
    object 1, # a comment in a csv ?
    """
    out_csv = []
    errors = []
    index = -1
    p = LineParser(csv=True)
    for line in infile:
        index += 1
        try:
            values = p.feed(line)
        except ListQuoteError, e:
            values = []
            e.line = line
            e.index = index
            errors.append(e)
        #
        out_csv.append(values)
    #
    if errors:
        e = CSVError("Parsing CSV failed. See 'errors' attribute.")
        e.csv = out_csv
        e.errors = errors
        raise e
    return out_csv

def csvwrite(inlist, stringify=False):
    """
    Given a list of lists it turns each entry into a line in a CSV.
    (Given a list of lists it returns a list of strings).
    
    The lines will *not* be ``\n`` terminated.
    
    Set stringify to ``True`` (default is ``False``) to convert entries to
    strings before creating the line.
    
    If stringify is ``False`` then any non string value will raise a
    ``TypeError``.
    
    Every member will be quoted using ``elem_quote``, but no escaping is done.
    
    Example of usage :
    
    .. raw:: html
    
        {+coloring}
        
        # escape each entry in each line (optional)
        for index in range(len(the_list)):
            the_list[index] = [quote_escape(val) for val in the_list[index]]
        #
        the_file = csvwrite(the_list)
        # add a '\n' to each line - ready to write to file
        the_file = [line + '\n' for line in the_file]
        
        {-coloring}
    
    >>> csvwrite([['object 1', 'object 2', 'object 3'], ['test 1', 'test 2', 'test 3'], ['obj 1', 'obj 2', 'obj 3']])
    ['"object 1", "object 2", "object 3"', '"test 1", "test 2", "test 3"', '"obj 1", "obj 2", "obj 3"']
    >>> csvwrite([[3, 3, 3]])
    Traceback (most recent call last):
    TypeError: Can only quote strings. "3"
    >>> csvwrite([[3, 3, 3]], True)
    ['3, 3, 3']
    """
    out_list = []
    for entry in inlist:
        if stringify:
            new_entry = []
            for val in entry:
                if not isinstance(val, basestring):
                    val = str(val)
                new_entry.append(val)
            entry = new_entry
        this_line = ', '.join([elem_quote(val) for val in entry])
        out_list.append(this_line)
    return out_list

############################################################################

def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()


"""
ISSUES/TODO
===========

Fix bug in simplelist

Triple quote multiline values ?

Doesn't allow Python style string escaping (but has '&mjf-quot;' and '&mjf-lf;').

Uses both \' and \" as quotes and sometimes doesn't quote at all - see 
elem_quote - may not *always* be compatible with other programs.

Allow space seperated lists ? e.g. 10 5 100 20

Lineparser could create tuples.

Allow ',' as an empty list ?

CHANGELOG
=========

2005/08/28 - Version 1.4.0
--------------------------

* Greater use of regular expressions for added speed
* Re-implemented ``lineparse`` as the ``LineParser`` object
* Added doctests
* Custom exceptions
* Changed the behaviour of ``csvread`` and ``csvwrite``
* Removed the CSV ``compare`` function and the ``uncomment`` function
* Only ``'#'`` allowed for comments
* ``elem_quote`` raises exceptions
* Changed behaviour of ``unquote``
* Added ``quote_escape`` and ``quote_unescape``
* Removed the ``uni_conv`` option in the CSV functions

.. note::
    
    These changes are quite extensive. If any of them cause you problems then 
    let me know. I can provide a workaround in the next release.

2005/06/01          Version 1.3.0
Fixed bug in lineparse handling of empty list members.
    Thnks to bug report and fix by Par Pandit <ppandit@yahoo.com>
The 'unquote' function is now regex based.
    (bugfix it now doesn't return a tuple if fullquote is 0)
Added the simplelist regex/function.
elem_quote and uncomment use a regex for clarity and speed.
Added a bunch of asserts to the tests.

2005/03/07          Version 1.2.1
makelist improved - better handling of empty or single member lists

2005/02/23          Version 1.2.0
Added uncomment for ConfigObj 3.3.0
Optimised unquote - not a character by character search any more.
lineparse does full '&mjf..;' escape conversions - even when unquote isn't used
makelist and elem_quote takes an 'encoding' keyword for string members to be used to decode strigns to unicode
optimised makelist (including a minor bugfix)
Change to lineparse - it wouldn't allow '[' or '(' inside elements unless they were quoted.

2004/12/04          Version 1.1.2
Changed the license (*again* - now OSI compatible).
Empty values are now quoted by elem_quote.

30-08-04            Version 1.1.1
Removed the unicode hammer in csvread.
Improved docs.

16-08-04            Version 1.1.0
Added handling for non-string elements in elem_quote (optional).
Replaced some old += with lists and ''.join() for speed improvements...
Using basestring and hasattr('__getitem__') tests instead of isinstance(list) and str in a couple of places.
Changed license text.
Made the tests useful.

19-06-04            Version 1.0.0
Seems to work ok. A worthy successor to listparse and csv_s - although not as elegant as it could be.

"""
# __init__.py
# A CGI Authentication and user account system
# Copyright (C) 2004/2005 Michael Foord
# E-mail: fuzzyman AT voidspace DOT org DOT uk

# logintools

# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# Comments, suggestions and bug reports welcome.

from login import *

__all__ = (
    'login',
    'isloggedin',
    'logout',
    'createuser',
    'checklogin',
    'displaylogin'
    )

__version__ = '0.6.2'

"""
CHANGELOG
=========

2005/09/09
----------

Changed module name to logintools.

Changed license to the BSD license.

Code cleanup - added ``__version__``.

"""
# admin.py
# part of logintools
# A CGI Authentication and user account system
# Copyright (C) 2004/2005 Michael Foord
# E-mail: fuzzyman AT voidspace DOT org DOT uk

# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/documents/BSD-LICENSE.txt

# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# Comments, suggestions and bug reports welcome.

# For information about bugfixes, updates and support, please join the Pythonutils mailing list.
# http://voidspace.org.uk/mailman/listinfo/pythonutils_voidspace.org.uk
# Comments, suggestions and bug reports welcome.

import sys
import os
sys.path.append('../modules')
sys.path.append('../../lib')
sys.path.insert(0, 'lib')

from database import SQL
from configobj import ConfigObj
from pathutils import *
from cgiutils import *
from loginutils import *

###################################################

def cSort(inlist, minisort=True):
    """A case insensitive sort. If minisort is True then elements of the list for which element1.lower() == element2.lower() will also be sorted.
    (See the examples/test stuff."""
    sortlist = []
    newlist = []
    sortdict = {}
    for entry in inlist:
        try:
            lentry = entry.lower()
        except AttributeError:
            sortlist.append(lentry)
        else:
            try:
                sortdict[lentry].append(entry)
            except KeyError:
                sortdict[lentry] = [entry]
                sortlist.append(lentry)

    sortlist.sort()
    for entry in sortlist:
        try:
            thislist = sortdict[entry]
            if minisort: thislist.sort()
            newlist = newlist + thislist
        except KeyError:
            newlist.append(entry)
    return newlist


####################################################
# Various default values etc

adminpage_file = 'admin_page.html'
adminmenu_file = 'admin_menu.txt'
adminconfig_file = 'admin_config.txt'       # the template used for the 'edit config' option.
admininvite_file = 'admin_invite.txt'       # the template to invite/create new users
adminuser_file = 'admin_eduser.txt'           # template for edit/delete user

MAXADMINLEV = 3                             # the maximum admin level it's possible for a user to have             
MINMAXAGE = 600                             # the minimum value for cookie max-age

pass_msg = '\nYour login name is "%s", your password is "%s".\nYou can change this once you have logged in.\n'
SCRIPTLOC = 'http://' + os.environ.get('HTTP_HOST', '')             # XXXX do we trust this in all cases ? (i.e. not always http - https)
numonpage = 1000                            # number of users shown on a page at a time

# table elements used to display the accounts in edit users
edit_table_s = '<table width="90%" cellspacing="15" bgcolor="#3377bb" class="table">'
table_e = '</table>'
elem_h = '<tr><td align="center"><table border="4" width="100%" bgcolor="#dddddd">'
elem_f = '</table></td></tr>'
form_s = '''<form method="post" action="%s"><input type="hidden" name="login" value="admin">
<input type="hidden" name="action" value="%s"><input type="hidden" name="admin" value="%s">
<input type="hidden" name="start" value="%s"><input type="hidden" name="username" value="%s">
'''
form_e = '</form>'

account_table = form_s + '''<tr>
<td align="center"><strong>Login Name : </strong></td><td align="center"><input type="text" name="loginname" value="%s"></td>
<td align="center"><strong>Real Name : </strong></td><td align="center"><input type="text" name="realname" value="%s"></td></tr><tr>
<td align="center"><strong>Email : </strong></td><td align="center"><input type="text" name="email" value="%s"></td>
<td align="center"><strong>Admin Level</strong></td><td align="center"><input type="text" name="adminlev" value="%s"></td></tr><tr>
<td align="center"><strong>New Password : </strong></td><td align="center"><input type="text" name="pass1"></td>
<td align="center"><strong>Cookie max-age : </strong></td><td align="center"><input type="text" name="maxage" value="%s"></td></tr><tr>
<td align="center"><strong>Confirm Password : </strong></td><td align="center"><input type="text" name="pass2"></td>
<td align="center"><strong>Editable : </strong></td><td align="center"><input type="checkbox" name="editable" %s ></td></tr><tr><td align="center">
<input type="reset"></td><td>&nbsp;</td><td>&nbsp;</td><td align="center"><input type="submit" value="Submit Changes"></td></tr><tr>''' + form_e + form_s + '''
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr><tr>
<td>&nbsp;</td><td align="center"><input type="checkbox" name="confirm">Confirm Delete</td><td align="center"><input type="submit" value="Delete User">
</td><td>&nbsp;</td></tr>''' + form_e 

####################################################

# main menu - offering
# edit config
# edit users (including delete)
# invite/create new users

# display edit config  (including values from the default user)                
# edit config

# display invite/create
# invite new users
# create new user file

# edit user                      - can't edit or delete yourself or the 'main admin' (saves having to change newcookie)
# choose a user to edit or delete

# display edit user for that account
# or delete user (confirm ?)

# edit user -
# change password
# rename (change login name - ?)
# change display name
# change email address


def admin(theform, userdir, thisscript, userconfig, action, newcookie):
    """Decide what admin action to perform. """
    adminaction = theform.getfirst('admin', '')
    if adminaction.startswith('editconfig'):
        editconfig(theform, userdir, thisscript, userconfig, action, newcookie)
    elif adminaction.startswith('invite'):
        invite(theform, userdir, thisscript, userconfig, action, newcookie)
    elif adminaction.startswith('edituser'):
        edituser(theform, userdir, thisscript, userconfig, action, newcookie)
    elif adminaction.startswith('doeditconfig'):
        doeditconfig(theform, userdir, thisscript, userconfig, action, newcookie)
    elif adminaction.startswith('doinvite'):
        doinvite(theform, userdir, thisscript, userconfig, action, newcookie)
    elif adminaction.startswith('edituser'):
        edituser(theform, userdir, thisscript, userconfig, action, newcookie)
    elif adminaction.startswith('doedituser'):
        doedituser(theform, userdir, thisscript, userconfig, action, newcookie)
    elif adminaction.startswith('deluser'):
        deluser(theform, userdir, thisscript, userconfig, action, newcookie)
        
    else:
        displaymenu(theform, userdir, thisscript, userconfig, action, newcookie)

def displaymenu(theform, userdir, thisscript, userconfig, action, newcookie):
    """Display the admin menu page."""
    config = ConfigObj(userdir + 'config.ini')
    templatedir = config['templatedir']
    
    adminpage = readfile(templatedir+adminpage_file)
    adminpage = adminpage.replace('**this script**', thisscript + '?action=' + action)
    
    url = '?login=admin&admin=%s&action=' + action 
    adminmenu = readfile(templatedir+adminmenu_file)
    adminmenu = adminmenu.replace('**edit config**', thisscript+url % 'editconfig')
    adminmenu = adminmenu.replace('**edit users**', thisscript+url % 'edituser')
    adminmenu = adminmenu.replace('**invite**', thisscript+url % 'invite')

    adminpage = adminpage.replace('**admin**', adminmenu)
    adminpage = adminpage.replace('**admin menu**', thisscript+'?login=admin'+'&action='+action)
        
    print newcookie
    print serverline
    print
    print adminpage.encode("utf-8")
    sys.exit()


def editconfig(theform, userdir, thisscript, userconfig, action, newcookie, msg=None, success=None):
    """Display the screen to edit the main config file.
    This includes the default user."""
    config = ConfigObj(userdir + 'config.ini')
    default = ConfigObj(userdir + 'default.ini')
    templatedir = config['templatedir']
    
    adminpage = readfile(templatedir+adminpage_file)
    adminpage = adminpage.replace('**this script**', thisscript + '?action=' + action)
    adminpage = adminpage.replace('**admin menu**', thisscript+'?login=admin'+'&action='+action)
# The values of this that are editable from config.ini are :
# newloginlink, adminmail, email_subject, email_message
#
# The values of this that are editable from default.ini are :
# max-age, editable

    if msg:
        adminpage = adminpage.replace('<br><!- message --> ', '<h2>'+msg+'</h2>')
    if msg and not success:
        loginlink = theform.getfirst('loginlink', '')
        if loginlink:
            loginlink = 'checked'
        adminmail = theform.getfirst('adminmail', '')
        emailsubj = theform.getfirst('emailsubject', '')
        emailmsg = theform.getfirst('emailmsg', '')
        maxage = theform.getfirst('maxage', '')
        editable = theform.getfirst('editable', '')
        if editable:
            editable = 'checked'
    else:
        loginlink = config['newloginlink'].lower()
        if loginlink == 'yes':
            loginlink = 'checked'
        else:
            loginlink = ''
        adminmail = config['adminmail']
        emailsubj = config['email_subject']
        emailmsg = config['email_message']
        maxage = default['max-age']
        editable = default['editable'].lower()
        if editable == 'yes':
            editable = 'checked'
        else:
            editable = ''
            
    configmenu = readfile(templatedir+adminconfig_file)
    configmenu = configmenu.replace('**loginlink**', loginlink)
    configmenu = configmenu.replace('**adminmail**', adminmail)
    configmenu = configmenu.replace('**email subject**', emailsubj)
    configmenu = configmenu.replace('**email message**',emailmsg)
    configmenu = configmenu.replace('**maxage**', maxage)
    configmenu = configmenu.replace('**editable**', editable)
    configmenu = configmenu.replace('**thisscript**', thisscript)
    configmenu = configmenu.replace('**action**', action)

    adminpage = adminpage.replace('**admin**', configmenu)
    print newcookie
    print serverline
    print
    print adminpage
    sys.exit()
    
def invite(theform, userdir, thisscript, userconfig, action, newcookie, msg=None, success=None):
    """Display the screen to create or invite a new user."""
    config = ConfigObj(userdir + 'config.ini')
    templatedir = config['templatedir']
    
    adminpage = readfile(templatedir+adminpage_file)
    adminpage = adminpage.replace('**this script**', thisscript + '?action=' + action)
    adminpage = adminpage.replace('**admin menu**', thisscript+'?login=admin'+'&action='+action)

# Values to be filled in are :
# **create1** and **create2** - the one switched on should be 'checked', the other should be ''

    if msg:
        adminpage = adminpage.replace('<br><!- message --> ', '<h2>'+msg+'</h2>')
    if msg and not success:
        create = theform.getfirst('create', '')
        if create == 'create':
            create1 = 'checked'
            create2 = ''
        else:
            create2 = 'checked'
            create1 = ''
        realname = theform.getfirst('realname', '')
        username = theform.getfirst('username', '')
        email = theform.getfirst('email', '')
        pass1 = theform.getfirst('pass1', '')
        pass2 = theform.getfirst('pass2', '')
        adminlev = theform.getfirst('adminlev', '')
    else:
        create2 = 'checked'
        create1 = ''
        realname = ''
        username = ''
        email = ''
        pass1 = randomstring(8)
        pass2 = pass1
        adminlev = '0'
        
    invitemenu = readfile(templatedir+admininvite_file)
    invitemenu = invitemenu.replace('**create1**', create1)
    invitemenu = invitemenu.replace('**create2**', create2)
    invitemenu = invitemenu.replace('**realname**', realname)
    invitemenu = invitemenu.replace('**username**', username)
    invitemenu = invitemenu.replace('**email**', email)
    invitemenu = invitemenu.replace('**pass1**', pass1)
    invitemenu = invitemenu.replace('**pass2**', pass2)
    invitemenu = invitemenu.replace('**adminlev**', adminlev)

    invitemenu = invitemenu.replace('**thisscript**', thisscript)
    invitemenu = invitemenu.replace('**action**', action)

    adminpage = adminpage.replace('**admin**', invitemenu)
    print newcookie
    print serverline
    print
    print adminpage
    sys.exit()
  
def edituser(theform, userdir, thisscript, userconfig, action, newcookie, msg=None, success=None):
    """Display the screen to edit or delete users.."""
    config = ConfigObj(userdir + 'config.ini')
    templatedir = config['templatedir']
    realadminlev = int(userconfig['admin'])
    
    adminpage = readfile(templatedir+adminpage_file)
    adminpage = adminpage.replace('**this script**', thisscript + '?action=' + action)
    adminpage = adminpage.replace('**admin menu**', thisscript+'?login=admin'+'&action='+action)
  
    userlist = [entry[:-4] for entry in os.listdir(userdir) if os.path.isfile(userdir+entry) and entry[:-4] not in RESERVEDNAMES ]
    mainadmin = config['adminuser']
    username = userconfig['username']
    if mainadmin in userlist:
        userlist.remove(mainadmin)
    if username in userlist:
        userlist.remove(username)
    userlist = cSort(userlist)
    
    start = int(theform.getfirst('start', '1'))
    length = len(userlist)
    if start*numonpage > length:
        start = length//numonpage + 1
    url = '<a href="' + thisscript + '?start=%s&login=admin&admin=edituser&action=' + action + '">%s</a>'
    indexline = '<div style="text-align:center;">%s</div>' % makeindexline(url, start, length, numonpage)

# need to be able to edit -
# username, realname, new password, confirm password, adminlev, email, max-age, editable
    index = (start-1)*numonpage + 1 
    last = min(length+1, index+numonpage)
    usermenu = indexline + '<br>' + edit_table_s
    while  index < last:                                # go through all the users
        thisuser = userlist[index-1]
        index += 1
        thisuserc = ConfigObj(userdir+thisuser+'.ini')
	
        #print "Content-Type: text/html\n" # blank line: end of headers
        #print userdir,thisuser,thisuserc,"admin" in thisuserc, "admin" in thisuserc.keys()
        if thisuserc=={}: continue
        adminlev = thisuserc.get('admin',0)
        if realadminlev <= int(adminlev):
            continue
        loginname = thisuser
        realname = thisuserc['realname']
        email = thisuserc['email']
        maxage = thisuserc['max-age']
        editable = ''
        if istrue(thisuserc['editable']):
            editable = 'checked'
        if theform.getfirst('username')==loginname and msg and not success:
            realname = theform.getfirst('realname', '')
            realname = theform.getfirst('realname', '')
            email = theform.getfirst('email', '')
            adminlev = theform.getfirst('adminlev', '')
            maxage = theform.getfirst('maxage', '')
            editable = theform.getfirst('editable', '')
            if editable:
                editable = 'checked'
            
        thevals = (thisscript, action, 'doedituser', start, loginname,
                   loginname, realname, email, adminlev, maxage, editable,
                   thisscript, action, 'deluser', start, loginname)
        
        usermenu += elem_h+str(index-1) + (account_table % thevals) + elem_f
        
        
        
        # kim's stuff!!!!!
        realname=realname.decode("utf-8")
        project=unicode(action.split("_")[-1].decode("utf-8"))
        sql=SQL(project)
        uid=sql.userid(thisuser, realname)
        #print "Content-Type: text/html\n" # blank line: end of headers
        #print "kim<br>", project,thisuser,uid
        # kim's stuff ends....
        
        
    usermenu += table_e + '<br>' + indexline

    eduserpage = readfile(templatedir+adminuser_file)
    eduserpage = eduserpage.replace('**account table**', usermenu)
    if msg:
        adminpage =  adminpage.replace('<br><!- message --> ', '<h2>%s</h2>' % msg) 

    adminpage = adminpage.replace('**admin**', eduserpage)
    print newcookie
    print serverline
    print
    print adminpage
    sys.exit()
    
##########################################################

def doeditconfig(theform, userdir, thisscript, userconfig, action, newcookie):
    """Receives the submission from the edit config page."""
    config = ConfigObj(userdir + 'config.ini')
    default = ConfigObj(userdir + 'default.ini')

    loginlink = theform.getfirst('loginlink', '')
    adminmail = theform.getfirst('adminmail', '')
    emailsubj = theform.getfirst('emailsubject', '')
    emailmsg = theform.getfirst('emailmsg', '')
    maxage = theform.getfirst('maxage', '')
    editable = theform.getfirst('editable', '')

    if adminmail and not validemail(adminmail):
        editconfig(theform, userdir, thisscript, userconfig, action, newcookie, "The adminmail doesn't appear to be a valid address.")
    if not maxage.isdigit():
        editconfig(theform, userdir, thisscript, userconfig, action, newcookie, "maxage must be a number.")
    if int(maxage) and int(maxage) < MINMAXAGE:
        editconfig(theform, userdir, thisscript, userconfig, action, newcookie, "maxage must be greater than %s." % MINMAXAGE)

    if loginlink:
        config['newloginlink'] = 'Yes'
    else:
        config['newloginlink'] = 'No'
    config['adminmail'] = adminmail
    config['email_subject'] = emailsubj
    config['email_message'] = emailmsg
    config.write()
    default['max-age'] = maxage
    if editable:
        default['editable'] = 'Yes'
    else:
        default['editable'] = 'No'
    default.write()

    displaymenu(theform, userdir, thisscript, userconfig, action, newcookie)            # XXXX should we send a msg here 'COnfig File Edited' ?
    
#####################################################################

def doinvite(theform, userdir, thisscript, userconfig, action, newcookie):
    """Receives the submission from the invite/create new user page."""
    config = ConfigObj(userdir + 'config.ini')
    default = ConfigObj(userdir + 'default.ini')

    create = theform.getfirst('create', '')
    realname = theform.getfirst('realname', '')
    username = theform.getfirst('username', '')
    email = validemail(theform.getfirst('email', ''))
    pass1 = theform.getfirst('pass1', '')
    pass2 = theform.getfirst('pass2', '')
    adminlev = theform.getfirst('adminlev', '')
    
    maxadminlev = min(int(userconfig['admin']), MAXADMINLEV)
    
    if not email:
        invite(theform, userdir, thisscript, userconfig, action, newcookie, 'The email address appears to be invalid.')
    if pass1 != pass2:
        invite(theform, userdir, thisscript, userconfig, action, newcookie, "The two passwords don't match.")
    if len(pass1) < 5:    
        invite(theform, userdir, thisscript, userconfig, action, newcookie, "The password must be at least five characters long.")
    if not realname:
        invite(theform, userdir, thisscript, userconfig, action, newcookie, 'You must supply a realname.')
    if not username:
        invite(theform, userdir, thisscript, userconfig, action, newcookie, 'You must supply a username.')
    if not adminlev.isdigit():
        invite(theform, userdir, thisscript, userconfig, action, newcookie, 'Admin level must be a number.')
    if int(adminlev) > maxadminlev:
        invite(theform, userdir, thisscript, userconfig, action, newcookie, 'Admin level is greater than the maximum allowed (%s).' % maxadminlev)

# now we need to check if the username already exists
    tempstore = ConfigObj(userdir + 'temp.ini')
    pendinglist = tempstore.get('pending', [])
    if os.path.isfile(userdir+username+'.ini') or username in pendinglist or username.lower() in RESERVEDNAMES:
        invite(theform, userdir, thisscript, userconfig, action, newcookie, 'username already exists.')
    for char in username.lower():
        if not char in validchars:
            invite(theform, userdir, thisscript, userconfig, action, newcookie, 'username contains invalid characters.')
# now we have verified the values - we *either* need to create a new username *or* send an invitiation
    
    if create == 'create':
        createuser(userdir, realname, username, email, pass1, adminlev)
        msg = 'New User Created'
    else:
        inviteuser(userdir, realname, username, email, pass1, thisscript, adminlev)
        msg = 'New User Invited'
    invite(theform, userdir, thisscript, userconfig, action, newcookie, msg, True)
    
    
####################################################################

def doedituser(theform, userdir, thisscript, userconfig, action, newcookie):
    """Receives form submissions from the 'edit user' page."""
# parameters to get :
# username, realname, email, adminlev, pass1, pass2
    username = theform.getfirst('username')         # the user we are editing
    loginname = theform.getfirst('loginname')       # the new user name (won't usually change I guess)
    realname = theform.getfirst('realname')
    email = theform.getfirst('email')
    adminlev = theform.getfirst('adminlev')
    pass1 = theform.getfirst('pass1')
    pass2 = theform.getfirst('pass2')
    maxage = theform.getfirst('maxage')
    editable = theform.getfirst('editable')
    
    maxadminlev = min(int(userconfig['admin']), MAXADMINLEV)

# check all the account values
# this could be turned into a generic 'account checker' function if we wanted.
    email = validemail(email)
    if not email:
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'The Email Address Appears to Be Invalid.')
    if not loginname:
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'You Must Supply a Login Name.')
    for char in loginname.lower():
        if not char in validchars:
            edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Login Name Contains Invalid Characters')
    if not realname:
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'You Must Supply a Real Name')
    if (pass1 or pass2) and not (pass1 and pass2):
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'To Change the Password - Enter it Twice')
    if pass1 != pass2:
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'The Two Passwords Are Different')
    if pass1 and len(pass1) < 5:
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Password Must Be at Least Five Characters')
    if not adminlev.isdigit():
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'The Admin Level Must Be a Number')
    if int(adminlev) > maxadminlev:
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Admin Level is Higher than the Max (%s).' % maxadminlev)
    if not maxage.isdigit():
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Cookie "max-age" Must Be a Number')
    if int(maxage) and int(maxage) < MINMAXAGE:
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Cookie "max-age" Must Be Greater Than %s' % MINMAXAGE)
    if editable:
        editable = 'Yes'
    else:
        editable = 'No'
    # let's just check if the username has changed
    thisuser = ConfigObj(userdir+username+'.ini') 
    if loginname != username:
        pendinglist = ConfigObj(userdir + 'temp.ini').get('pending', [])
        if os.path.isfile(userdir+loginname+'.ini') or loginname in pendinglist or loginname.lower() in RESERVEDNAMES:
            edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Login Name Chosen Already Exists')    
        thisuser.filename = userdir+loginname+'.ini'                # change to new name
        os.remove(userdir+username+'.ini')                          # free up the old name
    if pass1:
        from dataenc import pass_enc
        thisuser['password'] = pass_enc(pass1, daynumber=True, timestamp=True)
    #
    thisuser['realname'] = realname
    thisuser['email'] = email 
    thisuser['admin'] = adminlev 
    thisuser['max-age'] = maxage 
    thisuser['editable'] = editable
    thisuser.write()
    
#    edituser(theform, userdir, thisscript, userconfig, action, newcookie, '')
    edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Changes Made Successfully', True)

def deluser(theform, userdir, thisscript, userconfig, action, newcookie):
    """Receives form submissions from when 'delete user' is hit."""
# parameters to get :
# username, realname, email, adminlev, pass1, pass2
    username = theform.getfirst('username')
    confirm = theform.getfirst('confirm')
    if not confirm:
        edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Confirm Was Not Selected - Delete Not Done', True)
# XXXX we ought to check that the user being deleted isn't the main admin user and hasn't got a higher admin level
    os.remove(userdir+username+'.ini')      # is it really that easy    
    edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Delete Done Successfully', True)
    
###############################################
# createuser is in loginutils - but this uses a couple of values defined in this module

def inviteuser(userdir, realname, username, email, password, thisscript, adminlev):
    """Invite a new user."""
    from newlogin import savedetails
    from configobj import ConfigObj
    formdict = {'username' : username, 'pass1' : password, 'admin' : adminlev,
                'realname' : realname, 'email' : email, 'action' : '' }
    thekey = savedetails(userdir, formdict)
    config = ConfigObj(userdir + 'config.ini') 
    msg = config['email_message'] + '\n'
    msg = msg + SCRIPTLOC + thisscript + '?login=confirm&id=' + thekey + (pass_msg % (username, password))
    writefile('log.txt', msg)
    sendmailme(email, msg, config['email_subject'], email, html=False)

"""
CHANGELOG
=========

2005/10/30
----------

Fixed the email function... oops.

2005/09/16
----------

Removed dependency on caseless module (added ``cSort``).

2005/09/09
----------

Changes to work with pythonutils 0.2.1

"""
# -*- coding: utf-8 -*-
# login.py
# part of logintools
# A CGI Authentication and user account system
# Copyright (C) 2004/2005 Michael Foord
# E-mail: fuzzyman AT voidspace DOT org DOT uk

# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/documents/BSD-LICENSE.txt

# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# Comments, suggestions and bug reports welcome.

# For information about bugfixes, updates and support, please join the Pythonutils mailing list.
# http://voidspace.org.uk/mailman/listinfo/pythonutils_voidspace.org.uk
# Comments, suggestions and bug reports welcome.

import sys
import cgi
import os
#import sha
import hashlib
from time import time

# FIXME: cgitb is insecure and should be removed from production code.
import cgitb
cgitb.enable()


sys.path.append('../modules')
sys.path.insert(0, 'lib')

from configobj import ConfigObj
from dataenc import pass_enc, pass_dec, unexpired, table_enc, table_dec
from Cookie import SimpleCookie
from pathutils import *
from cgiutils import *
from loginutils import *

# a default max age of 15 days. This ensures encoded cookies don't have
# unlimited lifetimes.
AGETEST = (15,0,0)
#THISSCRIPT = os.environ['SCRIPT_NAME']
THISSCRIPT = os.environ.get('SCRIPT_NAME', '')
####################################################
# The names for the various template files used by login_utils
# These must be stored in templatedir
# Which must be given in 'config.ini' in userdir

logintemplate = 'login_page.html'
form_nojs = 'login_nojs.txt'

#####################################################

"""
Cookie string format is : ::

    username||password SHA||random string``

which is dataenc'ed with the date-time the cookie was generated.

The cookie string must successfully de-enc and the username - pass hash match
for the cookie to be a valid login. The password is hashed along with the
random string, so that the hash is different every time.

.. warning::

    We don't log failed login attempts. We could return ``None`` for no cookie
    and ``False`` for a failed login or 'bad' cookie.

The function called is usually determined by user choices. E.g.

    * Login
    * perform admin functions
    * create new login
    
The actual action is saved in the form field 'login'.

The function login determines which function to call. This means most scripts
only need directly import and call this function.

If there is no 'login' form field then the login function assumes that you
want to check if the user already has a valid login. This is the 'checklogin'
function.

Many operations of these scripts will generate new pages asking for further
intervention from the user. For example - login generates a login page which
waits for the user to enter a password. This means if the action interrupts a
script (e.g. before performing an action the script must verify user identity)
information about what the script was about to do may be lost. The login
function allows you to pass in an 'action' parameter. This must be a string
escaped to be URL safe. You can use this value to store information about what
was being done before the interruption. (A simple way of saving state).
"""
######################################################################
# login is the function most frequently imported
# and called directly by external scripts

def login(theform, userdir, thisscript=None, action=None):
    #print 'Content-type: text/plain\n\nxxxxxxxxxxxxxxxxxxxx',"error in projects:",type(action),action.encode("utf-8")
    """From the form decide which function to call."""
    # FIXME: this function got a bit more complicated than intended
    # it also handles checking logins when editaccount or admin
    # functions are being called.
    br = False
    loginaction = ''
    if thisscript is None:
        thisscript = THISSCRIPT
    #
    # used so that we can tell if we have a genuinely empty action
    # or not later on....
    action = sortaction(theform.getfirst('action') or action)
    #
    # let's work out what we're supposed - if nothing is specified we'll just
    # check if we have a valid login and then return
    try:
        loginaction = theform['login'].value
    except KeyError:
        # this function displays login and exits if there is no valid login
        action, userconfig, newcookie = checklogin(userdir, thisscript, action)
        # break - means go straight to the end
        br = True
    #
    # dologin is special
    # it can be called by functions here - they may require a user to login before continuing.
    # So if we are returning from a login - we may need to go onto another function
    if loginaction.startswith('login'):
        action, userconfig, newcookie = dologin(theform, userdir, thisscript,
                                                    action)       # this function displays login and exits if username doesn't exist or password doesn't match
# if this function returns, then the login attempt was succesful
        userconfig['numlogins'] = str(int(userconfig['numlogins']) + 1)     # increment the number of logins in the user config
        userconfig.write()
        if action[:11] == 'edacc-mjf||':
            loginaction = 'editaccount'
        elif action[:11] == 'admin-mjf||':
            loginaction = 'admin'
        else:
            br = True
        
# this is the list of possible actions
# they must either call sys.exit() or return action, userconfig, newcookie
# functions which can be called without an explicit checklogin must do their own first (to get the new cookie header) - e.g. admin and edit account (otherwise you could access them without being logged in)
# if we have already done all this, then br will be set to True
    if br:
        pass
    elif loginaction == 'showlogin':
        displaylogin(userdir, thisscript)
    elif loginaction == 'logout':
# this means a logout link can be implemented as myscript.py?login=logout
        print logout(userdir)   # this prints the empty cookie
        displaylogin(userdir, thisscript)       # we don't use 'action' in this case because user has explicitly logged out, outstanding action is lost.
    elif loginaction == 'newlogin':
        from newlogin import newlogin
        newlogin(userdir, thisscript, action)           # this script always exits - so no return, preserves action though if needed
    elif loginaction.startswith('donewlogin'):
        from newlogin import donewlogin
        donewlogin(theform, userdir, thisscript, action)           # this script always exits - so no return, preserves action though if needed
    elif loginaction == 'confirm':
        from newlogin import confirm
        action, userconfig, newcookie = confirm(theform, userdir, thisscript)           # no action sent in, because the action is stored in the temporary user store
        userconfig['created'] = str(time())
    elif loginaction.startswith('editaccount'):
        from newlogin import editaccount
        if action[:11] != 'edacc-mjf||':
            action, userconfig, newcookie = checklogin(userdir, thisscript, 'edacc-mjf||' + action)
        action = action[11:]
        if not istrue(userconfig['editable']):
            error('Sorry, this account cannot be edited.')
        editaccount(userdir, thisscript, userconfig, sortaction(action), newcookie)           # we have checked that we have a valid login. This script prints a page, so no return
    elif loginaction.startswith('doeditaccount'):
        from newlogin import doeditaccount
        action, userconfig, newcookie = checklogin(userdir, thisscript, action)
        if not istrue(userconfig['editable']):
            error('Sorry, this account cannot be edited.')
        action, userconfig, newcookie = doeditaccount(theform, userconfig, userdir, thisscript, sortaction(action), newcookie)           # we have checked that we have a valid login
    elif loginaction == 'admin':
        from admin import admin
        if action[:11] != 'admin-mjf||':
            action, userconfig, newcookie = checklogin(userdir, thisscript, 'admin-mjf||' + action)
        action = action[11:]
        adminlevel = int(userconfig['admin'])
        if not adminlevel:
            error("You're not authorised to do that. Sorry.")
        action, userconfig, newcookie = admin(theform, userdir, thisscript, userconfig, sortaction(action), newcookie)           # we have checked that we have a valid login. 

    else:
        displaylogin(userdir, thisscript, action) # we haven't understood - just display the login screen XXXX error message instead ?          
    
    print newcookie     # XXXX ought to be a way of returning the cookie object instead...
    if action == 'EMPTY_VAL_MJF':           # the programmer ought never to 'see' this value, although it might get passed to his script by the login code a few times....
        action = None
    userconfig['lastused'] = str(time())
    userconfig['numused'] = str(int(userconfig['numused']) + 1)
    userconfig.write()
    return action, userconfig
    
############################################################
# Action functions - check a login etc

def checklogin(userdir, thisscript=None, action=None):
    """Check if a user has a valid cookie and return an updated one
    or display the default login screen and exit."""
    if thisscript == None:
        thisscript = THISSCRIPT 
    test = isloggedin(userdir)
    if test:
        return action, test[0], test[1]
    else:
        displaylogin(userdir, thisscript, action)

def dologin(theform, userdir, thisscript=None, action=None):
    """Ths is the function called by login when the results of a login form are posted.
    I.e. this actually does the login
    If any errors occur (like wrong/missing password) it calls displaylogin and exits.
    """
    loginaction = theform['login'].value                # used when we have js and nojs versions of dologin
    theform = getform(['username', 'pass'], theform)
    if thisscript == None:
        thisscript = THISSCRIPT 
    if loginaction == 'loginnojs':
        username = theform['username']
        password  = theform['pass']
        test = checkpass(username, password, userdir, thisscript, action)
        return test or displaylogin(userdir, thisscript, action)
    displaylogin(userdir, thisscript, action)                       # XXXX should display error message
    
def displaylogin(userdir, thisscript=None, action=None):
    """This function will display the login page and then exit.
    Usually called if the user has no cookie or an expired/forged cookie.
    """
    if thisscript == None:
        thisscript = THISSCRIPT
    config = ConfigObj(userdir + 'config.ini')
    templatedir = config['templatedir']

    loginform = readfile(templatedir+form_nojs)
    loginform = loginform.replace('**script**', thisscript)

    loginpage = readfile(templatedir+logintemplate)  
    loginpage = loginpage.replace('**login form**', loginform)

    if istrue(config['newloginlink']):
        loginpage = loginpage.replace('<!-- **commstart**', '')
        loginpage = loginpage.replace('**commend** -->', '')
        loginpage = loginpage.replace('**new login link**', thisscript+'?login=newlogin')

    if action:
        loginpage = loginpage.replace('<!-- **action** -->', actionline % action)
    print serverline
    print '\r'
    try:print loginpage
    except: print loginpage.encode("utf-8")
    
    sys.exit()
    
def logout(userdir):
    """Returns the cookie header in the case of an explicit logout."""
    config = ConfigObj(userdir + 'config.ini')
    cookiepath =  config['cookiepath']
    #print "ppppp",cookiepath
    return emptycookie(cookiepath)
    
############################################################################

def isloggedin(userdir):
    """If user has sent us an in date, valid cookie then return updated cookie header,
    otherwise return False."""
    try:
        rawcookie = os.environ['HTTP_COOKIE']
    except KeyError:
        return False
    thecookie = SimpleCookie(rawcookie)
    try:
        cookiestring = thecookie['userid'].value
    except KeyError:
        return False
    test = decodestring(cookiestring, userdir)
    if not test:
        return False
    user, password, cookiepath = test
    thecookie = makecookie(user, password, cookiepath)
    return user, thecookie
    
def decodestring(cookiestring, userdir):
    """Given a username/password encoded into a string - decode it and check it's validity.
    It checks the username against the one stored in the user file..
    """
# try decoding the string, if it's badly formed then it may raise an excpetion - in which case we just return False
    try:
        instring, daynumber, timestamp = pass_dec(cookiestring)
    except:
        return False
# check it's not a really old (or copied) cookie
    if not unexpired(daynumber, timestamp, AGETEST):
        return False
# we've extracted the timestamped string from the cookie string.
# Let's pull out the username and password hash
    try:
        username, passhash, ranstring = instring.split('||')
    except ValueError:
        return False
    if not len(ranstring) == 10:
        return False
# Now we need to check it's a valid username and check the password
    if username in RESERVEDNAMES or not os.path.isfile(userdir+username+'.ini'):
        return False
    user = ConfigObj(userdir+username+'.ini')
    stampedpass = user['password']
    maxage = user['max-age']
    cookiepath = ConfigObj(userdir+'config.ini')['cookiepath']
# the password is time stamped - so we need to decode it 
    try:
        password, daynumber, timestamp = pass_dec(stampedpass)
    except:
        return False
    thishash = hashlib.sha1(password+ranstring).hexdigest()
    if thishash != passhash:
        return False
    return user, password, cookiepath

def encodestring(username, password):
    """Given a username and password return a new encoded string for use by decodecookie."""  
    ranstring = randomstring(10)
    thishash = hashlib.sha1(password + ranstring).hexdigest()
    return pass_enc('||'.join([username, thishash, ranstring]), daynumber=True, timestamp=True)

def checkpass(username, password, userdir, thisscript, action):
    """Check the password from a new login."""
# XXXX log failed login attempts
    if username in RESERVEDNAMES:
        return False
    if not os.path.isfile(userdir+username+'.ini'):
        return False
    user = ConfigObj(userdir+username+'.ini')
    stampedpass = user['password']
    cookiepath = ConfigObj(userdir+'config.ini')['cookiepath']
# we need to un-time stamp the password
    realpass, daynumber, timestamp = pass_dec(stampedpass)
    if realpass != password:
        return False

    #open('xxxtest.txt', 'w').write(str(user))
# if we've got this far then the login was successful and we need to return a cookie
    thecookie = makecookie(user, password, cookiepath)
    return action, user, thecookie

"""
CHANGELOG
=========

2005/09/09
----------

Changes to work with pythonutils 0.2.2

"""
# loginutils.py
# part of logintools
# A CGI Authentication and user account system
# Copyright (C) 2004/2005 Michael Foord
# E-mail: fuzzyman AT voidspace DOT org DOT uk

# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# Comments, suggestions and bug reports welcome.

# For information about bugfixes, updates and support, please join the Pythonutils mailing list.
# http://groups.google.com/group/pythonutils/
# Comments, suggestions and bug reports welcome.

import sys
import os
sys.path.append('../modules')
from cgiutils import *

actionline = '<input type="hidden" name="action" value="%s" />'
# a list of reserved names for users
RESERVEDNAMES = ['config', 'default', 'temp', 'emails', 'pending']

# Helper functions for the login scripts and tools
  
def makecookie(userconfig, password, cookiepath):
    """
    Return the current valid cookie heaader for the values supplied in the
    userconfig, the straight password and the cookiepath.
    """
    from login import encodestring
    from Cookie import SimpleCookie
    thecookie = SimpleCookie()
    cookiestring = encodestring(userconfig['username'],password)
    maxage = userconfig['max-age']
    thecookie['userid'] = cookiestring
    if maxage and int(maxage):            # possible cause of error here if the maxage value in a users file isn't an integer !!
        thecookie['userid']['max-age'] = int(maxage) 
    if cookiepath:
        thecookie['userid']['path'] = cookiepath
    return thecookie.output()                         # XXXX may need to be able to return the cookie object

def emptycookie(cookiepath=None):
    """Return an empty cookie with max-age 0.
     Used for logout features.
     """
    from Cookie import SimpleCookie
    thecookie = SimpleCookie()
    thecookie['userid'] = ''
    thecookie['userid']['max-age'] = 0 
    if cookiepath:
        thecookie['userid']['path'] = cookiepath 
    return thecookie.output()                       # XXXX may need to be able to return the cookie object

def sortaction(action):
    return action or 'EMPTY_VAL_MJF'

def createuser(userdir, realname, username, email, password, adminlev):
    """Create a new user."""
    from time import time
    from dataenc import pass_enc
    from configobj import ConfigObj
    
    user = ConfigObj(userdir+'default.ini')
    user.filename = userdir + username + '.ini'         # XXXX  this does no checkign htat the name is valid and doesn't already exist !!
    user['username'] = username
    user['realname'] = realname
    user['email'] = email
    user['admin'] = adminlev
    user['password'] = pass_enc(password, timestamp=True, daynumber=True)
    user['created'] = str(time())
    user.write()

"""
CHANGELOG
=========

2005/09/09
----------

Changes to work with pythonutils 0.2.1

"""
# newlogin.py
# part of logintools
# A CGI Authentication and user account system
# Copyright (C) 2004/2005 Michael Foord
# E-mail: fuzzyman AT voidspace DOT org DOT uk

# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# Comments, suggestions and bug reports welcome.

# For information about bugfixes, updates and support, please join the Pythonutils mailing list.
# http://groups.google.com/group/pythonutils/
# Comments, suggestions and bug reports welcome.

import sys
import os
sys.path.append('../modules')
from configobj import ConfigObj
from pathutils import *
from cgiutils import *
from loginutils import *

####################################################
# Various default values etc

newlogintemplate = 'newlogin_page.html'
newform_nojs = 'newlogin_nojs.txt'
logindone = 'login_done.html'
editform_nojs = 'editform_nojs.txt'
edacctemplate = 'edacc_page.html'
newloginlist = ['email', 'username', 'realname', ]
newloginformkeys = {'username' : 'Login Name', 'pass1' : 'Password', 'pass2' : 'Confirmation Password',
         'email' : 'Email Address', 'realname' : 'Real Name'}
edacckeys = ['realname', 'username', 'pass0', 'pass1', 'pass2', 'email']

# FIXME: do we trust this in all cases ? (is it part of the CGI spec or optional)
# can't we just use SCRIPT_NAME ?
SCRIPTLOC = 'http://' + os.environ.get('HTTP_HOST', '')

##########################################################
# functions for handling new logins

def newlogin(userdir, thisscript, action=None):
    """Display the newlogin form."""
    config = ConfigObj(userdir + 'config.ini')
    templatedir = config['templatedir']
    # check that new logins are enabled on this setup
    checknewlogin(userdir)
    #
    newloginform = readfile(templatedir+newform_nojs)
    newloginform = newloginform.replace('**script**', thisscript)
    #
    newloginpage = readfile(templatedir+newlogintemplate)  
    newloginpage = newloginpage.replace('**new login form**', newloginform)
    newloginpage = filltemplate(newloginpage)
    #
    if action:
        newloginpage = newloginpage.replace('<!-- **action** -->', actionline % action)
    print serverline
    print '\r'
    print newloginpage
    #
    sys.exit()

def donewlogin(theform, userdir, thisscript, action=None):
    """Process the results from new login form submissions."""
    loginaction = theform['login'].value
    if not loginaction == 'donewloginnojs':
        # only type of newlogin supported so far
        sys.exit()
    # check that new logins are enabled on this setup
    checknewlogin(userdir)
    allentries = theform.keys()
    vallist = allentries + [entry for entry in newloginformkeys if entry not in allentries]  
    formdict = getform(vallist, theform, nolist=True)
    for entry in newloginformkeys:
        if not formdict[entry]:
            invalidentry('Required field missing : "%s"' % newloginformkeys[entry], formdict, userdir, thisscript, action)
    # missingentry should redisplay the form, with values filled in, and then exit
    email = validateemail(formdict)
    if not email:
        invalidentry('Email address appears to be invalid.', formdict, userdir, thisscript, action)
    # if the address is invalid it should behave like missingentry
    # also max one login per email address ?
    login = validatelogin(formdict, userdir, thisscript, action)
    # if login name already exists or is invalid
    password = validatepass(formdict, userdir, thisscript, action)
    # if passwords don't match or are too short 
    realname = formdict['realname']
    # If we've got this far we've got a valid new login and need to save the
    # details, send the email and print a mesage
    #
    config = ConfigObj(userdir + 'config.ini') 
    link_url = savedetails(userdir, formdict, action)     # this includes any 'extra' keys, not just the ones we require      
    msg = config['email_message'] + '\n'
    msg = msg + SCRIPTLOC + thisscript + '?login=confirm&id=' + link_url
    writefile('log.txt', msg)
    sendmailme(email, msg, config['email_subject'], email, html=False)
    #
    templatedir = config['templatedir']
    logindonepage = readfile(templatedir+logindone)
    logindonepage = logindonepage.replace('**this script**', thisscript)
    #
    print serverline
    print '\r'
    print logindonepage
    #
    sys.exit()
    
def confirm(theform, userdir, thisscript):
    """Confirm a login.
    Either from an invite or from a user who has registered."""
    from dataenc import pass_dec, pass_enc
    from login import encodestring
    fail = False
    try:
        theval, daynumber, timestamp = pass_dec(theform['id'].value)
    except:
        # FIXME: bare except....
        newloginfail()
    tempstore = ConfigObj(userdir + 'temp.ini')
    if not tempstore.has_key(theval):
        newloginfail()
    uservals = tempstore[theval]
    del tempstore[theval]
    username = uservals['username']
    if username in tempstore['pending']:
        tempstore['pending'].remove(username)
    tempstore.write()
    #
    newconfig = ConfigObj(userdir + 'default.ini')
    newpath = userdir + username + '.ini'
    if os.path.isfile(newpath):
        newloginfail()
    newconfig.filename = newpath
    # FIXME: should this be '' ?
    action = None
    for entry in uservals:
        if entry == 'action':
            action = uservals[entry]
        elif entry == 'password':
            password = uservals[entry]
            newconfig[entry] = pass_enc(password, timestamp=True, daynumber=True)
        else:
            newconfig[entry] = uservals[entry]
    newconfig.write()
    #
    # next we need to create the cookie header to return it 
    from Cookie import SimpleCookie
    thecookie = SimpleCookie()
    thecookie['userid'] = encodestring(newconfig['username'], password)
    config = ConfigObj(userdir + 'config.ini')
    maxage = newconfig['max-age'] 
    cookiepath = config['cookiepath']
    if maxage and int(maxage):            # possible cause of error here if the maxage value in a users file isn't an integer !!
        thecookie['userid']['max-age'] = int(maxage) 
    if cookiepath:
        thecookie['userid']['path'] = cookiepath 
    if config['adminmail']:
        msg = 'A new user has created a login - "%s".\n\n' % thisscript
        for entry in newconfig:
            if entry != 'password':
                msg += entry + '   :   ' + newconfig[entry] + '\n'
        # FIXME: should be mailme
        sendmailme(config['adminmail'], msg, config['email_subject'],
                config['adminmail'], html=False)
    return action, newconfig, thecookie.output()
        
def editaccount(userdir, thisscript, userconfig, action, newcookie):
    """Display the form for the user to edit their account details."""
    msg = ''
    display_edit(userconfig, userdir, thisscript, msg, action, newcookie)
   
def doeditaccount(theform, userconfig, userdir, thisscript, action, newcookie):
    """Process the results from edit account form submissions."""
    from dataenc import pass_enc, pass_dec
    loginaction = theform['login'].value
    if not loginaction == 'doeditaccountnojs':                      # only type of newlogin supported so far
        sys.exit()
    allentries = theform.keys()
    vallist = allentries + [entry for entry in edacckeys if entry not in allentries]
    formdict = getform(vallist, theform, nolist=True)
    #
    oldpass = formdict['pass0']
    storedpass = pass_dec(userconfig['password'])[0] 
    pass1 = formdict['pass1']
    pass2 = formdict['pass2']
    #
    email = validateemail(formdict)
    oldemail = userconfig['email']
    if not email:
        msg = 'The email address you supplied appears to be invalid.'
        display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig)
    if email != oldemail and (not oldpass or oldpass != storedpass):
        msg = 'You must correctly enter your password to change your email address.'
        display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig)
    userconfig['email'] = email
    if not formdict['realname']:
        msg = 'You need to enter a name for us to use.'
        display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig)
    userconfig['realname'] = formdict['realname']
    if pass1 or pass2:
        if pass1 != pass2: 
            msg = "The two passwords don't match."
            display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig)
        if len(pass1) < 5:
            msg = "The password must be longer than 5 characters."
            display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig)
        if not oldpass or oldpass != storedpass:
            msg = 'You must correctly enter your current password to change it.'
            display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig)
        userconfig['password'] = pass_enc(pass1, daynumber=True, timestamp=True)
        newcookie = makecookie(userconfig, pass1, ConfigObj(userdir+'config.ini')['cookiepath'])
    for entry in formdict:
        if entry not in edacckeys:
            userconfig[entry] = formdict[entry]
    userconfig.write()
    return action, userconfig, newcookie                # XXXXX display values changed page
                                                        # XXXXX must change cookie if password changed

#################################################
# helper functions

# in the new login and edit account templates you need value="**keyname1**" in the relevant input fields
# plus a comment like this somewhere in the document:
# <!-- **keynamelist** keyname1, keyname2, keyname3 -->
# When a validation error occurs - the template is reprinted with the existing values filled into the template
# XXXX There's no way (yet) of making any of these new keys required keys though.
# XXXX the first time this happens it ought to fill in the values from 'default.ini'
def filltemplate(template, formdict=None, vallist=None):
    """Fill in the blanks in a new login form."""
    if not formdict:
        formdict = {}
    if not vallist:
        vallist = newloginlist
    index = template.find('**keynamelist**')
    if index == -1:
        keynames = []
    else:
        start = index
        while index < len(template)-1:
           index += 1
           if template[index] in '"\'><':
               break
        keynames = [entry.strip() for entry in template[start:index].split(',') if entry not in vallist]
    for entry in keynames+vallist:
        if entry in ['pass1', 'pass2']:
           continue
        # entry is for example "username"
        # kim's add on:
        template= template.decode("utf-8")
        #a=template.replace(             u'**%s**' % entry,    formdict.get(entry, '').decode("utf-8"))
        template = template.replace(    u'**%s**' % entry,    formdict.get(entry, '').decode("utf-8")    ).encode("utf-8")
    return template


def validateemail(formdict):
    email = formdict['email'].strip()
    return validemail(email)
  
def validatelogin(formdict, userdir, thisscript, action):
    name = formdict['username']
    tempstore = ConfigObj(userdir + 'temp.ini')
    pendinglist = tempstore.get('pending', [])
    if os.path.isfile(userdir+name+'.ini') or name in pendinglist or name.lower() in RESERVEDNAMES:
        invalidentry('Username already exists.', formdict, userdir, thisscript, action)
    for char in name.lower():
        if not char in validchars:
            invalidentry('Username contains invalid characters.', formdict, userdir, thisscript, action)
    return name

def validatepass(formdict, userdir, thisscript, action):
    pass1 = formdict['pass1']
    pass2 = formdict['pass2']
    if pass1 != pass2:
        invalidentry('The two passwords don\'t match.', formdict, userdir, thisscript, action)
    if len(pass1) < 5:
        invalidentry('The password must be at least 5 characters long.', formdict, userdir, thisscript, action)
    return pass1
    
def invalidentry(msg, formdict, userdir, thisscript, action=None):
    """Display the newlogin form."""
    config = ConfigObj(userdir + 'config.ini')
    templatedir = config['templatedir']
    #
    newloginform = readfile(templatedir+newform_nojs)
    newloginform = newloginform.replace('**script**', thisscript)
    #
    newloginpage = readfile(templatedir+newlogintemplate)  
    newloginpage = newloginpage.replace('**new login form**', newloginform)
    newloginpage = filltemplate(newloginpage, formdict)
    newloginpage = newloginpage.replace('<!-- **message** -->', '<h2>'+msg+'</h2><br>')
    #
    if action:
        newloginpage = newloginpage.replace('<!-- **action** -->', actionline % action)
    print serverline
    print '\r'
    print newloginpage
    #
    sys.exit()

def savedetails(userdir, formdict, action=None):
    """
    Given the form from a validated new login, it saves the details to the 
    temporary store.
    
    It also cleans up any out of date ones that haven't been used.
    """
    from dateutils import returndate, daycount
    from dataenc import pass_enc
    #
    tempstore = ConfigObj(userdir + 'temp.ini')
    if action: 
        formdict['action'] = action
    year, month, day = returndate()
    today = daycount(year, month, day)
    #
    for section in tempstore:
        if section[4:].isdigit():
            if int(section[4:]) > today + 30:
                name = tempstore[section]['username']
                tempstore['pending'].remove(name)
                del tempstore[section]
    #
    ran = randomstring(4)
    while tempstore.has_key(ran+str(today)):
        ran = randomstring(4)
    key = ran+str(today)
    tempstore[key] = {}
    store = tempstore[key]
    for entry in formdict:
        if entry == 'pass1' or entry == 'pass2':
            store['password'] = formdict[entry]
        elif entry == 'login':
            pass
        else:
            store[entry] = formdict[entry]
    if not tempstore.has_key('pending'):
        tempstore['pending'] = []
    tempstore['pending'].append(formdict['username'])
    tempstore.write()
    return pass_enc(key, timestamp=True, daynumber=True)

def checknewlogin(userdir):
    """Check that new logins are enabled on this setup.
    Return or display the error message and quit."""
    config = ConfigObj(userdir + 'config.ini')
    if istrue(config['newloginlink']):
        return
    error('Sorry, New Logins are Disabled on this System.')

def newloginfail():
    # FIXME: this could be more useful
    error('Sorry, you have either already confirmed your login, '
            'or it is out of date :-)')

def display_edit(formdict, userdir, thisscript, msg, action, newcookie,
                                                            userconfig=None):
    """
    Display the form for editing account details.
    This is a different form to creating a new account as it requires
    confirmation of the old password before doing certain things, like 
    changing email address and password.
    """
    from time import ctime
    print newcookie
    config = ConfigObj(userdir + 'config.ini')
    templatedir = config['templatedir']
    # if we are sending the userconfig as the formdict, we needn't explicitly
    # pass it in
    if not userconfig:
        userconfig = formdict
    #
    edaccform = unicode(readfile(templatedir+editform_nojs))
    edaccform = edaccform.replace('**script**', thisscript)
    #
    edaccpage = readfile(templatedir+edacctemplate)  
    edaccpage = edaccpage.replace('**edit account form**', edaccform)
    edaccpage = filltemplate(edaccpage, formdict)
    if msg:
        edaccpage = edaccpage.replace('<!-- **message** -->', '<h2>'+msg+'</h2><br>')
    edaccpage = edaccpage.replace('**created on**', ctime(float(userconfig['created'])))
    edaccpage = edaccpage.replace('**num logins**', userconfig['numlogins'])
    edaccpage = edaccpage.replace('**last used**', ctime(float(userconfig['lastused'])))
    edaccpage = edaccpage.replace('**num used**', userconfig['numused'])
    edaccpage = edaccpage.replace('**this script**', thisscript + '?action=' + action)
    #
    if action:
        edaccpage = edaccpage.replace('<!-- **action** -->', actionline % action)
    print serverline
    print '\r'
    print edaccpage.encode("utf-8")
    #
    sys.exit()


"""
CHANGELOG
=========

2005/10/30
----------

Fixed the email function... oops.

2005/10/11
----------

Bugfix in 

2005/09/09
----------

Changes to work with pythonutils 0.2.1

"""
# odict.py
# An Ordered Dictionary object
# Copyright (C) 2005 Nicola Larosa, Michael Foord
# E-mail: nico-NoSp@m-tekNico.net, fuzzyman AT voidspace DOT org DOT uk

# This software is licensed under the terms of the BSD license.
# http://www.voidspace.org.uk/python/license.shtml
# Basically you're free to copy, modify, distribute and relicense it,
# So long as you keep a copy of the license with it.

# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# For information about bugfixes, updates and support, please join the
# Pythonutils mailing list:
# http://groups.google.com/group/pythonutils/
# Comments, suggestions and bug reports welcome.

"""A dict that keeps keys in insertion order"""

__author__ = ('Nicola Larosa <nico-NoSp@m-tekNico.net>,'
    'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>')

__docformat__ = "restructuredtext en"

__revision__ = '$Id: odict.py 129 2005-09-12 18:15:28Z teknico $'

__version__ = '0.2.1'

__all__ = ['OrderedDict', 'SequenceOrderedDict']

from __future__ import generators
from warnings import warn
from types import SliceType

import sys
INTP_VER = sys.version_info[:2]
if INTP_VER < (2, 2):
    raise RuntimeError("Python v.2.2 or later needed")

class OrderedDict(dict):
    """
    A class of dictionary that keeps the insertion order of keys.
    
    All appropriate methods return keys, items, or values in an ordered way.
    
    All normal dictionary methods are available. Update and comparison is
    restricted to other OrderedDict objects.
    
    Various sequence methods are available, including the ability to explicitly
    mutate the key ordering.
    
    __contains__ tests:
    
    >>> d = OrderedDict(((1, 3),))
    >>> 1 in d
    1
    >>> 4 in d
    0
    
    __getitem__ tests:
    
    >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2]
    1
    >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4]
    Traceback (most recent call last):
    KeyError: 4
    
    __len__ tests:
    
    >>> len(OrderedDict())
    0
    >>> len(OrderedDict(((1, 3), (3, 2), (2, 1))))
    3
    
    get tests:
    
    >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
    >>> d.get(1)
    3
    >>> d.get(4) is None
    1
    >>> d.get(4, 5)
    5
    >>> d
    OrderedDict([(1, 3), (3, 2), (2, 1)])
    
    has_key tests:
    
    >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
    >>> d.has_key(1)
    1
    >>> d.has_key(4)
    0
    """

    def __init__(self, init_val=(), strict=False):
        """
        Create a new ordered dictionary. Cannot init from a normal dict,
        nor from kwargs, since items order is undefined in those cases.
        
        If the ``strict`` keyword argument is ``True`` (``False`` is the
        default) then when doing slice assignment - the ``OrderedDict`` you are
        assigning from *must not* contain any keys in the remaining dict.
        
        >>> OrderedDict()
        OrderedDict([])
        >>> OrderedDict({1: 1})
        Traceback (most recent call last):
        TypeError: undefined order, cannot get items from dict
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d
        OrderedDict([(1, 3), (3, 2), (2, 1)])
        >>> OrderedDict(d)
        OrderedDict([(1, 3), (3, 2), (2, 1)])
        """
        self.strict = strict
        dict.__init__(self)
        if isinstance(init_val, OrderedDict):
            self._sequence = init_val.keys()
            dict.update(self, init_val)
        elif isinstance(init_val, dict):
            # we lose compatibility with other ordered dict types this way
            raise TypeError('undefined order, cannot get items from dict')
        else:
            self._sequence = []
            # FIXME: efficiency ?
            # NOTE: Can't just do ``dict.__update__`` since duplicate items
            #   would be chosen in an arbitrary order, we need to guarantee
            #   that the last one is used.
            idx = 0
            # sequence of 2-item sequences, or error
            for item in init_val:
                try:
                    key, val = item
                except TypeError:
                    raise TypeError('cannot convert dictionary update'
                        ' sequence element #%d to a sequence' % idx)
                self[key] = val
                idx += 1

### Special methods ###

    def __delitem__(self, key):
        """
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> del d[3]
        >>> d
        OrderedDict([(1, 3), (2, 1)])
        >>> del d[3]
        Traceback (most recent call last):
        KeyError: 3
        >>> d[3] = 2
        >>> d
        OrderedDict([(1, 3), (2, 1), (3, 2)])
        >>> del d[0:1]
        >>> d
        OrderedDict([(2, 1), (3, 2)])
        """
        if isinstance(key, SliceType):
            # FIXME: efficiency ?
            keys = self._sequence[key]
            for entry in keys:
                dict.__delitem__(self, entry)
            del self._sequence[key]
        else:
            # do the dict.__delitem__ *first* as it raises
            # the more appropriate error
            dict.__delitem__(self, key)
            self._sequence.remove(key)

    def __eq__(self, other):
        """
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d == OrderedDict(d)
        1
        >>> d == OrderedDict(((1, 3), (2, 1), (3, 2)))
        0
        >>> d == OrderedDict(((1, 0), (3, 2), (2, 1)))
        0
        >>> d == OrderedDict(((0, 3), (3, 2), (2, 1)))
        0
        >>> d == dict(d)
        Traceback (most recent call last):
        TypeError: Equality undefined for OrderedDicts and dictionaries
        >>> d == False
        0
        """
        if isinstance(other, dict):
            if not isinstance(other, OrderedDict):
                raise TypeError('Equality undefined for OrderedDicts and '
                    'dictionaries')
            # FIXME: efficiency ?
            #   Generate both item lists for each compare
            return (self.items() == other.items())
        else:
            return False

    def __lt__(self, other):
        """
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
        >>> c < d
        1
        >>> d < c
        0
        >>> d < dict(c)
        Traceback (most recent call last):
        TypeError: Can only compare with other OrderedDicts
        """
        if not isinstance(other, OrderedDict):
            raise TypeError('Can only compare with other OrderedDicts')
        # FIXME: efficiency ?
        #   Generate both item lists for each compare
        return (self.items() < other.items())

    def __le__(self, other):
        """
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
        >>> e = OrderedDict(d)
        >>> c <= d
        1
        >>> d <= c
        0
        >>> d <= dict(c)
        Traceback (most recent call last):
        TypeError: Can only compare with other OrderedDicts
        >>> d <= e
        1
        """
        if not isinstance(other, OrderedDict):
            raise TypeError('Can only compare with other OrderedDicts')
        # FIXME: efficiency ?
        #   Generate both item lists for each compare
        return (self.items() <= other.items())

    def __ne__(self, other):
        """
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d != OrderedDict(d)
        0
        >>> d != OrderedDict(((1, 3), (2, 1), (3, 2)))
        1
        >>> d != OrderedDict(((1, 0), (3, 2), (2, 1)))
        1
        >>> d == OrderedDict(((0, 3), (3, 2), (2, 1)))
        0
        >>> d != dict(d)
        Traceback (most recent call last):
        TypeError: Inequality undefined for OrderedDicts and dictionaries
        >>> d != False
        1
        """
        if isinstance(other, dict):
            if not isinstance(other, OrderedDict):
                raise TypeError('Inequality undefined for OrderedDicts and '
                    'dictionaries')
            # FIXME: efficiency ?
            #   Generate both item lists for each compare
            return not (self.items() == other.items())
        else:
            return True

    def __gt__(self, other):
        """
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
        >>> d > c
        1
        >>> c > d
        0
        >>> d > dict(c)
        Traceback (most recent call last):
        TypeError: Can only compare with other OrderedDicts
        """
        if not isinstance(other, OrderedDict):
            raise TypeError('Can only compare with other OrderedDicts')
        # FIXME: efficiency ?
        #   Generate both item lists for each compare
        return (self.items() > other.items())

    def __ge__(self, other):
        """
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
        >>> e = OrderedDict(d)
        >>> c >= d
        0
        >>> d >= c
        1
        >>> d >= dict(c)
        Traceback (most recent call last):
        TypeError: Can only compare with other OrderedDicts
        >>> e >= d
        1
        """
        if not isinstance(other, OrderedDict):
            raise TypeError('Can only compare with other OrderedDicts')
        # FIXME: efficiency ?
        #   Generate both item lists for each compare
        return (self.items() >= other.items())

    def __repr__(self):
        """
        Used for __repr__ and __str__
        
        >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f'))))
        >>> r1
        "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])"
        >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd'))))
        >>> r2
        "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])"
        >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f'))))
        1
        >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd'))))
        1
        """
        return '%s([%s])' % (self.__class__.__name__, ', '.join(
            ['(%r, %r)' % (key, self[key]) for key in self._sequence]))

    def __setitem__(self, key, val):
        """
        Allows slice assignment, so long as the slice is an OrderedDict
        >>> d = OrderedDict()
        >>> d['a'] = 'b'
        >>> d['b'] = 'a'
        >>> d[3] = 12
        >>> d
        OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)])
        >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4)))
        >>> d
        OrderedDict([(1, 2), (2, 3), (3, 4)])
        >>> d[::2] = OrderedDict(((7, 8), (9, 10)))
        >>> d
        OrderedDict([(7, 8), (2, 3), (9, 10)])
        >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)))
        >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8)))
        >>> d
        OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)])
        >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True)
        >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8)))
        >>> d
        OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)])
        
        >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True)
        >>> a[3] = 4
        >>> a
        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> a
        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)])
        Traceback (most recent call last):
        ValueError: slice assignment must be from unique keys
        >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)))
        >>> a[3] = 4
        >>> a
        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> a
        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> a
        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> a
        OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)])
        
        >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> d[:1] = 3
        Traceback (most recent call last):
        TypeError: slice assignment requires an OrderedDict
        
        >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
        >>> d[:1] = OrderedDict([(9, 8)])
        >>> d
        OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)])
        """
        if isinstance(key, SliceType):
            if not isinstance(val, OrderedDict):
                # FIXME: allow a list of tuples ?
                raise TypeError('slice assignment requires an OrderedDict')
            keys = self._sequence[key]
            # NOTE: Could use ``range(*key.indices(len(self._sequence)))``
            indexes = range(len(self._sequence))[key]
            if key.step is None:
                # NOTE: new slice may not be the same size as the one being
                #   overwritten !
                # NOTE: What is the algorithm for an impossible slice ?
                #   e.g. d[5:3]
                pos = key.start or 0
                del self[key]
                newkeys = val.keys()
                for k in newkeys:
                    if k in self:
                        if self.strict:
                            raise ValueError('slice assignment must be from '
                                'unique keys')
                        else:
                            # NOTE: This removes duplicate keys *first*
                            #   so start position might have changed ?
                            del self[k]
                self._sequence = (self._sequence[:pos] + newkeys +
                    self._sequence[pos:])
                dict.update(self, val)
            else:
                # extended slice
                # length of new slice must be the same as the one being
                # replaced
                if len(keys) != len(val):
                    raise ValueError('attempt to assign sequence of size %s '
                        'to extended slice of size %s' % (len(val), len(keys)))
                # FIXME: efficiency ?
                del self[key]
                item_list = zip(indexes, val.items())
                # smallest indexes first - higher indexes not guaranteed to
                # exist
                item_list.sort()
                for pos, (newkey, newval) in item_list:
                    if self.strict and newkey in self:
                        raise ValueError('slice assignment must be from unique'
                        ' keys')
                    self.insert(pos, newkey, newval)
        else:
            if not self.has_key(key):
                self._sequence.append(key)
            dict.__setitem__(self, key, val)

    def __getitem__(self, key):
        """
        Allows slicing. Returns an OrderedDict if you slice.
        >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)])
        >>> b[::-1]
        OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)])
        >>> b[2:5]
        OrderedDict([(5, 2), (4, 3), (3, 4)])
        >>> type(b[2:4])
        <class '__main__.OrderedDict'>
        """
        if isinstance(key, SliceType):
            # FIXME: does this raise the error we want ?
            keys = self._sequence[key]
            # FIXME: efficiency ?
            return OrderedDict([(entry, self[entry]) for entry in keys])
        else:
            return dict.__getitem__(self, key)

    __str__ = __repr__

    def __setattr__(self, name, value):
        """
        Implemented so that accesses to ``sequence`` raise a warning and are
        diverted to the new ``setkeys`` method.
        """
        if name == 'sequence':
            warn('use of sequence attribute is deprecated. Use keys method '
                'instead.', DeprecationWarning)
            # NOTE: doesn't return anything
            self.setkeys(value)
        else:
            # FIXME: do we want to allow arbitrary setting of attributes ?
            #   or do we want to manage it ?
            object.__setattr__(self, name, value)

    def __getattr__(self, name):
        """
        Implemented so that access to ``sequence`` raises a warning.
        
        >>> d = OrderedDict()
        >>> d.sequence
        []
        """
        if name == 'sequence':
            warn('use of sequence attribute is deprecated. Use keys method '
                'instead.', DeprecationWarning)
            # NOTE: Still (currently) returns a direct reference. Need to
            #   because code that uses sequence will expect to be able to
            #   mutate it in place.
            return self._sequence
        else:
            # raise the appropriate error
            raise AttributeError("OrderedDict has no attribute '%s'" % name)

    def __deepcopy__(self, memo):
        """
        To allow deepcopy to work with OrderedDict.
        
        >>> from copy import deepcopy
        >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)])
        >>> a['test'] = {}
        >>> b = deepcopy(a)
        >>> b == a
        1
        >>> b is a
        0
        >>> a['test'] is b['test']
        0
        """
        from copy import deepcopy
        return self.__class__(deepcopy(self.items(), memo), self.strict)


### Read-only methods ###

    def copy(self):
        """
        >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy()
        OrderedDict([(1, 3), (3, 2), (2, 1)])
        """
        return OrderedDict(self)

    def items(self):
        """
        ``items`` returns a list of tuples representing all the 
        ``(key, value)`` pairs in the dictionary.
        
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.items()
        [(1, 3), (3, 2), (2, 1)]
        >>> d.clear()
        >>> d.items()
        []
        """
        return zip(self._sequence, self.values())

    def keys(self):
        """
        Return a list of keys in the ``OrderedDict``.
        
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.keys()
        [1, 3, 2]
        """
        return self._sequence[:]

    def values(self, values=None):
        """
        Return a list of all the values in the OrderedDict.
        
        Optionally you can pass in a list of values, which will replace the
        current list. The value list must be the same len as the OrderedDict.
        
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.values()
        [3, 2, 1]
        """
        return [self[key] for key in self._sequence]

    def iteritems(self):
        """
        >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems()
        >>> ii.next()
        (1, 3)
        >>> ii.next()
        (3, 2)
        >>> ii.next()
        (2, 1)
        >>> ii.next()
        Traceback (most recent call last):
        StopIteration
        """
        def make_iter(self=self):
            keys = self.iterkeys()
            while True:
                key = keys.next()
                yield (key, self[key])
        return make_iter()

    def iterkeys(self):
        """
        >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys()
        >>> ii.next()
        1
        >>> ii.next()
        3
        >>> ii.next()
        2
        >>> ii.next()
        Traceback (most recent call last):
        StopIteration
        """
        return iter(self._sequence)

    __iter__ = iterkeys

    def itervalues(self):
        """
        >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues()
        >>> iv.next()
        3
        >>> iv.next()
        2
        >>> iv.next()
        1
        >>> iv.next()
        Traceback (most recent call last):
        StopIteration
        """
        def make_iter(self=self):
            keys = self.iterkeys()
            while True:
                yield self[keys.next()]
        return make_iter()

### Read-write methods ###

    def clear(self):
        """
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.clear()
        >>> d
        OrderedDict([])
        """
        dict.clear(self)
        self._sequence = []

    def pop(self, key, *args):
        """
        No dict.pop in Python 2.2, gotta reimplement it
        
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.pop(3)
        2
        >>> d
        OrderedDict([(1, 3), (2, 1)])
        >>> d.pop(4)
        Traceback (most recent call last):
        KeyError: 4
        >>> d.pop(4, 0)
        0
        >>> d.pop(4, 0, 1)
        Traceback (most recent call last):
        TypeError: pop expected at most 2 arguments, got 3
        """
        if len(args) > 1:
            raise TypeError, ('pop expected at most 2 arguments, got %s' %
                (len(args) + 1))
        if key in self:
            val = self[key]
            del self[key]
        else:
            try:
                val = args[0]
            except IndexError:
                raise KeyError(key)
        return val

    def popitem(self, i=-1):
        """
        Delete and return an item specified by index, not a random one as in
        dict. The index is -1 by default (the last item).
        
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.popitem()
        (2, 1)
        >>> d
        OrderedDict([(1, 3), (3, 2)])
        >>> d.popitem(0)
        (1, 3)
        >>> OrderedDict().popitem()
        Traceback (most recent call last):
        KeyError: 'popitem(): dictionary is empty'
        >>> d.popitem(2)
        Traceback (most recent call last):
        IndexError: popitem(): index 2 not valid
        """
        if not self._sequence:
            raise KeyError('popitem(): dictionary is empty')
        try:
            key = self._sequence[i]
        except IndexError:
            raise IndexError('popitem(): index %s not valid' % i)
        return (key, self.pop(key))

    def setdefault(self, key, defval = None):
        """
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.setdefault(1)
        3
        >>> d.setdefault(4) is None
        1
        >>> d
        OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)])
        >>> d.setdefault(5, 0)
        0
        >>> d
        OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)])
        """
        if key in self:
            return self[key]
        else:
            self[key] = defval
            return defval

    def update(self, from_od):
        """
        Update from another OrderedDict or sequence of (key, value) pairs
        
        >>> d = OrderedDict()
        >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1))))
        >>> d
        OrderedDict([(1, 3), (3, 2), (2, 1)])
        >>> d.update({4: 4})
        Traceback (most recent call last):
        TypeError: undefined order, cannot get items from dict
        >>> d.update((4, 4))
        Traceback (most recent call last):
        TypeError: cannot convert dictionary update sequence element #0 to a sequence
        """
        if isinstance(from_od, OrderedDict):
            for key, val in from_od.items():
                self[key] = val
        elif isinstance(from_od, dict):
            # we lose compatibility with other ordered dict types this way
            raise TypeError('undefined order, cannot get items from dict')
        else:
            idx = 0
            # sequence of 2-item sequences, or error
            for item in from_od:
                try:
                    key, val = item
                except TypeError:
                    raise TypeError('cannot convert dictionary update'
                        ' sequence element #%d to a sequence' % idx)
                self[key] = val
                idx += 1

    def setitems(self, items):
        """
        This method allows you to set the items in the dict.
        
        It takes a list of tuples - of the same sort returned by the ``items``
        method.
        
        >>> d = OrderedDict()
        >>> d.setitems(((3, 1), (2, 3), (1, 2)))
        >>> d
        OrderedDict([(3, 1), (2, 3), (1, 2)])
        """
        self.clear()
        # FIXME: this allows you to pass in an OrderedDict as well :-)
        self.update(items)

    def setkeys(self, keys):
        """
        ``setkeys`` all ows you to pass in a new list of keys which will
        replace the current set. This must contain the same set of keys, but
        need not be in the same order.
        
        If you pass in new keys that don't match, a ``KeyError`` will be
        raised.
        
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.keys()
        [1, 3, 2]
        >>> d.setkeys((1, 2, 3))
        >>> d
        OrderedDict([(1, 3), (2, 1), (3, 2)])
        >>> d.setkeys(['a', 'b', 'c'])
        Traceback (most recent call last):
        KeyError: 'Keylist is not the same as current keylist.'
        """
        # FIXME: Efficiency ? (use set for Python 2.4 :-)
        # NOTE: list(keys) rather than keys[:] because keys[:] returns
        #   a tuple, if keys is a tuple.
        kcopy = list(keys)
        kcopy.sort()
        self._sequence.sort()
        if kcopy != self._sequence:
            raise KeyError('Keylist is not the same as current keylist.')
        # NOTE: This makes the _sequence attribute a new object, instead
        #       of changing it in place.
        # FIXME: efficiency ?
        self._sequence = list(keys)

    def setvalues(self, values):
        """
        You can pass in a list of values, which will replace the
        current list. The value list must be the same len as the OrderedDict.
        
        (Or a ``ValueError`` is raised.)
        
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.setvalues((1, 2, 3))
        >>> d
        OrderedDict([(1, 1), (3, 2), (2, 3)])
        >>> d.setvalues([6])
        Traceback (most recent call last):
        ValueError: Value list is not the same length as the OrderedDict.
        """
        if len(values) != len(self):
            # FIXME: correct error to raise ?
            raise ValueError('Value list is not the same length as the '
                'OrderedDict.')
        self.update(zip(self, values))

### Sequence Methods ###

    def index(self, key):
        """
        Return the position of the specified key in the OrderedDict.
        
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.index(3)
        1
        >>> d.index(4)
        Traceback (most recent call last):
        ValueError: list.index(x): x not in list
        """
        return self._sequence.index(key)

    def insert(self, index, key, value):
        """
        Takes ``index``, ``key``, and ``value`` as arguments.
        
        Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in
        the OrderedDict.
        
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.insert(0, 4, 0)
        >>> d
        OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)])
        >>> d.insert(0, 2, 1)
        >>> d
        OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)])
        >>> d.insert(8, 8, 1)
        >>> d
        OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)])
        """
        if key in self:
            # FIXME: efficiency ?
            del self[key]
        self._sequence.insert(index, key)
        dict.__setitem__(self, key, value)

    def reverse(self):
        """
        Reverse the order of the OrderedDict.
        
        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
        >>> d.reverse()
        >>> d
        OrderedDict([(2, 1), (3, 2), (1, 3)])
        """
        self._sequence.reverse()

    def sort(self, *args, **kwargs):
        """
        Sort the key order in the OrderedDict.
        
        This method takes the same arguments as the ``list.sort`` method on
        your version of Python.
        
        >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4)))
        >>> d.sort()
        >>> d
        OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)])
        """
        self._sequence.sort(*args, **kwargs)

class _keys(object):
    # FIXME: should this object be a subclass of list ?
    """
    Custom object for accessing the keys of an OrderedDict.
    
    Can be called like the normal ``OrderedDict.keys`` method, but also
    supports indexing and sequence methods.
    """

    def __init__(self, main):
        self._main = main

    def __call__(self):
        """Pretend to be the keys method."""
        return self._main._keys()

    def __getitem__(self, index):
        """Fetch the key at position i."""
        # NOTE: this automatically supports slicing :-)
        return self._main._sequence[index]

    def __setitem__(self, index, name):
        """
        You cannot assign to keys, but you can do slice assignment to re-order
        them.
        
        You can only do slice assignment if the new set of keys is a reordering
        of the original set.
        """
        if isinstance(index, SliceType):
            # FIXME: efficiency ?
            # check length is the same
            indexes = range(len(self._main._sequence))[index]
            if len(indexes) != len(name):
                raise ValueError('attempt to assign sequence of size %s '
                    'to slice of size %s' % (len(name), len(indexes)))
            # check they are the same keys
            # FIXME: Use set
            old_keys = self._main._sequence[index]
            new_keys = list(name)
            old_keys.sort()
            new_keys.sort()
            if old_keys != new_keys:
                raise KeyError('Keylist is not the same as current keylist.')
            orig_vals = [self._main[k] for k in name]
            del self._main[index]
            vals = zip(indexes, name, orig_vals)
            vals.sort()
            for i, k, v in vals:
                if self._main.strict and k in self._main:
                    raise ValueError('slice assignment must be from '
                        'unique keys')
                self._main.insert(i, k, v)
        else:
            raise ValueError('Cannot assign to keys')

    ### following methods pinched from UserList and adapted ###
    def __repr__(self): return repr(self._main._sequence)

    # FIXME: do we need to check if we are comparing with another ``_keys``
    #   object ? (like the __cast method of UserList)
    def __lt__(self, other): return self._main._sequence <  other
    def __le__(self, other): return self._main._sequence <= other
    def __eq__(self, other): return self._main._sequence == other
    def __ne__(self, other): return self._main._sequence != other
    def __gt__(self, other): return self._main._sequence >  other
    def __ge__(self, other): return self._main._sequence >= other
    # FIXME: do we need __cmp__ as well as rich comparisons ?
    def __cmp__(self, other): return cmp(self._main._sequence, other)

    def __contains__(self, item): return item in self._main._sequence
    def __len__(self): return len(self._main._sequence)
    def __iter__(self): return self._main.iterkeys()
    def count(self, item): return self._main._sequence.count(item)
    def index(self, item, *args): return self._main._sequence.index(item, *args)
    def reverse(self): self._main._sequence.reverse()
    def sort(self, *args, **kwds): self._main._sequence.sort(*args, **kwds)
    def __mul__(self, n): return self._main._sequence*n
    __rmul__ = __mul__
    def __add__(self, other): return self._main._sequence + other
    def __radd__(self, other): return other + self._main._sequence

    ## following methods not implemented for keys ##
    def __delitem__(self, i): raise TypeError('Can\'t delete items from keys')
    def __iadd__(self, other): raise TypeError('Can\'t add in place to keys')
    def __imul__(self, n): raise TypeError('Can\'t multiply keys in place')
    def append(self, item): raise TypeError('Can\'t append items to keys')
    def insert(self, i, item): raise TypeError('Can\'t insert items into keys')
    def pop(self, i=-1): raise TypeError('Can\'t pop items from keys')
    def remove(self, item): raise TypeError('Can\'t remove items from keys')
    def extend(self, other): raise TypeError('Can\'t extend keys')

class _items(object):
    """
    Custom object for accessing the items of an OrderedDict.
    
    Can be called like the normal ``OrderedDict.items`` method, but also
    supports indexing and sequence methods.
    """

    def __init__(self, main):
        self._main = main

    def __call__(self):
        """Pretend to be the items method."""
        return self._main._items()

    def __getitem__(self, index):
        """Fetch the item at position i."""
        if isinstance(index, SliceType):
            # fetching a slice returns an OrderedDict
            return self._main[index].items()
        key = self._main._sequence[index]
        return (key, self._main[key])

    def __setitem__(self, index, item):
        """Set item at position i to item."""
        if isinstance(index, SliceType):
            # NOTE: item must be an iterable (list of tuples)
            self._main[index] = OrderedDict(item)
        else:
            # FIXME: Does this raise a sensible error ?
            orig = self._main.keys[index]
            key, value = item
            if self._main.strict and key in self and (key != orig):
                raise ValueError('slice assignment must be from '
                        'unique keys')
            # delete the current one
            del self._main[self._main._sequence[index]]
            self._main.insert(index, key, value)

    def __delitem__(self, i):
        """Delete the item at position i."""
        key = self._main._sequence[i]
        if isinstance(i, SliceType):
            for k in key:
                # FIXME: efficiency ?
                del self._main[k]
        else:
            del self._main[key]

    ### following methods pinched from UserList and adapted ###
    def __repr__(self): return repr(self._main.items())

    # FIXME: do we need to check if we are comparing with another ``_items``
    #   object ? (like the __cast method of UserList)
    def __lt__(self, other): return self._main.items() <  other
    def __le__(self, other): return self._main.items() <= other
    def __eq__(self, other): return self._main.items() == other
    def __ne__(self, other): return self._main.items() != other
    def __gt__(self, other): return self._main.items() >  other
    def __ge__(self, other): return self._main.items() >= other
    def __cmp__(self, other): return cmp(self._main.items(), other)

    def __contains__(self, item): return item in self._main.items()
    def __len__(self): return len(self._main._sequence) # easier :-)
    def __iter__(self): return self._main.iteritems()
    def count(self, item): return self._main.items().count(item)
    def index(self, item, *args): return self._main.items().index(item, *args)
    def reverse(self): self._main.reverse()
    def sort(self, *args, **kwds): self._main.sort(*args, **kwds)
    def __mul__(self, n): return self._main.items()*n
    __rmul__ = __mul__
    def __add__(self, other): return self._main.items() + other
    def __radd__(self, other): return other + self._main.items()

    def append(self, item):
        """Add an item to the end."""
        # FIXME: this is only append if the key isn't already present
        key, value = item
        self._main[key] = value

    def insert(self, i, item):
        key, value = item
        self._main.insert(i, key, value)

    def pop(self, i=-1):
        key = self._main._sequence[i]
        return (key, self._main.pop(key))

    def remove(self, item):
        key, value = item
        try:
            assert value == self._main[key]
        except (KeyError, AssertionError):
            raise ValueError('ValueError: list.remove(x): x not in list')
        else:
            del self._main[key]

    def extend(self, other):
        # FIXME: is only a true extend if none of the keys already present
        for item in other:
            key, value = item
            self._main[key] = value

    def __iadd__(self, other):
        self.extend(other)

    ## following methods not implemented for items ##

    def __imul__(self, n): raise TypeError('Can\'t multiply items in place')


class _values(object):
    """
    Custom object for accessing the values of an OrderedDict.
    
    Can be called like the normal ``OrderedDict.values`` method, but also
    supports indexing and sequence methods.
    """

    def __init__(self, main):
        self._main = main

    def __call__(self):
        """Pretend to be the values method."""
        return self._main._values()

    def __getitem__(self, index):
        """Fetch the value at position i."""
        if isinstance(index, SliceType):
            return [self._main[key] for key in self._main._sequence[index]]
        else:
            return self._main[self._main._sequence[index]]

    def __setitem__(self, index, value):
        """
        Set the value at position i to value.
        
        You can only do slice assignment to values if you supply a sequence of
        equal length to the slice you are replacing.
        """
        if isinstance(index, SliceType):
            keys = self._main._sequence[index]
            if len(keys) != len(value):
                raise ValueError('attempt to assign sequence of size %s '
                    'to slice of size %s' % (len(name), len(keys)))
            # FIXME: efficiency ?  Would be better to calculate the indexes
            #   directly from the slice object
            # NOTE: the new keys can collide with existing keys (or even
            #   contain duplicates) - these will overwrite
            for key, val in zip(keys, value):
                self._main[key] = val
        else:
            self._main[self._main._sequence[index]] = value

    ### following methods pinched from UserList and adapted ###
    def __repr__(self): return repr(self._main.values())

    # FIXME: do we need to check if we are comparing with another ``_values``
    #   object ? (like the __cast method of UserList)
    def __lt__(self, other): return self._main.values() <  other
    def __le__(self, other): return self._main.values() <= other
    def __eq__(self, other): return self._main.values() == other
    def __ne__(self, other): return self._main.values() != other
    def __gt__(self, other): return self._main.values() >  other
    def __ge__(self, other): return self._main.values() >= other
    def __cmp__(self, other): return cmp(self._main.values(), other)

    def __contains__(self, item): return item in self._main.values()
    def __len__(self): return len(self._main._sequence) # easier :-)
    def __iter__(self): return self._main.itervalues()
    def count(self, item): return self._main.values().count(item)
    def index(self, item, *args): return self._main.values().index(item, *args)

    def reverse(self):
        """Reverse the values"""
        vals = self._main.values()
        vals.reverse()
        # FIXME: efficiency
        self[:] = vals

    def sort(self, *args, **kwds):
        """Sort the values."""
        vals = self._main.values()
        vals.sort(*args, **kwds)
        self[:] = vals

    def __mul__(self, n): return self._main.values()*n
    __rmul__ = __mul__
    def __add__(self, other): return self._main.values() + other
    def __radd__(self, other): return other + self._main.values()

    ## following methods not implemented for values ##
    def __delitem__(self, i): raise TypeError('Can\'t delete items from values')
    def __iadd__(self, other): raise TypeError('Can\'t add in place to values')
    def __imul__(self, n): raise TypeError('Can\'t multiply values in place')
    def append(self, item): raise TypeError('Can\'t append items to values')
    def insert(self, i, item): raise TypeError('Can\'t insert items into values')
    def pop(self, i=-1): raise TypeError('Can\'t pop items from values')
    def remove(self, item): raise TypeError('Can\'t remove items from values')
    def extend(self, other): raise TypeError('Can\'t extend values')


class SequenceOrderedDict(OrderedDict):
    """
    Experimental version of OrderedDict that has a custom object for ``keys``,
    ``values``, and ``items``.
    
    These are callable sequence objects that work as methods, or can be
    manipulated directly as sequences.
    
    Test for ``keys``, ``items`` and ``values``.
    
    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
    >>> d
    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
    >>> d.keys
    [1, 2, 3]
    >>> d.keys()
    [1, 2, 3]
    >>> d.setkeys((3, 2, 1))
    >>> d
    SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
    >>> d.setkeys((1, 2, 3))
    >>> d.keys[0]
    1
    >>> d.keys[:]
    [1, 2, 3]
    >>> d.keys[-1]
    3
    >>> d.keys[-2]
    2
    >>> d.keys[0:2] = [2, 1]
    >>> d
    SequenceOrderedDict([(2, 3), (1, 2), (3, 4)])
    >>> d.keys.reverse()
    >>> d.keys
    [3, 1, 2]
    >>> d.keys = [1, 2, 3]
    >>> d
    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
    >>> d.keys = [3, 1, 2]
    >>> d
    SequenceOrderedDict([(3, 4), (1, 2), (2, 3)])
    >>> a = SequenceOrderedDict()
    >>> b = SequenceOrderedDict()
    >>> a.keys == b.keys
    1
    >>> a['a'] = 3
    >>> a.keys == b.keys
    0
    >>> b['a'] = 3
    >>> a.keys == b.keys
    1
    >>> b['b'] = 3
    >>> a.keys == b.keys
    0
    >>> a.keys > b.keys
    0
    >>> a.keys < b.keys
    1
    >>> 'a' in a.keys
    1
    >>> len(b.keys)
    2
    >>> 'c' in d.keys
    0
    >>> 1 in d.keys
    1
    >>> [v for v in d.keys]
    [3, 1, 2]
    >>> d.keys.sort()
    >>> d.keys
    [1, 2, 3]
    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True)
    >>> d.keys[::-1] = [1, 2, 3]
    >>> d
    SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
    >>> d.keys[:2]
    [3, 2]
    >>> d.keys[:2] = [1, 3]
    Traceback (most recent call last):
    KeyError: 'Keylist is not the same as current keylist.'

    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
    >>> d
    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
    >>> d.values
    [2, 3, 4]
    >>> d.values()
    [2, 3, 4]
    >>> d.setvalues((4, 3, 2))
    >>> d
    SequenceOrderedDict([(1, 4), (2, 3), (3, 2)])
    >>> d.values[::-1]
    [2, 3, 4]
    >>> d.values[0]
    4
    >>> d.values[-2]
    3
    >>> del d.values[0]
    Traceback (most recent call last):
    TypeError: Can't delete items from values
    >>> d.values[::2] = [2, 4]
    >>> d
    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
    >>> 7 in d.values
    0
    >>> len(d.values)
    3
    >>> [val for val in d.values]
    [2, 3, 4]
    >>> d.values[-1] = 2
    >>> d.values.count(2)
    2
    >>> d.values.index(2)
    0
    >>> d.values[-1] = 7
    >>> d.values
    [2, 3, 7]
    >>> d.values.reverse()
    >>> d.values
    [7, 3, 2]
    >>> d.values.sort()
    >>> d.values
    [2, 3, 7]
    >>> d.values.append('anything')
    Traceback (most recent call last):
    TypeError: Can't append items to values
    >>> d.values = (1, 2, 3)
    >>> d
    SequenceOrderedDict([(1, 1), (2, 2), (3, 3)])
    
    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
    >>> d
    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
    >>> d.items()
    [(1, 2), (2, 3), (3, 4)]
    >>> d.setitems([(3, 4), (2 ,3), (1, 2)])
    >>> d
    SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
    >>> d.items[0]
    (3, 4)
    >>> d.items[:-1]
    [(3, 4), (2, 3)]
    >>> d.items[1] = (6, 3)
    >>> d.items
    [(3, 4), (6, 3), (1, 2)]
    >>> d.items[1:2] = [(9, 9)]
    >>> d
    SequenceOrderedDict([(3, 4), (9, 9), (1, 2)])
    >>> del d.items[1:2]
    >>> d
    SequenceOrderedDict([(3, 4), (1, 2)])
    >>> (3, 4) in d.items
    1
    >>> (4, 3) in d.items
    0
    >>> len(d.items)
    2
    >>> [v for v in d.items]
    [(3, 4), (1, 2)]
    >>> d.items.count((3, 4))
    1
    >>> d.items.index((1, 2))
    1
    >>> d.items.index((2, 1))
    Traceback (most recent call last):
    ValueError: list.index(x): x not in list
    >>> d.items.reverse()
    >>> d.items
    [(1, 2), (3, 4)]
    >>> d.items.reverse()
    >>> d.items.sort()
    >>> d.items
    [(1, 2), (3, 4)]
    >>> d.items.append((5, 6))
    >>> d.items
    [(1, 2), (3, 4), (5, 6)]
    >>> d.items.insert(0, (0, 0))
    >>> d.items
    [(0, 0), (1, 2), (3, 4), (5, 6)]
    >>> d.items.insert(-1, (7, 8))
    >>> d.items
    [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)]
    >>> d.items.pop()
    (5, 6)
    >>> d.items
    [(0, 0), (1, 2), (3, 4), (7, 8)]
    >>> d.items.remove((1, 2))
    >>> d.items
    [(0, 0), (3, 4), (7, 8)]
    >>> d.items.extend([(1, 2), (5, 6)])
    >>> d.items
    [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)]
    """

    def __init__(self, init_val=(), strict=True):
        OrderedDict.__init__(self, init_val, strict=strict)
        self._keys = self.keys
        self._values = self.values
        self._items = self.items
        self.keys = _keys(self)
        self.values = _values(self)
        self.items = _items(self)
        self._att_dict = {'keys': self.setkeys,
            'items': self.setitems,
            'values': self.setvalues
            }

    def __setattr__(self, name, value):
        """Protect keys, items, and values."""
        if not '_att_dict' in self.__dict__:
            object.__setattr__(self, name, value)
        else:
            try:
                fun = self._att_dict[name]
            except KeyError:
                OrderedDict.__setattr__(self, name, value)
            else:
                fun(value)

if __name__ == '__main__':
    # run the code tests in doctest format
    import doctest
    m = sys.modules.get('__main__')
    globs = m.__dict__.copy()
    globs.update({
        'INTP_VER': INTP_VER,
    })
    doctest.testmod(m, globs=globs)

"""
    ISSUES
    ======
    
    Slicing doesn't work in Python 2.2. This is because in 2.2, you can't index
    a sequence with a slice object. Could be implemented with ``operator``
    slicing functions (which don't support extended slices).
    
    TODO
    ====
    
    Addition (``__add__``) ? (This would just be syntactic sugar for
    ``update``)
    
    Implement the Python 2.4 arguments (``key`` and ``reverse``) for ``sort``
    for Python 2.2 and 2.3 ? (So the interface is stable)
    
    Add sequence methods ``move`` and ``rename`` ? (To change the name of a key
    at a specific index, and change the index of a key from one position to
    another)
    
    Allow assignment to keys (in ``SequenceOrderedDict``) to rename a key ?
    
    Do I *need* to implement ``__cmp__`` - I don't *think* so ?
    
    Allow slice assignment to ``OrderedDict`` (and `possibly
    ``SequenceOrderedDict.items``) from list of tuples as well as from an
    ``OrderedDict`` ?
    
    CHANGELOG
    =========
    
    2005/12/17
    ----------
    
    You can now test for equality and inequality with objects (except for
    dictionaries for which it is undefined). This allows you to do tests like :
    ::
    
        OrderedDict() == False
    
    Added the ``strict`` keyword, which raises a ``ValueError`` if you do slice
    assignment with keys that are already in the dictionary.
    
    Assignment to ``keys`` in ``SequenceOrderedDict`` is now only for
    re-ordering the keys.
    
    Fixed bug where slice assignment to ``keys`` could lose information. (and
    optimised by slicing ranges to get the indexes we are assigning to instead
    of indexing each key).
    
    You change keys, items, and values through new methods ``setkeys``,
    ``setitems``, and ``setvalues`` methods.
    
    Minor changes, thanks to Christoph Zwerschke for suggestions.
    
    Added ``__deepcopy__`` method (previously deepcopy failed).
    
    CHanged use of ``slice`` to ``types.SliceType`` for Python 2.2.
    
    0.2.1
    
    
    2005/12/02
    ----------
    
    Fixed bugs in ``__getattr__`` and ``popitem``
    
    Optimisation in ``OrderedDict.__init__`` when creating an instance from an
    ``OrderedDict``
    
    Changed ``FancyODict`` to ``SequenceOrderedDict``
    
    Implemented new ``__repr__``
    
    0.2.0
    
    2005/12/01
    ----------
    
    Added index to ``OrderedDict.popitem``
    
    2005/11/30
    ----------
    
    Implemented ``FancyODict``, which has ``keys``, ``items``, ``values`` as
    custom, callable, sequence objects.
    
    2005/11/26
    ----------
    
    By Michael Foord - from suggestions on comp.lang.python
    
    Hidden the ``sequence`` attribute
    
    ``items``, ``keys``, ``values`` can now take a list to replace the current
    keys, values, or items
    
    Implemented slicing (including deleting a slice and assigning to a slice)
    
    Implemented sequence methods ``sort``, ``reverse``, ``insert``, ``index``
    
    2005/09/10
    ----------
    
    By Nicola Larosa, based on code from Tim Wegener
      <twegener AT radlogic DOT com DOT au>
    
    Create itervalues and iteritems without creating the list up-front
    
    Added doctests for iter methods, and others.
    
    Optimized __setitem__ to be O(1) rather than O(N)
    
    Removed redefined methods that did not alter dict method behaviour,
      related doctests moved to the class docstring
    
    Added support for sequences of (key, value) pairs to update
    
    Removed redundant condition from __eq__
    
    Removed incorrect implementation of __str__
    
    2005/08/28
    ----------
    
    By Michael Foord
    
    Added __all__
    
    More than two arguments to ``pop`` now raises an error
    
    Version 0.1.0 finalised
    
    2005/08/13
    ----------
    
    By Nicola Larosa
    
    Added doctests everywhere, fixed much part of implementation
    
    Added comments at top, other doc vars
    
    2005/08/01
    ----------
    
    By Michael Foord
    
    Type tests changed to isinstance
    
    _keys changed to sequence attribute
    
    Allowed creating a dictionary by passing keyword arguments
    
    Shortened __repr__
    
    Fixed bug in popitem
    
    Other minor changes
"""
# -*- coding: utf-8 -*-
# 2005/12/06
# Version 0.2.4
# pathutils.py
# Functions useful for working with files and paths.
# http://www.voidspace.org.uk/python/recipebook.shtml#utils

# Copyright Michael Foord 2004
# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# For information about bugfixes, updates and support, please join the Pythonutils mailing list.
# http://groups.google.com/group/pythonutils/
# Comments, suggestions and bug reports welcome.
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# E-mail fuzzyman@voidspace.org.uk
from __future__ import generators
"""
This module contains convenience functions for working with files and paths.
"""

__version__ = '0.2.4'


import os
import sys
import time

__all__ = (
    'readlines',
    'writelines',
    'readbinary',
    'writebinary',
    'readfile',
    'writefile',
    'tslash',
    'relpath',
    'splitall',
    'walkfiles',
    'walkdirs',
    'walkemptydirs',
    'formatbytes',
    'fullcopy',
    'import_path',
    'onerror',
    'get_main_dir',
    'main_is_frozen',
    'Lock',
    'LockError',
    'LockFile',
    '__version__',
    )

######################################
# Functions to read and write files in text and binary mode.

def readlines(filename):
    """Passed a filename, it reads it, and returns a list of lines. (Read in text mode)"""
    filehandle = open(filename, 'r')
    outfile = filehandle.readlines()
    filehandle.close()
    return outfile

def writelines(filename, infile, newline=False):
    """
    Given a filename and a list of lines it writes the file. (In text mode)
    
    If ``newline`` is ``True`` (default is ``False``) it adds a newline to each
    line.
    """
    filehandle = open(filename, 'w')
    if newline:
        infile = [line + '\n' for line in infile]
    filehandle.writelines(infile)
    filehandle.close()

def readbinary(filename):
    """Given a filename, read a file in binary mode. It returns a single string."""
    filehandle = open(filename, 'rb')
    thisfile = filehandle.read()
    filehandle.close()
    return thisfile

def writebinary(filename, infile):
    """Given a filename and a string, write the file in binary mode. """
    filehandle = open(filename, 'wb')
    filehandle.write(infile)
    filehandle.close()

def readfile(filename):
    """Given a filename, read a file in text mode. It returns a single string."""
    filehandle = open(filename, 'r')
    outfile = filehandle.read()
    filehandle.close()
    return outfile

def writefile(filename, infile):
    """Given a filename and a string, write the file in text mode."""
    filehandle = open(filename, 'w')
    filehandle.write(infile)
    filehandle.close()
    
####################################################################
# Some functions for dealing with paths

def tslash(apath):
    """
    Add a trailing slash (``/``) to a path if it lacks one.
    
    It doesn't use ``os.sep`` because you end up in trouble on windoze, when you
    want separators for URLs.
    """
    if apath and apath != '.' and not apath.endswith('/') and not apath.endswith('\\'):
        return apath + '/'
    else:
        return apath

def relpath(origin, dest):
    """
    Return the relative path between origin and dest.
    
    If it's not possible return dest.
    
    
    If they are identical return ``os.curdir``
    
    Adapted from `path.py <http://www.jorendorff.com/articles/python/path/>`_ by Jason Orendorff. 
    """
    origin = os.path.abspath(origin).replace('\\', '/')
    dest = os.path.abspath(dest).replace('\\', '/')
    #
    orig_list = splitall(os.path.normcase(origin))
    # Don't normcase dest!  We want to preserve the case.
    dest_list = splitall(dest)
    #
    if orig_list[0] != os.path.normcase(dest_list[0]):
        # Can't get here from there.
        return dest
    #
    # Find the location where the two paths start to differ.
    i = 0
    for start_seg, dest_seg in zip(orig_list, dest_list):
        if start_seg != os.path.normcase(dest_seg):
            break
        i += 1
    #
    # Now i is the point where the two paths diverge.
    # Need a certain number of "os.pardir"s to work up
    # from the origin to the point of divergence.
    segments = [os.pardir] * (len(orig_list) - i)
    # Need to add the diverging part of dest_list.
    segments += dest_list[i:]
    if len(segments) == 0:
        # If they happen to be identical, use os.curdir.
        return os.curdir
    else:
        return os.path.join(*segments).replace('\\', '/')

def splitall(loc):
    """
    Return a list of the path components in loc. (Used by relpath_).
    
    The first item in the list will be  either ``os.curdir``, ``os.pardir``, empty,
    or the root directory of loc (for example, ``/`` or ``C:\\).
    
    The other items in the list will be strings.
        
    Adapted from *path.py* by Jason Orendorff.
    """
    parts = []
    while loc != os.curdir and loc != os.pardir:
        prev = loc
        loc, child = os.path.split(prev)
        if loc == prev:
            break
        parts.append(child)
    parts.append(loc)
    parts.reverse()
    return parts

#######################################################################
# a pre 2.3 walkfiles function - adapted from the path module by Jason Orendorff

join = os.path.join
isdir = os.path.isdir
isfile = os.path.isfile

def walkfiles(thisdir):
    """
    walkfiles(D) -> iterator over files in D, recursively. Yields full file paths.
    
    Adapted from path.py by Jason Orendorff.
    """
    for child in os.listdir(thisdir):
        thischild = join(thisdir, child)
        if isfile(thischild):
            yield thischild
        elif isdir(thischild):
            for f in walkfiles(thischild):
                yield f
                
def walkdirs(thisdir):
    """
    Walk through all the subdirectories in a tree. Recursively yields directory
    names (full paths).
    """
    for child in os.listdir(thisdir):
        thischild = join(thisdir, child)
        if isfile(thischild):
            continue
        elif isdir(thischild):
            for f in walkdirs(thischild):
                yield f
            yield thischild

def walkemptydirs(thisdir):
    """
    Recursively yield names of *empty* directories.
    
    These are the only paths omitted when using ``walkfiles``.
    """
    if not os.listdir(thisdir):
        # if the directory is empty.. then yield it
        yield thisdir   
    for child in os.listdir(thisdir):
        thischild = join(thisdir, child)
        if isdir(thischild):
            for emptydir in walkemptydirs(thischild):
                yield emptydir

###############################################################
# formatbytes takes a filesize (as returned by os.getsize() )
# and formats it for display in one of two ways !!

def formatbytes(sizeint, configdict=None, **configs):
    """
    Given a file size as an integer, return a nicely formatted string that
    represents the size. Has various options to control it's output.
    
    You can pass in a dictionary of arguments or keyword arguments. Keyword
    arguments override the dictionary and there are sensible defaults for options
    you don't set.
    
    Options and defaults are as follows :
    
    *    ``forcekb = False`` -         If set this forces the output to be in terms
    of kilobytes and bytes only.
    
    *    ``largestonly = True`` -    If set, instead of outputting 
        ``1 Mbytes, 307 Kbytes, 478 bytes`` it outputs using only the largest 
        denominator - e.g. ``1.3 Mbytes`` or ``17.2 Kbytes``
    
    *    ``kiloname = 'Kbytes'`` -    The string to use for kilobytes
    
    *    ``meganame = 'Mbytes'`` - The string to use for Megabytes
    
    *    ``bytename = 'bytes'`` -     The string to use for bytes
    
    *    ``nospace = True`` -        If set it outputs ``1Mbytes, 307Kbytes``, 
        notice there is no space.
    
    Example outputs : ::
    
        19Mbytes, 75Kbytes, 255bytes
        2Kbytes, 0bytes
        23.8Mbytes
    
    .. note::
    
        It currently uses the plural form even for singular.
    """
    defaultconfigs = {  'forcekb' : False,
                        'largestonly' : True,
                        'kiloname' : 'Kbytes',
                        'meganame' : 'Mbytes',
                        'bytename' : 'bytes',
                        'nospace' : True}
    if configdict is None:
        configdict = {}
    for entry in configs:
        # keyword parameters override the dictionary passed in
        configdict[entry] = configs[entry]
    #
    for keyword in defaultconfigs:
        if not configdict.has_key(keyword):
            configdict[keyword] = defaultconfigs[keyword]
    #
    if configdict['nospace']:
        space = ''
    else:
        space = ' '
    #
    mb, kb, rb = bytedivider(sizeint)
    if configdict['largestonly']:
        if mb and not configdict['forcekb']:
            return stringround(mb, kb)+ space + configdict['meganame']
        elif kb or configdict['forcekb']:
            if mb and configdict['forcekb']:
                kb += 1024*mb
            return stringround(kb, rb) + space+ configdict['kiloname']
        else:
            return str(rb) + space + configdict['bytename']
    else:
        outstr = ''
        if mb and not configdict['forcekb']:
            outstr = str(mb) + space + configdict['meganame'] +', '
        if kb or configdict['forcekb'] or mb:
            if configdict['forcekb']:
                kb += 1024*mb
            outstr += str(kb) + space + configdict['kiloname'] +', '
        return outstr + str(rb) + space + configdict['bytename']

def stringround(main, rest):
    """
    Given a file size in either (mb, kb) or (kb, bytes) - round it
    appropriately.
    """
    # divide an int by a float... get a float
    value = main + rest/1024.0
    return str(round(value, 1))

def bytedivider(nbytes):
    """
    Given an integer (probably a long integer returned by os.getsize() )
    it returns a tuple of (megabytes, kilobytes, bytes).
    
    This can be more easily converted into a formatted string to display the
    size of the file.
    """
    mb, remainder = divmod(nbytes, 1048576)
    kb, rb = divmod(remainder, 1024)
    return (mb, kb, rb)

########################################

def fullcopy(src, dst):
    """
    Copy file from src to dst.
    
    If the dst directory doesn't exist, we will attempt to create it using makedirs.
    """
    import shutil
    if not os.path.isdir(os.path.dirname(dst)):
        os.makedirs(os.path.dirname(dst))
    shutil.copy(src, dst)    

#######################################

def import_path(fullpath, strict=True):
    """
    Import a file from the full path. Allows you to import from anywhere,
    something ``__import__`` does not do.
    
    If strict is ``True`` (the default), raise an ``ImportError`` if the module
    is found in the "wrong" directory.
    
    Taken from firedrop2_ by `Hans Nowak`_
    
    .. _firedrop2: http://www.voidspace.org.uk/python/firedrop2/
    .. _Hans Nowak: http://zephyrfalcon.org
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.insert(0, path)
    try:
        module = __import__(filename)
    except ImportError:
        del sys.path[0]
        raise
    del sys.path[0]
    #
    if strict:
        path = os.path.split(module.__file__)[0]
        # FIXME: doesn't *startswith* allow room for errors ?
        if not fullpath.startswith(path):
            raise ImportError, "Module '%s' found, but not in '%s'" % (
                  filename, fullpath)
    #
    return module

##############################################################################
# These functions get us our directory name
# Even if py2exe or another freeze tool has been used

def main_is_frozen():
    """Return ``True`` if we're running from a frozen program."""
    import imp
    return (
        # new py2exe
        hasattr(sys, "frozen") or
        # tools/freeze
        imp.is_frozen("__main__"))

def get_main_dir():
    """Return the script directory - whether we're frozen or not."""
    if main_is_frozen():
        return os.path.abspath(os.path.dirname(sys.executable))
    return os.path.abspath(os.path.dirname(sys.argv[0]))

##############################

def onerror(func, path, exc_info):
    """
    Error handler for ``shutil.rmtree``.

    If the error is due to an access error (read only file)
    it attempts to add write permission and then retries.

    If the error is for another reason it re-raises the error.
    
    Usage : ``shutil.rmtree(path, onerror=onerror)``
    """
    import stat
    if not os.access(path, os.W_OK):
        # Is the error an access error ?
        os.chmod(path, stat.S_IWUSR)
        func(path)
    else:
        raise

##########################################################
# A set of object for providing simple, cross-platform file locking

class LockError(IOError):
    """The generic error for locking - it is a subclass of ``IOError``."""

class Lock(object):
    """A simple file lock, compatible with windows and Unixes."""

    def __init__(self, filename, timeout=5, step=0.1):
        """
        Create a ``Lock`` object on file ``filename``
        
        ``timeout`` is the time in seconds to wait before timing out, when
        attempting to acquire the lock.
        
        ``step`` is the number of seconds to wait in between each attempt to
        acquire the lock.
        
        """
        self.timeout = timeout
        self.step = step
        self.filename = filename
        self.locked = False

    def lock(self, force=True):
        """
        Lock the file for access by creating a directory of the same name (plus
        a trailing underscore).
        
        The file is only locked if you use this class to acquire the lock
        before accessing.
        
        If ``force`` is ``True`` (the default), then on timeout we forcibly
        acquire the lock.
        
        If ``force`` is ``False``, then on timeout a ``LockError`` is raised.
        """
        if self.locked:
            raise LockError('%s is already locked' % self.filename)
        t = 0
        while t < self.timeout:
            t += self.step
            try:
                os.mkdir(self._mungedname())
            except os.error, err:
                time.sleep(self.step)
            else:
                self.locked = True
                return
        if force:
            self.locked = True
        else:
            raise LockError('Failed to acquire lock on %s' % self.filename)

    def unlock(self, ignore=True):
        """
        Release the lock.
        
        If ``ignore`` is ``True`` and removing the lock directory fails, then
        the error is surpressed. (This may happen if the lock was acquired
        via a timeout.)
        """
        if not self.locked:
            raise LockError('%s is not locked' % self.filename)
        self.locked = False
        try:
            os.rmdir(self._mungedname())
        except os.error, err:
            if not ignore:
                raise LockError('unlocking appeared to fail - %s' %
                    self.filename)

    def _mungedname(self):
        """
        Override this in a subclass if you want to change the way ``Lock`` 
        creates the directory name.
        """
        return self.filename + '_'

    def __del__(self):
        """Auto unlock when object is deleted."""
        if self.locked:
            self.unlock()

class LockFile(Lock):
    """
    A file like object with an exclusive lock, whilst it is open.
    
    The lock is provided by the ``Lock`` class, which creates a directory
    with the same name as the file (plus a trailing underscore), to indicate
    that the file is locked.
    
    This is simple and cross platform, with some limitations :
    
        * Unusual process termination could result in the directory
          being left.
        * The process acquiring the lock must have permission to create a
          directory in the same location as the file.
        * It only locks the file against other processes that attempt to
          acquire a lock using ``LockFile`` or ``Lock``.
    """

    def __init__(self, filename, mode='r', bufsize=-1, timeout=5, step=0.1,
        force=True):
        """
        Create a file like object that is locked (using the ``Lock`` class)
        until it is closed.
        
        The file is only locked against another process that attempts to
        acquire a lock using ``Lock`` (or ``LockFile``).
        
        The lock is released automatically when the file is closed.
        
        The filename, mode and bufsize arguments have the same meaning as for
        the built in function ``open``.
        
        The timeout and step arguments have the same meaning as for a ``Lock``
        object.
        
        The force argument has the same meaning as for the ``Lock.lock`` method.
        
        A ``LockFile`` object has all the normal ``file`` methods and
        attributes.
        """
        Lock.__init__(self, filename, timeout, step)
        # may raise an error if lock is ``False``
        self.lock(force)
        # may also raise an error
        self._file = open(filename, mode, bufsize)

    def close(self, ignore=True):
        """
        close the file and release the lock.
        
        ignore has the same meaning as for ``Lock.unlock``
        """
        self._file.close()
        self.unlock(ignore)

    def __getattr__(self, name):
        """delegate appropriate method/attribute calls to the file."""
        if name not in self.__dict__:
            return getattr(self._file, name)
        else:
            return self.__dict__[self, name]

    def __setattr__(self, name, value):
        """Only allow attribute setting that don't clash with the file."""
        if not '_file' in self.__dict__:
            Lock.__setattr__(self, name, value)
        elif hasattr(self._file, name):
            return setattr(self._file, name, value)
        else:
            Lock.__setattr__(self, name, value)

    def __del__(self):
        """Auto unlock (and close file) when object is deleted."""
        if self.locked:
            self.unlock()
            self._file.close()

"""

Changelog
=========

2005/12/06      Version 0.2.4
-----------------------------

Fixed bug in ``onerror``. (Missing stat import)


2005/11/26      Version 0.2.3
-----------------------------

Added ``Lock``, ``LockError``, and ``LockFile``

Added ``__version__``


2005/11/13      Version 0.2.2
-----------------------------

Added the py2exe support functions.

Added ``onerror``.


2005/08/28      Version 0.2.1
-----------------------------

* Added ``import_path``
* Added ``__all__``
* Code cleanup


2005/06/01      Version 0.2.0
-----------------------------

Added ``walkdirs`` generator.


2005/03/11      Version 0.1.1
-----------------------------

Added rounding to ``formatbytes`` and improved ``bytedivider`` with ``divmod``.

Now explicit keyword parameters override the ``configdict`` in ``formatbytes``.


2005/02/18      Version 0.1.0
-----------------------------

The first numbered version.
"""
# setup.py
# Install script for ConfigObj
# Copyright (C) 2005 Michael Foord, Mark Andrews, Nicola Larosa
# E-mail: fuzzyman AT voidspace DOT org DOT uk
#         mark AT la-la DOT com
#         nico AT tekNico DOT net

# This software is licensed under the terms of the BSD license.
# http://www.voidspace.org.uk/python/license.shtml
# Basically you're free to copy, modify, distribute and relicense it,
# So long as you keep a copy of the license with it.

# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# For information about bugfixes, updates and support, please join the
# Rest2Web mailing list:
# http://lists.sourceforge.net/lists/listinfo/rest2web-develop
# Comments, suggestions and bug reports welcome.
"""
**setup.py** for ``configobj`` and ``validate`` modules.
"""

if __name__ == '__main__':
    import sys
    from distutils.core import setup
    from configobj import __version__ as VERSION

    NAME = 'configobj'
    MODULES = 'configobj', 'validate'
    DESCRIPTION = 'Config file reading, writing, and validation.'
    URL = 'http://www.voidspace.org.uk/python/configobj.html'
    LICENSE = 'BSD'
    PLATFORMS = ["Platform Independent"]

    if sys.version < '2.2.3':
        from distutils.dist import DistributionMetadata
        DistributionMetadata.classifiers = None
        DistributionMetadata.download_url = None

    setup(name= NAME,
          version= VERSION,
          description= DESCRIPTION,
          license = LICENSE,
          platforms = PLATFORMS,
          author= 'Michael Foord & Nicola Larosa',
          author_email= 'fuzzyman@voidspace.org.uk',
          url= URL,
          py_modules = MODULES,
         )
# 2005/01/06
# v2.1.0

# A flexible object to redirect standard output and standard error
# Allows logging to a file and to set a level of verbosity

# Copyright Michael Foord, 2004.
# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# For information about bugfixes, updates and support, please join the Pythonutils mailing list.
# http://groups.google.com/group/pythonutils/
# Comments, suggestions and bug reports welcome.
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# E-mail fuzzyman@voidspace.org.uk

"""
StandOut - the Flexible Output Object (FOO !)
Adds optional logging to a file and setting of verbosity levels to the stdout stream
This means that, for the most part, standard print statments can be used throughout
your program and StandOut handles the rest.

Your user can choose a 'verbosity' level (how much information they want to receive), you give your messages
a priority level, and only messages with a high enough priority are actually displayed.

A simple way of implementing varying degrees of verbosity.
Additionally the output can be captured to a log file with no extra work.
(simply pass in a filename when you craete the object and anything printed is sent to the file as well)

StandOut can now be used with sys.stderr as well as sys.stdout.
This includes logging both sys.stdout *and* sys.stderr to the same file.
See the sys.stderr section at the bottom of this.

SIMPLE USAGE
(also see the tests which illustrate usage).

stout = StandOut(verbosity=verbositylevel)

or to log to a file :
stout = StandOut(filename='log.txt')

The verbosity level can be changed at any time by setting stout.verbosity :
stout.verbosity = 6

The priority of messages defaults to 5. This can be changed by setting
stout.priority = 6 *or*
print '&priority-6;'

The priority of an individual line can be set by *starting* the line with a priority marker :
print '&priority-6;This text has a priority 6.'
*or* by using the stout.write() method with a priority value:
stout.write('This text has a priority 6.\n', 6)
(notice you must add the '\n' when using the stout.write method.)

Only messages with a priority equal to or greater than the current verbosity level will be printed.
e.g. if  stout.verbosity = 6
(or the stout object was created using stout=StandOut(verbosity=6) )
Only messages with a priority of 6 or above will be printed.

stout.write('This won't get printed\n, 5)
print '&priority-4;Nor will this'
stout.write('But this will\n', 6)
print '&priority-7;And so will this'

If for *any* reason you want to *actually* print a '&priority-n' marker at the start of a line
then you can escape it with a '&priority-e;' :
print '&priority-e;&priority-1;'
will actually print :
&priority-1;

StandOut will log to a file as well.
Set this by passing in a filename=filename keyword when you create the object *or* by setting stout.filename at any time.
The file has it's own priority, stout.file_verbosity.
Again this can be set when the object is created and/or changed at any time. See the full docs below.

This means your user can set a verbosity level (at the command line probably), you give each message a priority
setting and just use normal print statements in your program.
Only messages above your user's setting are actually displayed.
You can also set the log file to have a different priority threshhold to what is printed to the screen.
(So either less or more is logged to the file than is displayed at runtime.)

You can also pass in another function which can be used to display messages with (e.g. to a GUI window or whatever).
It also has it's own priority setting.

Any output method can be silenced by setting it to 0
All output can be silenced by setting the priority to 0

The stdout stream can be restored and any log file closed by calling stout.close()

verbosity = 1 is the highest
verbosity = 9 is the lowest     (only messages of priority 9 are printed)
verbosity = 0 is special - it switches off printing altogether

LIST OF KEYWORDS AND METHODS

StandOut Possible keyword arguments (with defaults shown) are :
(The following keywords also map to attributes of the StandOut object which can be read or set)
priority = 5
verbosity = 5
filename = None
file_verbosity = 5
file_mode = 'w'
print_fun = None
printfun_verbosity = 5

Keyword arguments should either be passed in as a dictionary *or* as keywords when the object is created.
If a dictionary is passed in, any other keywords will be ignored.
Any missing keywords will use the defaults.

Methods ( stout = StandOut() ):
stout.close()
stout.write(line, priority)
stout.set_print(function)
stout.setall(verbosity)

the original stdout can be reached using :
stout.output.write()

**NOTE** normal print statements make two calls to stdout.write(). Once for the text you are printing and another for the
trailing '\n' or ' '. StandOut captures this to make sure the trailing '\n' or ' ' is printed at the same priority
as the original line. This means you shouldn't use stout.write(line) where line uses the '&priority-n;' markers.
(Because stout.write(line) only makes one call, not two).
Either call stout.write(line, priority) to set a priority for that line.
*or* set stout.priority directly.



EXPLANATION OF KEYWORDS AND METHODS

priority = 5
This sets the priority for messages.
If priority is 5 - then only output methods with a 'verbosity' of 5 or lower will display them.
This value can later be set by adjusting the stout.priority attribute or using the priority markers.

verbosity = 5
This is the verbosity level for messages to be printed to the screen.
If the verbosity is 5 then only messages with a priority of 5 or higher will be sent to the screen.
(Like a normal print statement).
You can nadjust this at stout.verbosity

filename = None
If you pass in a filename when you create the object it will be used as a logfile.
It has it's own 'verbosity' level called 'file_verbosity'.
If you don't pass in a filename, you can later add one by setting stout.filename
Changing stout.filename after you have already set one is a bad thing to do :-)

file_verbosity = 5
This is the verbosity level of the log file.
Only messages with a priority higher than this will be sent to the logfile.

print_fun = None
If you pass in a function (that takes one parameter - the line to be printed) this will be used to print as well.
The function *isn't* stored at stout.print_fun - this value is just set to True to say we have a function.
This could be used for displaying to the output window of a GUI, for example.
If you want to pass in a function after obect creation then use the stout.set_print(function) method.
You musn't have print statements in your function or you will get stuck in a loop (call stout.output.write(line) instead)

printfun_verbosity = 5
Any function you pass in also has it's own verbosity setting - printfun_verbosity.

stream = 'output'
By default StandOut will divert the sys.stdout stream. Set to 'error' to divert the sys.stderr

share = False
You can divert both sys.stdout and sys.stderr. You can log both to the same file.
Set a filename for your sys.stdout object and set share = True for your sys.stderr object.
Any lines sent to sys.stderr will have a prefix attached to them. See 'error_marker'

error_marker = '[err] '
This is the marker put before every line logged from sys.stderr.
It only applies if share is on - this means both streams are logged to the same file.

stout.close()
When your program has finished with the obejct it should call stout.close() which restores sy.stdout and
closes any logfile we have been using.

stout.write(line, priority)
This can be used as an alternative way of specifying a priority for an individual line.
It leaves stout.priority unaffected.
Any calls to stout.write must have '\n' at the end if you want it to end with a newline.
If you don't specify a priority then it behaves like sys.stdout.write would.
Don't use priority markers with this and method and you can't use  priority = 0 (the priority setting will be ignored)

stout.set_print(function)
This is used to pass in an additional printing function after the object has been created.

stout.setall(verbosity)
Thisis a quick way of changing the verbosity for all three output methods.

Setting verbosity, file_verbosity or printfun_verbosity to 0 disables that ouput method.
Setting priority to 0 switches off all output.

If you want to print to stdout directly and bypass the stout object for any reason - it is saved at stout.output
Calls to stout.output.write(line) have the same effect that sys.stdout.write(line) would have had.

PRIORITY MARKERS

As well as directly setting stout.priority and using stout.write(line, priority)
You can set the priority of a individual  line *or* change the general priority setting just using print statements.
This is using 'priority markers'.

print '&priority-n;'  # sets the priority to n, where n is 0-9
print '&priority-n;The stuff to print'      # sets the priority of just that line to n

If you actually want to print '&priority-n;' at the start of a line then you should escape it by putting '&priority-e;'
in front of it. '&priority-e;' can also be escaped in the same way !

Don't use priority markers if you are making direct calls to stout.write()
use stout.write(line, priority) to set the priority of an individual line
or alter stout.priority to adjust the general priority.


sys.stderr

StandOut can now be used to divert sys.stderr as well as sys.stdout.
To create an output object that does for sys.stderr *exactly* the same as we would do for sys.stdout use :
stout2 = StandOut(stream='error')

It can log to a file and has all the properties that we had for sys.stdout.
If you wanted to log to the *same* file as you are using for sys.stdout you *can't* just pass it the same filename.
The two objects would both try to have a write lock on the same file.

What you do is pass the 'share' keyword to the error object when you create it :

stout = StandOut(filename='log.txt')
stout2 = StandOut(stream='error', share=True)

Anything sent to sys.stdout *or* sys.stderr will now be logged in the 'log.txt' file.
Every line sent to sys.stderr will be prefixed with '[err] ' which is the default error marker.
You can adjust this with the 'error_marker' keyword.

stout2 = StandOut(stream='error', share=True, error_marker='**ERROR** ')

"""

__all__ = ['StandOut']

import sys

class StandOut:
    
    stdout = None
    stderr = None
    
    def __init__(self, indict=None, **keywargs):
        """StandOut - the Flexible Output Object (FOO !)"""
        if indict is None:
            indict = {}
        #
        defaults = {
            'priority': 5,
            'verbosity': 5,
            'filename': None,
            'file_verbosity': 5,
            'file_mode': 'w',
            'print_fun': None,
            'printfun_verbosity': 5 ,
            'stream': 'output',
            'share': False,
            'error_marker': '[err] '
            }
        #
        if not indict:
            indict = keywargs
        for value in defaults:
            if not indict.has_key(value):
                indict[value] = defaults[value]
        #
        if indict['stream'].lower() == 'error':
            self.output = sys.stderr
            sys.stderr = StandOut.stderr = self
            self.stream = indict['stream'].lower()
        else:       
            self.output = sys.stdout
            sys.stdout = StandOut.stdout = self
            self.stream = indict['stream'].lower()

        self.filename = indict['filename']
        if self.filename:
            self.filehandle = file(self.filename, indict['file_mode'])
        else:
            self.filehandle = None

        self.file_mode = indict['file_mode']
        self.share = indict['share']
        self.err_marker = indict['error_marker']
        self.done_linefeed = True

        self.priority = indict['priority']              # current message priority
        self.file_verbosity = indict['file_verbosity']            # file output threshold
        self.verbosity = indict['verbosity']                    # stdout threshhold
        self.printfun_verbosity = indict['printfun_verbosity']    # print_fun threshold

        if indict['print_fun']:     # set up the print_fun if we have been given one
            self.print_fun = True
            self.thefun = [indict['print_fun']]
        else:
            self.print_fun = False

        self.markers = {}
        for num in range(10):                               # define the markers
            thismarker = '&priority-' + str(num) + ';'
            self.markers[thismarker] = num
        self.escapemarker = '&priority-e;'

        self.skip = 0
        self._lastpriority = 0
            
#########################################################################
        # public methods - available as methods of any instance of StandOut you create
        
    def write(self, line, priority = 0):
        """Print to any of the output methods we are using.
        Capture lines which set priority."""

        if self.skip:           # if the last line was a priority marker then self.skip is set and we should miss the '\n' or ' ' that is sent next
            self.skip = 0
            return
        
        if not priority:                
            if self._lastpriority:          # if the last line had a priority marker at the start of it, then the '\n' or ' ' that is sent next should have the same priority
                priority = self._lastpriority
                self._lastpriority = 0
            else:
                priority = self.priority

        if line in self.markers:                # if the line is a priority marker
            self.skip = 1                       # either a '\n' or a ' ' will now be sent  to sys.stdout.write() by print
            self.priority = self.markers[line]
            return
        
        if line[:12] in self.markers:           # if the line starts with a priority marker
            priority = int(line[10])            # the priority of this line is at position 10
            self._lastpriority = priority       # set this value so that the '\n' or ' ' that follows also has the same priority
            line = line[12:]                    # chop off the marker
        elif line[:12] == self.escapemarker:       
            line = line[12:]                    # this just removes our 'escape marker'
            
        if not priority:                    # if priority is set to 0 then we mute all output
            return

        if self.filename and not self.filehandle:                           # if a filename has been added since we opened
            self.filehandle = file(self.filename, self.file_mode)
        if self.filehandle and self.file_verbosity and priority >= self.file_verbosity:      # if we have a file and file_verbosity is high enough to output
            self.filehandle.write(line)
            
        if self.verbosity and priority >= self.verbosity:              # if verbosity is set high enough we print
            if self.share and self.stream == 'error' and hasattr(StandOut.stdout, 'filename'):      # if we are the error stream *and* share is on *and* stdout has a filename attribute..
                if self.done_linefeed:
                    StandOut.stdout.filehandle.write(self.err_marker)
                    self.done_linefeed = False
                if line.endswith('\n'):
                    self.done_linefeed = True
                    line = line[:-1]
                line = line.replace('\n', '\n' + self.err_marker)
                if self.done_linefeed:
                    line = line + '\n'
                StandOut.stdout.filehandle.write(line)                  # if 'share' is on we log to stdout file as well as print
#            StandOut.stdout.output.write('hello')
            self.output.write(line)
        # if we have a print function set and it's priority is high enough
        if self.print_fun and self.printfun_verbosity and priority >= self.printfun_verbosity:
            self.use_print(line)

    def close(self):
        """Restore the stdout stream and close the logging file if it's open."""
        if self.stream == 'error':
            sys.stderr = self.output
        else:    
            sys.stdout = self.output
        if self.filename and self.filehandle:
            self.filehandle.close()
            del self.filehandle
        
    def set_print(self, print_fun):
        """Set a new print_fun."""
        self.print_fun = True
        self.thefun = [print_fun]

    def setall(self, verbosity):
        """Sets the verbosity level for *all* the output methods."""
        self.verbosity = self.file_verbosity = self.printfun_verbosity = verbosity

    def flush(self):
        return self.output.flush()
    
    def writelines(self, inline):
        for line in inlines:
            self.write(line)
   
    def __getattr__(self, attribute):
        if not self.__dict__.has_key(attribute) or attribute == '__doc__':
            return getattr(self.output, attribute)
        return self.__dict__[attribute]

##########################################################
    # private methods, you shouldn't need to call these directly
    def use_print(self, line):
        """A wrapper function for the function passed in as 'print_fun'."""
        self.thefun[0](line)
        

if __name__ == '__main__':

    test = StandOut()
    print 'hello'
    test.priority = 4
    print "You shouldn't see this"
    test.verbosity = 4
    print 'You should see this'
    test.priority = 0
    print 'but not this'
    test.write('And you should see this\n', 5)
    print 'but not this'
    test.filename = 'test.txt'
    test.priority = 5
    test.setall(5)
    print 'This should go to the file test.txt as well as the screen.'
    test.file_verbosity = 7
    print '&priority-8;'
    print 'And this should be printed to both'
    print '&priority-6;But this should only go to the screen.'
    print 'And this should be printed to both, again.'

    def afunction(line):
        test.output.write('\nHello\n')

    test.set_print(afunction)
    print "We're now using another print function - which should mirror 'hello' to the screen."
    print "In practise you could use it to send output to a GUI window."
    print "Or perhaps format output."

    test2 = StandOut(stream='error', share=True)        # anything printed to sys.stderr, should now be logged to the stdout file as well 
    sys.stderr.write('Big Mistake')
    sys.stderr.write('\n')
    sys.stderr.write('Another Error')
    sys.stderr.write('\n')    
    
    test.close()
    test2.close()
    print 'Normality is now restored'
    print 'Any further problems, are entirely your own.'

"""
ISSUES/TODO
===========

Doctests

Could trap when stout.write(line) is used with a priority marker. (By checking
for the default value of priority).

Could add a ``both`` keyword argument to avoid having to use the `share``
keyword argument. It can just instantiate another StandOut itself.

CHANGELOG
=========

2005/01/06      Version 2.1.0
Added flush and writelines method.
Added the 'stream' keyword for diverting the sys.stderr stream as well.
Added __getattr__ for any undefined methods.
Added the 'share' and 'error_marker' keywords for logging sys.stderr to the same file as sys.stdout.

07-04-04        Version 2.0.0
A complete rewrite. It now redirects the stdout stream so that normal print statements can be used.
Much better.

06-04-04        Version 1.1.0
Fixed a bug in passing in newfunc. Previously it only worked if you had a dummy variable for self.
"""
# urlpath.py

# 0.1.0
# 2005/08/20

# Functions that handle url paths.
# Part of Pythonutils
# http://www.voidspace.org.uk/python/pythonutils.html

# Copyright Michael Foord, 2004 & 2005.
# Released subject to the BSD License
# Please see http://www.voidspace.org.uk/python/license.shtml

# For information about bugfixes, updates and support, please join the
# Pythonutils mailing list.
# http://groups.google.com/group/pythonutils/
# Comments, suggestions and bug reports welcome.
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# E-mail fuzzyman@voidspace.org.uk

import posixpath
import os
from urllib import url2pathname, pathname2url

__all__ = [
    'nativejoin',
    'pathjoin',
    'relpathto',
    'tslash',
    'relpath'
    ]

def pathjoin(base, *paths):
    """
    Join paths to a base, observing pardir.
    
    If base doesn't *end* with '/' we assume it's a file rather than a directory.
        (so we get rid of it)
    """
    # XXXX will posixpath.join do all this anyway?
    if base and not base.endswith('/'):
        # get rid of the filename
        base = '/'.join(base.split('/')[:-1])
    base = tslash(base)
    path = (base,) + paths
    return posixpath.normpath(posixpath.join(*path))

def nativejoin(base, path):
    """
    Joins two paths - returning a native file path.

    Given a base path and a relative location, (in posix format)
    return a file path in a (relatively) OS native way.
    """
    return url2pathname(pathjoin(base, path))

def relpathto(thisdir, origin, dest):
    """
    Given two paths relative to a directory, work out a path from origin
    to destination.

    Assumes UNIX/URL type relative paths.
    If origin doesn't *end* with '/' we assume it's a file rather than a
    directory.

    If the same paths are passed in :
        if the path ends with ('/') then we return ''
        else we return the last part of the path (presumably a filename)

    If thisdir doesn't start with '/' then we add one
        (this makes the top level of thisdir our root directory)
    """
    orig_thisdir = thisdir
    if not thisdir.startswith('/'):
        thisdir = '/' + thisdir
    orig_abs = posixpath.normpath(posixpath.join(thisdir, origin))
    dest_abs = posixpath.normpath(posixpath.join(thisdir, dest))
    if origin.endswith('/') and not orig_abs.endswith('/'):
        orig_abs = orig_abs + '/'
    if dest.endswith('/') and not dest_abs.endswith('/'):
        dest_abs = dest_abs + '/'
#    print orig_abs, dest_abs
    #
    # if the first item is a filename, we want to get rid of it
    orig_list = orig_abs.split('/')[:-1]
    dest_list = dest_abs.split('/')
#    print orig_list, dest_list

    if orig_list[0] != dest_list[0]:
        # can't get here from there
        # XXXX raise exception?
        return dest
    #
    # find the location where the two paths start to differ.
    i = 0
    for start_seg, dest_seg in zip(orig_list, dest_list):
        if start_seg != dest_seg:
            break
        i += 1
    #
    # now i is the point where the two paths diverge;
    # need a certain number of "os.pardir"s to work up
    # from the origin to the point of divergence.
    segments = ['..'] * (len(orig_list) - i)
    # need to add the diverging part of dest_list.
    segments += dest_list[i:]
    if len(segments) == 0:
        # if they happen to be identical paths
        # identical directories
        if dest.endswith('/'):
            return ''
        # just the filename - the last part of dest
        return dest_list[-1]
    else:
        return '/'.join(segments)

def relpath(origin, dest):
    """Given two absolute paths, work out a path from origin to destination.

    Assumes UNIX/URL type relative paths.
    If origin doesn't *end* with '/' we assume it's a file rather than
    a directory.

    If the same paths are passed in :
        if the path ends with ('/') then we return ''
        else we return the last part of the path (presumably a filename)

    If origin or dest don't start with '/' then we add it.

    We are *assuming* relative paths on the same device
        (i.e. same top level directory)
    """
    if not origin.startswith('/'):
        origin = '/' + origin
    if not dest.startswith('/'):
        dest = '/' + dest
    #
    # if the first item is a filename, we want to get rid of it
    orig_list = origin.split('/')[:-1]
    dest_list = dest.split('/')
    #
    # find the location where the two paths start to differ.
    i = 0
    for start_seg, dest_seg in zip(orig_list, dest_list):
        if start_seg != dest_seg:
            break
        i += 1

    # now i is the point where the two paths diverge.
    # need a certain number of "os.pardir"s to work up
    # from the origin to the point of divergence.
    segments = ['..'] * (len(orig_list) - i)
    # need to add the diverging part of dest_list.
    segments += dest_list[i:]
    if len(segments) == 0:
        # if they happen to be identical paths
        # identical directories
        if dest.endswith('/'):
            return ''
        # just the filename - the last part of dest
        return dest_list[-1]
    else:
        return '/'.join(segments)

def tslash(apath):
    """Add a trailing slash to a path if it needs one.

    Doesn't use os.sep because you end up jiggered on windoze - when you
    want separators for URLs.
    """
    if (apath and
            apath != '.' and
            not apath.endswith('/') and
            not apath.endswith('\\')):
        return apath + '/'
    else:
        return apath

##############################################

def testJoin():
    thelist = [
        ('/', 'fish.html'),
        ('/dir/dir/', '../file'),
        ('dir/dir/', '../file'),
        ('dir/dir/', '../../file'),
        ('dir/dir/', '../../../file'),
        ('/dir/dir/', '../notherdir/file'),
        ('/dir/dir/', '../../notherdir/file'),
        ('dir/dir/', '../../notherdir/file'),
        ('dir/dir/', '../../../notherdir/file'),
        ('', '../path'),
    ]
    for entry in thelist:
        print entry, '      ::        ', pathjoin(*entry)
        print entry, '      ::        ', nativejoin(*entry)
        print '\n'

def testRelpathto():
    thedir = '//toplevel/dirone/dirtwo/dirthree'
    thelist = [
        ('file1.html', 'file2.html'),
        ('file1.html', '../file2.html'),
        ('../file1.html', '../file2.html'),
        ('../file1.html', 'file2.html'),
        ('../fish1/fish2/', '../../sub1/sub2/'),
        ('../fish1/fish2/', 'sub1/sub2'),
        ('../../../fish1/fish2/', 'sub1/sub2/'),
        ('../../../fish1/fish2/', 'sub1/sub2/file1.html'),
   ]
    for orig, dest in thelist:
        print '(%s, %s)      : ' % (orig, dest), relpathto(thedir, orig, dest)

def testRelpathto2():
    thedir = 'section3/'
    thelist = [
        ('../archive/strangeindex1.html', 'article2.html'),
    ]
    for orig, dest in thelist:
        answer = relpathto(thedir, orig, dest)
        print '(%s, %s)      : ' % (orig, dest), answer

def testRelpath():
    thelist = [
        ('/hello/fish/', 'bungles'),
    ]
    for orig, dest in thelist:
        answer = relpath(orig, dest)
        print '(%s, %s)      : ' % (orig, dest), answer


if __name__ == '__main__':
    testJoin()
    testRelpathto()
    testRelpath()
#    testRelpathto2()

"""
TODO
====

More comprehensive tests.

CHANGELOG
2005/07/31
Can now pass mulitple args to ``pathjoin``.
Finalised as version 0.1.0

2005/06/18
Changes by Nicola Larosa
    Code cleanup
        lines shortened
        comments on line above code
        empty comments in empty lines

2005/05/28
Added relpath to __all__


TODO
Move into pythonutils
relpathto could call relpath (and so be shorter)
nativejoin could accept multiple paths
Could tslash be more elegant ?
"""

# validate.py
# A Validator object
# Copyright (C) 2005 Michael Foord, Mark Andrews, Nicola Larosa
# E-mail: fuzzyman AT voidspace DOT org DOT uk
#         mark AT la-la DOT com
#         nico AT tekNico DOT net

# This software is licensed under the terms of the BSD license.
# http://www.voidspace.org.uk/python/license.shtml
# Basically you're free to copy, modify, distribute and relicense it,
# So long as you keep a copy of the license with it.

# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
# For information about bugfixes, updates and support, please join the
# ConfigObj mailing list:
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
# Comments, suggestions and bug reports welcome.

"""
    The Validator object is used to check that supplied values 
    conform to a specification.
    
    The value can be supplied as a string - e.g. from a config file.
    In this case the check will also *convert* the value to
    the required type. This allows you to add validation
    as a transparent layer to access data stored as strings.
    The validation checks that the data is correct *and*
    converts it to the expected type.
    
    Some standard checks are provided for basic data types.
    Additional checks are easy to write. They can be
    provided when the ``Validator`` is instantiated or
    added afterwards.
    
    The standard functions work with the following basic data types :
    
    * integers
    * floats
    * booleans
    * strings
    * ip_addr
    
    plus lists of these datatypes
    
    Adding additional checks is done through coding simple functions.
    
    The full set of standard checks are : 
    
    * 'integer': matches integer values (including negative)
                 Takes optional 'min' and 'max' arguments : ::
    
                   integer()
                   integer(3, 9)  # any value from 3 to 9
                   integer(min=0) # any positive value
                   integer(max=9)
    
    * 'float': matches float values
               Has the same parameters as the integer check.
    
    * 'boolean': matches boolean values - ``True`` or ``False``
                 Acceptable string values for True are :
                   true, on, yes, 1
                 Acceptable string values for False are :
                   false, off, no, 0
    
                 Any other value raises an error.
    
    * 'ip_addr': matches an Internet Protocol address, v.4, represented
                 by a dotted-quad string, i.e. '1.2.3.4'.
    
    * 'string': matches any string.
                Takes optional keyword args 'min' and 'max'
                to specify min and max lengths of the string.
    
    * 'list': matches any list.
              Takes optional keyword args 'min', and 'max' to specify min and
              max sizes of the list.
    
    * 'int_list': Matches a list of integers.
                  Takes the same arguments as list.
    
    * 'float_list': Matches a list of floats.
                    Takes the same arguments as list.
    
    * 'bool_list': Matches a list of boolean values.
                   Takes the same arguments as list.
    
    * 'ip_addr_list': Matches a list of IP addresses.
                     Takes the same arguments as list.
    
    * 'string_list': Matches a list of strings.
                     Takes the same arguments as list.
    
    * 'mixed_list': Matches a list with different types in 
                    specific positions. List size must match
                    the number of arguments.
    
                    Each position can be one of :
                    'integer', 'float', 'ip_addr', 'string', 'boolean'
    
                    So to specify a list with two strings followed
                    by two integers, you write the check as : ::
    
                      mixed_list('string', 'string', 'integer', 'integer')
    
    * 'pass': This check matches everything ! It never fails
              and the value is unchanged.
    
              It is also the default if no check is specified.
    
    * 'option': This check matches any from a list of options.
                You specify this check with : ::
    
                  option('option 1', 'option 2', 'option 3')
    
    You can supply a default value (returned if no value is supplied)
    using the default keyword argument.
    
    You specify a list argument for default using a list constructor syntax in
    the check : ::
    
        checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3'))
    
    A badly formatted set of arguments will raise a ``VdtParamError``.
"""

__docformat__ = "restructuredtext en"

__version__ = '0.2.1'

__revision__ = '$Id: validate.py 123 2005-09-08 08:54:28Z fuzzyman $'

__all__ = (
    '__version__',
    'dottedQuadToNum',
    'numToDottedQuad',
    'ValidateError',
    'VdtUnknownCheckError',
    'VdtParamError',
    'VdtTypeError',
    'VdtValueError',
    'VdtValueTooSmallError',
    'VdtValueTooBigError',
    'VdtValueTooShortError',
    'VdtValueTooLongError',
    'VdtMissingValue',
    'Validator',
    'is_integer',
    'is_float',
    'is_bool',
    'is_list',
    'is_ip_addr',
    'is_string',
    'is_int_list',
    'is_bool_list',
    'is_float_list',
    'is_string_list',
    'is_ip_addr_list',
    'is_mixed_list',
    'is_option',
    '__docformat__',
)

import sys
INTP_VER = sys.version_info[:2]
if INTP_VER < (2, 2):
    raise RuntimeError("Python v.2.2 or later needed")

import re
StringTypes = (str, unicode)


_list_arg = re.compile(r'''
    (?:
        ([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\(
            (
                (?:
                    \s*
                    (?:
                        (?:".*?")|              # double quotes
                        (?:'.*?')|              # single quotes
                        (?:[^'",\s\)][^,\)]*?)  # unquoted
                    )
                    \s*,\s*
                )*
                (?:
                    (?:".*?")|              # double quotes
                    (?:'.*?')|              # single quotes
                    (?:[^'",\s\)][^,\)]*?)  # unquoted
                )?                          # last one
            )
        \)
    )
''', re.VERBOSE)    # two groups

_list_members = re.compile(r'''
    (
        (?:".*?")|              # double quotes
        (?:'.*?')|              # single quotes
        (?:[^'",\s=][^,=]*?)       # unquoted
    )
    (?:
    (?:\s*,\s*)|(?:\s*$)            # comma
    )
''', re.VERBOSE)    # one group

_paramstring = r'''
    (?:
        (
            (?:
                [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\(
                    (?:
                        \s*
                        (?:
                            (?:".*?")|              # double quotes
                            (?:'.*?')|              # single quotes
                            (?:[^'",\s\)][^,\)]*?)       # unquoted
                        )
                        \s*,\s*
                    )*
                    (?:
                        (?:".*?")|              # double quotes
                        (?:'.*?')|              # single quotes
                        (?:[^'",\s\)][^,\)]*?)       # unquoted
                    )?                              # last one
                \)
            )|
            (?:
                (?:".*?")|              # double quotes
                (?:'.*?')|              # single quotes
                (?:[^'",\s=][^,=]*?)|       # unquoted
                (?:                         # keyword argument
                    [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*
                    (?:
                        (?:".*?")|              # double quotes
                        (?:'.*?')|              # single quotes
                        (?:[^'",\s=][^,=]*?)       # unquoted
                    )
                )
            )
        )
        (?:
            (?:\s*,\s*)|(?:\s*$)            # comma
        )
    )
    '''

_matchstring = '^%s*' % _paramstring

# Python pre 2.2.1 doesn't have bool
try:
    bool
except NameError:
    def bool(val):
        """Simple boolean equivalent function. """
        if val:
            return 1
        else:
            return 0

def dottedQuadToNum(ip):
    """
    Convert decimal dotted quad string to long integer
    
    >>> dottedQuadToNum('1 ')
    1L
    >>> dottedQuadToNum(' 1.2')
    16777218L
    >>> dottedQuadToNum(' 1.2.3 ')
    16908291L
    >>> dottedQuadToNum('1.2.3.4')
    16909060L
    >>> dottedQuadToNum('1.2.3. 4')
    Traceback (most recent call last):
    ValueError: Not a good dotted-quad IP: 1.2.3. 4
    >>> dottedQuadToNum('255.255.255.255')
    4294967295L
    >>> dottedQuadToNum('255.255.255.256')
    Traceback (most recent call last):
    ValueError: Not a good dotted-quad IP: 255.255.255.256
    """
    
    # import here to avoid it when ip_addr values are not used
    import socket, struct
    
    try:
        return struct.unpack('!L',
            socket.inet_aton(ip.strip()))[0]
    except socket.error:
        # bug in inet_aton, corrected in Python 2.3
        if ip.strip() == '255.255.255.255':
            return 0xFFFFFFFFL
        else:
            raise ValueError('Not a good dotted-quad IP: %s' % ip)
    return

def numToDottedQuad(num):
    """
    Convert long int to dotted quad string
    
    >>> numToDottedQuad(-1L)
    Traceback (most recent call last):
    ValueError: Not a good numeric IP: -1
    >>> numToDottedQuad(1L)
    '0.0.0.1'
    >>> numToDottedQuad(16777218L)
    '1.0.0.2'
    >>> numToDottedQuad(16908291L)
    '1.2.0.3'
    >>> numToDottedQuad(16909060L)
    '1.2.3.4'
    >>> numToDottedQuad(4294967295L)
    '255.255.255.255'
    >>> numToDottedQuad(4294967296L)
    Traceback (most recent call last):
    ValueError: Not a good numeric IP: 4294967296
    """
    
    # import here to avoid it when ip_addr values are not used
    import socket, struct
    
    # no need to intercept here, 4294967295L is fine
    try:
        return socket.inet_ntoa(
            struct.pack('!L', long(num)))
    except (socket.error, struct.error, OverflowError):
        raise ValueError('Not a good numeric IP: %s' % num)

class ValidateError(Exception):
    """
    This error indicates that the check failed.
    It can be the base class for more specific errors.
    
    Any check function that fails ought to raise this error.
    (or a subclass)
    
    >>> raise ValidateError
    Traceback (most recent call last):
    ValidateError
    """

class VdtMissingValue(ValidateError):
    """No value was supplied to a check that needed one."""

class VdtUnknownCheckError(ValidateError):
    """An unknown check function was requested"""

    def __init__(self, value):
        """
        >>> raise VdtUnknownCheckError('yoda')
        Traceback (most recent call last):
        VdtUnknownCheckError: the check "yoda" is unknown.
        """
        ValidateError.__init__(
            self,
            'the check "%s" is unknown.' % value)

class VdtParamError(SyntaxError):
    """An incorrect parameter was passed"""

    def __init__(self, name, value):
        """
        >>> raise VdtParamError('yoda', 'jedi')
        Traceback (most recent call last):
        VdtParamError: passed an incorrect value "jedi" for parameter "yoda".
        """
        SyntaxError.__init__(
            self,
            'passed an incorrect value "%s" for parameter "%s".' % (
                value, name))

class VdtTypeError(ValidateError):
    """The value supplied was of the wrong type"""

    def __init__(self, value):
        """
        >>> raise VdtTypeError('jedi')
        Traceback (most recent call last):
        VdtTypeError: the value "jedi" is of the wrong type.
        """
        ValidateError.__init__(
            self,
            'the value "%s" is of the wrong type.' % value)

class VdtValueError(ValidateError):
    """
    The value supplied was of the correct type, but was not an allowed value.
    """

    def __init__(self, value):
        """
        >>> raise VdtValueError('jedi')
        Traceback (most recent call last):
        VdtValueError: the value "jedi" is unacceptable.
        """
        ValidateError.__init__(
            self,
            'the value "%s" is unacceptable.' % value)

class VdtValueTooSmallError(VdtValueError):
    """The value supplied was of the correct type, but was too small."""

    def __init__(self, value):
        """
        >>> raise VdtValueTooSmallError('0')
        Traceback (most recent call last):
        VdtValueTooSmallError: the value "0" is too small.
        """
        ValidateError.__init__(
            self,
            'the value "%s" is too small.' % value)

class VdtValueTooBigError(VdtValueError):
    """The value supplied was of the correct type, but was too big."""

    def __init__(self, value):
        """
        >>> raise VdtValueTooBigError('1')
        Traceback (most recent call last):
        VdtValueTooBigError: the value "1" is too big.
        """
        ValidateError.__init__(
            self,
            'the value "%s" is too big.' % value)

class VdtValueTooShortError(VdtValueError):
    """The value supplied was of the correct type, but was too short."""

    def __init__(self, value):
        """
        >>> raise VdtValueTooShortError('jed')
        Traceback (most recent call last):
        VdtValueTooShortError: the value "jed" is too short.
        """
        ValidateError.__init__(
            self,
            'the value "%s" is too short.' % (value,))

class VdtValueTooLongError(VdtValueError):
    """The value supplied was of the correct type, but was too long."""

    def __init__(self, value):
        """
        >>> raise VdtValueTooLongError('jedie')
        Traceback (most recent call last):
        VdtValueTooLongError: the value "jedie" is too long.
        """
        ValidateError.__init__(
            self,
            'the value "%s" is too long.' %  (value,))

class Validator(object):
    """
        Validator is an object that allows you to register a set of 'checks'.
        These checks take input and test that it conforms to the check.
        
        This can also involve converting the value from a string into
        the correct datatype.
        
        The ``check`` method takes an input string which configures which
        check is to be used and applies that check to a supplied value.
        
        An example input string would be:
        'int_range(param1, param2)'
        
        You would then provide something like:
        
        >>> def int_range_check(value, min, max):
        ...     # turn min and max from strings to integers
        ...     min = int(min)
        ...     max = int(max)
        ...     # check that value is of the correct type.
        ...     # possible valid inputs are integers or strings
        ...     # that represent integers
        ...     if not isinstance(value, (int, long, StringTypes)):
        ...         raise VdtTypeError(value)
        ...     elif isinstance(value, StringTypes):
        ...         # if we are given a string
        ...         # attempt to convert to an integer
        ...         try:
        ...             value = int(value)
        ...         except ValueError:
        ...             raise VdtValueError(value)
        ...     # check the value is between our constraints
        ...     if not min <= value:
        ...          raise VdtValueTooSmallError(value)
        ...     if not value <= max:
        ...          raise VdtValueTooBigError(value)
        ...     return value
        
        >>> fdict = {'int_range': int_range_check}
        >>> vtr1 = Validator(fdict)
        >>> vtr1.check('int_range(20, 40)', '30')
        30
        >>> vtr1.check('int_range(20, 40)', '60')
        Traceback (most recent call last):
        VdtValueTooBigError: the value "60" is too big.
        
        New functions can be added with : ::
        
        >>> vtr2 = Validator()       
        >>> vtr2.functions['int_range'] = int_range_check
        
        Or by passing in a dictionary of functions when Validator 
        is instantiated.
        
        Your functions *can* use keyword arguments,
        but the first argument should always be 'value'.
        
        If the function doesn't take additional arguments,
        the parentheses are optional in the check.
        It can be written with either of : ::
        
            keyword = function_name
            keyword = function_name()
        
        The first program to utilise Validator() was Michael Foord's
        ConfigObj, an alternative to ConfigParser which supports lists and
        can validate a config file using a config schema.
        For more details on using Validator with ConfigObj see:
        http://www.voidspace.org.uk/python/configobj.html
    """

    # this regex does the initial parsing of the checks
    _func_re = re.compile(r'(.+?)\((.*)\)')

    # this regex takes apart keyword arguments
    _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$')


    # this regex finds keyword=list(....) type values
    _list_arg = _list_arg

    # this regex takes individual values out of lists - in one pass
    _list_members = _list_members

    # These regexes check a set of arguments for validity
    # and then pull the members out
    _paramfinder = re.compile(_paramstring, re.VERBOSE)
    _matchfinder = re.compile(_matchstring, re.VERBOSE)


    def __init__(self, functions=None):
        """
        >>> vtri = Validator()
        """
        self.functions = {
            '': self._pass,
            'integer': is_integer,
            'float': is_float,
            'boolean': is_bool,
            'ip_addr': is_ip_addr,
            'string': is_string,
            'list': is_list,
            'int_list': is_int_list,
            'float_list': is_float_list,
            'bool_list': is_bool_list,
            'ip_addr_list': is_ip_addr_list,
            'string_list': is_string_list,
            'mixed_list': is_mixed_list,
            'pass': self._pass,
            'option': is_option,
        }
        if functions is not None:
            self.functions.update(functions)
        # tekNico: for use by ConfigObj
        self.baseErrorClass = ValidateError

    def check(self, check, value, missing=False):
        """
        Usage: check(check, value)
        
        Arguments:
            check: string representing check to apply (including arguments)
            value: object to be checked
        Returns value, converted to correct type if necessary
        
        If the check fails, raises a ``ValidateError`` subclass.
        
        >>> vtor.check('yoda', '')
        Traceback (most recent call last):
        VdtUnknownCheckError: the check "yoda" is unknown.
        >>> vtor.check('yoda()', '')
        Traceback (most recent call last):
        VdtUnknownCheckError: the check "yoda" is unknown.
        """
        fun_match = self._func_re.match(check)
        if fun_match:
            fun_name = fun_match.group(1)
            arg_string = fun_match.group(2)
            arg_match = self._matchfinder.match(arg_string)
            if arg_match is None:
                # Bad syntax
                raise VdtParamError
            fun_args = []
            fun_kwargs = {}
            # pull out args of group 2
            for arg in self._paramfinder.findall(arg_string):
                # args may need whitespace removing (before removing quotes)
                arg = arg.strip()
                listmatch = self._list_arg.match(arg)
                if listmatch:
                    key, val = self._list_handle(listmatch)
                    fun_kwargs[key] = val
                    continue
                keymatch = self._key_arg.match(arg)
                if keymatch:
                    val = self._unquote(keymatch.group(2))
                    fun_kwargs[keymatch.group(1)] = val
                    continue
                #
                fun_args.append(self._unquote(arg))
        else:
            # allows for function names without (args)
            (fun_name, fun_args, fun_kwargs) = (check, (), {})
        #
        if missing:
            try:
                value = fun_kwargs['default']
            except KeyError:
                raise VdtMissingValue
            if value == 'None':
                value = None
        if value is None:
            return None
# tekNico: default must be deleted if the value is specified too,
# otherwise the check function will get a spurious "default" keyword arg
        try:
            del fun_kwargs['default']
        except KeyError:
            pass
        try:
            fun = self.functions[fun_name]
        except KeyError:
            raise VdtUnknownCheckError(fun_name)
        else:
##            print fun_args
##            print fun_kwargs
            return fun(value, *fun_args, **fun_kwargs)

    def _unquote(self, val):
        """Unquote a value if necessary."""
        if (len(val) > 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]):
            val = val[1:-1]
        return val

    def _list_handle(self, listmatch):
        """Take apart a ``keyword=list('val, 'val')`` type string."""
        out = []
        name = listmatch.group(1)
        args = listmatch.group(2)
        for arg in self._list_members.findall(args):
            out.append(self._unquote(arg))
        return name, out

    def _pass(self, value):
        """
        Dummy check that always passes
        
        >>> vtor.check('', 0)
        0
        >>> vtor.check('', '0')
        '0'
        """
        return value


def _is_num_param(names, values, to_float=False):
    """
    Return numbers from inputs or raise VdtParamError.
    
    Lets ``None`` pass through.
    Pass in keyword argument ``to_float=True`` to
    use float for the conversion rather than int.
    
    >>> _is_num_param(('', ''), (0, 1.0))
    [0, 1]
    >>> _is_num_param(('', ''), (0, 1.0), to_float=True)
    [0.0, 1.0]
    >>> _is_num_param(('a'), ('a'))
    Traceback (most recent call last):
    VdtParamError: passed an incorrect value "a" for parameter "a".
    """
    fun = to_float and float or int
    out_params = []
    for (name, val) in zip(names, values):
        if val is None:
            out_params.append(val)
        elif isinstance(val, (int, long, float, StringTypes)):
            try:
                out_params.append(fun(val))
            except ValueError, e:
                raise VdtParamError(name, val)
        else:
            raise VdtParamError(name, val)
    return out_params

# built in checks
# you can override these by setting the appropriate name
# in Validator.functions
# note: if the params are specified wrongly in your input string,
#       you will also raise errors.

def is_integer(value, min=None, max=None):
    """
    A check that tests that a given value is an integer (int, or long)
    and optionally, between bounds. A negative value is accepted, while
    a float will fail.
    
    If the value is a string, then the conversion is done - if possible.
    Otherwise a VdtError is raised.
    
    >>> vtor.check('integer', '-1')
    -1
    >>> vtor.check('integer', '0')
    0
    >>> vtor.check('integer', 9)
    9
    >>> vtor.check('integer', 'a')
    Traceback (most recent call last):
    VdtTypeError: the value "a" is of the wrong type.
    >>> vtor.check('integer', '2.2')
    Traceback (most recent call last):
    VdtTypeError: the value "2.2" is of the wrong type.
    >>> vtor.check('integer(10)', '20')
    20
    >>> vtor.check('integer(max=20)', '15')
    15
    >>> vtor.check('integer(10)', '9')
    Traceback (most recent call last):
    VdtValueTooSmallError: the value "9" is too small.
    >>> vtor.check('integer(10)', 9)
    Traceback (most recent call last):
    VdtValueTooSmallError: the value "9" is too small.
    >>> vtor.check('integer(max=20)', '35')
    Traceback (most recent call last):
    VdtValueTooBigError: the value "35" is too big.
    >>> vtor.check('integer(max=20)', 35)
    Traceback (most recent call last):
    VdtValueTooBigError: the value "35" is too big.
    >>> vtor.check('integer(0, 9)', False)
    0
    """
#    print value, type(value)
    (min_val, max_val) = _is_num_param(('min', 'max'), (min, max))
    if not isinstance(value, (int, long, StringTypes)):
        raise VdtTypeError(value)
    if isinstance(value, StringTypes):
        # if it's a string - does it represent an integer ?
        try:
            value = int(value)
        except ValueError:
            raise VdtTypeError(value)
    if (min_val is not None) and (value < min_val):
        raise VdtValueTooSmallError(value)
    if (max_val is not None) and (value > max_val):
        raise VdtValueTooBigError(value)
    return value

def is_float(value, min=None, max=None):
    """
    A check that tests that a given value is a float
    (an integer will be accepted), and optionally - that it is between bounds.
    
    If the value is a string, then the conversion is done - if possible.
    Otherwise a VdtError is raised.
    
    This can accept negative values.
    
    >>> vtor.check('float', '2')
    2.0
    
    From now on we multiply the value to avoid comparing decimals
    
    >>> vtor.check('float', '-6.8') * 10
    -68.0
    >>> vtor.check('float', '12.2') * 10
    122.0
    >>> vtor.check('float', 8.4) * 10
    84.0
    >>> vtor.check('float', 'a')
    Traceback (most recent call last):
    VdtTypeError: the value "a" is of the wrong type.
    >>> vtor.check('float(10.1)', '10.2') * 10
    102.0
    >>> vtor.check('float(max=20.2)', '15.1') * 10
    151.0
    >>> vtor.check('float(10.0)', '9.0')
    Traceback (most recent call last):
    VdtValueTooSmallError: the value "9.0" is too small.
    >>> vtor.check('float(max=20.0)', '35.0')
    Traceback (most recent call last):
    VdtValueTooBigError: the value "35.0" is too big.
    """
    (min_val, max_val) = _is_num_param(
        ('min', 'max'), (min, max), to_float=True)
    if not isinstance(value, (int, long, float, StringTypes)):
        raise VdtTypeError(value)
    if not isinstance(value, float):
        # if it's a string - does it represent a float ?
        try:
            value = float(value)
        except ValueError:
            raise VdtTypeError(value)
    if (min_val is not None) and (value < min_val):
        raise VdtValueTooSmallError(value)
    if (max_val is not None) and (value > max_val):
        raise VdtValueTooBigError(value)
    return value

bool_dict = {
    True: True, 'on': True, '1': True, 'true': True, 'yes': True, 
    False: False, 'off': False, '0': False, 'false': False, 'no': False,
}

def is_bool(value):
    """
    Check if the value represents a boolean.
    
    >>> vtor.check('boolean', 0)
    0
    >>> vtor.check('boolean', False)
    0
    >>> vtor.check('boolean', '0')
    0
    >>> vtor.check('boolean', 'off')
    0
    >>> vtor.check('boolean', 'false')
    0
    >>> vtor.check('boolean', 'no')
    0
    >>> vtor.check('boolean', 'nO')
    0
    >>> vtor.check('boolean', 'NO')
    0
    >>> vtor.check('boolean', 1)
    1
    >>> vtor.check('boolean', True)
    1
    >>> vtor.check('boolean', '1')
    1
    >>> vtor.check('boolean', 'on')
    1
    >>> vtor.check('boolean', 'true')
    1
    >>> vtor.check('boolean', 'yes')
    1
    >>> vtor.check('boolean', 'Yes')
    1
    >>> vtor.check('boolean', 'YES')
    1
    >>> vtor.check('boolean', '')
    Traceback (most recent call last):
    VdtTypeError: the value "" is of the wrong type.
    >>> vtor.check('boolean', 'up')
    Traceback (most recent call last):
    VdtTypeError: the value "up" is of the wrong type.
    
    """
    if isinstance(value, StringTypes):
        try:
            return bool_dict[value.lower()]
        except KeyError:
            raise VdtTypeError(value)
    # we do an equality test rather than an identity test
    # this ensures Python 2.2 compatibilty
    # and allows 0 and 1 to represent True and False
    if value == False:
        return False
    elif value == True:
        return True
    else:
        raise VdtTypeError(value)


def is_ip_addr(value):
    """
    Check that the supplied value is an Internet Protocol address, v.4,
    represented by a dotted-quad string, i.e. '1.2.3.4'.
    
    >>> vtor.check('ip_addr', '1 ')
    '1'
    >>> vtor.check('ip_addr', ' 1.2')
    '1.2'
    >>> vtor.check('ip_addr', ' 1.2.3 ')
    '1.2.3'
    >>> vtor.check('ip_addr', '1.2.3.4')
    '1.2.3.4'
    >>> vtor.check('ip_addr', '0.0.0.0')
    '0.0.0.0'
    >>> vtor.check('ip_addr', '255.255.255.255')
    '255.255.255.255'
    >>> vtor.check('ip_addr', '255.255.255.256')
    Traceback (most recent call last):
    VdtValueError: the value "255.255.255.256" is unacceptable.
    >>> vtor.check('ip_addr', '1.2.3.4.5')
    Traceback (most recent call last):
    VdtValueError: the value "1.2.3.4.5" is unacceptable.
    >>> vtor.check('ip_addr', '1.2.3. 4')
    Traceback (most recent call last):
    VdtValueError: the value "1.2.3. 4" is unacceptable.
    >>> vtor.check('ip_addr', 0)
    Traceback (most recent call last):
    VdtTypeError: the value "0" is of the wrong type.
    """
    if not isinstance(value, StringTypes):
        raise VdtTypeError(value)
    value = value.strip()
    try:
        dottedQuadToNum(value)
    except ValueError:
        raise VdtValueError(value)
    return value

def is_list(value, min=None, max=None):
    """
    Check that the value is a list of values.
    
    You can optionally specify the minimum and maximum number of members.
    
    It does no check on list members.
    
    >>> vtor.check('list', ())
    ()
    >>> vtor.check('list', [])
    []
    >>> vtor.check('list', (1, 2))
    (1, 2)
    >>> vtor.check('list', [1, 2])
    [1, 2]
    >>> vtor.check('list', '12')
    '12'
    >>> vtor.check('list(3)', (1, 2))
    Traceback (most recent call last):
    VdtValueTooShortError: the value "(1, 2)" is too short.
    >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6))
    Traceback (most recent call last):
    VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long.
    >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4))
    (1, 2, 3, 4)
    >>> vtor.check('list', 0)
    Traceback (most recent call last):
    VdtTypeError: the value "0" is of the wrong type.
    """
    (min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
    try:
        num_members = len(value)
    except TypeError:
        raise VdtTypeError(value)
    if min_len is not None and num_members < min_len:
        raise VdtValueTooShortError(value)
    if max_len is not None and num_members > max_len:
        raise VdtValueTooLongError(value)
    return value

def is_string(value, min=None, max=None):
    """
    Check that the supplied value is a string.
    
    You can optionally specify the minimum and maximum number of members.
    
    >>> vtor.check('string', '0')
    '0'
    >>> vtor.check('string', 0)
    Traceback (most recent call last):
    VdtTypeError: the value "0" is of the wrong type.
    >>> vtor.check('string(2)', '12')
    '12'
    >>> vtor.check('string(2)', '1')
    Traceback (most recent call last):
    VdtValueTooShortError: the value "1" is too short.
    >>> vtor.check('string(min=2, max=3)', '123')
    '123'
    >>> vtor.check('string(min=2, max=3)', '1234')
    Traceback (most recent call last):
    VdtValueTooLongError: the value "1234" is too long.
    """
    if not isinstance(value, StringTypes):
        raise VdtTypeError(value)
    return is_list(value, min, max)

def is_int_list(value, min=None, max=None):
    """
    Check that the value is a list of integers.
    
    You can optionally specify the minimum and maximum number of members.
    
    Each list member is checked that it is an integer.
    
    >>> vtor.check('int_list', ())
    []
    >>> vtor.check('int_list', [])
    []
    >>> vtor.check('int_list', (1, 2))
    [1, 2]
    >>> vtor.check('int_list', [1, 2])
    [1, 2]
    >>> vtor.check('int_list', [1, 'a'])
    Traceback (most recent call last):
    VdtTypeError: the value "a" is of the wrong type.
    """
    return [is_integer(mem) for mem in is_list(value, min, max)]

def is_bool_list(value, min=None, max=None):
    """
    Check that the value is a list of booleans.
    
    You can optionally specify the minimum and maximum number of members.
    
    Each list member is checked that it is a boolean.
    
    >>> vtor.check('bool_list', ())
    []
    >>> vtor.check('bool_list', [])
    []
    >>> check_res = vtor.check('bool_list', (True, False))
    >>> check_res == [True, False]
    1
    >>> check_res = vtor.check('bool_list', [True, False])
    >>> check_res == [True, False]
    1
    >>> vtor.check('bool_list', [True, 'a'])
    Traceback (most recent call last):
    VdtTypeError: the value "a" is of the wrong type.
    """
    return [is_bool(mem) for mem in is_list(value, min, max)]

def is_float_list(value, min=None, max=None):
    """
    Check that the value is a list of floats.
    
    You can optionally specify the minimum and maximum number of members.
    
    Each list member is checked that it is a float.
    
    >>> vtor.check('float_list', ())
    []
    >>> vtor.check('float_list', [])
    []
    >>> vtor.check('float_list', (1, 2.0))
    [1.0, 2.0]
    >>> vtor.check('float_list', [1, 2.0])
    [1.0, 2.0]
    >>> vtor.check('float_list', [1, 'a'])
    Traceback (most recent call last):
    VdtTypeError: the value "a" is of the wrong type.
    """
    return [is_float(mem) for mem in is_list(value, min, max)]

def is_string_list(value, min=None, max=None):
    """
    Check that the value is a list of strings.
    
    You can optionally specify the minimum and maximum number of members.
    
    Each list member is checked that it is a string.
    
    >>> vtor.check('string_list', ())
    []
    >>> vtor.check('string_list', [])
    []
    >>> vtor.check('string_list', ('a', 'b'))
    ['a', 'b']
    >>> vtor.check('string_list', ['a', 1])
    Traceback (most recent call last):
    VdtTypeError: the value "1" is of the wrong type.
    """
    return [is_string(mem) for mem in is_list(value, min, max)]

def is_ip_addr_list(value, min=None, max=None):
    """
    Check that the value is a list of IP addresses.
    
    You can optionally specify the minimum and maximum number of members.
    
    Each list member is checked that it is an IP address.
    
    >>> vtor.check('ip_addr_list', ())
    []
    >>> vtor.check('ip_addr_list', [])
    []
    >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8'))
    ['1.2.3.4', '5.6.7.8']
    >>> vtor.check('ip_addr_list', ['a'])
    Traceback (most recent call last):
    VdtValueError: the value "a" is unacceptable.
    """
    return [is_ip_addr(mem) for mem in is_list(value, min, max)]

fun_dict = {
    'integer': is_integer,
    'float': is_float,
    'ip_addr': is_ip_addr,
    'string': is_string,
    'boolean': is_bool,
}

def is_mixed_list(value, *args):
    """
    Check that the value is a list.
    Allow specifying the type of each member.
    Work on lists of specific lengths.
    
    You specify each member as a positional argument specifying type
    
    Each type should be one of the following strings :
      'integer', 'float', 'ip_addr', 'string', 'boolean'
    
    So you can specify a list of two strings, followed by
    two integers as :
    
      mixed_list('string', 'string', 'integer', 'integer')
    
    The length of the list must match the number of positional
    arguments you supply.
    
    >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')"
    >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True))
    >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
    1
    >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True'))
    >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
    1
    >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True))
    Traceback (most recent call last):
    VdtTypeError: the value "b" is of the wrong type.
    >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a'))
    Traceback (most recent call last):
    VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short.
    >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b'))
    Traceback (most recent call last):
    VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long.
    >>> vtor.check(mix_str, 0)
    Traceback (most recent call last):
    VdtTypeError: the value "0" is of the wrong type.
    
    This test requires an elaborate setup, because of a change in error string
    output from the interpreter between Python 2.2 and 2.3 .
    
    >>> res_seq = (
    ...     'passed an incorrect value "',
    ...     'yoda',
    ...     '" for parameter "mixed_list".',
    ... )
    >>> if INTP_VER == (2, 2):
    ...     res_str = "".join(res_seq)
    ... else:
    ...     res_str = "'".join(res_seq)
    >>> try:
    ...     vtor.check('mixed_list("yoda")', ('a'))
    ... except VdtParamError, err:
    ...     str(err) == res_str
    1
    """
    try:
        length = len(value)
    except TypeError:
        raise VdtTypeError(value)
    if length < len(args):
        raise VdtValueTooShortError(value)
    elif length > len(args):
        raise VdtValueTooLongError(value)
    try:
        return [fun_dict[arg](val) for arg, val in zip(args, value)]
    except KeyError, e:
        raise VdtParamError('mixed_list', e)

def is_option(value, *options):
    """
    This check matches the value to any of a set of options.
    
    >>> vtor.check('option("yoda", "jedi")', 'yoda')
    'yoda'
    >>> vtor.check('option("yoda", "jedi")', 'jed')
    Traceback (most recent call last):
    VdtValueError: the value "jed" is unacceptable.
    >>> vtor.check('option("yoda", "jedi")', 0)
    Traceback (most recent call last):
    VdtTypeError: the value "0" is of the wrong type.
    """
    if not isinstance(value, StringTypes):
        raise VdtTypeError(value)
    if not value in options:
        raise VdtValueError(value)
    return value

def _test(value, *args, **keywargs):
    """
    A function that exists for test purposes.
    
    >>> checks = [
    ...     '3, 6, min=1, max=3, test=list(a, b, c)',
    ...     '3',
    ...     '3, 6',
    ...     '3,',
    ...     'min=1, test="a b c"',
    ...     'min=5, test="a, b, c"',
    ...     'min=1, max=3, test="a, b, c"',
    ...     'min=-100, test=-99',
    ...     'min=1, max=3',
    ...     '3, 6, test="36"',
    ...     '3, 6, test="a, b, c"',
    ...     '3, max=3, test=list("a", "b", "c")',
    ...     '''3, max=3, test=list("'a'", 'b', "x=(c)")''',
    ...     "test='x=fish(3)'",
    ...    ]
    >>> v = Validator({'test': _test})
    >>> for entry in checks:
    ...     print v.check(('test(%s)' % entry), 3)
    (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'})
    (3, ('3',), {})
    (3, ('3', '6'), {})
    (3, ('3',), {})
    (3, (), {'test': 'a b c', 'min': '1'})
    (3, (), {'test': 'a, b, c', 'min': '5'})
    (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'})
    (3, (), {'test': '-99', 'min': '-100'})
    (3, (), {'max': '3', 'min': '1'})
    (3, ('3', '6'), {'test': '36'})
    (3, ('3', '6'), {'test': 'a, b, c'})
    (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'})
    (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'})
    (3, (), {'test': 'x=fish(3)'})
    """
    return (value, args, keywargs)


if __name__ == '__main__':
    # run the code tests in doctest format
    import doctest
    m = sys.modules.get('__main__')
    globs = m.__dict__.copy()
    globs.update({
        'INTP_VER': INTP_VER,
        'vtor': Validator(),
    })
    doctest.testmod(m, globs=globs)

"""
    TODO
    ====
    
    Consider which parts of the regex stuff to put back in
    
    Can we implement a timestamp datatype ? (check DateUtil module)
    
    ISSUES
    ======
    
    If we could pull tuples out of arguments, it would be easier
    to specify arguments for 'mixed_lists'.
    
    CHANGELOG
    =========
    
    2005/12/16
    ----------
    
    Fixed bug so we can handle keyword argument values with commas.
    
    We now use a list constructor for passing list values to keyword arguments
    (including ``default``) : ::
    
        default=list("val", "val", "val")
    
    Added the ``_test`` test. {sm;:-)}
    
    0.2.1
    
    2005/12/12
    ----------
    
    Moved a function call outside a try...except block.
    
    2005/08/25
    ----------
    
    Most errors now prefixed ``Vdt``
    
    ``VdtParamError`` no longer derives from ``VdtError``
    
    Finalised as version 0.2.0
    
    2005/08/21
    ----------
    
    By Nicola Larosa
    
    Removed the "length" argument for lists and strings, and related tests
    
    2005/08/16
    ----------
    
    By Nicola Larosa
    
    Deleted the "none" and "multiple" types and checks
    
    Added the None value for all types in Validation.check
    
    2005/08/14
    ----------
    
    By Michael Foord
    
    Removed timestamp.
    
    By Nicola Larosa
    
    Fixed bug in Validator.check: when a value that has a default is also
    specified in the config file, the default must be deleted from fun_kwargs
    anyway, otherwise the check function will get a spurious "default" keyword
    argument
    
    Added "ip_addr_list" check
    
    2005/08/13
    ----------
    
    By Nicola Larosa
    
    Updated comments at top
    
    2005/08/11
    ----------
    
    By Nicola Larosa
    
    Added test for interpreter version: raises RuntimeError if earlier than
    2.2
    
    Fixed last is_mixed_list test to work on Python 2.2 too
    
    2005/08/10
    ----------
    
    By Nicola Larosa
    
    Restored Python2.2 compatibility by avoiding usage of dict.pop
    
    2005/08/07
    ----------
    
    By Nicola Larosa
    
    Adjusted doctests for Python 2.2.3 compatibility, one test still fails
    for trivial reasons (string output delimiters)
    
    2005/08/05
    ----------
    
    By Michael Foord
    
    Added __version__, __all__, and __docformat__
    
    Replaced ``basestring`` with ``types.StringTypes``
    
    2005/07/28
    ----------
    
    By Nicola Larosa
    
    Reformatted final docstring in ReST format, indented it for easier folding
    
    2005/07/20
    ----------
    
    By Nicola Larosa
    
    Added an 'ip_addr' IPv4 address value check, with tests
    
    Updated the tests for mixed_list to include IP addresses
    
    Changed all references to value "tests" into value "checks", including
    the main Validator method, and all code tests
    
    2005/07/19
    ----------
    
    By Nicola Larosa
    
    Added even more code tests
    
    Refined the mixed_list check
    
    2005/07/18
    ----------
    
    By Nicola Larosa
    
    Introduced more VdtValueError subclasses
    
    Collapsed the ``_function_test`` and ``_function_parse`` methods into the
    ``check`` one
    
    Refined the value checks, using the new VdtValueError subclasses
    
    Changed "is_string" to use "is_list"
    
    Added many more code tests
    
    Changed the "bool" value type to "boolean"
    
    Some more code cleanup
    
    2005/07/17
    ----------
    
    By Nicola Larosa
    
    Code tests converted to doctest format and placed in the respective
    docstrings, so they are automatically checked, and easier to update
    
    Changed local vars "min" and "max" to "min_len", "max_len", "min_val" and
    "max_val", to avoid shadowing the builtin functions (but left function
    parameters alone)
    
    Uniformed value check function names to is_* convention
    
    ``date`` type name changed to ``timestamp``
    
    Avoided some code duplication in list check functions
    
    Some more code cleanup
    
    2005/07/09
    ----------
    
    Recoded the standard functions
    
    2005/07/08
    ----------
    
    Improved paramfinder regex
    
    Ripped out all the regex stuff, checks, and the example functions
    (to be replaced !)
    
    2005/07/06
    ----------
    
    By Nicola Larosa
    
    Code cleanup
"""

#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2016 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
####


import codecs, glob, random, shutil, time, sys, os, argparse, psutil, subprocess
import regex as re

#import conll

verbose=False
#verbose=True



def parsing(infile, outfolder="parses/", memory=None, lemmatized=False):
	"""
	parsing function
	parserType is always graph!
	infile: an empty conll file
	
	"""
	if outfolder[-1]!="/": outfolder=outfolder+"/"
	outfile = outfolder+os.path.basename(infile)
	if outfile.endswith(".empty.conll"): outfile=outfile[:-len(".empty.conll")]

	if not memory: memory=str(psutil.virtual_memory().available/1000000000)+"G"
	anna, lemclass, tagclass, parseclass="mate/anna-3.3.jar", "is2.lemmatizer.Lemmatizer", "is2.tag.Tagger", "is2.parser.Parser"
	
	modelFolder="models/"
	lemodel=modelFolder+"LemModel"
	tagmodel=modelFolder+"TagModel"
	parsemodel=modelFolder+"ParseModel"
	
	lemcommand="java -Xmx{memory} -cp {anna} {lemclass} -model {lemodel} -test {infile} -out {outfile}.lem.conll".format(memory=memory, anna=anna, lemclass=lemclass, infile=infile, lemodel=lemodel, outfile=outfile)
	tagcommand="java -Xmx{memory} -cp {anna} {tagclass} -model {tagmodel} -test {outfile}.lem.conll -out {outfile}.tag.conll".format(memory=memory, anna=anna, tagclass=tagclass, tagmodel=tagmodel, outfile=outfile)
	parsecommand="java -Xmx{memory} -cp {anna} {parseclass} -model {parsemodel} -test {outfile}.tag.conll -out {outfile}.parse.conll".format(memory=memory, anna=anna, parseclass=parseclass, parsemodel=parsemodel, outfile=outfile)
	
	if lemodel and lemodel[-1]!="/":
		if verbose:print "\n\n========== lemmatizing...", lemcommand
		p1 = subprocess.Popen([lemcommand],shell=True, stdout=subprocess.PIPE)
		out, err = p1.communicate()
		if verbose:
			print out, err
	else: # TODO: add this part (for non inflectional languages like Chinese and for pre-lemmatized files)
		if lemmatized:
			print "copying",outfolder+os.path.basename(infile),"as lemma file"
			shutil.copyfile(outfolder+os.path.basename(infile), outfolder+os.path.basename(infile)+".lem.conll")
		else:
			print "adding toks as lems",outfolder+os.path.basename(infile)
			trees=conll.conllFile2trees(infile)
			with codecs.open(outfolder+os.path.basename(infile)+".lem.conll","w","utf-8") as lemf:
				for tree in trees:
					lemf.write(newconvert.treeToEmptyConll14Text(tree, lemma=False)+"\n")
	if verbose:
		print "\n\n========== tagging...", tagcommand
	p1 = subprocess.Popen([tagcommand],shell=True, stdout=subprocess.PIPE)
	out, err = p1.communicate()
	if verbose: 
		print out, err
		print "\n\n========== dep analysis...", parsecommand
	p1 = subprocess.Popen([parsecommand],shell=True,  stdout=subprocess.PIPE)
	out =  p1.stdout.read()
	if verbose: 
		print out
		print "\n\n========== parsed"

	#if checkIntegrity(outfile+'_parse') == False:
		#print "*********ERROR IN FILE", outfile+"_parse", "Please Try again*********"
	return outfile+".parse.conll"


def lireDictionnaires():
	"""
	Lecture des fichiers du lexique
	"""
	droporfeo='lexiqueMultiMots/' # where we can find the lexique folder
	specialCharDic={}
	#onlyLetters=re.compile(ur'''^\w+$''',re.U+re.I)	
	for lexfile in glob.glob(droporfeo+'*.sfplm'):
		# Lecture des fichiers .sfplm
		#print "reading",lexfile
		with codecs.open(lexfile, "r", "utf8") as f:
			for ligne in f:
				if len(ligne) and "\t" in ligne:
					t,lem=ligne.strip().split("\t")
					#if (not onlyLetters.match(t[0])) or (not onlyLetters.match(t[1:-1].replace("-",""))) or not onlyLetters.match(t[-1]) :
					specialCharDic[t]=None
	if verbose: print len(specialCharDic),"special character words"
	specialCharWords = sorted(specialCharDic, key=len, reverse=True)
	return specialCharWords


	
def removePuncsFromConllfile(conllfile):
	"""
	transforms the roots (0) to empty (-1)
	"""
	with codecs.open(conllfile,"r","utf-8") as f:
		conlltext=f.read()
		conlltext=conlltext.replace("0	_	punc","-1	_	punc")
	with codecs.open(conllfile,"w","utf-8") as f:
		f.write(conlltext)

	

def emptyFromSentence(sentencefile, remultimatch=None,outfolder="."):
	"""
	sentencefile with one sentence per line --> conll14.empty.conll
	if remultimatch not empty, it is used for tokenization
	"""
	newname=os.path.basename(sentencefile)
	if newname.endswith(".txt"): newname=newname[:-len(".txt")]
	newname+=".empty.conll"
	outname=os.path.join(outfolder,newname)
	with codecs.open(sentencefile,"r","utf-8") as f, codecs.open(outname,"w","utf-8") as g:
		for li in f:
			if remultimatch: 	toks = tokenize(li,remultimatch)
			else:			toks = simpletokenize(li)
			for num,tok in enumerate( toks ):
				g.write("\t".join([str(num+1), tok ]+["_"]*12)+'\n')
			g.write("\n")
	return outname


def simpletokenize(text, returnMatchInfo=False):
	"""
	simple punctuation and space based tokenization
	
	specific for french (apostrophes are glued to the precedent word: d' un
	in english we'd need: do n't Mike 's
	"""
	
	reponct=re.compile(ur'''(\s*[.;:,!?\(\)Â§"'Â«Â»\d]+)''',re.U+re.M) # prepare for default punctuation matching. removed - from list
	renogroupponct=re.compile(ur'''(\s*[;:,Â«Â»\(\)"])''', re.U+re.M) # signs that have to be alone - they cannot be grouped	
	
	## do the remaining simple token-based splitting
	toks=[]
	text=reponct.sub(r" \1 ",text).replace(" '","'") # spaces around punctuation, but not before hyphen (french specific!)
	text=renogroupponct.sub(r" \1 ",text).replace(" ~","~") # spaces around no group punctuation
	return text.split()
	
	
def numurltokenize(text, returnMatchInfo=False):
	"""
	number and url tokenization
	
	"""
	reurl=re.compile(ur'''(https?://|\w+@)?[\w\d\%\.]*\w\w\.\w\w[\w\d~/\%\#]*(\?[\w\d~/\%\#]+)*''', re.U+re.M+re.I)
	resignswithnumbers=re.compile(ur'''\d+[\d,.\s]+''', re.U+re.M)
	
	# do the url recognition
	toks=[] # couples : (tok, todo=0, done=1)
	laststart=0
	for m in reurl.finditer(text):
		#print 'reurl:%02d-%02d: %s' % (m.start(), m.end(), m.group(0))
		toks += [(text[laststart:m.start()],0), (m.group(0).strip(),1)] #
		#print toks
		laststart=m.end()
	toks += [(text[laststart:],0)] #
	#print toks
	
	# do the number recognition
	ntoks=[]
	for text,done in toks:
		if done:
			ntoks+=[(text,done)]
		else:
			laststart=0
			for m in resignswithnumbers.finditer(text):
				#print 'resignswithnumbers:%02d-%02d: %s' % (m.start(), m.end(), m.group(0))
				ntoks += [(text[laststart:m.start()],0), (m.group(0).strip(),1)] #
				#print toks
				laststart=m.end()
			ntoks += [(text[laststart:],0)] #
	#print ntoks
	
	if returnMatchInfo: 	return ntoks
	else:			return [t for t,done in ntoks]
	


def tokenize(line, remultimatch):
	"""
	tokenization of line
	uses remultimatch = compiled list of words ordered by size (bigger first)
	returns list of tokens
	"""
	
	# prepare line:
	line=line.replace(u"â","'")
	respaces=re.compile(ur'\s+')
	line=respaces.sub(r" ",line)
	
	# go!
	toks=[]
	for chunk, done in numurltokenize(line,returnMatchInfo=True):
		if done:
			toks+=[chunk]
		else:
			laststart=0
			for m in remultimatch.finditer(chunk):
				toks += simpletokenize(chunk[laststart:m.start()]) + [m.group(0).strip()] # strip is needed for cases like "etc. !"
				laststart=m.end()
			toks+=simpletokenize(chunk[laststart:])
	
	return toks
	

def parseSentenceFile(sentencefile, remultimatch, outfolder=None, memory=None, removePunct=True):
	"""
	main function
	sentencefile: file with one sentence per line
	remultimatch: compiled re to match multiwords
	"""
	if not memory: memory=str(psutil.virtual_memory().available/1000000000)+"G"
	if not outfolder: outfolder="parses/"
	
	print "preparing",sentencefile,"..."
	emptyConll = emptyFromSentence(sentencefile, remultimatch=remultimatch, outfolder=outfolder)
	print "parsing",sentencefile,"..."
	parsedfile=parsing(emptyConll, outfolder=outfolder, memory=memory)
	print "cleaning",parsedfile,"..."
	if removePunct: removePuncsFromConllfile(parsedfile)


if __name__ == "__main__":
	
	parser = argparse.ArgumentParser(description='wrapper of tokenizer and mate parser with French syntactic models')
	parser.add_argument('-s','--sentence', help='sentence between quotes', type=lambda s: unicode(s, 'utf8'), required=False)
	parser.add_argument('-f','--sentencesFile', help='file containing one sentence per line', type=lambda s: unicode(s, 'utf8'), required=False)
	args = vars(parser.parse_args())
	
	ti=time.time()
	
	specialCharWords=lireDictionnaires()
	# prepare multiword matching:
	multimatch = ur"(( |\b)"+    ur"( |\b)|( |\b)".join([re.escape(mm) for mm in specialCharWords])   +ur"( |\b))"
	remultimatch=re.compile(multimatch,re.U+re.I)
	
	if args.get("sentencesFile",None):
		parseSentenceFile(args.get("sentencesFile",None),remultimatch=remultimatch)
	if args.get("sentence",None):
		with codecs.open("parses/singleSentence.txt","w","utf-8") as singleSentenceFile:
			singleSentenceFile.write(args.get("sentence",None)+"\n")
			parseSentenceFile("parses/singleSentence.txt",remultimatch=remultimatch)
		with codecs.open("parses/singleSentence.parse.conll","r","utf-8") as singleSentenceParse:
			print singleSentenceParse.read()
	
	if verbose: print "it took",time.time()-ti,"seconds"

#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2016 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

import os, cgitb, cgi,time, sys, glob
from time import asctime, localtime
sys.path.append('modules')
from logintools import login
from logintools import isloggedin
from logintools import logout
from lib import config, database
cgitb.enable()


##############################################################################################"

def start():
	form = cgi.FieldStorage()
	thisfile = os.environ.get('SCRIPT_NAME',".")
	userdir = 'users/'

	logint = form.getvalue("login",None)
	if logint == "logout":
		print logout(userdir) # printing cookie header. important!
		print "Content-Type: text/html\n" # blank line: end of headers
		print '<script type="text/javascript">window.location.href=".";</script>'
	
	project = (form.getvalue("project",None))
	if project: project =project.decode("utf-8")
	
	action = form.getvalue("action",None)
	if action: action =action.decode("utf-8")
	if action:
		if action.startswith("project_"):project=action[8:]
	project = project.replace("(","").replace(")","").replace("<","").replace(">","")  # May not contain <>() to avoid XSS attacks
	if project: action=u"project_"+project
	if action:
		#action, userconfig = login(form, userdir, thisfile, action.encode("utf-8"))
		#action, userconfig = login(form, userdir, thisfile, action)
		try: 
			action, userconfig = login(form, userdir, thisfile, action)
		except Exception,e:
			print "Content-Type: text/html\n" # blank line: end of headers
			print "Error in project.cgi! Can't read the user config files. Please check that the user files are readible and writable by the apache user "+action.encode("utf-8")
			import traceback
			print "traceback:",traceback.print_exc()
			print "Exception:",str(e)
			sys.exit("something's wrong")
	else:
		action, userconfig = login(form, userdir, thisfile, action)
	adminLevel, username, realname = int(userconfig["admin"]),userconfig["username"].decode("utf-8"),userconfig["realname"].decode("utf-8")
	
	print "Content-Type: text/html\n" # blank line: end of headers
	projectconfig=None
	if logint == "confirm":
		print "Your account has been confirmed. You can log on now." # blank line: end of headers
		print '<script type="text/javascript">setTimeout(window.location.href=".",10000);</script>'
	try:	
		projectconfig = config.configProject(project) # read in the settings of the current project
	except:
		print "Error! Can't read the configuration files. Please check that the files project.cfg, functions.cfg, and categories.cfg are present in the project folder	"
		sys.exit("something's wrong")
	if not projectconfig:
		##print "Content-Type: text/html\n" # blank line: end of headers
		print "something went seriously wrong: can't read the configuration of the project"
		if project: print project.encode("utf-8")
		##print "Content-Type: text/html\n" # blank line: end of headers
		print '<script type="text/javascript">window.location.href=".";</script>'
		sys.exit("something's wrong")
	#sql=database.SQL(project)
	#userid = sql.userid(username, realname)
	try:
		sql=database.SQL(project)
		userid = sql.userid(username, realname)
	except Exception , e:
		from sqlite3 import OperationalError
		if isinstance(e, OperationalError) and str(e)in["attempt to write a readonly database","unable to open database file"]:
			print "Error! Make your database and the path leading there from the arborator base modifiable by the apache user!"
		if isinstance(e, KeyError):
			print "Error! the configuration file does not contain all the necessary keys:",str(e)
		else:
			print "strange error accessing the database.<br>",str(e)
			import traceback
			
			traceback.print_exc()
		sys.exit("something's wrong")
	
	if (not projectconfig) and (not action):
		print "something went seriously wrong: can't read the configuration of the project",project.encode("utf-8")
		#print '<script type="text/javascript">window.location.href=".";</script>'
		sys.exit("something's wrong")
	

	
	return project,projectconfig,sql,thisfile,username,userid,adminLevel,form

	
	
def export(textname, exportNumber, exportType, project):
	#textname=textname.encode("utf-8")
	print "<h2>Exporting",textname.encode("utf-8"),"(text number",exportNumber,")</h2>"
	
	if textname:
		fc,users,sc,doublegovs=sql.exportAnnotations(int(exportNumber), textname, exportType )
		if fc==0: 	print "<div style='padding:10;' class='ui-state-error ui-corner-all'>No files were exported, probably because the file is assigned to nobody.</div><br/>"
		else:		
			print "Exported",fc,"files and a total of",sc,"sentences.<br/><br/>"
			if exportType in ["allconll","allxml"]:
				print "Exported all existing annotated trees into",
				if exportType =="allconll": print textname.encode("utf-8")+".user.trees.conll files.<br/>"
				else: print textname.encode("utf-8")+".user.trees.rhaps.xml files.<br/>"
			else: 
				print "Exported complete files for each assignment into"
				if exportType =="todoconll": print textname.encode("utf-8")+".user.complete.conll10 files.<br/>"
				else: print textname.encode("utf-8")+".user.complete.rhaps.xml files.<br/>"
		print "<br/><div style='padding:10;'  class='ui-state-highlight ui-corner-all'>",
		if fc==1:print "The export file for",users[0],"is"
		else: print "All the",fc,"export files (for",(", ".join(users)).encode("utf-8")+") are"
		print " in the export folder <strong style='color:#DD137B'><a href='projects/{project}/export' target='_blank'>inside the project folder</a></strong> on the server.</div><br/>".format(project=project.replace("'","\\'").replace('"','\\"').encode("utf-8"))
		if doublegovs: print "<div style='padding:10;' class='ui-state-error ui-corner-all'>Achtung!<br/> The annotation contains multiple governors for one or more nodes. The lines have been doubled, and thus this is not a common Conll format!</div>"
	else:
		print "problem: no textname"
	
def reaction(project,projectconfig,sql,userid,form,query):
	"""
	contains reaction to actions, eg. uploading, erasing, exporting, ...
	"""
	filename = form.getvalue("filename",None)
	if filename: # upload file	
		from lib.treebankfiles import uploadConll
		filetype = form.getvalue("filetype",None)
		print "trying to upload",filename,filetype,"into the database of the project",project.encode("utf-8")
		if filetype in ["conll4","conll10","conll14"]:
			print "---------- here we go"
			nrsentences = uploadConll(sql,filename)
			print """<div class="ui-widget ui-widget-content ui-corner-all box">I added {nrsentences} sentences from {filename}.</div>""".format(nrsentences=nrsentences,filename=filename)
		else:	print """<div class="ui-widget ui-widget-content ui-corner-all box">Strange filetype: {filename}.</div>""".format(filename=filename)

	eraseNumber = form.getvalue("eraseNumber",None)
	if eraseNumber: # erase text
		print "erasing text number ",eraseNumber,"<br/>"
		sc=sql.removeText(int(eraseNumber))
		print "erased",sc,"sentences."

	#statustextid = form.getvalue("statustextid",None)
	#if statustextid: # erase text
		##print "statustextid, text number:",statustextid,"<br/>"
		#sql.statustext(int(statustextid),userid)

	exportNumber = form.getvalue("exportNumber",None)
	textname=None
	if exportNumber: # export text
		print '<div class="ui-widget ui-widget-content ui-corner-all box" >'
		
		textname = form.getvalue("textname",None)
		exportType = form.getvalue("exportType",None)
		#print exportNumber,type(exportNumber)
		exportNumber=int(exportNumber)
		if exportNumber==-1 :
			for t,tid in sorted( [(t,tid) for tid,t,nrtokens in sql.getall(None, "texts", None, None)]):
				export(t, tid, exportType, project)
			
		else: export(textname, exportNumber, exportType, project)
		
		print '</div>'

		
	userchoice = form.getlist('userchoice')
	textid = form.getvalue("textid",None)

	if userchoice and textid: # change todos
		removeid = int(form.getvalue("removeid",0))
		validator = int(form.getvalue("validator",0))
		if removeid:
			sql.removetextfromuser(int(textid),removeid)
		else:
			u=userchoice[0].split("(")[-1][:-1]
			sql.addtext2user(textid,u,validator)
			
	
	exochoice = form.getvalue("exochoice",None)
	if exochoice:
		
		textid = form.getvalue("textid",None)
		exotoknum = form.getvalue("exotoknum",None)
		
		print "changing exostatus of text id",textid,"to",exochoice
		sql.setExo(textid, exochoice, exotoknum)
		
	
	if query: # search results	
		print """<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;">"""
		
		res = sql.snippetSearch(query)
		if res: 
			print "<h2>Search results for {query}:</h2>".format(query=query)
			print res.encode("utf-8")
		else: print "no results for {query}</h2>".format(query=query)
		print """</div>"""
		
	




def printhtmlheader(project):
	
	print """<html>
			<head>
			<title>Arborator - {project} Project</title>
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
	<script type="text/javascript" src="script/jquery.js"></script>
	<script type="text/javascript" src="script/jquery-ui-1.8.18.custom.min.js"></script>
	<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen"/>
	<link href="css/arborator.css" rel="stylesheet" type="text/css">
	<script type="text/javascript">	
	project="{project}";
	userid="{userid}";
	username="{username}";
	""".format(project=project,userid=userid,username=username.encode("utf-8"))
	
	print """
	$('html').click(function() {
		$("#exportdiv").css({ display: 'none' } );	
		$("#userform").css({ display: 'none' } );	
		$("#exoform").css({ display: 'none' } );	
		});

	edit = function(textid,opensentence) {
		$("#textid").attr("value", textid );
		$("#opensentence").attr("value", opensentence );
		$("#editorform").submit();
	}
	removeSample = function(textid, text) {
		$("#eraseNumber").attr("value",textid);
		$("#question").html("Are you sure that you want to erase the text "+text+" from the database?")
		$('#dialog').dialog('open');
		}
	exportAnnos = function(textid, text, offset) {
		$("#exportNumber").attr("value",textid);
		$("#textname").attr("value",text);
		offset.left=offset.left-280;
		$("#exportdiv").offset(offset).css({ display: 'inline' } );
		}	
	exportGo = function(type) {
		$("#exportType").attr("value",type);
		console.log($("#export"))
		$("#export").submit();
		$("#dialog").html("<img src='images/loading.gif'>");
		$("#dialog").dialog({
			height: 230,
 			width:150,
			modal: true,
			title:"Please wait!!!<br>This can be long.",
			buttons: {}
			});
		$('#dialog').dialog('open');
	}
	adduser = function(textid, text, offset) {
		$("#utextid").attr("value",textid);
		$("#uremove").attr("value",0);
		$("#userform").css({ display: 'block',top: offset.top, left: offset.left } );
		}
		
	userchoice = function(v) {
		$("#validator").attr("value",v);
		$("#uremove").attr("value",0);
				
		$("#useraddremove").submit();	
		}
	
	userremove = function(tid,uid) {	
		$("#utextid").attr("value",tid);
		$("#uremove").attr("value",uid);
		$("#useraddremove").submit();
		}
	nextstatus = function(tid) {
		//console.log("nextstatus",tid)	
	
		$.ajax({
			type: "POST",
			url: "statusChange.cgi",
			data: {"project":project,"userid":userid,"username":username,"textid":tid}, 
			success: function(answer){
					$("#textStatus"+tid).html(answer.status);
					//console.log("changed!",answer);
					
				},
			error: function(XMLHttpRequest, textStatus, errorThrown){
				console.log("error",project,erasetreeid)
				
				}
			});
	
		}	
	
	
	
	nl2br = function(str) {   
		return (str + '').replace(/([^>\\r\\n]?)(\\r\\n|\\n\\r|\\r|\\n)/g, '$1'+ '<br>' +'$2');
	}

	startmate = function() {
		//$("#startmateform :input").prop('disabled', true);
		//checkMate();
		$("#parseStatus").css('background-image', 'url("images/ajax-loader.gif")');  
		$.ajax({
			type: "POST",
			url: "mate.cgi",
			data: {"project":project,"parserType":$( "#parserType" ).val(), "whoseTrees":$( "#whoseTrees" ).val(), "evaluationPercent":$( "#evaluationPercent" ).val(), "additionnalLexicon":$( "#additionnalLexicon" ).val(), "resultAnnotator":$( "#resultAnnotator" ).val()}, 
			success: function(answer){
					$("#parseStatus").html(answer);
					console.log("finished!",answer);
					//clearInterval(timer);
				},
			error: function(XMLHttpRequest, textStatus, errorThrown){
				console.log("mate error",project)
				
				}
			});
	
		timer = setInterval(checkMate, 5000);
		}
	
	checkMate = function() {
		$.ajax({
			url : "mate/parse.log",
			dataType: "text",
			cache: false,
			processData: false,
			success : function (data) {
				$("#parseStatus").html(nl2br(data));
				if (data.match("^Ready"))
					{
						console.log("Done");
						clearInterval(timer);
						$("#parseStatus").css('background-image', 'none'); 
						//$("#startmateform :input").prop('disabled', false);
					}
				}
			});
			

			
		}
	
	
	$(function () {
		$("#dialog").dialog({
			bgiframe: true,
			autoOpen: false,
			height: 350,
 			width:400,
			modal: true,
			buttons: {
				
				Cancel: function() {
					$(this).dialog('close');
				},
				"OK": function() {
					$(this).dialog('close');					
					$("#erase").submit();
				}
				}
			});
			
		$('#exochoice').change(function ()
			{
				$('#exochange').submit();
			});	
			
		

	})
	

	getEvaluation = function(project,uid) {
		$("#evalubutton"+uid).replaceWith("<img id='evalubutton"+uid+"' src='images/ajax-loader.gif'>");
		$.ajax({
			type: "GET",
			url: "getEvaluation.cgi",
			data: {"project":project,"uid":uid}, 
			success: function(answer){
					$("#evalubutton"+uid).replaceWith(answer);
					//console.log("got!"+answer);
			
				},
			error: function(XMLHttpRequest, textStatus, errorThrown){
				console.log("error",project)
				alert("error erasing"+XMLHttpRequest+ '\\n'+textStatus+ "\\n"+errorThrown);
				}
			});
		
	}
	
	</script>
	
	</head>
	"""
	
	
	
def printheadline(project):
	project = project.replace("(","").replace(")","").replace("<","").replace(">","")
	if os.path.exists("projects/"+project+"/"+project+".png"):img="<img src='projects/"+project+"/"+project+".png' align='top' height='18'>"
	else: img=""
	query = form.getvalue("searchtext","")
	print 	"""<body id="body">
			<div id="center" class="center" style="width:100%">
				<div id="navigation" style="width:100%;margin:0px;border:0px;" class="arbortitle  ui-widget-header ui-helper-clearfix">
				<a href='.' style='position: fixed;left:1px;top:1px'><img src="images/arboratorNano.png" border="0"></a>
				<a href="project.cgi?project={project}" style='position: fixed;left:120px;top:5px;color:white;' title="project overview">{img} {project} Annotation Project</a>
				<div style='margin:5 auto;' id='sentinfo'>Project Overview</div>
					<form method="get" action="project.cgi"  id="searchform"  style='position: fixed;right:1px;top:1px'>
						<input type="text" id="searchtext" name="searchtext" value="{query}"/>
						<input type="hidden" id="project" name="project" value="{project}">
						<span title="search" onClick="$('#searchform').submit();" style="cursor:pointer;" class="ui-button-icon-primary ui-icon ui-icon-search"></span>
					</form>
				</div>
		""".format(project=project,img=img,query=query)
	if os.path.exists("sitemessage.html"):print open("sitemessage.html").read()
	return query


		

def printmyassignments(project,sql,projectconfig,userid): # assigned texts
	
	txt = u"""
	
	<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;">
	You have been assigned the following texts of the <b>{project}</b> Project<br/>
	""".format(project=project)

	txt += u"""Please select the text to annotate:<br/><br/><table class="whitable">"""
	txt += u"	<tr><td>text name</td><td>number of sentences</td><td>number of tokens</td><td>trees modified by you</td><td>status</td></tr>"
	scount,mcount,tcount=0,0,0
	for _,_,tid,todotype,status,comment in sql.getall(None, "todos",["userid"], [userid]):
		try:
			tid,t,nrtokens = sql.getall(None, "texts",["rowid"],[tid])[0]
		except:
			sql.removeText(tid)
			print "we found a todo for a non existing text and removed it."
			continue
		try: todotype=int(todotype)
		except: todotype=0
		
		
		if todotype:txt += u"	<tr><td><b><span class='val'>validation of <br></span><a  style='cursor:pointer;' onclick=edit('"+str(tid)+"',1)>"+ t +"</a></b></td>"
		else:	txt += u"	<tr><td><b><a  style='cursor:pointer;' onclick=edit('"+str(tid)+"',1)>"+ t +"</a></b></td>"
		sn=sql.getnumber(None, "sentences", ["textid"], [tid])
		scount+=sn
		txt += u"		<td>"+str(sn)+"</td>"
		tn=sql.getNumberTokensPerText(tid)
		tcount+=tn
		txt += u"		<td>"+str(tn)+"</td>"
		txt += u"		<td>&nbsp;"
		mc = sql.getNumberTreesPerUserAndText(userid,tid)
		mcount+=mc
		txt += str(mc)
		txt += u"</td>"
		txt += u"		<td><a  style='cursor:pointer;dispay:block' id='textStatus"+str(tid)+"' "
		txt+= "onclick=nextstatus('"+str(tid)+"')>"+ projectconfig["text status"].get(status,"ooooo") +"</a></td>" 
		txt += u"</tr>"
	txt += u"	<tr> </tr><tr><td>a total of:</td><td>{scount}</td><td>{tcount}</td><td>{mcount}</td></tr>".format(scount=scount,mcount=mcount,tcount=tcount)
	txt += u"</table></div>"

	if scount: print txt.encode("utf-8")
	#else: print u"""<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;">
	#You have not been assigned any text of the <b>{project}</b> Project<br/></div>
	#""".format(project=project).encode("utf-8")


def printalltexts(project,sql,adminLevel):############################# all texts of the project
	nrt,stotal,ttotal,tanno,tvali=sql.getNumberTexts(),0,0,0,0
	project = project.replace("(","").replace(")","").replace("<","").replace(">","")
	print """
	<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;" id='projectbox'>
	The <b>{project}</b> Project has {nrt} texts and {nrs} sentences. <br/>
	<br/>
	<table class='whitable'>
	<thead><tr><th>text name</th><th>number of sentences</th><th>number of tokens</th><th>sentence length</th><th >annotators</th><th>validator</th><th title='non sollicitated trees by users not assigned to this text are listed here.'>other trees</th><th>exo</th><th>&nbsp;</th><th>&nbsp;</th><th>&nbsp;</th></tr></thead>
	""".format(project=project,nrt=nrt,nrs=sql.getNumberSentences())
	
	tuples=[]
	for t,tid,nrtokens in sorted( [(t,tid,nrtokens) for tid,t,nrtokens in sql.getall(None, "texts",None,None)]): # got to get it in alphabetical order .decode("utf-8")
		
		c1= u"<b><a  style='cursor:pointer;dispay:block'  onclick=edit('{tid}',1)>{t}</a></b></td>".format(tid=tid, t=t)
		sn=sql.getnumber(None, "sentences", ["textid"], [tid])
		
		stotal+=sn
		c2=str(sn)
		if not nrtokens:	nrtokens=sql.getNumberTokensPerText(tid) # ,recompute=True
		#nrtokens=sql.getNumberTokensPerText(tid,recompute=True) # recomputes the token number
		ttotal+=nrtokens
		c3=str(nrtokens)
		if sn: c4=str(round(float(nrtokens)/sn,2))
		else: c4="0"
		a,v = "",""
		tannot,tvalit=0,0
		reals=[]
		for _,uid,tid,todotype,status,comment in sql.getall(None, "todos",["textid"], [tid], orderby="order by userid"):
			try: todotype=int(todotype)
			except: todotype=0
			res= sql.getall(None, "users",["rowid"], [uid])
			if res :
				uid,user,realname = res[0]
				if not realname:	realname=sql.realname(user)
				av=u"<div  style='float:left;margin-right:10px;'>"+realname
				if status!="0": 	av+= "<i><sub>"+ projectconfig["text status"].get(status,"ooo")+ "</i></sub>"
				if adminLevel:	av+= "<div class='ui-button-icon-primary ui-icon ui-icon-circle-minus' style='float:right;cursor:pointer;margin-right:15px;'  onclick='var e=arguments[0];e.stopPropagation();userremove({tid},{uid});'></div>".format(tid=tid,uid=uid)
				av+="</div>"
				reals+=[realname]
				if todotype: # validators
					v+=av
					tvalit+=1
				else: # annotators
					a+=av
					tannot+=1		
		if not a: a="<div style='float:left;margin-right:15px;'>nobody yet</div>"
		if not v: v="<div style='float:left;margin-right:15px;'>nobody yet</div>" 
		tanno+=tannot
		tvali+=tvalit		
		c5,c6=a,v
		
		otherreals=[r for r, in sql.treesForText(tid) if r and r not in reals]
		#print "otherreals",otherreals, tid
		if otherreals:	c7=" ".join(otherreals)
		else:		c7="&nbsp;"
		if adminLevel:
			# exo stuff:
			et,esn = sql.getExo(tid)
			et = sql.exotypes[et] # transform exotypenumber into text
			if not esn: esns=""
			else: esns=esn
			c8= """<a style='cursor:pointer;' onclick='$("#exoform").val("{et}").css({{ top: $(this).offset().top-5, left: $(this).offset().left-5 }}).toggle();$("#exoform select").val("{et}");$("#exotoknum").val("{esn}");var e=arguments[0];e.stopPropagation();$("#etextid").val({tid})'>{et} {esns}</a>""".format(tid=tid,et=et,esn=esn,esns=esns)
			# user assignment:
			c9 = u"""<div style='float:left;cursor:pointer;' title='asign this text to a user' onclick="var e=arguments[0];e.stopPropagation();adduser({tid},'{t}',{{ top: e.pageY, left: e.pageX }}); ">asign<div style='float:right;cursor:pointer;' class='ui-button-icon-primary ui-icon ui-icon-person' ></div></div>&nbsp;&nbsp;&nbsp; """.format(tid=tid,t=t)
			# export
			c10 = u"""		<span style='cursor:pointer;'onclick="var e=arguments[0];e.stopPropagation();exportAnnos({tid},'{t}',$(this).offset());"  title='export annotations of this text' ><img src="images/dbexport.bw.png" border="0"></span>""".format(tid=tid,t=t)
		else:
			c8= sql.exotypes[sql.getExo(tid)[0]] # only exotype is shown
			c9= "&nbsp;"			
			c10 = """&nbsp;"""
			
		if adminLevel>1:	c11= u"""		<span class='ui-button-icon-primary ui-icon ui-icon-trash' style='cursor:pointer;' onclick="var e=arguments[0]; e.stopPropagation(); removeSample({tid},'{t}');"  title='remove all sentences from this file from database' ></span>""".format(tid=tid,t=t)
		else: 		c11= "&nbsp;"

		tuples+=[(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11)]
	
	for tup in tuples:
		print ('<tr><td>' + u"</td><td>".join(tup) + "</td></tr>").encode('utf-8')
	
	
	if adminLevel:allexport="""<span style='cursor:pointer;'onclick="var e=arguments[0];e.stopPropagation();exportAnnos(-1,'all',$(this).offset());"  title='export all annotations - very slow! blocks the database!' ><img src="images/dbexport.bw.png" border="0"></span>"""
	else:allexport=""
	evallinkhtml = '<a href="getEvaluation.cgi?project='+project+'">evaluation</a>'+' <a href="getEvaluation.cgi?project='+project+'&csv=csv">csv</a>'
	nvs=sql.getNumberValidatedSentences()
	nvt=sql.getNumberValidatedTokens()
	
	if nrt: # at least some texts exist
		if tanno: annota="{tanno} annotation assignments<br> (an average of {ava:.2f} assignments per text)".format(tanno=tanno,ava=float(tanno)/nrt)
		else: annota="&nbsp;"
		if tvali:	validata="{tvali} validation assignments <br> (an average of {avv:.2f} validators per text)".format(tvali=tvali,avv=float(tvali)/nrt)
		else:	validata="&nbsp;"
		
		if stotal:	avsentlen=round(float(ttotal)/stotal,2)
		else:		avsentlen="n/a"
		if nvs:		avsentlenval=round(float(nvt)/nvs,2)
		else:		avsentlenval="n/a"
		
		print """<TFOOT>	<tr style='padding-top:20;'><td>{nrt} texts</td><td>{stotal} sentences<br>{nvs} validated</td><td>{ttotal} tokens<br>{nvt} validated</td><td>{avsentlen}<br>{avsentlenval} validated</td><td>{annota}</td><td>{validata}</td><td>&nbsp;</td><td>{evallinkhtml}</td><td>&nbsp;</td><td>{allexport}&nbsp;</td><td>&nbsp;</td></tr> 	</TFOOT>""".format(stotal=stotal, nvs=nvs, nvt=nvt, nrt=nrt, ttotal=ttotal,avsentlen=avsentlen, avsentlenval=avsentlenval, annota=annota,validata=validata,evallinkhtml=evallinkhtml if adminLevel else "&nbsp;", allexport=allexport)
	print "</table></div>"


	
def printuserassignments(project,projectconfig,sql,adminLevel, lastseens):
	parser=projectconfig["configuration"]["importAnnotatorName"]
	teacher=projectconfig["configuration"].get("teacher",None)
	if teacher:
		teacherid=sql.userid(teacher)
	
	#scount,tcount=0,0
	
	
	tablecontent=""
	something=False
	for uid,u,rea in sql.getall(None, "users",None,None, orderby="order by realname"):
		if u!=parser:
			textids=[]
			treeidlists=[]
			totaltokens=[]
			titles=[]
			#sentenceidlists=[]
			txt=""
			
			sc,tc,tnrtrees,lastmodif=0,0,0,[]
			for _,uid,tid,todotype,status,comment in sql.getall(None, "todos",["userid"], [uid]): # for all the assignments (texts) for the given user
				try:	todotype=int(todotype)
				except: 	todotype=0
				textids+=[tid]
				status=projectconfig["text status"].get(status,"?")
				tid,t,nrtokens = sql.getall(None, "texts",["rowid"],[tid])[0]
				sn=sql.getnumber(None, "sentences", ["textid"], [tid])
				
				treenrs=sql.treeNrsForUserAndText(uid,tid)
				trees=sorted([(int(n),asctime(localtime(o))) for (sid,n,o,trid,) in treenrs])
				treeidlists+=[ [ (trid,sid) for (sid,n,o,trid,) in treenrs] ]
				nrtrees=len(treenrs)
				lastmodif+=[o for (sid,n,o,trid,) in treenrs]
				
				
				if sn==nrtrees:	trees="All"
				else:		trees=str(trees).replace("(","\n").replace("),","").replace("'","").replace(",",":").replace(")","")[1:-1] or "None"
				
				tn=nrtokens
				sc+=sn
				tc+=tn
				totaltokens+=[tn]
				titles+=[t]
				tnrtrees+=nrtrees
				if todotype:	txt+= "<span class='val'>"
				txt+= """			<span title="modified trees: {trees}">{t} (<span class='val'>{nrtrees}</span>/{sn}/{tn}/{status})</span>&nbsp;&nbsp;""".format(t=t.encode("utf-8"),sn=sn,tn=tn,status=status,nrtrees=nrtrees,trees=trees)
				if todotype:	txt+= "</span>  "
				something=True
			if sc:	
				tablecontent+= "	<tr ><td><b>{u}</b></td><td>".format(u=u)
				tablecontent+= txt
				avt="<span class='val'>{tnrtrees} ({avt:.1f}%)</span>".format(tnrtrees=tnrtrees,avt=float(tnrtrees)/sc*100)
			else:	
				continue
				tablecontent+= "uuu"
				avt=" 0"
			if lastmodif:		lastmodif=asctime(localtime(sorted(lastmodif)[-1]))
			if sc:			lastseen=asctime(localtime(lastseens.get(u,(0,0))[0]))
			else:			lastseen= "&nbsp;"
			tablecontent+= "			&nbsp;</td><td><b>{avt}</td><td>{sc}/{tc}<td>{lastseen}</td><td>{lastmodif}</td></b></td></tr>".format(tnrtrees=tnrtrees,avt=avt  ,sc=sc,tc=tc, lastseen=lastseen, lastmodif=lastmodif or "&nbsp;")	
			
			if teacher:
				
				tablecontent+= "<tr><td>{rea} </td><td colspan=5>".format(rea=rea.encode("utf-8"))
			
				tablecontent+= '''<input type="button" onclick="getEvaluation('{project}',{uid});" class="fg-button ui-state-default ui-corner-all" style="width:155px;z-index:33;" value="get {rea}'s evaluation" name="evalubutton{uid}" id="evalubutton{uid}" cursor="pointer">'''.format(project=project,uid=uid, rea=rea.encode("utf-8"))
				tablecontent+= "</td></tr>"
				tablecontent+= "<tr><td colspan=6 style='padding-bottom:20;'></td></tr>"
				#break
	if something:
		print """
		<div class="ui-widget ui-widget-content ui-corner-all box"  style="text-align:-moz-center;">
		The assignments of the <b>{project}</b> Project<br/>
		""".format(project=project)
		print """<br/><table class='whitable'>"""
		print """	<tr>
			<td>user name</td>
			<td>assigned texts: <br> text name (trees modified by user, total number of sentences, total number of tokens, status)</td>
			<td>total number of modified trees</td>
			<td>total number of sentences / <br>  total number of tokens</td> 
			<td>last seen</td> <td>last modification</td> """
		#if teacher: print "<td>result</td>"
		print "	</tr>"
		print tablecontent
		
		print "</table></div>"
		
	if adminLevel>1:
		print """<div class="ui-widget ui-widget-content ui-corner-all box">
	<a href="uploader.cgi?project={project}"><span class='ui-button-icon-primary ui-icon ui-icon-folder-open' style='' title='Add Conll or XML files containing annotations to the database' ></span>Add files to the database</a>
	</div></div></div></div></div></div>&nbsp;""".format(project=project)
	
	return now


	
def printmenues(project,sql):
	
	parser=projectconfig["configuration"]["importAnnotatorName"]
	print """<form method="get" action="editor.cgi" style="display: none;" id="editorform" >
			<input type="hidden" id="project" name="project" value="{project}">
			<input type="hidden" id="textid" name="textid" value="">
			<input type="hidden" id="opensentence" name="opensentence" value="1">
			
			<img src="images/ajax-loader.gif" border="0">
			<img src="images/loading.gif" border="0">
		</form>
	""".format(project=project)

	print """<form method="post" action="project.cgi?project={project}" style="display: none;" id="nextstatusform" >
			<input type="hidden" id="statustextid" name="statustextid" value="">
		</form>
	""".format(project=project)
	#<input type="hidden" id="project" name="project" value="{project}">
	
	print """<div id="exoform" style=" position:absolute;display: none;" onclick="var e=arguments[0];e.stopPropagation();">
			<form  method="post" action="project.cgi?project={project}" id="exochange" name="exochange">
				<input type="hidden" id="etextid" name="textid" value="">
				<select id="exochoice" name="exochoice" style="width:180px;"  >
					{options}
				</select>
				<input id="exotoknum" type="text" value="0" name="exotoknum" title="number of sentences per student (0=all)" style="width:20px;">
			</form>
		</div>		""".format(project=project, options="\n".join(["<option>"+ty+"</option>" for ty in sql.exotypes]))
	
	
	print """<div id="userform" style="position:absolute; display:none;" onclick="var e=arguments[0];e.stopPropagation();">
			<form  method="post" action="project.cgi?project={project}" id="useraddremove" name="useradd">
				<select id="userchoice" name="userchoice" style="width:180px;"  >""".format(project=project)

	user_rows = sorted(sql.getall(None, "users",None,None), key=lambda x: x[2].lower() if x[2] is not None else "")

	for uid,user,realname in user_rows:
			if user!=parser:
				#print [user]
				print "			<option>",
				if realname: print realname.encode('utf-8'),
				print " ("+user+")</option>"

		
	print """		</select>
				<div class='ui-button-icon-primary ui-icon ui-icon-circle-check' style='cursor:pointer; float:right;' onclick="var e=arguments[0];e.stopPropagation();userchoice(1);"  title='asign this user as validatator' ></div>
				<div class='ui-button-icon-primary ui-icon ui-icon-circle-plus' style='cursor:pointer; float:right;' onclick="var e=arguments[0];e.stopPropagation();userchoice(0);"  title='asign this user as annotator' ></div>
				<input type="hidden" id="utextid" name="textid" value="">
				<input type="hidden" id="uremove" name="removeid" value="false">
				<input type="hidden" id="validator" name="validator" value="0">
			</form>
		</div>"""

		
	print """<div class="ui-widget ui-widget-content ui-corner-all" style="position: absolute; padding: 5px;z-index:20;display:none;font-size:.8em;" id="exportdiv">
			<form id="export" name="export" method="POST"  action="project.cgi?project={project}" >
			
			
			
			<div id="radioset" class="ui-buttonset" style="margin-left:15px">
				export complete files per assignment as<br>
					<label for="radio1" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only ui-corner-left" onclick='exportGo("todosconll")' title="All assignments are exported as complete CoNLL files, independently of whether the annotator has actually changed the tree or not from the original parse tree. CoNLL files only contain the form, the lemma, the category, and the dependency.">
						<span class="ui-button-text">
							CoNLL file
						</span>
					</label><label for="radio2" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only  ui-corner-right" onclick='exportGo("todosxml")' title="All assignments are exported as complete XML files, independently of whether the annotator has actually changed the tree or not from the original parse tree.">
						<span class="ui-button-text">
								XML file
						</span>
					</label>
					
			</div>
			<br>
			<div id="radioset" class="ui-buttonset" style="margin-left:15px">
				export all trees by any annotator as<br>
					<label for="radio1" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only ui-corner-left"  onclick='exportGo("allconll")' title="All trees that any annotator has saved are exported, including the original parse trees. The resulting CoNLL files don't necessarily contain all sentences.  CoNLL files only contain the form, the lemma, the category, and the dependency.">
						<span class="ui-button-text">
							CoNLL file
						</span>
					</label><label for="radio2" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only  ui-corner-right" onclick='exportGo("allxml")' title="All trees that any annotator has saved are exported, including the original parse trees. The resulting XML files don't necessarily contain all sentences.">
						<span class="ui-button-text">
								XML file
						</span>
					</label>
					
			</div>
			<br>
			<div id="radioset" class="ui-buttonset" style="margin-left:15px">
				export most recent trees by any annotator as<br>
					<label for="radio1" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only ui-corner-left"  onclick='exportGo("lastconll")' title="The most recent tree for every sentence is exported, this may be the original parse trees. CoNLL files only contain the form, the lemma, the category, and the dependency.">
						<span class="ui-button-text">
							CoNLL file
						</span>
					</label><label for="radio2" class="ui-state-active ui-button ui-widget ui-state-default ui-button-text-only  ui-corner-right" onclick='exportGo("lastxml")' title="The most recent tree for every sentence is exported, this may be the original parse trees.">
						<span class="ui-button-text">
								XML file
						</span>
					</label>
					
			</div>
			<input type="hidden" id="exportNumber" name="exportNumber" value="">
			<input type="hidden" id="exportType" name="exportType" value="">
			<input type="hidden" id="textname" name="textname" value="">
			</form>
		</div>
		
		<div id="dialog" title="Confirmation" style="display: none;" >
			<div class="ui-state-error ui-corner-all" style="margin: 40px;padding: 10pt 0.7em;">
				<h2 id="question">Are you sure that you want to erase the text?</h2>
			</div>
		</div>
		<form method="post" action="project.cgi?project={project}" style="display: none;" id="erase" >
			<input type="hidden" id="eraseNumber" name="eraseNumber" value="">
			
		</form>
		
		""".format(project=project)

def printmate(project,projectconfig, sql, adminlevel):
	if projectconfig.get("configuration",{}).get("mate",{}):
	
		print """
	<div class="ui-widget ui-widget-content ui-corner-all box" style="text-align:-moz-center;">Mate:
	
		<form  method="post" id="startmateform" name="startmateform">
			For the <input type="hidden" id="etextid" name="textid" value="">
				<select id="parserType" name="parserType" style="width:100px;height:25px;"  >
					{parsertypeoptions}
				</select>-based parser, take
			<input type="radio" name="whoseTrees" value="v" checked>the validators' trees <input type="radio" name="whoseTrees">all validated trees,
			and use <input id="evaluationPercent" type="text" value="10" name="evaluationPercent" title="percentage of training data used for evaluation" style="width:33px;">% for evaluation.
			<br/>
			Use <input id="additionnalLexicon" type="text" value="" name="additionnalLexicon" title="name of additional lexicon to use" style="width:93px;"> as additional lexicon and store the parse results under
			<input id="resultAnnotator" type="text" value="mate" name="resultAnnotator" title="username for resulting trees" style="width:63px;">.
			<br/>
			<input type="button" cursor="pointer" id="buttonstartmate" name="buttonstartmate" value="start Mate" style="width:155px;z-index:33;" class="fg-button ui-state-default ui-corner-all" onclick="startmate();">
			
		</form>
		<div id="parseStatus" class="ui-widget ui-widget-content ui-corner-all box" style="background-position: center center;background-repeat:no-repeat;">			
		</div>
	
	</div>&nbsp;<br/>&nbsp;""".format(project=projectconfig.get("mate",None),parsertypeoptions="\n".join(["<option>"+ty+"</option>" for ty in ["graph","transition"]]))

#parserType
#whoseTrees
#evaluationPercent
#additionnalLexicon
#resultAnnotator

	
def printfooter(project,username,thisfile, now):
	"""
	"now" contains a list of all users currently online
	"""
	a=u"""<div class="arborfoot fg-toolbar ui-widget-header ui-helper-clearfix">logged in as {username}&nbsp;&nbsp;&nbsp;
	<a href="{thisfile}?login=logout&project={project}">logout</a>""".format(username=username,project=project,thisfile=thisfile)
	print (u"""<div class="arborfoot fg-toolbar ui-widget-header ui-helper-clearfix">logged in as {username}&nbsp;&nbsp;&nbsp;
	<a href="{thisfile}?login=logout&project={project}">logout</a>""".format(username=username,project=project,thisfile=thisfile)).encode("utf-8")
	if username!="guest": print (u'&nbsp;&nbsp;&nbsp;<a href="{thisfile}?login=editaccount&project={project}">edit the account {username}</a>'.format(username=username,project=project,thisfile=thisfile)).encode("utf-8")
	if adminLevel > 1: print (u'&nbsp;&nbsp;&nbsp;<a href="{thisfile}?login=admin&project={project}">User Administration</a>'.format(project=project,thisfile=thisfile)).encode("utf-8")
	if adminLevel: print (u'&nbsp;&nbsp;&nbsp;<a href="configedit.cgi?project={project}">Edit Configuration</a>'.format(project=project)).encode("utf-8")
	nowstring=u'<a  title="Ask questions or share your feelings with {r}..." href="mailto:{email}?subject=The%20Arborator%20is%20driving%20me%20crazy!">{r} ({u})</a>'
	now = [nowstring.format(u=u,r=unicode(r),email=email) for (u,r,email) in now if u!= username]
	if len(now)==1: print (u'&nbsp;&nbsp;&nbsp;You are not alone! This other user is online now: '+now[0]).encode("utf-8")
	elif len(now)>1: print (u'&nbsp;&nbsp;&nbsp;You are not alone! These other users are online now: '+u", ".join(now)).encode("utf-8")
	print "</div>"
	print '</body></html>'
	#&nbsp;&nbsp;&nbsp;<a href="admin.cgi?project={project}">Corpus Administration</a>
	

		
if __name__ == "__main__":
	
	
	project,projectconfig,sql,thisfile,username,userid,adminLevel,form = start()
	
	printhtmlheader(project.encode("utf-8"))
	query=printheadline(project.encode("utf-8"))
	
	reaction(project,projectconfig,sql,userid,form,query)
	printmyassignments(project,sql,projectconfig,userid)
	printalltexts(project.encode("utf-8"),sql,adminLevel)
	lastseens,now=sql.usersLastSeen()
	if adminLevel:	
		printuserassignments(project.encode("utf-8"),projectconfig,sql,adminLevel, lastseens)
		printmate(project.encode("utf-8"),projectconfig,sql,adminLevel)
	
	printmenues(project.encode("utf-8"),sql)
	printfooter(project,username,thisfile,now)
	
	










NOM	{"fill": "#BF7B3F"}
DET	{"fill": "#BF7B3F"}
ADJ	{"fill": "#BF7B3F"}
NUM	{"fill": "#BF7B3F"}
PRE	{"fill": "#BF7B3F"}
PRO	{"fill": "#BF7B3F"}
PRQ	{"fill": "#BF7B3F"}
VRB	{"fill": "#654b6d"}
VNF	{"fill": "#654b6d"}
VPP	{"fill": "#654b6d"}
ADN	{"fill": "#654b6d"}
ADV	{"fill": "#654b6d"}
CLI	{"fill": "#654b6d"}
CLN	{"fill": "#654b6d"}
CLS	{"fill": "#654b6d"}
CSU	{"fill": "#654b6d"}
COO	{"fill": "#43BF3F"}
INT	{"fill": "#A94AC7"}
X	{"fill": "#cc1225"}


NOM	{"fill": "#BF7B3F"}
DET	{"fill": "#BF7B3F"}
ADJ	{"fill": "#BF7B3F"}
NUM	{"fill": "#BF7B3F"}
PRE	{"fill": "#BF7B3F"}
PRO	{"fill": "#BF7B3F"}
PRQ	{"fill": "#BF7B3F"}
VRB	{"fill": "#654b6d"}
VNF	{"fill": "#654b6d"}
VPP	{"fill": "#654b6d"}
ADN	{"fill": "#654b6d"}
ADV	{"fill": "#654b6d"}
CLI	{"fill": "#654b6d"}
CLN	{"fill": "#654b6d"}
CLS	{"fill": "#654b6d"}
CSU	{"fill": "#654b6d"}
COO	{"fill": "#43BF3F"}
INT	{"fill": "#A94AC7"}
X	{"fill": "#cc1225"}


dep	{"stroke": "#bf7b3f","stroke-width":"1","stroke-dasharray": ""}
spe	{"stroke": "#bf7b3f","stroke-width":"1","stroke-dasharray": ""}
comp	{"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
ad	{"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
suj	{"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
root	{"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
aff	{"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
aux	{"stroke": "#000000","stroke-width":"1","stroke-dasharray": ""}
para	{"stroke": "#01949b","stroke-width":"1","stroke-dasharray": ""}
mark	{"stroke": "#43bf3f","stroke-width":"1","stroke-dasharray": ""}
dm	{"stroke": "#a94ac7","stroke-width":"1","stroke-dasharray": ""}
periph	{"stroke": "#a94ac7","stroke-width":"1","stroke-dasharray": ""}
morph	{"stroke": "#bfbd3f","stroke-width":"1","stroke-dasharray": ""}
disflink	{"stroke": "#cc1225","stroke-width":"1","stroke-dasharray": ""}

[configuration]
url			=	http://arborator.ilpga.fr/   # don't forget the trailing slash!
functionsfilename 		=	functions.config
categoriesfilename 		=	categories.config
defaultfunction		=	ADV
defaultcategory 		=	NN
defaultNumberSentences		=	50
newTree			=	nouvel arbre
erase			=	--ERASE--
root			=	ROOT
categoryindex		=	1 # the index in the list of shown features that will use category colors and open when clicked on 
defaultUser 		= 	default
directory			=	arborator
ericfuncsfilename 		=	ericfuncs.config
ericcatsfilename		=	ericcats.config
corpusfolder		=	corpus # in the root folder
db			=	arborator.db.sqlite
showAllTrees		=	True # TODO: bad hack
exportfolder		=	export # in the project folder
importAnnotatorName		=	parser # edit only before entering texts into the database
teacher	=	prof 
#baseAnnotatorName		=	prof # name of user that should serve as the base for the annotator to start her tree
#exoBaseAnnotatorName	=	x # name of user that should serve as the base for the student to start her tree
treeorder			=	newest # order of trees per sentence. what to put first. choices: newest, user, importAnnotator, default (alphabetical)
showTreesOfValidatedTexts	=	1 # give the number of the text status (see below) to be shown to all, anything but the numbers in text status will switch off this feature
allVisibleForNonAnnotators	=	1 # if 0: no, if 1: yes=if a user is not registered as annotator, she can see every tree
validatorsCanModifyTokens	=	1 # if 0 modif only by admins; if 1: validators, too
editable			=	1


[shown features]  # you decide which of the features of the token will be shown: t=token, cat=tag, ...
0 			=	t
1			=	tag
2			=	lemma

[shown sentencefeatures]  # you decide which of the features of the sentence will be shown: 
0 			=	markupIU

############################## scores for evalutation of student annotators #############
[evalutation scores]
governors			=	0
functions			=	0
categories			=	100
lemmas			=	0

############################## users whose annotation is visible to everyone #############
[visible to everyone]
0 			=	guest
1			=	parser


############################## different status of a text  #############
[text status]
0 			=	todo
1			=	ok
2			=	problem

############################## different status of a tree #############
[tree status]
0 			=	todo
1			=	ok
2			=	problem


############################## appearance of the tree javascript!!! #############
[look]
look			=	'''
tab=8; 			// space between tokens
line=25 		// line height
dependencyspace=320; 	// y-space for dependency representation
xoff=8; 		// placement of the start of a depdency link in relation to the center of the word
linedeps=6; 		// y distance between two arrows arriving at the same token (rare case)
pois=4; 		// size of arrow pointer
tokdepdist=15; 		// distance between tokens and depdendency relation
funccurvedist=8;	// distance between the function name and the curves highest point
depminh = 15; 		// minimum height for dependency
worddistancefactor = 2; // distant words get higher curves. this factor fixes how much higher.
rootTriggerSquare = 50; // distance from top of svg above which root connections are created and minimum width of this zone (from 0 to 50 par example the connection jumps to the middle)
extraspace=50 // extends the width of the svg in order to fit larger categories
defaultattris={"font": '14px "Arial"', "text-anchor":'start'};
attris = {"t":		{"font": '18px "Arial"', "text-anchor":'start',"fill": '#000',"cursor":'move',"stroke-width": '0'}, 
	  "cat":	{"font": '12px "Times"', "text-anchor":'start',"fill": '#036',"cursor":'pointer'},
	  "lemma":	{"font": '14px "Times"', "text-anchor":'start',"fill": '#036'},
	  "depline":	{"stroke": '#999',"stroke-width":'1',"stroke-dasharray": ''},
	  "deptext":	{"font": '12px "Times"', "font-style":'italic', "fill": '#999',"cursor":'pointer'},
	  "dragdepline":	{"stroke": '#8C1430',"stroke-width":'2',"stroke-dasharray": ''},
	  "dragdeplineroot":	{"stroke": '#985E16',"stroke-width":'2',"stroke-dasharray": ''},
	  "source":	{"fill": '#245606'},
	  "target":	{"fill": '#8C1430',"cursor": 'url("images/connect.png"),pointer', "font-style":'italic'}, 
	  "form":	{"font-style":'italic'}
	  };
'''
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2015 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####


import os, cgitb, cgi,time, sys, glob
from lib import config, conll, database
cgitb.enable()

def printhtmlheader():
	print """<html><head>
		<title>Arborator â Quickedit</title>
		<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
		
	
		
		<script type="text/javascript" src="script/jquery.js"></script>
		<script type="text/javascript" src="script/raphael.js"></script>
		
		<script type="text/javascript" src="script/jquery-ui-1.8.18.custom.min.js"></script>
		<script type="text/javascript" src="script/jquery.cookie.js"></script>
		
		<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />
		
		<script type='text/javascript' > 
		var currentsvg = "mmmm"; 
		</script>
		<script type="text/javascript" src="script/arborator.draw.js"></script>
		<script type="text/javascript" src="script/q.js"></script>
		<script type="text/javascript" src="script/jsundoable.js"></script>
		<script type="text/javascript" src="script/colpick.js"></script>
		
		<link href="css/arborator.css" rel="stylesheet" type="text/css">
		<link href="css/colpick.css" rel="stylesheet" type="text/css">

		
		<script type="text/javascript" src="annodoc/jquery.svg.min.js"></script>
		<script type="text/javascript" src="annodoc/jquery.svgdom.min.js"></script>
		<script type="text/javascript" src="annodoc/jquery.timeago.js"></script>
		<script type="text/javascript" src="annodoc/jquery-ui.min.js"></script>
		<script type="text/javascript" src="annodoc/waypoints.min.js"></script>
		<script type="text/javascript" src="annodoc/jquery.address.min.js"></script>

		<!--brat helper modules-->
		<script type="text/javascript" src="annodoc/configuration.js"></script>
		<script type="text/javascript" src="annodoc/util.js"></script>
		<script type="text/javascript" src="annodoc/annotation_log.js"></script>
		
		<!--<script type="text/javascript" src="annodoc/webfont.js"></script>-->
		<!--brat modules-->
		<script type="text/javascript" src="annodoc/dispatcher.js"></script>
		<script type="text/javascript" src="annodoc/url_monitor.js"></script>
		<script type="text/javascript" src="annodoc/visualizer.js"></script>

		// embedding configuration
		<script type="text/javascript" src="annodoc/config.js"></script>
		// project-specific collection data
		<script type="text/javascript" src="annodoc/collections.js"></script>

		// NOTE: non-local libraries
		<script type="text/javascript" src="annodoc/annodoc.js"></script>
		<script type="text/javascript" src="annodoc/conllu.js"></script>
				
		<link rel="stylesheet" href="annodoc/font-awesome.min.css">
		<link rel="stylesheet" type="text/css" href="annodoc/style.css"/>
		<link rel="stylesheet" type="text/css" href="annodoc/style-vis.css"/>
		<link rel="stylesheet" type="text/css" href="annodoc/hint.css"/>
		
		
		
	</head>"""
	
def printheadline():	
	print 	"""<body id="body">
			<div id="navigation" style="width:100%;margin:0px;border:0px;" class="arbortitle  ui-widget-header ui-helper-clearfix">
				<a href='.' style='position: fixed;left:1px;top:1px'><img src="images/arboratorNano.png" border="0" title="Arborator Main Page"></a>
				<img src="images/q.png" border="0" title="Arborator â Quickedit">
				<a style="position: fixed;right:1px;top:1px;right:5px;" id='help' title="restart the help animation"><img src="images/help.png" style="vertical-align: text-top" border="0">Help</a>
			</div>
			<div id="center" class="center" style="width:100%;float:right;top:0px;">
				<div id="trees">
			</div>
		"""

def printfooter():
	#print """
	#<p>The <code>acl:relcl</code> relation is used for relative clauses modifying
#a nominal. The relation points from the head of the nominal to the
#head of the relative clause.</p>



#<div class="annodoc">
#<pre><code class="language-conllu"># I wrote the letter with a quill.
#1  donner  donner  VERB  _  VerbForm=Inf  0  root  _  give
#2  les  le  DET  _  Definite=Def|Number=Plur  3  det  _  the
#3  jouets  jouet  NOUN  _  Gender=Masc|Number=Plur  1  dobj  _  toys
#4  Ã   Ã   ADP  _  _  6  case  _  to
#5  les  le  DET  _  Definite=Def|Number=Plur  6  det  _  the
#6  enfants  enfant  NOUN  _  Gender=Masc|Number=Plur  1  nmod  _  children
#</code></pre>


#<pre><code class="language-sdparse">J'ai vu l' homme qui t' aime \\n I saw the man who loves you
#acl:relcl(homme, aime)
#nsubj(aime, qui)
#dobj(aime, t')
#</code></pre>



#<pre><code class="language-conllu"># I wrote the letter with a quill.
#1   Ð¯         ja         PRON   _   Case=Nom|Number=Sing|Person=1|PronType=Prs        2   nsubj   _   I
#2   Ð½Ð°Ð¿Ð¸ÑÐ°Ð»   napisat'   VERB   _   Gender=Masc|Number=Sing|VerbForm=Part|Voice=Act   0   root    _   wrote
#3   Ð¿Ð¸ÑÑÐ¼Ð¾    pis'mo     NOUN   _   Case=Acc|Gender=Neut|Number=Sing                  2   dobj    _   the-letter
#4   Ð¿ÐµÑÐ¾Ð¼     pero       NOUN   _   Case=Ins|Gender=Neut|Number=Sing                  2   nmod    _   with-a-quill
#</code></pre>




#</div>


	#"""
	
	print "</div>"
	print '</body></html>'	
	
def printexport():
	print """
	<form method="post" action="convert_svg.cgi?project=quickie" name="ex" id="ex" style=" position:fixed;top:6px;right:30px;display:none;"  target="_blank">
		<input type="hidden" id="source" name="source" value="">
		<select id="exptype" name="type" class="ui-button ui-state-default ui-corner-all" style="height:16px;font: italic 10px Times,Serif;border:thin solid silver;" >
			<option>pdf</option>
			<option>ps</option>
			<option>odg</option>
			<option>jpg</option>
			<option>png</option>
			<option value="tif">tiff</option>
			<option>svg</option>
		</select>
		<input type="button" title="export" value="export" class="ui-button ui-state-default ui-corner-all" onClick="exportTree();"  style="padding: 0.0em 0.0em;" >
	</form>
	"""
	
def printforms():
	print """
	<div id="toggleAndBoxx" class="toggleAndBoxx">
	<div id="toggle" class="toggle" title="click here to show and hide the CoNLL input"></div>
	<div id="boxx" class="boxx">
		<div class="arrow" title="you can click here (or anywhere else outside the box) when you have finished editing the CoNLL format or sentence text area">â¬</div>
		<div id="textual" class="textual">Paste and edit CoNLL file below:
			<TEXTAREA NAME="conll" id="conll" title="Here you can paste and edit CoNLL files with 4, 10 or 14 columns" style="width:100%; height:400; background-image: url('images/conll10.png'); background-repeat: no-repeat; background-position: 95% bottom; "># conllu format
1	faut	falloir	V	_	_	0	root	_	must
2	que	que	C	_	_	4	mark	_	that
3	tu	tu	Pron	_	_	4	nsubj	_	you
4	fasses	faire	V	_	_	1	ccomp	_	make
5	envie	envie	N	_	_	4	dobj	_	desire
6-7	aux	aux	P+D	_	_	_	_	_	to_the
6	Ã 	Ã 	P	_	_	8	case	_	to
7	les	le	D	_	_	8	det	_	the
8	enfants	enfant	N	_	_	4	iobj	5:nmod	kids
9	de	de	P	_	_	10	mark	_	to
10	faire	faire	V	_	_	5	ccomp	_	make
11-12	du	du	P+D	_	_	_	_	_	of_the
11	de	de	P	_	_	13	case	_	of
12	le	le	D	_	_	13	det	_	the
13	bruit	bruit	N	_	_	10	dobj	_	noise

# conllu format using SpaceAfter=No
1	Can	can	AUX	MD	VerbForm=Fin	3	aux	_	_
2	you	you	PRON	PRP	Case=Nom|Person=2|PronType=Prs	3	nsubj	_	_
3	use	use	VERB	VB	VerbForm=Inf	0	root	_	_
4	the	the	DET	DT	Definite=Def|PronType=Art	10	det	_	_
5	'	'	PUNCT	``	_	6	punct	_	SpaceAfter=No
6	find	find	VERB	VB	VerbForm=Inf	10	compound	_	_
7	my	my	PRON	PRP$	Number=Sing|Person=1|Poss=Yes|PronType=Prs	8	nmod:poss	_	_
8	phone	phone	NOUN	NN	Number=Sing	6	dobj	_	SpaceAfter=No
9	'	'	PUNCT	''	_	6	punct	_	_
10	feature	feature	NOUN	NN	Number=Sing	3	dobj	_	_
11	to	to	PART	TO	_	12	mark	_	_
12	track	track	VERB	VB	VerbForm=Inf	3	advcl	_	_
13	someone	someone	NOUN	NN	Number=Sing	16	nmod:poss	_	_
14	else	else	ADJ	JJ	Degree=Pos	13	amod	_	SpaceAfter=No
15	's	's	PART	POS	_	13	case	_	_
16	phone	phone	NOUN	NN	Number=Sing	12	dobj	_	SpaceAfter=No
17	?	?	PUNCT	.	_	3	punct	_	_

# I wrote the letter with a quill.
1	Ð¯	ja	PRON	_	Case=Nom|Number=Sing|Person=1|PronType=Prs	2	nsubj	_	I
2	Ð½Ð°Ð¿Ð¸ÑÐ°Ð»	napisat'	VERB	_	Gender=Masc|Number=Sing|VerbForm=Part|Voice=Act	0	root	_	wrote
3	Ð¿Ð¸ÑÑÐ¼Ð¾	pis'mo	NOUN	_	Case=Acc|Gender=Neut|Number=Sing	2	dobj	_	the-letter
4	Ð¿ÐµÑÐ¾Ð¼	pero	NOUN	_	Case=Ins|Gender=Neut|Number=Sing	2	nmod	_	with-a-quill</TEXTAREA>
	"""
	
	print """
	<div style='float:xxx;'>
	
		<div ><span style='float:left;'>additional functions</span> <input type='text' id='addfuncs'  style='width:100%' title='Here you can add additional functions that do not appear in the CoNLL data (space or comma separated) in order to make them appear in the drop down menu when editing.' value='nsubj obj iobj csubj ccomp xcomp obl vocative expl dislocated advcl advmod discourse aux cop mark nmod appos nummod acl amod det clf case conj cc fixed flat compound list parataxis orphan goeswith reparandum punct root dep'></input></div>
		
		<div ><span style='float:left;'>additional POS tags</span> <input type='text' id='addcats'  style='width:100%;' title='Here you can add additional categories that do not appear in the CoNLL data (space or comma separated) in order to make them appear in the drop down menu when editing.' value='ADJ ADP PUNCT ADV AUX SYM INTJ CONJ X NOUN DET PROPN NUM VERB PART PRON SCONJ _'>  </input> </div>
		
		<div >or paste your sentences here:
		<TEXTAREA NAME="sentences" id="sentences"  title='Here you can provide unanalyzed textual data, one sentence per line. You can then interactively add the syntactic analysis of your sentences. This will erase all the existing trees'  style="width:100%; height:100;"></TEXTAREA>
		</div>
		
	</div>
			<input type="checkbox" id="annodoccheck"><label for="annodoccheck"><img src='images/annodoc.png' border='0' title='show annodoc graph' style='top:0px;'>  </label>

	
	
		
	<div style='float:right;'>
		<img  id="style" src='images/style.png' border='0' title='style' style='top:0px;'> 
		
	</div>
	<div id="styledialog" title="Design your dependency tree" style="background-color: white;">
		<div id="stylefunctions" title="functions">
	
		</div>
		<div style='float:right;'>
			<input type="checkbox" id="styleconllcheck">include style information in CoNLL
		</div>
	</div>
	
	"""
	#print "" checked="checked"
	print ""
	print "<br>"
	print """"""
	print "</div></div></div>"

def printmenues():
	print """	
	<div id="funcform" style="display:none;position:absolute;">
		<form  method="post" id="func" name="func" >
			<select id="funchoice" class='funcmenu' onClick="changeFunc(event);" size=0 style="height:0; width:80px;"  >
			</select>
		</form>
	</div>
	<div id="catform" style="display:none;position:absolute;">
		<form  method="post" id="cat" name="cat" >
			<select id="catchoice" class='funcmenu' onClick="changeCat();"  size=0 style="height:0; width:80px;"  >
			</select>
		</form>
	</div>
	"""
	
def printdialogs():
	print """
	<div id="dialog" title="Confirmation" style="display: none;" >
		<div class="ui-state-error ui-corner-all" style="margin: 40px;padding: 10pt 0.7em;">
			<h2 id="question">Are you sure?</h2>
		</div>
	</div>
	<div id="b" class="rbubble" style="right: 650px;top: 44px; position: absolute;display:none;">Paste a CoNLL file content here</div>
	<div id="bb" class="rbubble" style="right: 650px;top: 533px;position: absolute;">Or paste the sentences you want to analyze here.<br/>One sentence per line.</div>
	<div id="bbb" class="lbubble" style="left: 400px;top: 400px;position: absolute;">As soon as you click anywhere else, the graph is updated and you can modify the dependencies by dragging one word over the other.</div>
	<div id="bbbb" class="lbubble" style="left: 400px;top: 35px;position: absolute;">You can export the graph in various formats by clicking on the green arrow.</div>
			
	<input type="button" id="drawbutton" value="draw" class="ui-button ui-state-default ui-corner-all" onClick='$("#funchoice").empty(); $("#catchoice").empty();$("#stylefunctions").empty();$("#trees").empty();readConll();setupStyleDialog();drawTrees();nokeys=false;'  style="display: none;" >

	"""

##############################################################################################"

def start():
	form = cgi.FieldStorage()
	thisfile = os.environ.get('SCRIPT_NAME',".")
	
	print "Content-Type: text/html\n" # blank line: end of headers

##############################################################################################"

if __name__ == "__main__":
	start()
	printhtmlheader()
	printheadline()
	printforms()
	printexport()
	printmenues()
	printdialogs()
	printfooter()
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2016 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

import cgi, cgitb, sys
from StringIO import StringIO
from lib import depedit
cgitb.enable()
form = cgi.FieldStorage()

conllin = form.getvalue('conllin','').decode("utf-8")
grammar = form.getvalue('grammar','').decode("utf-8")
project = form.getvalue('project','').decode("utf-8")
addName = form.getvalue('addName','').decode("utf-8")

config_file = StringIO(grammar)
infile = StringIO(conllin)

print 'Content-type: text/plain\n\n',

class writer(object):
	log = []
	def write(self, data):
		self.log.append(data)
sys.stderr
logger = writer()
sys.stderr = logger

if project:
	print "database..."
	print 55445,project,64,addName
else:
	try:
		de=depedit.DepEdit(config_file)
		output_trees=de.run_depedit(infile)
		print output_trees.encode("utf-8")
	except:
		print u"uuuuuuuuu".join(logger.log)

#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2015 Kim Gerdes
# kim AT gerdes. fr
# http://arborator.ilpga.fr/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 3 of the GNU Affero General Public License (the "License")
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of of version 3 of the GNU Affero General Public License
# from http://www.gnu.org/licenses/agpl-3.0.html 
# For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

"""
module called from the editor.cgi to save trees
"""

import cgi, sys, json
from os import environ
sys.path.append('modules')
from logintools import isloggedin
from lib import database

test = isloggedin("users/")
if not test:sys.exit()
admin = int(test[0]["admin"])

method = environ.get( 'REQUEST_METHOD' )
if method == "POST" : #or True
	data = cgi.FieldStorage()
	
	project = data.getvalue("project",None).decode("utf-8")
	#project = data.getlist("project")
	tree = data.getvalue("tree",None)
	
	if tree:
		snode=json.loads(tree)["tree"] # TODO: add modify and save sentence features
		
		sentenceid = int(data.getvalue("sentenceid",0))
		sentencenr = int(data.getvalue("sentencenr",0))
		userid = int(data.getvalue("userid",0))
		username = data.getvalue("username","unknown")
		todo = data.getvalue("todo",-1)
		validator = int(data.getvalue("validator",0))
		addEmptyUser = data.getvalue("addEmptyUser",None)
		tokensChanged = eval(data.getvalue("tokensChanged","[]"))
		validvalid = eval(data.getvalue("validvalid","[]"))
		
		#print "Content-Type: application/json\r\n\r\n"
		#print sentenceid
		
		sql=database.SQL(project)
		#open("logger.txt","w").write("save.cgi "+ str([username,sentenceid,userid,snode,tokensChanged])+"\n")
		treeid, newtree, sent =sql.saveTree(sentenceid,userid,snode,tokensChanged)
		#open("logger.txt","a").write("_____ return to save.cgi "+ str([treeid, newtree, sent])+"\n")
		treelinks, firsttreeid, sentenceinfo = sql.links2AllTrees(sentenceid,sentencenr,username,admin,todo=todo, validator=validator,addEmptyUser=addEmptyUser,validvalid=validvalid)
		
		print "Content-Type: application/json\r\n\r\n"
		#print sentenceid
		if newtree:
			#print newtree
			newtree.update({"treeid":treeid, "treelinks":treelinks, "firsttreeid":firsttreeid, "sentence":sent})
			print json.dumps(newtree, sort_keys=True)
		else: print json.dumps({"treeid":treeid, "treelinks":treelinks, "firsttreeid":firsttreeid})
/*!
 * arborator script for dependency drawing 
 * version 1.0
 * http://arborator.ilpga.fr/
 *
 * Copyright 2010-2018, Kim Gerdes
 *
 * This program is free software:
 * Licensed under version 3 of the GNU Affero General Public License (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.gnu.org/licenses/agpl-3.0.html
 * 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. 
 *
 */



attrdirty = {"value":"save all modified trees",'disabled':false,"cursor":"pointer"};
attrclean = {"value":"all trees are saved",'disabled':true,"cursor":"default"};

noshowtokens = {'misc':{"SpaceAfter=No":"_"}}

///////////////////////// node object and functions //////////////// 
////////////////////////////////////////////////////////////////////


var currentsvg=false;
var isSelected=false;
var isDrag=false;
var dirty=[]; // list of dirty svgs
var lastSelected;
var keyediting=false;
var nokeys=false;
var numbersent=0;

function Pnode(index,token)
	{
		index=parseInt(index,10);
		this.index=index;
		this.gov={};
		if ("gov" in token) 
		{
			for (var i in token["gov"])
			{
				this.gov[parseInt(i,10)]=token["gov"][i]
			}
		}	
		this.width=0;
		this.features=new Object(); // contains for each shownfeature the corresponding text
		this.svgs=new Object(); // contains for each shownfeature the corresponding svg object
		var currenty=dependencyspace;
		
// 		console.log("________________________",index);
		
		
		for (var i in shownfeatures) 
			{	
			var f = shownfeatures[i]; // f= eg. "cat"
			if (token[f]===undefined) {token[f] = "_";} // ð
			this.features[f]=token[f];
			
			if ( (token[f] instanceof Object) ) // mode compare!!!
			{				
				
				var user0=token[f][0][1][0];
				if (user0=="ok")
				{
					c=token[f][0][0]
					var t = paper.text(currentx, currenty, c);
					t.attr(defaultattris);
					t.attr({fill: "#"+compacolors[user0],title:"All annotators agree."});
				}
				else
				{
					c=""
					ti=""
					for (i in token[f])
					{
						c=c+" "+token[f][i][0];
						ti=ti+" "+token[f][i][0]+" ("+token[f][i][1]+")";
					}
					var t = paper.text(currentx, currenty, c);
					t.attr(defaultattris);
					t.attr({fill: "red",title:ti});
					
				}
			
			
			}
			else // normal mode
			{
				if (f in noshowtokens && token[f] in noshowtokens[f])
					{tokenf=noshowtokens[f][token[f]];  }
				else 	{tokenf=token[f];}
				var t = paper.text(currentx, currenty, tokenf);
				t.attr(defaultattris);
				if (f in token) t.attr(attris[f]);				
			}
			t.attr("index",index);
			var wi=t.getBBox().width;
			t.width=wi;
			t.index=index;
		
			t.node.setAttribute("class",f);
			if (f in colored && token[f] in catDic) t.attr(catDic[token[f]]);
			this.svgs[f]=t;
			if (wi>this.width) this.width=wi;
			currenty=currenty+line;
			
			};
			
		
		for (var a in token) // remaining features
			{
				v=token[a]
				if (a in this.features || a=="gov") continue;				
				this.features[a]=v;
			}
		
			
		if (editable) activate(this.svgs[shownfeatures[0]], this.svgs[shownfeatures[categoryindex]]);

		svgwi=svgwi+this.width+tab;
		this.x=0;
		this.y=0;
		this.svgdep={};		// the svg dependency (the arrow and the function name): gives the svgobject for each govind

	};





//////////////////////////// only used for editing:
//////////////////////////////////////////////////////// 




createConnection = function () {
	
	$(currentsvg).mousemove(function(e) 
	{
// 		console.log("ddd",currentsvg.id,$(currentsvg));
		if (currentsvg!=this) 
		{
			reset()
			$(".toggler").removeClass("currentsentence"); // remove it from all togglers
			currentsvg=this;
			currentsvgchanged();
			$(".toggler:eq("+(parseInt(currentsvg.id.slice(3))-1)+")").addClass("currentsentence");			
		}
		if ( isDrag && !keyediting ) 
		{ 
			
			moveConnection(e.pageX - $(document).scrollLeft() - $(currentsvg).offset().left +3, e.pageY - $(currentsvg).offset().top - 10);
			
			isDrag.ox=currentsvg.dragConnection[2].getPointAtLength(0).x;
			isDrag.oy=currentsvg.dragConnection[2].getPointAtLength(0).y;
		}
	}).mouseup(function(e) 
	{
// 		console.log(e,"mouseup, isSelected:",isSelected , "yyyyyy",isDrag  ,isDrag.svg,"uuuuuuuuuuuuu",currentsvg ,isDrag.svg==currentsvg ,lastSelected)
// 		if (isSelected) console.log(isSelected.index);
		
		emptyclick=true;
		if (isSelected && isDrag && isSelected!=isDrag && isDrag.svg==currentsvg && isSelected.index!=lastSelected) 
			{ changeDrag(isDrag.index,isSelected.index);emptyclick=false;};
		if (isDrag && isDrag.rooting) 
			{ changeDrag(0,isDrag.index);emptyclick=false;}
		setTimeout("isDrag = false;",500);
		
		for (var i in currentsvg.words) currentsvg.words[i].svgs[shownfeatures[0]].attr(attris[shownfeatures[0]]);

		if (emptyclick) 
			{ 
// 				console.log("emptyclick")
				$('#funcform').hide(); $('#catform').hide(); $('#ex').hide(); $('#compbox').hide(); $('#checkbox').hide(); 
// 				currentsvg.dragConnection.hide(); 
				
				if (isDrag)
				{
					lastSelected=isDrag.index;
					gov=null;
					for (gov in currentsvg.words[lastSelected].gov) break; // get first (any) gov

					if (gov!=null) keyConnection(gov);
				}
				
			}
		else {$("#catchoice").focus();}
		isDrag = false;
		currentsvgchanged();
// 		console.log(3)
	});
	
	
// 	  attris["dragdepline"], attris["deptext"],
	return drawsvgDep(1,2,50,60,150,150,"", "",false,0); // arbitrary points
	
}
   

activate = function (t,cat) /*makes word node t (t=token, cat=cat) interactive  */
{
	t.attr({cursor:"move"}).mousedown(dragger).mouseover(function () {
// 		console.log("mouseover",t,isDrag,t==isDrag,keyediting,isSelected)
		if (t==isDrag || keyediting) return;	
		isSelected=t;
		
// 		console.log("isSelected:",isSelected)
		if (isDrag) 
			{
				for (var i in currentsvg.words) currentsvg.words[i].svgs[shownfeatures[0]].attr(attris[shownfeatures[0]]);	
				t.attr(attris["target"]);
				isDrag.attr(attris["source"]);
			}
	}).mouseout(function () {
		if (t==isDrag  || keyediting) return;		
// 		console.log("mouseout",t,isDrag,t==isDrag,keyediting,isSelected)
		isSelected=null;
		for (var i in currentsvg.words) currentsvg.words[i].svgs[shownfeatures[0]].attr(attris[shownfeatures[0]]);
		currentsvgchanged();

	}).dblclick(function () {
		openFeatureTable(this.index,currentsvg.words)
	})
	cat.click(function (e) {openCatMenu(this);})
}


var dragger = function (e) {
	
// 	console.log("____",currentsvg);
	if(e.ctrlKey) return true; // to allow dbl click if nothing else works. ugly hack!
	reset();
	isDrag = this;
	lastSelected=this.index;
	isDrag.rooting=false;
	isDrag.ox=currentsvg.dragConnection[2].getPointAtLength(0).x;
	isDrag.oy=currentsvg.dragConnection[2].getPointAtLength(0).y;
// 	moveConnection(e.pageX,e.pageY);
	moveConnection(e.pageX - $(document).scrollLeft() - $(currentsvg).offset().left +3, e.pageY - $(currentsvg).offset().top - 10);

	currentsvg.dragConnection.show();
	currentsvg.dragConnection.toFront();
	isDrag.svg=currentsvg;
	e.preventDefault && e.preventDefault();
    };




changeDrag = function (gov, dep) /* reaction if good connection was created, gov and dep are indeces */
{
// 	console.log("changedrag",gov,dep)
	var depn=currentsvg.words[dep];
	var func = null;
	for (i in depn.gov) { func = depn.gov[i]; break;}
	var c=currentsvg.dragConnection[1];
	a=c.getPointAtLength(c.getTotalLength()/2);
	svgpos=$(currentsvg).offset();
	openFunctionMenu(dep,gov,func,  svgpos.left+a.x, svgpos.top+a.y-funccurvedist);
	
}

clickOpenFunctionMenu = function(funcnode)  // funcnode=svg text node of the dependency link
	{
// 		console.log("clickOpenFunctionMenu",funcnode,$(currentsvg).position())
// 		svgpos=$(currentsvg).position();
// 		console.log("jjjj",$(currentsvg),currentsvg.id);
		svgpos=$(currentsvg).offset()
		openFunctionMenu(funcnode.index, funcnode.govind, funcnode.attr("text"), svgpos.left+funcnode.attr("x")-20, svgpos.top+funcnode.attr("y")-7 );   // TODO: make the position of the function menu parametrizable	
	}
	
	
openFunctionMenu = function(ind,govind,func,x,y)  // called from clickOpenFunctionMenu and from changeDrag, changeFunc closes the menu
	{
// 		currentnr = $(currentsvg).parent().attr("nr");
// 		console.log("kkkk",$(currentsvg),currentsvg.id);
		var ff=$('#funcform');
		var os=$("#funchoice")[0].options;
		funcnum=functions.indexOf(func);
		if (funcnum==null) funcnum=0;
		if (funcnum in os) os[funcnum].selected = true;
		ff.css({ top: y, left: x });
		ff.show();
		$("#funchoice").focus()
		ff.data("index", ind);
		ff.data("govind", govind);
		lastSelected = ind;
	}

openCatMenu = function(catnode)  // funcnode=svg text node of the dependency link
	{
		var cc=$('#catform');
		catnum=categories.indexOf(catnode.attr("text"));
		if (catnum<0) catnum=0;
		svgpos=$(currentsvg).position();
		var os=$("#catchoice")[0].options;
		if (catnum in os) os[catnum].selected = true;
// 		console.log(svgpos)
		cc.css({ top: svgpos.top+catnode.attr("y")-7, left: svgpos.left+catnode.attr("x")-7});
// 		+catnode.width
		cc.show();
		$("#catchoice").focus();
		cc.data("index",catnode.index);
		lastSelected = catnode.index;
	}




function changeFunc(event){ // called by onclick on the func menu or return
	
	var addDep=event.shiftKey;
	var ff=$('#funcform');
	if (ff.is(":hidden")) return false;	
	var func=$("#funchoice")[0].options[$("#funchoice")[0].selectedIndex].value;
	var id = ff.data("index");
	var govid = ff.data("govind");
	if (id==govid) govid=0;
	if (func==null) func=defaultfunction;
	gi2f = new Object(); // governor index to function
	gi2f[govid]=func;
	if (func==erase) eraseRelation(id, gi2f)
	else if (addDep) addRelations(id, gi2f);
	else establishRelations(id,gi2f );
	ff.hide();
	currentsvg.dragConnection.hide();
	
}	




function establishRelations(depind,gi2f)
{
	var depn=currentsvg.words[depind];
	for (i in depn.svgdep) { depn.svgdep[i].remove(); }
	oldgi2f={};
	info="removing"
	for (var i in depn.gov)
	{
		oldgi2f[i]=depn.gov[i]
		if (i in currentsvg.words) info=info+" link "+ currentsvg.words[i].features.t + " â"+ depn.gov[i]+ "â "+depn.features.t + " ";
		else info=info+" link â"+depn.gov[i]+ "â "+depn.features.t;
	}
	
	depn.gov={}
	
	info = info + " and replacing by"
	
	for (var i in gi2f)
	{
		depn.gov[i]=gi2f[i]
		if (i in currentsvg.words) info=info+" link "+ currentsvg.words[i].features.t + "â" + gi2f[i] + "â "+depn.features.t + " ";//normal link
		else info=info+" link â" + gi2f[i] + "â "+depn.features.t;//root link
		
	}
	
	drawalldeps();
	currentsvg.undo.undoable(info, establishRelations, [depind, oldgi2f]);
	
}


function eraseRelation(depind,gi2f)
{
	var depn=currentsvg.words[depind];
	for (i in depn.svgdep) { depn.svgdep[i].remove(); }
	info="removing"
	oldgi2f={};
	for (var i in depn.gov)
	{
		oldgi2f[i]=depn.gov[i]
		if (i in gi2f)
		{
			delete depn.gov[i];
			if (i in currentsvg.words) info=info+" link "+ currentsvg.words[i].features.t + " â"+ depn.gov[i]+ "â "+depn.features.t + " ";
			else info=info+" link â"+depn.gov[i]+ "â "+depn.features.t;
		}			
	}
	drawalldeps();
	currentsvg.undo.undoable(info, establishRelations, [depind,oldgi2f]);	
	
}

function addRelations(depind,gi2f)
{
	var depn=currentsvg.words[depind];

	for (i in depn.svgdep) { depn.svgdep[i].remove(); }
	
	oldgi2f={};
	
	for (var i in depn.gov) oldgi2f[i]=depn.gov[i]
	
	info = "adding"
	
	for (var i in gi2f)
	{
		depn.gov[i]=gi2f[i]
		
		if (i in currentsvg.words) info=info+" link "+ currentsvg.words[i].features.t + "â" + gi2f[i] + "â "+depn.features.t + " ";//normal link
		else info=info+" link â" + gi2f[i] + "â "+depn.features.t;//root link
		
	}
	
	drawalldeps();
	currentsvg.undo.undoable(info, establishRelations, [depind, oldgi2f]);
	
}


function changeCat(){ // called by onclick on the menu or return

	var cc=$('#catform');
	if (cc.is(":hidden")) return false;
	var cat=$("#catchoice")[0].options[$("#catchoice")[0].selectedIndex].value;
	if (cat==null) cat=defaultcategory;
	var id = cc.data("index");
	establishCat(id,cat);
	cc.hide();
}	

function establishCat(ind,cat)
{
	var node=currentsvg.words[ind];
	var oldcat=node.features[shownfeatures[categoryindex]];
	var info="replacing the category "+oldcat+"of the node "+currentsvg.words[ind].features.t+" by "+cat
	node.features[shownfeatures[categoryindex]]=cat;
	node.svgs[shownfeatures[categoryindex]].attr("text",cat);
	currentsvg.undo.undoable(info, establishCat, [ind, oldcat]);
	
}




moveConnection = function(x2,y2) // moves the connection so that it links the isDrag-element -------> to the x2,y2 position
	{	
		
		if (!isDrag || !currentsvg) return;
		var wi = isDrag.width/2;
		var wix = Math.max(wi,rootTriggerSquare/2);	
		if (x2<=isDrag.attr("x")+wi) var x1=isDrag.attr("x")+wi-xoff; // 1st case: cursor to the right of the word
		else var x1=isDrag.attr("x")+wi+xoff; // 2nd case: cursor to the left of the word

// 		console.log(y2,rootTriggerSquare, y2<rootTriggerSquare,x2>isDrag.attr("x")+wi-wix ,x2<isDrag.attr("x")+wi+wix);
		if (y2<rootTriggerSquare && x2>isDrag.attr("x")+wi-wix && x2<isDrag.attr("x")+wi+wix) // 3rd case: cursor rooting above
			{
// 				console.log("iiiiiiiiiiiii")
				isDrag.rooting=true;
				lineattris=attris["dragdeplineroot"]
				x1=isDrag.attr("x")+wi;
			}
		else 	// not rooting, normal
			{
				isDrag.rooting=false;
				lineattris=attris["dragdepline"];
			}
		var y1 = isDrag.attr("y")+pois-tokdepdist;
		applyPath(x1,y1,x2,y2,lineattris);
	}
  
applyPath = function(x1,y1,x2,y2,lineattris) // 
	{
// 		console.log(x1,y1,x2,y2,lineattris);
		var x1x2=Math.abs(x1-x2)/2;
		var yy = Math.max(y1-x1x2-worddistancefactor*3,-tokdepdist);// 3 is adhoc: curve looks like link between tokens that are 3 words apart. original: worddistancefactor*Math.abs(ind-govind) TODO: think about this...
		yy = Math.min(yy,y1-depminh);

		var path="M"+x1+","+y1+"C"+x1+","+yy+" "+x2+","+yy+" "+x2+","+y2;
	
		currentsvg.dragConnection[1].attr({path:path}).attr(lineattris);;// curve        
		currentsvg.dragConnection[2].translate(x2-isDrag.ox,y2-isDrag.oy).attr(lineattris);; // pointer
		
// 		console.log(";;;",currentsvg.dragConnection[1],lineattris)
		
// 		if (currentsvg.dragConnection[1].getAttribute('stroke-dasharray')==0) // firefox/raphael bug!
// 		{
// 			currentsvg.dragConnection[1].removeAttribute('stroke-dasharray')
// 			currentsvg.dragConnection[2].removeAttribute('stroke-dasharray')
// 		}
		
	}
	
	
	
	

//////////////////////////////////////// keystuff ///////////////////////////////
////////////////////////////////////////////////////////////////////////////////


onkey = function () {
	$(document).keypress(function(e) {
		if (nokeys) return true;
		e.stopImmediatePropagation();
		var code = (e.keyCode ? e.keyCode : e.which);
		
		switch(code)
		{
		case 8: // backspace
			if(currentsvg) {nextword(0)}
			return true;
		case 9: // tab
			if(currentsvg) {nextsentence()}
			else $(".toggler:first").click().focus();
			return false;
		case 13: // return
			if(currentsvg && $('#funcform').is(":visible"))
			{
				changeFunc(e);
				if (keyediting) nextword(1);
			}
			else if (currentsvg && $('#catform').is(":visible"))
			{ 
				changeCat();
				if (keyediting) nextword(1);
			}
			else if (currentsvg && keyediting)
			{ 
				changeDrag(isDrag.index,lastSelected )
			}
			return false;
		case 27: //esc 
			reset();
// 			$("#sentinfo").html("escaping");
			return false;
		case 32: // space
			if(currentsvg) {nextword(1)}
			else $(".toggler:first").click().focus();
			return false;
		case 37: // left
			if(currentsvg && keyediting && $('#funcform').is(":hidden") && $('#catform').is(":hidden")) moveGov(-1);
			break;
		case 38: // up
			if(currentsvg && keyediting && $('#funcform').is(":hidden") && $('#catform').is(":hidden")) keyOpenMenu(e.shiftKey || e.ctrlKey);
			break;
		case 39: // right
			if(currentsvg && keyediting && $('#funcform').is(":hidden") && $('#catform').is(":hidden")) moveGov(1);
			break;
		case 40: // down
			if(currentsvg && keyediting && $('#funcform').is(":hidden") && $('#catform').is(":hidden")) keyOpenMenu(e.shiftKey || e.ctrlKey);
			break;
		case 99: // "c"
			if(currentsvg && keyediting && $('#funcform').is(":hidden") && $('#catform').is(":hidden")) keyOpenMenu(true);
			break;
		case 102: // "f"
			if(currentsvg && keyediting && $('#funcform').is(":hidden") && $('#catform').is(":hidden")) keyOpenMenu(false);
			break;
		case 115: // "s"
			if(e.ctrlKey && dirty!=[] && currentsvg.words) saveAllTrees();
			return false; 
		default:
			return true;
		}
		
	}); 

}

keyOpenMenu = function (cat) {
// 	console.log("keyOpenMenu",cat);
	
// 	$("#sentinfo").html(" lastSelected "+" "+cat)

	if (cat) openCatMenu(currentsvg.words[lastSelected].svgs["cat"]);
	else 
	{
		i = (isDrag.index==lastSelected ? 0 : isDrag.index);
		clickOpenFunctionMenu(currentsvg.words[lastSelected].svgdep[i][0]);
	}
}


nextword = function (forward) {
	
	if (!currentsvg) return;
	$('#funcform').hide();
	$('#catform').hide();	
	if (forward) lastSelected++;
	else  lastSelected--;
	if (lastSelected > Object.keys(currentsvg.words).length) lastSelected=1;
	else if (lastSelected<=0) lastSelected=Object.keys(currentsvg.words).length;
	gov=0; // in case a node has no governor
	for (gov in currentsvg.words[lastSelected].gov) break; // get first (any) gov
	keyConnection(gov);

}


keyConnection = function (gov) {
	
// 	var p = $(currentsvg).position();
	for (var i in currentsvg.words) currentsvg.words[i].svgs[shownfeatures[0]].attr(attris[shownfeatures[0]]);
	keyediting=true;
	if (gov==0 || gov==lastSelected) //root
	{
		isDrag = currentsvg.words[lastSelected].svgs[shownfeatures[0]];
		isDrag.ox=currentsvg.dragConnection[2].getPointAtLength(0).x;
		isDrag.oy=currentsvg.dragConnection[2].getPointAtLength(0).y;
// 		moveConnection(isDrag.attr("x")+p.left+isDrag.width/2-10,p.top);
		moveConnection(isDrag.attr("x")+isDrag.width/2,0);
	}
	else
	{
		isDrag = currentsvg.words[gov].svgs[shownfeatures[0]]; // gov node
		isDrag.ox=currentsvg.dragConnection[2].getPointAtLength(0).x;
		isDrag.oy=currentsvg.dragConnection[2].getPointAtLength(0).y;
		depn = currentsvg.words[lastSelected].svgs[shownfeatures[0]];
// 		moveConnection(depn.attr("x")+p.left+depn.width/2,depn.attr("y")+p.top);
		moveConnection(depn.attr("x")+depn.width/2,depn.attr("y")-10);
		depn.attr(attris["target"]);
	}
	currentsvg.dragConnection.show();
	currentsvg.dragConnection.toFront();
	isDrag.attr(attris["source"]);
	return false;
}

reset = function () {
	if (currentsvg) 
	{
// 		currentsvg.dragConnection.hide();
		for (var i in currentsvg.words) currentsvg.words[i].svgs[shownfeatures[0]].attr(attris[shownfeatures[0]]);
	}
	keyediting=false;
	isDrag = false;
	$('#funcform').hide();
	$('#catform').hide();
	$('#ex').hide();
// 	lastSelected=0;
	
}


nextsentence = function () {

	reset();
	nr = parseInt(currentsvg.id.slice(3));
	$(".toggler").removeClass("currentsentence");	
	if (nr >= numbersent) nr=0;
	else if (nr<0) nr=numbersent	;
	nexttog = $(".toggler:eq("+(nr)+")");
	if ( nexttog.hasClass("expanded") ) 
	{
		currentsvg=$("#svg"+(nr+1))[0];
	}
	else nexttog.click();
	
	$("html,body").animate({scrollTop: $(currentsvg).offset().top-[100]},500); // scroll to -100 speed .7 seconds

	nexttog.addClass("currentsentence");

}

moveGov = function (dir) {
	var n = Object.keys(currentsvg.words).length;
// 	console.log("isDrag",isDrag,isDrag.index);
	keyConnection((((parseInt(isDrag.index)+dir-1)%n)+n)%n + 1 ) // javascript modulo bug for negative numbers workaround :-s
}

/////////////////// end key stuff


///////////// end editing


////////////////////////////////////////////////////////////////
/////////////////////essential drawing stuff////////////////////
////////////////////////////////////////////////////////////////



drawsvgDep = function(ind,govind,x1,y1,x2,y2,func,tooltip, color, funcposi)
	{
		// ind: word index
// 		govind: index of governor
// 		x1,y1: start coordinates
// 		x2,y2: target coordinates
// 		func: function name
// 		tooltip, color, funcposi: only for compare mode
// 		console.log("drawsvgDep",ind,govind,func);
		var set=currentsvg.paper.set();
		var x1x2=Math.abs(x1-x2)/2;
		var yy = Math.max(y1-x1x2-worddistancefactor*Math.abs(ind-govind),-tokdepdist);
		yy = Math.min(yy,y1-depminh);

		var cstr="M"+x1+","+y1+"C"+x1+","+yy+" "+x2+","+yy+" "+(x2+.01)+","+y2; // 0.01: workaround for strange bug in the computation of getTotalLength!!!
		var c = currentsvg.paper.path(cstr).attr(attris["depline"]).attr({"x":x1,"y":y1,"smooth":true});
// 		console.log("'''",c.getTotalLength());
		var poi = currentsvg.paper.pointer(x2,y2,pois,0).attr(attris["depline"]);
// 		.data("xfunc",func);

		a=c.getPointAtLength(c.getTotalLength()/2);
		t = currentsvg.paper.text(a.x, a.y-funccurvedist-funcposi*10, func);
		t.attr(attris["deptext"]);
		t.index=ind;
		t.govind=govind;
		if (editable) t.click(function (e) {
// 				console.log(e);
				clickOpenFunctionMenu(this);
			})
		
		if (govind==0) { // case: head of sentence (root): the function name needs special treetment
			
// 			console.log("--",c.getPointAtLength(100),c.getTotalLength(),t,a.y-funccurvedist-funcposi*10,a.y,funccurvedist,funcposi*10)
			var newx=a.x+t.getBBox().width/2+funccurvedist/2;
			if (newx+t.getBBox().width/2 > svgwi) newx=a.x-t.getBBox().width/2-funccurvedist/2;
			if (newx-t.getBBox().width/2 < 0) newx=a.x;
			t.attr("x",newx);
		}
// 		console.log(color,colored,functions)
		if (color) // case compare: colors depend on users
		{
			c.attr({stroke: color});
			poi.attr({stroke: color});
			t.attr({fill: color,title:tooltip});
		}
		else if ("dependency" in colored && func in funcDic) // normal graph: color depends on function
		      {
			var att = funcDic[func];
			c.attr(att);			
			poi.attr({"stroke": att["stroke"]});
			t.attr({fill: att["stroke"]});
		      };
		if (c.node.getAttribute('stroke-dasharray')==0) // firefox/raphael bug!
			c.node.removeAttribute('stroke-dasharray');
		if (poi.node.getAttribute('stroke-dasharray')==0)
			poi.node.removeAttribute('stroke-dasharray');
		
		set.push(t); // text
		set.push(c); // curve
		set.push(poi); // pointer
		return set;
	};
	

drawDep = function(ind,govind,func,c)	// draw dependency of type func from govind to ind (c is usually 0, only for multiple heads)
	{
// 		console.log("drawDep",ind,govind,func);
		if (govind==0 ) // head of sentence
		{
			node2=currentsvg.words[ind];
			var x2=node2.svgs[shownfeatures[0]].attr("x")+node2.svgs[shownfeatures[0]].width/2;
			var y2=node2.svgs[shownfeatures[0]].attr("y")-tokdepdist-c*linedeps;
			var x1=x2;
			var y1=0;
		}
		else // normal dependency
		{
			var node1=currentsvg.words[govind];	// from node
			var node2=currentsvg.words[ind];	// to node
			if (node1==null) {delete node2.gov[govind];return;}
			if (ind<govind) var x1=node1.svgs[shownfeatures[0]].attr("x")+node1.svgs[shownfeatures[0]].width/2-xoff;
			else var x1=node1.svgs[shownfeatures[0]].attr("x")+node1.svgs[shownfeatures[0]].width/2+xoff;
			var x2=node2.svgs[shownfeatures[0]].attr("x")+node2.svgs[shownfeatures[0]].width/2;
			var y1=node1.svgs[shownfeatures[0]].attr("y")+pois-tokdepdist;
			var y2=node2.svgs[shownfeatures[0]].attr("y")-tokdepdist-c*linedeps;
		};
		
		if ( (func instanceof Object) ) // mode compare!!!
		{
// 			console.log("start",ind,govind,func);
			var user0=func[0][1][0];

			if (user0=="ok")
			{
				func=func[0][0]
				node2.svgdep[govind]=drawsvgDep(ind,govind,x1,y1,x2,y2,func, "All annotators agree.", "#"+compacolors[user0],0);
// 				node2.deplineattris, node2.deptextattris,
			}
			else
			{
				var lastset=false;
				for (i in func)
				{
					var funcusers=func[i];
					i=parseInt(i,10);						
					for (j in funcusers[1])
					{
						var u=funcusers[1][j];
						j=parseInt(j,10);
// 						console.log(node2.svgdep[govind]);
// 						if (node2.svgdep[govind]) alert("yyy");  node2.deplineattris, node2.deptextattris,
						var newset=drawsvgDep(ind,govind, x1+j*2,y1, parseInt(x2,10)+j*2, y2, funcusers[0], funcusers[0]+" ("+u+") ", "#"+compacolors[u], i+j);
// 						if (node2.svgdep[govind]) alert(newset);
						if (lastset) lastset.push(newset);
						else lastset=newset;
						node2.svgdep[govind]=lastset;
// 						console.log(node2.svgdep[govind]);
						
					}
					
// 					nfunc = nfunc +funcusers[0]+ " ";
// 					txt = txt+
				}
// 				func=nfunc
				
			}
// 			console.log(user0,func)  node2.deplineattris, node2.deptextattris,
			
		
		}
		// normal dependency:
		else node2.svgdep[govind]=drawsvgDep(ind,govind,x1,y1,x2,y2,func, "click to change", false, 0);
// 		console.log(":::",ind,govind,node2,node2.svgdep[govind])
// 		
	};


drawalldeps = function() 
	{
		
	for (var i in currentsvg.words)
		{
			var n = currentsvg.words[i];
			for (var i in n.svgdep) 
				{
				n.svgdep[i].remove();
				delete n.svgdep[i];
				}
			n.svgdep={};
			var c=0;
			for (var i in n.gov) 
			{
				drawDep(n.index,i,n.gov[i],c);
				c+=1;
			};
		};
	
	};

	
drawalldeps2 = function() 
	{
	
	var distdic = new Object();
	
	for (var i in currentsvg.words)
		{
			var n = currentsvg.words[i];
			for (var i in n.gov) 
				{
					i=parseInt(i,10)
					if (i != 0) 
					{
						dis=Math.abs(n.index-i)
						
						if (dis in distdic) distdic[dis].push([n.index,i])//
						else distdic[dis]=[ [n.index,i] ]
// 						console.log(n.index,i,dis,distdic,distdic[dis],n.gov)
					}
				};	
			
		};
	
	for (var i in currentsvg.words)
		{
			var n = currentsvg.words[i];
			for (var i in n.svgdep) 
				{
				n.svgdep[i].remove();
				delete n.svgdep[i];
				}
			n.svgdep={};
			var c=0;
			for (var i in n.gov) 
			{
				drawDep(n.index,i,n.gov[i],c);
				c+=1;
			};
		};
	};
	
////////////////////////// initialisation ///////////////////////////
/////////////////////////////////////////////////////////////////////


function keys(obj)
	{
		var ks = [];

		for(var key in obj)
		{
			if (obj.hasOwnProperty(key)) ks.push(key);
		}

		return ks;
	}


makewordsxx = function() 
	{
		var words = new Object();
		svgwi=0;
		currentx=tab;
// 		console.log("ttt",tokens)
// 		ks=keys(tokens);
		
// 		ks.sort();
// 		console.log("kkkk",ks)

		for (var i in ks) 
			{
// 				console.log("iii",i, tokens[ks[i]]);
			var node = new Pnode( i, tokens[i]);
			words[i]=node;
			currentx=currentx+node.width+tab;
			};
		
		return words;
	};


makewords = function() 
	{
		var words = new Object();
		svgwi=0;
		currentx=tab;
		for (var i in tokens) 
			{
			var node = new Pnode( i, tokens[i]);
			words[i]=node;
			currentx=currentx+node.width+tab;
			};
		
		return words;
	};



start = function (holder, nr) {
// 	console.log("start",nr);
// 	settings();
	paper = Raphael(holder, window.innerWidth-100,100);
	currentsvg=paper.canvas;
	currentsvg.paper=paper;
	currentsvg.id="svg"+nr;
	
	draw();
	
// 	$("html,body").animate({scrollTop: $(currentsvg).offset().top-[100]},500);
	$(".toggler").removeClass("currentsentence");
	$(".toggler:eq("+(nr-1)+")").addClass("currentsentence");
	$("#export"+nr).css({ visibility: 'visible' });
	currentsvg.undo = new UndoManager({ 
				undoChange: function() { dirt(); },
				redoChange: function() { dirt(); }
					})  
	
	
	

};


draw = function() {
	
	currentsvg.words = makewords();
	if (editable) 
	{
		if(currentsvg.dragConnection) currentsvg.dragConnection = drawsvgDep(1,2,50,60,150,150,"", "",false,0).hide();
		else currentsvg.dragConnection = createConnection().hide();
		lastSelected=0;
	}
	currentsvg.setAttribute("width",svgwi+extraspace);
	currentsvg.setAttribute("height",dependencyspace+shownfeatures.length*line);
	
// 	TODO:work on the difference between drawalldeps and drawalldeps2
	drawalldeps();	
	
	
	
}


currentsvgchanged = function () { } // to be overridden in q.js

	
// 	console.log(attris[shownfeatures[categoryindex]]);
// 	console.log(shownfeatures[categoryindex]);

//////////////////////////////////////////////////////////////////////

Raphael.fn.pointer = function (x,y, size, angle) {
	var startpoint = x+","+(y+size);
	var lefttop = "0,0" +(-size/2)+","+(-size*1.5)+" "+(-size/2)+","+(-size*1.5);
	var righttop = (size/2)+"," +(size/2)+" "+(size/2)+"," +(size/2)+ " "+(size)+",0";
	var arrowPath = this.path("M"+ startpoint+"c"+lefttop+ "c"+righttop+ "z");
	arrowPath.rotate(angle);
	return arrowPath;
}



/*!
 * arborator script for tree handling 
 * version 1.0
 * http://arborator.ilpga.fr/
 *
 * Copyright 2010-2017, Kim Gerdes
 *
 * This program is free software:
 * Licensed under version 3 of the GNU Affero General Public License (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.gnu.org/licenses/agpl-3.0.html
 * 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. 
 *
 */


saveCGI="save.cgi";
eraseTreeCGI="eraseTree.cgi";
statusChangeCGI="statusChange.cgi";
getTreeCGI="getTree.cgi";
exportCGI="export.cgi";
connectCGI="connect.cgi";

treeid=0;
commentid=0;
erasetreeid=0;
compacolors={};
tokensChanged=[];
teacher=0 // this variable is set to 1 in some types of exercices. then saving a (teacher) tree is impossible.

if (typeof tokenModifyable == 'undefined') tokenModifyable=0;

//////////////////////////////////////////////
/////// initialisation ///////////////////////
//////////////////////////////////////////////



$(document).ready(function(){
		

	$('#loader')
	.hide()  // hide it initially
	.ajaxStart(function() {
		$(this).show();
		})
	.ajaxStop(function() {
		$(this).hide();
		});
	
	$(".toggler").click(function()
		{
			toggleOpen($(this), $(this).attr("nr"), $(this).attr("treeid"));	
		})
	$(".othertree").click(function()
		{
			toggleOpen($(this).parent().prev(),  $(this).attr("nr"), $(this).attr("treeid"));	
		})
	$(".saveimg").click(function()
		{
			var nr=$(this).parent().attr("nr");
			var sid=$(this).parent().attr("sid");
			
			saveTree(project,nr,sid,userid, username);
		});
	$(".undoimg").click(function()
		{
			var nr=$(this).parent().attr("nr");
			var sid=$(this).parent().attr("sid");
			currentsvg=$('#svg'+nr)[0];
			currentsvg.undo.undo();
		});
	$(".redoimg").click(function()
		{
			var nr=$(this).parent().attr("nr");
			var sid=$(this).parent().attr("sid");
			currentsvg=$('#svg'+nr)[0];
			currentsvg.undo.redo();
		});
	
	$(".connectRight").click(function(evt)
		{
			var nr=$(this).parent().attr("nr");
			var sid=$(this).parent().attr("sid");
			if (evt.ctrlKey)
				splitRight(project,nr,sid);
			else
				connectRight(project,nr,sid);
		});
	$(".check").click(function(evt)
		{
			var nr=$(this).parent().attr("nr");
			var sid=$(this).parent().attr("sid");
			check(project,nr,sid, $(this).attr("graphical"));
			
		});

		
	onkey();
	settings();
	if (editable) $('.toggler[nr="'+opensentence+'"]').click();
	else openAllTrees();

	/////////////////// navigateAway initialisation:
	
	window.onbeforeunload = function(){ if (dirty.length>0){ return "useless message"} }
	
	
	
	
	$("#dialog").dialog({
			bgiframe: true,
			autoOpen: false,
			height: 350,
 			width:400,
			modal: true,
			buttons: {
				
				Cancel: function() {
					$(this).dialog('close');
					return false;
				},
				"Burn it!!!": function() {
					$(this).dialog('close');
					reallyEraseTree();
					return true;
				}
				}
			});
	
	
// 'Erase node completely': function(){deleteNode($("#fdialog").attr("index"));} ,
// 				'Add as new node': function(){featureChanged(-1);} ,
	$("#fdialog").dialog({
				bgiframe: true,
				autoOpen: false,
				height: 450,
				width:600,
				modal: true,
			});
	if (tokenModifyable>0) $("#fdialog").dialog({
				buttons: {
					'Erase this node': function() {eraseNode(parseInt($("#fdialog").attr("index"),10));$(this).dialog('close');},
					'Duplicate this node': function() {createNode(parseInt($("#fdialog").attr("index"),10));$(this).dialog('close');},
					Cancel: function() {$(this).dialog('close');},
					'OK': function(){featureChanged($("#fdialog").attr("index"));$(this).dialog('close');} 
				}
			});
	else	$("#fdialog").dialog({
				buttons: {
					Cancel: function() {$(this).dialog('close');},
					'OK': function(){featureChanged($("#fdialog").attr("index"));$(this).dialog('close');} 
				}
			});
		
	
// 	makeSplitter();
	
});




// end initialisation ///////////////////////



toggleOpen = function(the, nr, tid) {
	treeid=tid
 
// 	console.log("!!!",the,nr);
	if (the.hasClass("expanded") ) // case: tree already open, closing
	{
		currentsvg=$('#svg'+nr)[0];
		if (dirty.indexOf(currentsvg.id)==-1)
		{
			$("#redo"+nr).css({ visibility: 'hidden' });
			$("#undo"+nr).css({ visibility: 'hidden' });
			$("#export"+nr).css({ visibility: 'hidden' });
			$("#holder"+nr).remove();
			$("#edit"+nr).remove();
			reset();
			$(".toggler").removeClass("currentsentence");
			currentsvg=false;
			the.toggleClass('expanded');
		}
		else
		{
			the.parent().after('<div id="savewarning" style="margin:0 20;" class="ui-state-error ui-corner-all">The tree is unsaved. Save before closing.</div>')
			$("#savewarning").delay(2000).fadeOut('slow'); ;
		}
		
	}
	else // case: no tree showing for this sentence, opening sentence
	{
		
		if (editable) 
		{
// 			treeid=the.attr("treeid");
			$("#holder"+nr).remove();
			the.parent().after('<div id="holder'+nr+'" class="svgholder"> </div>');
			getTree(nr,treeid);
			initialOtherTree(nr);
		}
		the.toggleClass('expanded');
	}
  
  
}

openAllTrees = function() {
	
	
	$( ".toggler:not(.expanded)'" ).each(function( i ) {
		console.log( i + ": " +$(this).attr("nr") );
		var nr = $(this).attr("nr");
// 		if (nr!=1) return(1);
		var treeid = $(this).attr("treeid");
		$("#holder"+nr).remove();
		$(this).parent().after('<div id="holder'+nr+'" class="svgholder"> </div>');
		getTree(nr,treeid);
		initialOtherTree(nr);
		
		$(this).toggleClass('expanded');
		
		
	});

// 	
}


initialOtherTree = function(nr) {
// 	console.log("initialOtherTree",nr);
	$("#othertrees"+nr+" > a").off("click").click(function()
	{
		if (dirty.indexOf(currentsvg.id)==-1) // the currentsvg is not dirty
		{
			treeid=$(this).attr("treeid");
			nr=$(this).parent().parent().attr("nr");
			sid=$(this).parent().parent().attr("sid");
			$("#holder"+nr).remove();
			$(this).parent().parent().after('<div id="holder'+nr+'" class="svgholder"> </div>');
			getTree(nr,treeid);
		}
		else
		{
			$(this).parent().parent().after('<div id="savewarning" style="margin:0 20;" class="ui-state-error ui-corner-all">The tree is unsaved. Save before closing.</div>')
			$("#savewarning").delay(2000).fadeOut('slow'); ;
		}
				
	});
	
	$("#export"+nr).show().click(function()
	{
// 		console.log("export clicked",$(this).offset(),$("#ex").attr("nr"));
		$("#ex").show();
		$("#ex").offset($(this).offset()).attr("nr",$(this).attr("nr"));
// 		console.log("export clicked",$(this).offset(),$("#ex"),$("#ex").attr("nr"),$("#ex").css("visibility"))

	});
	
}


getJSON = function (nr, treeid) {
	
	$('#sentencediv'+nr).append($("#loader"));
	delete tokens;
	tokens=new Object();
	
	$.getJSON(
		getTreeCGI,
		{
				project: project,
				treeid: treeid,
				filename: filename,
				filetype: filetype,
				nr: nr			
		},
		function(data)
				{
					startTree(nr,data)
				}
			
	);
	initialOtherTree(nr);	
	
}

var allData=new Object();
var allGoodData=new Object();
var tokens=new Object();



getTree = function(nr, treeid) {
	if (editable)
	{
// 			console.log("getTree",nr,treeid);
		$("#othertrees"+nr+" > .thistree").removeClass("thistree").addClass("othertree");
		$('a.othertree[treeid=' + treeid + ']').removeClass("othertree").addClass("thistree");
		getJSON(nr, treeid)
		
	}
	else
	{
		$.get('svg/contentSnippet.html', function (response) {
// 			console.log("cool",response);
			$("#holder"+nr).html(response);
		}).fail( function() {
			console.log("oh non!"+nr+"-"+"treeid");
			getJSON(nr, treeid);
			
			
		})
		;
// 		$("#holder"+nr).load('svg/contentSnippet.html');
		
	}
}


startTree = function (nr, data) {
	delete redata;
	redata = new Object();

	$("html, body").animate({scrollTop: $("#loader").offset().top-50}, 1000,'easeInOutCubic');
	
	$.each(data, function(key, val) 
		{
			redata[key]=val;
		});
	if ("tree" in redata)
		{
			allData[nr]=new Object();
			$.each(redata.tree, function(key, val) 
			{
				tokens[key]=val;
				allData[nr][key]=val; // todo: only use allData, forget about tokens!
				
			});
			start("holder"+nr, nr); // call to function from arborator.draw.js
			$("#sentencediv"+nr).attr("treeid",treeid);
		}
	if ("goodtree" in redata)
		{
			allGoodData[nr]=new Object();
			$.each(redata.goodtree, function(key, val) 
			{
				allGoodData[nr][key]=val; 
				
			});
		}
	
	if ("teacher" in redata)
		{
			teacher = redata["teacher"];
		}
		else
		{
			teacher = 0;
		}
	
	currentsvg.sentencefeatures={}
	for (a in redata)
	{
		if (!(["tree","goodtree","teacher"].includes(a)))
		{
			if (shownsentencefeatures.includes(a))
			{
				$('#holder'+nr).append('<p>'+redata[a]+'</p>');
			}
		}
	}
	if (!editable)
	{

		var svg = $('#svg'+nr)[0].paper.toSVG();
	}

	$("#save"+nr).css({ visibility: 'hidden' });
	$("#undo"+nr).css({ visibility: 'hidden' });
	$("#redo"+nr).css({ visibility: 'hidden' });
	
}


writeSvg = function(treeid,svg) {
	
	console.log("writeSvg")
	$.ajax({
		type: "POST",
		url: "writeSvg.cgi",
		data: {	"project":project,	"treeid":treeid,
			"svg":svg
		},
		success: function(answer){
				console.log("cool:saved:"+answer)				
			},
		error: function(XMLHttpRequest, textStatus, errorThrown){
			console.log("uncool:notsaved:"+errorThrown)
			}
		});	
	
	
}



getCompareTree = function(project, treeids, nr) {
	
	$("#othertrees"+nr+" > .thistree").removeClass("thistree").addClass("othertree");

	$('#sentencediv'+nr).append($("#loader"));
        delete tokens;
        tokens=new Object();
// 	delete redata;
// 	redata = new Object();
	treeid=0;
        $.getJSON(
	      getTreeCGI,
	      {
			"project": project,
			"treeids":treeids,
			"compare":true
	      },
	      function(data)
			{
				startTree(nr,data);
			}

        );
}


function countChecked() {
	var n = $("input:checked").length;
	if (n>1) $("#compasubmit").show();
	else $("#compasubmit").hide();
	  
}


compare = function (data,snr,colors) {	
	var formhtml="";
	compacolors=colors;
	$.each(data, function(k, v) {
// 		console.log(k,v)
		if (k=="guest") formhtml=formhtml+'<input type="checkbox" name="'+k+'" value="'+v+'" >'+k+'<span style="color:#'+colors[k]+'">â¼</span><br>'
		else formhtml=formhtml+'<input type="checkbox" name="'+k+'" value="'+v+'" checked>'+k+'<span style="color:#'+colors[k]+'">â¼</span><br>'
		});
	$("#complist").html(formhtml);
	$(":checkbox").click(countChecked);
	countChecked();
	
// 	$("#compbox").offset($("#compa"+snr).offset()).attr("nr",snr);
	$("#compbox").css({ top: $("#compa"+snr).offset().top, left: $("#compa"+snr).offset().left }).attr("nr",snr);
	$("#compbox").show();
	
}

compareSubmit = function () {	
	treeidname={};
	$.each($("input:checked"), function(k, v) {
// 		console.log(k,v.value,v.name);
		treeidname[v.value]=v.name
// 		console.log(treeidname);
	});
	var nr = $("#compbox").attr("nr");
	$("#holder"+nr).remove();
// 	console.log("yyyyyyyyyyy",$("#sentencediv"+nr));
	$("#sentencediv"+nr).after('<div id="holder'+nr+'" class="svgholder"> </div>');
	getCompareTree(project, treeidname, nr);
	
}
	
exportTree = function () {
	
	nr=$("#ex").attr("nr");
	treeid = $("#sentencediv"+nr).attr("treeid");
// 	console.log("exportTree",nr,treeid);
	
	var v=$("#exptype")[0].options[$("#exptype")[0].selectedIndex].value;
	
	if(v=="conll" || v=="xml") 
		{
			var currentsvg=$('#svg'+nr)[0];			
			$('#source').attr("value",stringify(currentsvg));
// 			$('#cat').attr("value",shownfeatures[categoryindex]);
			$('#ex').attr("action",exportCGI).submit();			
		}
	else 	{
			$("svg").attr("xmlns","http://www.w3.org/2000/svg");
			$("svg").attr("xmlns:xlink","http://www.w3.org/1999/xlink");
			var header = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'
			$('#source').attr( "value", header+$('#holder'+nr+' >svg').clone().wrap('<p>').parent().html() );			
			$('#ex').submit();
		
		}
}

	
function dirt() { 
	
	var nr=currentsvg.id.substr(3);
	
	if (teacher) 
	{
		return;
	}
// 	console.log("dirt starts",nr,dirty,currentsvg.id,dirty.indexOf(currentsvg.id))
	if (currentsvg.undo.undo_available)
	{
		var ind=dirty.indexOf(currentsvg.id);
		$("#save"+nr).css({ visibility: 'visible' });
		$("#undo"+nr).css({ visibility: 'visible' }).attr("title","undo: "+currentsvg.undo.undo_names[currentsvg.undo.undo_available - 1]);
		if (ind!=-1) dirty.splice(ind, 1);
		dirty.push(currentsvg.id);
	}
	else
	{	
		var ind=dirty.indexOf(currentsvg.id);
		$("#save"+nr).css({ visibility: 'hidden' });
		$("#undo"+nr).css({ visibility: 'hidden' });
		if (ind!=-1) dirty.splice(ind, 1);
	}
	if (currentsvg.undo.redo_available)
	{
		$("#redo"+nr).css({ visibility: 'visible' }).attr("title","redo: "+currentsvg.undo.redo_names[currentsvg.undo.redo_available - 1]);
	}	
	else
	{	
		$("#redo"+nr).css({ visibility: 'hidden' });
	}	
	if (dirty.length==0)	$("#sav").attr(attrclean).addClass( "ui-state-disabled " );
	else			$("#sav").attr(attrdirty).removeClass( "ui-state-disabled " );
// 	console.log("end dirt",dirty)
	
}
	
	
	

openFeatureTable = function (index,words) {
	// called from draw.js: openFeatureTable(this.index,currentsvg.words)
	
	var tbody='<tbody id="featabody">'
	var fs = words[index].features;
	
	// 	console.log("openFeatureTable", index,words[index])
	
	tbody+='<tr><input size=8 type="hidden" name="wordid" value='+index+'></tr>';

	for (a in fs) 
		{
		ivstyle="";
		iastyle="";
		if (shownfeatures.indexOf(a)==0 && tokenModifyable==0) {iastyle="readonly='readonly'";ivstyle="readonly='readonly'";}
		else if (shownfeatures.indexOf(a)>=0) iastyle="readonly='readonly'";
		tbody+='<tr><td style="padding:0;"><input size=8 type="text" name="att-'+a+'" value="'+a+'" '+iastyle+' ></td><td style="padding:0;"><input size=8 type="text" name="val-'+a+'" value="'+fs[a]+'"  '+ivstyle+' ></td></tr>';
		}
	tbody+='<tr><td style="padding:0;"><input size=8 type="text"  name="att-new" value=""></td><td style="padding:0;"><input size=8 type="text"  name="val-new" value=""></td></tr>';
	tbody+='</tbody>'
	$('#featabody').before(tbody).remove();
	$('#fdialog').attr("index",index);
	$('#ui-dialog-title-fdialog').text("Features of Node "+index);
	$("input").focus(function () {nokeys=true;}).blur(function () {nokeys=false;});
	$('#fdialog').dialog('open');
}	
	
	

featureChanged = function (ind) {
	
	newfeatures={};
	$("#featab input").each(function (i) 
		{
			if (this.name.substr(0,4)=="att-") 
			{
				var a=this.name.substr(4,this.name.length);
				var v=$("input[name='val-" +a+ "']").attr("value");
				if (v) newfeatures[this.value]=v;			
			}
		})
	setFeatures(ind,newfeatures);
	}
	
	
setFeatures = function (nodenumber,features) {
	
	// 	console.log("setting",nodenumber,features);
	
	var n = currentsvg.words[nodenumber];
	var oldfeatures = n.features;
	
	n.features=features;	
	
	sdiv=$(currentsvg).parent().prev()
	var nr = sdiv.attr("nr");
	delete tokens;
	tokens=new Object();
	
// 	svgws=$.extend(true, {}, currentsvg.words);
	
	for (i in currentsvg.words)
	{
		tokens[i]={"gov":currentsvg.words[i]["gov"]};
		for (j in currentsvg.words[i]["features"])
		{
// 			console.log(j,currentsvg.words[i]["features"][j])
			tokens[i][j]=currentsvg.words[i]["features"][j];			
		}
	}
	paper.clear();
	
	draw();
// 	currentsvg.words=svgws
	currentsvg.undo.undoable("reestablish manually changed features", setFeatures, [nodenumber, oldfeatures]);
	if (tokenModifyable>0) tokensChanged.push([nodenumber,0]);
}
	




	
createNode = function(ind) {
	createindex=ind;
		
	$("#question").html("Are you sure that you want to duplicate this node? <br>This change will immediately apply to <b>all annotations</b> of this sentence and <b>cannot</b> be undone.")
	$("#dialog").dialog({	
			height: 450,
			width:500,
			buttons: {
			
				Cancel: function() {
					$(this).dialog('close');
					return false;
					},
				"Duplicate the node": function() {
					$(this).dialog('close');
					reallyCreateNode();
					return true;
					}
				}
		});
	$('#ui-dialog-title-dialog').text("Please confirm");
	$('#dialog').dialog('open');
		
		}	
	
reallyCreateNode = function() {
	var thenr=currentsvg.id.substr(3);
	var thesid=$("#sentencediv"+thenr).attr("sid");
	
	if (tokenModifyable>0) tokensChanged.push([createindex,1]);
	console.log(project,thenr,thesid,userid,username,tokensChanged);
	
	saveTree(project,thenr,thesid,userid,username);
	
}



	
eraseNode = function(ind) {
	eraseindex=ind;
		
		$("#question").html("Are you sure that you want to erase this node? <br>This change will immediately apply to <b>all annotations</b> of this sentence and <b>cannot</b> be undone. You and the other users will lose the changes they have done on this node.")
		$("#dialog").dialog({	height: 450,
				buttons: {
				
					Cancel: function() {
						$(this).dialog('close');
						return false;
						},
					"Erase the node": function() {
						$(this).dialog('close');
						reallyEraseNode();
						return true;
						}
					}
			});
		$('#ui-dialog-title-dialog').text("Please confirm");
		$('#dialog').dialog('open');
		
		}	
	
reallyEraseNode = function() {
	var thenr=currentsvg.id.substr(3);
	var thesid=$("#sentencediv"+thenr).attr("sid");
	
	if (tokenModifyable>0) tokensChanged.push([eraseindex,-1]);
	console.log(project,thenr,thesid,userid,username,tokensChanged);
	
	saveTree(project,thenr,thesid,userid,username);
	
}

///////////////////////// connecting/splitting //////////////////////
////////////////////////////////////////////////////////////////////


connectRight = function(project,nr, sid) {

	// TODO: kick this out:
	// 	reallyconnectRight(project,nr, sid);
	// 	return;
	
	$("#question").html("Are you sure that you want to join this sentence with the following sentence? This <b>cannot</b> be undone and will affect all the users.")
	$("#dialog").dialog({height: 450,
			buttons: {
			
				Cancel: function() {
					$(this).dialog('close');
					return false;
					},
				"Connect the sentence": function() {
					$(this).dialog('close');
					reallyconnectRight(project,nr, sid);
					return true;
					}
				}
		});
	$('#ui-dialog-title-dialog').text("Please confirm");
	$('#dialog').dialog('open');
	}

splitRight = function(project,nr, sid) {

	// TODO: kick this out:
	// 	reallyconnectRight(project,nr, sid);
	// 	return;
	if (lastSelected && currentsvg.id.substr(3)==nr) // word selected and clicked the connect button on the last modified svg
		{
			console.log(lastSelected,currentsvg.words[lastSelected],currentsvg.words[lastSelected].features.t,treeid)
			$("#question").html("Are you sure that you want to split this sentence after the word '"+currentsvg.words[lastSelected].features.t +"'?<br/> This <blink>cannot</blink> be undone and will affect all the users.")
			$("#dialog").dialog({height: 450,
					buttons: {
					
						Cancel: function() {
							$(this).dialog('close');
							return false;
							},
						"Split the sentence": function() {
							$(this).dialog('close');
							reallysplitRight(project,nr, sid,currentsvg.words[lastSelected].index);
							return true;
							}
						}
				});
			$('#ui-dialog-title-dialog').text("Please confirm");
			$('#dialog').dialog('open');
		}
	}
	
	
reallyconnectRight = function(project,nr, sid) {

	$.ajax({
		type: "POST",
		url: connectCGI,
		data: {"project":project,"nr":nr,"sid":sid,"connectsplit":"connect"}, 
		success: function(answer){
				console.log("sentence nr "+nr+" connected!\n"+answer);
				location.reload(); 
			},
		error: function(XMLHttpRequest, textStatus, errorThrown){
			console.log("error connecting",project,nr,sid)
			alert("error connecting"+XMLHttpRequest+ "\n"+textStatus+ "\n"+errorThrown);
			}
		});
	}

reallysplitRight = function(project,nr, sid, toknr) {

	$.ajax({
		type: "POST",
		url: connectCGI,
		data: {"project":project,"nr":nr,"sid":sid,"toknr":toknr,"connectsplit":"split"}, 
		success: function(answer){
				console.log("sentence nr "+nr+" split!\n"+answer);
				location.reload(); 
			},
		error: function(XMLHttpRequest, textStatus, errorThrown){
			console.log("error connecting",project,nr,sid)
			alert("error connecting"+XMLHttpRequest+ "\n"+textStatus+ "\n"+errorThrown);
			}
		});
	}

///////////////////////// check ///////////////////////////////
////////////////////////////////////////////////////////////////////	
	

check = function(project,snr, sid, graphical) {	
	
	prepareData(snr); // useful to be able to compare even before saving
// 	console.log(allData, allGoodData, snr);

	var goodcat=[],badcat=[],gooddep=[],baddep=[];
	if (!(snr in allGoodData)) return;
	$.each(allGoodData[snr], function(toknr, nodedic) 
	{
		console.log(toknr,allData[snr][toknr]);
		if (nodedic.tag == allData[snr][toknr].tag) goodcat.push(toknr);	
		else badcat.push(toknr);
		if ($.isEmptyObject( nodedic.gov )) return;
		$.each(nodedic.gov, function(govnum, func) 
		{
			if ((func == allData[snr][toknr].gov[govnum] || allData[snr][toknr].gov[govnum]==null && govnum==-1)) gooddep.push(toknr);
			else baddep.push(toknr);
		})
	});
	if (badcat.length==1) 	html="1 category error: "
	else 		html=badcat.length+" category errors: "
	html += (goodcat.length/(goodcat.length+badcat.length)*100).toFixed(1)+"% correct.<br/>"
	if (baddep.length==1) 	html+="1 dependency error: "
	else		html+=baddep.length+" dependency errors: "
	html += (gooddep.length/(gooddep.length+baddep.length)*100).toFixed(1)+"% correct.<br/>"
	$("#checklist").html(html);
	$("#checkbox").css({ top: $("#check"+snr).offset().top, left: $("#check"+snr).offset().left })
	$("#checkbox").show();
	if (graphical==1)
	{
		for (var i in currentsvg.words)
		{
			var n = currentsvg.words[i];
			if(badcat.indexOf(i)==-1) n.svgs.tag.attr({"fill":"black"});
			else n.svgs.tag.attr("fill","red");
			if(baddep.indexOf(i)==-1) n.svgs.t.attr({"fill":"black"});
			else n.svgs.t.attr("fill","red");
		}
	}
	
}

///////////////////////// saving etc ///////////////////////////////
////////////////////////////////////////////////////////////////////

prepareData = function (nr) {
	var currentsvg=$('#svg'+nr)[0];	
	tree=stringify(currentsvg);
	allData[nr]=new Object();
	$.each(nsimple, function(toknr, strangedic) 
	{
		allData[nr][toknr]=new Object();
		allData[nr][toknr].tag = strangedic.features.tag;
		allData[nr][toknr].gov = strangedic.gov;
	});
}
saveAllTrees = function () {
	
		oldcsvg=currentsvg;		
		for (i in dirty.slice())
		{
			var thenr=dirty[i].substr(3);
			var thesid=$("#sentencediv"+thenr).attr("sid")
			saveTree(project,thenr,thesid,userid,username);
		}
		currentsvg=oldcsvg;
	
	}

	
simpleFeatStruc = function (svg) {
	nsimple={}
	var good={index:0,gov:0,features:0};
	for (var i in svg.words)
		{ 
			nsimple[i]={}
			for (j in svg.words[i]["gov"]) // correct compare governors before saving
			{
				if (svg.words[i]["gov"][j] instanceof Object) svg.words[i]["gov"][j] = svg.words[i]["gov"][j][0][0];
			}
			if (svg.words[i]["features"][shownfeatures[categoryindex]] instanceof Object)  // correct compare cats before saving
			{
				svg.words[i]["features"][shownfeatures[categoryindex]]=svg.words[i]["features"][shownfeatures[categoryindex]][0][0]
			}
			
			
			for (feat in good) nsimple[i][feat]=svg.words[i][feat]; // copy the good features into nsimple before saving
// 			console.log("vvv",currentsvg.words[i]["gov"])
		}
	return {"tree":nsimple,"sentencefeatures":svg.sentencefeatures};
	
	}
	
stringify = function (currentsvg) {
	return JSON.stringify(simpleFeatStruc(currentsvg), "")
	}
	
saveTree = function (project,nr,sid,uid,username) {
	prepareData(nr);
	$("#save"+nr).addClass( "ui-state-disabled " );
	
	$.ajax({
		type: "POST",
		url: saveCGI,
		data: {	"project":project,	"tree":tree,
			"sentenceid":sid,	"sentencenr":nr,
			"userid":uid,		"username":username,	"todo":todo, 
			"addEmptyUser":addEmptyUser, "validvalid":validvalid,
			"validator":validator,	"tokensChanged":JSON.stringify(tokensChanged, "")
		},
		success: function(answer){
				if ( !(answer instanceof Object) || !("treeid" in answer) ) 
				{
					alert("Problem saving the tree. Please check the javascript console for more information.");
// 					console.log(answer);
					$("#save"+nr).removeClass( "ui-state-disabled " );
					return;
				
				}
				
// 				console.log(tokensChanged,tokensChanged.length, tokensChanged != []);
				if (tokensChanged.length>0) // special stuff for duplicating and erasing nodes
				{
					tokens=new Object();
					$("#holder"+nr).remove();
					$("#sentencediv"+nr).after('<div id="holder'+nr+'" class="svgholder"> </div>');
					startTree(nr,answer);
					$("#toggler"+nr).html(nr+": "+answer.sentence);
					initialOtherTree(nr);
					tokensChanged=[];
						
				}
							
				dirty.splice(dirty.indexOf(currentsvg.id), 1);
				if (dirty.length==0)	$("#sav").attr(attrclean).addClass( "ui-state-disabled " );
				else		$("#sav").attr(attrdirty).removeClass( "ui-state-disabled " );
				$("#save"+nr).css({ visibility: 'hidden' });
				$("#othertrees"+nr).html(answer.treelinks);
				
				treeid=answer.treeid;
				$('a.othertree[treeid=' + treeid + ']').removeClass("othertree").addClass("thistree");
				initialOtherTree(nr);
				$("#save"+nr).removeClass( "ui-state-disabled " );
				
				
			},
		error: function(XMLHttpRequest, textStatus, errorThrown){
			console.log("error",project,nr,sid,uid,username)
			alert("error saving"+XMLHttpRequest+ "\n"+textStatus+ "\n"+errorThrown);
			$("#save"+nr).removeClass( "ui-state-disabled " );	
			}
		});
	// 	console.log("saved",project,nr,sid,uid,username)
	}

	
	
eraseSentenceNumber=0;
	
eraseTree = function(nr, treeid, treecreator) {
	
	eraseSentenceNumber=nr;
	erasetreeid=treeid;
	$("#question").html("<span style='font-size:.7em;'>Are you sure that you want to completely erase from the database the beautiful analysis made by "+treecreator+"?</span>")
	$('#dialog').dialog('open');
	
	}	
	
reallyEraseTree = function() {
	$.ajax({
		type: "POST",
		url: eraseTreeCGI,
		data: {"project":project,"treeid":erasetreeid,"sentencenr":nr,"username":username}, 
		success: function(answer){
				
				$("#othertrees"+eraseSentenceNumber).html(answer);
				initialOtherTree(eraseSentenceNumber);
				console.log(nr+"erased!"+answer);
				
			},
		error: function(XMLHttpRequest, textStatus, errorThrown){
			console.log("error",project,erasetreeid)
			alert("error erasing"+XMLHttpRequest+ "\n"+textStatus+ "\n"+errorThrown);
			}
		});
	
}


nexttreestatus =  function(sid,snr) {

	treeid = $("#toggler"+snr).attr("treeid");
	
// 	console.log("nexttreestatus. treeid:",treeid)	
	if (treeid!=null)
		$.ajax({
			type: "POST",
			url: statusChangeCGI,
			data: {"project":project,"treeid":treeid,"snr":snr,"userid":userid,"username":username,"sid":sid, "textid":textid}, 
			success: function(answer){
	// 				var nr=currentsvg.id.substr(3);
					$("#status"+snr).html(answer.status);
					$("#othertrees"+snr).html(answer.links);
	// 				initialOtherTree(nr);
					console.log("changed!",answer);
					
				},
			error: function(XMLHttpRequest, textStatus, errorThrown){
				console.log("error",project,treeid)
				alert("error erasing"+XMLHttpRequest+ "\n"+textStatus+ "\n"+errorThrown);
				}
			});
		
}








	
	
	
	
	/*
saveComment = function (answerto) {
// 	alert("saveComment");

	var comment = $('#txtDefaultHtmlArea').htmlarea('toHtmlString');
	$.ajax({
		type: "POST",
		url: saveCGI,
		data: {		"comment":comment,
				"sentenceid":sentenceid,
				"userid":userid,
				"answerto":answerto
			},
		success: function(x){
				$("#comments").prepend('<div class="comment"><div class="commentitle"><cite style="float: left;"><img title="comment" src="images/commentNano.png"/>'+username+'</cite><em style="float: right;">now</em></div><br/>'+comment+'</div>');
			},
		error: function(XMLHttpRequest, textStatus, errorThrown){
				alert("error saving comment"+XMLHttpRequest+ textStatus+ errorThrown);
			}
		});
		
	$("#wysiwyg").hide();
		
	}*/





/*
commentstuff = function () {
	$("#newcomment").click(function () { 
// 		$(this).hide(); comments
		$('#wysiwyg').show();
// 		$("#txtDefaultHtmlArea").htmlarea();
		$("#txtDefaultHtmlArea").htmlarea({



                // Do something once the editor has finished loading
                loaded: function() {
                    //// 'this' is equal to the jHtmlArea object
                    //alert("jHtmlArea has loaded!");
//                     this.showHTMLView(); // show the HTML view once the editor has finished loading
		    		document.getElementById("wysiwyg").scrollIntoView(true);
				$("#wysiwyg").focus();

                }
            });

		
		
// 		$("#txtDefaultHtmlArea").htmlarea(); // Initialize jHtmlArea's with all default values
	});*/
	

	/*
	$("#tabledialog").dialog({
			bgiframe: true,
			autoOpen: false,
			height: 444,
 			width:window.innerWidth-50,
// 			modal: true,
			buttons: {
				
				Cancel: function() {
					$(this).dialog('close');
				},
				"OK": function() {
					$(this).dialog('close');
					//alert(treeid);
					$("#eraseCommentNumber").attr("value",commentid);
					$("#erasecomment").submit();
				}
				}
			});
	
	$("#confirmdialog").dialog({
			bgiframe: true,
			autoOpen: false,
			height: 350,
 			width:400,
// 			modal: true,
			buttons: {
				
				Cancel: function() {
					$(this).dialog('close');
				},
				"OK": function() {
					$(this).dialog('close');
					//alert(treeid);
// 					$("#eraseCommentNumber").attr("value",commentid);
// 					$("#erasecomment").submit();
				}
				}
			});
	$('#corrdialog').dialog({
			bgiframe: true,
			autoOpen: true,
			height: 350,
 			width:window.innerWidth-50,
			position: 'bottom',
// 			modal: true,
			buttons: {
				
				Cancel: function() {
					$(this).dialog('close');
				},
				"OK": function() {
					$(this).dialog('close');
					
				}
				}
			});
	
// $('#wysiwyg').show();
// $("#txtDefaultHtmlArea").htmlarea(); // Initialize jHtmlArea's with all default values
	
  
  	
	};*/



/*!
 * arborator script for dependency viewing 
 * version 1.0
 * http://arborator.ilpga.fr/
 *
 * Copyright 2010-2016, Kim Gerdes
 *
 * This program is free software:
 * Licensed under version 3 of the GNU Affero General Public License (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.gnu.org/licenses/agpl-3.0.html
 * 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. 
 *
 */


///////////////////////// settings //////////////// 
////////////////////////////////////////////////////////////////////


shownfeatures = ['t','cat','lemma'];
shownsentencefeatures = ['markupIU'];
functions = [];
funcDic = {};
categoryindex = '1';
root = 'ROOT';
catDic =  {} ;


tab=8; 		// space between tokens
line=25 		// line height
dependencyspace=320; 	// y-space for dependency representation
xoff=8; 		// placement of the start of a depdency link in relation to the center of the word
linedeps=6; 		// y distance between two arrows arriving at the same token (rare case)
pois=4; 		// size of arrow pointer
tokdepdist=15; 		// distance between tokens and depdendency relation
funccurvedist=8;	// distance between the function name and the curves highest point
depminh = 15; 		// minimum height for dependency
worddistancefactor = 2; 	// distant words get higher curves. this factor fixes how much higher.
extraspace=50 		// extends the width of the svg in order to fit larger categories
defaultattris={"font": '14px "Arial"', "text-anchor":'start'};
attris = {	"t":	{"font": '18px "Arial"', "text-anchor":'start',"fill": '#000',"stroke-width": '0'}, 
	"cat":	{"font": '12px "Times"', "text-anchor":'start',"fill": '#000'},
	"lemma":	{"font": '14px "Times"', "text-anchor":'start',"fill": '#000'},
	"depline":	{"stroke": '#000',"stroke-width":'1',"stroke-dasharray": ''},
	"deptext":	{"font": '12px "Times"', "font-style":'italic', "fill": '#000'},
	"form":	{"font-style":'italic'}
	  };

colored={"dependency":null}; //object of elements to be colored. just leave the object empty {} if you don't want colors
attris[shownfeatures[categoryindex]]=attris["cat"]
colored[shownfeatures[categoryindex]]=null;
	
	




///////////////////////// node object and functions //////////////// 
////////////////////////////////////////////////////////////////////


function Pnode(index,token)
	{
		index=parseInt(index,10);
		this.index=index;
		this.gov={};
		if ("gov" in token) 
		{
			for (var i in token["gov"])
			{
				this.gov[parseInt(i,10)]=token["gov"][i]
			}
		}	
		this.width=0;
		this.features=new Object(); // contains for each shownfeature the corresponding text
		this.svgs=new Object(); // contains for each shownfeature the corresponding svg object
		var currenty=dependencyspace;
				
		
		for (var i in shownfeatures) 
			{	
			var f = shownfeatures[i]; // f= eg. "cat"
			
			this.features[f]=token[f];
			
			if ( (token[f] instanceof Object) ) // mode compare!!!
			{				
				
				var user0=token[f][0][1][0];
				if (user0=="ok")
				{
					c=token[f][0][0]
					var t = paper.text(currentx, currenty, c);
					t.attr(defaultattris);
					t.attr({fill: "#"+compacolors[user0],title:"All annotators agree."});
				}
				else
				{
					c=""
					ti=""
					for (i in token[f])
					{
						c=c+" "+token[f][i][0];
						ti=ti+" "+token[f][i][0]+" ("+token[f][i][1]+")";
					}
					var t = paper.text(currentx, currenty, c);
					t.attr(defaultattris);
					t.attr({fill: "red",title:ti});
					
				}
			
			
			}
			else // normal mode
			{
				var t = paper.text(currentx, currenty, token[f]);
				t.attr(defaultattris);
				if (f in token) t.attr(attris[f]);				
			}
			t.attr("index",index);
			var wi=t.getBBox().width;
			t.width=wi;
			t.index=index;
		
			t.node.setAttribute("class",f);
			if (f in colored && token[f] in catDic) t.attr(catDic[token[f]]);
			this.svgs[f]=t;
			if (wi>this.width) this.width=wi;
			currenty=currenty+line;
			
			};
			
		
		for (var a in token) // remaining features
			{
				v=token[a]
				if (a in this.features || a=="gov") continue;				
				this.features[a]=v;
			}
		
			

		svgwi=svgwi+this.width+tab;
		this.x=0;
		this.y=0;
		this.svgdep={};		// the svg dependency (the arrow and the function name): gives the svgobject for each govind

	};



	

////////////////////////////////////////////////////////////////
/////////////////////essential drawing stuff////////////////////
////////////////////////////////////////////////////////////////



drawsvgDep = function(ind,govind,x1,y1,x2,y2,func,tooltip, color, funcposi)
	{
		// ind: word index
// 		govind: index of governor
// 		x1,y1: start coordinates
// 		x2,y2: target coordinates
// 		func: function name
// 		tooltip, color, funcposi: only for compare mode
		
		var set=currentsvg.paper.set();
		var x1x2=Math.abs(x1-x2)/2;
		var yy = Math.max(y1-x1x2-worddistancefactor*Math.abs(ind-govind),-tokdepdist);
		yy = Math.min(yy,y1-depminh);

		var cstr="M"+x1+","+y1+"C"+x1+","+yy+" "+x2+","+yy+" "+x2+","+y2;
		var c = currentsvg.paper.path(cstr).attr(attris["depline"]).attr({"x":x1,"y":y1});

		var poi = currentsvg.paper.pointer(x2,y2,pois,0).attr(attris["depline"]);

		a=c.getPointAtLength(c.getTotalLength()/2);
		t = currentsvg.paper.text(a.x, a.y-funccurvedist-funcposi*10, func);
		t.attr(attris["deptext"]);
		t.index=ind;
		t.govind=govind;
		
		if (govind==0) { // case: head of sentence (root): the function name needs special treetment
			var newx=a.x+t.getBBox().width/2+funccurvedist/2;
			if (newx+t.getBBox().width/2 > svgwi) newx=a.x-t.getBBox().width/2-funccurvedist/2;
			if (newx-t.getBBox().width/2 < 0) newx=a.x;
			t.attr("x",newx);
		}
		if (color) // case compare: colors depend on users
		{
			c.attr({stroke: color});
			poi.attr({stroke: color});
			t.attr({fill: color,title:tooltip});
			
		
		}
		else if ("dependency" in colored && func in funcDic) // normal graph: color depends on function
		      {
			var att = funcDic[func];
			c.attr(att);
			poi.attr({stroke: att["stroke"]});
			t.attr({fill: att["stroke"]});
		      };
		
		set.push(t); // text
		set.push(c); // curve
		set.push(poi); // pointer
		
		return set;
	};
	

drawDep = function(ind,govind,func,c)	// draw dependency of type func from govind to ind (c is usually 0, only for multiple heads)
	{

		if (govind==0 ) // head of sentence
		{
			node2=currentsvg.words[ind];
			var x2=node2.svgs[shownfeatures[0]].attr("x")+node2.svgs[shownfeatures[0]].width/2;
			var y2=node2.svgs[shownfeatures[0]].attr("y")-tokdepdist;
			var x1=x2;
			var y1=0;
		}
		else // normal dependency
		{
			var node1=currentsvg.words[govind];
			var node2=currentsvg.words[ind];
			if (node1==null) {delete node2.gov[govind];return;}
			if (ind<govind) var x1=node1.svgs[shownfeatures[0]].attr("x")+node1.svgs[shownfeatures[0]].width/2-xoff;
			else var x1=node1.svgs[shownfeatures[0]].attr("x")+node1.svgs[shownfeatures[0]].width/2+xoff;
			var x2=node2.svgs[shownfeatures[0]].attr("x")+node2.svgs[shownfeatures[0]].width/2;
			var y1=node1.svgs[shownfeatures[0]].attr("y")+pois-tokdepdist;
			var y2=node2.svgs[shownfeatures[0]].attr("y")-tokdepdist-c*linedeps;
		};
		
		if ( (func instanceof Object) ) // mode compare!!!
		{
			var user0=func[0][1][0];

			if (user0=="ok")
			{
				func=func[0][0]
				node2.svgdep[govind]=drawsvgDep(ind,govind,x1,y1,x2,y2,func,  "All annotators agree.", "#"+compacolors[user0],0);
			}
			else
			{
				var lastset=false;
				for (i in func)
				{
					var funcusers=func[i];
					i=parseInt(i,10);
					for (j in funcusers[1])
					{
						var u=funcusers[1][j];
						j=parseInt(j,10);
						var newset=drawsvgDep(ind,govind, x1+j*2,y1, parseInt(x2,10)+j*2, y2, funcusers[0], funcusers[0]+" ("+u+") ", "#"+compacolors[u], i+j);
						if (lastset) lastset.push(newset);
						else lastset=newset;
						node2.svgdep[govind]=lastset;
						
					}
					
				}
				
			}
			
		
		}
		else node2.svgdep[govind]=drawsvgDep(ind,govind,x1,y1,x2,y2,func, "click to change", false, 0);
	};


drawalldeps = function() 
	{
		
	for (var i in currentsvg.words)
		{
			var n = currentsvg.words[i];
			for (var i in n.svgdep) 
				{
					n.svgdep[i].remove();
					delete n.svgdep[i];
				}
			n.svgdep={};
			var c=0;
			for (var i in n.gov) 
			{
				drawDep(n.index,i,n.gov[i],c);
				c+=1;
			};
		};
	};

	
drawalldeps2 = function() 
	{
	
	var distdic = new Object();
	
	for (var i in currentsvg.words)
		{
			var n = currentsvg.words[i];
			for (var i in n.gov) 
				{
					i=parseInt(i,10)
					if (i != 0) 
					{
						dis=Math.abs(n.index-i)
						if (dis in distdic) distdic[dis].push([n.index,i])//
						else distdic[dis]=[ [n.index,i] ]
					}
				};	
			
		};
	
	for (var i in currentsvg.words)
		{
			var n = currentsvg.words[i];
			for (var i in n.svgdep) 
				{
					n.svgdep[i].remove();
					delete n.svgdep[i];
				}
			n.svgdep={};
			var c=0;
			for (var i in n.gov) 
			{
				drawDep(n.index,i,n.gov[i],c);
				c+=1;
			};
		};
	};
	
////////////////////////// initialisation ///////////////////////////
/////////////////////////////////////////////////////////////////////




makewords = function(tokens) 
	{
		var words = new Object();
		svgwi=0;
		currentx=tab;
		for (var i in tokens) 
			{
			var node = new Pnode( i, tokens[i]);
			words[i]=node;
			currentx=currentx+node.width+tab;
			};
		
		return words;
	};


draw = function (holder, tokens) {
	paper = Raphael(holder, window.innerWidth-100,100);
	currentsvg=paper.canvas;
	currentsvg.paper=paper;
	

	currentsvg.words = makewords(tokens);
	currentsvg.setAttribute("width",svgwi+extraspace);
	currentsvg.setAttribute("height",dependencyspace+shownfeatures.length*line);	
	drawalldeps();	
	
}



//////////////////////////////////////////////////////////////////////

Raphael.fn.pointer = function (x,y, size, angle) {
	var startpoint = x+","+(y+size);
	var lefttop = "0,0" +(-size/2)+","+(-size*1.5)+" "+(-size/2)+","+(-size*1.5);
	var righttop = (size/2)+"," +(size/2)+" "+(size/2)+"," +(size/2)+ " "+(size)+",0";
	var arrowPath = this.path("M"+ startpoint+"c"+lefttop+ "c"+righttop+ "z");
	arrowPath.rotate(angle);
	return arrowPath;
}



/*!
 * arborator script for dependency viewing 
 * version 1.0
 * http://arborator.ilpga.fr/
 *
 * Copyright 2010-2012, Kim Gerdes
 *
 * This program is free software:
 * Licensed under version 3 of the GNU Affero General Public License (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.gnu.org/licenses/agpl-3.0.html
 * 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. 
 *
 */

shownfeatures=["t","cat","lemma"];shownsentencefeatures=["markupIU"];functions=["dep","sub","pred","obj","obl","ad","root","para","junc","dep_inherited","sub_inherited","pred_inherited","obj_inherited","obl_inherited","ad_inherited","root_inherited","para_coord","para_hyper","para_intens","para_disfl","para_reform","para_dform","para_negot","attention","--ERASE--","--ERASE--"];funcDic={ad:{stroke:"#000000","stroke-width":"1","stroke-dasharray":""},"--ERASE--":{stroke:"#d00202","stroke-width":"1"},pred_inherited:{stroke:"#999999","stroke-width":"1","stroke-dasharray":". "},para_disfl:{stroke:"#1437AD","stroke-width":"1","stroke-dasharray":""},para_intens:{stroke:"#1437AD","stroke-width":"1","stroke-dasharray":""},sub_inherited:{stroke:"#999999","stroke-width":"1","stroke-dasharray":". "},sub:{stroke:"#000000","stroke-width":"1","stroke-dasharray":""},obl_inherited:{stroke:"#999999","stroke-width":"1","stroke-dasharray":". "},pred:{stroke:"#000000","stroke-width":"1","stroke-dasharray":""},para_negot:{stroke:"#1437AD","stroke-width":"1","stroke-dasharray":""},para_coord:{stroke:"#1437AD","stroke-width":"1","stroke-dasharray":""},dep_inherited:{stroke:"#999999","stroke-width":"1","stroke-dasharray":". "},para:{stroke:"#01949B","stroke-width":"1","stroke-dasharray":""},junc:{stroke:"#00C618","stroke-width":"1","stroke-dasharray":""},attention:{stroke:"#DD137B","stroke-width":"1","stroke-dasharray":"- "},para_dform:{stroke:"#1437AD","stroke-width":"1","stroke-dasharray":""},para_hyper:{stroke:"#1437AD","stroke-width":"1","stroke-dasharray":""},ad_inherited:{stroke:"#999999","stroke-width":"1","stroke-dasharray":". "},root_inherited:{stroke:"#999999","stroke-width":"1","stroke-dasharray":". "},para_reform:{stroke:"#1437AD","stroke-width":"1","stroke-dasharray":""},obl:{stroke:"#000000","stroke-width":"1","stroke-dasharray":""},obj:{stroke:"#000000","stroke-width":"1","stroke-dasharray":""},dep:{stroke:"#000000","stroke-width":"1","stroke-dasharray":""},obj_inherited:{stroke:"#999999","stroke-width":"1","stroke-dasharray":". "},root:{stroke:"#000000","stroke-width":"1","stroke-dasharray":""}};categoryindex="1";root="ROOT";catDic={Adv:{fill:"#69399d"},PP:{fill:"#69399d"},Qu:{fill:"#69399d"},D:{fill:"#69399d"},Cl:{fill:"#69399d"},I:{fill:"#69399d"},Pro:{fill:"#69399d"},J:{fill:"#69399d"},"Pre ":{fill:"#69399d"},N:{fill:"#69399d"},V:{fill:"#69399d"},CS:{fill:"#69399d"},X:{fill:"#69399d"},Adj:{fill:"#69399d"}};tab=8;line=25;dependencyspace=320;xoff=8;linedeps=6;pois=4;tokdepdist=15;funccurvedist=8;depminh=15;worddistancefactor=2;extraspace=50;defaultattris={font:'14px "Arial"',"text-anchor":"start"};attris={t:{font:'18px "Arial"',"text-anchor":"start",fill:"#000","stroke-width":"0"},cat:{font:'12px "Times"',"text-anchor":"start",fill:"#036"},lemma:{font:'14px "Times"',"text-anchor":"start",fill:"#036"},depline:{stroke:"#999","stroke-width":"1","stroke-dasharray":""},deptext:{font:'12px "Times"',"font-style":"italic",fill:"#999"},form:{"font-style":"italic"}};colored={dependency:null};attris[shownfeatures[categoryindex]]=attris.cat;colored[shownfeatures[categoryindex]]=null;

function Pnode(k,e){k=parseInt(k,10);this.index=k;this.gov={};if("gov" in e){for(var g in e.gov){this.gov[parseInt(g,10)]=e.gov[g]}}this.width=0;this.features=new Object();this.svgs=new Object();var m=dependencyspace;for(var g in shownfeatures){var h=shownfeatures[g];this.features[h]=e[h];if((e[h] instanceof Object)){var b=e[h][0][1][0];if(b=="ok"){c=e[h][0][0];var n=paper.text(currentx,m,c);n.attr(defaultattris);n.attr({fill:"#"+compacolors[b],title:"All annotators agree."})}else{c="";ti="";for(g in e[h]){c=c+" "+e[h][g][0];ti=ti+" "+e[h][g][0]+" ("+e[h][g][1]+")"}var n=paper.text(currentx,m,c);n.attr(defaultattris);n.attr({fill:"red",title:ti})}}else{var n=paper.text(currentx,m,e[h]);n.attr(defaultattris);if(h in e){n.attr(attris[h])}}n.attr("index",k);var d=n.getBBox().width;n.width=d;n.index=k;n.node.setAttribute("class",h);if(h in colored&&e[h] in catDic){n.attr(catDic[e[h]])}this.svgs[h]=n;if(d>this.width){this.width=d}m=m+line}for(var l in e){v=e[l];if(l in this.features||l=="gov"){continue}this.features[l]=v}svgwi=svgwi+this.width+tab;this.x=0;this.y=0;this.svgdep={}}drawsvgDep=function(k,b,w,e,u,d,h,g,r,m){var o=currentsvg.paper.set();var s=Math.abs(w-u)/2;var p=Math.max(e-s-worddistancefactor*Math.abs(k-b),-tokdepdist);p=Math.min(p,e-depminh);var l="M"+w+","+e+"C"+w+","+p+" "+u+","+p+" "+u+","+d;var x=currentsvg.paper.path(l).attr(attris.depline).attr({x:w,y:e});var n=currentsvg.paper.pointer(u,d,pois,0).attr(attris.depline);a=x.getPointAtLength(x.getTotalLength()/2);t=currentsvg.paper.text(a.x,a.y-funccurvedist-m*10,h);t.attr(attris.deptext);t.index=k;t.govind=b;if(b==0){var f=a.x+t.getBBox().width/2+funccurvedist/2;if(f+t.getBBox().width/2>svgwi){f=a.x-t.getBBox().width/2-funccurvedist/2}if(f-t.getBBox().width/2<0){f=a.x}t.attr("x",f)}if(r){x.attr({stroke:r});n.attr({stroke:r});t.attr({fill:r,title:g})}else{if("dependency" in colored&&h in funcDic){var q=funcDic[h];x.attr(q);n.attr({stroke:q.stroke});t.attr({fill:q.stroke})}}o.push(t);o.push(x);o.push(n);return o};drawDep=function(g,k,h,l){if(k==0){r=currentsvg.words[g];var d=r.svgs[shownfeatures[0]].attr("x")+r.svgs[shownfeatures[0]].width/2;var n=r.svgs[shownfeatures[0]].attr("y")-tokdepdist;var e=d;var p=0}else{var s=currentsvg.words[k];var r=currentsvg.words[g];if(s==null){delete r.gov[k];return}if(g<k){var e=s.svgs[shownfeatures[0]].attr("x")+s.svgs[shownfeatures[0]].width/2-xoff}else{var e=s.svgs[shownfeatures[0]].attr("x")+s.svgs[shownfeatures[0]].width/2+xoff}var d=r.svgs[shownfeatures[0]].attr("x")+r.svgs[shownfeatures[0]].width/2;var p=s.svgs[shownfeatures[0]].attr("y")+pois-tokdepdist;var n=r.svgs[shownfeatures[0]].attr("y")-tokdepdist-l*linedeps}if((h instanceof Object)){var b=h[0][1][0];if(b=="ok"){h=h[0][0];r.svgdep[k]=drawsvgDep(g,k,e,p,d,n,h,"All annotators agree.","#"+compacolors[b],0)}else{var o=false;for(i in h){var f=h[i];i=parseInt(i,10);for(j in f[1]){var q=f[1][j];j=parseInt(j,10);var m=drawsvgDep(g,k,e+j*2,p,parseInt(d,10)+j*2,n,f[0],f[0]+" ("+q+") ","#"+compacolors[q],i+j);if(o){o.push(m)}else{o=m}r.svgdep[k]=o}}}}else{r.svgdep[k]=drawsvgDep(g,k,e,p,d,n,h,"click to change",false,0)}};drawalldeps=function(){for(var b in currentsvg.words){var e=currentsvg.words[b];for(var b in e.svgdep){e.svgdep[b].remove();delete e.svgdep[b]}e.svgdep={};var d=0;for(var b in e.gov){drawDep(e.index,b,e.gov[b],d);d+=1}}};drawalldeps2=function(){var b=new Object();for(var d in currentsvg.words){var f=currentsvg.words[d];for(var d in f.gov){d=parseInt(d,10);if(d!=0){dis=Math.abs(f.index-d);if(dis in b){b[dis].push([f.index,d])}else{b[dis]=[[f.index,d]]}}}}for(var d in currentsvg.words){var f=currentsvg.words[d];for(var d in f.svgdep){f.svgdep[d].remove();delete f.svgdep[d]}f.svgdep={};var e=0;for(var d in f.gov){drawDep(f.index,d,f.gov[d],e);e+=1}}};makewords=function(e){var f=new Object();svgwi=0;currentx=tab;for(var b in e){var d=new Pnode(b,e[b]);f[b]=d;currentx=currentx+d.width+tab}return f};draw=function(b,d){paper=Raphael(b,window.innerWidth-100,100);currentsvg=paper.canvas;currentsvg.paper=paper;currentsvg.words=makewords(d);currentsvg.setAttribute("width",svgwi+extraspace);currentsvg.setAttribute("height",dependencyspace+shownfeatures.length*line);drawalldeps()};Raphael.fn.pointer=function(d,l,e,k){var b=d+","+(l+e);var f="0,0"+(-e/2)+","+(-e*1.5)+" "+(-e/2)+","+(-e*1.5);var g=(e/2)+","+(e/2)+" "+(e/2)+","+(e/2)+" "+(e)+",0";var h=this.path("M"+b+"c"+f+"c"+g+"z");h.rotate(k);return h};
/*
colpick Color Picker
Copyright 2013 Jose Vargas. Licensed under GPL license. Based on Stefan Petre's Color Picker www.eyecon.ro, dual licensed under the MIT and GPL licenses

For usage and examples: colpick.com/plugin
 */

(function ($) {
	var colpick = function () {
		var
			tpl = '<div class="colpick"><div class="colpick_color"><div class="colpick_color_overlay1"><div class="colpick_color_overlay2"><div class="colpick_selector_outer"><div class="colpick_selector_inner"></div></div></div></div></div><div class="colpick_hue"><div class="colpick_hue_arrs"><div class="colpick_hue_larr"></div><div class="colpick_hue_rarr"></div></div></div><div class="colpick_new_color"></div><div class="colpick_current_color"></div><div class="colpick_hex_field"><div class="colpick_field_letter">#</div><input type="text" maxlength="6" size="6" /></div><div class="colpick_rgb_r colpick_field"><div class="colpick_field_letter">R</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_rgb_g colpick_field"><div class="colpick_field_letter">G</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_rgb_b colpick_field"><div class="colpick_field_letter">B</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_hsb_h colpick_field"><div class="colpick_field_letter">H</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_hsb_s colpick_field"><div class="colpick_field_letter">S</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_hsb_b colpick_field"><div class="colpick_field_letter">B</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_submit"></div></div>',
			defaults = {
				showEvent: 'click',
				onShow: function () {},
				onBeforeShow: function(){},
				onHide: function () {},
				onChange: function () {},
				onSubmit: function () {},
				colorScheme: 'light',
				color: '3289c7',
				livePreview: true,
				flat: false,
				layout: 'full',
				submit: 1,
				submitText: 'OK',
				height: 156
			},
			//Fill the inputs of the plugin
			fillRGBFields = function  (hsb, cal) {
				var rgb = hsbToRgb(hsb);
				$(cal).data('colpick').fields
					.eq(1).val(rgb.r).end()
					.eq(2).val(rgb.g).end()
					.eq(3).val(rgb.b).end();
			},
			fillHSBFields = function  (hsb, cal) {
				$(cal).data('colpick').fields
					.eq(4).val(Math.round(hsb.h)).end()
					.eq(5).val(Math.round(hsb.s)).end()
					.eq(6).val(Math.round(hsb.b)).end();
			},
			fillHexFields = function (hsb, cal) {
				$(cal).data('colpick').fields.eq(0).val(hsbToHex(hsb));
			},
			//Set the round selector position
			setSelector = function (hsb, cal) {
				$(cal).data('colpick').selector.css('backgroundColor', '#' + hsbToHex({h: hsb.h, s: 100, b: 100}));
				$(cal).data('colpick').selectorIndic.css({
					left: parseInt($(cal).data('colpick').height * hsb.s/100, 10),
					top: parseInt($(cal).data('colpick').height * (100-hsb.b)/100, 10)
				});
			},
			//Set the hue selector position
			setHue = function (hsb, cal) {
				$(cal).data('colpick').hue.css('top', parseInt($(cal).data('colpick').height - $(cal).data('colpick').height * hsb.h/360, 10));
			},
			//Set current and new colors
			setCurrentColor = function (hsb, cal) {
				$(cal).data('colpick').currentColor.css('backgroundColor', '#' + hsbToHex(hsb));
			},
			setNewColor = function (hsb, cal) {
				$(cal).data('colpick').newColor.css('backgroundColor', '#' + hsbToHex(hsb));
			},
			//Called when the new color is changed
			change = function (ev) {
				var cal = $(this).parent().parent(), col;
				if (this.parentNode.className.indexOf('_hex') > 0) {
					cal.data('colpick').color = col = hexToHsb(fixHex(this.value));
					fillRGBFields(col, cal.get(0));
					fillHSBFields(col, cal.get(0));
				} else if (this.parentNode.className.indexOf('_hsb') > 0) {
					cal.data('colpick').color = col = fixHSB({
						h: parseInt(cal.data('colpick').fields.eq(4).val(), 10),
						s: parseInt(cal.data('colpick').fields.eq(5).val(), 10),
						b: parseInt(cal.data('colpick').fields.eq(6).val(), 10)
					});
					fillRGBFields(col, cal.get(0));
					fillHexFields(col, cal.get(0));
				} else {
					cal.data('colpick').color = col = rgbToHsb(fixRGB({
						r: parseInt(cal.data('colpick').fields.eq(1).val(), 10),
						g: parseInt(cal.data('colpick').fields.eq(2).val(), 10),
						b: parseInt(cal.data('colpick').fields.eq(3).val(), 10)
					}));
					fillHexFields(col, cal.get(0));
					fillHSBFields(col, cal.get(0));
				}
				setSelector(col, cal.get(0));
				setHue(col, cal.get(0));
				setNewColor(col, cal.get(0));
				cal.data('colpick').onChange.apply(cal.parent(), [col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el, 0]);
			},
			//Change style on blur and on focus of inputs
			blur = function (ev) {
				$(this).parent().removeClass('colpick_focus');
			},
			focus = function () {
				$(this).parent().parent().data('colpick').fields.parent().removeClass('colpick_focus');
				$(this).parent().addClass('colpick_focus');
			},
			//Increment/decrement arrows functions
			downIncrement = function (ev) {
				ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
				var field = $(this).parent().find('input').focus();
				var current = {
					el: $(this).parent().addClass('colpick_slider'),
					max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255),
					y: ev.pageY,
					field: field,
					val: parseInt(field.val(), 10),
					preview: $(this).parent().parent().data('colpick').livePreview
				};
				$(document).mouseup(current, upIncrement);
				$(document).mousemove(current, moveIncrement);
			},
			moveIncrement = function (ev) {
				ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val - ev.pageY + ev.data.y, 10))));
				if (ev.data.preview) {
					change.apply(ev.data.field.get(0), [true]);
				}
				return false;
			},
			upIncrement = function (ev) {
				change.apply(ev.data.field.get(0), [true]);
				ev.data.el.removeClass('colpick_slider').find('input').focus();
				$(document).off('mouseup', upIncrement);
				$(document).off('mousemove', moveIncrement);
				return false;
			},
			//Hue slider functions
			downHue = function (ev) {
				ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
				var current = {
					cal: $(this).parent(),
					y: $(this).offset().top
				};
				$(document).on('mouseup touchend',current,upHue);
				$(document).on('mousemove touchmove',current,moveHue);
				
				var pageY = ((ev.type == 'touchstart') ? ev.originalEvent.changedTouches[0].pageY : ev.pageY );
				change.apply(
					current.cal.data('colpick')
					.fields.eq(4).val(parseInt(360*(current.cal.data('colpick').height - (pageY - current.y))/current.cal.data('colpick').height, 10))
						.get(0),
					[current.cal.data('colpick').livePreview]
				);
				return false;
			},
			moveHue = function (ev) {
				var pageY = ((ev.type == 'touchmove') ? ev.originalEvent.changedTouches[0].pageY : ev.pageY );
				change.apply(
					ev.data.cal.data('colpick')
					.fields.eq(4).val(parseInt(360*(ev.data.cal.data('colpick').height - Math.max(0,Math.min(ev.data.cal.data('colpick').height,(pageY - ev.data.y))))/ev.data.cal.data('colpick').height, 10))
						.get(0),
					[ev.data.preview]
				);
				return false;
			},
			upHue = function (ev) {
				fillRGBFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
				fillHexFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
				$(document).off('mouseup touchend',upHue);
				$(document).off('mousemove touchmove',moveHue);
				return false;
			},
			//Color selector functions
			downSelector = function (ev) {
				ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
				var current = {
					cal: $(this).parent(),
					pos: $(this).offset()
				};
				current.preview = current.cal.data('colpick').livePreview;
				
				$(document).on('mouseup touchend',current,upSelector);
				$(document).on('mousemove touchmove',current,moveSelector);

				var payeX,pageY;
				if(ev.type == 'touchstart') {
					pageX = ev.originalEvent.changedTouches[0].pageX,
					pageY = ev.originalEvent.changedTouches[0].pageY;
				} else {
					pageX = ev.pageX;
					pageY = ev.pageY;
				}

				change.apply(
					current.cal.data('colpick').fields
					.eq(6).val(parseInt(100*(current.cal.data('colpick').height - (pageY - current.pos.top))/current.cal.data('colpick').height, 10)).end()
					.eq(5).val(parseInt(100*(pageX - current.pos.left)/current.cal.data('colpick').height, 10))
					.get(0),
					[current.preview]
				);
				return false;
			},
			moveSelector = function (ev) {
				var payeX,pageY;
				if(ev.type == 'touchmove') {
					pageX = ev.originalEvent.changedTouches[0].pageX,
					pageY = ev.originalEvent.changedTouches[0].pageY;
				} else {
					pageX = ev.pageX;
					pageY = ev.pageY;
				}

				change.apply(
					ev.data.cal.data('colpick').fields
					.eq(6).val(parseInt(100*(ev.data.cal.data('colpick').height - Math.max(0,Math.min(ev.data.cal.data('colpick').height,(pageY - ev.data.pos.top))))/ev.data.cal.data('colpick').height, 10)).end()
					.eq(5).val(parseInt(100*(Math.max(0,Math.min(ev.data.cal.data('colpick').height,(pageX - ev.data.pos.left))))/ev.data.cal.data('colpick').height, 10))
					.get(0),
					[ev.data.preview]
				);
				return false;
			},
			upSelector = function (ev) {
				fillRGBFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
				fillHexFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
				$(document).off('mouseup touchend',upSelector);
				$(document).off('mousemove touchmove',moveSelector);
				return false;
			},
			//Submit button
			clickSubmit = function (ev) {
				var cal = $(this).parent();
				var col = cal.data('colpick').color;
				cal.data('colpick').origColor = col;
				setCurrentColor(col, cal.get(0));
				cal.data('colpick').onSubmit(col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el);
			},
			//Show/hide the color picker
			show = function (ev) {
				// Prevent the trigger of any direct parent
				ev.stopPropagation();
				var cal = $('#' + $(this).data('colpickId'));
				cal.data('colpick').onBeforeShow.apply(this, [cal.get(0)]);
				var pos = $(this).offset();
				var top = pos.top + this.offsetHeight;
				var left = pos.left;
				var viewPort = getViewport();
				var calW = cal.width();
				if (left + calW > viewPort.l + viewPort.w) {
					left -= calW;
				}
				cal.css({left: left + 'px', top: top + 'px'});
				if (cal.data('colpick').onShow.apply(this, [cal.get(0)]) != false) {
					cal.show();
				}
				//Hide when user clicks outside
				$('html').mousedown({cal:cal}, hide);
				cal.mousedown(function(ev){ev.stopPropagation();})
			},
			hide = function (ev) {
				if (ev.data.cal.data('colpick').onHide.apply(this, [ev.data.cal.get(0)]) != false) {
					ev.data.cal.hide();
				}
				$('html').off('mousedown', hide);
			},
			getViewport = function () {
				var m = document.compatMode == 'CSS1Compat';
				return {
					l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft),
					w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth)
				};
			},
			//Fix the values if the user enters a negative or high value
			fixHSB = function (hsb) {
				return {
					h: Math.min(360, Math.max(0, hsb.h)),
					s: Math.min(100, Math.max(0, hsb.s)),
					b: Math.min(100, Math.max(0, hsb.b))
				};
			}, 
			fixRGB = function (rgb) {
				return {
					r: Math.min(255, Math.max(0, rgb.r)),
					g: Math.min(255, Math.max(0, rgb.g)),
					b: Math.min(255, Math.max(0, rgb.b))
				};
			},
			fixHex = function (hex) {
				var len = 6 - hex.length;
				if (len > 0) {
					var o = [];
					for (var i=0; i<len; i++) {
						o.push('0');
					}
					o.push(hex);
					hex = o.join('');
				}
				return hex;
			},
			restoreOriginal = function () {
				var cal = $(this).parent();
				var col = cal.data('colpick').origColor;
				cal.data('colpick').color = col;
				fillRGBFields(col, cal.get(0));
				fillHexFields(col, cal.get(0));
				fillHSBFields(col, cal.get(0));
				setSelector(col, cal.get(0));
				setHue(col, cal.get(0));
				setNewColor(col, cal.get(0));
			};
		return {
			init: function (opt) {
				opt = $.extend({}, defaults, opt||{});
				//Set color
				if (typeof opt.color == 'string') {
					opt.color = hexToHsb(opt.color);
				} else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) {
					opt.color = rgbToHsb(opt.color);
				} else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) {
					opt.color = fixHSB(opt.color);
				} else {
					return this;
				}
				
				//For each selected DOM element
				return this.each(function () {
					//If the element does not have an ID
					if (!$(this).data('colpickId')) {
						var options = $.extend({}, opt);
						options.origColor = opt.color;
						//Generate and assign a random ID
						var id = 'collorpicker_' + parseInt(Math.random() * 1000);
						$(this).data('colpickId', id);
						//Set the tpl's ID and get the HTML
						var cal = $(tpl).attr('id', id);
						//Add class according to layout
						cal.addClass('colpick_'+options.layout+(options.submit?'':' colpick_'+options.layout+'_ns'));
						//Add class if the color scheme is not default
						if(options.colorScheme != 'light') {
							cal.addClass('colpick_'+options.colorScheme);
						}
						//Setup submit button
						cal.find('div.colpick_submit').html(options.submitText).click(clickSubmit);
						//Setup input fields
						options.fields = cal.find('input').change(change).blur(blur).focus(focus);
						cal.find('div.colpick_field_arrs').mousedown(downIncrement).end().find('div.colpick_current_color').click(restoreOriginal);
						//Setup hue selector
						options.selector = cal.find('div.colpick_color').on('mousedown touchstart',downSelector);
						options.selectorIndic = options.selector.find('div.colpick_selector_outer');
						//Store parts of the plugin
						options.el = this;
						options.hue = cal.find('div.colpick_hue_arrs');
						huebar = options.hue.parent();
						//Paint the hue bar
						var UA = navigator.userAgent.toLowerCase();
						var isIE = navigator.appName === 'Microsoft Internet Explorer';
						var IEver = isIE ? parseFloat( UA.match( /msie ([0-9]{1,}[\.0-9]{0,})/ )[1] ) : 0;
						var ngIE = ( isIE && IEver < 10 );
						var stops = ['#ff0000','#ff0080','#ff00ff','#8000ff','#0000ff','#0080ff','#00ffff','#00ff80','#00ff00','#80ff00','#ffff00','#ff8000','#ff0000'];
						if(ngIE) {
							var i, div;
							for(i=0; i<=11; i++) {
								div = $('<div></div>').attr('style','height:8.333333%; filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='+stops[i]+', endColorstr='+stops[i+1]+'); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='+stops[i]+', endColorstr='+stops[i+1]+')";');
								huebar.append(div);
							}
						} else {
							stopList = stops.join(',');
							huebar.attr('style','background:-webkit-linear-gradient(top,'+stopList+'); background: -o-linear-gradient(top,'+stopList+'); background: -ms-linear-gradient(top,'+stopList+'); background:-moz-linear-gradient(top,'+stopList+'); -webkit-linear-gradient(top,'+stopList+'); background:linear-gradient(to bottom,'+stopList+'); ');
						}
						cal.find('div.colpick_hue').on('mousedown touchstart',downHue);
						options.newColor = cal.find('div.colpick_new_color');
						options.currentColor = cal.find('div.colpick_current_color');
						//Store options and fill with default color
						cal.data('colpick', options);
						fillRGBFields(options.color, cal.get(0));
						fillHSBFields(options.color, cal.get(0));
						fillHexFields(options.color, cal.get(0));
						setHue(options.color, cal.get(0));
						setSelector(options.color, cal.get(0));
						setCurrentColor(options.color, cal.get(0));
						setNewColor(options.color, cal.get(0));
						//Append to body if flat=false, else show in place
						if (options.flat) {
							cal.appendTo(this).show();
							cal.css({
								position: 'relative',
								display: 'block'
							});
						} else {
							cal.appendTo(document.body);
							$(this).on(options.showEvent, show);
							cal.css({
								position:'absolute'
							});
						}
					}
				});
			},
			//Shows the picker
			showPicker: function() {
				return this.each( function () {
					if ($(this).data('colpickId')) {
						show.apply(this);
					}
				});
			},
			//Hides the picker
			hidePicker: function() {
				return this.each( function () {
					if ($(this).data('colpickId')) {
						$('#' + $(this).data('colpickId')).hide();
					}
				});
			},
			//Sets a color as new and current (default)
			setColor: function(col, setCurrent) {
				setCurrent = (typeof setCurrent === "undefined") ? 1 : setCurrent;
				if (typeof col == 'string') {
					col = hexToHsb(col);
				} else if (col.r != undefined && col.g != undefined && col.b != undefined) {
					col = rgbToHsb(col);
				} else if (col.h != undefined && col.s != undefined && col.b != undefined) {
					col = fixHSB(col);
				} else {
					return this;
				}
				return this.each(function(){
					if ($(this).data('colpickId')) {
						var cal = $('#' + $(this).data('colpickId'));
						cal.data('colpick').color = col;
						cal.data('colpick').origColor = col;
						fillRGBFields(col, cal.get(0));
						fillHSBFields(col, cal.get(0));
						fillHexFields(col, cal.get(0));
						setHue(col, cal.get(0));
						setSelector(col, cal.get(0));
						
						setNewColor(col, cal.get(0));
						cal.data('colpick').onChange.apply(cal.parent(), [col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el, 1]);
						if(setCurrent) {
							setCurrentColor(col, cal.get(0));
						}
					}
				});
			}
		};
	}();
	//Color space convertions
	var hexToRgb = function (hex) {
		var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
		return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)};
	};
	var hexToHsb = function (hex) {
		return rgbToHsb(hexToRgb(hex));
	};
	var rgbToHsb = function (rgb) {
		var hsb = {h: 0, s: 0, b: 0};
		var min = Math.min(rgb.r, rgb.g, rgb.b);
		var max = Math.max(rgb.r, rgb.g, rgb.b);
		var delta = max - min;
		hsb.b = max;
		hsb.s = max != 0 ? 255 * delta / max : 0;
		if (hsb.s != 0) {
			if (rgb.r == max) hsb.h = (rgb.g - rgb.b) / delta;
			else if (rgb.g == max) hsb.h = 2 + (rgb.b - rgb.r) / delta;
			else hsb.h = 4 + (rgb.r - rgb.g) / delta;
		} else hsb.h = -1;
		hsb.h *= 60;
		if (hsb.h < 0) hsb.h += 360;
		hsb.s *= 100/255;
		hsb.b *= 100/255;
		return hsb;
	};
	var hsbToRgb = function (hsb) {
		var rgb = {};
		var h = hsb.h;
		var s = hsb.s*255/100;
		var v = hsb.b*255/100;
		if(s == 0) {
			rgb.r = rgb.g = rgb.b = v;
		} else {
			var t1 = v;
			var t2 = (255-s)*v/255;
			var t3 = (t1-t2)*(h%60)/60;
			if(h==360) h = 0;
			if(h<60) {rgb.r=t1;	rgb.b=t2; rgb.g=t2+t3}
			else if(h<120) {rgb.g=t1; rgb.b=t2;	rgb.r=t1-t3}
			else if(h<180) {rgb.g=t1; rgb.r=t2;	rgb.b=t2+t3}
			else if(h<240) {rgb.b=t1; rgb.r=t2;	rgb.g=t1-t3}
			else if(h<300) {rgb.b=t1; rgb.g=t2;	rgb.r=t2+t3}
			else if(h<360) {rgb.r=t1; rgb.g=t2;	rgb.b=t1-t3}
			else {rgb.r=0; rgb.g=0;	rgb.b=0}
		}
		return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)};
	};
	var rgbToHex = function (rgb) {
		var hex = [
			rgb.r.toString(16),
			rgb.g.toString(16),
			rgb.b.toString(16)
		];
		$.each(hex, function (nr, val) {
			if (val.length == 1) {
				hex[nr] = '0' + val;
			}
		});
		return hex.join('');
	};
	var hsbToHex = function (hsb) {
		return rgbToHex(hsbToRgb(hsb));
	};
	$.fn.extend({
		colpick: colpick.init,
		colpickHide: colpick.hidePicker,
		colpickShow: colpick.showPicker,
		colpickSetColor: colpick.setColor
	});
	$.extend({
		colpick:{ 
			rgbToHex: rgbToHex,
			rgbToHsb: rgbToHsb,
			hsbToHex: hsbToHex,
			hsbToRgb: hsbToRgb,
			hexToHsb: hexToHsb,
			hexToRgb: hexToRgb
		}
	});
})(jQuery);
ï»¿/*
* jHtmlArea 0.7.0 - WYSIWYG Html Editor jQuery Plugin
* Copyright (c) 2009 Chris Pietschmann
* http://jhtmlarea.codeplex.com
* Licensed under the Microsoft Reciprocal License (Ms-RL)
* http://jhtmlarea.codeplex.com/license
*/
(function($) {
    $.fn.htmlarea = function(opts) {
        if (opts && typeof (opts) === "string") {
            var args = [];
            for (var i = 1; i < arguments.length; i++) { args.push(arguments[i]); }
            var htmlarea = jHtmlArea(this[0]);
            var f = htmlarea[opts];
            if (f) { return f.apply(htmlarea, args); }
        }
        return this.each(function() { jHtmlArea(this, opts); });
    };
    var jHtmlArea = window.jHtmlArea = function(elem, options) {
        if (elem.jquery) {
            return jHtmlArea(elem[0]);
        }
        if (elem.jhtmlareaObject) {
            return elem.jhtmlareaObject;
        } else {
            return new jHtmlArea.fn.init(elem, options);
        }
    };
    jHtmlArea.fn = jHtmlArea.prototype = {

        // The current version of jHtmlArea being used
        jhtmlarea: "0.7.0",

        init: function(elem, options) {
            if (elem.nodeName.toLowerCase() === "textarea") {
                var opts = $.extend({}, jHtmlArea.defaultOptions, options);
                elem.jhtmlareaObject = this;

                var textarea = this.textarea = $(elem);
                var container = this.container = $("<div/>").addClass("jHtmlArea").width(textarea.width()).insertAfter(textarea);

                var toolbar = this.toolbar = $("<div/>").addClass("ToolBar").appendTo(container);
                priv.initToolBar.call(this, opts);

                var iframe = this.iframe = $("<iframe/>").height(textarea.height());
                iframe.width(textarea.width() - ($.browser.msie ? 0 : 4));
                var htmlarea = this.htmlarea = $("<div/>").append(iframe);

                container.append(htmlarea).append(textarea.hide());

                priv.initEditor.call(this, opts);
                priv.attachEditorEvents.call(this);

                // Fix total height to match TextArea
                iframe.height(iframe.height() - toolbar.height());
                toolbar.width(textarea.width() - 2);

                if (opts.loaded) { opts.loaded.call(this); }
            }
        },
        dispose: function() {
            this.textarea.show().insertAfter(this.container);
            this.container.remove();
            this.textarea[0].jhtmlareaObject = null;
        },
        execCommand: function(a, b, c) {
            this.iframe[0].contentWindow.focus();
            this.editor.execCommand(a, b || false, c || null);
            this.updateTextArea();
        },
        ec: function(a, b, c) {
            this.execCommand(a, b, c);
        },
        queryCommandValue: function(a) {
            this.iframe[0].contentWindow.focus();
            return this.editor.queryCommandValue(a);
        },
        qc: function(a) {
            return this.queryCommandValue(a);
        },
        getSelectedHTML: function() {
            if ($.browser.msie) {
                return this.getRange().htmlText;
            } else {
                var elem = this.getRange().cloneContents();
                return $("<p/>").append($(elem)).html();
            }
        },
        getSelection: function() {
            if ($.browser.msie) {
                //return (this.editor.parentWindow.getSelection) ? this.editor.parentWindow.getSelection() : this.editor.selection;
                return this.editor.selection;
            } else {
                return this.iframe[0].contentDocument.defaultView.getSelection();
            }
        },
        getRange: function() {
            var s = this.getSelection();
            if (!s) { return null; }
            //return (s.rangeCount > 0) ? s.getRangeAt(0) : s.createRange();
            return (s.getRangeAt) ? s.getRangeAt(0) : s.createRange();
        },
        html: function(v) {
            if (v) {
                this.pastHTML(v);
            } else {
                return toHtmlString();
            }
        },
        pasteHTML: function(html) {
            this.iframe[0].contentWindow.focus();
            var r = this.getRange();
            if ($.browser.msie) {
                r.pasteHTML(html);
            } else if ($.browser.mozilla) {
                r.deleteContents();
                r.insertNode($((html.indexOf("<") != 0) ? $("<span/>").append(html) : html)[0]);
            } else { // Safari
                r.deleteContents();
                r.insertNode($(this.iframe[0].contentWindow.document.createElement("span")).append($((html.indexOf("<") != 0) ? "<span>" + html + "</span>" : html))[0]);
            }
            r.collapse(false);
            r.select();
        },
        cut: function() {
            this.ec("cut");
        },
        copy: function() {
            this.ec("copy");
        },
        paste: function() {
            this.ec("paste");
        },
        bold: function() { this.ec("bold"); },
        italic: function() { this.ec("italic"); },
        underline: function() { this.ec("underline"); },
        strikeThrough: function() { this.ec("strikethrough"); },
        image: function(url) {
            if ($.browser.msie && !url) {
                this.ec("insertImage", true);
            } else {
                this.ec("insertImage", false, (url || prompt("Image URL:", "http://")));
            }
        },
        removeFormat: function() {
            this.ec("removeFormat", false, []);
            this.unlink();
        },
        link: function() {
            if ($.browser.msie) {
                this.ec("createLink", true);
            } else {
                this.ec("createLink", false, prompt("Link URL:", "http://"));
            }
        },
        unlink: function() { this.ec("unlink", false, []); },
        orderedList: function() { this.ec("insertorderedlist"); },
        unorderedList: function() { this.ec("insertunorderedlist"); },
        superscript: function() { this.ec("superscript"); },
        subscript: function() { this.ec("subscript"); },

        p: function() {
            this.formatBlock("<p>");
        },
        h1: function() {
            this.heading(1);
        },
        h2: function() {
            this.heading(2);
        },
        h3: function() {
            this.heading(3);
        },
        h4: function() {
            this.heading(4);
        },
        h5: function() {
            this.heading(5);
        },
        h6: function() {
            this.heading(6);
        },
        heading: function(h) {
            this.formatBlock($.browser.msie ? "Heading " + h : "h" + h);
        },

        indent: function() {
            this.ec("indent");
        },
        outdent: function() {
            this.ec("outdent");
        },

        insertHorizontalRule: function() {
            this.ec("insertHorizontalRule", false, "ht");
        },

        justifyLeft: function() {
            this.ec("justifyLeft");
        },
        justifyCenter: function() {
            this.ec("justifyCenter");
        },
        justifyRight: function() {
            this.ec("justifyRight");
        },

        increaseFontSize: function() {
            if ($.browser.msie) {
                this.ec("fontSize", false, this.qc("fontSize") + 1);
            } else if ($.browser.safari) {
                this.getRange().surroundContents($(this.iframe[0].contentWindow.document.createElement("span")).css("font-size", "larger")[0]);
            } else {
                this.ec("increaseFontSize", false, "big");
            }
        },
        decreaseFontSize: function() {
            if ($.browser.msie) {
                this.ec("fontSize", false, this.qc("fontSize") - 1);
            } else if ($.browser.safari) {
                this.getRange().surroundContents($(this.iframe[0].contentWindow.document.createElement("span")).css("font-size", "smaller")[0]);
            } else {
                this.ec("decreaseFontSize", false, "small");
            }
        },

        forecolor: function(c) {
            this.ec("foreColor", false, c || prompt("Enter HTML Color:", "#"));
        },

        formatBlock: function(v) {
            this.ec("formatblock", false, v || null);
        },

        showHTMLView: function() {
            this.updateTextArea();
            this.textarea.show();
            this.htmlarea.hide();
            $("ul li:not(li:has(a.html))", this.toolbar).hide();
            $("ul:not(:has(:visible))", this.toolbar).hide();
            $("ul li a.html", this.toolbar).addClass("highlighted");
        },
        hideHTMLView: function() {
            this.updateHtmlArea();
            this.textarea.hide();
            this.htmlarea.show();
            $("ul", this.toolbar).show();
            $("ul li", this.toolbar).show().find("a.html").removeClass("highlighted");
        },
        toggleHTMLView: function() {
            (this.textarea.is(":hidden")) ? this.showHTMLView() : this.hideHTMLView();
        },

        toHtmlString: function() {
            return this.editor.body.innerHTML;
        },
        toString: function() {
            return this.editor.body.innerText;
        },

        updateTextArea: function() {
            this.textarea.val(this.toHtmlString());
        },
        updateHtmlArea: function() {
            this.editor.body.innerHTML = this.textarea.val();
        }
    };
    jHtmlArea.fn.init.prototype = jHtmlArea.fn;

    jHtmlArea.defaultOptions = {
        toolbar: [
        ["html"], ["bold", "italic", "underline", "strikethrough", "|", "subscript", "superscript"],
        ["increasefontsize", "decreasefontsize"],
        ["orderedlist", "unorderedlist"],
        ["indent", "outdent"],
        ["justifyleft", "justifycenter", "justifyright"],
        ["link", "unlink", "image", "horizontalrule"],
        ["p", "h1", "h2", "h3", "h4", "h5", "h6"],
        ["cut", "copy", "paste"]
    ],
        css: null,
        toolbarText: {
            bold: "Bold", italic: "Italic", underline: "Underline", strikethrough: "Strike-Through",
            cut: "Cut", copy: "Copy", paste: "Paste",
            h1: "Heading 1", h2: "Heading 2", h3: "Heading 3", h4: "Heading 4", h5: "Heading 5", h6: "Heading 6", p: "Paragraph",
            indent: "Indent", outdent: "Outdent", horizontalrule: "Insert Horizontal Rule",
            justifyleft: "Left Justify", justifycenter: "Center Justify", justifyright: "Right Justify",
            increasefontsize: "Increase Font Size", decreasefontsize: "Decrease Font Size", forecolor: "Text Color",
            link: "Insert Link", unlink: "Remove Link", image: "Insert Image",
            orderedlist: "Insert Ordered List", unorderedlist: "Insert Unordered List",
            subscript: "Subscript", superscript: "Superscript",
            html: "Show/Hide HTML Source View"
        }
    };
    var priv = {
        toolbarButtons: {
            strikethrough: "strikeThrough", orderedlist: "orderedList", unorderedlist: "unorderedList",
            horizontalrule: "insertHorizontalRule",
            justifyleft: "justifyLeft", justifycenter: "justifyCenter", justifyright: "justifyRight",
            increasefontsize: "increaseFontSize", decreasefontsize: "decreaseFontSize",
            html: function(btn) {
                this.toggleHTMLView();
            }
        },
        initEditor: function(options) {
            var edit = this.editor = this.iframe[0].contentWindow.document;
            edit.designMode = 'on';
            edit.open();
            edit.write(this.textarea.val());
            edit.close();
            if (options.css) {
                var e = edit.createElement('link'); e.rel = 'stylesheet'; e.type = 'text/css'; e.href = options.css; edit.getElementsByTagName('head')[0].appendChild(e);
            }
        },
        initToolBar: function(options) {
            var that = this;

            var menuItem = function(className, altText, action) {
                return $("<li/>").append($("<a href='javascript:void(0);'/>").addClass(className).attr("title", altText).click(function() { action.call(that, $(this)); }));
            };

            function addButtons(arr) {
                var ul = $("<ul/>").appendTo(that.toolbar);
                for (var i = 0; i < arr.length; i++) {
                    var e = arr[i];
                    if ((typeof (e)).toLowerCase() === "string") {
                        if (e === "|") {
                            ul.append($('<li class="separator"/>'));
                        } else {
                            var f = (function(e) {
                                // If button name exists in priv.toolbarButtons then call the "method" defined there, otherwise call the method with the same name
                                var m = priv.toolbarButtons[e] || e;
                                if ((typeof (m)).toLowerCase() === "function") {
                                    return function(btn) { m.call(this, btn); };
                                } else {
                                    return function() { this[m](); this.editor.body.focus(); };
                                }
                            })(e.toLowerCase());
                            var t = options.toolbarText[e.toLowerCase()];
                            ul.append(menuItem(e.toLowerCase(), t || e, f));
                        }
                    } else {
                        ul.append(menuItem(e.css, e.text, e.action));
                    }
                }
            };
            if (options.toolbar.length !== 0 && priv.isArray(options.toolbar[0])) {
                for (var i = 0; i < options.toolbar.length; i++) {
                    addButtons(options.toolbar[i]);
                }
            } else {
                addButtons(options.toolbar);
            }
        },
        attachEditorEvents: function() {
            var t = this;

            var fnHA = function() {
                t.updateHtmlArea();
            };

            this.textarea.click(fnHA).
                keyup(fnHA).
                keydown(fnHA).
                mousedown(fnHA).
                blur(fnHA);



            var fnTA = function() {
                t.updateTextArea();
            };

            $(this.editor.body).click(fnTA).
                keyup(fnTA).
                keydown(fnTA).
                mousedown(fnTA).
                blur(fnTA);

            $('form').submit(function() { t.toggleHTMLView(); t.toggleHTMLView(); });
            //$(this.textarea[0].form).submit(function() { //this.textarea.closest("form").submit(function() {


            // Fix for ASP.NET Postback Model
            if (window.__doPostBack) {
                var old__doPostBack = __doPostBack;
                window.__doPostBack = function() {
                    if (t) {
                        if (t.toggleHTMLView) {
                            t.toggleHTMLView();
                            t.toggleHTMLView();
                        }
                    }
                    return old__doPostBack.apply(window, arguments);
                };
            }
            
        },
        isArray: function(v) {
            return v && typeof v === 'object' && typeof v.length === 'number' && typeof v.splice === 'function' && !(v.propertyIsEnumerable('length'));
        }
    };
})(jQuery);
/**
 * jQuery Lined Textarea Plugin 
 *   http://alan.blog-city.com/jquerylinedtextarea.htm
 *
 * Copyright (c) 2010 Alan Williamson
 * 
 * Version: 
 *    $Id: jquery-linedtextarea.js 464 2010-01-08 10:36:33Z alan $
 *
 * Released under the MIT License:
 *    http://www.opensource.org/licenses/mit-license.php
 * 
 * Usage:
 *   Displays a line number count column to the left of the textarea
 *   
 *   Class up your textarea with a given class, or target it directly
 *   with JQuery Selectors
 *   
 *   $(".lined").linedtextarea({
 *   	selectedLine: 10,
 *    selectedClass: 'lineselect'
 *   });
 *
 * History:
 *   - 2010.01.08: Fixed a Google Chrome layout problem
 *   - 2010.01.07: Refactored code for speed/readability; Fixed horizontal sizing
 *   - 2010.01.06: Initial Release
 *
 */
(function($) {

	$.fn.linedtextarea = function(options) {
		
		// Get the Options
		var opts = $.extend({}, $.fn.linedtextarea.defaults, options);
		
		
		/*
		 * Helper function to make sure the line numbers are always
		 * kept up to the current system
		 */
		var fillOutLines = function(codeLines, h, lineNo){
			while ( (codeLines.height() - h ) <= 0 ){
				if ( lineNo == opts.selectedLine )
					codeLines.append("<div class='lineno lineselect'>" + lineNo + "</div>");
				else
					codeLines.append("<div class='lineno'>" + lineNo + "</div>");
				
				lineNo++;
			}
			return lineNo;
		};
		
		
		/*
		 * Iterate through each of the elements are to be applied to
		 */
		return this.each(function() {
			var lineNo = 1;
			var textarea = $(this);
			
			/* Turn off the wrapping of as we don't want to screw up the line numbers */
			textarea.attr("wrap", "off");
			textarea.css({resize:'none'});
			var originalTextAreaWidth	= textarea.outerWidth();

			/* Wrap the text area in the elements we need */
			textarea.wrap("<div class='linedtextarea'></div>");
			var linedTextAreaDiv	= textarea.parent().wrap("<div class='linedwrap' style='width:" + originalTextAreaWidth + "px'></div>");
			var linedWrapDiv 			= linedTextAreaDiv.parent();
			
			linedWrapDiv.prepend("<div class='lines' style='width:50px'></div>");
			
			var linesDiv	= linedWrapDiv.find(".lines");
			linesDiv.height( textarea.height() + 6 );
			
			
			/* Draw the number bar; filling it out where necessary */
			linesDiv.append( "<div class='codelines'></div>" );
			var codeLinesDiv	= linesDiv.find(".codelines");
			lineNo = fillOutLines( codeLinesDiv, linesDiv.height(), 1 );

			/* Move the textarea to the selected line */ 
			if ( opts.selectedLine != -1 && !isNaN(opts.selectedLine) ){
				var fontSize = parseInt( textarea.height() / (lineNo-2) );
				var position = parseInt( fontSize * opts.selectedLine ) - (textarea.height()/2);
				textarea[0].scrollTop = position;
			}

			
			/* Set the width */
			var sidebarWidth					= linesDiv.outerWidth();
			var paddingHorizontal 		= parseInt( linedWrapDiv.css("border-left-width") ) + parseInt( linedWrapDiv.css("border-right-width") ) + parseInt( linedWrapDiv.css("padding-left") ) + parseInt( linedWrapDiv.css("padding-right") );
			var linedWrapDivNewWidth 	= originalTextAreaWidth - paddingHorizontal;
			var textareaNewWidth			= originalTextAreaWidth - sidebarWidth - paddingHorizontal - 20;

			textarea.width( textareaNewWidth );
			linedWrapDiv.width( linedWrapDivNewWidth );
			

			
			/* React to the scroll event */
			textarea.scroll( function(tn){
				var domTextArea		= $(this)[0];
				var scrollTop 		= domTextArea.scrollTop;
				var clientHeight 	= domTextArea.clientHeight;
				codeLinesDiv.css( {'margin-top': (-1*scrollTop) + "px"} );
				lineNo = fillOutLines( codeLinesDiv, scrollTop + clientHeight, lineNo );
			});


			/* Should the textarea get resized outside of our control */
			textarea.resize( function(tn){
				var domTextArea	= $(this)[0];
				linesDiv.height( domTextArea.clientHeight + 6 );
			});

		});
	};

  // default options
  $.fn.linedtextarea.defaults = {
  	selectedLine: -1,
  	selectedClass: 'lineselect'
  };
})(jQuery);
/*! jQuery UI - v1.11.4 - 2015-04-09
* http://jqueryui.com
* Includes: core.js, widget.js, mouse.js, position.js, draggable.js, droppable.js, resizable.js, selectable.js, sortable.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, menu.js, progressbar.js, selectmenu.js, slider.js, spinner.js, tabs.js, tooltip.js
* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */

(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,s){var n,a,o,r=t.nodeName.toLowerCase();return"area"===r?(n=t.parentNode,a=n.name,t.href&&a&&"map"===n.nodeName.toLowerCase()?(o=e("img[usemap='#"+a+"']")[0],!!o&&i(o)):!1):(/^(input|select|textarea|button|object)$/.test(r)?!t.disabled:"a"===r?t.href||s:s)&&i(t)}function i(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}function s(e){for(var t,i;e.length&&e[0]!==document;){if(t=e.css("position"),("absolute"===t||"relative"===t||"fixed"===t)&&(i=parseInt(e.css("zIndex"),10),!isNaN(i)&&0!==i))return i;e=e.parent()}return 0}function n(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},e.extend(this._defaults,this.regional[""]),this.regional.en=e.extend(!0,{},this.regional[""]),this.regional["en-US"]=e.extend(!0,{},this.regional.en),this.dpDiv=a(e("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function a(t){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.delegate(i,"mouseout",function(){e(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).removeClass("ui-datepicker-next-hover")}).delegate(i,"mouseover",o)}function o(){e.datepicker._isDisabledDatepicker(v.inline?v.dpDiv.parent()[0]:v.input[0])||(e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),e(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).addClass("ui-datepicker-next-hover"))}function r(t,i){e.extend(t,i);for(var s in i)null==i[s]&&(t[s]=i[s]);return t}function h(e){return function(){var t=this.element.val();e.apply(this,arguments),this._refresh(),t!==this.element.val()&&this._trigger("change")}}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.4",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(t){var i=this.css("position"),s="absolute"===i,n=t?/(auto|scroll|hidden)/:/(auto|scroll)/,a=this.parents().filter(function(){var t=e(this);return s&&"static"===t.css("position")?!1:n.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==i&&a.length?a:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(i){return t(i,!isNaN(e.attr(i,"tabindex")))},tabbable:function(i){var s=e.attr(i,"tabindex"),n=isNaN(s);return(n||s>=0)&&t(i,!n)}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(t,i){function s(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+i]=function(t){return void 0===t?o["inner"+i].call(this):this.each(function(){e(this).css(a,s(this,t)+"px")})},e.fn["outer"+i]=function(t,n){return"number"!=typeof t?o["outer"+i].call(this,t):this.each(function(){e(this).css(a,s(this,t,!0,n)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var i,s,n=e(this[0]);n.length&&n[0]!==document;){if(i=n.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(s=parseInt(n.css("zIndex"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),e.ui.plugin={add:function(t,i,s){var n,a=e.ui[t].prototype;for(n in s)a.plugins[n]=a.plugins[n]||[],a.plugins[n].push([i,s[n]])},call:function(e,t,i,s){var n,a=e.plugins[t];if(a&&(s||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(n=0;a.length>n;n++)e.options[a[n][0]]&&a[n][1].apply(e.element,i)}};var l=0,u=Array.prototype.slice;e.cleanData=function(t){return function(i){var s,n,a;for(a=0;null!=(n=i[a]);a++)try{s=e._data(n,"events"),s&&s.remove&&e(n).triggerHandler("remove")}catch(o){}t(i)}}(e.cleanData),e.widget=function(t,i,s){var n,a,o,r,h={},l=t.split(".")[0];return t=t.split(".")[1],n=l+"-"+t,s||(s=i,i=e.Widget),e.expr[":"][n.toLowerCase()]=function(t){return!!e.data(t,n)},e[l]=e[l]||{},a=e[l][t],o=e[l][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new o(e,t)},e.extend(o,a,{version:s.version,_proto:e.extend({},s),_childConstructors:[]}),r=new i,r.options=e.widget.extend({},r.options),e.each(s,function(t,s){return e.isFunction(s)?(h[t]=function(){var e=function(){return i.prototype[t].apply(this,arguments)},n=function(e){return i.prototype[t].apply(this,e)};return function(){var t,i=this._super,a=this._superApply;return this._super=e,this._superApply=n,t=s.apply(this,arguments),this._super=i,this._superApply=a,t}}(),void 0):(h[t]=s,void 0)}),o.prototype=e.widget.extend(r,{widgetEventPrefix:a?r.widgetEventPrefix||t:t},h,{constructor:o,namespace:l,widgetName:t,widgetFullName:n}),a?(e.each(a._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var i,s,n=u.call(arguments,1),a=0,o=n.length;o>a;a++)for(i in n[a])s=n[a][i],n[a].hasOwnProperty(i)&&void 0!==s&&(t[i]=e.isPlainObject(s)?e.isPlainObject(t[i])?e.widget.extend({},t[i],s):e.widget.extend({},s):s);return t},e.widget.bridge=function(t,i){var s=i.prototype.widgetFullName||t;e.fn[t]=function(n){var a="string"==typeof n,o=u.call(arguments,1),r=this;return a?this.each(function(){var i,a=e.data(this,s);return"instance"===n?(r=a,!1):a?e.isFunction(a[n])&&"_"!==n.charAt(0)?(i=a[n].apply(a,o),i!==a&&void 0!==i?(r=i&&i.jquery?r.pushStack(i.get()):i,!1):void 0):e.error("no such method '"+n+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+n+"'")}):(o.length&&(n=e.widget.extend.apply(null,[n].concat(o))),this.each(function(){var t=e.data(this,s);t?(t.option(n||{}),t._init&&t._init()):e.data(this,s,new i(n,this))})),r}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(t,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=l++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=e(),this.hoverable=e(),this.focusable=e(),i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var s,n,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},s=t.split("."),t=s.shift(),s.length){for(n=o[t]=e.widget.extend({},this.options[t]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(t=s.pop(),1===arguments.length)return void 0===n[t]?null:n[t];n[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,s){var n,a=this;"boolean"!=typeof t&&(s=i,i=t,t=!1),s?(i=n=e(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),e.each(s,function(s,o){function r(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||e.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(t,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(i).undelegate(i),this.bindings=e(this.bindings.not(t).get()),this.focusable=e(this.focusable.not(t).get()),this.hoverable=e(this.hoverable.not(t).get())},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,o=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),o=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&e.effects&&e.effects.effect[r]?s[t](n):r!==t&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}}),e.widget;var d=!1;e(document).mouseup(function(){d=!1}),e.widget("ui.mouse",{version:"1.11.4",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!d){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var i=this,s=1===t.which,n="string"==typeof this.options.cancel&&t.target.nodeName?e(t.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(t)!==!1,!this._mouseStarted)?(t.preventDefault(),!0):(!0===e.data(t.target,this.widgetName+".preventClickEvent")&&e.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return i._mouseMove(e)},this._mouseUpDelegate=function(e){return i._mouseUp(e)},this.document.bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),d=!0,!0)):!0}},_mouseMove:function(t){if(this._mouseMoved){if(e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button)return this._mouseUp(t);if(!t.which)return this._mouseUp(t)}return(t.which||t.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){return this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),d=!1,!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),function(){function t(e,t,i){return[parseFloat(e[0])*(p.test(e[0])?t/100:1),parseFloat(e[1])*(p.test(e[1])?i/100:1)]}function i(t,i){return parseInt(e.css(t,i),10)||0}function s(t){var i=t[0];return 9===i.nodeType?{width:t.width(),height:t.height(),offset:{top:0,left:0}}:e.isWindow(i)?{width:t.width(),height:t.height(),offset:{top:t.scrollTop(),left:t.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:t.outerWidth(),height:t.outerHeight(),offset:t.offset()}}e.ui=e.ui||{};var n,a,o=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,u=/top|center|bottom/,d=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,p=/%$/,f=e.fn.position;e.position={scrollbarWidth:function(){if(void 0!==n)return n;var t,i,s=e("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),a=s.children()[0];return e("body").append(s),t=a.offsetWidth,s.css("overflow","scroll"),i=a.offsetWidth,t===i&&(i=s[0].clientWidth),s.remove(),n=t-i},getScrollInfo:function(t){var i=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),s=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),n="scroll"===i||"auto"===i&&t.width<t.element[0].scrollWidth,a="scroll"===s||"auto"===s&&t.height<t.element[0].scrollHeight;return{width:a?e.position.scrollbarWidth():0,height:n?e.position.scrollbarWidth():0}},getWithinInfo:function(t){var i=e(t||window),s=e.isWindow(i[0]),n=!!i[0]&&9===i[0].nodeType;return{element:i,isWindow:s,isDocument:n,offset:i.offset()||{left:0,top:0},scrollLeft:i.scrollLeft(),scrollTop:i.scrollTop(),width:s||n?i.width():i.outerWidth(),height:s||n?i.height():i.outerHeight()}}},e.fn.position=function(n){if(!n||!n.of)return f.apply(this,arguments);n=e.extend({},n);var p,m,g,v,y,b,_=e(n.of),x=e.position.getWithinInfo(n.within),w=e.position.getScrollInfo(x),k=(n.collision||"flip").split(" "),T={};return b=s(_),_[0].preventDefault&&(n.at="left top"),m=b.width,g=b.height,v=b.offset,y=e.extend({},v),e.each(["my","at"],function(){var e,t,i=(n[this]||"").split(" ");1===i.length&&(i=l.test(i[0])?i.concat(["center"]):u.test(i[0])?["center"].concat(i):["center","center"]),i[0]=l.test(i[0])?i[0]:"center",i[1]=u.test(i[1])?i[1]:"center",e=d.exec(i[0]),t=d.exec(i[1]),T[this]=[e?e[0]:0,t?t[0]:0],n[this]=[c.exec(i[0])[0],c.exec(i[1])[0]]}),1===k.length&&(k[1]=k[0]),"right"===n.at[0]?y.left+=m:"center"===n.at[0]&&(y.left+=m/2),"bottom"===n.at[1]?y.top+=g:"center"===n.at[1]&&(y.top+=g/2),p=t(T.at,m,g),y.left+=p[0],y.top+=p[1],this.each(function(){var s,l,u=e(this),d=u.outerWidth(),c=u.outerHeight(),f=i(this,"marginLeft"),b=i(this,"marginTop"),D=d+f+i(this,"marginRight")+w.width,S=c+b+i(this,"marginBottom")+w.height,N=e.extend({},y),M=t(T.my,u.outerWidth(),u.outerHeight());"right"===n.my[0]?N.left-=d:"center"===n.my[0]&&(N.left-=d/2),"bottom"===n.my[1]?N.top-=c:"center"===n.my[1]&&(N.top-=c/2),N.left+=M[0],N.top+=M[1],a||(N.left=h(N.left),N.top=h(N.top)),s={marginLeft:f,marginTop:b},e.each(["left","top"],function(t,i){e.ui.position[k[t]]&&e.ui.position[k[t]][i](N,{targetWidth:m,targetHeight:g,elemWidth:d,elemHeight:c,collisionPosition:s,collisionWidth:D,collisionHeight:S,offset:[p[0]+M[0],p[1]+M[1]],my:n.my,at:n.at,within:x,elem:u})}),n.using&&(l=function(e){var t=v.left-N.left,i=t+m-d,s=v.top-N.top,a=s+g-c,h={target:{element:_,left:v.left,top:v.top,width:m,height:g},element:{element:u,left:N.left,top:N.top,width:d,height:c},horizontal:0>i?"left":t>0?"right":"center",vertical:0>a?"top":s>0?"bottom":"middle"};d>m&&m>r(t+i)&&(h.horizontal="center"),c>g&&g>r(s+a)&&(h.vertical="middle"),h.important=o(r(t),r(i))>o(r(s),r(a))?"horizontal":"vertical",n.using.call(this,e,h)}),u.offset(e.extend(N,{using:l}))})},e.ui.position={fit:{left:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=e.left-t.collisionPosition.marginLeft,h=n-r,l=r+t.collisionWidth-a-n;t.collisionWidth>a?h>0&&0>=l?(i=e.left+h+t.collisionWidth-a-n,e.left+=h-i):e.left=l>0&&0>=h?n:h>l?n+a-t.collisionWidth:n:h>0?e.left+=h:l>0?e.left-=l:e.left=o(e.left-r,e.left)},top:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollTop:s.offset.top,a=t.within.height,r=e.top-t.collisionPosition.marginTop,h=n-r,l=r+t.collisionHeight-a-n;t.collisionHeight>a?h>0&&0>=l?(i=e.top+h+t.collisionHeight-a-n,e.top+=h-i):e.top=l>0&&0>=h?n:h>l?n+a-t.collisionHeight:n:h>0?e.top+=h:l>0?e.top-=l:e.top=o(e.top-r,e.top)}},flip:{left:function(e,t){var i,s,n=t.within,a=n.offset.left+n.scrollLeft,o=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=e.left-t.collisionPosition.marginLeft,u=l-h,d=l+t.collisionWidth-o-h,c="left"===t.my[0]?-t.elemWidth:"right"===t.my[0]?t.elemWidth:0,p="left"===t.at[0]?t.targetWidth:"right"===t.at[0]?-t.targetWidth:0,f=-2*t.offset[0];0>u?(i=e.left+c+p+f+t.collisionWidth-o-a,(0>i||r(u)>i)&&(e.left+=c+p+f)):d>0&&(s=e.left-t.collisionPosition.marginLeft+c+p+f-h,(s>0||d>r(s))&&(e.left+=c+p+f))},top:function(e,t){var i,s,n=t.within,a=n.offset.top+n.scrollTop,o=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=e.top-t.collisionPosition.marginTop,u=l-h,d=l+t.collisionHeight-o-h,c="top"===t.my[1],p=c?-t.elemHeight:"bottom"===t.my[1]?t.elemHeight:0,f="top"===t.at[1]?t.targetHeight:"bottom"===t.at[1]?-t.targetHeight:0,m=-2*t.offset[1];0>u?(s=e.top+p+f+m+t.collisionHeight-o-a,(0>s||r(u)>s)&&(e.top+=p+f+m)):d>0&&(i=e.top-t.collisionPosition.marginTop+p+f+m-h,(i>0||d>r(i))&&(e.top+=p+f+m))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,i,s,n,o,r=document.getElementsByTagName("body")[0],h=document.createElement("div");t=document.createElement(r?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},r&&e.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(o in s)t.style[o]=s[o];t.appendChild(h),i=r||document.documentElement,i.insertBefore(t,i.firstChild),h.style.cssText="position: absolute; left: 10.7432222px;",n=e(h).offset().left,a=n>10&&11>n,t.innerHTML="",i.removeChild(t)}()}(),e.ui.position,e.widget("ui.draggable",e.ui.mouse,{version:"1.11.4",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._setHandleClassName(),this._mouseInit()},_setOption:function(e,t){this._super(e,t),"handle"===e&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(t){var i=this.options;return this._blurActiveElement(t),this.helper||i.disabled||e(t.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(t),this.handle?(this._blockFrames(i.iframeFix===!0?"iframe":i.iframeFix),!0):!1)},_blockFrames:function(t){this.iframeBlocks=this.document.find(t).map(function(){var t=e(this);return e("<div>").css("position","absolute").appendTo(t.parent()).outerWidth(t.outerWidth()).outerHeight(t.outerHeight()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(t){var i=this.document[0];if(this.handleElement.is(t.target))try{i.activeElement&&"body"!==i.activeElement.nodeName.toLowerCase()&&e(i.activeElement).blur()}catch(s){}},_mouseStart:function(t){var i=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===e(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(t),this.originalPosition=this.position=this._generatePosition(t,!1),this.originalPageX=t.pageX,this.originalPageY=t.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._normalizeRightBottom(),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_refreshOffsets:function(e){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:e.pageX-this.offset.left,top:e.pageY-this.offset.top}},_mouseDrag:function(t,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",t,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var i=this,s=!1;return e.ui.ddmanager&&!this.options.dropBehaviour&&(s=e.ui.ddmanager.drop(this,t)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",t)!==!1&&i._clear()}):this._trigger("stop",t)!==!1&&this._clear(),!1},_mouseUp:function(t){return this._unblockFrames(),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),this.handleElement.is(t.target)&&this.element.focus(),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){return this.options.handle?!!e(t.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this.handleElement.addClass("ui-draggable-handle")},_removeHandleClassName:function(){this.handleElement.removeClass("ui-draggable-handle")},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper),n=s?e(i.helper.apply(this.element[0],[t])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return n.parents("body").length||n.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&n[0]===this.element[0]&&this._setPositionRelative(),n[0]===this.element[0]||/(fixed|absolute)/.test(n.css("position"))||n.css("position","absolute"),n},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_isRootNode:function(e){return/(html|body)/i.test(e.tagName)||e===this.document[0]},_getParentOffset:function(){var t=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var e=this.element.position(),t=this._isRootNode(this.scrollParent[0]);return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+(t?0:this.scrollParent.scrollTop()),left:e.left-(parseInt(this.helper.css("left"),10)||0)+(t?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options,a=this.document[0];return this.relativeContainer=null,n.containment?"window"===n.containment?(this.containment=[e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,e(window).scrollLeft()+e(window).width()-this.helperProportions.width-this.margins.left,e(window).scrollTop()+(e(window).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===n.containment?(this.containment=[0,0,e(a).width()-this.helperProportions.width-this.margins.left,(e(a).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=e(n.containment),s=i[0],s&&(t=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(t?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i),void 0):(this.containment=null,void 0)},_convertPositionTo:function(e,t){t||(t=this.position);var i="absolute"===e?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:t.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:t.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(e,t){var i,s,n,a,o=this.options,r=this._isRootNode(this.scrollParent[0]),h=e.pageX,l=e.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),t&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,e.pageX-this.offset.click.left<i[0]&&(h=i[0]+this.offset.click.left),e.pageY-this.offset.click.top<i[1]&&(l=i[1]+this.offset.click.top),e.pageX-this.offset.click.left>i[2]&&(h=i[2]+this.offset.click.left),e.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,h=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a),"y"===o.axis&&(h=this.originalPageX),"x"===o.axis&&(l=this.originalPageY)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)}
},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_normalizeRightBottom:function(){"y"!==this.options.axis&&"auto"!==this.helper.css("right")&&(this.helper.width(this.helper.width()),this.helper.css("right","auto")),"x"!==this.options.axis&&"auto"!==this.helper.css("bottom")&&(this.helper.height(this.helper.height()),this.helper.css("bottom","auto"))},_trigger:function(t,i,s){return s=s||this._uiHash(),e.ui.plugin.call(this,t,[i,s,this],!0),/^(drag|start|stop)/.test(t)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),e.Widget.prototype._trigger.call(this,t,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,i,s){var n=e.extend({},i,{item:s.element});s.sortables=[],e(s.options.connectToSortable).each(function(){var i=e(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",t,n))})},stop:function(t,i,s){var n=e.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,e.each(s.sortables,function(){var e=this;e.isOver?(e.isOver=0,s.cancelHelperRemoval=!0,e.cancelHelperRemoval=!1,e._storedCSS={position:e.placeholder.css("position"),top:e.placeholder.css("top"),left:e.placeholder.css("left")},e._mouseStop(t),e.options.helper=e.options._helper):(e.cancelHelperRemoval=!0,e._trigger("deactivate",t,n))})},drag:function(t,i,s){e.each(s.sortables,function(){var n=!1,a=this;a.positionAbs=s.positionAbs,a.helperProportions=s.helperProportions,a.offset.click=s.offset.click,a._intersectsWith(a.containerCache)&&(n=!0,e.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==a&&this._intersectsWith(this.containerCache)&&e.contains(a.element[0],this.element[0])&&(n=!1),n})),n?(a.isOver||(a.isOver=1,s._parent=i.helper.parent(),a.currentItem=i.helper.appendTo(a.element).data("ui-sortable-item",!0),a.options._helper=a.options.helper,a.options.helper=function(){return i.helper[0]},t.target=a.currentItem[0],a._mouseCapture(t,!0),a._mouseStart(t,!0,!0),a.offset.click.top=s.offset.click.top,a.offset.click.left=s.offset.click.left,a.offset.parent.left-=s.offset.parent.left-a.offset.parent.left,a.offset.parent.top-=s.offset.parent.top-a.offset.parent.top,s._trigger("toSortable",t),s.dropped=a.element,e.each(s.sortables,function(){this.refreshPositions()}),s.currentItem=s.element,a.fromOutside=s),a.currentItem&&(a._mouseDrag(t),i.position=a.position)):a.isOver&&(a.isOver=0,a.cancelHelperRemoval=!0,a.options._revert=a.options.revert,a.options.revert=!1,a._trigger("out",t,a._uiHash(a)),a._mouseStop(t,!0),a.options.revert=a.options._revert,a.options.helper=a.options._helper,a.placeholder&&a.placeholder.remove(),i.helper.appendTo(s._parent),s._refreshOffsets(t),i.position=s._generatePosition(t,!0),s._trigger("fromSortable",t),s.dropped=!1,e.each(s.sortables,function(){this.refreshPositions()}))})}}),e.ui.plugin.add("draggable","cursor",{start:function(t,i,s){var n=e("body"),a=s.options;n.css("cursor")&&(a._cursor=n.css("cursor")),n.css("cursor",a.cursor)},stop:function(t,i,s){var n=s.options;n._cursor&&e("body").css("cursor",n._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,i,s){var n=e(i.helper),a=s.options;n.css("opacity")&&(a._opacity=n.css("opacity")),n.css("opacity",a.opacity)},stop:function(t,i,s){var n=s.options;n._opacity&&e(i.helper).css("opacity",n._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(e,t,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&"HTML"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(t,i,s){var n=s.options,a=!1,o=s.scrollParentNotHidden[0],r=s.document[0];o!==r&&"HTML"!==o.tagName?(n.axis&&"x"===n.axis||(s.overflowOffset.top+o.offsetHeight-t.pageY<n.scrollSensitivity?o.scrollTop=a=o.scrollTop+n.scrollSpeed:t.pageY-s.overflowOffset.top<n.scrollSensitivity&&(o.scrollTop=a=o.scrollTop-n.scrollSpeed)),n.axis&&"y"===n.axis||(s.overflowOffset.left+o.offsetWidth-t.pageX<n.scrollSensitivity?o.scrollLeft=a=o.scrollLeft+n.scrollSpeed:t.pageX-s.overflowOffset.left<n.scrollSensitivity&&(o.scrollLeft=a=o.scrollLeft-n.scrollSpeed))):(n.axis&&"x"===n.axis||(t.pageY-e(r).scrollTop()<n.scrollSensitivity?a=e(r).scrollTop(e(r).scrollTop()-n.scrollSpeed):e(window).height()-(t.pageY-e(r).scrollTop())<n.scrollSensitivity&&(a=e(r).scrollTop(e(r).scrollTop()+n.scrollSpeed))),n.axis&&"y"===n.axis||(t.pageX-e(r).scrollLeft()<n.scrollSensitivity?a=e(r).scrollLeft(e(r).scrollLeft()-n.scrollSpeed):e(window).width()-(t.pageX-e(r).scrollLeft())<n.scrollSensitivity&&(a=e(r).scrollLeft(e(r).scrollLeft()+n.scrollSpeed)))),a!==!1&&e.ui.ddmanager&&!n.dropBehaviour&&e.ui.ddmanager.prepareOffsets(s,t)}}),e.ui.plugin.add("draggable","snap",{start:function(t,i,s){var n=s.options;s.snapElements=[],e(n.snap.constructor!==String?n.snap.items||":data(ui-draggable)":n.snap).each(function(){var t=e(this),i=t.offset();this!==s.element[0]&&s.snapElements.push({item:this,width:t.outerWidth(),height:t.outerHeight(),top:i.top,left:i.left})})},drag:function(t,i,s){var n,a,o,r,h,l,u,d,c,p,f=s.options,m=f.snapTolerance,g=i.offset.left,v=g+s.helperProportions.width,y=i.offset.top,b=y+s.helperProportions.height;for(c=s.snapElements.length-1;c>=0;c--)h=s.snapElements[c].left-s.margins.left,l=h+s.snapElements[c].width,u=s.snapElements[c].top-s.margins.top,d=u+s.snapElements[c].height,h-m>v||g>l+m||u-m>b||y>d+m||!e.contains(s.snapElements[c].item.ownerDocument,s.snapElements[c].item)?(s.snapElements[c].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,t,e.extend(s._uiHash(),{snapItem:s.snapElements[c].item})),s.snapElements[c].snapping=!1):("inner"!==f.snapMode&&(n=m>=Math.abs(u-b),a=m>=Math.abs(d-y),o=m>=Math.abs(h-v),r=m>=Math.abs(l-g),n&&(i.position.top=s._convertPositionTo("relative",{top:u-s.helperProportions.height,left:0}).top),a&&(i.position.top=s._convertPositionTo("relative",{top:d,left:0}).top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h-s.helperProportions.width}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l}).left)),p=n||a||o||r,"outer"!==f.snapMode&&(n=m>=Math.abs(u-y),a=m>=Math.abs(d-b),o=m>=Math.abs(h-g),r=m>=Math.abs(l-v),n&&(i.position.top=s._convertPositionTo("relative",{top:u,left:0}).top),a&&(i.position.top=s._convertPositionTo("relative",{top:d-s.helperProportions.height,left:0}).top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l-s.helperProportions.width}).left)),!s.snapElements[c].snapping&&(n||a||o||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,t,e.extend(s._uiHash(),{snapItem:s.snapElements[c].item})),s.snapElements[c].snapping=n||a||o||r||p)}}),e.ui.plugin.add("draggable","stack",{start:function(t,i,s){var n,a=s.options,o=e.makeArray(e(a.stack)).sort(function(t,i){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(i).css("zIndex"),10)||0)});o.length&&(n=parseInt(e(o[0]).css("zIndex"),10)||0,e(o).each(function(t){e(this).css("zIndex",n+t)}),this.css("zIndex",n+o.length))}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,i,s){var n=e(i.helper),a=s.options;n.css("zIndex")&&(a._zIndex=n.css("zIndex")),n.css("zIndex",a.zIndex)},stop:function(t,i,s){var n=s.options;n._zIndex&&e(i.helper).css("zIndex",n._zIndex)}}),e.ui.draggable,e.widget("ui.droppable",{version:"1.11.4",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var t,i=this.options,s=i.accept;this.isover=!1,this.isout=!0,this.accept=e.isFunction(s)?s:function(e){return e.is(s)},this.proportions=function(){return arguments.length?(t=arguments[0],void 0):t?t:t={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},this._addToManager(i.scope),i.addClasses&&this.element.addClass("ui-droppable")},_addToManager:function(t){e.ui.ddmanager.droppables[t]=e.ui.ddmanager.droppables[t]||[],e.ui.ddmanager.droppables[t].push(this)},_splice:function(e){for(var t=0;e.length>t;t++)e[t]===this&&e.splice(t,1)},_destroy:function(){var t=e.ui.ddmanager.droppables[this.options.scope];this._splice(t),this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(t,i){if("accept"===t)this.accept=e.isFunction(i)?i:function(e){return e.is(i)};else if("scope"===t){var s=e.ui.ddmanager.droppables[this.options.scope];this._splice(s),this._addToManager(i)}this._super(t,i)},_activate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),i&&this._trigger("activate",t,this.ui(i))},_deactivate:function(t){var i=e.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),i&&this._trigger("deactivate",t,this.ui(i))},_over:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",t,this.ui(i)))},_out:function(t){var i=e.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",t,this.ui(i)))},_drop:function(t,i){var s=i||e.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var i=e(this).droppable("instance");return i.options.greedy&&!i.options.disabled&&i.options.scope===s.options.scope&&i.accept.call(i.element[0],s.currentItem||s.element)&&e.ui.intersect(s,e.extend(i,{offset:i.element.offset()}),i.options.tolerance,t)?(n=!0,!1):void 0}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",t,this.ui(s)),this.element):!1):!1},ui:function(e){return{draggable:e.currentItem||e.element,helper:e.helper,position:e.position,offset:e.positionAbs}}}),e.ui.intersect=function(){function e(e,t,i){return e>=t&&t+i>e}return function(t,i,s,n){if(!i.offset)return!1;var a=(t.positionAbs||t.position.absolute).left+t.margins.left,o=(t.positionAbs||t.position.absolute).top+t.margins.top,r=a+t.helperProportions.width,h=o+t.helperProportions.height,l=i.offset.left,u=i.offset.top,d=l+i.proportions().width,c=u+i.proportions().height;switch(s){case"fit":return a>=l&&d>=r&&o>=u&&c>=h;case"intersect":return a+t.helperProportions.width/2>l&&d>r-t.helperProportions.width/2&&o+t.helperProportions.height/2>u&&c>h-t.helperProportions.height/2;case"pointer":return e(n.pageY,u,i.proportions().height)&&e(n.pageX,l,i.proportions().width);case"touch":return(o>=u&&c>=o||h>=u&&c>=h||u>o&&h>c)&&(a>=l&&d>=a||r>=l&&d>=r||l>a&&r>d);default:return!1}}}(),e.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(t,i){var s,n,a=e.ui.ddmanager.droppables[t.options.scope]||[],o=i?i.type:null,r=(t.currentItem||t.element).find(":data(ui-droppable)").addBack();e:for(s=0;a.length>s;s++)if(!(a[s].options.disabled||t&&!a[s].accept.call(a[s].element[0],t.currentItem||t.element))){for(n=0;r.length>n;n++)if(r[n]===a[s].element[0]){a[s].proportions().height=0;continue e}a[s].visible="none"!==a[s].element.css("display"),a[s].visible&&("mousedown"===o&&a[s]._activate.call(a[s],i),a[s].offset=a[s].element.offset(),a[s].proportions({width:a[s].element[0].offsetWidth,height:a[s].element[0].offsetHeight}))}},drop:function(t,i){var s=!1;return e.each((e.ui.ddmanager.droppables[t.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&e.ui.intersect(t,this,this.options.tolerance,i)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],t.currentItem||t.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(t,i){t.element.parentsUntil("body").bind("scroll.droppable",function(){t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)})},drag:function(t,i){t.options.refreshPositions&&e.ui.ddmanager.prepareOffsets(t,i),e.each(e.ui.ddmanager.droppables[t.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,a,o=e.ui.intersect(t,this,this.options.tolerance,i),r=!o&&this.isover?"isout":o&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,a=this.element.parents(":data(ui-droppable)").filter(function(){return e(this).droppable("instance").options.scope===n}),a.length&&(s=e(a[0]).droppable("instance"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(t,i){t.element.parentsUntil("body").unbind("scroll.droppable"),t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,i)}},e.ui.droppable,e.widget("ui.resizable",e.ui.mouse,{version:"1.11.4",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(e){return parseInt(e,10)||0},_isNumber:function(e){return!isNaN(parseInt(e,10))},_hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return t[s]>0?!0:(t[s]=1,n=t[s]>0,t[s]=0,n)},_create:function(){var t,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(e("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=e(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),t=this.handles.split(","),this.handles={},i=0;t.length>i;i++)s=e.trim(t[i]),a="ui-resizable-"+s,n=e("<div class='ui-resizable-handle "+a+"'></div>"),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(t){var i,s,n,a;t=t||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=e(this.handles[i]),this._on(this.handles[i],{mousedown:o._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=e(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),t.css(n,a),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(e(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(e(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t,i=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),t=this.element,this.originalElement.css({position:t.css("position"),width:t.outerWidth(),height:t.outerHeight(),top:t.css("top"),left:t.css("left")}).insertAfter(t),t.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(t){var i,s,n=!1;for(i in this.handles)s=e(this.handles[i])[0],(s===t.target||e.contains(s,t.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(t){var i,s,n,a=this.options,o=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),a.containment&&(i+=e(a.containment).scrollLeft()||0,s+=e(a.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:o.width(),height:o.height()},this.originalSize=this._helper?{width:o.outerWidth(),height:o.outerHeight()}:{width:o.width(),height:o.height()},this.sizeDiff={width:o.outerWidth()-o.width(),height:o.outerHeight()-o.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof a.aspectRatio?a.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=e(".ui-resizable-"+this.axis).css("cursor"),e("body").css("cursor","auto"===n?this.axis+"-resize":n),o.addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var i,s,n=this.originalMousePosition,a=this.axis,o=t.pageX-n.left||0,r=t.pageY-n.top||0,h=this._change[a];return this._updatePrevProperties(),h?(i=h.apply(this,[t,o,r]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(i=this._updateRatio(i,t)),i=this._respectSize(i,t),this._updateCache(i),this._propagate("resize",t),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),e.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",t,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(t){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,u=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:u.sizeDiff.height,a=s?0:u.sizeDiff.width,o={width:u.helper.width()-a,height:u.helper.height()-n},r=parseInt(u.element.css("left"),10)+(u.position.left-u.originalPosition.left)||null,h=parseInt(u.element.css("top"),10)+(u.position.top-u.originalPosition.top)||null,l.animate||this.element.css(e.extend(o,{top:h,left:r})),u.helper.height(u.size.height),u.helper.width(u.size.width),this._helper&&!l.animate&&this._proportionallyResize()),e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var e={};return this.position.top!==this.prevPosition.top&&(e.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(e.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(e.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(e.height=this.size.height+"px"),this.helper.css(e),e},_updateVirtualBoundaries:function(e){var t,i,s,n,a,o=this.options;a={minWidth:this._isNumber(o.minWidth)?o.minWidth:0,maxWidth:this._isNumber(o.maxWidth)?o.maxWidth:1/0,minHeight:this._isNumber(o.minHeight)?o.minHeight:0,maxHeight:this._isNumber(o.maxHeight)?o.maxHeight:1/0},(this._aspectRatio||e)&&(t=a.minHeight*this.aspectRatio,s=a.minWidth/this.aspectRatio,i=a.maxHeight*this.aspectRatio,n=a.maxWidth/this.aspectRatio,t>a.minWidth&&(a.minWidth=t),s>a.minHeight&&(a.minHeight=s),a.maxWidth>i&&(a.maxWidth=i),a.maxHeight>n&&(a.maxHeight=n)),this._vBoundaries=a},_updateCache:function(e){this.offset=this.helper.offset(),this._isNumber(e.left)&&(this.position.left=e.left),this._isNumber(e.top)&&(this.position.top=e.top),this._isNumber(e.height)&&(this.size.height=e.height),this._isNumber(e.width)&&(this.size.width=e.width)},_updateRatio:function(e){var t=this.position,i=this.size,s=this.axis;return this._isNumber(e.height)?e.width=e.height*this.aspectRatio:this._isNumber(e.width)&&(e.height=e.width/this.aspectRatio),"sw"===s&&(e.left=t.left+(i.width-e.width),e.top=null),"nw"===s&&(e.top=t.top+(i.height-e.height),e.left=t.left+(i.width-e.width)),e},_respectSize:function(e){var t=this._vBoundaries,i=this.axis,s=this._isNumber(e.width)&&t.maxWidth&&t.maxWidth<e.width,n=this._isNumber(e.height)&&t.maxHeight&&t.maxHeight<e.height,a=this._isNumber(e.width)&&t.minWidth&&t.minWidth>e.width,o=this._isNumber(e.height)&&t.minHeight&&t.minHeight>e.height,r=this.originalPosition.left+this.originalSize.width,h=this.position.top+this.size.height,l=/sw|nw|w/.test(i),u=/nw|ne|n/.test(i);return a&&(e.width=t.minWidth),o&&(e.height=t.minHeight),s&&(e.width=t.maxWidth),n&&(e.height=t.maxHeight),a&&l&&(e.left=r-t.minWidth),s&&l&&(e.left=r-t.maxWidth),o&&u&&(e.top=h-t.minHeight),n&&u&&(e.top=h-t.maxHeight),e.width||e.height||e.left||!e.top?e.width||e.height||e.top||!e.left||(e.left=null):e.top=null,e},_getPaddingPlusBorderDimensions:function(e){for(var t=0,i=[],s=[e.css("borderTopWidth"),e.css("borderRightWidth"),e.css("borderBottomWidth"),e.css("borderLeftWidth")],n=[e.css("paddingTop"),e.css("paddingRight"),e.css("paddingBottom"),e.css("paddingLeft")];4>t;t++)i[t]=parseInt(s[t],10)||0,i[t]+=parseInt(n[t],10)||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var e,t=0,i=this.helper||this.element;this._proportionallyResizeElements.length>t;t++)e=this._proportionallyResizeElements[t],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(e)),e.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var t=this.element,i=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||e("<div style='overflow:hidden;'></div>"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var i=this.originalSize,s=this.originalPosition;return{left:s.left+t,width:i.width-t}},n:function(e,t,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(e,t,i){return{height:this.originalSize.height+i}},se:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},sw:function(t,i,s){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,i,s]))},ne:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,i,s]))},nw:function(t,i,s){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,i,s]))}},_propagate:function(t,i){e.ui.plugin.call(this,t,[i,this.ui()]),"resize"!==t&&this._trigger(t,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),e.ui.plugin.add("resizable","animate",{stop:function(t){var i=e(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,u=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(e.extend(h,u&&l?{top:u,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&e(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(){var t,i,s,n,a,o,r,h=e(this).resizable("instance"),l=h.options,u=h.element,d=l.containment,c=d instanceof e?d.get(0):/parent/.test(d)?u.parent().get(0):d;c&&(h.containerElement=e(c),/document/.test(d)||d===document?(h.containerOffset={left:0,top:0},h.containerPosition={left:0,top:0},h.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}):(t=e(c),i=[],e(["Top","Right","Left","Bottom"]).each(function(e,s){i[e]=h._num(t.css("padding"+s))}),h.containerOffset=t.offset(),h.containerPosition=t.position(),h.containerSize={height:t.innerHeight()-i[3],width:t.innerWidth()-i[1]},s=h.containerOffset,n=h.containerSize.height,a=h.containerSize.width,o=h._hasScroll(c,"left")?c.scrollWidth:a,r=h._hasScroll(c)?c.scrollHeight:n,h.parentData={element:c,left:s.left,top:s.top,width:o,height:r}))},resize:function(t){var i,s,n,a,o=e(this).resizable("instance"),r=o.options,h=o.containerOffset,l=o.position,u=o._aspectRatio||t.shiftKey,d={top:0,left:0},c=o.containerElement,p=!0;c[0]!==document&&/static/.test(c.css("position"))&&(d=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-d.left),u&&(o.size.height=o.size.width/o.aspectRatio,p=!1),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),u&&(o.size.width=o.size.height*o.aspectRatio,p=!1),o.position.top=o._helper?h.top:0),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a?(o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top):(o.offset.left=o.element.offset().left,o.offset.top=o.element.offset().top),i=Math.abs(o.sizeDiff.width+(o._helper?o.offset.left-d.left:o.offset.left-h.left)),s=Math.abs(o.sizeDiff.height+(o._helper?o.offset.top-d.top:o.offset.top-h.top)),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,u&&(o.size.height=o.size.width/o.aspectRatio,p=!1)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,u&&(o.size.width=o.size.height*o.aspectRatio,p=!1)),p||(o.position.left=o.prevPosition.left,o.position.top=o.prevPosition.top,o.size.width=o.prevSize.width,o.size.height=o.prevSize.height)},stop:function(){var t=e(this).resizable("instance"),i=t.options,s=t.containerOffset,n=t.containerPosition,a=t.containerElement,o=e(t.helper),r=o.offset(),h=o.outerWidth()-t.sizeDiff.width,l=o.outerHeight()-t.sizeDiff.height;t._helper&&!i.animate&&/relative/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l}),t._helper&&!i.animate&&/static/.test(a.css("position"))&&e(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),e.ui.plugin.add("resizable","alsoResize",{start:function(){var t=e(this).resizable("instance"),i=t.options;e(i.alsoResize).each(function(){var t=e(this);t.data("ui-resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})},resize:function(t,i){var s=e(this).resizable("instance"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0};e(n.alsoResize).each(function(){var t=e(this),s=e(this).data("ui-resizable-alsoresize"),n={},a=t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(a,function(e,t){var i=(s[t]||0)+(r[t]||0);i&&i>=0&&(n[t]=i||null)}),t.css(n)})},stop:function(){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","ghost",{start:function(){var t=e(this).resizable("instance"),i=t.options,s=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),t.ghost.appendTo(t.helper)},resize:function(){var t=e(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=e(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(){var t,i=e(this).resizable("instance"),s=i.options,n=i.size,a=i.originalSize,o=i.originalPosition,r=i.axis,h="number"==typeof s.grid?[s.grid,s.grid]:s.grid,l=h[0]||1,u=h[1]||1,d=Math.round((n.width-a.width)/l)*l,c=Math.round((n.height-a.height)/u)*u,p=a.width+d,f=a.height+c,m=s.maxWidth&&p>s.maxWidth,g=s.maxHeight&&f>s.maxHeight,v=s.minWidth&&s.minWidth>p,y=s.minHeight&&s.minHeight>f;s.grid=h,v&&(p+=l),y&&(f+=u),m&&(p-=l),g&&(f-=u),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=o.top-c):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=o.left-d):((0>=f-u||0>=p-l)&&(t=i._getPaddingPlusBorderDimensions(this)),f-u>0?(i.size.height=f,i.position.top=o.top-c):(f=u-t.height,i.size.height=f,i.position.top=o.top+a.height-f),p-l>0?(i.size.width=p,i.position.left=o.left-d):(p=l-t.width,i.size.width=p,i.position.left=o.left+a.width-p))}}),e.ui.resizable,e.widget("ui.selectable",e.ui.mouse,{version:"1.11.4",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var t,i=this;
this.element.addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){t=e(i.options.filter,i.element[0]),t.addClass("ui-selectee"),t.each(function(){var t=e(this),i=t.offset();e.data(this,"selectable-item",{element:this,$element:t,left:i.left,top:i.top,right:i.left+t.outerWidth(),bottom:i.top+t.outerHeight(),startselected:!1,selected:t.hasClass("ui-selected"),selecting:t.hasClass("ui-selecting"),unselecting:t.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=t.addClass("ui-selectee"),this._mouseInit(),this.helper=e("<div class='ui-selectable-helper'></div>")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(t){var i=this,s=this.options;this.opos=[t.pageX,t.pageY],this.options.disabled||(this.selectees=e(s.filter,this.element[0]),this._trigger("start",t),e(s.appendTo).append(this.helper),this.helper.css({left:t.pageX,top:t.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=e.data(this,"selectable-item");s.startselected=!0,t.metaKey||t.ctrlKey||(s.$element.removeClass("ui-selected"),s.selected=!1,s.$element.addClass("ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",t,{unselecting:s.element}))}),e(t.target).parents().addBack().each(function(){var s,n=e.data(this,"selectable-item");return n?(s=!t.metaKey&&!t.ctrlKey||!n.$element.hasClass("ui-selected"),n.$element.removeClass(s?"ui-unselecting":"ui-selected").addClass(s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",t,{selecting:n.element}):i._trigger("unselecting",t,{unselecting:n.element}),!1):void 0}))},_mouseDrag:function(t){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,a=this.opos[0],o=this.opos[1],r=t.pageX,h=t.pageY;return a>r&&(i=r,r=a,a=i),o>h&&(i=h,h=o,o=i),this.helper.css({left:a,top:o,width:r-a,height:h-o}),this.selectees.each(function(){var i=e.data(this,"selectable-item"),l=!1;i&&i.element!==s.element[0]&&("touch"===n.tolerance?l=!(i.left>r||a>i.right||i.top>h||o>i.bottom):"fit"===n.tolerance&&(l=i.left>a&&r>i.right&&i.top>o&&h>i.bottom),l?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,s._trigger("selecting",t,{selecting:i.element}))):(i.selecting&&((t.metaKey||t.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",t,{unselecting:i.element}))),i.selected&&(t.metaKey||t.ctrlKey||i.startselected||(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",t,{unselecting:i.element})))))}),!1}},_mouseStop:function(t){var i=this;return this.dragged=!1,e(".ui-unselecting",this.element[0]).each(function(){var s=e.data(this,"selectable-item");s.$element.removeClass("ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",t,{unselected:s.element})}),e(".ui-selecting",this.element[0]).each(function(){var s=e.data(this,"selectable-item");s.$element.removeClass("ui-selecting").addClass("ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",t,{selected:s.element})}),this._trigger("stop",t),this.helper.remove(),!1}}),e.widget("ui.sortable",e.ui.mouse,{version:"1.11.4",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(e,t,i){return e>=t&&t+i>e},_isFloating:function(e){return/left|right/.test(e.css("float"))||/inline|table-cell/.test(e.css("display"))},_create:function(){this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(e,t){this._super(e,t),"handle"===e&&this._setHandleClassName()},_setHandleClassName:function(){this.element.find(".ui-sortable-handle").removeClass("ui-sortable-handle"),e.each(this.items,function(){(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item).addClass("ui-sortable-handle")})},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").find(".ui-sortable-handle").removeClass("ui-sortable-handle"),this._mouseDestroy();for(var e=this.items.length-1;e>=0;e--)this.items[e].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(t,i){var s=null,n=!1,a=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(t),e(t.target).parents().each(function(){return e.data(this,a.widgetName+"-item")===a?(s=e(this),!1):void 0}),e.data(t.target,a.widgetName+"-item")===a&&(s=e(t.target)),s?!this.options.handle||i||(e(this.options.handle,s).find("*").addBack().each(function(){this===t.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(t,i,s){var n,a,o=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(t),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,o.cursorAt&&this._adjustOffsetFromHelper(o.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),o.containment&&this._setContainment(),o.cursor&&"auto"!==o.cursor&&(a=this.document.find("body"),this.storedCursor=a.css("cursor"),a.css("cursor",o.cursor),this.storedStylesheet=e("<style>*{ cursor: "+o.cursor+" !important; }</style>").appendTo(a)),o.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",o.opacity)),o.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",o.zIndex)),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",t,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",t,this._uiHash(this));return e.ui.ddmanager&&(e.ui.ddmanager.current=this),e.ui.ddmanager&&!o.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(t),!0},_mouseDrag:function(t){var i,s,n,a,o=this.options,r=!1;for(this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-t.pageY<o.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+o.scrollSpeed:t.pageY-this.overflowOffset.top<o.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-o.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-t.pageX<o.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+o.scrollSpeed:t.pageX-this.overflowOffset.left<o.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-o.scrollSpeed)):(t.pageY-this.document.scrollTop()<o.scrollSensitivity?r=this.document.scrollTop(this.document.scrollTop()-o.scrollSpeed):this.window.height()-(t.pageY-this.document.scrollTop())<o.scrollSensitivity&&(r=this.document.scrollTop(this.document.scrollTop()+o.scrollSpeed)),t.pageX-this.document.scrollLeft()<o.scrollSensitivity?r=this.document.scrollLeft(this.document.scrollLeft()-o.scrollSpeed):this.window.width()-(t.pageX-this.document.scrollLeft())<o.scrollSensitivity&&(r=this.document.scrollLeft(this.document.scrollLeft()+o.scrollSpeed))),r!==!1&&e.ui.ddmanager&&!o.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t)),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),i=this.items.length-1;i>=0;i--)if(s=this.items[i],n=s.item[0],a=this._intersectsWithPointer(s),a&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===a?"next":"prev"]()[0]!==n&&!e.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!e.contains(this.element[0],n):!0)){if(this.direction=1===a?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(t,s),this._trigger("change",t,this._uiHash());break}return this._contactContainers(t),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),this._trigger("sort",t,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(t,i){if(t){if(e.ui.ddmanager&&!this.options.dropBehaviour&&e.ui.ddmanager.drop(this,t),this.options.revert){var s=this,n=this.placeholder.offset(),a=this.options.axis,o={};a&&"x"!==a||(o.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollLeft)),a&&"y"!==a||(o.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,e(this.helper).animate(o,parseInt(this.options.revert,10)||500,function(){s._clear(t)})}else this._clear(t,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var t=this.containers.length-1;t>=0;t--)this.containers[t]._trigger("deactivate",null,this._uiHash(this)),this.containers[t].containerCache.over&&(this.containers[t]._trigger("out",null,this._uiHash(this)),this.containers[t].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),e.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?e(this.domPosition.prev).after(this.currentItem):e(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(t){var i=this._getItemsAsjQuery(t&&t.connected),s=[];return t=t||{},e(i).each(function(){var i=(e(t.item||this).attr(t.attribute||"id")||"").match(t.expression||/(.+)[\-=_](.+)/);i&&s.push((t.key||i[1]+"[]")+"="+(t.key&&t.expression?i[1]:i[2]))}),!s.length&&t.key&&s.push(t.key+"="),s.join("&")},toArray:function(t){var i=this._getItemsAsjQuery(t&&t.connected),s=[];return t=t||{},i.each(function(){s.push(e(t.item||this).attr(t.attribute||"id")||"")}),s},_intersectsWith:function(e){var t=this.positionAbs.left,i=t+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,a=e.left,o=a+e.width,r=e.top,h=r+e.height,l=this.offset.click.top,u=this.offset.click.left,d="x"===this.options.axis||s+l>r&&h>s+l,c="y"===this.options.axis||t+u>a&&o>t+u,p=d&&c;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>e[this.floating?"width":"height"]?p:t+this.helperProportions.width/2>a&&o>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(e){var t="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top,e.height),i="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left,e.width),s=t&&i,n=this._getDragVerticalDirection(),a=this._getDragHorizontalDirection();return s?this.floating?a&&"right"===a||"down"===n?2:1:n&&("down"===n?2:1):!1},_intersectsWithSides:function(e){var t=this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top+e.height/2,e.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left+e.width/2,e.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&t||"up"===s&&!t)},_getDragVerticalDirection:function(){var e=this.positionAbs.top-this.lastPositionAbs.top;return 0!==e&&(e>0?"down":"up")},_getDragHorizontalDirection:function(){var e=this.positionAbs.left-this.lastPositionAbs.left;return 0!==e&&(e>0?"right":"left")},refresh:function(e){return this._refreshItems(e),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var e=this.options;return e.connectWith.constructor===String?[e.connectWith]:e.connectWith},_getItemsAsjQuery:function(t){function i(){r.push(this)}var s,n,a,o,r=[],h=[],l=this._connectWith();if(l&&t)for(s=l.length-1;s>=0;s--)for(a=e(l[s],this.document[0]),n=a.length-1;n>=0;n--)o=e.data(a[n],this.widgetFullName),o&&o!==this&&!o.options.disabled&&h.push([e.isFunction(o.options.items)?o.options.items.call(o.element):e(o.options.items,o.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),o]);for(h.push([e.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):e(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return e(r)},_removeCurrentsFromItems:function(){var t=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=e.grep(this.items,function(e){for(var i=0;t.length>i;i++)if(t[i]===e.item[0])return!1;return!0})},_refreshItems:function(t){this.items=[],this.containers=[this];var i,s,n,a,o,r,h,l,u=this.items,d=[[e.isFunction(this.options.items)?this.options.items.call(this.element[0],t,{item:this.currentItem}):e(this.options.items,this.element),this]],c=this._connectWith();if(c&&this.ready)for(i=c.length-1;i>=0;i--)for(n=e(c[i],this.document[0]),s=n.length-1;s>=0;s--)a=e.data(n[s],this.widgetFullName),a&&a!==this&&!a.options.disabled&&(d.push([e.isFunction(a.options.items)?a.options.items.call(a.element[0],t,{item:this.currentItem}):e(a.options.items,a.element),a]),this.containers.push(a));for(i=d.length-1;i>=0;i--)for(o=d[i][1],r=d[i][0],s=0,l=r.length;l>s;s++)h=e(r[s]),h.data(this.widgetName+"-item",o),u.push({item:h,instance:o,width:0,height:0,left:0,top:0})},refreshPositions:function(t){this.floating=this.items.length?"x"===this.options.axis||this._isFloating(this.items[0].item):!1,this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,a;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?e(this.options.toleranceElement,s.item):s.item,t||(s.width=n.outerWidth(),s.height=n.outerHeight()),a=n.offset(),s.left=a.left,s.top=a.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)a=this.containers[i].element.offset(),this.containers[i].containerCache.left=a.left,this.containers[i].containerCache.top=a.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(t){t=t||this;var i,s=t.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=t.currentItem[0].nodeName.toLowerCase(),n=e("<"+s+">",t.document[0]).addClass(i||t.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tbody"===s?t._createTrPlaceholder(t.currentItem.find("tr").eq(0),e("<tr>",t.document[0]).appendTo(n)):"tr"===s?t._createTrPlaceholder(t.currentItem,n):"img"===s&&n.attr("src",t.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(e,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(t.currentItem.innerHeight()-parseInt(t.currentItem.css("paddingTop")||0,10)-parseInt(t.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(t.currentItem.innerWidth()-parseInt(t.currentItem.css("paddingLeft")||0,10)-parseInt(t.currentItem.css("paddingRight")||0,10)))}}),t.placeholder=e(s.placeholder.element.call(t.element,t.currentItem)),t.currentItem.after(t.placeholder),s.placeholder.update(t,t.placeholder)},_createTrPlaceholder:function(t,i){var s=this;t.children().each(function(){e("<td>&#160;</td>",s.document[0]).attr("colspan",e(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(t){var i,s,n,a,o,r,h,l,u,d,c=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!e.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(c&&e.contains(this.containers[i].element[0],c.element[0]))continue;c=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",t,this._uiHash(this)),this.containers[i].containerCache.over=0);if(c)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",t,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,a=null,u=c.floating||this._isFloating(this.currentItem),o=u?"left":"top",r=u?"width":"height",d=u?"clientX":"clientY",s=this.items.length-1;s>=0;s--)e.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[o],l=!1,t[d]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(t[d]-h)&&(n=Math.abs(t[d]-h),a=this.items[s],this.direction=l?"up":"down"));if(!a&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",t,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;a?this._rearrange(t,a,null,!0):this._rearrange(t,null,this.containers[p].element,!0),this._trigger("change",t,this._uiHash()),this.containers[p]._trigger("change",t,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",t,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper)?e(i.helper.apply(this.element[0],[t,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||e("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&e.ui.ie)&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var e=this.currentItem.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.width():this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(t=e(n.containment)[0],i=e(n.containment).offset(),s="hidden"!==e(t).css("overflow"),this.containment=[i.left+(parseInt(e(t).css("borderLeftWidth"),10)||0)+(parseInt(e(t).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(e(t).css("borderTopWidth"),10)||0)+(parseInt(e(t).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(t.scrollWidth,t.offsetWidth):t.offsetWidth)-(parseInt(e(t).css("borderLeftWidth"),10)||0)-(parseInt(e(t).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(t.scrollHeight,t.offsetHeight):t.offsetHeight)-(parseInt(e(t).css("borderTopWidth"),10)||0)-(parseInt(e(t).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(t,i){i||(i=this.position);var s="absolute"===t?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,a=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():a?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():a?0:n.scrollLeft())*s}},_generatePosition:function(t){var i,s,n=this.options,a=t.pageX,o=t.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(t.pageX-this.offset.click.left<this.containment[0]&&(a=this.containment[0]+this.offset.click.left),t.pageY-this.offset.click.top<this.containment[1]&&(o=this.containment[1]+this.offset.click.top),t.pageX-this.offset.click.left>this.containment[2]&&(a=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(o=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((o-this.originalPageY)/n.grid[1])*n.grid[1],o=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((a-this.originalPageX)/n.grid[0])*n.grid[0],a=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:o-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:a-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(e,t,i,s){i?i[0].appendChild(this.placeholder[0]):t.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?t.item[0]:t.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(e,t){function i(e,t,i){return function(s){i._trigger(e,s,t._uiHash(t))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!t&&n.push(function(e){this._trigger("receive",e,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||t||n.push(function(e){this._trigger("update",e,this._uiHash())}),this!==this.currentContainer&&(t||(n.push(function(e){this._trigger("remove",e,this._uiHash())}),n.push(function(e){return function(t){e._trigger("receive",t,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(e){return function(t){e._trigger("update",t,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)t||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,t||this._trigger("beforeStop",e,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!t){for(s=0;n.length>s;s++)n[s].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){e.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(t){var i=t||this;return{helper:i.helper,placeholder:i.placeholder||e([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:t?t.element:null}}}),e.widget("ui.accordion",{version:"1.11.4",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var t=this.options;this.prevShow=this.prevHide=e(),this.element.addClass("ui-accordion ui-widget ui-helper-reset").attr("role","tablist"),t.collapsible||t.active!==!1&&null!=t.active||(t.active=0),this._processPanels(),0>t.active&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():e()}},_createIcons:function(){var t=this.options.icons;t&&(e("<span>").addClass("ui-accordion-header-icon ui-icon "+t.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(t.header).addClass(t.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var e;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").removeUniqueId(),this._destroyIcons(),e=this.headers.next().removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").css("display","").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&e.css("height","")},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):("event"===e&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(t)),this._super(e,t),"collapsible"!==e||t||this.options.active!==!1||this._activate(0),"icons"===e&&(this._destroyIcons(),t&&this._createIcons()),"disabled"===e&&(this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!t)),void 0)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var i=e.ui.keyCode,s=this.headers.length,n=this.headers.index(t.target),a=!1;switch(t.keyCode){case i.RIGHT:case i.DOWN:a=this.headers[(n+1)%s];break;case i.LEFT:case i.UP:a=this.headers[(n-1+s)%s];break;case i.SPACE:case i.ENTER:this._eventHandler(t);break;case i.HOME:a=this.headers[0];break;case i.END:a=this.headers[s-1]}a&&(e(t.target).attr("tabIndex",-1),e(a).attr("tabIndex",0),a.focus(),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===e.ui.keyCode.UP&&t.ctrlKey&&e(t.currentTarget).prev().focus()},refresh:function(){var t=this.options;this._processPanels(),t.active===!1&&t.collapsible===!0||!this.headers.length?(t.active=!1,this.active=e()):t.active===!1?this._activate(0):this.active.length&&!e.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=e()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){var e=this.headers,t=this.panels;this.headers=this.element.find(this.options.header).addClass("ui-accordion-header ui-state-default ui-corner-all"),this.panels=this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide(),t&&(this._off(e.not(this.headers)),this._off(t.not(this.panels)))
},_refresh:function(){var t,i=this.options,s=i.heightStyle,n=this.element.parent();this.active=this._findActive(i.active).addClass("ui-accordion-header-active ui-state-active ui-corner-top").removeClass("ui-corner-all"),this.active.next().addClass("ui-accordion-content-active").show(),this.headers.attr("role","tab").each(function(){var t=e(this),i=t.uniqueId().attr("id"),s=t.next(),n=s.uniqueId().attr("id");t.attr("aria-controls",n),s.attr("aria-labelledby",i)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(i.event),"fill"===s?(t=n.height(),this.element.siblings(":visible").each(function(){var i=e(this),s=i.css("position");"absolute"!==s&&"fixed"!==s&&(t-=i.outerHeight(!0))}),this.headers.each(function(){t-=e(this).outerHeight(!0)}),this.headers.next().each(function(){e(this).height(Math.max(0,t-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===s&&(t=0,this.headers.next().each(function(){t=Math.max(t,e(this).css("height","").height())}).height(t))},_activate:function(t){var i=this._findActive(t)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):e()},_setupEvents:function(t){var i={keydown:"_keydown"};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var i=this.options,s=this.active,n=e(t.currentTarget),a=n[0]===s[0],o=a&&i.collapsible,r=o?e():n.next(),h=s.next(),l={oldHeader:s,oldPanel:h,newHeader:o?e():n,newPanel:r};t.preventDefault(),a&&!i.collapsible||this._trigger("beforeActivate",t,l)===!1||(i.active=o?!1:this.headers.index(n),this.active=a?e():n,this._toggle(l),s.removeClass("ui-accordion-header-active ui-state-active"),i.icons&&s.children(".ui-accordion-header-icon").removeClass(i.icons.activeHeader).addClass(i.icons.header),a||(n.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),i.icons&&n.children(".ui-accordion-header-icon").removeClass(i.icons.header).addClass(i.icons.activeHeader),n.next().addClass("ui-accordion-content-active")))},_toggle:function(t){var i=t.newPanel,s=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=s,this.options.animate?this._animate(i,s,t):(s.hide(),i.show(),this._toggleComplete(t)),s.attr({"aria-hidden":"true"}),s.prev().attr({"aria-selected":"false","aria-expanded":"false"}),i.length&&s.length?s.prev().attr({tabIndex:-1,"aria-expanded":"false"}):i.length&&this.headers.filter(function(){return 0===parseInt(e(this).attr("tabIndex"),10)}).attr("tabIndex",-1),i.attr("aria-hidden","false").prev().attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_animate:function(e,t,i){var s,n,a,o=this,r=0,h=e.css("box-sizing"),l=e.length&&(!t.length||e.index()<t.index()),u=this.options.animate||{},d=l&&u.down||u,c=function(){o._toggleComplete(i)};return"number"==typeof d&&(a=d),"string"==typeof d&&(n=d),n=n||d.easing||u.easing,a=a||d.duration||u.duration,t.length?e.length?(s=e.show().outerHeight(),t.animate(this.hideProps,{duration:a,easing:n,step:function(e,t){t.now=Math.round(e)}}),e.hide().animate(this.showProps,{duration:a,easing:n,complete:c,step:function(e,i){i.now=Math.round(e),"height"!==i.prop?"content-box"===h&&(r+=i.now):"content"!==o.options.heightStyle&&(i.now=Math.round(s-t.outerHeight()-r),r=0)}}),void 0):t.animate(this.hideProps,a,n,c):e.animate(this.showProps,a,n,c)},_toggleComplete:function(e){var t=e.oldPanel;t.removeClass("ui-accordion-content-active").prev().removeClass("ui-corner-top").addClass("ui-corner-all"),t.length&&(t.parent()[0].className=t.parent()[0].className),this._trigger("activate",null,e)}}),e.widget("ui.menu",{version:"1.11.4",defaultElement:"<ul>",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},items:"> *",menus:"ul",position:{my:"left-1 top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item":function(e){e.preventDefault()},"click .ui-menu-item":function(t){var i=e(t.target);!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&e(this.document[0].activeElement).closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(t){if(!this.previousFilter){var i=e(t.currentTarget);i.siblings(".ui-state-active").removeClass("ui-state-active"),this.focus(t,i)}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(e,t){var i=this.active||this.element.find(this.options.items).eq(0);t||this.focus(e,i)},blur:function(t){this._delay(function(){e.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){this._closeOnDocumentClick(e)&&this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-menu-icons ui-front").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").removeUniqueId().removeClass("ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var t=e(this);t.data("ui-menu-submenu-carat")&&t.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(t){var i,s,n,a,o=!0;switch(t.keyCode){case e.ui.keyCode.PAGE_UP:this.previousPage(t);break;case e.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case e.ui.keyCode.HOME:this._move("first","first",t);break;case e.ui.keyCode.END:this._move("last","last",t);break;case e.ui.keyCode.UP:this.previous(t);break;case e.ui.keyCode.DOWN:this.next(t);break;case e.ui.keyCode.LEFT:this.collapse(t);break;case e.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case e.ui.keyCode.ENTER:case e.ui.keyCode.SPACE:this._activate(t);break;case e.ui.keyCode.ESCAPE:this.collapse(t);break;default:o=!1,s=this.previousFilter||"",n=String.fromCharCode(t.keyCode),a=!1,clearTimeout(this.filterTimer),n===s?a=!0:n=s+n,i=this._filterMenuItems(n),i=a&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(n=String.fromCharCode(t.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(t,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}o&&t.preventDefault()},_activate:function(e){this.active.is(".ui-state-disabled")||(this.active.is("[aria-haspopup='true']")?this.expand(e):this.select(e))},refresh:function(){var t,i,s=this,n=this.options.icons.submenu,a=this.element.find(this.options.menus);this.element.toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length),a.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-front").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=e(this),i=t.parent(),s=e("<span>").addClass("ui-menu-icon ui-icon "+n).data("ui-menu-submenu-carat",!0);i.attr("aria-haspopup","true").prepend(s),t.attr("aria-labelledby",i.attr("id"))}),t=a.add(this.element),i=t.find(this.options.items),i.not(".ui-menu-item").each(function(){var t=e(this);s._isDivider(t)&&t.addClass("ui-widget-content ui-menu-divider")}),i.not(".ui-menu-item, .ui-menu-divider").addClass("ui-menu-item").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!e.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(e,t){"icons"===e&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(t.submenu),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},focus:function(e,t){var i,s;this.blur(e,e&&"focus"===e.type),this._scrollIntoView(t),this.active=t.first(),s=this.active.addClass("ui-state-focus").removeClass("ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),this.active.parent().closest(".ui-menu-item").addClass("ui-state-active"),e&&"keydown"===e.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=t.children(".ui-menu"),i.length&&e&&/^mouse/.test(e.type)&&this._startOpening(i),this.activeMenu=t.parent(),this._trigger("focus",e,{item:t})},_scrollIntoView:function(t){var i,s,n,a,o,r;this._hasScroll()&&(i=parseFloat(e.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(e.css(this.activeMenu[0],"paddingTop"))||0,n=t.offset().top-this.activeMenu.offset().top-i-s,a=this.activeMenu.scrollTop(),o=this.activeMenu.height(),r=t.outerHeight(),0>n?this.activeMenu.scrollTop(a+n):n+r>o&&this.activeMenu.scrollTop(a+n-o+r))},blur:function(e,t){t||clearTimeout(this.timer),this.active&&(this.active.removeClass("ui-state-focus"),this.active=null,this._trigger("blur",e,{item:this.active}))},_startOpening:function(e){clearTimeout(this.timer),"true"===e.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(e)},this.delay))},_open:function(t){var i=e.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(t,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:e(t&&t.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(t),this.activeMenu=s},this.delay)},_close:function(e){e||(e=this.active?this.active.parent():this.element),e.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find(".ui-state-active").not(".ui-state-focus").removeClass("ui-state-active")},_closeOnDocumentClick:function(t){return!e(t.target).closest(".ui-menu").length},_isDivider:function(e){return!/[^\-\u2014\u2013\s]/.test(e.text())},collapse:function(e){var t=this.active&&this.active.parent().closest(".ui-menu-item",this.element);t&&t.length&&(this._close(),this.focus(e,t))},expand:function(e){var t=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();t&&t.length&&(this._open(t.parent()),this._delay(function(){this.focus(e,t)}))},next:function(e){this._move("next","first",e)},previous:function(e){this._move("prev","last",e)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(e,t,i){var s;this.active&&(s="first"===e||"last"===e?this.active["first"===e?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[e+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[t]()),this.focus(i,s)},nextPage:function(t){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=e(this),0>i.offset().top-s-n}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(t),void 0)},previousPage:function(t){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=e(this),i.offset().top-s+n>0}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items).first())),void 0):(this.next(t),void 0)},_hasScroll:function(){return this.element.outerHeight()<this.element.prop("scrollHeight")},select:function(t){this.active=this.active||e(t.target).closest(".ui-menu-item");var i={item:this.active};this.active.has(".ui-menu").length||this.collapseAll(t,!0),this._trigger("select",t,i)},_filterMenuItems:function(t){var i=t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&"),s=RegExp("^"+i,"i");return this.activeMenu.find(this.options.items).filter(".ui-menu-item").filter(function(){return s.test(e.trim(e(this).text()))})}}),e.widget("ui.autocomplete",{version:"1.11.4",defaultElement:"<input>",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var t,i,s,n=this.element[0].nodeName.toLowerCase(),a="textarea"===n,o="input"===n;this.isMultiLine=a?!0:o?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[a||o?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return t=!0,s=!0,i=!0,void 0;t=!1,s=!1,i=!1;var a=e.ui.keyCode;switch(n.keyCode){case a.PAGE_UP:t=!0,this._move("previousPage",n);break;case a.PAGE_DOWN:t=!0,this._move("nextPage",n);break;case a.UP:t=!0,this._keyEvent("previous",n);break;case a.DOWN:t=!0,this._keyEvent("next",n);break;case a.ENTER:this.menu.active&&(t=!0,n.preventDefault(),this.menu.select(n));break;case a.TAB:this.menu.active&&this.menu.select(n);break;case a.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(t)return t=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=e.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(e){return s?(s=!1,e.preventDefault(),void 0):(this._searchTimeout(e),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(e),this._change(e),void 0)}}),this._initSource(),this.menu=e("<ul>").addClass("ui-autocomplete ui-front").appendTo(this._appendTo()).menu({role:null}).hide().menu("instance"),this._on(this.menu.element,{mousedown:function(t){t.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur});var i=this.menu.element[0];e(t.target).closest(".ui-menu-item").length||this._delay(function(){var t=this;this.document.one("mousedown",function(s){s.target===t.element[0]||s.target===i||e.contains(i,s.target)||t.close()})})},menufocus:function(t,i){var s,n;return this.isNewMenu&&(this.isNewMenu=!1,t.originalEvent&&/^mouse/.test(t.originalEvent.type))?(this.menu.blur(),this.document.one("mousemove",function(){e(t.target).trigger(t.originalEvent)}),void 0):(n=i.item.data("ui-autocomplete-item"),!1!==this._trigger("focus",t,{item:n})&&t.originalEvent&&/^key/.test(t.originalEvent.type)&&this._value(n.value),s=i.item.attr("aria-label")||n.value,s&&e.trim(s).length&&(this.liveRegion.children().hide(),e("<div>").text(s).appendTo(this.liveRegion)),void 0)},menuselect:function(e,t){var i=t.item.data("ui-autocomplete-item"),s=this.previous;this.element[0]!==this.document[0].activeElement&&(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s,this.selectedItem=i})),!1!==this._trigger("select",e,{item:i})&&this._value(i.value),this.term=this._value(),this.close(e),this.selectedItem=i}}),this.liveRegion=e("<span>",{role:"status","aria-live":"assertive","aria-relevant":"additions"}).addClass("ui-helper-hidden-accessible").appendTo(this.document[0].body),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(e,t){this._super(e,t),"source"===e&&this._initSource(),"appendTo"===e&&this.menu.element.appendTo(this._appendTo()),"disabled"===e&&t&&this.xhr&&this.xhr.abort()},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t&&t[0]||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_initSource:function(){var t,i,s=this;e.isArray(this.options.source)?(t=this.options.source,this.source=function(i,s){s(e.ui.autocomplete.filter(t,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(t,n){s.xhr&&s.xhr.abort(),s.xhr=e.ajax({url:i,data:t,dataType:"json",success:function(e){n(e)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(e){clearTimeout(this.searching),this.searching=this._delay(function(){var t=this.term===this._value(),i=this.menu.element.is(":visible"),s=e.altKey||e.ctrlKey||e.metaKey||e.shiftKey;(!t||t&&!i&&!s)&&(this.selectedItem=null,this.search(null,e))},this.options.delay)},search:function(e,t){return e=null!=e?e:this._value(),this.term=this._value(),e.length<this.options.minLength?this.close(t):this._trigger("search",t)!==!1?this._search(e):void 0},_search:function(e){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.cancelSearch=!1,this.source({term:e},this._response())},_response:function(){var t=++this.requestIndex;return e.proxy(function(e){t===this.requestIndex&&this.__response(e),this.pending--,this.pending||this.element.removeClass("ui-autocomplete-loading")},this)},__response:function(e){e&&(e=this._normalize(e)),this._trigger("response",null,{content:e}),!this.options.disabled&&e&&e.length&&!this.cancelSearch?(this._suggest(e),this._trigger("open")):this._close()},close:function(e){this.cancelSearch=!0,this._close(e)},_close:function(e){this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.blur(),this.isNewMenu=!0,this._trigger("close",e))},_change:function(e){this.previous!==this._value()&&this._trigger("change",e,{item:this.selectedItem})},_normalize:function(t){return t.length&&t[0].label&&t[0].value?t:e.map(t,function(t){return"string"==typeof t?{label:t,value:t}:e.extend({},t,{label:t.label||t.value,value:t.value||t.label})})},_suggest:function(t){var i=this.menu.element.empty();this._renderMenu(i,t),this.isNewMenu=!0,this.menu.refresh(),i.show(),this._resizeMenu(),i.position(e.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next()},_resizeMenu:function(){var e=this.menu.element;e.outerWidth(Math.max(e.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(t,i){var s=this;e.each(i,function(e,i){s._renderItemData(t,i)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-autocomplete-item",t)},_renderItem:function(t,i){return e("<li>").text(i.label).appendTo(t)},_move:function(e,t){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(e)||this.menu.isLastItem()&&/^next/.test(e)?(this.isMultiLine||this._value(this.term),this.menu.blur(),void 0):(this.menu[e](t),void 0):(this.search(null,t),void 0)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(e,t){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(e,t),t.preventDefault())}}),e.extend(e.ui.autocomplete,{escapeRegex:function(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(t,i){var s=RegExp(e.ui.autocomplete.escapeRegex(i),"i");return e.grep(t,function(e){return s.test(e.label||e.value||e)})}}),e.widget("ui.autocomplete",e.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(e){return e+(e>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(t){var i;this._superApply(arguments),this.options.disabled||this.cancelSearch||(i=t&&t.length?this.options.messages.results(t.length):this.options.messages.noResults,this.liveRegion.children().hide(),e("<div>").text(i).appendTo(this.liveRegion))}}),e.ui.autocomplete;var c,p="ui-button ui-widget ui-state-default ui-corner-all",f="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",m=function(){var t=e(this);setTimeout(function(){t.find(":ui-button").button("refresh")},1)},g=function(t){var i=t.name,s=t.form,n=e([]);return i&&(i=i.replace(/'/g,"\\'"),n=s?e(s).find("[name='"+i+"'][type=radio]"):e("[name='"+i+"'][type=radio]",t.ownerDocument).filter(function(){return!this.form})),n};e.widget("ui.button",{version:"1.11.4",defaultElement:"<button>",options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset"+this.eventNamespace).bind("reset"+this.eventNamespace,m),"boolean"!=typeof this.options.disabled?this.options.disabled=!!this.element.prop("disabled"):this.element.prop("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var t=this,i=this.options,s="checkbox"===this.type||"radio"===this.type,n=s?"":"ui-state-active";null===i.label&&(i.label="input"===this.type?this.buttonElement.val():this.buttonElement.html()),this._hoverable(this.buttonElement),this.buttonElement.addClass(p).attr("role","button").bind("mouseenter"+this.eventNamespace,function(){i.disabled||this===c&&e(this).addClass("ui-state-active")}).bind("mouseleave"+this.eventNamespace,function(){i.disabled||e(this).removeClass(n)}).bind("click"+this.eventNamespace,function(e){i.disabled&&(e.preventDefault(),e.stopImmediatePropagation())}),this._on({focus:function(){this.buttonElement.addClass("ui-state-focus")},blur:function(){this.buttonElement.removeClass("ui-state-focus")}}),s&&this.element.bind("change"+this.eventNamespace,function(){t.refresh()}),"checkbox"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){return i.disabled?!1:void 0}):"radio"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){if(i.disabled)return!1;e(this).addClass("ui-state-active"),t.buttonElement.attr("aria-pressed","true");var s=t.element[0];g(s).not(s).map(function(){return e(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown"+this.eventNamespace,function(){return i.disabled?!1:(e(this).addClass("ui-state-active"),c=this,t.document.one("mouseup",function(){c=null}),void 0)}).bind("mouseup"+this.eventNamespace,function(){return i.disabled?!1:(e(this).removeClass("ui-state-active"),void 0)}).bind("keydown"+this.eventNamespace,function(t){return i.disabled?!1:((t.keyCode===e.ui.keyCode.SPACE||t.keyCode===e.ui.keyCode.ENTER)&&e(this).addClass("ui-state-active"),void 0)}).bind("keyup"+this.eventNamespace+" blur"+this.eventNamespace,function(){e(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(t){t.keyCode===e.ui.keyCode.SPACE&&e(this).click()})),this._setOption("disabled",i.disabled),this._resetButton()},_determineButtonType:function(){var e,t,i;this.type=this.element.is("[type=checkbox]")?"checkbox":this.element.is("[type=radio]")?"radio":this.element.is("input")?"input":"button","checkbox"===this.type||"radio"===this.type?(e=this.element.parents().last(),t="label[for='"+this.element.attr("id")+"']",this.buttonElement=e.find(t),this.buttonElement.length||(e=e.length?e.siblings():this.element.siblings(),this.buttonElement=e.filter(t),this.buttonElement.length||(this.buttonElement=e.find(t))),this.element.addClass("ui-helper-hidden-accessible"),i=this.element.is(":checked"),i&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.prop("aria-pressed",i)):this.buttonElement=this.element},widget:function(){return this.buttonElement},_destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(p+" ui-state-active "+f).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title")},_setOption:function(e,t){return this._super(e,t),"disabled"===e?(this.widget().toggleClass("ui-state-disabled",!!t),this.element.prop("disabled",!!t),t&&("checkbox"===this.type||"radio"===this.type?this.buttonElement.removeClass("ui-state-focus"):this.buttonElement.removeClass("ui-state-focus ui-state-active")),void 0):(this._resetButton(),void 0)},refresh:function(){var t=this.element.is("input, button")?this.element.is(":disabled"):this.element.hasClass("ui-button-disabled");t!==this.options.disabled&&this._setOption("disabled",t),"radio"===this.type?g(this.element[0]).each(function(){e(this).is(":checked")?e(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):e(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):"checkbox"===this.type&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if("input"===this.type)return this.options.label&&this.element.val(this.options.label),void 0;var t=this.buttonElement.removeClass(f),i=e("<span></span>",this.document[0]).addClass("ui-button-text").html(this.options.label).appendTo(t.empty()).text(),s=this.options.icons,n=s.primary&&s.secondary,a=[];s.primary||s.secondary?(this.options.text&&a.push("ui-button-text-icon"+(n?"s":s.primary?"-primary":"-secondary")),s.primary&&t.prepend("<span class='ui-button-icon-primary ui-icon "+s.primary+"'></span>"),s.secondary&&t.append("<span class='ui-button-icon-secondary ui-icon "+s.secondary+"'></span>"),this.options.text||(a.push(n?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||t.attr("title",e.trim(i)))):a.push("ui-button-text-only"),t.addClass(a.join(" "))}}),e.widget("ui.buttonset",{version:"1.11.4",options:{items:"button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(e,t){"disabled"===e&&this.buttons.button("option",e,t),this._super(e,t)},refresh:function(){var t="rtl"===this.element.css("direction"),i=this.element.find(this.options.items),s=i.filter(":ui-button");i.not(":ui-button").button(),s.button("refresh"),this.buttons=i.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(t?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(t?"ui-corner-left":"ui-corner-right").end().end()},_destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy")}}),e.ui.button,e.extend(e.ui,{datepicker:{version:"1.11.4"}});var v;e.extend(n.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(e){return r(this._defaults,e||{}),this},_attachDatepicker:function(t,i){var s,n,a;s=t.nodeName.toLowerCase(),n="div"===s||"span"===s,t.id||(this.uuid+=1,t.id="dp"+this.uuid),a=this._newInst(e(t),n),a.settings=e.extend({},i||{}),"input"===s?this._connectDatepicker(t,a):n&&this._inlineDatepicker(t,a)},_newInst:function(t,i){var s=t[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:s,input:t,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:i,dpDiv:i?a(e("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(t,i){var s=e(t);i.append=e([]),i.trigger=e([]),s.hasClass(this.markerClassName)||(this._attachments(s,i),s.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp),this._autoSize(i),e.data(t,"datepicker",i),i.settings.disabled&&this._disableDatepicker(t))},_attachments:function(t,i){var s,n,a,o=this._get(i,"appendText"),r=this._get(i,"isRTL");i.append&&i.append.remove(),o&&(i.append=e("<span class='"+this._appendClass+"'>"+o+"</span>"),t[r?"before":"after"](i.append)),t.unbind("focus",this._showDatepicker),i.trigger&&i.trigger.remove(),s=this._get(i,"showOn"),("focus"===s||"both"===s)&&t.focus(this._showDatepicker),("button"===s||"both"===s)&&(n=this._get(i,"buttonText"),a=this._get(i,"buttonImage"),i.trigger=e(this._get(i,"buttonImageOnly")?e("<img/>").addClass(this._triggerClass).attr({src:a,alt:n,title:n}):e("<button type='button'></button>").addClass(this._triggerClass).html(a?e("<img/>").attr({src:a,alt:n,title:n}):n)),t[r?"before":"after"](i.trigger),i.trigger.click(function(){return e.datepicker._datepickerShowing&&e.datepicker._lastInput===t[0]?e.datepicker._hideDatepicker():e.datepicker._datepickerShowing&&e.datepicker._lastInput!==t[0]?(e.datepicker._hideDatepicker(),e.datepicker._showDatepicker(t[0])):e.datepicker._showDatepicker(t[0]),!1}))},_autoSize:function(e){if(this._get(e,"autoSize")&&!e.inline){var t,i,s,n,a=new Date(2009,11,20),o=this._get(e,"dateFormat");o.match(/[DM]/)&&(t=function(e){for(i=0,s=0,n=0;e.length>n;n++)e[n].length>i&&(i=e[n].length,s=n);return s},a.setMonth(t(this._get(e,o.match(/MM/)?"monthNames":"monthNamesShort"))),a.setDate(t(this._get(e,o.match(/DD/)?"dayNames":"dayNamesShort"))+20-a.getDay())),e.input.attr("size",this._formatDate(e,a).length)}},_inlineDatepicker:function(t,i){var s=e(t);s.hasClass(this.markerClassName)||(s.addClass(this.markerClassName).append(i.dpDiv),e.data(t,"datepicker",i),this._setDate(i,this._getDefaultDate(i),!0),this._updateDatepicker(i),this._updateAlternate(i),i.settings.disabled&&this._disableDatepicker(t),i.dpDiv.css("display","block"))},_dialogDatepicker:function(t,i,s,n,a){var o,h,l,u,d,c=this._dialogInst;return c||(this.uuid+=1,o="dp"+this.uuid,this._dialogInput=e("<input type='text' id='"+o+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.keydown(this._doKeyDown),e("body").append(this._dialogInput),c=this._dialogInst=this._newInst(this._dialogInput,!1),c.settings={},e.data(this._dialogInput[0],"datepicker",c)),r(c.settings,n||{}),i=i&&i.constructor===Date?this._formatDate(c,i):i,this._dialogInput.val(i),this._pos=a?a.length?a:[a.pageX,a.pageY]:null,this._pos||(h=document.documentElement.clientWidth,l=document.documentElement.clientHeight,u=document.documentElement.scrollLeft||document.body.scrollLeft,d=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[h/2-100+u,l/2-150+d]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),c.settings.onSelect=s,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),e.blockUI&&e.blockUI(this.dpDiv),e.data(this._dialogInput[0],"datepicker",c),this
},_destroyDatepicker:function(t){var i,s=e(t),n=e.data(t,"datepicker");s.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),e.removeData(t,"datepicker"),"input"===i?(n.append.remove(),n.trigger.remove(),s.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):("div"===i||"span"===i)&&s.removeClass(this.markerClassName).empty(),v===n&&(v=null))},_enableDatepicker:function(t){var i,s,n=e(t),a=e.data(t,"datepicker");n.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!1,a.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().removeClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}))},_disableDatepicker:function(t){var i,s,n=e(t),a=e.data(t,"datepicker");n.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!0,a.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().addClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}),this._disabledInputs[this._disabledInputs.length]=t)},_isDisabledDatepicker:function(e){if(!e)return!1;for(var t=0;this._disabledInputs.length>t;t++)if(this._disabledInputs[t]===e)return!0;return!1},_getInst:function(t){try{return e.data(t,"datepicker")}catch(i){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(t,i,s){var n,a,o,h,l=this._getInst(t);return 2===arguments.length&&"string"==typeof i?"defaults"===i?e.extend({},e.datepicker._defaults):l?"all"===i?e.extend({},l.settings):this._get(l,i):null:(n=i||{},"string"==typeof i&&(n={},n[i]=s),l&&(this._curInst===l&&this._hideDatepicker(),a=this._getDateDatepicker(t,!0),o=this._getMinMaxDate(l,"min"),h=this._getMinMaxDate(l,"max"),r(l.settings,n),null!==o&&void 0!==n.dateFormat&&void 0===n.minDate&&(l.settings.minDate=this._formatDate(l,o)),null!==h&&void 0!==n.dateFormat&&void 0===n.maxDate&&(l.settings.maxDate=this._formatDate(l,h)),"disabled"in n&&(n.disabled?this._disableDatepicker(t):this._enableDatepicker(t)),this._attachments(e(t),l),this._autoSize(l),this._setDate(l,a),this._updateAlternate(l),this._updateDatepicker(l)),void 0)},_changeDatepicker:function(e,t,i){this._optionDatepicker(e,t,i)},_refreshDatepicker:function(e){var t=this._getInst(e);t&&this._updateDatepicker(t)},_setDateDatepicker:function(e,t){var i=this._getInst(e);i&&(this._setDate(i,t),this._updateDatepicker(i),this._updateAlternate(i))},_getDateDatepicker:function(e,t){var i=this._getInst(e);return i&&!i.inline&&this._setDateFromField(i,t),i?this._getDate(i):null},_doKeyDown:function(t){var i,s,n,a=e.datepicker._getInst(t.target),o=!0,r=a.dpDiv.is(".ui-datepicker-rtl");if(a._keyEvent=!0,e.datepicker._datepickerShowing)switch(t.keyCode){case 9:e.datepicker._hideDatepicker(),o=!1;break;case 13:return n=e("td."+e.datepicker._dayOverClass+":not(."+e.datepicker._currentClass+")",a.dpDiv),n[0]&&e.datepicker._selectDay(t.target,a.selectedMonth,a.selectedYear,n[0]),i=e.datepicker._get(a,"onSelect"),i?(s=e.datepicker._formatDate(a),i.apply(a.input?a.input[0]:null,[s,a])):e.datepicker._hideDatepicker(),!1;case 27:e.datepicker._hideDatepicker();break;case 33:e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(a,"stepBigMonths"):-e.datepicker._get(a,"stepMonths"),"M");break;case 34:e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(a,"stepBigMonths"):+e.datepicker._get(a,"stepMonths"),"M");break;case 35:(t.ctrlKey||t.metaKey)&&e.datepicker._clearDate(t.target),o=t.ctrlKey||t.metaKey;break;case 36:(t.ctrlKey||t.metaKey)&&e.datepicker._gotoToday(t.target),o=t.ctrlKey||t.metaKey;break;case 37:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,r?1:-1,"D"),o=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(a,"stepBigMonths"):-e.datepicker._get(a,"stepMonths"),"M");break;case 38:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,-7,"D"),o=t.ctrlKey||t.metaKey;break;case 39:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,r?-1:1,"D"),o=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(a,"stepBigMonths"):+e.datepicker._get(a,"stepMonths"),"M");break;case 40:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,7,"D"),o=t.ctrlKey||t.metaKey;break;default:o=!1}else 36===t.keyCode&&t.ctrlKey?e.datepicker._showDatepicker(this):o=!1;o&&(t.preventDefault(),t.stopPropagation())},_doKeyPress:function(t){var i,s,n=e.datepicker._getInst(t.target);return e.datepicker._get(n,"constrainInput")?(i=e.datepicker._possibleChars(e.datepicker._get(n,"dateFormat")),s=String.fromCharCode(null==t.charCode?t.keyCode:t.charCode),t.ctrlKey||t.metaKey||" ">s||!i||i.indexOf(s)>-1):void 0},_doKeyUp:function(t){var i,s=e.datepicker._getInst(t.target);if(s.input.val()!==s.lastVal)try{i=e.datepicker.parseDate(e.datepicker._get(s,"dateFormat"),s.input?s.input.val():null,e.datepicker._getFormatConfig(s)),i&&(e.datepicker._setDateFromField(s),e.datepicker._updateAlternate(s),e.datepicker._updateDatepicker(s))}catch(n){}return!0},_showDatepicker:function(t){if(t=t.target||t,"input"!==t.nodeName.toLowerCase()&&(t=e("input",t.parentNode)[0]),!e.datepicker._isDisabledDatepicker(t)&&e.datepicker._lastInput!==t){var i,n,a,o,h,l,u;i=e.datepicker._getInst(t),e.datepicker._curInst&&e.datepicker._curInst!==i&&(e.datepicker._curInst.dpDiv.stop(!0,!0),i&&e.datepicker._datepickerShowing&&e.datepicker._hideDatepicker(e.datepicker._curInst.input[0])),n=e.datepicker._get(i,"beforeShow"),a=n?n.apply(t,[t,i]):{},a!==!1&&(r(i.settings,a),i.lastVal=null,e.datepicker._lastInput=t,e.datepicker._setDateFromField(i),e.datepicker._inDialog&&(t.value=""),e.datepicker._pos||(e.datepicker._pos=e.datepicker._findPos(t),e.datepicker._pos[1]+=t.offsetHeight),o=!1,e(t).parents().each(function(){return o|="fixed"===e(this).css("position"),!o}),h={left:e.datepicker._pos[0],top:e.datepicker._pos[1]},e.datepicker._pos=null,i.dpDiv.empty(),i.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),e.datepicker._updateDatepicker(i),h=e.datepicker._checkOffset(i,h,o),i.dpDiv.css({position:e.datepicker._inDialog&&e.blockUI?"static":o?"fixed":"absolute",display:"none",left:h.left+"px",top:h.top+"px"}),i.inline||(l=e.datepicker._get(i,"showAnim"),u=e.datepicker._get(i,"duration"),i.dpDiv.css("z-index",s(e(t))+1),e.datepicker._datepickerShowing=!0,e.effects&&e.effects.effect[l]?i.dpDiv.show(l,e.datepicker._get(i,"showOptions"),u):i.dpDiv[l||"show"](l?u:null),e.datepicker._shouldFocusInput(i)&&i.input.focus(),e.datepicker._curInst=i))}},_updateDatepicker:function(t){this.maxRows=4,v=t,t.dpDiv.empty().append(this._generateHTML(t)),this._attachHandlers(t);var i,s=this._getNumberOfMonths(t),n=s[1],a=17,r=t.dpDiv.find("."+this._dayOverClass+" a");r.length>0&&o.apply(r.get(0)),t.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),n>1&&t.dpDiv.addClass("ui-datepicker-multi-"+n).css("width",a*n+"em"),t.dpDiv[(1!==s[0]||1!==s[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),t.dpDiv[(this._get(t,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),t===e.datepicker._curInst&&e.datepicker._datepickerShowing&&e.datepicker._shouldFocusInput(t)&&t.input.focus(),t.yearshtml&&(i=t.yearshtml,setTimeout(function(){i===t.yearshtml&&t.yearshtml&&t.dpDiv.find("select.ui-datepicker-year:first").replaceWith(t.yearshtml),i=t.yearshtml=null},0))},_shouldFocusInput:function(e){return e.input&&e.input.is(":visible")&&!e.input.is(":disabled")&&!e.input.is(":focus")},_checkOffset:function(t,i,s){var n=t.dpDiv.outerWidth(),a=t.dpDiv.outerHeight(),o=t.input?t.input.outerWidth():0,r=t.input?t.input.outerHeight():0,h=document.documentElement.clientWidth+(s?0:e(document).scrollLeft()),l=document.documentElement.clientHeight+(s?0:e(document).scrollTop());return i.left-=this._get(t,"isRTL")?n-o:0,i.left-=s&&i.left===t.input.offset().left?e(document).scrollLeft():0,i.top-=s&&i.top===t.input.offset().top+r?e(document).scrollTop():0,i.left-=Math.min(i.left,i.left+n>h&&h>n?Math.abs(i.left+n-h):0),i.top-=Math.min(i.top,i.top+a>l&&l>a?Math.abs(a+r):0),i},_findPos:function(t){for(var i,s=this._getInst(t),n=this._get(s,"isRTL");t&&("hidden"===t.type||1!==t.nodeType||e.expr.filters.hidden(t));)t=t[n?"previousSibling":"nextSibling"];return i=e(t).offset(),[i.left,i.top]},_hideDatepicker:function(t){var i,s,n,a,o=this._curInst;!o||t&&o!==e.data(t,"datepicker")||this._datepickerShowing&&(i=this._get(o,"showAnim"),s=this._get(o,"duration"),n=function(){e.datepicker._tidyDialog(o)},e.effects&&(e.effects.effect[i]||e.effects[i])?o.dpDiv.hide(i,e.datepicker._get(o,"showOptions"),s,n):o.dpDiv["slideDown"===i?"slideUp":"fadeIn"===i?"fadeOut":"hide"](i?s:null,n),i||n(),this._datepickerShowing=!1,a=this._get(o,"onClose"),a&&a.apply(o.input?o.input[0]:null,[o.input?o.input.val():"",o]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),e.blockUI&&(e.unblockUI(),e("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(e){e.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(t){if(e.datepicker._curInst){var i=e(t.target),s=e.datepicker._getInst(i[0]);(i[0].id!==e.datepicker._mainDivId&&0===i.parents("#"+e.datepicker._mainDivId).length&&!i.hasClass(e.datepicker.markerClassName)&&!i.closest("."+e.datepicker._triggerClass).length&&e.datepicker._datepickerShowing&&(!e.datepicker._inDialog||!e.blockUI)||i.hasClass(e.datepicker.markerClassName)&&e.datepicker._curInst!==s)&&e.datepicker._hideDatepicker()}},_adjustDate:function(t,i,s){var n=e(t),a=this._getInst(n[0]);this._isDisabledDatepicker(n[0])||(this._adjustInstDate(a,i+("M"===s?this._get(a,"showCurrentAtPos"):0),s),this._updateDatepicker(a))},_gotoToday:function(t){var i,s=e(t),n=this._getInst(s[0]);this._get(n,"gotoCurrent")&&n.currentDay?(n.selectedDay=n.currentDay,n.drawMonth=n.selectedMonth=n.currentMonth,n.drawYear=n.selectedYear=n.currentYear):(i=new Date,n.selectedDay=i.getDate(),n.drawMonth=n.selectedMonth=i.getMonth(),n.drawYear=n.selectedYear=i.getFullYear()),this._notifyChange(n),this._adjustDate(s)},_selectMonthYear:function(t,i,s){var n=e(t),a=this._getInst(n[0]);a["selected"+("M"===s?"Month":"Year")]=a["draw"+("M"===s?"Month":"Year")]=parseInt(i.options[i.selectedIndex].value,10),this._notifyChange(a),this._adjustDate(n)},_selectDay:function(t,i,s,n){var a,o=e(t);e(n).hasClass(this._unselectableClass)||this._isDisabledDatepicker(o[0])||(a=this._getInst(o[0]),a.selectedDay=a.currentDay=e("a",n).html(),a.selectedMonth=a.currentMonth=i,a.selectedYear=a.currentYear=s,this._selectDate(t,this._formatDate(a,a.currentDay,a.currentMonth,a.currentYear)))},_clearDate:function(t){var i=e(t);this._selectDate(i,"")},_selectDate:function(t,i){var s,n=e(t),a=this._getInst(n[0]);i=null!=i?i:this._formatDate(a),a.input&&a.input.val(i),this._updateAlternate(a),s=this._get(a,"onSelect"),s?s.apply(a.input?a.input[0]:null,[i,a]):a.input&&a.input.trigger("change"),a.inline?this._updateDatepicker(a):(this._hideDatepicker(),this._lastInput=a.input[0],"object"!=typeof a.input[0]&&a.input.focus(),this._lastInput=null)},_updateAlternate:function(t){var i,s,n,a=this._get(t,"altField");a&&(i=this._get(t,"altFormat")||this._get(t,"dateFormat"),s=this._getDate(t),n=this.formatDate(i,s,this._getFormatConfig(t)),e(a).each(function(){e(this).val(n)}))},noWeekends:function(e){var t=e.getDay();return[t>0&&6>t,""]},iso8601Week:function(e){var t,i=new Date(e.getTime());return i.setDate(i.getDate()+4-(i.getDay()||7)),t=i.getTime(),i.setMonth(0),i.setDate(1),Math.floor(Math.round((t-i)/864e5)/7)+1},parseDate:function(t,i,s){if(null==t||null==i)throw"Invalid arguments";if(i="object"==typeof i?""+i:i+"",""===i)return null;var n,a,o,r,h=0,l=(s?s.shortYearCutoff:null)||this._defaults.shortYearCutoff,u="string"!=typeof l?l:(new Date).getFullYear()%100+parseInt(l,10),d=(s?s.dayNamesShort:null)||this._defaults.dayNamesShort,c=(s?s.dayNames:null)||this._defaults.dayNames,p=(s?s.monthNamesShort:null)||this._defaults.monthNamesShort,f=(s?s.monthNames:null)||this._defaults.monthNames,m=-1,g=-1,v=-1,y=-1,b=!1,_=function(e){var i=t.length>n+1&&t.charAt(n+1)===e;return i&&n++,i},x=function(e){var t=_(e),s="@"===e?14:"!"===e?20:"y"===e&&t?4:"o"===e?3:2,n="y"===e?s:1,a=RegExp("^\\d{"+n+","+s+"}"),o=i.substring(h).match(a);if(!o)throw"Missing number at position "+h;return h+=o[0].length,parseInt(o[0],10)},w=function(t,s,n){var a=-1,o=e.map(_(t)?n:s,function(e,t){return[[t,e]]}).sort(function(e,t){return-(e[1].length-t[1].length)});if(e.each(o,function(e,t){var s=t[1];return i.substr(h,s.length).toLowerCase()===s.toLowerCase()?(a=t[0],h+=s.length,!1):void 0}),-1!==a)return a+1;throw"Unknown name at position "+h},k=function(){if(i.charAt(h)!==t.charAt(n))throw"Unexpected literal at position "+h;h++};for(n=0;t.length>n;n++)if(b)"'"!==t.charAt(n)||_("'")?k():b=!1;else switch(t.charAt(n)){case"d":v=x("d");break;case"D":w("D",d,c);break;case"o":y=x("o");break;case"m":g=x("m");break;case"M":g=w("M",p,f);break;case"y":m=x("y");break;case"@":r=new Date(x("@")),m=r.getFullYear(),g=r.getMonth()+1,v=r.getDate();break;case"!":r=new Date((x("!")-this._ticksTo1970)/1e4),m=r.getFullYear(),g=r.getMonth()+1,v=r.getDate();break;case"'":_("'")?k():b=!0;break;default:k()}if(i.length>h&&(o=i.substr(h),!/^\s+/.test(o)))throw"Extra/unparsed characters found in date: "+o;if(-1===m?m=(new Date).getFullYear():100>m&&(m+=(new Date).getFullYear()-(new Date).getFullYear()%100+(u>=m?0:-100)),y>-1)for(g=1,v=y;;){if(a=this._getDaysInMonth(m,g-1),a>=v)break;g++,v-=a}if(r=this._daylightSavingAdjust(new Date(m,g-1,v)),r.getFullYear()!==m||r.getMonth()+1!==g||r.getDate()!==v)throw"Invalid date";return r},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(e,t,i){if(!t)return"";var s,n=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,a=(i?i.dayNames:null)||this._defaults.dayNames,o=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,r=(i?i.monthNames:null)||this._defaults.monthNames,h=function(t){var i=e.length>s+1&&e.charAt(s+1)===t;return i&&s++,i},l=function(e,t,i){var s=""+t;if(h(e))for(;i>s.length;)s="0"+s;return s},u=function(e,t,i,s){return h(e)?s[t]:i[t]},d="",c=!1;if(t)for(s=0;e.length>s;s++)if(c)"'"!==e.charAt(s)||h("'")?d+=e.charAt(s):c=!1;else switch(e.charAt(s)){case"d":d+=l("d",t.getDate(),2);break;case"D":d+=u("D",t.getDay(),n,a);break;case"o":d+=l("o",Math.round((new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()-new Date(t.getFullYear(),0,0).getTime())/864e5),3);break;case"m":d+=l("m",t.getMonth()+1,2);break;case"M":d+=u("M",t.getMonth(),o,r);break;case"y":d+=h("y")?t.getFullYear():(10>t.getYear()%100?"0":"")+t.getYear()%100;break;case"@":d+=t.getTime();break;case"!":d+=1e4*t.getTime()+this._ticksTo1970;break;case"'":h("'")?d+="'":c=!0;break;default:d+=e.charAt(s)}return d},_possibleChars:function(e){var t,i="",s=!1,n=function(i){var s=e.length>t+1&&e.charAt(t+1)===i;return s&&t++,s};for(t=0;e.length>t;t++)if(s)"'"!==e.charAt(t)||n("'")?i+=e.charAt(t):s=!1;else switch(e.charAt(t)){case"d":case"m":case"y":case"@":i+="0123456789";break;case"D":case"M":return null;case"'":n("'")?i+="'":s=!0;break;default:i+=e.charAt(t)}return i},_get:function(e,t){return void 0!==e.settings[t]?e.settings[t]:this._defaults[t]},_setDateFromField:function(e,t){if(e.input.val()!==e.lastVal){var i=this._get(e,"dateFormat"),s=e.lastVal=e.input?e.input.val():null,n=this._getDefaultDate(e),a=n,o=this._getFormatConfig(e);try{a=this.parseDate(i,s,o)||n}catch(r){s=t?"":s}e.selectedDay=a.getDate(),e.drawMonth=e.selectedMonth=a.getMonth(),e.drawYear=e.selectedYear=a.getFullYear(),e.currentDay=s?a.getDate():0,e.currentMonth=s?a.getMonth():0,e.currentYear=s?a.getFullYear():0,this._adjustInstDate(e)}},_getDefaultDate:function(e){return this._restrictMinMax(e,this._determineDate(e,this._get(e,"defaultDate"),new Date))},_determineDate:function(t,i,s){var n=function(e){var t=new Date;return t.setDate(t.getDate()+e),t},a=function(i){try{return e.datepicker.parseDate(e.datepicker._get(t,"dateFormat"),i,e.datepicker._getFormatConfig(t))}catch(s){}for(var n=(i.toLowerCase().match(/^c/)?e.datepicker._getDate(t):null)||new Date,a=n.getFullYear(),o=n.getMonth(),r=n.getDate(),h=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,l=h.exec(i);l;){switch(l[2]||"d"){case"d":case"D":r+=parseInt(l[1],10);break;case"w":case"W":r+=7*parseInt(l[1],10);break;case"m":case"M":o+=parseInt(l[1],10),r=Math.min(r,e.datepicker._getDaysInMonth(a,o));break;case"y":case"Y":a+=parseInt(l[1],10),r=Math.min(r,e.datepicker._getDaysInMonth(a,o))}l=h.exec(i)}return new Date(a,o,r)},o=null==i||""===i?s:"string"==typeof i?a(i):"number"==typeof i?isNaN(i)?s:n(i):new Date(i.getTime());return o=o&&"Invalid Date"==""+o?s:o,o&&(o.setHours(0),o.setMinutes(0),o.setSeconds(0),o.setMilliseconds(0)),this._daylightSavingAdjust(o)},_daylightSavingAdjust:function(e){return e?(e.setHours(e.getHours()>12?e.getHours()+2:0),e):null},_setDate:function(e,t,i){var s=!t,n=e.selectedMonth,a=e.selectedYear,o=this._restrictMinMax(e,this._determineDate(e,t,new Date));e.selectedDay=e.currentDay=o.getDate(),e.drawMonth=e.selectedMonth=e.currentMonth=o.getMonth(),e.drawYear=e.selectedYear=e.currentYear=o.getFullYear(),n===e.selectedMonth&&a===e.selectedYear||i||this._notifyChange(e),this._adjustInstDate(e),e.input&&e.input.val(s?"":this._formatDate(e))},_getDate:function(e){var t=!e.currentYear||e.input&&""===e.input.val()?null:this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return t},_attachHandlers:function(t){var i=this._get(t,"stepMonths"),s="#"+t.id.replace(/\\\\/g,"\\");t.dpDiv.find("[data-handler]").map(function(){var t={prev:function(){e.datepicker._adjustDate(s,-i,"M")},next:function(){e.datepicker._adjustDate(s,+i,"M")},hide:function(){e.datepicker._hideDatepicker()},today:function(){e.datepicker._gotoToday(s)},selectDay:function(){return e.datepicker._selectDay(s,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return e.datepicker._selectMonthYear(s,this,"M"),!1},selectYear:function(){return e.datepicker._selectMonthYear(s,this,"Y"),!1}};e(this).bind(this.getAttribute("data-event"),t[this.getAttribute("data-handler")])})},_generateHTML:function(e){var t,i,s,n,a,o,r,h,l,u,d,c,p,f,m,g,v,y,b,_,x,w,k,T,D,S,N,M,C,A,P,I,H,z,F,E,W,L,O,j=new Date,R=this._daylightSavingAdjust(new Date(j.getFullYear(),j.getMonth(),j.getDate())),Y=this._get(e,"isRTL"),J=this._get(e,"showButtonPanel"),B=this._get(e,"hideIfNoPrevNext"),K=this._get(e,"navigationAsDateFormat"),V=this._getNumberOfMonths(e),U=this._get(e,"showCurrentAtPos"),q=this._get(e,"stepMonths"),G=1!==V[0]||1!==V[1],X=this._daylightSavingAdjust(e.currentDay?new Date(e.currentYear,e.currentMonth,e.currentDay):new Date(9999,9,9)),Q=this._getMinMaxDate(e,"min"),$=this._getMinMaxDate(e,"max"),Z=e.drawMonth-U,et=e.drawYear;if(0>Z&&(Z+=12,et--),$)for(t=this._daylightSavingAdjust(new Date($.getFullYear(),$.getMonth()-V[0]*V[1]+1,$.getDate())),t=Q&&Q>t?Q:t;this._daylightSavingAdjust(new Date(et,Z,1))>t;)Z--,0>Z&&(Z=11,et--);for(e.drawMonth=Z,e.drawYear=et,i=this._get(e,"prevText"),i=K?this.formatDate(i,this._daylightSavingAdjust(new Date(et,Z-q,1)),this._getFormatConfig(e)):i,s=this._canAdjustMonth(e,-1,et,Z)?"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"e":"w")+"'>"+i+"</span></a>":B?"":"<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"e":"w")+"'>"+i+"</span></a>",n=this._get(e,"nextText"),n=K?this.formatDate(n,this._daylightSavingAdjust(new Date(et,Z+q,1)),this._getFormatConfig(e)):n,a=this._canAdjustMonth(e,1,et,Z)?"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"w":"e")+"'>"+n+"</span></a>":B?"":"<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"w":"e")+"'>"+n+"</span></a>",o=this._get(e,"currentText"),r=this._get(e,"gotoCurrent")&&e.currentDay?X:R,o=K?this.formatDate(o,r,this._getFormatConfig(e)):o,h=e.inline?"":"<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>"+this._get(e,"closeText")+"</button>",l=J?"<div class='ui-datepicker-buttonpane ui-widget-content'>"+(Y?h:"")+(this._isInRange(e,r)?"<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'>"+o+"</button>":"")+(Y?"":h)+"</div>":"",u=parseInt(this._get(e,"firstDay"),10),u=isNaN(u)?0:u,d=this._get(e,"showWeek"),c=this._get(e,"dayNames"),p=this._get(e,"dayNamesMin"),f=this._get(e,"monthNames"),m=this._get(e,"monthNamesShort"),g=this._get(e,"beforeShowDay"),v=this._get(e,"showOtherMonths"),y=this._get(e,"selectOtherMonths"),b=this._getDefaultDate(e),_="",w=0;V[0]>w;w++){for(k="",this.maxRows=4,T=0;V[1]>T;T++){if(D=this._daylightSavingAdjust(new Date(et,Z,e.selectedDay)),S=" ui-corner-all",N="",G){if(N+="<div class='ui-datepicker-group",V[1]>1)switch(T){case 0:N+=" ui-datepicker-group-first",S=" ui-corner-"+(Y?"right":"left");break;case V[1]-1:N+=" ui-datepicker-group-last",S=" ui-corner-"+(Y?"left":"right");break;default:N+=" ui-datepicker-group-middle",S=""}N+="'>"}for(N+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+S+"'>"+(/all|left/.test(S)&&0===w?Y?a:s:"")+(/all|right/.test(S)&&0===w?Y?s:a:"")+this._generateMonthYearHeader(e,Z,et,Q,$,w>0||T>0,f,m)+"</div><table class='ui-datepicker-calendar'><thead>"+"<tr>",M=d?"<th class='ui-datepicker-week-col'>"+this._get(e,"weekHeader")+"</th>":"",x=0;7>x;x++)C=(x+u)%7,M+="<th scope='col'"+((x+u+6)%7>=5?" class='ui-datepicker-week-end'":"")+">"+"<span title='"+c[C]+"'>"+p[C]+"</span></th>";for(N+=M+"</tr></thead><tbody>",A=this._getDaysInMonth(et,Z),et===e.selectedYear&&Z===e.selectedMonth&&(e.selectedDay=Math.min(e.selectedDay,A)),P=(this._getFirstDayOfMonth(et,Z)-u+7)%7,I=Math.ceil((P+A)/7),H=G?this.maxRows>I?this.maxRows:I:I,this.maxRows=H,z=this._daylightSavingAdjust(new Date(et,Z,1-P)),F=0;H>F;F++){for(N+="<tr>",E=d?"<td class='ui-datepicker-week-col'>"+this._get(e,"calculateWeek")(z)+"</td>":"",x=0;7>x;x++)W=g?g.apply(e.input?e.input[0]:null,[z]):[!0,""],L=z.getMonth()!==Z,O=L&&!y||!W[0]||Q&&Q>z||$&&z>$,E+="<td class='"+((x+u+6)%7>=5?" ui-datepicker-week-end":"")+(L?" ui-datepicker-other-month":"")+(z.getTime()===D.getTime()&&Z===e.selectedMonth&&e._keyEvent||b.getTime()===z.getTime()&&b.getTime()===D.getTime()?" "+this._dayOverClass:"")+(O?" "+this._unselectableClass+" ui-state-disabled":"")+(L&&!v?"":" "+W[1]+(z.getTime()===X.getTime()?" "+this._currentClass:"")+(z.getTime()===R.getTime()?" ui-datepicker-today":""))+"'"+(L&&!v||!W[2]?"":" title='"+W[2].replace(/'/g,"&#39;")+"'")+(O?"":" data-handler='selectDay' data-event='click' data-month='"+z.getMonth()+"' data-year='"+z.getFullYear()+"'")+">"+(L&&!v?"&#xa0;":O?"<span class='ui-state-default'>"+z.getDate()+"</span>":"<a class='ui-state-default"+(z.getTime()===R.getTime()?" ui-state-highlight":"")+(z.getTime()===X.getTime()?" ui-state-active":"")+(L?" ui-priority-secondary":"")+"' href='#'>"+z.getDate()+"</a>")+"</td>",z.setDate(z.getDate()+1),z=this._daylightSavingAdjust(z);N+=E+"</tr>"}Z++,Z>11&&(Z=0,et++),N+="</tbody></table>"+(G?"</div>"+(V[0]>0&&T===V[1]-1?"<div class='ui-datepicker-row-break'></div>":""):""),k+=N}_+=k}return _+=l,e._keyEvent=!1,_},_generateMonthYearHeader:function(e,t,i,s,n,a,o,r){var h,l,u,d,c,p,f,m,g=this._get(e,"changeMonth"),v=this._get(e,"changeYear"),y=this._get(e,"showMonthAfterYear"),b="<div class='ui-datepicker-title'>",_="";if(a||!g)_+="<span class='ui-datepicker-month'>"+o[t]+"</span>";else{for(h=s&&s.getFullYear()===i,l=n&&n.getFullYear()===i,_+="<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>",u=0;12>u;u++)(!h||u>=s.getMonth())&&(!l||n.getMonth()>=u)&&(_+="<option value='"+u+"'"+(u===t?" selected='selected'":"")+">"+r[u]+"</option>");_+="</select>"}if(y||(b+=_+(!a&&g&&v?"":"&#xa0;")),!e.yearshtml)if(e.yearshtml="",a||!v)b+="<span class='ui-datepicker-year'>"+i+"</span>";else{for(d=this._get(e,"yearRange").split(":"),c=(new Date).getFullYear(),p=function(e){var t=e.match(/c[+\-].*/)?i+parseInt(e.substring(1),10):e.match(/[+\-].*/)?c+parseInt(e,10):parseInt(e,10);return isNaN(t)?c:t},f=p(d[0]),m=Math.max(f,p(d[1]||"")),f=s?Math.max(f,s.getFullYear()):f,m=n?Math.min(m,n.getFullYear()):m,e.yearshtml+="<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";m>=f;f++)e.yearshtml+="<option value='"+f+"'"+(f===i?" selected='selected'":"")+">"+f+"</option>";e.yearshtml+="</select>",b+=e.yearshtml,e.yearshtml=null}return b+=this._get(e,"yearSuffix"),y&&(b+=(!a&&g&&v?"":"&#xa0;")+_),b+="</div>"},_adjustInstDate:function(e,t,i){var s=e.drawYear+("Y"===i?t:0),n=e.drawMonth+("M"===i?t:0),a=Math.min(e.selectedDay,this._getDaysInMonth(s,n))+("D"===i?t:0),o=this._restrictMinMax(e,this._daylightSavingAdjust(new Date(s,n,a)));e.selectedDay=o.getDate(),e.drawMonth=e.selectedMonth=o.getMonth(),e.drawYear=e.selectedYear=o.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(e)},_restrictMinMax:function(e,t){var i=this._getMinMaxDate(e,"min"),s=this._getMinMaxDate(e,"max"),n=i&&i>t?i:t;return s&&n>s?s:n},_notifyChange:function(e){var t=this._get(e,"onChangeMonthYear");t&&t.apply(e.input?e.input[0]:null,[e.selectedYear,e.selectedMonth+1,e])},_getNumberOfMonths:function(e){var t=this._get(e,"numberOfMonths");return null==t?[1,1]:"number"==typeof t?[1,t]:t},_getMinMaxDate:function(e,t){return this._determineDate(e,this._get(e,t+"Date"),null)},_getDaysInMonth:function(e,t){return 32-this._daylightSavingAdjust(new Date(e,t,32)).getDate()},_getFirstDayOfMonth:function(e,t){return new Date(e,t,1).getDay()},_canAdjustMonth:function(e,t,i,s){var n=this._getNumberOfMonths(e),a=this._daylightSavingAdjust(new Date(i,s+(0>t?t:n[0]*n[1]),1));return 0>t&&a.setDate(this._getDaysInMonth(a.getFullYear(),a.getMonth())),this._isInRange(e,a)},_isInRange:function(e,t){var i,s,n=this._getMinMaxDate(e,"min"),a=this._getMinMaxDate(e,"max"),o=null,r=null,h=this._get(e,"yearRange");return h&&(i=h.split(":"),s=(new Date).getFullYear(),o=parseInt(i[0],10),r=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(o+=s),i[1].match(/[+\-].*/)&&(r+=s)),(!n||t.getTime()>=n.getTime())&&(!a||t.getTime()<=a.getTime())&&(!o||t.getFullYear()>=o)&&(!r||r>=t.getFullYear())},_getFormatConfig:function(e){var t=this._get(e,"shortYearCutoff");return t="string"!=typeof t?t:(new Date).getFullYear()%100+parseInt(t,10),{shortYearCutoff:t,dayNamesShort:this._get(e,"dayNamesShort"),dayNames:this._get(e,"dayNames"),monthNamesShort:this._get(e,"monthNamesShort"),monthNames:this._get(e,"monthNames")}},_formatDate:function(e,t,i,s){t||(e.currentDay=e.selectedDay,e.currentMonth=e.selectedMonth,e.currentYear=e.selectedYear);var n=t?"object"==typeof t?t:this._daylightSavingAdjust(new Date(s,i,t)):this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return this.formatDate(this._get(e,"dateFormat"),n,this._getFormatConfig(e))}}),e.fn.datepicker=function(t){if(!this.length)return this;e.datepicker.initialized||(e(document).mousedown(e.datepicker._checkExternalClick),e.datepicker.initialized=!0),0===e("#"+e.datepicker._mainDivId).length&&e("body").append(e.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof t||"isDisabled"!==t&&"getDate"!==t&&"widget"!==t?"option"===t&&2===arguments.length&&"string"==typeof arguments[1]?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof t?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this].concat(i)):e.datepicker._attachDatepicker(this,t)}):e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i))},e.datepicker=new n,e.datepicker.initialized=!1,e.datepicker.uuid=(new Date).getTime(),e.datepicker.version="1.11.4",e.datepicker,e.widget("ui.dialog",{version:"1.11.4",options:{appendTo:"body",autoOpen:!0,buttons:[],closeOnEscape:!0,closeText:"Close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var i=e(this).css(t).offset().top;0>i&&e(this).css("top",t.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},sizeRelatedOptions:{buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},resizableRelatedOptions:{maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),this.options.title=this.options.title||this.originalTitle,this._createWrapper(),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(this.uiDialog),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&e.fn.draggable&&this._makeDraggable(),this.options.resizable&&e.fn.resizable&&this._makeResizable(),this._isOpen=!1,this._trackFocus()},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var t=this.options.appendTo;return t&&(t.jquery||t.nodeType)?e(t):this.document.find(t||"body").eq(0)},_destroy:function(){var e,t=this.originalPosition;this._untrackInstance(),this._destroyOverlay(),this.element.removeUniqueId().removeClass("ui-dialog-content ui-widget-content").css(this.originalCss).detach(),this.uiDialog.stop(!0,!0).remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),e=t.parent.children().eq(t.index),e.length&&e[0]!==this.element[0]?e.before(this.element):t.parent.append(this.element)},widget:function(){return this.uiDialog},disable:e.noop,enable:e.noop,close:function(t){var i,s=this;if(this._isOpen&&this._trigger("beforeClose",t)!==!1){if(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),!this.opener.filter(":focusable").focus().length)try{i=this.document[0].activeElement,i&&"body"!==i.nodeName.toLowerCase()&&e(i).blur()}catch(n){}this._hide(this.uiDialog,this.options.hide,function(){s._trigger("close",t)})}},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(t,i){var s=!1,n=this.uiDialog.siblings(".ui-front:visible").map(function(){return+e(this).css("z-index")}).get(),a=Math.max.apply(null,n);return a>=+this.uiDialog.css("z-index")&&(this.uiDialog.css("z-index",a+1),s=!0),s&&!i&&this._trigger("focus",t),s},open:function(){var t=this;
return this._isOpen?(this._moveToTop()&&this._focusTabbable(),void 0):(this._isOpen=!0,this.opener=e(this.document[0].activeElement),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this.overlay&&this.overlay.css("z-index",this.uiDialog.css("z-index")-1),this._show(this.uiDialog,this.options.show,function(){t._focusTabbable(),t._trigger("focus")}),this._makeFocusTarget(),this._trigger("open"),void 0)},_focusTabbable:function(){var e=this._focusedElement;e||(e=this.element.find("[autofocus]")),e.length||(e=this.element.find(":tabbable")),e.length||(e=this.uiDialogButtonPane.find(":tabbable")),e.length||(e=this.uiDialogTitlebarClose.filter(":tabbable")),e.length||(e=this.uiDialog),e.eq(0).focus()},_keepFocus:function(t){function i(){var t=this.document[0].activeElement,i=this.uiDialog[0]===t||e.contains(this.uiDialog[0],t);i||this._focusTabbable()}t.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=e("<div>").addClass("ui-dialog ui-widget ui-widget-content ui-corner-all ui-front "+this.options.dialogClass).hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._on(this.uiDialog,{keydown:function(t){if(this.options.closeOnEscape&&!t.isDefaultPrevented()&&t.keyCode&&t.keyCode===e.ui.keyCode.ESCAPE)return t.preventDefault(),this.close(t),void 0;if(t.keyCode===e.ui.keyCode.TAB&&!t.isDefaultPrevented()){var i=this.uiDialog.find(":tabbable"),s=i.filter(":first"),n=i.filter(":last");t.target!==n[0]&&t.target!==this.uiDialog[0]||t.shiftKey?t.target!==s[0]&&t.target!==this.uiDialog[0]||!t.shiftKey||(this._delay(function(){n.focus()}),t.preventDefault()):(this._delay(function(){s.focus()}),t.preventDefault())}},mousedown:function(e){this._moveToTop(e)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var t;this.uiDialogTitlebar=e("<div>").addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(this.uiDialog),this._on(this.uiDialogTitlebar,{mousedown:function(t){e(t.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.focus()}}),this.uiDialogTitlebarClose=e("<button type='button'></button>").button({label:this.options.closeText,icons:{primary:"ui-icon-closethick"},text:!1}).addClass("ui-dialog-titlebar-close").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(e){e.preventDefault(),this.close(e)}}),t=e("<span>").uniqueId().addClass("ui-dialog-title").prependTo(this.uiDialogTitlebar),this._title(t),this.uiDialog.attr({"aria-labelledby":t.attr("id")})},_title:function(e){this.options.title||e.html("&#160;"),e.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=e("<div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),this.uiButtonSet=e("<div>").addClass("ui-dialog-buttonset").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var t=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),e.isEmptyObject(i)||e.isArray(i)&&!i.length?(this.uiDialog.removeClass("ui-dialog-buttons"),void 0):(e.each(i,function(i,s){var n,a;s=e.isFunction(s)?{click:s,text:i}:s,s=e.extend({type:"button"},s),n=s.click,s.click=function(){n.apply(t.element[0],arguments)},a={icons:s.icons,text:s.showText},delete s.icons,delete s.showText,e("<button></button>",s).button(a).appendTo(t.uiButtonSet)}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function t(e){return{position:e.position,offset:e.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(s,n){e(this).addClass("ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",s,t(n))},drag:function(e,s){i._trigger("drag",e,t(s))},stop:function(n,a){var o=a.offset.left-i.document.scrollLeft(),r=a.offset.top-i.document.scrollTop();s.position={my:"left top",at:"left"+(o>=0?"+":"")+o+" "+"top"+(r>=0?"+":"")+r,of:i.window},e(this).removeClass("ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",n,t(a))}})},_makeResizable:function(){function t(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}var i=this,s=this.options,n=s.resizable,a=this.uiDialog.css("position"),o="string"==typeof n?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:o,start:function(s,n){e(this).addClass("ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",s,t(n))},resize:function(e,s){i._trigger("resize",e,t(s))},stop:function(n,a){var o=i.uiDialog.offset(),r=o.left-i.document.scrollLeft(),h=o.top-i.document.scrollTop();s.height=i.uiDialog.height(),s.width=i.uiDialog.width(),s.position={my:"left top",at:"left"+(r>=0?"+":"")+r+" "+"top"+(h>=0?"+":"")+h,of:i.window},e(this).removeClass("ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",n,t(a))}}).css("position",a)},_trackFocus:function(){this._on(this.widget(),{focusin:function(t){this._makeFocusTarget(),this._focusedElement=e(t.target)}})},_makeFocusTarget:function(){this._untrackInstance(),this._trackingInstances().unshift(this)},_untrackInstance:function(){var t=this._trackingInstances(),i=e.inArray(this,t);-1!==i&&t.splice(i,1)},_trackingInstances:function(){var e=this.document.data("ui-dialog-instances");return e||(e=[],this.document.data("ui-dialog-instances",e)),e},_minHeight:function(){var e=this.options;return"auto"===e.height?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(){var e=this.uiDialog.is(":visible");e||this.uiDialog.show(),this.uiDialog.position(this.options.position),e||this.uiDialog.hide()},_setOptions:function(t){var i=this,s=!1,n={};e.each(t,function(e,t){i._setOption(e,t),e in i.sizeRelatedOptions&&(s=!0),e in i.resizableRelatedOptions&&(n[e]=t)}),s&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",n)},_setOption:function(e,t){var i,s,n=this.uiDialog;"dialogClass"===e&&n.removeClass(this.options.dialogClass).addClass(t),"disabled"!==e&&(this._super(e,t),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:""+t}),"draggable"===e&&(i=n.is(":data(ui-draggable)"),i&&!t&&n.draggable("destroy"),!i&&t&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(s=n.is(":data(ui-resizable)"),s&&!t&&n.resizable("destroy"),s&&"string"==typeof t&&n.resizable("option","handles",t),s||t===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var e,t,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),e=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),t=Math.max(0,s.minHeight-e),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-e):"none","auto"===s.height?this.element.css({minHeight:t,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-e)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var t=e(this);return e("<div>").css({position:"absolute",width:t.outerWidth(),height:t.outerHeight()}).appendTo(t.parent()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(t){return e(t.target).closest(".ui-dialog").length?!0:!!e(t.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var t=!0;this._delay(function(){t=!1}),this.document.data("ui-dialog-overlays")||this._on(this.document,{focusin:function(e){t||this._allowInteraction(e)||(e.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=e("<div>").addClass("ui-widget-overlay ui-front").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var e=this.document.data("ui-dialog-overlays")-1;e?this.document.data("ui-dialog-overlays",e):this.document.unbind("focusin").removeData("ui-dialog-overlays"),this.overlay.remove(),this.overlay=null}}}),e.widget("ui.progressbar",{version:"1.11.4",options:{max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min}),this.valueDiv=e("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(e){return void 0===e?this.options.value:(this.options.value=this._constrainedValue(e),this._refreshValue(),void 0)},_constrainedValue:function(e){return void 0===e&&(e=this.options.value),this.indeterminate=e===!1,"number"!=typeof e&&(e=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,e))},_setOptions:function(e){var t=e.value;delete e.value,this._super(e),this.options.value=this._constrainedValue(t),this._refreshValue()},_setOption:function(e,t){"max"===e&&(t=Math.max(this.min,t)),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var t=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||t>this.min).toggleClass("ui-corner-right",t===this.options.max).width(i.toFixed(0)+"%"),this.element.toggleClass("ui-progressbar-indeterminate",this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=e("<div class='ui-progressbar-overlay'></div>").appendTo(this.valueDiv))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":t}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==t&&(this.oldValue=t,this._trigger("change")),t===this.options.max&&this._trigger("complete")}}),e.widget("ui.selectmenu",{version:"1.11.4",defaultElement:"<select>",options:{appendTo:null,disabled:null,icons:{button:"ui-icon-triangle-1-s"},position:{my:"left top",at:"left bottom",collision:"none"},width:null,change:null,close:null,focus:null,open:null,select:null},_create:function(){var e=this.element.uniqueId().attr("id");this.ids={element:e,button:e+"-button",menu:e+"-menu"},this._drawButton(),this._drawMenu(),this.options.disabled&&this.disable()},_drawButton:function(){var t=this;this.label=e("label[for='"+this.ids.element+"']").attr("for",this.ids.button),this._on(this.label,{click:function(e){this.button.focus(),e.preventDefault()}}),this.element.hide(),this.button=e("<span>",{"class":"ui-selectmenu-button ui-widget ui-state-default ui-corner-all",tabindex:this.options.disabled?-1:0,id:this.ids.button,role:"combobox","aria-expanded":"false","aria-autocomplete":"list","aria-owns":this.ids.menu,"aria-haspopup":"true"}).insertAfter(this.element),e("<span>",{"class":"ui-icon "+this.options.icons.button}).prependTo(this.button),this.buttonText=e("<span>",{"class":"ui-selectmenu-text"}).appendTo(this.button),this._setText(this.buttonText,this.element.find("option:selected").text()),this._resizeButton(),this._on(this.button,this._buttonEvents),this.button.one("focusin",function(){t.menuItems||t._refreshMenu()}),this._hoverable(this.button),this._focusable(this.button)},_drawMenu:function(){var t=this;this.menu=e("<ul>",{"aria-hidden":"true","aria-labelledby":this.ids.button,id:this.ids.menu}),this.menuWrap=e("<div>",{"class":"ui-selectmenu-menu ui-front"}).append(this.menu).appendTo(this._appendTo()),this.menuInstance=this.menu.menu({role:"listbox",select:function(e,i){e.preventDefault(),t._setSelection(),t._select(i.item.data("ui-selectmenu-item"),e)},focus:function(e,i){var s=i.item.data("ui-selectmenu-item");null!=t.focusIndex&&s.index!==t.focusIndex&&(t._trigger("focus",e,{item:s}),t.isOpen||t._select(s,e)),t.focusIndex=s.index,t.button.attr("aria-activedescendant",t.menuItems.eq(s.index).attr("id"))}}).menu("instance"),this.menu.addClass("ui-corner-bottom").removeClass("ui-corner-all"),this.menuInstance._off(this.menu,"mouseleave"),this.menuInstance._closeOnDocumentClick=function(){return!1},this.menuInstance._isDivider=function(){return!1}},refresh:function(){this._refreshMenu(),this._setText(this.buttonText,this._getSelectedItem().text()),this.options.width||this._resizeButton()},_refreshMenu:function(){this.menu.empty();var e,t=this.element.find("option");t.length&&(this._parseOptions(t),this._renderMenu(this.menu,this.items),this.menuInstance.refresh(),this.menuItems=this.menu.find("li").not(".ui-selectmenu-optgroup"),e=this._getSelectedItem(),this.menuInstance.focus(null,e),this._setAria(e.data("ui-selectmenu-item")),this._setOption("disabled",this.element.prop("disabled")))},open:function(e){this.options.disabled||(this.menuItems?(this.menu.find(".ui-state-focus").removeClass("ui-state-focus"),this.menuInstance.focus(null,this._getSelectedItem())):this._refreshMenu(),this.isOpen=!0,this._toggleAttr(),this._resizeMenu(),this._position(),this._on(this.document,this._documentClick),this._trigger("open",e))},_position:function(){this.menuWrap.position(e.extend({of:this.button},this.options.position))},close:function(e){this.isOpen&&(this.isOpen=!1,this._toggleAttr(),this.range=null,this._off(this.document),this._trigger("close",e))},widget:function(){return this.button},menuWidget:function(){return this.menu},_renderMenu:function(t,i){var s=this,n="";e.each(i,function(i,a){a.optgroup!==n&&(e("<li>",{"class":"ui-selectmenu-optgroup ui-menu-divider"+(a.element.parent("optgroup").prop("disabled")?" ui-state-disabled":""),text:a.optgroup}).appendTo(t),n=a.optgroup),s._renderItemData(t,a)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-selectmenu-item",t)},_renderItem:function(t,i){var s=e("<li>");return i.disabled&&s.addClass("ui-state-disabled"),this._setText(s,i.label),s.appendTo(t)},_setText:function(e,t){t?e.text(t):e.html("&#160;")},_move:function(e,t){var i,s,n=".ui-menu-item";this.isOpen?i=this.menuItems.eq(this.focusIndex):(i=this.menuItems.eq(this.element[0].selectedIndex),n+=":not(.ui-state-disabled)"),s="first"===e||"last"===e?i["first"===e?"prevAll":"nextAll"](n).eq(-1):i[e+"All"](n).eq(0),s.length&&this.menuInstance.focus(t,s)},_getSelectedItem:function(){return this.menuItems.eq(this.element[0].selectedIndex)},_toggle:function(e){this[this.isOpen?"close":"open"](e)},_setSelection:function(){var e;this.range&&(window.getSelection?(e=window.getSelection(),e.removeAllRanges(),e.addRange(this.range)):this.range.select(),this.button.focus())},_documentClick:{mousedown:function(t){this.isOpen&&(e(t.target).closest(".ui-selectmenu-menu, #"+this.ids.button).length||this.close(t))}},_buttonEvents:{mousedown:function(){var e;window.getSelection?(e=window.getSelection(),e.rangeCount&&(this.range=e.getRangeAt(0))):this.range=document.selection.createRange()},click:function(e){this._setSelection(),this._toggle(e)},keydown:function(t){var i=!0;switch(t.keyCode){case e.ui.keyCode.TAB:case e.ui.keyCode.ESCAPE:this.close(t),i=!1;break;case e.ui.keyCode.ENTER:this.isOpen&&this._selectFocusedItem(t);break;case e.ui.keyCode.UP:t.altKey?this._toggle(t):this._move("prev",t);break;case e.ui.keyCode.DOWN:t.altKey?this._toggle(t):this._move("next",t);break;case e.ui.keyCode.SPACE:this.isOpen?this._selectFocusedItem(t):this._toggle(t);break;case e.ui.keyCode.LEFT:this._move("prev",t);break;case e.ui.keyCode.RIGHT:this._move("next",t);break;case e.ui.keyCode.HOME:case e.ui.keyCode.PAGE_UP:this._move("first",t);break;case e.ui.keyCode.END:case e.ui.keyCode.PAGE_DOWN:this._move("last",t);break;default:this.menu.trigger(t),i=!1}i&&t.preventDefault()}},_selectFocusedItem:function(e){var t=this.menuItems.eq(this.focusIndex);t.hasClass("ui-state-disabled")||this._select(t.data("ui-selectmenu-item"),e)},_select:function(e,t){var i=this.element[0].selectedIndex;this.element[0].selectedIndex=e.index,this._setText(this.buttonText,e.label),this._setAria(e),this._trigger("select",t,{item:e}),e.index!==i&&this._trigger("change",t,{item:e}),this.close(t)},_setAria:function(e){var t=this.menuItems.eq(e.index).attr("id");this.button.attr({"aria-labelledby":t,"aria-activedescendant":t}),this.menu.attr("aria-activedescendant",t)},_setOption:function(e,t){"icons"===e&&this.button.find("span.ui-icon").removeClass(this.options.icons.button).addClass(t.button),this._super(e,t),"appendTo"===e&&this.menuWrap.appendTo(this._appendTo()),"disabled"===e&&(this.menuInstance.option("disabled",t),this.button.toggleClass("ui-state-disabled",t).attr("aria-disabled",t),this.element.prop("disabled",t),t?(this.button.attr("tabindex",-1),this.close()):this.button.attr("tabindex",0)),"width"===e&&this._resizeButton()},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t&&t[0]||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_toggleAttr:function(){this.button.toggleClass("ui-corner-top",this.isOpen).toggleClass("ui-corner-all",!this.isOpen).attr("aria-expanded",this.isOpen),this.menuWrap.toggleClass("ui-selectmenu-open",this.isOpen),this.menu.attr("aria-hidden",!this.isOpen)},_resizeButton:function(){var e=this.options.width;e||(e=this.element.show().outerWidth(),this.element.hide()),this.button.outerWidth(e)},_resizeMenu:function(){this.menu.outerWidth(Math.max(this.button.outerWidth(),this.menu.width("").outerWidth()+1))},_getCreateOptions:function(){return{disabled:this.element.prop("disabled")}},_parseOptions:function(t){var i=[];t.each(function(t,s){var n=e(s),a=n.parent("optgroup");i.push({element:n,index:t,value:n.val(),label:n.text(),optgroup:a.attr("label")||"",disabled:a.prop("disabled")||n.prop("disabled")})}),this.items=i},_destroy:function(){this.menuWrap.remove(),this.button.remove(),this.element.show(),this.element.removeUniqueId(),this.label.attr("for",this.ids.element)}}),e.widget("ui.slider",e.ui.mouse,{version:"1.11.4",widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},numPages:5,_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this._calculateNewMax(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"),this._refresh(),this._setOption("disabled",this.options.disabled),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var t,i,s=this.options,n=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),a="<span class='ui-slider-handle ui-state-default ui-corner-all' tabindex='0'></span>",o=[];for(i=s.values&&s.values.length||1,n.length>i&&(n.slice(i).remove(),n=n.slice(0,i)),t=n.length;i>t;t++)o.push(a);this.handles=n.add(e(o.join("")).appendTo(this.element)),this.handle=this.handles.eq(0),this.handles.each(function(t){e(this).data("ui-slider-handle-index",t)})},_createRange:function(){var t=this.options,i="";t.range?(t.range===!0&&(t.values?t.values.length&&2!==t.values.length?t.values=[t.values[0],t.values[0]]:e.isArray(t.values)&&(t.values=t.values.slice(0)):t.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?this.range.removeClass("ui-slider-range-min ui-slider-range-max").css({left:"",bottom:""}):(this.range=e("<div></div>").appendTo(this.element),i="ui-slider-range ui-widget-header ui-corner-all"),this.range.addClass(i+("min"===t.range||"max"===t.range?" ui-slider-range-"+t.range:""))):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){this._off(this.handles),this._on(this.handles,this._handleEvents),this._hoverable(this.handles),this._focusable(this.handles)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-widget ui-widget-content ui-corner-all"),this._mouseDestroy()},_mouseCapture:function(t){var i,s,n,a,o,r,h,l,u=this,d=this.options;return d.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),i={x:t.pageX,y:t.pageY},s=this._normValueFromMouse(i),n=this._valueMax()-this._valueMin()+1,this.handles.each(function(t){var i=Math.abs(s-u.values(t));(n>i||n===i&&(t===u._lastChangedValue||u.values(t)===d.min))&&(n=i,a=e(this),o=t)}),r=this._start(t,o),r===!1?!1:(this._mouseSliding=!0,this._handleIndex=o,a.addClass("ui-state-active").focus(),h=a.offset(),l=!e(t.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:t.pageX-h.left-a.width()/2,top:t.pageY-h.top-a.height()/2-(parseInt(a.css("borderTopWidth"),10)||0)-(parseInt(a.css("borderBottomWidth"),10)||0)+(parseInt(a.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(t,o,s),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(e){var t={x:e.pageX,y:e.pageY},i=this._normValueFromMouse(t);return this._slide(e,this._handleIndex,i),!1},_mouseStop:function(e){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(e,this._handleIndex),this._change(e,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(e){var t,i,s,n,a;return"horizontal"===this.orientation?(t=this.elementSize.width,i=e.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(t=this.elementSize.height,i=e.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),s=i/t,s>1&&(s=1),0>s&&(s=0),"vertical"===this.orientation&&(s=1-s),n=this._valueMax()-this._valueMin(),a=this._valueMin()+s*n,this._trimAlignValue(a)},_start:function(e,t){var i={handle:this.handles[t],value:this.value()};return this.options.values&&this.options.values.length&&(i.value=this.values(t),i.values=this.values()),this._trigger("start",e,i)},_slide:function(e,t,i){var s,n,a;this.options.values&&this.options.values.length?(s=this.values(t?0:1),2===this.options.values.length&&this.options.range===!0&&(0===t&&i>s||1===t&&s>i)&&(i=s),i!==this.values(t)&&(n=this.values(),n[t]=i,a=this._trigger("slide",e,{handle:this.handles[t],value:i,values:n}),s=this.values(t?0:1),a!==!1&&this.values(t,i))):i!==this.value()&&(a=this._trigger("slide",e,{handle:this.handles[t],value:i}),a!==!1&&this.value(i))},_stop:function(e,t){var i={handle:this.handles[t],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(t),i.values=this.values()),this._trigger("stop",e,i)},_change:function(e,t){if(!this._keySliding&&!this._mouseSliding){var i={handle:this.handles[t],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(t),i.values=this.values()),this._lastChangedValue=t,this._trigger("change",e,i)}},value:function(e){return arguments.length?(this.options.value=this._trimAlignValue(e),this._refreshValue(),this._change(null,0),void 0):this._value()},values:function(t,i){var s,n,a;if(arguments.length>1)return this.options.values[t]=this._trimAlignValue(i),this._refreshValue(),this._change(null,t),void 0;if(!arguments.length)return this._values();if(!e.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(t):this.value();for(s=this.options.values,n=arguments[0],a=0;s.length>a;a+=1)s[a]=this._trimAlignValue(n[a]),this._change(null,a);this._refreshValue()},_setOption:function(t,i){var s,n=0;switch("range"===t&&this.options.range===!0&&("min"===i?(this.options.value=this._values(0),this.options.values=null):"max"===i&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),e.isArray(this.options.values)&&(n=this.options.values.length),"disabled"===t&&this.element.toggleClass("ui-state-disabled",!!i),this._super(t,i),t){case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue(),this.handles.css("horizontal"===i?"bottom":"left","");break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),s=0;n>s;s+=1)this._change(null,s);this._animateOff=!1;break;case"step":case"min":case"max":this._animateOff=!0,this._calculateNewMax(),this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_value:function(){var e=this.options.value;return e=this._trimAlignValue(e)},_values:function(e){var t,i,s;if(arguments.length)return t=this.options.values[e],t=this._trimAlignValue(t);if(this.options.values&&this.options.values.length){for(i=this.options.values.slice(),s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(i[s]);return i}return[]},_trimAlignValue:function(e){if(this._valueMin()>=e)return this._valueMin();if(e>=this._valueMax())return this._valueMax();var t=this.options.step>0?this.options.step:1,i=(e-this._valueMin())%t,s=e-i;return 2*Math.abs(i)>=t&&(s+=i>0?t:-t),parseFloat(s.toFixed(5))},_calculateNewMax:function(){var e=this.options.max,t=this._valueMin(),i=this.options.step,s=Math.floor(+(e-t).toFixed(this._precision())/i)*i;e=s+t,this.max=parseFloat(e.toFixed(this._precision()))},_precision:function(){var e=this._precisionOf(this.options.step);return null!==this.options.min&&(e=Math.max(e,this._precisionOf(this.options.min))),e},_precisionOf:function(e){var t=""+e,i=t.indexOf(".");return-1===i?0:t.length-i-1},_valueMin:function(){return this.options.min},_valueMax:function(){return this.max},_refreshValue:function(){var t,i,s,n,a,o=this.options.range,r=this.options,h=this,l=this._animateOff?!1:r.animate,u={};this.options.values&&this.options.values.length?this.handles.each(function(s){i=100*((h.values(s)-h._valueMin())/(h._valueMax()-h._valueMin())),u["horizontal"===h.orientation?"left":"bottom"]=i+"%",e(this).stop(1,1)[l?"animate":"css"](u,r.animate),h.options.range===!0&&("horizontal"===h.orientation?(0===s&&h.range.stop(1,1)[l?"animate":"css"]({left:i+"%"},r.animate),1===s&&h.range[l?"animate":"css"]({width:i-t+"%"},{queue:!1,duration:r.animate})):(0===s&&h.range.stop(1,1)[l?"animate":"css"]({bottom:i+"%"},r.animate),1===s&&h.range[l?"animate":"css"]({height:i-t+"%"},{queue:!1,duration:r.animate}))),t=i}):(s=this.value(),n=this._valueMin(),a=this._valueMax(),i=a!==n?100*((s-n)/(a-n)):0,u["horizontal"===this.orientation?"left":"bottom"]=i+"%",this.handle.stop(1,1)[l?"animate":"css"](u,r.animate),"min"===o&&"horizontal"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({width:i+"%"},r.animate),"max"===o&&"horizontal"===this.orientation&&this.range[l?"animate":"css"]({width:100-i+"%"},{queue:!1,duration:r.animate}),"min"===o&&"vertical"===this.orientation&&this.range.stop(1,1)[l?"animate":"css"]({height:i+"%"},r.animate),"max"===o&&"vertical"===this.orientation&&this.range[l?"animate":"css"]({height:100-i+"%"},{queue:!1,duration:r.animate}))},_handleEvents:{keydown:function(t){var i,s,n,a,o=e(t.target).data("ui-slider-handle-index");switch(t.keyCode){case e.ui.keyCode.HOME:case e.ui.keyCode.END:case e.ui.keyCode.PAGE_UP:case e.ui.keyCode.PAGE_DOWN:case e.ui.keyCode.UP:case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:case e.ui.keyCode.LEFT:if(t.preventDefault(),!this._keySliding&&(this._keySliding=!0,e(t.target).addClass("ui-state-active"),i=this._start(t,o),i===!1))return}switch(a=this.options.step,s=n=this.options.values&&this.options.values.length?this.values(o):this.value(),t.keyCode){case e.ui.keyCode.HOME:n=this._valueMin();break;case e.ui.keyCode.END:n=this._valueMax();break;case e.ui.keyCode.PAGE_UP:n=this._trimAlignValue(s+(this._valueMax()-this._valueMin())/this.numPages);break;case e.ui.keyCode.PAGE_DOWN:n=this._trimAlignValue(s-(this._valueMax()-this._valueMin())/this.numPages);break;case e.ui.keyCode.UP:case e.ui.keyCode.RIGHT:if(s===this._valueMax())return;n=this._trimAlignValue(s+a);break;case e.ui.keyCode.DOWN:case e.ui.keyCode.LEFT:if(s===this._valueMin())return;n=this._trimAlignValue(s-a)}this._slide(t,o,n)},keyup:function(t){var i=e(t.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(t,i),this._change(t,i),e(t.target).removeClass("ui-state-active"))}}}),e.widget("ui.spinner",{version:"1.11.4",defaultElement:"<input>",widgetEventPrefix:"spin",options:{culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var t={},i=this.element;return e.each(["min","max","step"],function(e,s){var n=i.attr(s);void 0!==n&&n.length&&(t[s]=n)}),t},_events:{keydown:function(e){this._start(e)&&this._keydown(e)&&e.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",e),void 0)},mousewheel:function(e,t){if(t){if(!this.spinning&&!this._start(e))return!1;this._spin((t>0?1:-1)*this.options.step,e),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(e)},100),e.preventDefault()}},"mousedown .ui-spinner-button":function(t){function i(){var e=this.element[0]===this.document[0].activeElement;e||(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===this.document[0].activeElement?this.previous:this.element.val(),t.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(t)!==!1&&this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(t){return e(t.currentTarget).hasClass("ui-state-active")?this._start(t)===!1?!1:(this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_draw:function(){var e=this.uiSpinner=this.element.addClass("ui-spinner-input").attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml());this.element.attr("role","spinbutton"),this.buttons=e.find(".ui-spinner-button").attr("tabIndex",-1).button().removeClass("ui-corner-all"),this.buttons.height()>Math.ceil(.5*e.height())&&e.height()>0&&e.height(e.height()),this.options.disabled&&this.disable()
},_keydown:function(t){var i=this.options,s=e.ui.keyCode;switch(t.keyCode){case s.UP:return this._repeat(null,1,t),!0;case s.DOWN:return this._repeat(null,-1,t),!0;case s.PAGE_UP:return this._repeat(null,i.page,t),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,t),!0}return!1},_uiSpinnerHtml:function(){return"<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>"},_buttonHtml:function(){return"<a class='ui-spinner-button ui-spinner-up ui-corner-tr'><span class='ui-icon "+this.options.icons.up+"'>&#9650;</span>"+"</a>"+"<a class='ui-spinner-button ui-spinner-down ui-corner-br'>"+"<span class='ui-icon "+this.options.icons.down+"'>&#9660;</span>"+"</a>"},_start:function(e){return this.spinning||this._trigger("start",e)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(e,t,i){e=e||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,t,i)},e),this._spin(t*this.options.step,i)},_spin:function(e,t){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+e*this._increment(this.counter)),this.spinning&&this._trigger("spin",t,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(t){var i=this.options.incremental;return i?e.isFunction(i)?i(t):Math.floor(t*t*t/5e4-t*t/500+17*t/200+1):1},_precision:function(){var e=this._precisionOf(this.options.step);return null!==this.options.min&&(e=Math.max(e,this._precisionOf(this.options.min))),e},_precisionOf:function(e){var t=""+e,i=t.indexOf(".");return-1===i?0:t.length-i-1},_adjustValue:function(e){var t,i,s=this.options;return t=null!==s.min?s.min:0,i=e-t,i=Math.round(i/s.step)*s.step,e=t+i,e=parseFloat(e.toFixed(this._precision())),null!==s.max&&e>s.max?s.max:null!==s.min&&s.min>e?s.min:e},_stop:function(e){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",e))},_setOption:function(e,t){if("culture"===e||"numberFormat"===e){var i=this._parse(this.element.val());return this.options[e]=t,this.element.val(this._format(i)),void 0}("max"===e||"min"===e||"step"===e)&&"string"==typeof t&&(t=this._parse(t)),"icons"===e&&(this.buttons.first().find(".ui-icon").removeClass(this.options.icons.up).addClass(t.up),this.buttons.last().find(".ui-icon").removeClass(this.options.icons.down).addClass(t.down)),this._super(e,t),"disabled"===e&&(this.widget().toggleClass("ui-state-disabled",!!t),this.element.prop("disabled",!!t),this.buttons.button(t?"disable":"enable"))},_setOptions:h(function(e){this._super(e)}),_parse:function(e){return"string"==typeof e&&""!==e&&(e=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(e,10,this.options.culture):+e),""===e||isNaN(e)?null:e},_format:function(e){return""===e?"":window.Globalize&&this.options.numberFormat?Globalize.format(e,this.options.numberFormat,this.options.culture):e},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},isValid:function(){var e=this.value();return null===e?!1:e===this._adjustValue(e)},_value:function(e,t){var i;""!==e&&(i=this._parse(e),null!==i&&(t||(i=this._adjustValue(i)),e=this._format(i))),this.element.val(e),this._refresh()},_destroy:function(){this.element.removeClass("ui-spinner-input").prop("disabled",!1).removeAttr("autocomplete").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:h(function(e){this._stepUp(e)}),_stepUp:function(e){this._start()&&(this._spin((e||1)*this.options.step),this._stop())},stepDown:h(function(e){this._stepDown(e)}),_stepDown:function(e){this._start()&&(this._spin((e||1)*-this.options.step),this._stop())},pageUp:h(function(e){this._stepUp((e||1)*this.options.page)}),pageDown:h(function(e){this._stepDown((e||1)*this.options.page)}),value:function(e){return arguments.length?(h(this._value).call(this,e),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}}),e.widget("ui.tabs",{version:"1.11.4",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var e=/#.*$/;return function(t){var i,s;t=t.cloneNode(!1),i=t.href.replace(e,""),s=location.href.replace(e,"");try{i=decodeURIComponent(i)}catch(n){}try{s=decodeURIComponent(s)}catch(n){}return t.hash.length>1&&i===s}}(),_create:function(){var t=this,i=this.options;this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",i.collapsible),this._processTabs(),i.active=this._initialActive(),e.isArray(i.disabled)&&(i.disabled=e.unique(i.disabled.concat(e.map(this.tabs.filter(".ui-state-disabled"),function(e){return t.tabs.index(e)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):e(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var t=this.options.active,i=this.options.collapsible,s=location.hash.substring(1);return null===t&&(s&&this.tabs.each(function(i,n){return e(n).attr("aria-controls")===s?(t=i,!1):void 0}),null===t&&(t=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===t||-1===t)&&(t=this.tabs.length?0:!1)),t!==!1&&(t=this.tabs.index(this.tabs.eq(t)),-1===t&&(t=i?!1:0)),!i&&t===!1&&this.anchors.length&&(t=0),t},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):e()}},_tabKeydown:function(t){var i=e(this.document[0].activeElement).closest("li"),s=this.tabs.index(i),n=!0;if(!this._handlePageNav(t)){switch(t.keyCode){case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:s++;break;case e.ui.keyCode.UP:case e.ui.keyCode.LEFT:n=!1,s--;break;case e.ui.keyCode.END:s=this.anchors.length-1;break;case e.ui.keyCode.HOME:s=0;break;case e.ui.keyCode.SPACE:return t.preventDefault(),clearTimeout(this.activating),this._activate(s),void 0;case e.ui.keyCode.ENTER:return t.preventDefault(),clearTimeout(this.activating),this._activate(s===this.options.active?!1:s),void 0;default:return}t.preventDefault(),clearTimeout(this.activating),s=this._focusNextTab(s,n),t.ctrlKey||t.metaKey||(i.attr("aria-selected","false"),this.tabs.eq(s).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",s)},this.delay))}},_panelKeydown:function(t){this._handlePageNav(t)||t.ctrlKey&&t.keyCode===e.ui.keyCode.UP&&(t.preventDefault(),this.active.focus())},_handlePageNav:function(t){return t.altKey&&t.keyCode===e.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):t.altKey&&t.keyCode===e.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(t,i){function s(){return t>n&&(t=0),0>t&&(t=n),t}for(var n=this.tabs.length-1;-1!==e.inArray(s(),this.options.disabled);)t=i?t+1:t-1;return t},_focusNextTab:function(e,t){return e=this._findNextTab(e,t),this.tabs.eq(e).focus(),e},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):"disabled"===e?(this._setupDisabled(t),void 0):(this._super(e,t),"collapsible"===e&&(this.element.toggleClass("ui-tabs-collapsible",t),t||this.options.active!==!1||this._activate(0)),"event"===e&&this._setupEvents(t),"heightStyle"===e&&this._setupHeightStyle(t),void 0)},_sanitizeSelector:function(e){return e?e.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var t=this.options,i=this.tablist.children(":has(a[href])");t.disabled=e.map(i.filter(".ui-state-disabled"),function(e){return i.index(e)}),this._processTabs(),t.active!==!1&&this.anchors.length?this.active.length&&!e.contains(this.tablist[0],this.active[0])?this.tabs.length===t.disabled.length?(t.active=!1,this.active=e()):this._activate(this._findNextTab(Math.max(0,t.active-1),!1)):t.active=this.tabs.index(this.active):(t.active=!1,this.active=e()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var t=this,i=this.tabs,s=this.anchors,n=this.panels;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist").delegate("> li","mousedown"+this.eventNamespace,function(t){e(this).is(".ui-state-disabled")&&t.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){e(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return e("a",this)[0]}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=e(),this.anchors.each(function(i,s){var n,a,o,r=e(s).uniqueId().attr("id"),h=e(s).closest("li"),l=h.attr("aria-controls");t._isLocal(s)?(n=s.hash,o=n.substring(1),a=t.element.find(t._sanitizeSelector(n))):(o=h.attr("aria-controls")||e({}).uniqueId()[0].id,n="#"+o,a=t.element.find(n),a.length||(a=t._createPanel(o),a.insertAfter(t.panels[i-1]||t.tablist)),a.attr("aria-live","polite")),a.length&&(t.panels=t.panels.add(a)),l&&h.data("ui-tabs-aria-controls",l),h.attr({"aria-controls":o,"aria-labelledby":r}),a.attr("aria-labelledby",r)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel"),i&&(this._off(i.not(this.tabs)),this._off(s.not(this.anchors)),this._off(n.not(this.panels)))},_getList:function(){return this.tablist||this.element.find("ol,ul").eq(0)},_createPanel:function(t){return e("<div>").attr("id",t).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(t){e.isArray(t)&&(t.length?t.length===this.anchors.length&&(t=!0):t=!1);for(var i,s=0;i=this.tabs[s];s++)t===!0||-1!==e.inArray(s,t)?e(i).addClass("ui-state-disabled").attr("aria-disabled","true"):e(i).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=t},_setupEvents:function(t){var i={};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(e){e.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(t){var i,s=this.element.parent();"fill"===t?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var t=e(this),s=t.css("position");"absolute"!==s&&"fixed"!==s&&(i-=t.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=e(this).outerHeight(!0)}),this.panels.each(function(){e(this).height(Math.max(0,i-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===t&&(i=0,this.panels.each(function(){i=Math.max(i,e(this).height("").height())}).height(i))},_eventHandler:function(t){var i=this.options,s=this.active,n=e(t.currentTarget),a=n.closest("li"),o=a[0]===s[0],r=o&&i.collapsible,h=r?e():this._getPanelForTab(a),l=s.length?this._getPanelForTab(s):e(),u={oldTab:s,oldPanel:l,newTab:r?e():a,newPanel:h};t.preventDefault(),a.hasClass("ui-state-disabled")||a.hasClass("ui-tabs-loading")||this.running||o&&!i.collapsible||this._trigger("beforeActivate",t,u)===!1||(i.active=r?!1:this.tabs.index(a),this.active=o?e():a,this.xhr&&this.xhr.abort(),l.length||h.length||e.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(a),t),this._toggle(t,u))},_toggle:function(t,i){function s(){a.running=!1,a._trigger("activate",t,i)}function n(){i.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),o.length&&a.options.show?a._show(o,a.options.show,s):(o.show(),s())}var a=this,o=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),n()}):(i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),r.hide(),n()),r.attr("aria-hidden","true"),i.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),o.length&&r.length?i.oldTab.attr("tabIndex",-1):o.length&&this.tabs.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),o.attr("aria-hidden","false"),i.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(t){var i,s=this._findActive(t);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return t===!1?e():this.tabs.eq(t)},_getIndex:function(e){return"string"==typeof e&&(e=this.anchors.index(this.anchors.filter("[href$='"+e+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeUniqueId(),this.tablist.unbind(this.eventNamespace),this.tabs.add(this.panels).each(function(){e.data(this,"ui-tabs-destroy")?e(this).remove():e(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var t=e(this),i=t.data("ui-tabs-aria-controls");i?t.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):t.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(t){var i=this.options.disabled;i!==!1&&(void 0===t?i=!1:(t=this._getIndex(t),i=e.isArray(i)?e.map(i,function(e){return e!==t?e:null}):e.map(this.tabs,function(e,i){return i!==t?i:null})),this._setupDisabled(i))},disable:function(t){var i=this.options.disabled;if(i!==!0){if(void 0===t)i=!0;else{if(t=this._getIndex(t),-1!==e.inArray(t,i))return;i=e.isArray(i)?e.merge([t],i).sort():[t]}this._setupDisabled(i)}},load:function(t,i){t=this._getIndex(t);var s=this,n=this.tabs.eq(t),a=n.find(".ui-tabs-anchor"),o=this._getPanelForTab(n),r={tab:n,panel:o},h=function(e,t){"abort"===t&&s.panels.stop(!1,!0),n.removeClass("ui-tabs-loading"),o.removeAttr("aria-busy"),e===s.xhr&&delete s.xhr};this._isLocal(a[0])||(this.xhr=e.ajax(this._ajaxSettings(a,i,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(n.addClass("ui-tabs-loading"),o.attr("aria-busy","true"),this.xhr.done(function(e,t,n){setTimeout(function(){o.html(e),s._trigger("load",i,r),h(n,t)},1)}).fail(function(e,t){setTimeout(function(){h(e,t)},1)})))},_ajaxSettings:function(t,i,s){var n=this;return{url:t.attr("href"),beforeSend:function(t,a){return n._trigger("beforeLoad",i,e.extend({jqXHR:t,ajaxSettings:a},s))}}},_getPanelForTab:function(t){var i=e(t).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}}),e.widget("ui.tooltip",{version:"1.11.4",options:{content:function(){var t=e(this).attr("title")||"";return e("<a>").text(t).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_addDescribedBy:function(t,i){var s=(t.attr("aria-describedby")||"").split(/\s+/);s.push(i),t.data("ui-tooltip-id",i).attr("aria-describedby",e.trim(s.join(" ")))},_removeDescribedBy:function(t){var i=t.data("ui-tooltip-id"),s=(t.attr("aria-describedby")||"").split(/\s+/),n=e.inArray(i,s);-1!==n&&s.splice(n,1),t.removeData("ui-tooltip-id"),s=e.trim(s.join(" ")),s?t.attr("aria-describedby",s):t.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable(),this.liveRegion=e("<div>").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).addClass("ui-helper-hidden-accessible").appendTo(this.document[0].body)},_setOption:function(t,i){var s=this;return"disabled"===t?(this[i?"_disable":"_enable"](),this.options[t]=i,void 0):(this._super(t,i),"content"===t&&e.each(this.tooltips,function(e,t){s._updateContent(t.element)}),void 0)},_disable:function(){var t=this;e.each(this.tooltips,function(i,s){var n=e.Event("blur");n.target=n.currentTarget=s.element[0],t.close(n,!0)}),this.element.find(this.options.items).addBack().each(function(){var t=e(this);t.is("[title]")&&t.data("ui-tooltip-title",t.attr("title")).removeAttr("title")})},_enable:function(){this.element.find(this.options.items).addBack().each(function(){var t=e(this);t.data("ui-tooltip-title")&&t.attr("title",t.data("ui-tooltip-title"))})},open:function(t){var i=this,s=e(t?t.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),t&&"mouseover"===t.type&&s.parents().each(function(){var t,s=e(this);s.data("ui-tooltip-open")&&(t=e.Event("blur"),t.target=t.currentTarget=this,i.close(t,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._registerCloseHandlers(t,s),this._updateContent(s,t))},_updateContent:function(e,t){var i,s=this.options.content,n=this,a=t?t.type:null;return"string"==typeof s?this._open(t,e,s):(i=s.call(e[0],function(i){n._delay(function(){e.data("ui-tooltip-open")&&(t&&(t.type=a),this._open(t,e,i))})}),i&&this._open(t,e,i),void 0)},_open:function(t,i,s){function n(e){l.of=e,o.is(":hidden")||o.position(l)}var a,o,r,h,l=e.extend({},this.options.position);if(s){if(a=this._find(i))return a.tooltip.find(".ui-tooltip-content").html(s),void 0;i.is("[title]")&&(t&&"mouseover"===t.type?i.attr("title",""):i.removeAttr("title")),a=this._tooltip(i),o=a.tooltip,this._addDescribedBy(i,o.attr("id")),o.find(".ui-tooltip-content").html(s),this.liveRegion.children().hide(),s.clone?(h=s.clone(),h.removeAttr("id").find("[id]").removeAttr("id")):h=s,e("<div>").html(h).appendTo(this.liveRegion),this.options.track&&t&&/^mouse/.test(t.type)?(this._on(this.document,{mousemove:n}),n(t)):o.position(e.extend({of:i},this.options.position)),o.hide(),this._show(o,this.options.show),this.options.show&&this.options.show.delay&&(r=this.delayedShow=setInterval(function(){o.is(":visible")&&(n(l.of),clearInterval(r))},e.fx.interval)),this._trigger("open",t,{tooltip:o})}},_registerCloseHandlers:function(t,i){var s={keyup:function(t){if(t.keyCode===e.ui.keyCode.ESCAPE){var s=e.Event(t);s.currentTarget=i[0],this.close(s,!0)}}};i[0]!==this.element[0]&&(s.remove=function(){this._removeTooltip(this._find(i).tooltip)}),t&&"mouseover"!==t.type||(s.mouseleave="close"),t&&"focusin"!==t.type||(s.focusout="close"),this._on(!0,i,s)},close:function(t){var i,s=this,n=e(t?t.currentTarget:this.element),a=this._find(n);return a?(i=a.tooltip,a.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&!n.attr("title")&&n.attr("title",n.data("ui-tooltip-title")),this._removeDescribedBy(n),a.hiding=!0,i.stop(!0),this._hide(i,this.options.hide,function(){s._removeTooltip(e(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),t&&"mouseleave"===t.type&&e.each(this.parents,function(t,i){e(i.element).attr("title",i.title),delete s.parents[t]}),a.closing=!0,this._trigger("close",t,{tooltip:i}),a.hiding||(a.closing=!1)),void 0):(n.removeData("ui-tooltip-open"),void 0)},_tooltip:function(t){var i=e("<div>").attr("role","tooltip").addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||"")),s=i.uniqueId().attr("id");return e("<div>").addClass("ui-tooltip-content").appendTo(i),i.appendTo(this.document[0].body),this.tooltips[s]={element:t,tooltip:i}},_find:function(e){var t=e.data("ui-tooltip-id");return t?this.tooltips[t]:null},_removeTooltip:function(e){e.remove(),delete this.tooltips[e.attr("id")]},_destroy:function(){var t=this;e.each(this.tooltips,function(i,s){var n=e.Event("blur"),a=s.element;n.target=n.currentTarget=a[0],t.close(n,!0),e("#"+i).remove(),a.data("ui-tooltip-title")&&(a.attr("title")||a.attr("title",a.data("ui-tooltip-title")),a.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}})});
/*!
 * jQuery UI 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI
 */(function(a,b){function d(b){return!a(b).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}function c(b,c){var e=b.nodeName.toLowerCase();if("area"===e){var f=b.parentNode,g=f.name,h;if(!b.href||!g||f.nodeName.toLowerCase()!=="map")return!1;h=a("img[usemap=#"+g+"]")[0];return!!h&&d(h)}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"==e?b.href||c:c)&&d(b)}a.ui=a.ui||{};a.ui.version||(a.extend(a.ui,{version:"1.8.18",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(b,c){return typeof b=="number"?this.each(function(){var d=this;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):this._focus.apply(this,arguments)},scrollParent:function(){var b;a.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?b=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0):b=this.parents().filter(function(){return/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!b.length?a(document):b},zIndex:function(c){if(c!==b)return this.css("zIndex",c);if(this.length){var d=a(this[0]),e,f;while(d.length&&d[0]!==document){e=d.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){f=parseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!==0)return f}d=d.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),a.each(["Width","Height"],function(c,d){function h(b,c,d,f){a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)});return c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){if(c===b)return g["inner"+d].call(this);return this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){if(typeof b!="number")return g["outer"+d].call(this,b);return this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!!d&&!!a.element[0].parentNode)for(var e=0;e<d.length;e++)a.options[d[e][0]]&&d[e][1].apply(a.element,c)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(b,c){if(a(b).css("overflow")==="hidden")return!1;var d=c&&c==="left"?"scrollLeft":"scrollTop",e=!1;if(b[d]>0)return!0;b[d]=1,e=b[d]>0,b[d]=0;return e},isOverAxis:function(a,b,c){return a>b&&a<b+c},isOver:function(b,c,d,e,f,g){return a.ui.isOverAxis(b,d,f)&&a.ui.isOverAxis(c,e,g)}}))})(jQuery);/*!
 * jQuery UI Widget 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Widget
 */(function(a,b){if(a.cleanData){var c=a.cleanData;a.cleanData=function(b){for(var d=0,e;(e=b[d])!=null;d++)try{a(e).triggerHandler("remove")}catch(f){}c(b)}}else{var d=a.fn.remove;a.fn.remove=function(b,c){return this.each(function(){c||(!b||a.filter(b,[this]).length)&&a("*",this).add([this]).each(function(){try{a(this).triggerHandler("remove")}catch(b){}});return d.call(a(this),b,c)})}}a.widget=function(b,c,d){var e=b.split(".")[0],f;b=b.split(".")[1],f=e+"-"+b,d||(d=c,c=a.Widget),a.expr[":"][f]=function(c){return!!a.data(c,b)},a[e]=a[e]||{},a[e][b]=function(a,b){arguments.length&&this._createWidget(a,b)};var g=new c;g.options=a.extend(!0,{},g.options),a[e][b].prototype=a.extend(!0,g,{namespace:e,widgetName:b,widgetEventPrefix:a[e][b].prototype.widgetEventPrefix||b,widgetBaseClass:f},d),a.widget.bridge(b,a[e][b])},a.widget.bridge=function(c,d){a.fn[c]=function(e){var f=typeof e=="string",g=Array.prototype.slice.call(arguments,1),h=this;e=!f&&g.length?a.extend.apply(null,[!0,e].concat(g)):e;if(f&&e.charAt(0)==="_")return h;f?this.each(function(){var d=a.data(this,c),f=d&&a.isFunction(d[e])?d[e].apply(d,g):d;if(f!==d&&f!==b){h=f;return!1}}):this.each(function(){var b=a.data(this,c);b?b.option(e||{})._init():a.data(this,c,new d(e,this))});return h}},a.Widget=function(a,b){arguments.length&&this._createWidget(a,b)},a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:!1},_createWidget:function(b,c){a.data(c,this.widgetName,this),this.element=a(c),this.options=a.extend(!0,{},this.options,this._getCreateOptions(),b);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()}),this._create(),this._trigger("create"),this._init()},_getCreateOptions:function(){return a.metadata&&a.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName),this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled "+"ui-state-disabled")},widget:function(){return this.element},option:function(c,d){var e=c;if(arguments.length===0)return a.extend({},this.options);if(typeof c=="string"){if(d===b)return this.options[c];e={},e[c]=d}this._setOptions(e);return this},_setOptions:function(b){var c=this;a.each(b,function(a,b){c._setOption(a,b)});return this},_setOption:function(a,b){this.options[a]=b,a==="disabled"&&this.widget()[b?"addClass":"removeClass"](this.widgetBaseClass+"-disabled"+" "+"ui-state-disabled").attr("aria-disabled",b);return this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_trigger:function(b,c,d){var e,f,g=this.options[b];d=d||{},c=a.Event(c),c.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase(),c.target=this.element[0],f=c.originalEvent;if(f)for(e in f)e in c||(c[e]=f[e]);this.element.trigger(c,d);return!(a.isFunction(g)&&g.call(this.element[0],c,d)===!1||c.isDefaultPrevented())}}})(jQuery);/*!
 * jQuery UI Mouse 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Mouse
 *
 * Depends:
 *	jquery.ui.widget.js
 */(function(a,b){var c=!1;a(document).mouseup(function(a){c=!1}),a.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var b=this;this.element.bind("mousedown."+this.widgetName,function(a){return b._mouseDown(a)}).bind("click."+this.widgetName,function(c){if(!0===a.data(c.target,b.widgetName+".preventClickEvent")){a.removeData(c.target,b.widgetName+".preventClickEvent"),c.stopImmediatePropagation();return!1}}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(b){if(!c){this._mouseStarted&&this._mouseUp(b),this._mouseDownEvent=b;var d=this,e=b.which==1,f=typeof this.options.cancel=="string"&&b.target.nodeName?a(b.target).closest(this.options.cancel).length:!1;if(!e||f||!this._mouseCapture(b))return!0;this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){d.mouseDelayMet=!0},this.options.delay));if(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)){this._mouseStarted=this._mouseStart(b)!==!1;if(!this._mouseStarted){b.preventDefault();return!0}}!0===a.data(b.target,this.widgetName+".preventClickEvent")&&a.removeData(b.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(a){return d._mouseMove(a)},this._mouseUpDelegate=function(a){return d._mouseUp(a)},a(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),b.preventDefault(),c=!0;return!0}},_mouseMove:function(b){if(a.browser.msie&&!(document.documentMode>=9)&&!b.button)return this._mouseUp(b);if(this._mouseStarted){this._mouseDrag(b);return b.preventDefault()}this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b));return!this._mouseStarted},_mouseUp:function(b){a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,b.target==this._mouseDownEvent.target&&a.data(b.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b));return!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}})})(jQuery);/*
 * jQuery UI Position 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Position
 */(function(a,b){a.ui=a.ui||{};var c=/left|center|right/,d=/top|center|bottom/,e="center",f={},g=a.fn.position,h=a.fn.offset;a.fn.position=function(b){if(!b||!b.of)return g.apply(this,arguments);b=a.extend({},b);var h=a(b.of),i=h[0],j=(b.collision||"flip").split(" "),k=b.offset?b.offset.split(" "):[0,0],l,m,n;i.nodeType===9?(l=h.width(),m=h.height(),n={top:0,left:0}):i.setTimeout?(l=h.width(),m=h.height(),n={top:h.scrollTop(),left:h.scrollLeft()}):i.preventDefault?(b.at="left top",l=m=0,n={top:b.of.pageY,left:b.of.pageX}):(l=h.outerWidth(),m=h.outerHeight(),n=h.offset()),a.each(["my","at"],function(){var a=(b[this]||"").split(" ");a.length===1&&(a=c.test(a[0])?a.concat([e]):d.test(a[0])?[e].concat(a):[e,e]),a[0]=c.test(a[0])?a[0]:e,a[1]=d.test(a[1])?a[1]:e,b[this]=a}),j.length===1&&(j[1]=j[0]),k[0]=parseInt(k[0],10)||0,k.length===1&&(k[1]=k[0]),k[1]=parseInt(k[1],10)||0,b.at[0]==="right"?n.left+=l:b.at[0]===e&&(n.left+=l/2),b.at[1]==="bottom"?n.top+=m:b.at[1]===e&&(n.top+=m/2),n.left+=k[0],n.top+=k[1];return this.each(function(){var c=a(this),d=c.outerWidth(),g=c.outerHeight(),h=parseInt(a.curCSS(this,"marginLeft",!0))||0,i=parseInt(a.curCSS(this,"marginTop",!0))||0,o=d+h+(parseInt(a.curCSS(this,"marginRight",!0))||0),p=g+i+(parseInt(a.curCSS(this,"marginBottom",!0))||0),q=a.extend({},n),r;b.my[0]==="right"?q.left-=d:b.my[0]===e&&(q.left-=d/2),b.my[1]==="bottom"?q.top-=g:b.my[1]===e&&(q.top-=g/2),f.fractions||(q.left=Math.round(q.left),q.top=Math.round(q.top)),r={left:q.left-h,top:q.top-i},a.each(["left","top"],function(c,e){a.ui.position[j[c]]&&a.ui.position[j[c]][e](q,{targetWidth:l,targetHeight:m,elemWidth:d,elemHeight:g,collisionPosition:r,collisionWidth:o,collisionHeight:p,offset:k,my:b.my,at:b.at})}),a.fn.bgiframe&&c.bgiframe(),c.offset(a.extend(q,{using:b.using}))})},a.ui.position={fit:{left:function(b,c){var d=a(window),e=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft();b.left=e>0?b.left-e:Math.max(b.left-c.collisionPosition.left,b.left)},top:function(b,c){var d=a(window),e=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop();b.top=e>0?b.top-e:Math.max(b.top-c.collisionPosition.top,b.top)}},flip:{left:function(b,c){if(c.at[0]!==e){var d=a(window),f=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft(),g=c.my[0]==="left"?-c.elemWidth:c.my[0]==="right"?c.elemWidth:0,h=c.at[0]==="left"?c.targetWidth:-c.targetWidth,i=-2*c.offset[0];b.left+=c.collisionPosition.left<0?g+h+i:f>0?g+h+i:0}},top:function(b,c){if(c.at[1]!==e){var d=a(window),f=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop(),g=c.my[1]==="top"?-c.elemHeight:c.my[1]==="bottom"?c.elemHeight:0,h=c.at[1]==="top"?c.targetHeight:-c.targetHeight,i=-2*c.offset[1];b.top+=c.collisionPosition.top<0?g+h+i:f>0?g+h+i:0}}}},a.offset.setOffset||(a.offset.setOffset=function(b,c){/static/.test(a.curCSS(b,"position"))&&(b.style.position="relative");var d=a(b),e=d.offset(),f=parseInt(a.curCSS(b,"top",!0),10)||0,g=parseInt(a.curCSS(b,"left",!0),10)||0,h={top:c.top-e.top+f,left:c.left-e.left+g};"using"in c?c.using.call(b,h):d.css(h)},a.fn.offset=function(b){var c=this[0];if(!c||!c.ownerDocument)return null;if(b)return this.each(function(){a.offset.setOffset(this,b)});return h.call(this)}),function(){var b=document.getElementsByTagName("body")[0],c=document.createElement("div"),d,e,g,h,i;d=document.createElement(b?"div":"body"),g={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},b&&a.extend(g,{position:"absolute",left:"-1000px",top:"-1000px"});for(var j in g)d.style[j]=g[j];d.appendChild(c),e=b||document.documentElement,e.insertBefore(d,e.firstChild),c.style.cssText="position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;",h=a(c).offset(function(a,b){return b}).offset(),d.innerHTML="",e.removeChild(d),i=h.top+h.left+(b?2e3:0),f.fractions=i>21&&i<22}()})(jQuery);/*
 * jQuery UI Button 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Button
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 */(function(a,b){var c,d,e,f,g="ui-button ui-widget ui-state-default ui-corner-all",h="ui-state-hover ui-state-active ",i="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",j=function(){var b=a(this).find(":ui-button");setTimeout(function(){b.button("refresh")},1)},k=function(b){var c=b.name,d=b.form,e=a([]);c&&(d?e=a(d).find("[name='"+c+"']"):e=a("[name='"+c+"']",b.ownerDocument).filter(function(){return!this.form}));return e};a.widget("ui.button",{options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",j),typeof this.options.disabled!="boolean"?this.options.disabled=!!this.element.propAttr("disabled"):this.element.propAttr("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var b=this,h=this.options,i=this.type==="checkbox"||this.type==="radio",l="ui-state-hover"+(i?"":" ui-state-active"),m="ui-state-focus";h.label===null&&(h.label=this.buttonElement.html()),this.buttonElement.addClass(g).attr("role","button").bind("mouseenter.button",function(){h.disabled||(a(this).addClass("ui-state-hover"),this===c&&a(this).addClass("ui-state-active"))}).bind("mouseleave.button",function(){h.disabled||a(this).removeClass(l)}).bind("click.button",function(a){h.disabled&&(a.preventDefault(),a.stopImmediatePropagation())}),this.element.bind("focus.button",function(){b.buttonElement.addClass(m)}).bind("blur.button",function(){b.buttonElement.removeClass(m)}),i&&(this.element.bind("change.button",function(){f||b.refresh()}),this.buttonElement.bind("mousedown.button",function(a){h.disabled||(f=!1,d=a.pageX,e=a.pageY)}).bind("mouseup.button",function(a){!h.disabled&&(d!==a.pageX||e!==a.pageY)&&(f=!0)})),this.type==="checkbox"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).toggleClass("ui-state-active"),b.buttonElement.attr("aria-pressed",b.element[0].checked)}):this.type==="radio"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).addClass("ui-state-active"),b.buttonElement.attr("aria-pressed","true");var c=b.element[0];k(c).not(c).map(function(){return a(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown.button",function(){if(h.disabled)return!1;a(this).addClass("ui-state-active"),c=this,a(document).one("mouseup",function(){c=null})}).bind("mouseup.button",function(){if(h.disabled)return!1;a(this).removeClass("ui-state-active")}).bind("keydown.button",function(b){if(h.disabled)return!1;(b.keyCode==a.ui.keyCode.SPACE||b.keyCode==a.ui.keyCode.ENTER)&&a(this).addClass("ui-state-active")}).bind("keyup.button",function(){a(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(b){b.keyCode===a.ui.keyCode.SPACE&&a(this).click()})),this._setOption("disabled",h.disabled),this._resetButton()},_determineButtonType:function(){this.element.is(":checkbox")?this.type="checkbox":this.element.is(":radio")?this.type="radio":this.element.is("input")?this.type="input":this.type="button";if(this.type==="checkbox"||this.type==="radio"){var a=this.element.parents().filter(":last"),b="label[for='"+this.element.attr("id")+"']";this.buttonElement=a.find(b),this.buttonElement.length||(a=a.length?a.siblings():this.element.siblings(),this.buttonElement=a.filter(b),this.buttonElement.length||(this.buttonElement=a.find(b))),this.element.addClass("ui-helper-hidden-accessible");var c=this.element.is(":checked");c&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.attr("aria-pressed",c)}else this.buttonElement=this.element},widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(g+" "+h+" "+i).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title"),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments);b==="disabled"?c?this.element.propAttr("disabled",!0):this.element.propAttr("disabled",!1):this._resetButton()},refresh:function(){var b=this.element.is(":disabled");b!==this.options.disabled&&this._setOption("disabled",b),this.type==="radio"?k(this.element[0]).each(function(){a(this).is(":checked")?a(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):a(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):this.type==="checkbox"&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var b=this.buttonElement.removeClass(i),c=a("<span></span>",this.element[0].ownerDocument).addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,e=d.primary&&d.secondary,f=[];d.primary||d.secondary?(this.options.text&&f.push("ui-button-text-icon"+(e?"s":d.primary?"-primary":"-secondary")),d.primary&&b.prepend("<span class='ui-button-icon-primary ui-icon "+d.primary+"'></span>"),d.secondary&&b.append("<span class='ui-button-icon-secondary ui-icon "+d.secondary+"'></span>"),this.options.text||(f.push(e?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||b.attr("title",c))):f.push("ui-button-text-only"),b.addClass(f.join(" "))}}}),a.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c),a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var b=this.element.css("direction")==="rtl";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(b?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(b?"ui-corner-left":"ui-corner-right").end().end()},destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy"),a.Widget.prototype.destroy.call(this)}})})(jQuery);/*
 * jQuery UI Dialog 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Dialog
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *  jquery.ui.button.js
 *	jquery.ui.draggable.js
 *	jquery.ui.mouse.js
 *	jquery.ui.position.js
 *	jquery.ui.resizable.js
 */(function(a,b){var c="ui-dialog ui-widget ui-widget-content ui-corner-all ",d={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},e={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},f=a.attrFn||{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0,click:!0};a.widget("ui.dialog",{options:{autoOpen:!0,buttons:{},closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:!1,maxWidth:!1,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",collision:"fit",using:function(b){var c=a(this).css(b).offset().top;c<0&&a(this).css("top",b.top-c)}},resizable:!0,show:null,stack:!0,title:"",width:300,zIndex:1e3},_create:function(){this.originalTitle=this.element.attr("title"),typeof this.originalTitle!="string"&&(this.originalTitle=""),this.options.title=this.options.title||this.originalTitle;var b=this,d=b.options,e=d.title||"&#160;",f=a.ui.dialog.getTitleId(b.element),g=(b.uiDialog=a("<div></div>")).appendTo(document.body).hide().addClass(c+d.dialogClass).css({zIndex:d.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(c){d.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}).attr({role:"dialog","aria-labelledby":f}).mousedown(function(a){b.moveToTop(!1,a)}),h=b.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g),i=(b.uiDialogTitlebar=a("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),j=a('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){j.addClass("ui-state-hover")},function(){j.removeClass("ui-state-hover")}).focus(function(){j.addClass("ui-state-focus")}).blur(function(){j.removeClass("ui-state-focus")}).click(function(a){b.close(a);return!1}).appendTo(i),k=(b.uiDialogTitlebarCloseText=a("<span></span>")).addClass("ui-icon ui-icon-closethick").text(d.closeText).appendTo(j),l=a("<span></span>").addClass("ui-dialog-title").attr("id",f).html(e).prependTo(i);a.isFunction(d.beforeclose)&&!a.isFunction(d.beforeClose)&&(d.beforeClose=d.beforeclose),i.find("*").add(i).disableSelection(),d.draggable&&a.fn.draggable&&b._makeDraggable(),d.resizable&&a.fn.resizable&&b._makeResizable(),b._createButtons(d.buttons),b._isOpen=!1,a.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy(),a.uiDialog.hide(),a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"),a.uiDialog.remove(),a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(b){var c=this,d,e;if(!1!==c._trigger("beforeClose",b)){c.overlay&&c.overlay.destroy(),c.uiDialog.unbind("keypress.ui-dialog"),c._isOpen=!1,c.options.hide?c.uiDialog.hide(c.options.hide,function(){c._trigger("close",b)}):(c.uiDialog.hide(),c._trigger("close",b)),a.ui.dialog.overlay.resize(),c.options.modal&&(d=0,a(".ui-dialog").each(function(){this!==c.uiDialog[0]&&(e=a(this).css("z-index"),isNaN(e)||(d=Math.max(d,e)))}),a.ui.dialog.maxZ=d);return c}},isOpen:function(){return this._isOpen},moveToTop:function(b,c){var d=this,e=d.options,f;if(e.modal&&!b||!e.stack&&!e.modal)return d._trigger("focus",c);e.zIndex>a.ui.dialog.maxZ&&(a.ui.dialog.maxZ=e.zIndex),d.overlay&&(a.ui.dialog.maxZ+=1,d.overlay.$el.css("z-index",a.ui.dialog.overlay.maxZ=a.ui.dialog.maxZ)),f={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()},a.ui.dialog.maxZ+=1,d.uiDialog.css("z-index",a.ui.dialog.maxZ),d.element.attr(f),d._trigger("focus",c);return d},open:function(){if(!this._isOpen){var b=this,c=b.options,d=b.uiDialog;b.overlay=c.modal?new a.ui.dialog.overlay(b):null,b._size(),b._position(c.position),d.show(c.show),b.moveToTop(!0),c.modal&&d.bind("keydown.ui-dialog",function(b){if(b.keyCode===a.ui.keyCode.TAB){var c=a(":tabbable",this),d=c.filter(":first"),e=c.filter(":last");if(b.target===e[0]&&!b.shiftKey){d.focus(1);return!1}if(b.target===d[0]&&b.shiftKey){e.focus(1);return!1}}}),a(b.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus(),b._isOpen=!0,b._trigger("open");return b}},_createButtons:function(b){var c=this,d=!1,e=a("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=a("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);c.uiDialog.find(".ui-dialog-buttonpane").remove(),typeof b=="object"&&b!==null&&a.each(b,function(){return!(d=!0)}),d&&(a.each(b,function(b,d){d=a.isFunction(d)?{click:d,text:b}:d;var e=a('<button type="button"></button>').click(function(){d.click.apply(c.element[0],arguments)}).appendTo(g);a.each(d,function(a,b){a!=="click"&&(a in f?e[a](b):e.attr(a,b))}),a.fn.button&&e.button()}),e.appendTo(c.uiDialog))},_makeDraggable:function(){function f(a){return{position:a.position,offset:a.offset}}var b=this,c=b.options,d=a(document),e;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(d,g){e=c.height==="auto"?"auto":a(this).height(),a(this).height(a(this).height()).addClass("ui-dialog-dragging"),b._trigger("dragStart",d,f(g))},drag:function(a,c){b._trigger("drag",a,f(c))},stop:function(g,h){c.position=[h.position.left-d.scrollLeft(),h.position.top-d.scrollTop()],a(this).removeClass("ui-dialog-dragging").height(e),b._trigger("dragStop",g,f(h)),a.ui.dialog.overlay.resize()}})},_makeResizable:function(c){function h(a){return{originalPosition:a.originalPosition,originalSize:a.originalSize,position:a.position,size:a.size}}c=c===b?this.options.resizable:c;var d=this,e=d.options,f=d.uiDialog.css("position"),g=typeof c=="string"?c:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:g,start:function(b,c){a(this).addClass("ui-dialog-resizing"),d._trigger("resizeStart",b,h(c))},resize:function(a,b){d._trigger("resize",a,h(b))},stop:function(b,c){a(this).removeClass("ui-dialog-resizing"),e.height=a(this).height(),e.width=a(this).width(),d._trigger("resizeStop",b,h(c)),a.ui.dialog.overlay.resize()}}).css("position",f).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(b){var c=[],d=[0,0],e;if(b){if(typeof b=="string"||typeof b=="object"&&"0"in b)c=b.split?b.split(" "):[b[0],b[1]],c.length===1&&(c[1]=c[0]),a.each(["left","top"],function(a,b){+c[a]===c[a]&&(d[a]=c[a],c[a]=b)}),b={my:c.join(" "),at:c.join(" "),offset:d.join(" ")};b=a.extend({},a.ui.dialog.prototype.options.position,b)}else b=a.ui.dialog.prototype.options.position;e=this.uiDialog.is(":visible"),e||this.uiDialog.show(),this.uiDialog.css({top:0,left:0}).position(a.extend({of:window},b)),e||this.uiDialog.hide()},_setOptions:function(b){var c=this,f={},g=!1;a.each(b,function(a,b){c._setOption(a,b),a in d&&(g=!0),a in e&&(f[a]=b)}),g&&this._size(),this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",f)},_setOption:function(b,d){var e=this,f=e.uiDialog;switch(b){case"beforeclose":b="beforeClose";break;case"buttons":e._createButtons(d);break;case"closeText":e.uiDialogTitlebarCloseText.text(""+d);break;case"dialogClass":f.removeClass(e.options.dialogClass).addClass(c+d);break;case"disabled":d?f.addClass("ui-dialog-disabled"):f.removeClass("ui-dialog-disabled");break;case"draggable":var g=f.is(":data(draggable)");g&&!d&&f.draggable("destroy"),!g&&d&&e._makeDraggable();break;case"position":e._position(d);break;case"resizable":var h=f.is(":data(resizable)");h&&!d&&f.resizable("destroy"),h&&typeof d=="string"&&f.resizable("option","handles",d),!h&&d!==!1&&e._makeResizable(d);break;case"title":a(".ui-dialog-title",e.uiDialogTitlebar).html(""+(d||"&#160;"))}a.Widget.prototype._setOption.apply(e,arguments)},_size:function(){var b=this.options,c,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0}),b.minWidth>b.width&&(b.width=b.minWidth),c=this.uiDialog.css({height:"auto",width:b.width}).height(),d=Math.max(0,b.minHeight-c);if(b.height==="auto")if(a.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();var f=this.element.css("height","auto").height();e||this.uiDialog.hide(),this.element.height(Math.max(f,d))}else this.element.height(Math.max(b.height-c,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}}),a.extend(a.ui.dialog,{version:"1.8.18",uuid:0,maxZ:0,getTitleId:function(a){var b=a.attr("id");b||(this.uuid+=1,b=this.uuid);return"ui-dialog-title-"+b},overlay:function(b){this.$el=a.ui.dialog.overlay.create(b)}}),a.extend(a.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:a.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(b){this.instances.length===0&&(setTimeout(function(){a.ui.dialog.overlay.instances.length&&a(document).bind(a.ui.dialog.overlay.events,function(b){if(a(b.target).zIndex()<a.ui.dialog.overlay.maxZ)return!1})},1),a(document).bind("keydown.dialog-overlay",function(c){b.options.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}),a(window).bind("resize.dialog-overlay",a.ui.dialog.overlay.resize));var c=(this.oldInstances.pop()||a("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});a.fn.bgiframe&&c.bgiframe(),this.instances.push(c);return c},destroy:function(b){var c=a.inArray(b,this.instances);c!=-1&&this.oldInstances.push(this.instances.splice(c,1)[0]),this.instances.length===0&&a([document,window]).unbind(".dialog-overlay"),b.remove();var d=0;a.each(this.instances,function(){d=Math.max(d,this.css("z-index"))}),this.maxZ=d},height:function(){var b,c;if(a.browser.msie&&a.browser.version<7){b=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),c=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return b<c?a(window).height()+"px":b+"px"}return a(document).height()+"px"},width:function(){var b,c;if(a.browser.msie){b=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth),c=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return b<c?a(window).width()+"px":b+"px"}return a(document).width()+"px"},resize:function(){var b=a([]);a.each(a.ui.dialog.overlay.instances,function(){b=b.add(this)}),b.css({width:0,height:0}).css({width:a.ui.dialog.overlay.width(),height:a.ui.dialog.overlay.height()})}}),a.extend(a.ui.dialog.overlay.prototype,{destroy:function(){a.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);/*
 * jQuery UI Progressbar 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Progressbar
 *
 * Depends:
 *   jquery.ui.core.js
 *   jquery.ui.widget.js
 */(function(a,b){a.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()}),this.valueDiv=a("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this.oldValue=this._value(),this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove(),a.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===b)return this._value();this._setOption("value",a);return this},_setOption:function(b,c){b==="value"&&(this.options.value=c,this._refreshValue(),this._value()===this.options.max&&this._trigger("complete")),a.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;typeof a!="number"&&(a=0);return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*this._value()/this.options.max},_refreshValue:function(){var a=this.value(),b=this._percentage();this.oldValue!==a&&(this.oldValue=a,this._trigger("change")),this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(b.toFixed(0)+"%"),this.element.attr("aria-valuenow",a)}}),a.extend(a.ui.progressbar,{version:"1.8.18"})})(jQuery);/*
 * jQuery UI Effects 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/
 */jQuery.effects||function(a,b){function l(b){if(!b||typeof b=="number"||a.fx.speeds[b])return!0;if(typeof b=="string"&&!a.effects[b])return!0;return!1}function k(b,c,d,e){typeof b=="object"&&(e=c,d=null,c=b,b=c.effect),a.isFunction(c)&&(e=c,d=null,c={});if(typeof c=="number"||a.fx.speeds[c])e=d,d=c,c={};a.isFunction(d)&&(e=d,d=null),c=c||{},d=d||c.duration,d=a.fx.off?0:typeof d=="number"?d:d in a.fx.speeds?a.fx.speeds[d]:a.fx.speeds._default,e=e||c.complete;return[b,c,d,e]}function j(a,b){var c={_:0},d;for(d in b)a[d]!=b[d]&&(c[d]=b[d]);return c}function i(b){var c,d;for(c in b)d=b[c],(d==null||a.isFunction(d)||c in g||/scrollbar/.test(c)||!/color/i.test(c)&&isNaN(parseFloat(d)))&&delete b[c];return b}function h(){var a=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,b={},c,d;if(a&&a.length&&a[0]&&a[a[0]]){var e=a.length;while(e--)c=a[e],typeof a[c]=="string"&&(d=c.replace(/\-(\w)/g,function(a,b){return b.toUpperCase()}),b[d]=a[c])}else for(c in a)typeof a[c]=="string"&&(b[c]=a[c]);return b}function d(b,d){var e;do{e=a.curCSS(b,d);if(e!=""&&e!="transparent"||a.nodeName(b,"body"))break;d="backgroundColor"}while(b=b.parentNode);return c(e)}function c(b){var c;if(b&&b.constructor==Array&&b.length==3)return b;if(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))return[parseInt(c[1],10),parseInt(c[2],10),parseInt(c[3],10)];if(c=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))return[parseFloat(c[1])*2.55,parseFloat(c[2])*2.55,parseFloat(c[3])*2.55];if(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))return[parseInt(c[1],16),parseInt(c[2],16),parseInt(c[3],16)];if(c=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))return[parseInt(c[1]+c[1],16),parseInt(c[2]+c[2],16),parseInt(c[3]+c[3],16)];if(c=/rgba\(0, 0, 0, 0\)/.exec(b))return e.transparent;return e[a.trim(b).toLowerCase()]}a.effects={},a.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","borderColor","color","outlineColor"],function(b,e){a.fx.step[e]=function(a){a.colorInit||(a.start=d(a.elem,e),a.end=c(a.end),a.colorInit=!0),a.elem.style[e]="rgb("+Math.max(Math.min(parseInt(a.pos*(a.end[0]-a.start[0])+a.start[0],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[1]-a.start[1])+a.start[1],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[2]-a.start[2])+a.start[2],10),255),0)+")"}});var e={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},f=["add","remove","toggle"],g={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};a.effects.animateClass=function(b,c,d,e){a.isFunction(d)&&(e=d,d=null);return this.queue(function(){var g=a(this),k=g.attr("style")||" ",l=i(h.call(this)),m,n=g.attr("class");a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),m=i(h.call(this)),g.attr("class",n),g.animate(j(l,m),{queue:!1,duration:c,easing:d,complete:function(){a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),typeof g.attr("style")=="object"?(g.attr("style").cssText="",g.attr("style").cssText=k):g.attr("style",k),e&&e.apply(this,arguments),a.dequeue(this)}})})},a.fn.extend({_addClass:a.fn.addClass,addClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{add:b},c,d,e]):this._addClass(b)},_removeClass:a.fn.removeClass,removeClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{remove:b},c,d,e]):this._removeClass(b)},_toggleClass:a.fn.toggleClass,toggleClass:function(c,d,e,f,g){return typeof d=="boolean"||d===b?e?a.effects.animateClass.apply(this,[d?{add:c}:{remove:c},e,f,g]):this._toggleClass(c,d):a.effects.animateClass.apply(this,[{toggle:c},d,e,f])},switchClass:function(b,c,d,e,f){return a.effects.animateClass.apply(this,[{add:c,remove:b},d,e,f])}}),a.extend(a.effects,{version:"1.8.18",save:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.data("ec.storage."+b[c],a[0].style[b[c]])},restore:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.css(b[c],a.data("ec.storage."+b[c]))},setMode:function(a,b){b=="toggle"&&(b=a.is(":hidden")?"show":"hide");return b},getBaseline:function(a,b){var c,d;switch(a[0]){case"top":c=0;break;case"middle":c=.5;break;case"bottom":c=1;break;default:c=a[0]/b.height}switch(a[1]){case"left":d=0;break;case"center":d=.5;break;case"right":d=1;break;default:d=a[1]/b.width}return{x:d,y:c}},createWrapper:function(b){if(b.parent().is(".ui-effects-wrapper"))return b.parent();var c={width:b.outerWidth(!0),height:b.outerHeight(!0),"float":b.css("float")},d=a("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e=document.activeElement;b.wrap(d),(b[0]===e||a.contains(b[0],e))&&a(e).focus(),d=b.parent(),b.css("position")=="static"?(d.css({position:"relative"}),b.css({position:"relative"})):(a.extend(c,{position:b.css("position"),zIndex:b.css("z-index")}),a.each(["top","left","bottom","right"],function(a,d){c[d]=b.css(d),isNaN(parseInt(c[d],10))&&(c[d]="auto")}),b.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"}));return d.css(c).show()},removeWrapper:function(b){var c,d=document.activeElement;if(b.parent().is(".ui-effects-wrapper")){c=b.parent().replaceWith(b),(b[0]===d||a.contains(b[0],d))&&a(d).focus();return c}return b},setTransition:function(b,c,d,e){e=e||{},a.each(c,function(a,c){unit=b.cssUnit(c),unit[0]>0&&(e[c]=unit[0]*d+unit[1])});return e}}),a.fn.extend({effect:function(b,c,d,e){var f=k.apply(this,arguments),g={options:f[1],duration:f[2],callback:f[3]},h=g.options.mode,i=a.effects[b];if(a.fx.off||!i)return h?this[h](g.duration,g.callback):this.each(function(){g.callback&&g.callback.call(this)});return i.call(this,g)},_show:a.fn.show,show:function(a){if(l(a))return this._show.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="show";return this.effect.apply(this,b)},_hide:a.fn.hide,hide:function(a){if(l(a))return this._hide.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="hide";return this.effect.apply(this,b)},__toggle:a.fn.toggle,toggle:function(b){if(l(b)||typeof b=="boolean"||a.isFunction(b))return this.__toggle.apply(this,arguments);var c=k.apply(this,arguments);c[1].mode="toggle";return this.effect.apply(this,c)},cssUnit:function(b){var c=this.css(b),d=[];a.each(["em","px","%","pt"],function(a,b){c.indexOf(b)>0&&(d=[parseFloat(c),b])});return d}}),a.easing.jswing=a.easing.swing,a.extend(a.easing,{def:"easeOutQuad",swing:function(b,c,d,e,f){return a.easing[a.easing.def](b,c,d,e,f)},easeInQuad:function(a,b,c,d,e){return d*(b/=e)*b+c},easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c},easeInOutQuad:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b+c;return-d/2*(--b*(b-2)-1)+c},easeInCubic:function(a,b,c,d,e){return d*(b/=e)*b*b+c},easeOutCubic:function(a,b,c,d,e){return d*((b=b/e-1)*b*b+1)+c},easeInOutCubic:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b+c;return d/2*((b-=2)*b*b+2)+c},easeInQuart:function(a,b,c,d,e){return d*(b/=e)*b*b*b+c},easeOutQuart:function(a,b,c,d,e){return-d*((b=b/e-1)*b*b*b-1)+c},easeInOutQuart:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b+c;return-d/2*((b-=2)*b*b*b-2)+c},easeInQuint:function(a,b,c,d,e){return d*(b/=e)*b*b*b*b+c},easeOutQuint:function(a,b,c,d,e){return d*((b=b/e-1)*b*b*b*b+1)+c},easeInOutQuint:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b*b+c;return d/2*((b-=2)*b*b*b*b+2)+c},easeInSine:function(a,b,c,d,e){return-d*Math.cos(b/e*(Math.PI/2))+d+c},easeOutSine:function(a,b,c,d,e){return d*Math.sin(b/e*(Math.PI/2))+c},easeInOutSine:function(a,b,c,d,e){return-d/2*(Math.cos(Math.PI*b/e)-1)+c},easeInExpo:function(a,b,c,d,e){return b==0?c:d*Math.pow(2,10*(b/e-1))+c},easeOutExpo:function(a,b,c,d,e){return b==e?c+d:d*(-Math.pow(2,-10*b/e)+1)+c},easeInOutExpo:function(a,b,c,d,e){if(b==0)return c;if(b==e)return c+d;if((b/=e/2)<1)return d/2*Math.pow(2,10*(b-1))+c;return d/2*(-Math.pow(2,-10*--b)+2)+c},easeInCirc:function(a,b,c,d,e){return-d*(Math.sqrt(1-(b/=e)*b)-1)+c},easeOutCirc:function(a,b,c,d,e){return d*Math.sqrt(1-(b=b/e-1)*b)+c},easeInOutCirc:function(a,b,c,d,e){if((b/=e/2)<1)return-d/2*(Math.sqrt(1-b*b)-1)+c;return d/2*(Math.sqrt(1-(b-=2)*b)+1)+c},easeInElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g))+c},easeOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*b)*Math.sin((b*e-f)*2*Math.PI/g)+d+c},easeInOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e/2)==2)return c+d;g||(g=e*.3*1.5);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);if(b<1)return-0.5*h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)+c;return h*Math.pow(2,-10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)*.5+d+c},easeInBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);return e*(c/=f)*c*((g+1)*c-g)+d},easeOutBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);return e*((c=c/f-1)*c*((g+1)*c+g)+1)+d},easeInOutBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);if((c/=f/2)<1)return e/2*c*c*(((g*=1.525)+1)*c-g)+d;return e/2*((c-=2)*c*(((g*=1.525)+1)*c+g)+2)+d},easeInBounce:function(b,c,d,e,f){return e-a.easing.easeOutBounce(b,f-c,0,e,f)+d},easeOutBounce:function(a,b,c,d,e){return(b/=e)<1/2.75?d*7.5625*b*b+c:b<2/2.75?d*(7.5625*(b-=1.5/2.75)*b+.75)+c:b<2.5/2.75?d*(7.5625*(b-=2.25/2.75)*b+.9375)+c:d*(7.5625*(b-=2.625/2.75)*b+.984375)+c},easeInOutBounce:function(b,c,d,e,f){if(c<f/2)return a.easing.easeInBounce(b,c*2,0,e,f)*.5+d;return a.easing.easeOutBounce(b,c*2-f,0,e,f)*.5+e*.5+d}})}(jQuery);/*
 * jQuery UI Effects Fade 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Fade
 *
 * Depends:
 *	jquery.effects.core.js
 */(function(a,b){a.effects.fade=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"hide");c.animate({opacity:d},{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
 * jQuery UI Effects Highlight 1.8.18
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Effects/Highlight
 *
 * Depends:
 *	jquery.effects.core.js
 */(function(a,b){a.effects.highlight=function(b){return this.queue(function(){var c=a(this),d=["backgroundImage","backgroundColor","opacity"],e=a.effects.setMode(c,b.options.mode||"show"),f={backgroundColor:c.css("backgroundColor")};e=="hide"&&(f.opacity=0),a.effects.save(c,d),c.show().css({backgroundImage:"none",backgroundColor:b.options.color||"#ffff99"}).animate(f,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),e=="show"&&!a.support.opacity&&this.style.removeAttribute("filter"),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);
/*!
 * jQuery Cookie Plugin v1.4.1
 * https://github.com/carhartl/jquery-cookie
 *
 * Copyright 2013 Klaus Hartl
 * Released under the MIT license
 */
(function (factory) {
	if (typeof define === 'function' && define.amd) {
		// AMD
		define(['jquery'], factory);
	} else if (typeof exports === 'object') {
		// CommonJS
		factory(require('jquery'));
	} else {
		// Browser globals
		factory(jQuery);
	}
}(function ($) {

	var pluses = /\+/g;

	function encode(s) {
		return config.raw ? s : encodeURIComponent(s);
	}

	function decode(s) {
		return config.raw ? s : decodeURIComponent(s);
	}

	function stringifyCookieValue(value) {
		return encode(config.json ? JSON.stringify(value) : String(value));
	}

	function parseCookieValue(s) {
		if (s.indexOf('"') === 0) {
			// This is a quoted cookie as according to RFC2068, unescape...
			s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
		}

		try {
			// Replace server-side written pluses with spaces.
			// If we can't decode the cookie, ignore it, it's unusable.
			// If we can't parse the cookie, ignore it, it's unusable.
			s = decodeURIComponent(s.replace(pluses, ' '));
			return config.json ? JSON.parse(s) : s;
		} catch(e) {}
	}

	function read(s, converter) {
		var value = config.raw ? s : parseCookieValue(s);
		return $.isFunction(converter) ? converter(value) : value;
	}

	var config = $.cookie = function (key, value, options) {

		// Write

		if (value !== undefined && !$.isFunction(value)) {
			options = $.extend({}, config.defaults, options);

			if (typeof options.expires === 'number') {
				var days = options.expires, t = options.expires = new Date();
				t.setTime(+t + days * 864e+5);
			}

			return (document.cookie = [
				encode(key), '=', stringifyCookieValue(value),
				options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
				options.path    ? '; path=' + options.path : '',
				options.domain  ? '; domain=' + options.domain : '',
				options.secure  ? '; secure' : ''
			].join(''));
		}

		// Read

		var result = key ? undefined : {};

		// To prevent the for loop in the first place assign an empty array
		// in case there are no cookies at all. Also prevents odd result when
		// calling $.cookie().
		var cookies = document.cookie ? document.cookie.split('; ') : [];

		for (var i = 0, l = cookies.length; i < l; i++) {
			var parts = cookies[i].split('=');
			var name = decode(parts.shift());
			var cookie = parts.join('=');

			if (key && key === name) {
				// If second argument (value) is a function it's a converter...
				result = read(cookie, value);
				break;
			}

			// Prevent storing a cookie that we couldn't decode.
			if (!key && (cookie = read(cookie)) !== undefined) {
				result[name] = cookie;
			}
		}

		return result;
	};

	config.defaults = {};

	$.removeCookie = function (key, options) {
		if ($.cookie(key) === undefined) {
			return false;
		}

		// Must not alter options, thus extending a fresh object...
		$.cookie(key, '', $.extend({}, options, { expires: -1 }));
		return !$.cookie(key);
	};

}));
 
/*
 * jQuery File Upload User Interface Plugin 4.4.1
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://creativecommons.org/licenses/MIT/
 */

/*jslint browser: true */
/*global jQuery, FileReader, URL, webkitURL */

(function ($) {
    'use strict';

    var undef = 'undefined',
        func = 'function',
        UploadHandler,
        methods,

        MultiLoader = function (callBack) {
            var loaded = 0,
                list = [];
            this.complete = function () {
                loaded += 1;
                if (loaded === list.length + 1) {
                    // list.length * onComplete + 1 * onLoadAll
                    callBack(list);
                    loaded = 0;
                    list = [];
                }
            };
            this.push = function (item) {
                list.push(item);
            };
            this.getList = function () {
                return list;
            };
        };
        
    UploadHandler = function (container, options) {
        var uploadHandler = this,
            dragOverTimeout,
            isDropZoneEnlarged,
            multiLoader = new MultiLoader(function (list) {
                uploadHandler.hideProgressBarAll(function () {
                    uploadHandler.resetProgressBarAll();
                    if (typeof uploadHandler.onCompleteAll === func) {
                        uploadHandler.onCompleteAll(list);
                    }
                });
            }),
            getUploadTable = function (handler) {
                return typeof handler.uploadTable === func ?
                    handler.uploadTable(handler) : handler.uploadTable;
            },
            getDownloadTable = function (handler) {
                return typeof handler.downloadTable === func ?
                    handler.downloadTable(handler) : handler.downloadTable;
            };
        
        this.requestHeaders = {'Accept': 'application/json, text/javascript, */*; q=0.01'};
        this.dropZone = container;
        this.imageTypes = /^image\/(gif|jpeg|png)$/;
        this.previewMaxWidth = this.previewMaxHeight = 80;
        this.previewLoadDelay = 100;
        this.previewAsCanvas = true;
        this.previewSelector = '.file_upload_preview';
        this.progressSelector = '.file_upload_progress div';
        this.cancelSelector = '.file_upload_cancel button';
        this.cssClassSmall = 'file_upload_small';
        this.cssClassLarge = 'file_upload_large';
        this.cssClassHighlight = 'file_upload_highlight';
        this.dropEffect = 'highlight';
        this.uploadTable = this.downloadTable = null;
        this.buildUploadRow = this.buildDownloadRow = null;
        this.progressAllNode = null;

        this.loadImage = function (file, callBack, maxWidth, maxHeight, imageTypes, noCanvas) {
            var img,
                scaleImage,
                urlAPI,
                fileReader;
            if (imageTypes && !imageTypes.test(file.type)) {
                return null;
            }
            scaleImage = function (img) {
                var canvas = document.createElement('canvas'),
                    scale = Math.min(
                        (maxWidth || img.width) / img.width,
                        (maxHeight || img.height) / img.height
                    );
                if (scale > 1) {
                    scale = 1;
                }
                img.width = parseInt(img.width * scale, 10);
                img.height = parseInt(img.height * scale, 10);
                if (noCanvas || typeof canvas.getContext !== func) {
                    return img;
                }
                canvas.width = img.width;
                canvas.height = img.height;
                canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
                return canvas;
            };
            img = document.createElement('img');
            urlAPI = typeof URL !== undef ? URL : typeof webkitURL !== undef ? webkitURL : null;
            if (urlAPI && typeof urlAPI.createObjectURL === func) {
                img.onload = function () {
                    urlAPI.revokeObjectURL(this.src);
                    callBack(scaleImage(img));
                };
                img.src = urlAPI.createObjectURL(file);
            } else if (typeof FileReader !== undef &&
                    typeof FileReader.prototype.readAsDataURL === func) {
                img.onload = function () {
                    callBack(scaleImage(img));
                };
                fileReader = new FileReader();
                fileReader.onload = function (e) {
                    img.src = e.target.result;
                };
                fileReader.readAsDataURL(file);
            } else {
                callBack(null);
            }
        };

        this.addNode = function (parentNode, node, callBack) {
            if (parentNode && parentNode.length && node && node.length) {
                node.css('display', 'none').appendTo(parentNode).fadeIn(function () {
                    if (typeof callBack === func) {
                        try {
                            callBack();
                        } catch (e) {
                            // Fix endless exception loop:
                            node.stop();
                            throw e;
                        }
                    }
                });
            } else if (typeof callBack === func) {
                callBack();
            }
        };

        this.removeNode = function (node, callBack) {
            if (node && node.length) {
                node.fadeOut(function () {
                    node.remove();
                    if (typeof callBack === func) {
                        try {
                            callBack();
                        } catch (e) {
                            // Fix endless exception loop:
                            node.stop();
                            throw e;
                        }
                    }
                });
            } else if (typeof callBack === func) {
                callBack();
            }
        };
        
        this.replaceNode = function (oldNode, newNode, callBack) {
            if (!(newNode && newNode.length)) {
                return uploadHandler.removeNode(oldNode, callBack);
            }
            if (oldNode && oldNode.length) {
                oldNode.fadeOut(function () {
                    newNode.css('display', 'none');
                    oldNode.replaceWith(newNode);
                    newNode.fadeIn(function () {
                        if (typeof callBack === func) {
                            try {
                                callBack();
                            } catch (e) {
                                // Fix endless exception loop:
                                oldNode.stop();
                                newNode.stop();
                                throw e;
                            }
                        }
                    });
                });
            } else if (typeof callBack === func) {
                callBack();
            }
        };

        this.resetProgressBarAll = function () {
            if (uploadHandler.progressbarAll) {
                uploadHandler.progressbarAll.progressbar(
                    'value',
                    0
                );
            }
        };

        this.hideProgressBarAll = function (callBack) {
            if (uploadHandler.progressbarAll && !$(getUploadTable(uploadHandler))
                    .find(uploadHandler.progressSelector + ':visible:first').length) {
                uploadHandler.progressbarAll.fadeOut(callBack);
            } else if (typeof callBack === func) {
                callBack();
            }
        };

        this.onAbort = function (event, files, index, xhr, handler) {
            handler.removeNode(handler.uploadRow, handler.hideProgressBarAll);
        };
        
        this.cancelUpload = function (event, files, index, xhr, handler) {
            var readyState = xhr.readyState;
            xhr.abort();
            // If readyState is below 2, abort() has no effect:
            if (typeof readyState !== 'number' || readyState < 2) {
                handler.onAbort(event, files, index, xhr, handler);
            }
        };
        
        this.initProgressBar = function (node, value) {
            if (!node || !node.length) {
                return null;
            }
            if (typeof node.progressbar === func) {
                return node.progressbar({
                    value: value
                });
            } else {
                node.addClass('progressbar')
                    .append($('<div/>').css('width', value + '%'))
                    .progressbar = function (key, value) {
                        return this.each(function () {
                            if (key === 'destroy') {
                                $(this).removeClass('progressbar').empty();
                            } else {
                                $(this).children().css('width', value + '%');
                            }
                        });
                    };
                return node;
            }
        };
        
        this.destroyProgressBar = function (node) {
            if (!node || !node.length) {
                return null;
            }
            return node.progressbar('destroy');
        };
        
        this.initUploadProgress = function (xhr, handler) {
            if (!xhr.upload && handler.progressbar) {
                handler.progressbar.progressbar(
                    'value',
                    100 // indeterminate progress displayed by a full animated progress bar
                );
            }
        };

        this.initUploadProgressAll = function () {
            if (uploadHandler.progressbarAll && uploadHandler.progressbarAll.is(':hidden')) {
                uploadHandler.progressbarAll.fadeIn();
            }
        };

        this.onSend = function (event, files, index, xhr, handler) {
            handler.initUploadProgress(xhr, handler);
        };

        this.onProgress = function (event, files, index, xhr, handler) {
            if (handler.progressbar && event.lengthComputable) {
                handler.progressbar.progressbar(
                    'value',
                    parseInt(event.loaded / event.total * 100, 10)
                );
            }
        };

        this.onProgressAll = function (event, list) {
            if (uploadHandler.progressbarAll && event.lengthComputable) {
                uploadHandler.progressbarAll.progressbar(
                    'value',
                    parseInt(event.loaded / event.total * 100, 10)
                );
            }
        };

        this.onLoadAll = function (list) {
            multiLoader.complete();
        };

        this.initProgressBarAll = function () {
            if (!uploadHandler.progressbarAll) {
                uploadHandler.progressbarAll = uploadHandler.initProgressBar(
                    (typeof uploadHandler.progressAllNode === func ?
                    uploadHandler.progressAllNode(uploadHandler) : uploadHandler.progressAllNode),
                    0
                );
            }
        };
        
        this.destroyProgressBarAll = function () {
            uploadHandler.destroyProgressBar(uploadHandler.progressbarAll);
        };

        this.loadPreviewImage = function (files, index, handler) {
            index = index || 0;
            handler.uploadRow.find(handler.previewSelector).each(function () {
                var previewNode = $(this),
                    file = files[index];
                setTimeout(function () {
                    handler.loadImage(
                        file,
                        function (img) {
                            handler.addNode(
                                previewNode,
                                $(img)
                            );
                        },
                        handler.previewMaxWidth,
                        handler.previewMaxHeight,
                        handler.imageTypes,
                        !handler.previewAsCanvas
                    );
                }, handler.previewLoadDelay);
                index += 1;
            });
        };

        this.initUploadRow = function (event, files, index, xhr, handler) {
            var uploadRow = handler.uploadRow = (typeof handler.buildUploadRow === func ?
                handler.buildUploadRow(files, index, handler) : null);
            if (uploadRow) {
                handler.progressbar = handler.initProgressBar(
                    uploadRow.find(handler.progressSelector),
                    0
                );
                uploadRow.find(handler.cancelSelector).click(function (e) {
                    handler.cancelUpload(e, files, index, xhr, handler);
                    e.preventDefault();
                });
                handler.loadPreviewImage(files, index, handler);
            }
        };
        
        this.initUpload = function (event, files, index, xhr, handler, callBack) {
            handler.initUploadRow(event, files, index, xhr, handler);
            handler.addNode(
                getUploadTable(handler),
                handler.uploadRow,
                function () {
                    if (typeof handler.beforeSend === func) {
                        handler.beforeSend(event, files, index, xhr, handler, callBack);
                    } else {
                        callBack();
                    }
                }
            );
            handler.initUploadProgressAll();
        };
        
        this.parseResponse = function (xhr, handler) {
            if (typeof xhr.responseText !== undef) {
                return $.parseJSON(xhr.responseText);
            } else {
                // Instead of an XHR object, an iframe is used for legacy browsers:
                return $.parseJSON(xhr.contents().text());
            }
        };
        
        this.initDownloadRow = function (event, files, index, xhr, handler) {
            var json, downloadRow;
            try {
                json = handler.response = handler.parseResponse(xhr, handler);
            } catch (e) {
                if (typeof handler.onError === func) {
                    handler.originalEvent = event;
                    handler.onError(e, files, index, xhr, handler);
                } else {
                    throw e;
                }
            }
            downloadRow = handler.downloadRow = (typeof handler.buildDownloadRow === func ?
                handler.buildDownloadRow(json, handler) : null);
        };
        
        this.onLoad = function (event, files, index, xhr, handler) {
            var uploadTable = getUploadTable(handler),
                downloadTable = getDownloadTable(handler),
                callBack = function () {
                    if (typeof handler.onComplete === func) {
                        handler.onComplete(event, files, index, xhr, handler);
                    }
                    multiLoader.complete();
                };
            multiLoader.push(Array.prototype.slice.call(arguments, 1));
            handler.initDownloadRow(event, files, index, xhr, handler);
            if (uploadTable && handler.uploadRow && handler.uploadRow.length &&
                    (!downloadTable || uploadTable.get(0) === downloadTable.get(0))) {
                handler.replaceNode(handler.uploadRow, handler.downloadRow, callBack);
            } else {
                handler.removeNode(handler.uploadRow, function () {
                    handler.addNode(
                        downloadTable,
                        handler.downloadRow,
                        callBack
                    );
                });
            }
        };

        this.dropZoneEnlarge = function () {
            if (!isDropZoneEnlarged) {
                if (typeof uploadHandler.dropZone.switchClass === func) {
                    uploadHandler.dropZone.switchClass(
                        uploadHandler.cssClassSmall,
                        uploadHandler.cssClassLarge
                    );
                } else {
                    uploadHandler.dropZone.addClass(uploadHandler.cssClassLarge);
                    uploadHandler.dropZone.removeClass(uploadHandler.cssClassSmall);
                }
                isDropZoneEnlarged = true;
            }
        };
        
        this.dropZoneReduce = function () {
            if (typeof uploadHandler.dropZone.switchClass === func) {
                uploadHandler.dropZone.switchClass(
                    uploadHandler.cssClassLarge,
                    uploadHandler.cssClassSmall
                );
            } else {
                uploadHandler.dropZone.addClass(uploadHandler.cssClassSmall);
                uploadHandler.dropZone.removeClass(uploadHandler.cssClassLarge);
            }
            isDropZoneEnlarged = false;
        };

        this.onDocumentDragEnter = function (event) {
            uploadHandler.dropZoneEnlarge();
        };
        
        this.onDocumentDragOver = function (event) {
            if (dragOverTimeout) {
                clearTimeout(dragOverTimeout);
            }
            dragOverTimeout = setTimeout(function () {
                uploadHandler.dropZoneReduce();
            }, 200);
        };
        
        this.onDragEnter = this.onDragLeave = function (event) {
            uploadHandler.dropZone.toggleClass(uploadHandler.cssClassHighlight);
        };
        
        this.onDrop = function (event) {
            if (dragOverTimeout) {
                clearTimeout(dragOverTimeout);
            }
            if (uploadHandler.dropEffect && typeof uploadHandler.dropZone.effect === func) {
                uploadHandler.dropZone.effect(uploadHandler.dropEffect, function () {
                    uploadHandler.dropZone.removeClass(uploadHandler.cssClassHighlight);
                    uploadHandler.dropZoneReduce();
                });
            } else {
                uploadHandler.dropZone.removeClass(uploadHandler.cssClassHighlight);
                uploadHandler.dropZoneReduce();
            }
        };

        this.init = function () {
            uploadHandler.initProgressBarAll();
            if (typeof uploadHandler.initExtended === func) {
                uploadHandler.initExtended();
            }
        };
        
        this.destroy = function () {
            if (typeof uploadHandler.destroyExtended === func) {
                uploadHandler.destroyExtended();
            }
            uploadHandler.destroyProgressBarAll();
        };

        $.extend(this, options);
    };

    methods = {
        init : function (options) {
            return this.each(function () {
                $(this).fileUpload(new UploadHandler($(this), options));
            });
        },
        
        option: function (option, value, namespace) {
            if (!option || (typeof option === 'string' && typeof value === undef)) {
                return $(this).fileUpload('option', option, value, namespace);
            }
            return this.each(function () {
                $(this).fileUpload('option', option, value, namespace);
            });
        },
            
        destroy : function (namespace) {
            return this.each(function () {
                $(this).fileUpload('destroy', namespace);
            });
        },
        
        upload: function (files, namespace) {
            return this.each(function () {
                $(this).fileUpload('upload', files, namespace);
            });
        }
    };
    
    $.fn.fileUploadUI = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method "' + method + '" does not exist on jQuery.fileUploadUI');
        }
    };
    
}(jQuery));
/*
 * jQuery File Upload Plugin 4.4.3
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://creativecommons.org/licenses/MIT/
 */

/*jslint browser: true */
/*global XMLHttpRequestUpload, File, FileReader, FormData, ProgressEvent, unescape, jQuery, upload */

(function ($) {
    'use strict';

    var defaultNamespace = 'file_upload',
        undef = 'undefined',
        func = 'function',
        FileUpload,
        methods,

        MultiLoader = function (callBack, numOrList) {
            var loaded = 0,
                list = [];
            if (numOrList) {
                if (numOrList.length) {
                    list = numOrList;
                } else {
                    list[numOrList - 1] = null;
                }
            }
            this.complete = function () {
                loaded += 1;
                if (loaded === list.length) {
                    callBack(list);
                    loaded = 0;
                    list = [];
                }
            };
            this.push = function (item) {
                list.push(item);
            };
            this.getList = function () {
                return list;
            };
        },
        
        SequenceHandler = function () {
            var sequence = [];
            this.push = function (callBack) {
                sequence.push(callBack);
                if (sequence.length === 1) {
                    callBack();
                }
            };
            this.next = function () {
                sequence.shift();
                if (sequence.length) {
                    sequence[0]();
                }
            };
        };
        
    FileUpload = function (container) {
        var fileUpload = this,
            uploadForm,
            fileInput,
            settings = {
                namespace: defaultNamespace,
                uploadFormFilter: function (index) {
                    return true;
                },
                fileInputFilter: function (index) {
                    return true;
                },
                cssClass: defaultNamespace,
                dragDropSupport: true,
                dropZone: container,
                url: function (form) {
                    return form.attr('action');
                },
                method: function (form) {
                    return form.attr('method');
                },
                fieldName: function (input) {
                    return input.attr('name');
                },
                formData: function (form) {
                    return form.serializeArray();
                },
                requestHeaders: null,
                multipart: true,
                multiFileRequest: false,
                withCredentials: false,
                forceIframeUpload: false,
                sequentialUploads: false,
                maxChunkSize: null,
                maxFileReaderSize: 50000000
            },
            documentListeners = {},
            dropZoneListeners = {},
            protocolRegExp = /^http(s)?:\/\//,
            optionsReference,
            multiLoader = new MultiLoader(function (list) {
                if (typeof settings.onLoadAll === func) {
                    settings.onLoadAll(list);
                }
            }),
            sequenceHandler = new SequenceHandler(),
            
            completeNext = function () {
                multiLoader.complete();
                sequenceHandler.next();
            },

            isXHRUploadCapable = function () {
                return typeof XMLHttpRequest !== undef && typeof XMLHttpRequestUpload !== undef &&
                    typeof File !== undef && (!settings.multipart || typeof FormData !== undef ||
                    (typeof FileReader !== undef && typeof XMLHttpRequest.prototype.sendAsBinary === func));
            },

            initEventHandlers = function () {
                if (settings.dragDropSupport) {
                    if (typeof settings.onDocumentDragEnter === func) {
                        documentListeners['dragenter.' + settings.namespace] = function (e) {
                            settings.onDocumentDragEnter(e);
                        };
                    }
                    if (typeof settings.onDocumentDragLeave === func) {
                        documentListeners['dragleave.' + settings.namespace] = function (e) {
                            settings.onDocumentDragLeave(e);
                        };
                    }
                    documentListeners['dragover.'   + settings.namespace] = fileUpload.onDocumentDragOver;
                    documentListeners['drop.'       + settings.namespace] = fileUpload.onDocumentDrop;
                    $(document).bind(documentListeners);
                    if (typeof settings.onDragEnter === func) {
                        dropZoneListeners['dragenter.' + settings.namespace] = function (e) {
                            settings.onDragEnter(e);
                        };
                    }
                    if (typeof settings.onDragLeave === func) {
                        dropZoneListeners['dragleave.' + settings.namespace] = function (e) {
                            settings.onDragLeave(e);
                        };
                    }
                    dropZoneListeners['dragover.'   + settings.namespace] = fileUpload.onDragOver;
                    dropZoneListeners['drop.'       + settings.namespace] = fileUpload.onDrop;
                    settings.dropZone.bind(dropZoneListeners);
                }
                fileInput.bind('change.' + settings.namespace, fileUpload.onChange);
            },

            removeEventHandlers = function () {
                $.each(documentListeners, function (key, value) {
                    $(document).unbind(key, value);
                });
                $.each(dropZoneListeners, function (key, value) {
                    settings.dropZone.unbind(key, value);
                });
                fileInput.unbind('change.' + settings.namespace);
            },

            isChunkedUpload = function (settings) {
                return typeof settings.uploadedBytes !== undef;
            },

            createProgressEvent = function (lengthComputable, loaded, total) {
                var event;
                if (typeof document.createEvent === func && typeof ProgressEvent !== undef) {
                    event = document.createEvent('ProgressEvent');
                    event.initProgressEvent(
                        'progress',
                        false,
                        false,
                        lengthComputable,
                        loaded,
                        total
                    );
                } else {
                    event = {
                        lengthComputable: true,
                        loaded: loaded,
                        total: total
                    };
                }
                return event;
            },

            getProgressTotal = function (files, index, settings) {
                var i,
                    total;
                if (typeof settings.progressTotal === undef) {
                    if (files[index]) {
                        total = files[index].size;
                        settings.progressTotal = total ? total : 1;
                    } else {
                        total = 0;
                        for (i = 0; i < files.length; i += 1) {
                            total += files[i].size;
                        }
                        settings.progressTotal = total;
                    }
                }
                return settings.progressTotal;
            },

            handleGlobalProgress = function (event, files, index, xhr, settings) {
                var progressEvent,
                    loaderList,
                    globalLoaded = 0,
                    globalTotal = 0;
                if (event.lengthComputable && typeof settings.onProgressAll === func) {
                    settings.progressLoaded = parseInt(
                        event.loaded / event.total * getProgressTotal(files, index, settings),
                        10
                    );
                    loaderList = multiLoader.getList();
                    $.each(loaderList, function (index, item) {
                        // item is an array with [files, index, xhr, settings]
                        globalLoaded += item[3].progressLoaded || 0;
                        globalTotal += getProgressTotal(item[0], item[1], item[3]);
                    });
                    progressEvent = createProgressEvent(
                        true,
                        globalLoaded,
                        globalTotal
                    );
                    settings.onProgressAll(progressEvent, loaderList);
                }
            },
            
            handleLoadEvent = function (event, files, index, xhr, settings) {
                var progressEvent;
                if (isChunkedUpload(settings)) {
                    settings.uploadedBytes += settings.chunkSize;
                    progressEvent = createProgressEvent(
                        true,
                        settings.uploadedBytes,
                        files[index].size
                    );
                    if (typeof settings.onProgress === func) {
                        settings.onProgress(progressEvent, files, index, xhr, settings);
                    }
                    handleGlobalProgress(progressEvent, files, index, xhr, settings);
                    if (settings.uploadedBytes < files[index].size) {
                        if (typeof settings.resumeUpload === func) {
                            settings.resumeUpload(
                                event,
                                files,
                                index,
                                xhr,
                                settings,
                                function () {
                                    upload(event, files, index, xhr, settings, true);
                                }
                            );
                        } else {
                            upload(event, files, index, xhr, settings, true);
                        }
                        return;
                    }
                }
                settings.progressLoaded = getProgressTotal(files, index, settings);
                if (typeof settings.onLoad === func) {
                    settings.onLoad(event, files, index, xhr, settings);
                }
                completeNext();
            },
            
            handleProgressEvent = function (event, files, index, xhr, settings) {
                var progressEvent = event;
                if (isChunkedUpload(settings) && event.lengthComputable) {
                    progressEvent = createProgressEvent(
                        true,
                        settings.uploadedBytes + parseInt(event.loaded / event.total * settings.chunkSize, 10),
                        files[index].size
                    );
                }
                if (typeof settings.onProgress === func) {
                    settings.onProgress(progressEvent, files, index, xhr, settings);
                }
                handleGlobalProgress(progressEvent, files, index, xhr, settings);
            },
            
            initUploadEventHandlers = function (files, index, xhr, settings) {
                if (xhr.upload) {
                    xhr.upload.onprogress = function (e) {
                        handleProgressEvent(e, files, index, xhr, settings);
                    };
                }
                xhr.onload = function (e) {
                    handleLoadEvent(e, files, index, xhr, settings);
                };
                xhr.onabort = function (e) {
                    settings.progressTotal = settings.progressLoaded;
                    if (typeof settings.onAbort === func) {
                        settings.onAbort(e, files, index, xhr, settings);
                    }
                    completeNext();
                };
                xhr.onerror = function (e) {
                    settings.progressTotal = settings.progressLoaded;
                    if (typeof settings.onError === func) {
                        settings.onError(e, files, index, xhr, settings);
                    }
                    completeNext();
                };
            },

            getUrl = function (settings) {
                if (typeof settings.url === func) {
                    return settings.url(settings.uploadForm || uploadForm);
                }
                return settings.url;
            },
            
            getMethod = function (settings) {
                if (typeof settings.method === func) {
                    return settings.method(settings.uploadForm || uploadForm);
                }
                return settings.method;
            },
            
            getFieldName = function (settings) {
                if (typeof settings.fieldName === func) {
                    return settings.fieldName(settings.fileInput || fileInput);
                }
                return settings.fieldName;
            },

            getFormData = function (settings) {
                var formData;
                if (typeof settings.formData === func) {
                    return settings.formData(settings.uploadForm || uploadForm);
                } else if ($.isArray(settings.formData)) {
                    return settings.formData;
                } else if (settings.formData) {
                    formData = [];
                    $.each(settings.formData, function (name, value) {
                        formData.push({name: name, value: value});
                    });
                    return formData;
                }
                return [];
            },

            isSameDomain = function (url) {
                if (protocolRegExp.test(url)) {
                    var host = location.host,
                        indexStart = location.protocol.length + 2,
                        index = url.indexOf(host, indexStart),
                        pathIndex = index + host.length;
                    if ((index === indexStart || index === url.indexOf('@', indexStart) + 1) &&
                            (url.length === pathIndex || $.inArray(url.charAt(pathIndex), ['/', '?', '#']) !== -1)) {
                        return true;
                    }
                    return false;
                }
                return true;
            },

            initUploadRequest = function (files, index, xhr, settings) {
                var file = files[index],
                    url = getUrl(settings),
                    sameDomain = isSameDomain(url);
                xhr.open(getMethod(settings), url, true);
                if (sameDomain) {
                    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                    if (!settings.multipart || isChunkedUpload(settings)) {
                        xhr.setRequestHeader('X-File-Name', file.name);
                        xhr.setRequestHeader('X-File-Type', file.type);
                        xhr.setRequestHeader('X-File-Size', file.size);
                        if (!isChunkedUpload(settings)) {
                            xhr.setRequestHeader('Content-Type', file.type);
                        } else if (!settings.multipart) {
                            xhr.setRequestHeader('Content-Type', 'application/octet-stream');
                        }
                    }
                } else if (settings.withCredentials) {
                    xhr.withCredentials = true;
                }
                if ($.isArray(settings.requestHeaders)) {
                    $.each(settings.requestHeaders, function (index, header) {
                        xhr.setRequestHeader(header.name, header.value);
                    });
                } else if (settings.requestHeaders) {
                    $.each(settings.requestHeaders, function (name, value) {
                        xhr.setRequestHeader(name, value);
                    });
                }
            },

            formDataUpload = function (files, xhr, settings) {
                var formData = new FormData(),
                    i;
                $.each(getFormData(settings), function (index, field) {
                    formData.append(field.name, field.value);
                });
                for (i = 0; i < files.length; i += 1) {
                    formData.append(getFieldName(settings), files[i]);
                }
                xhr.send(formData);
            },

            loadFileContent = function (file, callBack) {
                file.reader = new FileReader();
                file.reader.onload = callBack;
                file.reader.readAsBinaryString(file);
            },

            utf8encode = function (str) {
                return unescape(encodeURIComponent(str));
            },

            buildMultiPartFormData = function (boundary, files, filesFieldName, fields) {
                var doubleDash = '--',
                    crlf     = '\r\n',
                    formData = '',
                    buffer = [];
                $.each(fields, function (index, field) {
                    formData += doubleDash + boundary + crlf +
                        'Content-Disposition: form-data; name="' +
                        utf8encode(field.name) +
                        '"' + crlf + crlf +
                        utf8encode(field.value) + crlf;
                });
                $.each(files, function (index, file) {
                    formData += doubleDash + boundary + crlf +
                        'Content-Disposition: form-data; name="' +
                        utf8encode(filesFieldName) +
                        '"; filename="' + utf8encode(file.name) + '"' + crlf +
                        'Content-Type: ' + utf8encode(file.type) + crlf + crlf;
                    buffer.push(formData);
                    buffer.push(file.reader.result);
                    delete file.reader;
                    formData = crlf;
                });
                formData += doubleDash + boundary + doubleDash + crlf;
                buffer.push(formData);
                return buffer.join('');
            },
            
            fileReaderUpload = function (files, xhr, settings) {
                var boundary = '----MultiPartFormBoundary' + (new Date()).getTime(),
                    loader,
                    i;
                xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
                loader = new MultiLoader(function () {
                    xhr.sendAsBinary(buildMultiPartFormData(
                        boundary,
                        files,
                        getFieldName(settings),
                        getFormData(settings)
                    ));
                }, files.length);
                for (i = 0; i < files.length; i += 1) {
                    loadFileContent(files[i], loader.complete);
                }
            },

            getBlob = function (file, settings) {
                var blob,
                    ub = settings.uploadedBytes,
                    mcs = settings.maxChunkSize;
                if (file && typeof file.slice === func && (ub || (mcs && mcs < file.size))) {
                    settings.uploadedBytes = ub = ub || 0;
                    blob = file.slice(ub, mcs || file.size - ub);
                    settings.chunkSize = blob.size;
                    return blob;
                }
                return file;
            },

            upload = function (event, files, index, xhr, settings, nextChunk) {
                var send;
                send = function () {
                    if (!nextChunk) {
                        if (typeof settings.onSend === func &&
                                settings.onSend(event, files, index, xhr, settings) === false) {
                            completeNext();
                            return;
                        }
                    }
                    var blob = getBlob(files[index], settings),
                        filesToUpload;
                    initUploadEventHandlers(files, index, xhr, settings);
                    initUploadRequest(files, index, xhr, settings);
                    if (!settings.multipart) {
                        if (xhr.upload) {
                            xhr.send(blob);
                        } else {
                            $.error('Browser does not support XHR file uploads');
                        }
                    } else {
                        filesToUpload = (typeof index === 'number') ? [blob] : files;
                        if (typeof FormData !== undef) {
                            formDataUpload(filesToUpload, xhr, settings);
                        } else if (typeof FileReader !== undef && typeof xhr.sendAsBinary === func) {
                            fileReaderUpload(filesToUpload, xhr, settings);
                        } else {
                            $.error('Browser does not support multipart/form-data XHR file uploads');
                        }
                    }
                };
                if (!nextChunk) {
                    multiLoader.push(Array.prototype.slice.call(arguments, 1));
                    if (settings.sequentialUploads) {
                        sequenceHandler.push(send);
                        return;
                    }
                }
                send();
            },

            handleUpload = function (event, files, input, form, index) {
                var xhr = new XMLHttpRequest(),
                    uploadSettings = $.extend({}, settings);
                uploadSettings.fileInput = input;
                uploadSettings.uploadForm = form;
                if (typeof uploadSettings.initUpload === func) {
                    uploadSettings.initUpload(
                        event,
                        files,
                        index,
                        xhr,
                        uploadSettings,
                        function () {
                            upload(event, files, index, xhr, uploadSettings);
                        }
                    );
                } else {
                    upload(event, files, index, xhr, uploadSettings);
                }
            },

            handleLegacyGlobalProgress = function (event, files, index, iframe, settings) {
                var total = 0,
                    progressEvent;
                if (typeof index === undef) {
                    $.each(files, function (index, file) {
                        total += file.size ? file.size : 1;
                    });
                } else {
                    total = files[index].size ? files[index].size : 1;
                }
                progressEvent = createProgressEvent(true, total, total);
                settings.progressLoaded = total;
                handleGlobalProgress(progressEvent, files, index, iframe, settings);
            },

            legacyUploadFormDataInit = function (input, form, settings) {
                var formData = getFormData(settings);
                form.find(':input').not(':disabled')
                    .attr('disabled', true)
                    .addClass(settings.namespace + '_disabled');
                $.each(formData, function (index, field) {
                    $('<input type="hidden"/>')
                        .attr('name', field.name)
                        .val(field.value)
                        .addClass(settings.namespace + '_form_data')
                        .appendTo(form);
                });
                input
                    .attr('name', getFieldName(settings))
                    .appendTo(form);
            },

            legacyUploadFormDataReset = function (input, form, settings) {
                input.detach();
                form.find('.' + settings.namespace + '_disabled')
                    .removeAttr('disabled')
                    .removeClass(settings.namespace + '_disabled');
                form.find('.' + settings.namespace + '_form_data').remove();
            },

            legacyUpload = function (event, files, input, form, iframe, settings, index) {
                var send;
                send = function () {
                    if (typeof settings.onSend === func && settings.onSend(event, files, index, iframe, settings) === false) {
                        completeNext();
                        return;
                    }
                    var originalAction = form.attr('action'),
                        originalMethod = form.attr('method'),
                        originalTarget = form.attr('target');
                    iframe
                        .unbind('abort')
                        .bind('abort', function (e) {
                            iframe.readyState = 0;
                            // javascript:false as iframe src prevents warning popups on HTTPS in IE6
                            // concat is used here to prevent the "Script URL" JSLint error:
                            iframe.unbind('load').attr('src', 'javascript'.concat(':false;'));
                            handleLegacyGlobalProgress(e, files, index, iframe, settings);
                            if (typeof settings.onAbort === func) {
                                settings.onAbort(e, files, index, iframe, settings);
                            }
                            completeNext();
                        })
                        .unbind('load')
                        .bind('load', function (e) {
                            iframe.readyState = 4;
                            handleLegacyGlobalProgress(e, files, index, iframe, settings);
                            if (typeof settings.onLoad === func) {
                                settings.onLoad(e, files, index, iframe, settings);
                            }
                            // Fix for IE endless progress bar activity bug
                            // (happens on form submits to iframe targets):
                            $('<iframe src="javascript:false;" style="display:none;"></iframe>')
                                .appendTo(form).remove();
                            completeNext();
                        });
                    form
                        .attr('action', getUrl(settings))
                        .attr('method', getMethod(settings))
                        .attr('target', iframe.attr('name'));
                    legacyUploadFormDataInit(input, form, settings);
                    iframe.readyState = 2;
                    form.get(0).submit();
                    legacyUploadFormDataReset(input, form, settings);
                    form
                        .attr('action', originalAction)
                        .attr('method', originalMethod)
                        .attr('target', originalTarget);
                };
                multiLoader.push([files, index, iframe, settings]);
                if (settings.sequentialUploads) {
                    sequenceHandler.push(send);
                } else {
                    send();
                }
            },

            normalizeFile = function (index, file) {
                if (typeof file.name === undef && typeof file.size === undef) {
                    file.name = file.fileName;
                    file.size = file.fileSize;
                }
            },

            handleLegacyUpload = function (event, input, form, index) {
                if (!(event && input && form)) {
                    $.error('Iframe based File Upload requires a file input change event');
                    return;
                }
                // javascript:false as iframe src prevents warning popups on HTTPS in IE6:
                var iframe = $('<iframe src="javascript:false;" style="display:none;" name="iframe_' +
                    settings.namespace + '_' + (new Date()).getTime() + '"></iframe>'),
                    uploadSettings = $.extend({}, settings),
                    files = event.target && event.target.files;
                files = files ? Array.prototype.slice.call(files, 0) : [{name: input.val(), type: null, size: null}];
                $.each(files, normalizeFile);
                index = files.length === 1 ? 0 : index;
                uploadSettings.fileInput = input;
                uploadSettings.uploadForm = form;
                iframe.readyState = 0;
                iframe.abort = function () {
                    iframe.trigger('abort');
                };
                iframe.bind('load', function () {
                    iframe.unbind('load');
                    if (typeof uploadSettings.initUpload === func) {
                        uploadSettings.initUpload(
                            event,
                            files,
                            index,
                            iframe,
                            uploadSettings,
                            function () {
                                legacyUpload(event, files, input, form, iframe, uploadSettings, index);
                            }
                        );
                    } else {
                        legacyUpload(event, files, input, form, iframe, uploadSettings, index);
                    }
                }).appendTo(form);
            },

            canHandleXHRUploadSize = function (files) {
                var bytes = 0,
                    totalBytes = 0,
                    i;
                if (settings.multipart && typeof FormData === undef) {
                    for (i = 0; i < files.length; i += 1) {
                        bytes = files[i].size;
                        if (bytes > settings.maxFileReaderSize) {
                            return false;
                        }
                        totalBytes += bytes;
                    }
                    if (settings.multiFileRequest && totalBytes > settings.maxFileReaderSize) {
                        return false;
                    }
                }
                return true;
            },

            handleFiles = function (event, files, input, form) {
                if (!canHandleXHRUploadSize(files)) {
                    handleLegacyUpload(event, input, form);
                    return;
                }
                var i;
                files = Array.prototype.slice.call(files, 0);
                $.each(files, normalizeFile);
                if (settings.multiFileRequest && settings.multipart && files.length) {
                    handleUpload(event, files, input, form);
                } else {
                    for (i = 0; i < files.length; i += 1) {
                        handleUpload(event, files, input, form, i);
                    }
                }
            },
            
            initUploadForm = function () {
                uploadForm = (container.is('form') ? container : container.find('form'))
                    .filter(settings.uploadFormFilter);
            },
            
            initFileInput = function () {
                fileInput = (uploadForm.length ? uploadForm : container).find('input:file')
                    .filter(settings.fileInputFilter);
            },
            
            replaceFileInput = function (input) {
                var inputClone = input.clone(true);
                $('<form/>').append(inputClone).get(0).reset();
                input.after(inputClone).detach();
                initFileInput();
            };

        this.onDocumentDragOver = function (e) {
            if (typeof settings.onDocumentDragOver === func &&
                    settings.onDocumentDragOver(e) === false) {
                return false;
            }
            e.preventDefault();
        };
        
        this.onDocumentDrop = function (e) {
            if (typeof settings.onDocumentDrop === func &&
                    settings.onDocumentDrop(e) === false) {
                return false;
            }
            e.preventDefault();
        };

        this.onDragOver = function (e) {
            if (typeof settings.onDragOver === func &&
                    settings.onDragOver(e) === false) {
                return false;
            }
            var dataTransfer = e.originalEvent.dataTransfer;
            if (dataTransfer && dataTransfer.files) {
                dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy';
                e.preventDefault();
            }
        };

        this.onDrop = function (e) {
            if (typeof settings.onDrop === func &&
                    settings.onDrop(e) === false) {
                return false;
            }
            var dataTransfer = e.originalEvent.dataTransfer;
            if (dataTransfer && dataTransfer.files && isXHRUploadCapable()) {
                handleFiles(e, dataTransfer.files);
            }
            e.preventDefault();
        };
        
        this.onChange = function (e) {
            if (typeof settings.onChange === func &&
                    settings.onChange(e) === false) {
                return false;
            }
            var input = $(e.target),
                form = $(e.target.form);
            if (form.length === 1) {
                input.data(defaultNamespace + '_form', form);
                replaceFileInput(input);
            } else {
                form = input.data(defaultNamespace + '_form');
            }
            if (!settings.forceIframeUpload && e.target.files && isXHRUploadCapable()) {
                handleFiles(e, e.target.files, input, form);
            } else {
                handleLegacyUpload(e, input, form);
            }
        };

        this.init = function (options) {
            if (options) {
                $.extend(settings, options);
                optionsReference = options;
            }
            initUploadForm();
            initFileInput();
            if (container.data(settings.namespace)) {
                $.error('FileUpload with namespace "' + settings.namespace + '" already assigned to this element');
                return;
            }
            container
                .data(settings.namespace, fileUpload)
                .addClass(settings.cssClass);
            settings.dropZone.not(container).addClass(settings.cssClass);
            initEventHandlers();
            if (typeof settings.init === func) {
                settings.init();
            }
        };

        this.options = function (options) {
            var oldCssClass,
                oldDropZone,
                uploadFormFilterUpdate,
                fileInputFilterUpdate;
            if (typeof options === undef) {
                return $.extend({}, settings);
            }
            if (optionsReference) {
                $.extend(optionsReference, options);
            }
            removeEventHandlers();
            $.each(options, function (name, value) {
                switch (name) {
                case 'namespace':
                    $.error('The FileUpload namespace cannot be updated.');
                    return;
                case 'uploadFormFilter':
                    uploadFormFilterUpdate = true;
                    fileInputFilterUpdate = true;
                    break;
                case 'fileInputFilter':
                    fileInputFilterUpdate = true;
                    break;
                case 'cssClass':
                    oldCssClass = settings.cssClass;
                    break;
                case 'dropZone':
                    oldDropZone = settings.dropZone;
                    break;
                }
                settings[name] = value;
            });
            if (uploadFormFilterUpdate) {
                initUploadForm();
            }
            if (fileInputFilterUpdate) {
                initFileInput();
            }
            if (typeof oldCssClass !== undef) {
                container
                    .removeClass(oldCssClass)
                    .addClass(settings.cssClass);
                (oldDropZone ? oldDropZone : settings.dropZone).not(container)
                    .removeClass(oldCssClass);
                settings.dropZone.not(container).addClass(settings.cssClass);
            } else if (oldDropZone) {
                oldDropZone.not(container).removeClass(settings.cssClass);
                settings.dropZone.not(container).addClass(settings.cssClass);
            }
            initEventHandlers();
        };
        
        this.option = function (name, value) {
            var options;
            if (typeof value === undef) {
                return settings[name];
            }
            options = {};
            options[name] = value;
            fileUpload.options(options);
        };
        
        this.destroy = function () {
            if (typeof settings.destroy === func) {
                settings.destroy();
            }
            removeEventHandlers();
            container
                .removeData(settings.namespace)
                .removeClass(settings.cssClass);
            settings.dropZone.not(container).removeClass(settings.cssClass);
        };
        
        this.upload = function (files) {
            if (typeof files.length === undef) {
                files = [files];
            }
            handleFiles(null, files);
        };
    };

    methods = {
        init : function (options) {
            return this.each(function () {
                (new FileUpload($(this))).init(options);
            });
        },
        
        option: function (option, value, namespace) {
            namespace = namespace ? namespace : defaultNamespace;
            var fileUpload = $(this).data(namespace);
            if (fileUpload) {
                if (!option) {
                    return fileUpload.options();
                } else if (typeof option === 'string' && typeof value === undef) {
                    return fileUpload.option(option);
                }
            } else {
                $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
            }
            return this.each(function () {
                var fu = $(this).data(namespace);
                if (fu) {
                    if (typeof option === 'string') {
                        fu.option(option, value);
                    } else {
                        fu.options(option);
                    }
                } else {
                    $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
                }
            });
        },
                
        destroy: function (namespace) {
            namespace = namespace ? namespace : defaultNamespace;
            return this.each(function () {
                var fileUpload = $(this).data(namespace);
                if (fileUpload) {
                    fileUpload.destroy();
                } else {
                    $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
                }
            });
        },
        
        upload: function (files, namespace) {
            namespace = namespace ? namespace : defaultNamespace;
            return this.each(function () {
                var fileUpload = $(this).data(namespace);
                if (fileUpload) {
                    fileUpload.upload(files);
                } else {
                    $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
                }
            });
        }
    };
    
    $.fn.fileUpload = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method "' + method + '" does not exist on jQuery.fileUpload');
        }
    };
    
}(jQuery));
/*! jQuery v1.7.1 jquery.com | jquery.org/license */
(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function cb(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function ca(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bE.test(a)?d(a,e):ca(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)ca(a+"["+e+"]",b[e],c,d);else d(a,b)}function b_(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bT,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bP),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bC(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bx:by,g=0,h=e.length;if(d>0){if(c!=="border")for(;g<h;g++)c||(d-=parseFloat(f.css(a,"padding"+e[g]))||0),c==="margin"?d+=parseFloat(f.css(a,c+e[g]))||0:d-=parseFloat(f.css(a,"border"+e[g]+"Width"))||0;return d+"px"}d=bz(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0;if(c)for(;g<h;g++)d+=parseFloat(f.css(a,"padding"+e[g]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+e[g]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+e[g]))||0);return d+"px"}function bp(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c+(i[c][d].namespace?".":"")+i[c][d].namespace,i[c][d],i[c][d].data)}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test("Â ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?m(g):h==="function"&&(!a.unique||!o.has(g))&&c.push(g)},n=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,l=j||0,j=0,k=c.length;for(;c&&l<k;l++)if(c[l].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}i=!1,c&&(a.once?e===!0?o.disable():c=[]:d&&d.length&&(e=d.shift(),o.fireWith(e[0],e[1])))},o={add:function(){if(c){var a=c.length;m(arguments),i?k=c.length:e&&e!==!0&&(j=a,n(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){i&&f<=k&&(k--,f<=l&&l--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&o.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(i?a.once||d.push([b,c]):(!a.once||!e)&&n(b,c));return this},fire:function(){o.fireWith(this,arguments);return this},fired:function(){return!!e}};return o};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p,q=c.createElement("div"),r=c.documentElement;q.setAttribute("className","t"),q.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="<div "+n+"><div></div></div>"+"<table "+n+" cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="<div style='width:4px;'></div>",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h=null;if(typeof a=="undefined"){if(this.length){h=f.data(this[0]);if(this[0].nodeType===1&&!f._data(this[0],"parsedAttrs")){e=this[0].attributes;for(var i=0,j=e.length;i<j;i++)g=e[i].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),l(this[0],g,h[g]));f._data(this[0],"parsedAttrs",!0)}}return h}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split("."),d[1]=d[1]?"."+d[1]:"";if(c===b){h=this.triggerHandler("getData"+d[1]+"!",[d[0]]),h===b&&this.length&&(h=f.data(this[0],a),h=l(this[0],a,h));return h===b&&d[1]?this.data(d[0]):h}return this.each(function(){var b=f(this),e=[d[0],c];b.triggerHandler("setData"+d[1]+"!",e),f.data(this,a,c),b.triggerHandler("changeData"+d[1]+"!",e)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise()}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h<g;h++)e=d[h],e&&(c=f.propFix[e]||e,f.attr(a,e,""),a.removeAttribute(v?e:c),u.test(e)&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};
f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=[],j,k,l,m,n,o,p,q,r,s,t;g[0]=c,c.delegateTarget=this;if(e&&!c.target.disabled&&(!c.button||c.type!=="click")){m=f(this),m.context=this.ownerDocument||this;for(l=c.target;l!=this;l=l.parentNode||this){o={},q=[],m[0]=l;for(j=0;j<e;j++)r=d[j],s=r.selector,o[s]===b&&(o[s]=r.quick?H(l,r.quick):m.is(s)),o[s]&&q.push(r);q.length&&i.push({elem:l,matches:q})}}d.length>e&&i.push({elem:this,matches:d.slice(e)});for(j=0;j<i.length&&!c.isPropagationStopped();j++){p=i[j],c.currentTarget=p.elem;for(k=0;k<p.matches.length&&!c.isImmediatePropagationStopped();k++){r=p.matches[k];if(h||!c.namespace&&!r.namespace||c.namespace_re&&c.namespace_re.test(r.namespace))c.data=r.data,c.handleObj=r,n=((f.event.special[r.origType]||{}).handle||r.handler).apply(p.elem,g),n!==b&&(c.result=n,n===!1&&(c.preventDefault(),c.stopPropagation()))}}return c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0)}),d._submit_attached=!0)})},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on.call(this,a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.type+"."+e.namespace:e.type,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.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(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.POS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function()
{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bp)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bn(k[i]);else bn(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bq=/alpha\([^)]*\)/i,br=/opacity=([^)]*)/,bs=/([A-Z]|^ms)/g,bt=/^-?\d+(?:px)?$/i,bu=/^-?\d/,bv=/^([\-+])=([\-+.\de]+)/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bv.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bC(a,b,d);f.swap(a,bw,function(){e=bC(a,b,d)});return e}},set:function(a,b){if(!bt.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cv(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cu("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cu("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cv(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cn.test(h)?(o=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),o?(f._data(this,"toggle"+i,o==="show"?"hide":"show"),j[o]()):j[h]()):(k=co.exec(h),l=j.cur(),k?(m=parseFloat(k[2]),n=k[3]||(f.cssNumber[i]?"":"px"),n!=="px"&&(f.style(this,i,(m||1)+n),l=(m||1)/j.cur()*l,f.style(this,i,l+n)),k[1]&&(m=(k[1]==="-="?-1:1)*m+l),j.custom(l,m,n)):j.custom(l,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:cu("show",1),slideUp:cu("hide",1),slideToggle:cu("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cr||cs(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){e.options.hide&&f._data(e.elem,"fxshow"+e.prop)===b&&f._data(e.elem,"fxshow"+e.prop,e.start)},h()&&f.timers.push(h)&&!cp&&(cp=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cr||cs(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cp),cp=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(["width","height"],function(a,b){f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cy(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.support.fixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.support.fixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);

(function(){var settings={'max_undo':20,'undoChange':function(){},'redoChange':function(){}};var UndoManager=function(set){this.action_index={};this.next_id=1;this.undo_queue=[];this.redo_queue=[];this.current_action=null;this.group=null;this.group_queue=[];this.group_id=0;this.working='';this.undo_available=0;this.undo_names=[];this.redo_available=0;this.redo_names=[];this.changeSettings(set);};UndoManager.prototype.changeSettings=function(set){this.settings=settings;for(attrname in set){if(set.hasOwnProperty(attrname)){this.settings[attrname]=set[attrname];}}
this.settings=$.extend(settings,set);};UndoManager.prototype.groupRunner=function(actions){var id;while((id=actions.pop())){var action=this.action_index[id];delete this.action_index[id];action[1].apply(action[3],action[2]);}};UndoManager.prototype.undoable=function(name,func,parameters,context,id,add_to_queue_overwrite){if(this.group!==null){id=id||this.next_id++;this.action_index[id]=[name,func,parameters,context];this.group[2][0].push(id);return this;}
var queue=this.undo_queue;if(this.working=='UNDO'){queue=this.redo_queue;}
id=id||this.next_id++;this.action_index[id]=[name,func,parameters,context];if(!add_to_queue_overwrite){queue.push(id);if(queue.length>this.settings.max_undo){queue.shift();if(this.working=='UNDO'){this.redo_available--;this.redo_names.shift();}else{this.undo_available--;this.undo_names.shift();}}
if(this.working=='UNDO'){this.redo_available++;this.redo_names.push(this.current_action[0]);this.settings.redoChange();}else{this.undo_available++;if(this.working=='REDO'){this.undo_names.push(this.current_action[0]);}else{this.clearRedoQueue();this.undo_names.push(name);}
this.settings.undoChange();}}
return this;};UndoManager.prototype.undo=function(){if(this.undo_queue.length>0){var id=this.undo_queue.pop();this.undo_available--;this.undo_names.pop();var action=this.action_index[id];delete this.action_index[id];this.working='UNDO';this.current_action=action;this.startGroup(action[0]);action[1].apply(action[3],action[2]);this.endGroup();delete this.current_action;this.working='';this.settings.undoChange();}
return this;};UndoManager.prototype.clearUndoQueue=function(){for(var i=this.undo_queue.length-1;i>=0;i--){delete this.action_index[this.undo_queue[i]];}
this.undo_queue=[];this.undo_available=0;this.redo_names=[];this.settings.undoChange();return this;};UndoManager.prototype.clearQueues=function(){this.action_index={};this.undo_queue=[];this.undo_available=0;this.undo_names=[];this.redo_queue=[];this.redo_available=0;this.redo_names=[];this.settings.undoChange();this.settings.redoChange();return this;};UndoManager.prototype.redo=function(){if(this.redo_queue.length>0){var id=this.redo_queue.pop();this.redo_available--;this.redo_names.pop();var action=this.action_index[id];delete this.action_index[id];this.working='REDO';this.current_action=action;this.startGroup(action[0]);action[1].apply(action[3],action[2]);this.endGroup();delete this.current_action;this.working='';this.settings.redoChange();}
return this;};UndoManager.prototype.clearRedoQueue=function(){for(var i=this.redo_queue.length-1;i>=0;i--){delete this.action_index[this.redo_queue[i]];}
this.redo_queue=[];this.redo_available=0;this.redo_names=[];this.settings.redoChange();return this;};UndoManager.prototype.startGroup=function(name){if(this.group!==null){this.group_queue.push(this.group);}
this.group_id=this.next_id++;this.group=[name,this.groupRunner,[[]],this,this.group_id];return this.group_id;};UndoManager.prototype.endGroup=function(){var grp=this.group;if(this.group_queue.length>0){this.group=this.group_queue.pop();}else{this.group=null;}
this.undoable.apply(this,grp);return this;};UndoManager.prototype.exitGroup=function(rollback){if(rollback!==false){var grp=this.group;this.startGroup('rolling back');grp[1].apply(grp[3],grp[2]);this.exitGroup(false);}
if(this.group_queue.length>0){this.group=this.group_queue.pop();}else{this.group=null;}
return this;};UndoManager.prototype.resumeGroup=function(id){if(this.group!==null){this.group_queue.push(this.group);}
this.group=this.action_index[id];this.group[5]=true;this.group_id=id;return this;};window.UndoManager=UndoManager;})();
 
conlls = {	10: 	{"id": 0, "t":1, "lemma": 2, "cat": 3, "xpos":4, "morph":5, "gov":6, "func":7, "xgov":8, "gloss":9}, 
		14: 	{"id": 0, "t":1, "lemma": 3, "cat": 5, "gov":8, "func":11}, 
		4: 	{"t":0, "lemma": 0, "cat": 1, "gov":2, "func":3} 
	}
 
 
function conllNodesToTree(treeline)
	// reads a conll representation of a single tree
	{
		var nodes = treeline.split('\n');
		var tree={};
		var uextra={};
		var words=[]
		var lastid=0;
		var skipuntil=0;
		$.each(nodes, function(id,nodeline){ // for each conll line:
			var nodeline=$.trim(nodeline);
			if (nodeline.charAt(0) == "#") {
				if (!(lastid in uextra)) uextra[lastid]=[];
				uextra[lastid].push(nodeline)
				return true;
			}
			var elements = nodeline.split('\t');
			el=elements.length;
			if (!(el in conlls) && el>10) el=10;
			if (el > 4) id=elements[conlls[el]["id"]];
			else if (elements[conlls[el]["t"]] != "_") id++;
			if (lastid!=id) // needed for the arborator encoding of multiple govs
			{
				var t=elements[conlls[el]["t"]];
				var tokids=id.split("-")
				if (tokids.length == 1) {
					tree[id]={}
					tree[id]["gov"]={};
					tree[id]["t"]=t;
					tree[id]["lemma"]=elements[conlls[el]["lemma"]];
					tree[id]["cat"]=elements[conlls[el]["cat"]];
					if (id>skipuntil) words.push(t);
					if (el==10) {
						tree[id]["xpos"]=elements[conlls[el]["xpos"]];
						tree[id]["morph"]=elements[conlls[el]["morph"]];
						tree[id]["gloss"]=elements[conlls[el]["gloss"]];
						if (tree[id]["gloss"]=="SpaceAfter=No"){
							tree[id]["gloss"]="_";
							tree[id]["NoSpaceAfter"]=true;
						}
						var xgov = elements[conlls[el]["xgov"]];
						if (xgov.indexOf(':') > -1){
							var xgovs=xgov.split("|");
							$.each(xgovs, function(ind,xg){ 
								// for each extra governor line:
								var xgs=xg.split(":")
								if (xgs.length >=2) {
									// if it's not just _
									var gov=xgs[0];
									var func= xgs.slice(1).join(":");
									tree[id]["gov"][gov]=func;
								}
								
							});

						}
					}
				}
				else if (tokids.length == 2){
					skipuntil = parseInt(tokids[1])
					words.push(elements[conlls[el]["t"]]);
					if (!(lastid in uextra)) uextra[lastid]=[];
					uextra[lastid].push(nodeline)
				}
				else {
					if (!(lastid in uextra)) uextra[lastid]=[];
					uextra[lastid].push(nodeline)
				}
			}
			gov = elements[conlls[el]["gov"]];
			if (gov!="" && gov!="_") 
			{
				if (gov==-1)
				{
					gov = elements[conlls[el]["gov"]+1];
				}
				var func = elements[conlls[el]["func"]];
				if (func.indexOf('::') !== -1) 
					{	
						var stydic = func.substring(func.indexOf("::") + 1);
						func = func.split("::")[0];
						if (stydic!="") funcDic[func] = $.parseJSON(stydic);
						$('#styleconllcheck').prop('checked', true);
					};
// 				func=func.replace(/\W+/g, "_");
				tree[id]["gov"][gov]=func;
				functions[func]=true;
			}
			lastid=id;
			});
		
		sentence="";
		$.each(words, function(jj,word){ // making the sentence with correct spacing:
			sentence+=word;
			if (!(("NoSpaceAfter" in tree[jj+1]) && tree[jj+1]["NoSpaceAfter"]==true)) sentence+=" ";
		});
		
		return {tree:tree, uextra:uextra, words:words, sentence:sentence};
	}



/*!
 * arborator script for quickie 
 * version 1.0
 * http://arborator.ilpga.fr/
 *
 * Copyright 2014-2018, Kim Gerdes
 *
 * This program is free software:
 * Licensed under version 3 of the GNU Affero General Public License (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.gnu.org/licenses/agpl-3.0.html
 * 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. 
 *
 */ 


// needs arborator.draw.js
// does not need arborator.edit.js


conlls = {	10: 	{"id": 0, "t":1, "lemma": 2, "cat": 3, "xpos":4, "morph":5, "gov":6, "func":7, "xgov":8, "gloss":9}, 
// 		14: 	{"id": 0, "t":1, "lemma": 3, "cat": 5, "gov":8, "func":10}, 
		14: 	{"id": 0, "t":1, "lemma": 3, "cat": 5, "gov":9, "func":11}, 
		4: 	{"t":0, "lemma": 0, "cat": 1, "gov":2, "func":3} 
	}
el=10;
defaultCat="_"

inDrawTrees=false;
sentencesText="One sentence per line\nClick outside of this area when ready";

trees=[]; // list of tree objects
uextras=[]; // list of comments. each comment is a hashtable position(=line)->comment
conlltrees=[]; // list of conll strings


var root = 'annodoc/';
var webFontURLs = [
	root + 'Astloch-Bold.ttf',
	root + 'PT_Sans-Caption-Web-Regular.ttf',
	root + 'Liberation_Sans-Regular.ttf'
];

svgheader = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'


$(function() {

// 	console.log( "ready!" );
	$("#annodoccheck").prop('checked', false);
	$("#annodoccheck").button();
	$('#annodoccheck').change(function() {
		if (this.checked) {drawAnnodoc();$(this).button('option', 'label', 'Hide Annodoc graph');}
		else {$( ".annodoc" ).remove();$(this).button('option', 'label', 'Show Annodoc graph');}
	}); 
	settings();

	if (window.opener) 	// this window was opened by another window i.e. by depedit
		try {
			conllarea=window.opener.$(window.name); // use the name that depedit gave me to connect to the right conll area
			}
		catch(err) {
			console.log("problem with window.opener.$(window.name)");
			conllarea=$("#conll");
			$("#toggleAndBoxx").show();
		}
		
		
	else 			// this window is independent
		{
			conllarea=$("#conll");
			$("#toggleAndBoxx").show();
		}
	var conll = $.urlParam('conll');
	if (conll!=null) conllarea.val(decodeURIComponent(conll));
	
	readConll();
	setupStyleDialog()
	drawTrees();

	$("#sentences").val(sentencesText)
	conllarea.focus(function() {
		nokeys=true;
	});
	conllarea.blur(function() {
// 		console.log("textarea changed!!");
		$("#funchoice").empty();
		$("#catchoice").empty();
		$("#stylefunctions").empty();
		$("#trees").empty();
		readConll();
		setupStyleDialog() 
		drawTrees();
		nokeys=false;
	});
	$('#sentences').focus(function() {
		nokeys=true;
	});
	$('#sentences').blur(function() {
// 		console.log("#sentences changed!!");
		nokeys=false;
		if (sentencesText!=$("#sentences").val()) // TODO: sentence really changed: set sentencesText
		{
			conllize();
			$("#trees").empty();
			readConll();
			setupStyleDialog() 
			drawTrees();
			sentencesText=$("#sentences").val();
		}
	});
	$('#addfuncs').focus(function() {
		nokeys=true;
	});
	$('#addfuncs').blur(function() {
		nokeys=false;
	});
	$('#addcats').focus(function() {
		nokeys=true;
	});
	$('#addcats').blur(function() {
		nokeys=false;
	});
	$(".toggle").on("click", function(e){
		$(this).toggleClass("expanded");
		$(".boxx").slideToggle();
	});
	
	$("#style").click(function()	{
		$( "#styledialog" ).dialog( "open" );
	});
	
	$("#share").click(function()	{
		history.pushState('URL with conll', 'URL with conll', location.href.split("?")[0]+"?conll="+encodeURIComponent(conllarea.val()));
	});
	$("#share").dblclick(function(e)	{
		e.preventDefault && e.preventDefault();
		history.pushState('URL with conll', 'URL with conll', location.href.split("?")[0]);
	});
	$( "#styledialog" ).dialog({
			bgiframe: true,
			autoOpen: false,
			height: 350,
 			width:400,
			draggable: true
			});
	
	$("#style").click(function()	{
		styling();
	});
	
	$("#help").click(function()	{
		help()
	});
	
	$('#styleconllcheck').change(function(){
		var c = this.checked ? '#f00' : '#09f';
		$('p').css('color', c);
	});
	
	
	
	
	conllarea.focus();
	
// 	help();
});


settings = function() 
	{
		shownfeatures=["t", "cat", "lemma","gloss"]; // recomputed in readConll
		erase="--ERASE--"
		categoryindex=1;
		tab=8; 			// space between tokens
		line=18 			// line height
		dependencyspace=200; 	// y-space for dependency representation
		xoff=8; 			// placement of the start of a depdency link in relation to the center of the word
		linedeps=6; 		// y distance between two arrows arriving at the same token (rare case)
		pois=4; 			// size of arrow pointer
		tokdepdist=15; 		// distance between tokens and depdendency relation
		funccurvedist=8;		// distance between the function name and the curves highest point
		depminh = 15; 		// minimum height for dependency
		worddistancefactor = -1; 	// distant words get higher curves. this factor fixes how much higher.
		rootTriggerSquare = 50; 	// distance from top of svg above which root connections are created and minimum width of this zone (from 0 to 50 par example the connection jumps to the middle)
		extraspace=50 		// extends the width of the svg in order to fit larger categories
		defaultattris={"font": '14px Arial', "text-anchor":'start'};
		attris = {"t":		{"font": '18px Arial', "text-anchor":'start',"fill": '#000',"cursor":'move',"stroke-width": '0'}, 
			"cat":	{"font": '12px Times', "text-anchor":'start',"fill": '#036',"cursor":'pointer'},
			"lemma":	{"font": '14px Times', "text-anchor":'start',"fill": '#036'},
			"depline":	{"stroke": '#000',"stroke-width":'1',"stroke-dasharray": ''},
			"deptext":	{"font": '12px Times', "font-style":'italic', "fill": '#000',"cursor":'pointer'},
			"dragdepline":	{"stroke": '#dd137b',"stroke-width":'2'},
			"dragdeplineroot":	{"stroke": '#654b6d',"stroke-width":'2'},
			"source":	{"fill": '#654b6d'},
			"target":	{"fill": '#dd137b',"cursor": 'url("images/connect.png"),pointer', "font-style":'italic'}, 
			"form":	{"font-style":'italic'}
			};
		colored={"dependency":null}; 	//object of elements to be colored. the colors have to be defined in config.py. just leave the object empty {} if you don't want colors
		attris[shownfeatures[categoryindex]]=attris["cat"]
		colored[shownfeatures[categoryindex]]=null;
		
		catDic={};
		var fd = $.cookie("funcDic");
		if (fd == null) funcDic = {};
		else funcDic= $.parseJSON(fd);
		
		editable=true;
		tokenModifyable=true;
	}


function drawTrees() {
	inDrawTrees=true;
	$.each(trees, function(i, tree){
		tokens=new Object();
		$.each(tree, function(key, val) 
			{
				tokens[key]=val;
			});
		start("holder"+i, i);
		$("#svg"+i).css("float","left").attr("nr",i);
	})
	$(".connect").css("top",dependencyspace-line)
	$(".cut").css("top",dependencyspace-line)
	inDrawTrees=false;
	final();
}

$.urlParam = function(name){
	var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
	if (results==null){
		return null;
	}
	else{
		return decodeURI(results[1]) || 0;
	}
}


function final() {

	$(".toggler").click(function()
		{
			$('#holder'+$(this).attr("nr")).toggle(333)
		})
	$(".othertree").click(function()
		{
			toggleOpen($(this).parent().prev(),	$(this).attr("nr"), $(this).attr("treeid"));	
		})
	$(".saveimg").click(function()
		{
			var nr=$(this).parent().attr("nr");
			var sid=$(this).parent().attr("sid");
			
			saveTree(project,nr,sid,userid, username);
		});
	$(".undoimg").click(function()
		{
			var nr=$(this).parent().attr("nr");
			var sid=$(this).parent().attr("sid");
			currentsvg=$('#svg'+nr)[0];
			currentsvgchanged();
			currentsvg.undo.undo();
		});
	$(".redoimg").click(function()
		{
			var nr=$(this).parent().attr("nr");
			var sid=$(this).parent().attr("sid");
			currentsvg=$('#svg'+nr)[0];
			currentsvgchanged();
			currentsvg.undo.redo();
		});
	
	$(".connect").click(function()
		{
			
			var nr=parseInt($(this).parent().attr("nr"));
			currentsvg=$('#svg'+nr)[0];
			var basewords=currentsvg.words;
			var ind=Object.keys(basewords).length;
			var words=$('#svg'+(nr+1))[0].words
			var addnodes = Object.keys(words); //.sort();
			$.each(addnodes, function(i, nodei){
				var node = words[nodei]
				var newid = parseInt(nodei)+ind;
				node["index"]=newid;
				var newgov={};
				for (var j in node.gov) 
				{	
					var newgovind = parseInt(j)
					if (j!=0) newgovind+=ind
					newgov[newgovind]=node.gov[j];
				}
				node.gov = newgov;
				basewords[newid]=node;
			});
			var thisconll = wordsToConll(basewords);
			conlltrees.splice(nr,2,thisconll)
			conllarea.val(conlltrees.join("\n"));
			$("#trees").empty();
			readConll();
			setupStyleDialog() 
			drawTrees();
			
		});
	$(".cut").click(function()
		{
			var nr=parseInt($(this).parent().attr("nr"));
			currentsvg=$('#svg'+nr)[0];
			var allwords=currentsvg.words;
			var ind=isSelected.index;
			var newwords1={};
			var newwords2={};
			$.each(allwords, function(i, nodei){
				var node = allwords[i];
// 				console.log(allwords,nodei,allwords[i]);
				var newgov={};
				if (i<=ind)
					{
						for (var j in node.gov) 
						{	
							if (j<=ind) newgov[j]=node.gov[j];
						}
						newwords1[i]=node;
					}
				else
					{
						var newid = parseInt(i)-ind;
						node["index"]=newid;
						for (var j in node.gov) 
						{	
							if (j>ind) newgov[j-ind]=node.gov[j];
						}
						newwords2[newid]=node;
					}
				node.gov = newgov;
			});
			
			conlltrees.splice(nr,1,wordsToConll(newwords1),wordsToConll(newwords2))
			conllarea.val(conlltrees.join("\n"));
			$("#trees").empty();
			readConll();
			setupStyleDialog() 
			drawTrees();
			
		});
	
	$(".exportimg").click(function()
	{
		$("#ex").show();
		$("#ex").offset($(this).offset()).attr("nr",$(this).attr("nr"));
	});
	
	onkey();
	
	$("#dialog").dialog({
			bgiframe: true,
			autoOpen: false,
			height: 350,
 			width:400,
			modal: true,
			buttons: {
				
				Cancel: function() {
					$(this).dialog('close');
					return false;
				},
				"Burn it!!!": function() {
					$(this).dialog('close');
					reallyEraseTree();
					return true;
				}
				}
			});
	
	
// 'Erase node completely': function(){deleteNode($("#fdialog").attr("index"));} ,
// 				'Add as new node': function(){featureChanged(-1);} ,
	$("#fdialog").dialog({
				bgiframe: true,
				autoOpen: false,
				height: 450,
				width: 600,
				modal: true,
			});
	if (tokenModifyable>0) $("#fdialog").dialog({
				buttons: {
					'Erase this node': function() {eraseNode(parseInt($("#fdialog").attr("index"),10));$(this).dialog('close');},
					'Duplicate this node': function() {createNode(parseInt($("#fdialog").attr("index"),10));$(this).dialog('close');},
					Cancel: function() {$(this).dialog('close');},
					'OK': function(){featureChanged($("#fdialog").attr("index"));$(this).dialog('close');} 
				}
			});
	else	$("#fdialog").dialog({
				buttons: {
					Cancel: function() {$(this).dialog('close');},
					'OK': function(){featureChanged($("#fdialog").attr("index"));$(this).dialog('close');} 
				}
			});
		
	
	$(document).mouseup(function()
	{
		if(isDrag)
		{
			currentsvg.dragConnection.hide();
			isDrag = false;
		}
	}); 
	
	
// 	var svg = paper.toSVG();
// 	$("#e").html(svg);
// 	
}

	
function readConll() {
	// reads the conll representation of the whole treebank that is in the conll field
	categories={};
	addcats=$.trim($("#addcats").val())
	if (addcats) { $.each(addcats.split(/[,\s]+/), function(){ categories[this]=true;}) }
	
	functions={};
	addfuncs=$.trim($("#addfuncs").val())
	if (addfuncs) { $.each(addfuncs.split(/[,\s]+/), function(){functions[this]=true;}) }
	$.each(funcDic, function(key,val){functions[key]=true;});
	
	trees=[]; // list of tree objects
	uextras=[]; // list of comments. each comment is a hashtable position(=line)->comment
	conlltrees=[]; // list of conll strings
	treenr=0;
	
	var treelines = $.trim(conllarea.val()).split(/\n\s*\n\s*\n*/);
// 	console.log(232,treelines)
	$.each(treelines, function(i,treeline){ // splits the conll txt into tree segments:
		
		words=[]
		
		conlltrees.push(treeline);
		
		var data=conllNodesToTree(treeline);
		
		trees.push(data.tree);
		uextras.push(data.uextra)
		sentence="";
		$.each(words, function(jj,word){ // making the sentence with correct spacing:
			sentence+=word;
			if (jj+1 in data.tree && !(("NoSpaceAfter" in data.tree[jj+1]) && data.tree[jj+1]["NoSpaceAfter"]==true)) sentence+=" ";
		});
		gt=''
		if (treenr!=treelines.length-1) gt+='<div class="connect" title="connect with next sentence." id="connect'+i+'">&gt;</div>'
		gt += '<div class="cut" title="cut after selected word." id="cut'+i+'"><img src="images/cut.png"/></div>'
		$("#trees").append('<div id="sentencediv'+treenr+'" class="sentencediv" nr="'+treenr+'"> <a id="toggler'+treenr+'" class="toggler expanded" nr="'+treenr+'"> '+treenr+': '+sentence+' </a>		<img id="undo'+treenr+'" class="undoimg"	src="images/undo.png">				<img id="redo'+treenr+'" class="redoimg"	src="images/redo.png"> <img id="export'+treenr+'" class="exportimg" border="0" title="export the dependency graph in various image formats" nr="'+treenr+'" src="images/export.png" style="visibility: visible;"> </div> <div id="holder'+treenr+'" class="svgholder"	nr="'+treenr+'">'+gt+'</div>')
		
		treenr++;
	});
	conllarea.css("backgroundImage","url('images/conll"+el+".png')");	
	categories=$.map(categories, function(element,index) {return index}).sort() // to array
	$.each(categories, function(key, val) 
			{
				$("#catchoice").append("<option>"+val+"</option>")
			});
	$("#catchoice").attr("size",categories.length);
	$("#catchoice").css("height",categories.length*13.5);
	functions=$.map(functions, function(element,index) {return index}).sort() // to array
	functions.push("");
	functions.push(erase);
	
	// 	adapt which nodes should be shown depending on what we find on the first node
	firstnode=trees[0][Math.min.apply(Math,Object.keys(trees[0]))]; // take lowest existing treenode number
	shownfeatures = $.grep(shownfeatures, function (attri,i)
		{ // for each shownfeatures :
			if (i < 2 || ((attri in firstnode) && firstnode[attri]!=defaultCat)) { 
				// either the first two (token and cat) or non default value:
				return true; // keep it
				}
			return false; // kick it out
		});
	
}



function conllNodesToTree(treeline)
	// reads a conll representation of a single tree
	{
		var nodes = treeline.split('\n');
		var tree={};
		var uextra={};
		var lastid=0;
		var skipuntil=0;
		$.each(nodes, function(id,nodeline){ // for each conll line:
			var nodeline=$.trim(nodeline);
			if (nodeline.charAt(0) == "#") {
				if (!(lastid in uextra)) uextra[lastid]=[];
				uextra[lastid].push(nodeline)
				return true;
			}
			var elements = nodeline.split('\t');
			el=elements.length;
			// console.log(el,'###',nodeline,"***",elements);

			if (!(el in conlls) && el>10) el=10;
			if (el > 4) id=elements[conlls[el]["id"]];
			else if (elements[conlls[el]["t"]] != "_") id++;
			if (lastid!=id) // needed for the arborator encoding of multiple govs
			{
				var t=elements[conlls[el]["t"]];
				var tokids=id.split("-")
				if (tokids.length == 1) {
					tree[id]={}
					tree[id]["gov"]={};
					tree[id]["t"]=t;
					tree[id]["lemma"]=elements[conlls[el]["lemma"]];
					tree[id]["cat"]=elements[conlls[el]["cat"]];
					if (id>skipuntil) words.push(t);
					if (el==10) {
						tree[id]["xpos"]=elements[conlls[el]["xpos"]];
						tree[id]["morph"]=elements[conlls[el]["morph"]];
						tree[id]["gloss"]=elements[conlls[el]["gloss"]];
						if (tree[id]["gloss"]=="SpaceAfter=No"){
							tree[id]["gloss"]="_";
							tree[id]["NoSpaceAfter"]=true;
						}
						var xgov = elements[conlls[el]["xgov"]];
						if (xgov.indexOf(':') > -1){
							var xgovs=xgov.split("|");
							$.each(xgovs, function(ind,xg){ 
								// for each extra governor line:
								var xgs=xg.split(":")
								if (xgs.length >=2) {
									// if it's not just _
									var gov=xgs[0];
									var func= xgs.slice(1).join(":");
									tree[id]["gov"][gov]=func;
								}
								
							});

						}
					}
				}
				else if (tokids.length == 2){
					skipuntil = parseInt(tokids[1])
					words.push(elements[conlls[el]["t"]]);
					if (!(lastid in uextra)) uextra[lastid]=[];
					uextra[lastid].push(nodeline)
				}
				else {
					if (!(lastid in uextra)) uextra[lastid]=[];
					uextra[lastid].push(nodeline)
				}
			}
			gov = elements[conlls[el]["gov"]];
			if (gov!="" && gov!="_") 
			{
				if (gov==-1)
				{
					gov = elements[conlls[el]["gov"]+1];
				}
				var func = elements[conlls[el]["func"]];
				if (func.indexOf('::') !== -1) 
					{	
						var stydic = func.substring(func.indexOf("::") + 1);
						func = func.split("::")[0];
						if (stydic!="") funcDic[func] = $.parseJSON(stydic);
						$('#styleconllcheck').prop('checked', true);
					};
// 				func=func.replace(/\W+/g, "_");
				tree[id]["gov"][gov]=func;
				functions[func]=true;
			}
			categories[elements[conlls[el]["cat"]]]=true;
			lastid=id;
			});	
		return {tree:tree, uextra:uextra};
	}



				
			
function conllize()
	// sentences to conll
	{
		var conll="";
		$.each($.trim($("#sentences").val()).split(/\s*\n\s*/), function(i,sentence){
			var words=sentence.split(/\s+/);
			for (i in words)
			{		
				var word = words[i] ;
				conll=conll+(parseInt(i)+1)+"\t"+word+"\t"+word+"	"+defaultCat+"	_	_	0	root	_	_\n";
			}
			conll+="\n"
		})
		conllarea.val(conll);
	}


	
help = function() {	
	$("#b").fadeIn(2000).delay(2222).fadeOut(2000);
	$("#bb").delay(2222).fadeIn(2000).delay(1111).fadeOut(2000);
	$("#bbb").delay(4444).fadeIn(2000).delay(1111).fadeOut(2000);
	$("#bbbb").delay(6444).fadeIn(2000).delay(1111).fadeOut(2000);
}

styling = function() {	
	$('.color-box').colpick({
			colorScheme:'dark',
			layout:'rgbhex',
			color:'dd137b',
			onSubmit:function(hsb,hex,rgb,el) {
				$(el).css('background-color', '#'+hex);
				var name = $(el).attr('name');
				if (name in funcDic) 
					funcDic[name]["stroke"]='#'+hex;
				else 	funcDic[name]={"stroke":'#'+hex};
				$("#trees").empty();
				readConll();
				drawTrees();
				$(el).colpickHide();
				$.cookie("funcDic", JSON.stringify(funcDic), { expires: 365 });
			}
		});
	
	$( ".selector" ).change(function(handler) {
			var name=$(this).attr('name');
			if (name in funcDic) 
				funcDic[name]["stroke-dasharray"]=$(this).val();
			else 	funcDic[name]={"stroke":'#000',"stroke-dasharray":$(this).val()};
			$("#trees").empty();
			readConll();
			drawTrees();
		});

	$('#styledialog').dialog('open');
}



setupStyleDialog = function() {
	var lineStyles1='<select name="'
	var lineStyles2='" class="selector" title="line style">	<option value="" style="font-size:.6em;height:5px;" selected>âââââ</option>	<option value="- ">---------</option>	<option value=". " >âââââ</option> </select>'
	$("#stylefunctions").empty();
	$("#funchoice").empty();
	
	$.each(functions, function(i, val) 
			{
				$("#funchoice").append("<option style='color:"+((val in funcDic) ? funcDic[val]["stroke"] : "#000")+";'>"+val+"</option>")
				if (val!="" && val!=erase) 
				{
					$("#stylefunctions").append("<div><div class='color-box' style='background-color:"+((val in funcDic) ? funcDic[val]["stroke"] : "#000")+";' name='"+val+"'></div><div>"+val+" "+lineStyles1+val+lineStyles2+"</div></div><br>")
				}
			});
	$("#funchoice").attr("size",functions.length);
	$("#funchoice").css("height",functions.length*13.5);
}

currentsvgchanged = function () {

	var nr = $(currentsvg).attr("nr");
	$(".connect").hide();
	$(".cut").hide();
	$("#connect"+nr).show();
	if (isSelected && isSelected.index < Object.keys(currentsvg.words).length) // if selected word and not last word
	{
		$("#cut"+nr).attr("title","cut after selected word '"+isSelected.attr("text")+"'").show();
		
	}
}

wordsToConll = function (words) {
	thisconll=""
	var uextra=uextras[currentnr];
	if (0 in uextra) thisconll+=uextra[0].join("\n")+"\n";
	for (var i in words)
		{
			var n = words[i];
			var c=0;
			
			if ($.isEmptyObject(n.gov)) // no governors
			{					
				if (el == 4) 	ar = [n.features.t,n.features.cat,"_","_"];
				else if (el == 10) 	ar = [n.index,n.features.t,n.features.lemma,n.features.cat,n.features.xpos,n.features.morph,"_","_","_",( (("NoSpaceAfter" in n.features) && n.features["NoSpaceAfter"]==true) ? "SpaceAfter=No" : n.features.gloss)];
				else if (el == 14) 	ar = [n.index,n.features.t,"_",n.features.lemma,"_",n.features.cat,"_","_","_","_","_","_","_","_"]
				thisconll+=ar.join("\t")+"\n"
			}
			else // at least one governor
			{
				var ngovs = Object.keys(n.gov).sort();
				if (el == 10) { 
					// conllu case: pushing additional govs into special column
					var xgovsl=[]
					while (ngovs.length > 1) {
						var xj=ngovs.pop();
						var func = $('#styleconllcheck').prop('checked') ? n.gov[xj]+"::"+(n.gov[xj] in funcDic ? JSON.stringify(funcDic[n.gov[xj]]) : "") :	n.gov[xj];
						xgovsl.push(xj+":"+func);
					}
					xgovs=xgovsl.join("|");
					if (xgovs.length==0) xgovs="_";
				}
				$.each(ngovs, function(ind, j){ // loop over array of gov keys:
					var t=n.features.t;
					var cat=n.features.cat;
					var func = $('#styleconllcheck').prop('checked') ? n.gov[j]+"::"+(n.gov[j] in funcDic ? JSON.stringify(funcDic[n.gov[j]]) : "") :	n.gov[j];
					c+=1;
					if (el == 4)	{	
								if (c>1) {t="_";cat="_"}
								ar = [t,cat,j,func]
							}
					else if (el == 10) 	ar = [n.index,t,n.features.lemma,n.features.cat,n.features.xpos,n.features.morph,j,func,xgovs,( (("NoSpaceAfter" in n.features) && n.features["NoSpaceAfter"]==true) ? "SpaceAfter=No" : n.features.gloss)]
					else if (el == 14) 	ar = [n.index,t,"_",n.features.lemma,"_",n.features.cat,"_","_",j,j,func,func,"_","_"]
					thisconll+=ar.join("\t")+"\n"
				});
			}
			if (i in uextra) thisconll+=uextra[i].join("\n")+"\n";
		};
	return thisconll;
}


// overriding the arborator.draw function to add conll stuff
drawalldeps = function() 
	{
	currentnr = $(currentsvg).parent().attr("nr");
	var thisconll=wordsToConll(currentsvg.words);
	conlltrees[currentnr]=thisconll;
	for (var i in currentsvg.words)
		{
			var n = currentsvg.words[i];
			for (var j in n.svgdep) 
			{
				n.svgdep[j].remove();
				delete n.svgdep[j];
			}
			n.svgdep={};
			var c=0;
			
			var distarray = $.map(n.gov, function(value, index) {return [[Math.abs(index-i),index]];}).sort(); // order drawing of multiple governors by increasing distance
			for (var jj in distarray) 
			{
				var j=distarray[jj][1];
				drawDep(n.index,j,n.gov[j],c);
				c+=1;
			};
			
		};
		
// 	deptreeexperiment();
		
	if (inDrawTrees) return; // if i got here by reading the conll text content, no need to again write it back into the conll textarea
	
	
	// making new conlltext
	var conlltext = "";
	$.each(conlltrees, function(i, conlltree){ // just modifies the changed tree, keeps the other conll strings
			if (i==currentnr) conlltext+=thisconll+"\n";
			else conlltext+=conlltrees[i]+"\n"
		})
	
// 	if (autostart) 
	conllarea.val(conlltext);
			
	
// 	console.log("autostart",autostart)

	
// 	console.log(window.location.href.slice(-13) != "?autostart=no", 88888, window.opener)
	};


// overriding the changeCat function to add conll stuff

function changeCat(){ // called by onclick on the menu or return

	var cc=$('#catform');
	if (cc.is(":hidden")) return false;
	var cat=$("#catchoice")[0].options[$("#catchoice")[0].selectedIndex].value;
	if (cat==null) cat=defaultcategory;
	var id = cc.data("index");
	establishCat(id,cat);
	cc.hide();
	
	currentnr = $(currentsvg).parent().attr("nr");
	var thisconll=wordsToConll(currentsvg.words);
	conlltrees[currentnr]=thisconll;
// 	console.log(currentnr);
	var conlltext = "";
	$.each(conlltrees, function(i, conlltree){ // just modifies the changed tree, keeps the other conll strings
			if (i==currentnr) conlltext+=thisconll+"\n";
			else conlltext+=conlltrees[i]+"\n"
		})
	conllarea.val(conlltext);
		
}	
	
	
	
deptreeexperiment = function(){ // unfinished experiment of creating an unordered tree representation
		
	var roots=[];
	var leaves=[];
	for (var i in currentsvg.words)
		{
			var n = currentsvg.words[i];
			var k = Object.keys(n.gov);
			if (k.length==0||k.length==1 && k[0]==0)
				roots.push(n);
			n.kids=n.kids||[];
			n.levels=n.levels||{};
			n.levels[0]=n.width;
			for (var j in n.gov) 
			{
				if (j==0) continue;
				var nn = currentsvg.words[j];
				nn.kids=nn.kids||[];
				nn.kids.push(n);
			};
		};
// 	console.log(roots);
	for (var i in roots) console.log("$$$",walkdown(roots[i],0,0,[]));
// 	for (var i in currentsvg.words)
// 		{
// 			var n = currentsvg.words[i];
// 			var k = Object.keys(n.gov);
// 			if (k.length==0)
// 				leaves.push(n);
// 		}
// 	for (var i in leaves)
// 		{
// 			var n = currentsvg.words[i];
// 			
// 		}
	console.log("end deptree",currentsvg.words);
		
}
	
walkdown = function(n,level,maxi,already){
	console.log("walkingdown",n.index,n.features.t,n,level,maxi,already);
	
	if (level < n.level) n.level=level;
	n.level=n.level||level
	
	console.log("***",already.indexOf(n.index));
	if (already.indexOf(n.index)>-1) return maxi;
	already.push(n.index);
	if (level>maxi) maxi=level;
	for (var i in n.kids) {
		m=walkdown(n.kids[i], level+1,maxi,already );
		if (m>maxi) maxi=m;
	}
	
	
	console.log("***walkingdown",n.index,n,already);
	
	for (var i in n.kids) 
	{
		var kid=n.kids[i];
		for (var j in kid.levels) 
		{
			var j=parseInt(j);
			console.log("i'm in",n.index,"kid",kid.index,"kid level",j);
			n.levels[j+1]=(n.levels[j+1]||0)+kid.levels[j];
			
		}
	}
	
	
	console.log("___walkingdown",n.index,n.level,"==",n.levels,n,already);
	return maxi	
}
	
	
exportTree = function () {
	
	nr=$("#ex").attr("nr");
	
	var v=$("#exptype")[0].options[$("#exptype")[0].selectedIndex].value;

	$("svg").attr("xmlns","http://www.w3.org/2000/svg");
	$("svg").attr("xmlns:xlink","http://www.w3.org/1999/xlink");
	
// 			$('#source').attr("value",header+$('#holder'+nr).html());
	$('#source').attr( "value", svgheader+$('#holder'+nr+' >svg').clone().wrap('<p>').parent().html() );
	
	if(v=="xxxsvg")	// TODO: no need to go via the server to show svg content
	{
		var x=window.open();
		x.document.open();
		x.document.write(svgheader+$('#holder'+nr+' >svg').clone().wrap('<p>'));
		x.document.close();	
	}
	else $('#ex').submit();
		
// 		}
}

openFeatureTable = function() {
	console.log("no feature table in the quick mode.")
}


	
	
function dirt() { 
	var nr=currentsvg.id.substr(3);
	if (currentsvg.undo.undo_available)
	{
		var ind=dirty.indexOf(currentsvg.id);
		$("#save"+nr).css({ visibility: 'visible' });
		$("#undo"+nr).css({ visibility: 'visible' }).attr("title","undo: "+currentsvg.undo.undo_names[currentsvg.undo.undo_available - 1]);
		if (ind!=-1) dirty.splice(ind, 1);
		dirty.push(currentsvg.id);
	}
	else
	{	
		var ind=dirty.indexOf(currentsvg.id);
		$("#save"+nr).css({ visibility: 'hidden' });
		$("#undo"+nr).css({ visibility: 'hidden' });
		if (ind!=-1) dirty.splice(ind, 1);
	}
	if (currentsvg.undo.redo_available)
	{
		$("#redo"+nr).css({ visibility: 'visible' }).attr("title","redo: "+currentsvg.undo.redo_names[currentsvg.undo.redo_available - 1]);
	}	
	else
	{	
		$("#redo"+nr).css({ visibility: 'hidden' });
	}	
	if (dirty.length==0)	$("#sav").attr(attrclean).addClass( "ui-state-disabled " );
	else			$("#sav").attr(attrdirty).removeClass( "ui-state-disabled " );
	
}

function drawAnnodoc() {
	// bug in annodoc: tabs are not accepted as separator for conll!!
	readConll();
	$.each(conlltrees, function(i, conlltree){
			$( '<div class="annodoc" id="annodoc'+i+'">\n<pre><code class="language-conllu">\n'+ conlltree.replace( /\t/g, "  ")+ '\n</code></pre></div>').insertAfter( '#holder'+i );
// 			$( '<xspan class="annodoc"><img id="export'+i+'" class="exportannodoc" border="0" style="visibility: visible;" src="images/export.bw.png" nr="'+i+'" title="export the Annodoc svg graph"></xspan>').insertAfter( '#export'+i );

		})
	
	
	Annodoc.activate(Config.bratCollData, Collections.listing);
	
// 	$(".exportannodoc").click(function()
// 	{
// 			console.log($(this).attr("nr"))
// 			console.log( $("#annodoc"+$(this).attr("nr")+" svg" ).attr("xmlns:xlink","http://www.w3.org/1999/xlink").attr("xmlns","http://www.w3.org/1999/xlink").prop("outerHTML") );
// 			
// 			
// 	
// // 			$('#source').attr("value",header+$('#holder'+nr).html());
// // 	$('#source').attr( "value", svgheader+$('#holder'+nr+' >svg').clone().wrap('<p>').parent().html() );
// 	
// 			
// 			
// // 		$("#ex").show();
// // 		$("#ex").offset($(this).offset()).attr("nr",$(this).attr("nr"));
// 	});
	
	
}
// ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ \\
// â RaphaÃ«l 2.1.4 - JavaScript Vector Library                          â \\
// ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤ \\
// â Copyright Â© 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com)    â \\
// â Copyright Â© 2008-2012 Sencha Labs (http://sencha.com)              â \\
// ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤ \\
// â Licensed under the MIT (http://raphaeljs.com/license.html) license.â \\
// ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ \\
!function(a){var b,c,d="0.4.2",e="hasOwnProperty",f=/[\.\/]/,g="*",h=function(){},i=function(a,b){return a-b},j={n:{}},k=function(a,d){a=String(a);var e,f=c,g=Array.prototype.slice.call(arguments,2),h=k.listeners(a),j=0,l=[],m={},n=[],o=b;b=a,c=0;for(var p=0,q=h.length;q>p;p++)"zIndex"in h[p]&&(l.push(h[p].zIndex),h[p].zIndex<0&&(m[h[p].zIndex]=h[p]));for(l.sort(i);l[j]<0;)if(e=m[l[j++]],n.push(e.apply(d,g)),c)return c=f,n;for(p=0;q>p;p++)if(e=h[p],"zIndex"in e)if(e.zIndex==l[j]){if(n.push(e.apply(d,g)),c)break;do if(j++,e=m[l[j]],e&&n.push(e.apply(d,g)),c)break;while(e)}else m[e.zIndex]=e;else if(n.push(e.apply(d,g)),c)break;return c=f,b=o,n.length?n:null};k._events=j,k.listeners=function(a){var b,c,d,e,h,i,k,l,m=a.split(f),n=j,o=[n],p=[];for(e=0,h=m.length;h>e;e++){for(l=[],i=0,k=o.length;k>i;i++)for(n=o[i].n,c=[n[m[e]],n[g]],d=2;d--;)b=c[d],b&&(l.push(b),p=p.concat(b.f||[]));o=l}return p},k.on=function(a,b){if(a=String(a),"function"!=typeof b)return function(){};for(var c=a.split(f),d=j,e=0,g=c.length;g>e;e++)d=d.n,d=d.hasOwnProperty(c[e])&&d[c[e]]||(d[c[e]]={n:{}});for(d.f=d.f||[],e=0,g=d.f.length;g>e;e++)if(d.f[e]==b)return h;return d.f.push(b),function(a){+a==+a&&(b.zIndex=+a)}},k.f=function(a){var b=[].slice.call(arguments,1);return function(){k.apply(null,[a,null].concat(b).concat([].slice.call(arguments,0)))}},k.stop=function(){c=1},k.nt=function(a){return a?new RegExp("(?:\\.|\\/|^)"+a+"(?:\\.|\\/|$)").test(b):b},k.nts=function(){return b.split(f)},k.off=k.unbind=function(a,b){if(!a)return void(k._events=j={n:{}});var c,d,h,i,l,m,n,o=a.split(f),p=[j];for(i=0,l=o.length;l>i;i++)for(m=0;m<p.length;m+=h.length-2){if(h=[m,1],c=p[m].n,o[i]!=g)c[o[i]]&&h.push(c[o[i]]);else for(d in c)c[e](d)&&h.push(c[d]);p.splice.apply(p,h)}for(i=0,l=p.length;l>i;i++)for(c=p[i];c.n;){if(b){if(c.f){for(m=0,n=c.f.length;n>m;m++)if(c.f[m]==b){c.f.splice(m,1);break}!c.f.length&&delete c.f}for(d in c.n)if(c.n[e](d)&&c.n[d].f){var q=c.n[d].f;for(m=0,n=q.length;n>m;m++)if(q[m]==b){q.splice(m,1);break}!q.length&&delete c.n[d].f}}else{delete c.f;for(d in c.n)c.n[e](d)&&c.n[d].f&&delete c.n[d].f}c=c.n}},k.once=function(a,b){var c=function(){return k.unbind(a,c),b.apply(this,arguments)};return k.on(a,c)},k.version=d,k.toString=function(){return"You are running Eve "+d},"undefined"!=typeof module&&module.exports?module.exports=k:"undefined"!=typeof define?define("eve",[],function(){return k}):a.eve=k}(window||this),function(a,b){"function"==typeof define&&define.amd?define(["eve"],function(c){return b(a,c)}):b(a,a.eve||"function"==typeof require&&require("eve"))}(this,function(a,b){function c(a){if(c.is(a,"function"))return u?a():b.on("raphael.DOMload",a);if(c.is(a,V))return c._engine.create[D](c,a.splice(0,3+c.is(a[0],T))).add(a);var d=Array.prototype.slice.call(arguments,0);if(c.is(d[d.length-1],"function")){var e=d.pop();return u?e.call(c._engine.create[D](c,d)):b.on("raphael.DOMload",function(){e.call(c._engine.create[D](c,d))})}return c._engine.create[D](c,arguments)}function d(a){if("function"==typeof a||Object(a)!==a)return a;var b=new a.constructor;for(var c in a)a[z](c)&&(b[c]=d(a[c]));return b}function e(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return a.push(a.splice(c,1)[0])}function f(a,b,c){function d(){var f=Array.prototype.slice.call(arguments,0),g=f.join("â"),h=d.cache=d.cache||{},i=d.count=d.count||[];return h[z](g)?(e(i,g),c?c(h[g]):h[g]):(i.length>=1e3&&delete h[i.shift()],i.push(g),h[g]=a[D](b,f),c?c(h[g]):h[g])}return d}function g(){return this.hex}function h(a,b){for(var c=[],d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}function i(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}function j(a,b,c,d,e,f,g,h,j){null==j&&(j=1),j=j>1?1:0>j?0:j;for(var k=j/2,l=12,m=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],n=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],o=0,p=0;l>p;p++){var q=k*m[p]+k,r=i(q,a,c,e,g),s=i(q,b,d,f,h),t=r*r+s*s;o+=n[p]*N.sqrt(t)}return k*o}function k(a,b,c,d,e,f,g,h,i){if(!(0>i||j(a,b,c,d,e,f,g,h)<i)){var k,l=1,m=l/2,n=l-m,o=.01;for(k=j(a,b,c,d,e,f,g,h,n);Q(k-i)>o;)m/=2,n+=(i>k?1:-1)*m,k=j(a,b,c,d,e,f,g,h,n);return n}}function l(a,b,c,d,e,f,g,h){if(!(O(a,c)<P(e,g)||P(a,c)>O(e,g)||O(b,d)<P(f,h)||P(b,d)>O(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(k){var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(!(n<+P(a,c).toFixed(2)||n>+O(a,c).toFixed(2)||n<+P(e,g).toFixed(2)||n>+O(e,g).toFixed(2)||o<+P(b,d).toFixed(2)||o>+O(b,d).toFixed(2)||o<+P(f,h).toFixed(2)||o>+O(f,h).toFixed(2)))return{x:l,y:m}}}}function m(a,b,d){var e=c.bezierBBox(a),f=c.bezierBBox(b);if(!c.isBBoxIntersect(e,f))return d?0:[];for(var g=j.apply(0,a),h=j.apply(0,b),i=O(~~(g/5),1),k=O(~~(h/5),1),m=[],n=[],o={},p=d?0:[],q=0;i+1>q;q++){var r=c.findDotsAtSegment.apply(c,a.concat(q/i));m.push({x:r.x,y:r.y,t:q/i})}for(q=0;k+1>q;q++)r=c.findDotsAtSegment.apply(c,b.concat(q/k)),n.push({x:r.x,y:r.y,t:q/k});for(q=0;i>q;q++)for(var s=0;k>s;s++){var t=m[q],u=m[q+1],v=n[s],w=n[s+1],x=Q(u.x-t.x)<.001?"y":"x",y=Q(w.x-v.x)<.001?"y":"x",z=l(t.x,t.y,u.x,u.y,v.x,v.y,w.x,w.y);if(z){if(o[z.x.toFixed(4)]==z.y.toFixed(4))continue;o[z.x.toFixed(4)]=z.y.toFixed(4);var A=t.t+Q((z[x]-t[x])/(u[x]-t[x]))*(u.t-t.t),B=v.t+Q((z[y]-v[y])/(w[y]-v[y]))*(w.t-v.t);A>=0&&1.001>=A&&B>=0&&1.001>=B&&(d?p++:p.push({x:z.x,y:z.y,t1:P(A,1),t2:P(B,1)}))}}return p}function n(a,b,d){a=c._path2curve(a),b=c._path2curve(b);for(var e,f,g,h,i,j,k,l,n,o,p=d?0:[],q=0,r=a.length;r>q;q++){var s=a[q];if("M"==s[0])e=i=s[1],f=j=s[2];else{"C"==s[0]?(n=[e,f].concat(s.slice(1)),e=n[6],f=n[7]):(n=[e,f,e,f,i,j,i,j],e=i,f=j);for(var t=0,u=b.length;u>t;t++){var v=b[t];if("M"==v[0])g=k=v[1],h=l=v[2];else{"C"==v[0]?(o=[g,h].concat(v.slice(1)),g=o[6],h=o[7]):(o=[g,h,g,h,k,l,k,l],g=k,h=l);var w=m(n,o,d);if(d)p+=w;else{for(var x=0,y=w.length;y>x;x++)w[x].segment1=q,w[x].segment2=t,w[x].bez1=n,w[x].bez2=o;p=p.concat(w)}}}}}return p}function o(a,b,c,d,e,f){null!=a?(this.a=+a,this.b=+b,this.c=+c,this.d=+d,this.e=+e,this.f=+f):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0)}function p(){return this.x+H+this.y+H+this.width+" Ã "+this.height}function q(a,b,c,d,e,f){function g(a){return((l*a+k)*a+j)*a}function h(a,b){var c=i(a,b);return((o*c+n)*c+m)*c}function i(a,b){var c,d,e,f,h,i;for(e=a,i=0;8>i;i++){if(f=g(e)-a,Q(f)<b)return e;if(h=(3*l*e+2*k)*e+j,Q(h)<1e-6)break;e-=f/h}if(c=0,d=1,e=a,c>e)return c;if(e>d)return d;for(;d>c;){if(f=g(e),Q(f-a)<b)return e;a>f?c=e:d=e,e=(d-c)/2+c}return e}var j=3*b,k=3*(d-b)-j,l=1-j-k,m=3*c,n=3*(e-c)-m,o=1-m-n;return h(a,1/(200*f))}function r(a,b){var c=[],d={};if(this.ms=b,this.times=1,a){for(var e in a)a[z](e)&&(d[_(e)]=a[e],c.push(_(e)));c.sort(lb)}this.anim=d,this.top=c[c.length-1],this.percents=c}function s(a,d,e,f,g,h){e=_(e);var i,j,k,l,m,n,p=a.ms,r={},s={},t={};if(f)for(v=0,x=ic.length;x>v;v++){var u=ic[v];if(u.el.id==d.id&&u.anim==a){u.percent!=e?(ic.splice(v,1),k=1):j=u,d.attr(u.totalOrigin);break}}else f=+s;for(var v=0,x=a.percents.length;x>v;v++){if(a.percents[v]==e||a.percents[v]>f*a.top){e=a.percents[v],m=a.percents[v-1]||0,p=p/a.top*(e-m),l=a.percents[v+1],i=a.anim[e];break}f&&d.attr(a.anim[a.percents[v]])}if(i){if(j)j.initstatus=f,j.start=new Date-j.ms*f;else{for(var y in i)if(i[z](y)&&(db[z](y)||d.paper.customAttributes[z](y)))switch(r[y]=d.attr(y),null==r[y]&&(r[y]=cb[y]),s[y]=i[y],db[y]){case T:t[y]=(s[y]-r[y])/p;break;case"colour":r[y]=c.getRGB(r[y]);var A=c.getRGB(s[y]);t[y]={r:(A.r-r[y].r)/p,g:(A.g-r[y].g)/p,b:(A.b-r[y].b)/p};break;case"path":var B=Kb(r[y],s[y]),C=B[1];for(r[y]=B[0],t[y]=[],v=0,x=r[y].length;x>v;v++){t[y][v]=[0];for(var D=1,F=r[y][v].length;F>D;D++)t[y][v][D]=(C[v][D]-r[y][v][D])/p}break;case"transform":var G=d._,H=Pb(G[y],s[y]);if(H)for(r[y]=H.from,s[y]=H.to,t[y]=[],t[y].real=!0,v=0,x=r[y].length;x>v;v++)for(t[y][v]=[r[y][v][0]],D=1,F=r[y][v].length;F>D;D++)t[y][v][D]=(s[y][v][D]-r[y][v][D])/p;else{var K=d.matrix||new o,L={_:{transform:G.transform},getBBox:function(){return d.getBBox(1)}};r[y]=[K.a,K.b,K.c,K.d,K.e,K.f],Nb(L,s[y]),s[y]=L._.transform,t[y]=[(L.matrix.a-K.a)/p,(L.matrix.b-K.b)/p,(L.matrix.c-K.c)/p,(L.matrix.d-K.d)/p,(L.matrix.e-K.e)/p,(L.matrix.f-K.f)/p]}break;case"csv":var M=I(i[y])[J](w),N=I(r[y])[J](w);if("clip-rect"==y)for(r[y]=N,t[y]=[],v=N.length;v--;)t[y][v]=(M[v]-r[y][v])/p;s[y]=M;break;default:for(M=[][E](i[y]),N=[][E](r[y]),t[y]=[],v=d.paper.customAttributes[y].length;v--;)t[y][v]=((M[v]||0)-(N[v]||0))/p}var O=i.easing,P=c.easing_formulas[O];if(!P)if(P=I(O).match(Z),P&&5==P.length){var Q=P;P=function(a){return q(a,+Q[1],+Q[2],+Q[3],+Q[4],p)}}else P=nb;if(n=i.start||a.start||+new Date,u={anim:a,percent:e,timestamp:n,start:n+(a.del||0),status:0,initstatus:f||0,stop:!1,ms:p,easing:P,from:r,diff:t,to:s,el:d,callback:i.callback,prev:m,next:l,repeat:h||a.times,origin:d.attr(),totalOrigin:g},ic.push(u),f&&!j&&!k&&(u.stop=!0,u.start=new Date-p*f,1==ic.length))return kc();k&&(u.start=new Date-u.ms*f),1==ic.length&&jc(kc)}b("raphael.anim.start."+d.id,d,a)}}function t(a){for(var b=0;b<ic.length;b++)ic[b].el.paper==a&&ic.splice(b--,1)}c.version="2.1.2",c.eve=b;var u,v,w=/[, ]+/,x={circle:1,rect:1,path:1,ellipse:1,text:1,image:1},y=/\{(\d+)\}/g,z="hasOwnProperty",A={doc:document,win:a},B={was:Object.prototype[z].call(A.win,"Raphael"),is:A.win.Raphael},C=function(){this.ca=this.customAttributes={}},D="apply",E="concat",F="ontouchstart"in A.win||A.win.DocumentTouch&&A.doc instanceof DocumentTouch,G="",H=" ",I=String,J="split",K="click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[J](H),L={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},M=I.prototype.toLowerCase,N=Math,O=N.max,P=N.min,Q=N.abs,R=N.pow,S=N.PI,T="number",U="string",V="array",W=Object.prototype.toString,X=(c._ISURL=/^url\(['"]?(.+?)['"]?\)$/i,/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i),Y={NaN:1,Infinity:1,"-Infinity":1},Z=/^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,$=N.round,_=parseFloat,ab=parseInt,bb=I.prototype.toUpperCase,cb=c._availableAttrs={"arrow-end":"none","arrow-start":"none",blur:0,"clip-rect":"0 0 1e9 1e9",cursor:"default",cx:0,cy:0,fill:"#fff","fill-opacity":1,font:'10px "Arial"',"font-family":'"Arial"',"font-size":"10","font-style":"normal","font-weight":400,gradient:0,height:0,href:"http://raphaeljs.com/","letter-spacing":0,opacity:1,path:"M0,0",r:0,rx:0,ry:0,src:"",stroke:"#000","stroke-dasharray":"","stroke-linecap":"butt","stroke-linejoin":"butt","stroke-miterlimit":0,"stroke-opacity":1,"stroke-width":1,target:"_blank","text-anchor":"middle",title:"Raphael",transform:"",width:0,x:0,y:0},db=c._availableAnimAttrs={blur:T,"clip-rect":"csv",cx:T,cy:T,fill:"colour","fill-opacity":T,"font-size":T,height:T,opacity:T,path:"path",r:T,rx:T,ry:T,stroke:"colour","stroke-opacity":T,"stroke-width":T,transform:"transform",width:T,x:T,y:T},eb=/[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/,fb={hs:1,rg:1},gb=/,?([achlmqrstvxz]),?/gi,hb=/([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/gi,ib=/([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/gi,jb=/(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/gi,kb=(c._radial_gradient=/^r(?:\(([^,]+?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*([^\)]+?)\))?/,{}),lb=function(a,b){return _(a)-_(b)},mb=function(){},nb=function(a){return a},ob=c._rectPath=function(a,b,c,d,e){return e?[["M",a+e,b],["l",c-2*e,0],["a",e,e,0,0,1,e,e],["l",0,d-2*e],["a",e,e,0,0,1,-e,e],["l",2*e-c,0],["a",e,e,0,0,1,-e,-e],["l",0,2*e-d],["a",e,e,0,0,1,e,-e],["z"]]:[["M",a,b],["l",c,0],["l",0,d],["l",-c,0],["z"]]},pb=function(a,b,c,d){return null==d&&(d=c),[["M",a,b],["m",0,-d],["a",c,d,0,1,1,0,2*d],["a",c,d,0,1,1,0,-2*d],["z"]]},qb=c._getPath={path:function(a){return a.attr("path")},circle:function(a){var b=a.attrs;return pb(b.cx,b.cy,b.r)},ellipse:function(a){var b=a.attrs;return pb(b.cx,b.cy,b.rx,b.ry)},rect:function(a){var b=a.attrs;return ob(b.x,b.y,b.width,b.height,b.r)},image:function(a){var b=a.attrs;return ob(b.x,b.y,b.width,b.height)},text:function(a){var b=a._getBBox();return ob(b.x,b.y,b.width,b.height)},set:function(a){var b=a._getBBox();return ob(b.x,b.y,b.width,b.height)}},rb=c.mapPath=function(a,b){if(!b)return a;var c,d,e,f,g,h,i;for(a=Kb(a),e=0,g=a.length;g>e;e++)for(i=a[e],f=1,h=i.length;h>f;f+=2)c=b.x(i[f],i[f+1]),d=b.y(i[f],i[f+1]),i[f]=c,i[f+1]=d;return a};if(c._g=A,c.type=A.win.SVGAngle||A.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")?"SVG":"VML","VML"==c.type){var sb,tb=A.doc.createElement("div");if(tb.innerHTML='<v:shape adj="1"/>',sb=tb.firstChild,sb.style.behavior="url(#default#VML)",!sb||"object"!=typeof sb.adj)return c.type=G;tb=null}c.svg=!(c.vml="VML"==c.type),c._Paper=C,c.fn=v=C.prototype=c.prototype,c._id=0,c._oid=0,c.is=function(a,b){return b=M.call(b),"finite"==b?!Y[z](+a):"array"==b?a instanceof Array:"null"==b&&null===a||b==typeof a&&null!==a||"object"==b&&a===Object(a)||"array"==b&&Array.isArray&&Array.isArray(a)||W.call(a).slice(8,-1).toLowerCase()==b},c.angle=function(a,b,d,e,f,g){if(null==f){var h=a-d,i=b-e;return h||i?(180+180*N.atan2(-i,-h)/S+360)%360:0}return c.angle(a,b,f,g)-c.angle(d,e,f,g)},c.rad=function(a){return a%360*S/180},c.deg=function(a){return Math.round(180*a/S%360*1e3)/1e3},c.snapTo=function(a,b,d){if(d=c.is(d,"finite")?d:10,c.is(a,V)){for(var e=a.length;e--;)if(Q(a[e]-b)<=d)return a[e]}else{a=+a;var f=b%a;if(d>f)return b-f;if(f>a-d)return b-f+a}return b};c.createUUID=function(a,b){return function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(a,b).toUpperCase()}}(/[xy]/g,function(a){var b=16*N.random()|0,c="x"==a?b:3&b|8;return c.toString(16)});c.setWindow=function(a){b("raphael.setWindow",c,A.win,a),A.win=a,A.doc=A.win.document,c._engine.initWin&&c._engine.initWin(A.win)};var ub=function(a){if(c.vml){var b,d=/^\s+|\s+$/g;try{var e=new ActiveXObject("htmlfile");e.write("<body>"),e.close(),b=e.body}catch(g){b=createPopup().document.body}var h=b.createTextRange();ub=f(function(a){try{b.style.color=I(a).replace(d,G);var c=h.queryCommandValue("ForeColor");return c=(255&c)<<16|65280&c|(16711680&c)>>>16,"#"+("000000"+c.toString(16)).slice(-6)}catch(e){return"none"}})}else{var i=A.doc.createElement("i");i.title="RaphaÃ«l Colour Picker",i.style.display="none",A.doc.body.appendChild(i),ub=f(function(a){return i.style.color=a,A.doc.defaultView.getComputedStyle(i,G).getPropertyValue("color")})}return ub(a)},vb=function(){return"hsb("+[this.h,this.s,this.b]+")"},wb=function(){return"hsl("+[this.h,this.s,this.l]+")"},xb=function(){return this.hex},yb=function(a,b,d){if(null==b&&c.is(a,"object")&&"r"in a&&"g"in a&&"b"in a&&(d=a.b,b=a.g,a=a.r),null==b&&c.is(a,U)){var e=c.getRGB(a);a=e.r,b=e.g,d=e.b}return(a>1||b>1||d>1)&&(a/=255,b/=255,d/=255),[a,b,d]},zb=function(a,b,d,e){a*=255,b*=255,d*=255;var f={r:a,g:b,b:d,hex:c.rgb(a,b,d),toString:xb};return c.is(e,"finite")&&(f.opacity=e),f};c.color=function(a){var b;return c.is(a,"object")&&"h"in a&&"s"in a&&"b"in a?(b=c.hsb2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.hex=b.hex):c.is(a,"object")&&"h"in a&&"s"in a&&"l"in a?(b=c.hsl2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.hex=b.hex):(c.is(a,"string")&&(a=c.getRGB(a)),c.is(a,"object")&&"r"in a&&"g"in a&&"b"in a?(b=c.rgb2hsl(a),a.h=b.h,a.s=b.s,a.l=b.l,b=c.rgb2hsb(a),a.v=b.b):(a={hex:"none"},a.r=a.g=a.b=a.h=a.s=a.v=a.l=-1)),a.toString=xb,a},c.hsb2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,d=a.o,a=a.h),a*=360;var e,f,g,h,i;return a=a%360/60,i=c*b,h=i*(1-Q(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],zb(e,f,g,d)},c.hsl2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h),(a>1||b>1||c>1)&&(a/=360,b/=100,c/=100),a*=360;var e,f,g,h,i;return a=a%360/60,i=2*b*(.5>c?c:1-c),h=i*(1-Q(a%2-1)),e=f=g=c-i/2,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],zb(e,f,g,d)},c.rgb2hsb=function(a,b,c){c=yb(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;return f=O(a,b,c),g=f-P(a,b,c),d=0==g?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=0==g?0:g/f,{h:d,s:e,b:f,toString:vb}},c.rgb2hsl=function(a,b,c){c=yb(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;return g=O(a,b,c),h=P(a,b,c),i=g-h,d=0==i?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=0==i?0:.5>f?i/(2*f):i/(2-2*f),{h:d,s:e,l:f,toString:wb}},c._path2string=function(){return this.join(",").replace(gb,"$1")};c._preload=function(a,b){var c=A.doc.createElement("img");c.style.cssText="position:absolute;left:-9999em;top:-9999em",c.onload=function(){b.call(this),this.onload=null,A.doc.body.removeChild(this)},c.onerror=function(){A.doc.body.removeChild(this)},A.doc.body.appendChild(c),c.src=a};c.getRGB=f(function(a){if(!a||(a=I(a)).indexOf("-")+1)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:g};if("none"==a)return{r:-1,g:-1,b:-1,hex:"none",toString:g};!(fb[z](a.toLowerCase().substring(0,2))||"#"==a.charAt())&&(a=ub(a));var b,d,e,f,h,i,j=a.match(X);return j?(j[2]&&(e=ab(j[2].substring(5),16),d=ab(j[2].substring(3,5),16),b=ab(j[2].substring(1,3),16)),j[3]&&(e=ab((h=j[3].charAt(3))+h,16),d=ab((h=j[3].charAt(2))+h,16),b=ab((h=j[3].charAt(1))+h,16)),j[4]&&(i=j[4][J](eb),b=_(i[0]),"%"==i[0].slice(-1)&&(b*=2.55),d=_(i[1]),"%"==i[1].slice(-1)&&(d*=2.55),e=_(i[2]),"%"==i[2].slice(-1)&&(e*=2.55),"rgba"==j[1].toLowerCase().slice(0,4)&&(f=_(i[3])),i[3]&&"%"==i[3].slice(-1)&&(f/=100)),j[5]?(i=j[5][J](eb),b=_(i[0]),"%"==i[0].slice(-1)&&(b*=2.55),d=_(i[1]),"%"==i[1].slice(-1)&&(d*=2.55),e=_(i[2]),"%"==i[2].slice(-1)&&(e*=2.55),("deg"==i[0].slice(-3)||"Â°"==i[0].slice(-1))&&(b/=360),"hsba"==j[1].toLowerCase().slice(0,4)&&(f=_(i[3])),i[3]&&"%"==i[3].slice(-1)&&(f/=100),c.hsb2rgb(b,d,e,f)):j[6]?(i=j[6][J](eb),b=_(i[0]),"%"==i[0].slice(-1)&&(b*=2.55),d=_(i[1]),"%"==i[1].slice(-1)&&(d*=2.55),e=_(i[2]),"%"==i[2].slice(-1)&&(e*=2.55),("deg"==i[0].slice(-3)||"Â°"==i[0].slice(-1))&&(b/=360),"hsla"==j[1].toLowerCase().slice(0,4)&&(f=_(i[3])),i[3]&&"%"==i[3].slice(-1)&&(f/=100),c.hsl2rgb(b,d,e,f)):(j={r:b,g:d,b:e,toString:g},j.hex="#"+(16777216|e|d<<8|b<<16).toString(16).slice(1),c.is(f,"finite")&&(j.opacity=f),j)):{r:-1,g:-1,b:-1,hex:"none",error:1,toString:g}},c),c.hsb=f(function(a,b,d){return c.hsb2rgb(a,b,d).hex}),c.hsl=f(function(a,b,d){return c.hsl2rgb(a,b,d).hex}),c.rgb=f(function(a,b,c){return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)}),c.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||.75},c=this.hsb2rgb(b.h,b.s,b.b);return b.h+=.075,b.h>1&&(b.h=0,b.s-=.2,b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b})),c.hex},c.getColor.reset=function(){delete this.start},c.parsePathString=function(a){if(!a)return null;var b=Ab(a);if(b.arr)return Cb(b.arr);var d={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0},e=[];return c.is(a,V)&&c.is(a[0],V)&&(e=Cb(a)),e.length||I(a).replace(hb,function(a,b,c){var f=[],g=b.toLowerCase();if(c.replace(jb,function(a,b){b&&f.push(+b)}),"m"==g&&f.length>2&&(e.push([b][E](f.splice(0,2))),g="l",b="m"==b?"l":"L"),"r"==g)e.push([b][E](f));else for(;f.length>=d[g]&&(e.push([b][E](f.splice(0,d[g]))),d[g]););}),e.toString=c._path2string,b.arr=Cb(e),e},c.parseTransformString=f(function(a){if(!a)return null;var b=[];return c.is(a,V)&&c.is(a[0],V)&&(b=Cb(a)),b.length||I(a).replace(ib,function(a,c,d){{var e=[];M.call(c)}d.replace(jb,function(a,b){b&&e.push(+b)}),b.push([c][E](e))}),b.toString=c._path2string,b});var Ab=function(a){var b=Ab.ps=Ab.ps||{};return b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)b[z](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])}),b[a]};c.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=R(j,3),l=R(j,2),m=i*i,n=m*i,o=k*a+3*l*i*c+3*j*i*i*e+n*g,p=k*b+3*l*i*d+3*j*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,w=j*e+i*g,x=j*f+i*h,y=90-180*N.atan2(q-s,r-t)/S;return(q>s||t>r)&&(y+=180),{x:o,y:p,m:{x:q,y:r},n:{x:s,y:t},start:{x:u,y:v},end:{x:w,y:x},alpha:y}},c.bezierBBox=function(a,b,d,e,f,g,h,i){c.is(a,"array")||(a=[a,b,d,e,f,g,h,i]);var j=Jb.apply(null,a);return{x:j.min.x,y:j.min.y,x2:j.max.x,y2:j.max.y,width:j.max.x-j.min.x,height:j.max.y-j.min.y}},c.isPointInsideBBox=function(a,b,c){return b>=a.x&&b<=a.x2&&c>=a.y&&c<=a.y2},c.isBBoxIntersect=function(a,b){var d=c.isPointInsideBBox;return d(b,a.x,a.y)||d(b,a.x2,a.y)||d(b,a.x,a.y2)||d(b,a.x2,a.y2)||d(a,b.x,b.y)||d(a,b.x2,b.y)||d(a,b.x,b.y2)||d(a,b.x2,b.y2)||(a.x<b.x2&&a.x>b.x||b.x<a.x2&&b.x>a.x)&&(a.y<b.y2&&a.y>b.y||b.y<a.y2&&b.y>a.y)},c.pathIntersection=function(a,b){return n(a,b)},c.pathIntersectionNumber=function(a,b){return n(a,b,1)},c.isPointInsidePath=function(a,b,d){var e=c.pathBBox(a);return c.isPointInsideBBox(e,b,d)&&n(a,[["M",b,d],["H",e.x2+10]],1)%2==1},c._removedFactory=function(a){return function(){b("raphael.log",null,"RaphaÃ«l: you are calling to method â"+a+"â of removed object",a)}};var Bb=c.pathBBox=function(a){var b=Ab(a);if(b.bbox)return d(b.bbox);if(!a)return{x:0,y:0,width:0,height:0,x2:0,y2:0};a=Kb(a);for(var c,e=0,f=0,g=[],h=[],i=0,j=a.length;j>i;i++)if(c=a[i],"M"==c[0])e=c[1],f=c[2],g.push(e),h.push(f);else{var k=Jb(e,f,c[1],c[2],c[3],c[4],c[5],c[6]);g=g[E](k.min.x,k.max.x),h=h[E](k.min.y,k.max.y),e=c[5],f=c[6]}var l=P[D](0,g),m=P[D](0,h),n=O[D](0,g),o=O[D](0,h),p=n-l,q=o-m,r={x:l,y:m,x2:n,y2:o,width:p,height:q,cx:l+p/2,cy:m+q/2};return b.bbox=d(r),r},Cb=function(a){var b=d(a);return b.toString=c._path2string,b},Db=c._pathToRelative=function(a){var b=Ab(a);if(b.rel)return Cb(b.rel);c.is(a,V)&&c.is(a&&a[0],V)||(a=c.parsePathString(a));var d=[],e=0,f=0,g=0,h=0,i=0;"M"==a[0][0]&&(e=a[0][1],f=a[0][2],g=e,h=f,i++,d.push(["M",e,f]));for(var j=i,k=a.length;k>j;j++){var l=d[j]=[],m=a[j];if(m[0]!=M.call(m[0]))switch(l[0]=M.call(m[0]),l[0]){case"a":l[1]=m[1],l[2]=m[2],l[3]=m[3],l[4]=m[4],l[5]=m[5],l[6]=+(m[6]-e).toFixed(3),l[7]=+(m[7]-f).toFixed(3);break;case"v":l[1]=+(m[1]-f).toFixed(3);break;case"m":g=m[1],h=m[2];default:for(var n=1,o=m.length;o>n;n++)l[n]=+(m[n]-(n%2?e:f)).toFixed(3)}else{l=d[j]=[],"m"==m[0]&&(g=m[1]+e,h=m[2]+f);for(var p=0,q=m.length;q>p;p++)d[j][p]=m[p]}var r=d[j].length;switch(d[j][0]){case"z":e=g,f=h;break;case"h":e+=+d[j][r-1];break;case"v":f+=+d[j][r-1];break;default:e+=+d[j][r-2],f+=+d[j][r-1]}}return d.toString=c._path2string,b.rel=Cb(d),d},Eb=c._pathToAbsolute=function(a){var b=Ab(a);if(b.abs)return Cb(b.abs);if(c.is(a,V)&&c.is(a&&a[0],V)||(a=c.parsePathString(a)),!a||!a.length)return[["M",0,0]];var d=[],e=0,f=0,g=0,i=0,j=0;"M"==a[0][0]&&(e=+a[0][1],f=+a[0][2],g=e,i=f,j++,d[0]=["M",e,f]);for(var k,l,m=3==a.length&&"M"==a[0][0]&&"R"==a[1][0].toUpperCase()&&"Z"==a[2][0].toUpperCase(),n=j,o=a.length;o>n;n++){if(d.push(k=[]),l=a[n],l[0]!=bb.call(l[0]))switch(k[0]=bb.call(l[0]),k[0]){case"A":k[1]=l[1],k[2]=l[2],k[3]=l[3],k[4]=l[4],k[5]=l[5],k[6]=+(l[6]+e),k[7]=+(l[7]+f);break;case"V":k[1]=+l[1]+f;break;case"H":k[1]=+l[1]+e;break;case"R":for(var p=[e,f][E](l.slice(1)),q=2,r=p.length;r>q;q++)p[q]=+p[q]+e,p[++q]=+p[q]+f;d.pop(),d=d[E](h(p,m));break;case"M":g=+l[1]+e,i=+l[2]+f;default:for(q=1,r=l.length;r>q;q++)k[q]=+l[q]+(q%2?e:f)}else if("R"==l[0])p=[e,f][E](l.slice(1)),d.pop(),d=d[E](h(p,m)),k=["R"][E](l.slice(-2));else for(var s=0,t=l.length;t>s;s++)k[s]=l[s];switch(k[0]){case"Z":e=g,f=i;break;case"H":e=k[1];break;case"V":f=k[1];break;case"M":g=k[k.length-2],i=k[k.length-1];default:e=k[k.length-2],f=k[k.length-1]}}return d.toString=c._path2string,b.abs=Cb(d),d},Fb=function(a,b,c,d){return[a,b,c,d,c,d]},Gb=function(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]},Hb=function(a,b,c,d,e,g,h,i,j,k){var l,m=120*S/180,n=S/180*(+e||0),o=[],p=f(function(a,b,c){var d=a*N.cos(c)-b*N.sin(c),e=a*N.sin(c)+b*N.cos(c);return{x:d,y:e}});if(k)y=k[0],z=k[1],w=k[2],x=k[3];else{l=p(a,b,-n),a=l.x,b=l.y,l=p(i,j,-n),i=l.x,j=l.y;var q=(N.cos(S/180*e),N.sin(S/180*e),(a-i)/2),r=(b-j)/2,s=q*q/(c*c)+r*r/(d*d);s>1&&(s=N.sqrt(s),c=s*c,d=s*d);var t=c*c,u=d*d,v=(g==h?-1:1)*N.sqrt(Q((t*u-t*r*r-u*q*q)/(t*r*r+u*q*q))),w=v*c*r/d+(a+i)/2,x=v*-d*q/c+(b+j)/2,y=N.asin(((b-x)/d).toFixed(9)),z=N.asin(((j-x)/d).toFixed(9));y=w>a?S-y:y,z=w>i?S-z:z,0>y&&(y=2*S+y),0>z&&(z=2*S+z),h&&y>z&&(y-=2*S),!h&&z>y&&(z-=2*S)}var A=z-y;if(Q(A)>m){var B=z,C=i,D=j;z=y+m*(h&&z>y?1:-1),i=w+c*N.cos(z),j=x+d*N.sin(z),o=Hb(i,j,c,d,e,0,h,C,D,[z,B,w,x])}A=z-y;var F=N.cos(y),G=N.sin(y),H=N.cos(z),I=N.sin(z),K=N.tan(A/4),L=4/3*c*K,M=4/3*d*K,O=[a,b],P=[a+L*G,b-M*F],R=[i+L*I,j-M*H],T=[i,j];if(P[0]=2*O[0]-P[0],P[1]=2*O[1]-P[1],k)return[P,R,T][E](o);o=[P,R,T][E](o).join()[J](",");for(var U=[],V=0,W=o.length;W>V;V++)U[V]=V%2?p(o[V-1],o[V],n).y:p(o[V],o[V+1],n).x;return U},Ib=function(a,b,c,d,e,f,g,h,i){var j=1-i;return{x:R(j,3)*a+3*R(j,2)*i*c+3*j*i*i*e+R(i,3)*g,y:R(j,3)*b+3*R(j,2)*i*d+3*j*i*i*f+R(i,3)*h}},Jb=f(function(a,b,c,d,e,f,g,h){var i,j=e-2*c+a-(g-2*e+c),k=2*(c-a)-2*(e-c),l=a-c,m=(-k+N.sqrt(k*k-4*j*l))/2/j,n=(-k-N.sqrt(k*k-4*j*l))/2/j,o=[b,h],p=[a,g];return Q(m)>"1e12"&&(m=.5),Q(n)>"1e12"&&(n=.5),m>0&&1>m&&(i=Ib(a,b,c,d,e,f,g,h,m),p.push(i.x),o.push(i.y)),n>0&&1>n&&(i=Ib(a,b,c,d,e,f,g,h,n),p.push(i.x),o.push(i.y)),j=f-2*d+b-(h-2*f+d),k=2*(d-b)-2*(f-d),l=b-d,m=(-k+N.sqrt(k*k-4*j*l))/2/j,n=(-k-N.sqrt(k*k-4*j*l))/2/j,Q(m)>"1e12"&&(m=.5),Q(n)>"1e12"&&(n=.5),m>0&&1>m&&(i=Ib(a,b,c,d,e,f,g,h,m),p.push(i.x),o.push(i.y)),n>0&&1>n&&(i=Ib(a,b,c,d,e,f,g,h,n),p.push(i.x),o.push(i.y)),{min:{x:P[D](0,p),y:P[D](0,o)},max:{x:O[D](0,p),y:O[D](0,o)}}}),Kb=c._path2curve=f(function(a,b){var c=!b&&Ab(a);if(!b&&c.curve)return Cb(c.curve);for(var d=Eb(a),e=b&&Eb(b),f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},h=(function(a,b,c){var d,e,f={T:1,Q:1};if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];switch(!(a[0]in f)&&(b.qx=b.qy=null),a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"][E](Hb[D](0,[b.x,b.y][E](a.slice(1))));break;case"S":"C"==c||"S"==c?(d=2*b.x-b.bx,e=2*b.y-b.by):(d=b.x,e=b.y),a=["C",d,e][E](a.slice(1));break;case"T":"Q"==c||"T"==c?(b.qx=2*b.x-b.qx,b.qy=2*b.y-b.qy):(b.qx=b.x,b.qy=b.y),a=["C"][E](Gb(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"][E](Gb(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][E](Fb(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][E](Fb(b.x,b.y,a[1],b.y));break;case"V":a=["C"][E](Fb(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][E](Fb(b.x,b.y,b.X,b.Y))}return a}),i=function(a,b){if(a[b].length>7){a[b].shift();for(var c=a[b];c.length;)k[b]="A",e&&(l[b]="A"),a.splice(b++,0,["C"][E](c.splice(0,6)));a.splice(b,1),p=O(d.length,e&&e.length||0)}},j=function(a,b,c,f,g){a&&b&&"M"==a[g][0]&&"M"!=b[g][0]&&(b.splice(g,0,["M",f.x,f.y]),c.bx=0,c.by=0,c.x=a[g][1],c.y=a[g][2],p=O(d.length,e&&e.length||0))},k=[],l=[],m="",n="",o=0,p=O(d.length,e&&e.length||0);p>o;o++){d[o]&&(m=d[o][0]),"C"!=m&&(k[o]=m,o&&(n=k[o-1])),d[o]=h(d[o],f,n),"A"!=k[o]&&"C"==m&&(k[o]="C"),i(d,o),e&&(e[o]&&(m=e[o][0]),"C"!=m&&(l[o]=m,o&&(n=l[o-1])),e[o]=h(e[o],g,n),"A"!=l[o]&&"C"==m&&(l[o]="C"),i(e,o)),j(d,e,f,g,o),j(e,d,g,f,o);var q=d[o],r=e&&e[o],s=q.length,t=e&&r.length;f.x=q[s-2],f.y=q[s-1],f.bx=_(q[s-4])||f.x,f.by=_(q[s-3])||f.y,g.bx=e&&(_(r[t-4])||g.x),g.by=e&&(_(r[t-3])||g.y),g.x=e&&r[t-2],g.y=e&&r[t-1]}return e||(c.curve=Cb(d)),e?[d,e]:d},null,Cb),Lb=(c._parseDots=f(function(a){for(var b=[],d=0,e=a.length;e>d;d++){var f={},g=a[d].match(/^([^:]*):?([\d\.]*)/);if(f.color=c.getRGB(g[1]),f.color.error)return null;f.color=f.color.hex,g[2]&&(f.offset=g[2]+"%"),b.push(f)}for(d=1,e=b.length-1;e>d;d++)if(!b[d].offset){for(var h=_(b[d-1].offset||0),i=0,j=d+1;e>j;j++)if(b[j].offset){i=b[j].offset;break}i||(i=100,j=e),i=_(i);for(var k=(i-h)/(j-d+1);j>d;d++)h+=k,b[d].offset=h+"%"}return b}),c._tear=function(a,b){a==b.top&&(b.top=a.prev),a==b.bottom&&(b.bottom=a.next),a.next&&(a.next.prev=a.prev),a.prev&&(a.prev.next=a.next)}),Mb=(c._tofront=function(a,b){b.top!==a&&(Lb(a,b),a.next=null,a.prev=b.top,b.top.next=a,b.top=a)},c._toback=function(a,b){b.bottom!==a&&(Lb(a,b),a.next=b.bottom,a.prev=null,b.bottom.prev=a,b.bottom=a)},c._insertafter=function(a,b,c){Lb(a,c),b==c.top&&(c.top=a),b.next&&(b.next.prev=a),a.next=b.next,a.prev=b,b.next=a},c._insertbefore=function(a,b,c){Lb(a,c),b==c.bottom&&(c.bottom=a),b.prev&&(b.prev.next=a),a.prev=b.prev,b.prev=a,a.next=b},c.toMatrix=function(a,b){var c=Bb(a),d={_:{transform:G},getBBox:function(){return c}};return Nb(d,b),d.matrix}),Nb=(c.transformPath=function(a,b){return rb(a,Mb(a,b))},c._extractTransform=function(a,b){if(null==b)return a._.transform;b=I(b).replace(/\.{3}|\u2026/g,a._.transform||G);var d=c.parseTransformString(b),e=0,f=0,g=0,h=1,i=1,j=a._,k=new o;if(j.transform=d||[],d)for(var l=0,m=d.length;m>l;l++){var n,p,q,r,s,t=d[l],u=t.length,v=I(t[0]).toLowerCase(),w=t[0]!=v,x=w?k.invert():0;"t"==v&&3==u?w?(n=x.x(0,0),p=x.y(0,0),q=x.x(t[1],t[2]),r=x.y(t[1],t[2]),k.translate(q-n,r-p)):k.translate(t[1],t[2]):"r"==v?2==u?(s=s||a.getBBox(1),k.rotate(t[1],s.x+s.width/2,s.y+s.height/2),e+=t[1]):4==u&&(w?(q=x.x(t[2],t[3]),r=x.y(t[2],t[3]),k.rotate(t[1],q,r)):k.rotate(t[1],t[2],t[3]),e+=t[1]):"s"==v?2==u||3==u?(s=s||a.getBBox(1),k.scale(t[1],t[u-1],s.x+s.width/2,s.y+s.height/2),h*=t[1],i*=t[u-1]):5==u&&(w?(q=x.x(t[3],t[4]),r=x.y(t[3],t[4]),k.scale(t[1],t[2],q,r)):k.scale(t[1],t[2],t[3],t[4]),h*=t[1],i*=t[2]):"m"==v&&7==u&&k.add(t[1],t[2],t[3],t[4],t[5],t[6]),j.dirtyT=1,a.matrix=k}a.matrix=k,j.sx=h,j.sy=i,j.deg=e,j.dx=f=k.e,j.dy=g=k.f,1==h&&1==i&&!e&&j.bbox?(j.bbox.x+=+f,j.bbox.y+=+g):j.dirtyT=1}),Ob=function(a){var b=a[0];switch(b.toLowerCase()){case"t":return[b,0,0];case"m":return[b,1,0,0,1,0,0];case"r":return 4==a.length?[b,0,a[2],a[3]]:[b,0];case"s":return 5==a.length?[b,1,1,a[3],a[4]]:3==a.length?[b,1,1]:[b,1]}},Pb=c._equaliseTransform=function(a,b){b=I(b).replace(/\.{3}|\u2026/g,a),a=c.parseTransformString(a)||[],b=c.parseTransformString(b)||[];
for(var d,e,f,g,h=O(a.length,b.length),i=[],j=[],k=0;h>k;k++){if(f=a[k]||Ob(b[k]),g=b[k]||Ob(f),f[0]!=g[0]||"r"==f[0].toLowerCase()&&(f[2]!=g[2]||f[3]!=g[3])||"s"==f[0].toLowerCase()&&(f[3]!=g[3]||f[4]!=g[4]))return;for(i[k]=[],j[k]=[],d=0,e=O(f.length,g.length);e>d;d++)d in f&&(i[k][d]=f[d]),d in g&&(j[k][d]=g[d])}return{from:i,to:j}};c._getContainer=function(a,b,d,e){var f;return f=null!=e||c.is(a,"object")?a:A.doc.getElementById(a),null!=f?f.tagName?null==b?{container:f,width:f.style.pixelWidth||f.offsetWidth,height:f.style.pixelHeight||f.offsetHeight}:{container:f,width:b,height:d}:{container:1,x:a,y:b,width:d,height:e}:void 0},c.pathToRelative=Db,c._engine={},c.path2curve=Kb,c.matrix=function(a,b,c,d,e,f){return new o(a,b,c,d,e,f)},function(a){function b(a){return a[0]*a[0]+a[1]*a[1]}function d(a){var c=N.sqrt(b(a));a[0]&&(a[0]/=c),a[1]&&(a[1]/=c)}a.add=function(a,b,c,d,e,f){var g,h,i,j,k=[[],[],[]],l=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]],m=[[a,c,e],[b,d,f],[0,0,1]];for(a&&a instanceof o&&(m=[[a.a,a.c,a.e],[a.b,a.d,a.f],[0,0,1]]),g=0;3>g;g++)for(h=0;3>h;h++){for(j=0,i=0;3>i;i++)j+=l[g][i]*m[i][h];k[g][h]=j}this.a=k[0][0],this.b=k[1][0],this.c=k[0][1],this.d=k[1][1],this.e=k[0][2],this.f=k[1][2]},a.invert=function(){var a=this,b=a.a*a.d-a.b*a.c;return new o(a.d/b,-a.b/b,-a.c/b,a.a/b,(a.c*a.f-a.d*a.e)/b,(a.b*a.e-a.a*a.f)/b)},a.clone=function(){return new o(this.a,this.b,this.c,this.d,this.e,this.f)},a.translate=function(a,b){this.add(1,0,0,1,a,b)},a.scale=function(a,b,c,d){null==b&&(b=a),(c||d)&&this.add(1,0,0,1,c,d),this.add(a,0,0,b,0,0),(c||d)&&this.add(1,0,0,1,-c,-d)},a.rotate=function(a,b,d){a=c.rad(a),b=b||0,d=d||0;var e=+N.cos(a).toFixed(9),f=+N.sin(a).toFixed(9);this.add(e,f,-f,e,b,d),this.add(1,0,0,1,-b,-d)},a.x=function(a,b){return a*this.a+b*this.c+this.e},a.y=function(a,b){return a*this.b+b*this.d+this.f},a.get=function(a){return+this[I.fromCharCode(97+a)].toFixed(4)},a.toString=function(){return c.svg?"matrix("+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+")":[this.get(0),this.get(2),this.get(1),this.get(3),0,0].join()},a.toFilter=function(){return"progid:DXImageTransform.Microsoft.Matrix(M11="+this.get(0)+", M12="+this.get(2)+", M21="+this.get(1)+", M22="+this.get(3)+", Dx="+this.get(4)+", Dy="+this.get(5)+", sizingmethod='auto expand')"},a.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]},a.split=function(){var a={};a.dx=this.e,a.dy=this.f;var e=[[this.a,this.c],[this.b,this.d]];a.scalex=N.sqrt(b(e[0])),d(e[0]),a.shear=e[0][0]*e[1][0]+e[0][1]*e[1][1],e[1]=[e[1][0]-e[0][0]*a.shear,e[1][1]-e[0][1]*a.shear],a.scaley=N.sqrt(b(e[1])),d(e[1]),a.shear/=a.scaley;var f=-e[0][1],g=e[1][1];return 0>g?(a.rotate=c.deg(N.acos(g)),0>f&&(a.rotate=360-a.rotate)):a.rotate=c.deg(N.asin(f)),a.isSimple=!(+a.shear.toFixed(9)||a.scalex.toFixed(9)!=a.scaley.toFixed(9)&&a.rotate),a.isSuperSimple=!+a.shear.toFixed(9)&&a.scalex.toFixed(9)==a.scaley.toFixed(9)&&!a.rotate,a.noRotation=!+a.shear.toFixed(9)&&!a.rotate,a},a.toTransformString=function(a){var b=a||this[J]();return b.isSimple?(b.scalex=+b.scalex.toFixed(4),b.scaley=+b.scaley.toFixed(4),b.rotate=+b.rotate.toFixed(4),(b.dx||b.dy?"t"+[b.dx,b.dy]:G)+(1!=b.scalex||1!=b.scaley?"s"+[b.scalex,b.scaley,0,0]:G)+(b.rotate?"r"+[b.rotate,0,0]:G)):"m"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)]}}(o.prototype);var Qb=navigator.userAgent.match(/Version\/(.*?)\s/)||navigator.userAgent.match(/Chrome\/(\d+)/);v.safari="Apple Computer, Inc."==navigator.vendor&&(Qb&&Qb[1]<4||"iP"==navigator.platform.slice(0,2))||"Google Inc."==navigator.vendor&&Qb&&Qb[1]<8?function(){var a=this.rect(-99,-99,this.width+99,this.height+99).attr({stroke:"none"});setTimeout(function(){a.remove()})}:mb;for(var Rb=function(){this.returnValue=!1},Sb=function(){return this.originalEvent.preventDefault()},Tb=function(){this.cancelBubble=!0},Ub=function(){return this.originalEvent.stopPropagation()},Vb=function(a){var b=A.doc.documentElement.scrollTop||A.doc.body.scrollTop,c=A.doc.documentElement.scrollLeft||A.doc.body.scrollLeft;return{x:a.clientX+c,y:a.clientY+b}},Wb=function(){return A.doc.addEventListener?function(a,b,c,d){var e=function(a){var b=Vb(a);return c.call(d,a,b.x,b.y)};if(a.addEventListener(b,e,!1),F&&L[b]){var f=function(b){for(var e=Vb(b),f=b,g=0,h=b.targetTouches&&b.targetTouches.length;h>g;g++)if(b.targetTouches[g].target==a){b=b.targetTouches[g],b.originalEvent=f,b.preventDefault=Sb,b.stopPropagation=Ub;break}return c.call(d,b,e.x,e.y)};a.addEventListener(L[b],f,!1)}return function(){return a.removeEventListener(b,e,!1),F&&L[b]&&a.removeEventListener(L[b],f,!1),!0}}:A.doc.attachEvent?function(a,b,c,d){var e=function(a){a=a||A.win.event;var b=A.doc.documentElement.scrollTop||A.doc.body.scrollTop,e=A.doc.documentElement.scrollLeft||A.doc.body.scrollLeft,f=a.clientX+e,g=a.clientY+b;return a.preventDefault=a.preventDefault||Rb,a.stopPropagation=a.stopPropagation||Tb,c.call(d,a,f,g)};a.attachEvent("on"+b,e);var f=function(){return a.detachEvent("on"+b,e),!0};return f}:void 0}(),Xb=[],Yb=function(a){for(var c,d=a.clientX,e=a.clientY,f=A.doc.documentElement.scrollTop||A.doc.body.scrollTop,g=A.doc.documentElement.scrollLeft||A.doc.body.scrollLeft,h=Xb.length;h--;){if(c=Xb[h],F&&a.touches){for(var i,j=a.touches.length;j--;)if(i=a.touches[j],i.identifier==c.el._drag.id){d=i.clientX,e=i.clientY,(a.originalEvent?a.originalEvent:a).preventDefault();break}}else a.preventDefault();var k,l=c.el.node,m=l.nextSibling,n=l.parentNode,o=l.style.display;A.win.opera&&n.removeChild(l),l.style.display="none",k=c.el.paper.getElementByPoint(d,e),l.style.display=o,A.win.opera&&(m?n.insertBefore(l,m):n.appendChild(l)),k&&b("raphael.drag.over."+c.el.id,c.el,k),d+=g,e+=f,b("raphael.drag.move."+c.el.id,c.move_scope||c.el,d-c.el._drag.x,e-c.el._drag.y,d,e,a)}},Zb=function(a){c.unmousemove(Yb).unmouseup(Zb);for(var d,e=Xb.length;e--;)d=Xb[e],d.el._drag={},b("raphael.drag.end."+d.el.id,d.end_scope||d.start_scope||d.move_scope||d.el,a);Xb=[]},$b=c.el={},_b=K.length;_b--;)!function(a){c[a]=$b[a]=function(b,d){return c.is(b,"function")&&(this.events=this.events||[],this.events.push({name:a,f:b,unbind:Wb(this.shape||this.node||A.doc,a,b,d||this)})),this},c["un"+a]=$b["un"+a]=function(b){for(var d=this.events||[],e=d.length;e--;)d[e].name!=a||!c.is(b,"undefined")&&d[e].f!=b||(d[e].unbind(),d.splice(e,1),!d.length&&delete this.events);return this}}(K[_b]);$b.data=function(a,d){var e=kb[this.id]=kb[this.id]||{};if(0==arguments.length)return e;if(1==arguments.length){if(c.is(a,"object")){for(var f in a)a[z](f)&&this.data(f,a[f]);return this}return b("raphael.data.get."+this.id,this,e[a],a),e[a]}return e[a]=d,b("raphael.data.set."+this.id,this,d,a),this},$b.removeData=function(a){return null==a?kb[this.id]={}:kb[this.id]&&delete kb[this.id][a],this},$b.getData=function(){return d(kb[this.id]||{})},$b.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)},$b.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var ac=[];$b.drag=function(a,d,e,f,g,h){function i(i){(i.originalEvent||i).preventDefault();var j=i.clientX,k=i.clientY,l=A.doc.documentElement.scrollTop||A.doc.body.scrollTop,m=A.doc.documentElement.scrollLeft||A.doc.body.scrollLeft;if(this._drag.id=i.identifier,F&&i.touches)for(var n,o=i.touches.length;o--;)if(n=i.touches[o],this._drag.id=n.identifier,n.identifier==this._drag.id){j=n.clientX,k=n.clientY;break}this._drag.x=j+m,this._drag.y=k+l,!Xb.length&&c.mousemove(Yb).mouseup(Zb),Xb.push({el:this,move_scope:f,start_scope:g,end_scope:h}),d&&b.on("raphael.drag.start."+this.id,d),a&&b.on("raphael.drag.move."+this.id,a),e&&b.on("raphael.drag.end."+this.id,e),b("raphael.drag.start."+this.id,g||f||this,i.clientX+m,i.clientY+l,i)}return this._drag={},ac.push({el:this,start:i}),this.mousedown(i),this},$b.onDragOver=function(a){a?b.on("raphael.drag.over."+this.id,a):b.unbind("raphael.drag.over."+this.id)},$b.undrag=function(){for(var a=ac.length;a--;)ac[a].el==this&&(this.unmousedown(ac[a].start),ac.splice(a,1),b.unbind("raphael.drag.*."+this.id));!ac.length&&c.unmousemove(Yb).unmouseup(Zb),Xb=[]},v.circle=function(a,b,d){var e=c._engine.circle(this,a||0,b||0,d||0);return this.__set__&&this.__set__.push(e),e},v.rect=function(a,b,d,e,f){var g=c._engine.rect(this,a||0,b||0,d||0,e||0,f||0);return this.__set__&&this.__set__.push(g),g},v.ellipse=function(a,b,d,e){var f=c._engine.ellipse(this,a||0,b||0,d||0,e||0);return this.__set__&&this.__set__.push(f),f},v.path=function(a){a&&!c.is(a,U)&&!c.is(a[0],V)&&(a+=G);var b=c._engine.path(c.format[D](c,arguments),this);return this.__set__&&this.__set__.push(b),b},v.image=function(a,b,d,e,f){var g=c._engine.image(this,a||"about:blank",b||0,d||0,e||0,f||0);return this.__set__&&this.__set__.push(g),g},v.text=function(a,b,d){var e=c._engine.text(this,a||0,b||0,I(d));return this.__set__&&this.__set__.push(e),e},v.set=function(a){!c.is(a,"array")&&(a=Array.prototype.splice.call(arguments,0,arguments.length));var b=new mc(a);return this.__set__&&this.__set__.push(b),b.paper=this,b.type="set",b},v.setStart=function(a){this.__set__=a||this.set()},v.setFinish=function(){var a=this.__set__;return delete this.__set__,a},v.getSize=function(){var a=this.canvas.parentNode;return{width:a.offsetWidth,height:a.offsetHeight}},v.setSize=function(a,b){return c._engine.setSize.call(this,a,b)},v.setViewBox=function(a,b,d,e,f){return c._engine.setViewBox.call(this,a,b,d,e,f)},v.top=v.bottom=null,v.raphael=c;var bc=function(a){var b=a.getBoundingClientRect(),c=a.ownerDocument,d=c.body,e=c.documentElement,f=e.clientTop||d.clientTop||0,g=e.clientLeft||d.clientLeft||0,h=b.top+(A.win.pageYOffset||e.scrollTop||d.scrollTop)-f,i=b.left+(A.win.pageXOffset||e.scrollLeft||d.scrollLeft)-g;return{y:h,x:i}};v.getElementByPoint=function(a,b){var c=this,d=c.canvas,e=A.doc.elementFromPoint(a,b);if(A.win.opera&&"svg"==e.tagName){var f=bc(d),g=d.createSVGRect();g.x=a-f.x,g.y=b-f.y,g.width=g.height=1;var h=d.getIntersectionList(g,null);h.length&&(e=h[h.length-1])}if(!e)return null;for(;e.parentNode&&e!=d.parentNode&&!e.raphael;)e=e.parentNode;return e==c.canvas.parentNode&&(e=d),e=e&&e.raphael?c.getById(e.raphaelid):null},v.getElementsByBBox=function(a){var b=this.set();return this.forEach(function(d){c.isBBoxIntersect(d.getBBox(),a)&&b.push(d)}),b},v.getById=function(a){for(var b=this.bottom;b;){if(b.id==a)return b;b=b.next}return null},v.forEach=function(a,b){for(var c=this.bottom;c;){if(a.call(b,c)===!1)return this;c=c.next}return this},v.getElementsByPoint=function(a,b){var c=this.set();return this.forEach(function(d){d.isPointInside(a,b)&&c.push(d)}),c},$b.isPointInside=function(a,b){var d=this.realPath=qb[this.type](this);return this.attr("transform")&&this.attr("transform").length&&(d=c.transformPath(d,this.attr("transform"))),c.isPointInsidePath(d,a,b)},$b.getBBox=function(a){if(this.removed)return{};var b=this._;return a?((b.dirty||!b.bboxwt)&&(this.realPath=qb[this.type](this),b.bboxwt=Bb(this.realPath),b.bboxwt.toString=p,b.dirty=0),b.bboxwt):((b.dirty||b.dirtyT||!b.bbox)&&((b.dirty||!this.realPath)&&(b.bboxwt=0,this.realPath=qb[this.type](this)),b.bbox=Bb(rb(this.realPath,this.matrix)),b.bbox.toString=p,b.dirty=b.dirtyT=0),b.bbox)},$b.clone=function(){if(this.removed)return null;var a=this.paper[this.type]().attr(this.attr());return this.__set__&&this.__set__.push(a),a},$b.glow=function(a){if("text"==this.type)return null;a=a||{};var b={width:(a.width||10)+(+this.attr("stroke-width")||1),fill:a.fill||!1,opacity:a.opacity||.5,offsetx:a.offsetx||0,offsety:a.offsety||0,color:a.color||"#000"},c=b.width/2,d=this.paper,e=d.set(),f=this.realPath||qb[this.type](this);f=this.matrix?rb(f,this.matrix):f;for(var g=1;c+1>g;g++)e.push(d.path(f).attr({stroke:b.color,fill:b.fill?b.color:"none","stroke-linejoin":"round","stroke-linecap":"round","stroke-width":+(b.width/c*g).toFixed(3),opacity:+(b.opacity/c).toFixed(3)}));return e.insertBefore(this).translate(b.offsetx,b.offsety)};var cc=function(a,b,d,e,f,g,h,i,l){return null==l?j(a,b,d,e,f,g,h,i):c.findDotsAtSegment(a,b,d,e,f,g,h,i,k(a,b,d,e,f,g,h,i,l))},dc=function(a,b){return function(d,e,f){d=Kb(d);for(var g,h,i,j,k,l="",m={},n=0,o=0,p=d.length;p>o;o++){if(i=d[o],"M"==i[0])g=+i[1],h=+i[2];else{if(j=cc(g,h,i[1],i[2],i[3],i[4],i[5],i[6]),n+j>e){if(b&&!m.start){if(k=cc(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),l+=["C"+k.start.x,k.start.y,k.m.x,k.m.y,k.x,k.y],f)return l;m.start=l,l=["M"+k.x,k.y+"C"+k.n.x,k.n.y,k.end.x,k.end.y,i[5],i[6]].join(),n+=j,g=+i[5],h=+i[6];continue}if(!a&&!b)return k=cc(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),{x:k.x,y:k.y,alpha:k.alpha}}n+=j,g=+i[5],h=+i[6]}l+=i.shift()+i}return m.end=l,k=a?n:b?m:c.findDotsAtSegment(g,h,i[0],i[1],i[2],i[3],i[4],i[5],1),k.alpha&&(k={x:k.x,y:k.y,alpha:k.alpha}),k}},ec=dc(1),fc=dc(),gc=dc(0,1);c.getTotalLength=ec,c.getPointAtLength=fc,c.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return gc(a,b).end;var d=gc(a,c,1);return b?gc(d,b).end:d},$b.getTotalLength=function(){var a=this.getPath();if(a)return this.node.getTotalLength?this.node.getTotalLength():ec(a)},$b.getPointAtLength=function(a){var b=this.getPath();if(b)return fc(b,a)},$b.getPath=function(){var a,b=c._getPath[this.type];if("text"!=this.type&&"set"!=this.type)return b&&(a=b(this)),a},$b.getSubpath=function(a,b){var d=this.getPath();if(d)return c.getSubpath(d,a,b)};var hc=c.easing_formulas={linear:function(a){return a},"<":function(a){return R(a,1.7)},">":function(a){return R(a,.48)},"<>":function(a){var b=.48-a/1.04,c=N.sqrt(.1734+b*b),d=c-b,e=R(Q(d),1/3)*(0>d?-1:1),f=-c-b,g=R(Q(f),1/3)*(0>f?-1:1),h=e+g+.5;return 3*(1-h)*h*h+h*h*h},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a-=1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){return a==!!a?a:R(2,-10*a)*N.sin(2*(a-.075)*S/.3)+1},bounce:function(a){var b,c=7.5625,d=2.75;return 1/d>a?b=c*a*a:2/d>a?(a-=1.5/d,b=c*a*a+.75):2.5/d>a?(a-=2.25/d,b=c*a*a+.9375):(a-=2.625/d,b=c*a*a+.984375),b}};hc.easeIn=hc["ease-in"]=hc["<"],hc.easeOut=hc["ease-out"]=hc[">"],hc.easeInOut=hc["ease-in-out"]=hc["<>"],hc["back-in"]=hc.backIn,hc["back-out"]=hc.backOut;var ic=[],jc=a.requestAnimationFrame||a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame||a.msRequestAnimationFrame||function(a){setTimeout(a,16)},kc=function(){for(var a=+new Date,d=0;d<ic.length;d++){var e=ic[d];if(!e.el.removed&&!e.paused){var f,g,h=a-e.start,i=e.ms,j=e.easing,k=e.from,l=e.diff,m=e.to,n=(e.t,e.el),o={},p={};if(e.initstatus?(h=(e.initstatus*e.anim.top-e.prev)/(e.percent-e.prev)*i,e.status=e.initstatus,delete e.initstatus,e.stop&&ic.splice(d--,1)):e.status=(e.prev+(e.percent-e.prev)*(h/i))/e.anim.top,!(0>h))if(i>h){var q=j(h/i);for(var r in k)if(k[z](r)){switch(db[r]){case T:f=+k[r]+q*i*l[r];break;case"colour":f="rgb("+[lc($(k[r].r+q*i*l[r].r)),lc($(k[r].g+q*i*l[r].g)),lc($(k[r].b+q*i*l[r].b))].join(",")+")";break;case"path":f=[];for(var t=0,u=k[r].length;u>t;t++){f[t]=[k[r][t][0]];for(var v=1,w=k[r][t].length;w>v;v++)f[t][v]=+k[r][t][v]+q*i*l[r][t][v];f[t]=f[t].join(H)}f=f.join(H);break;case"transform":if(l[r].real)for(f=[],t=0,u=k[r].length;u>t;t++)for(f[t]=[k[r][t][0]],v=1,w=k[r][t].length;w>v;v++)f[t][v]=k[r][t][v]+q*i*l[r][t][v];else{var x=function(a){return+k[r][a]+q*i*l[r][a]};f=[["m",x(0),x(1),x(2),x(3),x(4),x(5)]]}break;case"csv":if("clip-rect"==r)for(f=[],t=4;t--;)f[t]=+k[r][t]+q*i*l[r][t];break;default:var y=[][E](k[r]);for(f=[],t=n.paper.customAttributes[r].length;t--;)f[t]=+y[t]+q*i*l[r][t]}o[r]=f}n.attr(o),function(a,c,d){setTimeout(function(){b("raphael.anim.frame."+a,c,d)})}(n.id,n,e.anim)}else{if(function(a,d,e){setTimeout(function(){b("raphael.anim.frame."+d.id,d,e),b("raphael.anim.finish."+d.id,d,e),c.is(a,"function")&&a.call(d)})}(e.callback,n,e.anim),n.attr(m),ic.splice(d--,1),e.repeat>1&&!e.next){for(g in m)m[z](g)&&(p[g]=e.totalOrigin[g]);e.el.attr(p),s(e.anim,e.el,e.anim.percents[0],null,e.totalOrigin,e.repeat-1)}e.next&&!e.stop&&s(e.anim,e.el,e.next,null,e.totalOrigin,e.repeat)}}}c.svg&&n&&n.paper&&n.paper.safari(),ic.length&&jc(kc)},lc=function(a){return a>255?255:0>a?0:a};$b.animateWith=function(a,b,d,e,f,g){var h=this;if(h.removed)return g&&g.call(h),h;var i=d instanceof r?d:c.animation(d,e,f,g);s(i,h,i.percents[0],null,h.attr());for(var j=0,k=ic.length;k>j;j++)if(ic[j].anim==b&&ic[j].el==a){ic[k-1].start=ic[j].start;break}return h},$b.onAnimation=function(a){return a?b.on("raphael.anim.frame."+this.id,a):b.unbind("raphael.anim.frame."+this.id),this},r.prototype.delay=function(a){var b=new r(this.anim,this.ms);return b.times=this.times,b.del=+a||0,b},r.prototype.repeat=function(a){var b=new r(this.anim,this.ms);return b.del=this.del,b.times=N.floor(O(a,0))||1,b},c.animation=function(a,b,d,e){if(a instanceof r)return a;(c.is(d,"function")||!d)&&(e=e||d||null,d=null),a=Object(a),b=+b||0;var f,g,h={};for(g in a)a[z](g)&&_(g)!=g&&_(g)+"%"!=g&&(f=!0,h[g]=a[g]);if(f)return d&&(h.easing=d),e&&(h.callback=e),new r({100:h},b);if(e){var i=0;for(var j in a){var k=ab(j);a[z](j)&&k>i&&(i=k)}i+="%",!a[i].callback&&(a[i].callback=e)}return new r(a,b)},$b.animate=function(a,b,d,e){var f=this;if(f.removed)return e&&e.call(f),f;var g=a instanceof r?a:c.animation(a,b,d,e);return s(g,f,g.percents[0],null,f.attr()),f},$b.setTime=function(a,b){return a&&null!=b&&this.status(a,P(b,a.ms)/a.ms),this},$b.status=function(a,b){var c,d,e=[],f=0;if(null!=b)return s(a,this,-1,P(b,1)),this;for(c=ic.length;c>f;f++)if(d=ic[f],d.el.id==this.id&&(!a||d.anim==a)){if(a)return d.status;e.push({anim:d.anim,status:d.status})}return a?0:e},$b.pause=function(a){for(var c=0;c<ic.length;c++)ic[c].el.id!=this.id||a&&ic[c].anim!=a||b("raphael.anim.pause."+this.id,this,ic[c].anim)!==!1&&(ic[c].paused=!0);return this},$b.resume=function(a){for(var c=0;c<ic.length;c++)if(ic[c].el.id==this.id&&(!a||ic[c].anim==a)){var d=ic[c];b("raphael.anim.resume."+this.id,this,d.anim)!==!1&&(delete d.paused,this.status(d.anim,d.status))}return this},$b.stop=function(a){for(var c=0;c<ic.length;c++)ic[c].el.id!=this.id||a&&ic[c].anim!=a||b("raphael.anim.stop."+this.id,this,ic[c].anim)!==!1&&ic.splice(c--,1);return this},b.on("raphael.remove",t),b.on("raphael.clear",t),$b.toString=function(){return"RaphaÃ«lâs object"};var mc=function(a){if(this.items=[],this.length=0,this.type="set",a)for(var b=0,c=a.length;c>b;b++)!a[b]||a[b].constructor!=$b.constructor&&a[b].constructor!=mc||(this[this.items.length]=this.items[this.items.length]=a[b],this.length++)},nc=mc.prototype;nc.push=function(){for(var a,b,c=0,d=arguments.length;d>c;c++)a=arguments[c],!a||a.constructor!=$b.constructor&&a.constructor!=mc||(b=this.items.length,this[b]=this.items[b]=a,this.length++);return this},nc.pop=function(){return this.length&&delete this[this.length--],this.items.pop()},nc.forEach=function(a,b){for(var c=0,d=this.items.length;d>c;c++)if(a.call(b,this.items[c],c)===!1)return this;return this};for(var oc in $b)$b[z](oc)&&(nc[oc]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a][D](c,b)})}}(oc));return nc.attr=function(a,b){if(a&&c.is(a,V)&&c.is(a[0],"object"))for(var d=0,e=a.length;e>d;d++)this.items[d].attr(a[d]);else for(var f=0,g=this.items.length;g>f;f++)this.items[f].attr(a,b);return this},nc.clear=function(){for(;this.length;)this.pop()},nc.splice=function(a,b){a=0>a?O(this.length+a,0):a,b=O(0,P(this.length-a,b));var c,d=[],e=[],f=[];for(c=2;c<arguments.length;c++)f.push(arguments[c]);for(c=0;b>c;c++)e.push(this[a+c]);for(;c<this.length-a;c++)d.push(this[a+c]);var g=f.length;for(c=0;c<g+d.length;c++)this.items[a+c]=this[a+c]=g>c?f[c]:d[c-g];for(c=this.items.length=this.length-=b-g;this[c];)delete this[c++];return new mc(e)},nc.exclude=function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]==a)return this.splice(b,1),!0},nc.animate=function(a,b,d,e){(c.is(d,"function")||!d)&&(e=d||null);var f,g,h=this.items.length,i=h,j=this;if(!h)return this;e&&(g=function(){!--h&&e.call(j)}),d=c.is(d,U)?d:g;var k=c.animation(a,b,d,g);for(f=this.items[--i].animate(k);i--;)this.items[i]&&!this.items[i].removed&&this.items[i].animateWith(f,k,k),this.items[i]&&!this.items[i].removed||h--;return this},nc.insertAfter=function(a){for(var b=this.items.length;b--;)this.items[b].insertAfter(a);return this},nc.getBBox=function(){for(var a=[],b=[],c=[],d=[],e=this.items.length;e--;)if(!this.items[e].removed){var f=this.items[e].getBBox();a.push(f.x),b.push(f.y),c.push(f.x+f.width),d.push(f.y+f.height)}return a=P[D](0,a),b=P[D](0,b),c=O[D](0,c),d=O[D](0,d),{x:a,y:b,x2:c,y2:d,width:c-a,height:d-b}},nc.clone=function(a){a=this.paper.set();for(var b=0,c=this.items.length;c>b;b++)a.push(this.items[b].clone());return a},nc.toString=function(){return"RaphaÃ«lâs set"},nc.glow=function(a){var b=this.paper.set();return this.forEach(function(c){var d=c.glow(a);null!=d&&d.forEach(function(a){b.push(a)})}),b},nc.isPointInside=function(a,b){var c=!1;return this.forEach(function(d){return d.isPointInside(a,b)?(c=!0,!1):void 0}),c},c.registerFont=function(a){if(!a.face)return a;this.fonts=this.fonts||{};var b={w:a.w,face:{},glyphs:{}},c=a.face["font-family"];for(var d in a.face)a.face[z](d)&&(b.face[d]=a.face[d]);if(this.fonts[c]?this.fonts[c].push(b):this.fonts[c]=[b],!a.svg){b.face["units-per-em"]=ab(a.face["units-per-em"],10);for(var e in a.glyphs)if(a.glyphs[z](e)){var f=a.glyphs[e];if(b.glyphs[e]={w:f.w,k:{},d:f.d&&"M"+f.d.replace(/[mlcxtrv]/g,function(a){return{l:"L",c:"C",x:"z",t:"m",r:"l",v:"c"}[a]||"M"})+"z"},f.k)for(var g in f.k)f[z](g)&&(b.glyphs[e].k[g]=f.k[g])}}return a},v.getFont=function(a,b,d,e){if(e=e||"normal",d=d||"normal",b=+b||{normal:400,bold:700,lighter:300,bolder:800}[b]||400,c.fonts){var f=c.fonts[a];if(!f){var g=new RegExp("(^|\\s)"+a.replace(/[^\w\d\s+!~.:_-]/g,G)+"(\\s|$)","i");for(var h in c.fonts)if(c.fonts[z](h)&&g.test(h)){f=c.fonts[h];break}}var i;if(f)for(var j=0,k=f.length;k>j&&(i=f[j],i.face["font-weight"]!=b||i.face["font-style"]!=d&&i.face["font-style"]||i.face["font-stretch"]!=e);j++);return i}},v.print=function(a,b,d,e,f,g,h,i){g=g||"middle",h=O(P(h||0,1),-1),i=O(P(i||1,3),1);var j,k=I(d)[J](G),l=0,m=0,n=G;if(c.is(e,"string")&&(e=this.getFont(e)),e){j=(f||16)/e.face["units-per-em"];for(var o=e.face.bbox[J](w),p=+o[0],q=o[3]-o[1],r=0,s=+o[1]+("baseline"==g?q+ +e.face.descent:q/2),t=0,u=k.length;u>t;t++){if("\n"==k[t])l=0,x=0,m=0,r+=q*i;else{var v=m&&e.glyphs[k[t-1]]||{},x=e.glyphs[k[t]];l+=m?(v.w||e.w)+(v.k&&v.k[k[t]]||0)+e.w*h:0,m=1}x&&x.d&&(n+=c.transformPath(x.d,["t",l*j,r*j,"s",j,j,p,s,"t",(a-p)/j,(b-s)/j]))}}return this.path(n).attr({fill:"#000",stroke:"none"})},v.add=function(a){if(c.is(a,"array"))for(var b,d=this.set(),e=0,f=a.length;f>e;e++)b=a[e]||{},x[z](b.type)&&d.push(this[b.type]().attr(b));return d},c.format=function(a,b){var d=c.is(b,V)?[0][E](b):arguments;return a&&c.is(a,U)&&d.length-1&&(a=a.replace(y,function(a,b){return null==d[++b]?G:d[b]})),a||G},c.fullfill=function(){var a=/\{([^\}]+)\}/g,b=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g,c=function(a,c,d){var e=d;return c.replace(b,function(a,b,c,d,f){b=b||d,e&&(b in e&&(e=e[b]),"function"==typeof e&&f&&(e=e()))}),e=(null==e||e==d?a:e)+""};return function(b,d){return String(b).replace(a,function(a,b){return c(a,b,d)})}}(),c.ninja=function(){return B.was?A.win.Raphael=B.is:delete Raphael,c},c.st=nc,b.on("raphael.DOMload",function(){u=!0}),function(a,b,d){function e(){/in/.test(a.readyState)?setTimeout(e,9):c.eve("raphael.DOMload")}null==a.readyState&&a.addEventListener&&(a.addEventListener(b,d=function(){a.removeEventListener(b,d,!1),a.readyState="complete"},!1),a.readyState="loading"),e()}(document,"DOMContentLoaded"),function(){if(c.svg){var a="hasOwnProperty",b=String,d=parseFloat,e=parseInt,f=Math,g=f.max,h=f.abs,i=f.pow,j=/[, ]+/,k=c.eve,l="",m=" ",n="http://www.w3.org/1999/xlink",o={block:"M5,0 0,2.5 5,5z",classic:"M5,0 0,2.5 5,5 3.5,3 3.5,2z",diamond:"M2.5,0 5,2.5 2.5,5 0,2.5z",open:"M6,1 1,3.5 6,6",oval:"M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"},p={};c.toString=function(){return"Your browser supports SVG.\nYou are running RaphaÃ«l "+this.version};var q=function(d,e){if(e){"string"==typeof d&&(d=q(d));for(var f in e)e[a](f)&&("xlink:"==f.substring(0,6)?d.setAttributeNS(n,f.substring(6),b(e[f])):d.setAttribute(f,b(e[f])))}else d=c._g.doc.createElementNS("http://www.w3.org/2000/svg",d),d.style&&(d.style.webkitTapHighlightColor="rgba(0,0,0,0)");return d},r=function(a,e){var j="linear",k=a.id+e,m=.5,n=.5,o=a.node,p=a.paper,r=o.style,s=c._g.doc.getElementById(k);if(!s){if(e=b(e).replace(c._radial_gradient,function(a,b,c){if(j="radial",b&&c){m=d(b),n=d(c);var e=2*(n>.5)-1;i(m-.5,2)+i(n-.5,2)>.25&&(n=f.sqrt(.25-i(m-.5,2))*e+.5)&&.5!=n&&(n=n.toFixed(5)-1e-5*e)}return l}),e=e.split(/\s*\-\s*/),"linear"==j){var t=e.shift();if(t=-d(t),isNaN(t))return null;var u=[0,0,f.cos(c.rad(t)),f.sin(c.rad(t))],v=1/(g(h(u[2]),h(u[3]))||1);u[2]*=v,u[3]*=v,u[2]<0&&(u[0]=-u[2],u[2]=0),u[3]<0&&(u[1]=-u[3],u[3]=0)}var w=c._parseDots(e);if(!w)return null;if(k=k.replace(/[\(\)\s,\xb0#]/g,"_"),a.gradient&&k!=a.gradient.id&&(p.defs.removeChild(a.gradient),delete a.gradient),!a.gradient){s=q(j+"Gradient",{id:k}),a.gradient=s,q(s,"radial"==j?{fx:m,fy:n}:{x1:u[0],y1:u[1],x2:u[2],y2:u[3],gradientTransform:a.matrix.invert()}),p.defs.appendChild(s);for(var x=0,y=w.length;y>x;x++)s.appendChild(q("stop",{offset:w[x].offset?w[x].offset:x?"100%":"0%","stop-color":w[x].color||"#fff"}))}}return q(o,{fill:"url('"+document.location+"#"+k+"')",opacity:1,"fill-opacity":1}),r.fill=l,r.opacity=1,r.fillOpacity=1,1},s=function(a){var b=a.getBBox(1);q(a.pattern,{patternTransform:a.matrix.invert()+" translate("+b.x+","+b.y+")"})},t=function(d,e,f){if("path"==d.type){for(var g,h,i,j,k,m=b(e).toLowerCase().split("-"),n=d.paper,r=f?"end":"start",s=d.node,t=d.attrs,u=t["stroke-width"],v=m.length,w="classic",x=3,y=3,z=5;v--;)switch(m[v]){case"block":case"classic":case"oval":case"diamond":case"open":case"none":w=m[v];break;case"wide":y=5;break;case"narrow":y=2;break;case"long":x=5;break;case"short":x=2}if("open"==w?(x+=2,y+=2,z+=2,i=1,j=f?4:1,k={fill:"none",stroke:t.stroke}):(j=i=x/2,k={fill:t.stroke,stroke:"none"}),d._.arrows?f?(d._.arrows.endPath&&p[d._.arrows.endPath]--,d._.arrows.endMarker&&p[d._.arrows.endMarker]--):(d._.arrows.startPath&&p[d._.arrows.startPath]--,d._.arrows.startMarker&&p[d._.arrows.startMarker]--):d._.arrows={},"none"!=w){var A="raphael-marker-"+w,B="raphael-marker-"+r+w+x+y+"-obj"+d.id;c._g.doc.getElementById(A)?p[A]++:(n.defs.appendChild(q(q("path"),{"stroke-linecap":"round",d:o[w],id:A})),p[A]=1);var C,D=c._g.doc.getElementById(B);D?(p[B]++,C=D.getElementsByTagName("use")[0]):(D=q(q("marker"),{id:B,markerHeight:y,markerWidth:x,orient:"auto",refX:j,refY:y/2}),C=q(q("use"),{"xlink:href":"#"+A,transform:(f?"rotate(180 "+x/2+" "+y/2+") ":l)+"scale("+x/z+","+y/z+")","stroke-width":(1/((x/z+y/z)/2)).toFixed(4)}),D.appendChild(C),n.defs.appendChild(D),p[B]=1),q(C,k);var E=i*("diamond"!=w&&"oval"!=w);f?(g=d._.arrows.startdx*u||0,h=c.getTotalLength(t.path)-E*u):(g=E*u,h=c.getTotalLength(t.path)-(d._.arrows.enddx*u||0)),k={},k["marker-"+r]="url(#"+B+")",(h||g)&&(k.d=c.getSubpath(t.path,g,h)),q(s,k),d._.arrows[r+"Path"]=A,d._.arrows[r+"Marker"]=B,d._.arrows[r+"dx"]=E,d._.arrows[r+"Type"]=w,d._.arrows[r+"String"]=e}else f?(g=d._.arrows.startdx*u||0,h=c.getTotalLength(t.path)-g):(g=0,h=c.getTotalLength(t.path)-(d._.arrows.enddx*u||0)),d._.arrows[r+"Path"]&&q(s,{d:c.getSubpath(t.path,g,h)}),delete d._.arrows[r+"Path"],delete d._.arrows[r+"Marker"],delete d._.arrows[r+"dx"],delete d._.arrows[r+"Type"],delete d._.arrows[r+"String"];for(k in p)if(p[a](k)&&!p[k]){var F=c._g.doc.getElementById(k);F&&F.parentNode.removeChild(F)}}},u={"":[0],none:[0],"-":[3,1],".":[1,1],"-.":[3,1,1,1],"-..":[3,1,1,1,1,1],". ":[1,3],"- ":[4,3],"--":[8,3],"- .":[4,3,1,3],"--.":[8,3,1,3],"--..":[8,3,1,3,1,3]},v=function(a,c,d){if(c=u[b(c).toLowerCase()]){for(var e=a.attrs["stroke-width"]||"1",f={round:e,square:e,butt:0}[a.attrs["stroke-linecap"]||d["stroke-linecap"]]||0,g=[],h=c.length;h--;)g[h]=c[h]*e+(h%2?1:-1)*f;q(a.node,{"stroke-dasharray":g.join(",")})}},w=function(d,f){var i=d.node,k=d.attrs,m=i.style.visibility;i.style.visibility="hidden";for(var o in f)if(f[a](o)){if(!c._availableAttrs[a](o))continue;var p=f[o];switch(k[o]=p,o){case"blur":d.blur(p);break;case"title":var u=i.getElementsByTagName("title");if(u.length&&(u=u[0]))u.firstChild.nodeValue=p;else{u=q("title");var w=c._g.doc.createTextNode(p);u.appendChild(w),i.appendChild(u)}break;case"href":case"target":var x=i.parentNode;if("a"!=x.tagName.toLowerCase()){var z=q("a");x.insertBefore(z,i),z.appendChild(i),x=z}"target"==o?x.setAttributeNS(n,"show","blank"==p?"new":p):x.setAttributeNS(n,o,p);break;case"cursor":i.style.cursor=p;break;case"transform":d.transform(p);break;case"arrow-start":t(d,p);break;case"arrow-end":t(d,p,1);break;case"clip-rect":var A=b(p).split(j);if(4==A.length){d.clip&&d.clip.parentNode.parentNode.removeChild(d.clip.parentNode);var B=q("clipPath"),C=q("rect");B.id=c.createUUID(),q(C,{x:A[0],y:A[1],width:A[2],height:A[3]}),B.appendChild(C),d.paper.defs.appendChild(B),q(i,{"clip-path":"url(#"+B.id+")"}),d.clip=C}if(!p){var D=i.getAttribute("clip-path");if(D){var E=c._g.doc.getElementById(D.replace(/(^url\(#|\)$)/g,l));E&&E.parentNode.removeChild(E),q(i,{"clip-path":l}),delete d.clip}}break;case"path":"path"==d.type&&(q(i,{d:p?k.path=c._pathToAbsolute(p):"M0,0"}),d._.dirty=1,d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1)));break;case"width":if(i.setAttribute(o,p),d._.dirty=1,!k.fx)break;o="x",p=k.x;case"x":k.fx&&(p=-k.x-(k.width||0));case"rx":if("rx"==o&&"rect"==d.type)break;case"cx":i.setAttribute(o,p),d.pattern&&s(d),d._.dirty=1;break;case"height":if(i.setAttribute(o,p),d._.dirty=1,!k.fy)break;o="y",p=k.y;case"y":k.fy&&(p=-k.y-(k.height||0));case"ry":if("ry"==o&&"rect"==d.type)break;case"cy":i.setAttribute(o,p),d.pattern&&s(d),d._.dirty=1;break;case"r":"rect"==d.type?q(i,{rx:p,ry:p}):i.setAttribute(o,p),d._.dirty=1;break;case"src":"image"==d.type&&i.setAttributeNS(n,"href",p);break;case"stroke-width":(1!=d._.sx||1!=d._.sy)&&(p/=g(h(d._.sx),h(d._.sy))||1),i.setAttribute(o,p),k["stroke-dasharray"]&&v(d,k["stroke-dasharray"],f),d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"stroke-dasharray":v(d,p,f);break;case"fill":var F=b(p).match(c._ISURL);if(F){B=q("pattern");var G=q("image");B.id=c.createUUID(),q(B,{x:0,y:0,patternUnits:"userSpaceOnUse",height:1,width:1}),q(G,{x:0,y:0,"xlink:href":F[1]}),B.appendChild(G),function(a){c._preload(F[1],function(){var b=this.offsetWidth,c=this.offsetHeight;q(a,{width:b,height:c}),q(G,{width:b,height:c}),d.paper.safari()})}(B),d.paper.defs.appendChild(B),q(i,{fill:"url(#"+B.id+")"}),d.pattern=B,d.pattern&&s(d);break}var H=c.getRGB(p);if(H.error){if(("circle"==d.type||"ellipse"==d.type||"r"!=b(p).charAt())&&r(d,p)){if("opacity"in k||"fill-opacity"in k){var I=c._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l));if(I){var J=I.getElementsByTagName("stop");q(J[J.length-1],{"stop-opacity":("opacity"in k?k.opacity:1)*("fill-opacity"in k?k["fill-opacity"]:1)})}}k.gradient=p,k.fill="none";break}}else delete f.gradient,delete k.gradient,!c.is(k.opacity,"undefined")&&c.is(f.opacity,"undefined")&&q(i,{opacity:k.opacity}),!c.is(k["fill-opacity"],"undefined")&&c.is(f["fill-opacity"],"undefined")&&q(i,{"fill-opacity":k["fill-opacity"]});H[a]("opacity")&&q(i,{"fill-opacity":H.opacity>1?H.opacity/100:H.opacity});case"stroke":H=c.getRGB(p),i.setAttribute(o,H.hex),"stroke"==o&&H[a]("opacity")&&q(i,{"stroke-opacity":H.opacity>1?H.opacity/100:H.opacity}),"stroke"==o&&d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"gradient":("circle"==d.type||"ellipse"==d.type||"r"!=b(p).charAt())&&r(d,p);break;
case"opacity":k.gradient&&!k[a]("stroke-opacity")&&q(i,{"stroke-opacity":p>1?p/100:p});case"fill-opacity":if(k.gradient){I=c._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l)),I&&(J=I.getElementsByTagName("stop"),q(J[J.length-1],{"stop-opacity":p}));break}default:"font-size"==o&&(p=e(p,10)+"px");var K=o.replace(/(\-.)/g,function(a){return a.substring(1).toUpperCase()});i.style[K]=p,d._.dirty=1,i.setAttribute(o,p)}}y(d,f),i.style.visibility=m},x=1.2,y=function(d,f){if("text"==d.type&&(f[a]("text")||f[a]("font")||f[a]("font-size")||f[a]("x")||f[a]("y"))){var g=d.attrs,h=d.node,i=h.firstChild?e(c._g.doc.defaultView.getComputedStyle(h.firstChild,l).getPropertyValue("font-size"),10):10;if(f[a]("text")){for(g.text=f.text;h.firstChild;)h.removeChild(h.firstChild);for(var j,k=b(f.text).split("\n"),m=[],n=0,o=k.length;o>n;n++)j=q("tspan"),n&&q(j,{dy:i*x,x:g.x}),j.appendChild(c._g.doc.createTextNode(k[n])),h.appendChild(j),m[n]=j}else for(m=h.getElementsByTagName("tspan"),n=0,o=m.length;o>n;n++)n?q(m[n],{dy:i*x,x:g.x}):q(m[0],{dy:0});q(h,{x:g.x,y:g.y}),d._.dirty=1;var p=d._getBBox(),r=g.y-(p.y+p.height/2);r&&c.is(r,"finite")&&q(m[0],{dy:r})}},z=function(a){return a.parentNode&&"a"===a.parentNode.tagName.toLowerCase()?a.parentNode:a},A=function(a,b){this[0]=this.node=a,a.raphael=!0,this.id=c._oid++,a.raphaelid=this.id,this.matrix=c.matrix(),this.realPath=null,this.paper=b,this.attrs=this.attrs||{},this._={transform:[],sx:1,sy:1,deg:0,dx:0,dy:0,dirty:1},!b.bottom&&(b.bottom=this),this.prev=b.top,b.top&&(b.top.next=this),b.top=this,this.next=null},B=c.el;A.prototype=B,B.constructor=A,c._engine.path=function(a,b){var c=q("path");b.canvas&&b.canvas.appendChild(c);var d=new A(c,b);return d.type="path",w(d,{fill:"none",stroke:"#000",path:a}),d},B.rotate=function(a,c,e){if(this.removed)return this;if(a=b(a).split(j),a.length-1&&(c=d(a[1]),e=d(a[2])),a=d(a[0]),null==e&&(c=e),null==c||null==e){var f=this.getBBox(1);c=f.x+f.width/2,e=f.y+f.height/2}return this.transform(this._.transform.concat([["r",a,c,e]])),this},B.scale=function(a,c,e,f){if(this.removed)return this;if(a=b(a).split(j),a.length-1&&(c=d(a[1]),e=d(a[2]),f=d(a[3])),a=d(a[0]),null==c&&(c=a),null==f&&(e=f),null==e||null==f)var g=this.getBBox(1);return e=null==e?g.x+g.width/2:e,f=null==f?g.y+g.height/2:f,this.transform(this._.transform.concat([["s",a,c,e,f]])),this},B.translate=function(a,c){return this.removed?this:(a=b(a).split(j),a.length-1&&(c=d(a[1])),a=d(a[0])||0,c=+c||0,this.transform(this._.transform.concat([["t",a,c]])),this)},B.transform=function(b){var d=this._;if(null==b)return d.transform;if(c._extractTransform(this,b),this.clip&&q(this.clip,{transform:this.matrix.invert()}),this.pattern&&s(this),this.node&&q(this.node,{transform:this.matrix}),1!=d.sx||1!=d.sy){var e=this.attrs[a]("stroke-width")?this.attrs["stroke-width"]:1;this.attr({"stroke-width":e})}return this},B.hide=function(){return!this.removed&&this.paper.safari(this.node.style.display="none"),this},B.show=function(){return!this.removed&&this.paper.safari(this.node.style.display=""),this},B.remove=function(){var a=z(this.node);if(!this.removed&&a.parentNode){var b=this.paper;b.__set__&&b.__set__.exclude(this),k.unbind("raphael.*.*."+this.id),this.gradient&&b.defs.removeChild(this.gradient),c._tear(this,b),a.parentNode.removeChild(a),this.removeData();for(var d in this)this[d]="function"==typeof this[d]?c._removedFactory(d):null;this.removed=!0}},B._getBBox=function(){if("none"==this.node.style.display){this.show();var a=!0}var b,c=!1;this.paper.canvas.parentElement?b=this.paper.canvas.parentElement.style:this.paper.canvas.parentNode&&(b=this.paper.canvas.parentNode.style),b&&"none"==b.display&&(c=!0,b.display="");var d={};try{d=this.node.getBBox()}catch(e){d={x:this.node.clientLeft,y:this.node.clientTop,width:this.node.clientWidth,height:this.node.clientHeight}}finally{d=d||{},c&&(b.display="none")}return a&&this.hide(),d},B.attr=function(b,d){if(this.removed)return this;if(null==b){var e={};for(var f in this.attrs)this.attrs[a](f)&&(e[f]=this.attrs[f]);return e.gradient&&"none"==e.fill&&(e.fill=e.gradient)&&delete e.gradient,e.transform=this._.transform,e}if(null==d&&c.is(b,"string")){if("fill"==b&&"none"==this.attrs.fill&&this.attrs.gradient)return this.attrs.gradient;if("transform"==b)return this._.transform;for(var g=b.split(j),h={},i=0,l=g.length;l>i;i++)b=g[i],h[b]=b in this.attrs?this.attrs[b]:c.is(this.paper.customAttributes[b],"function")?this.paper.customAttributes[b].def:c._availableAttrs[b];return l-1?h:h[g[0]]}if(null==d&&c.is(b,"array")){for(h={},i=0,l=b.length;l>i;i++)h[b[i]]=this.attr(b[i]);return h}if(null!=d){var m={};m[b]=d}else null!=b&&c.is(b,"object")&&(m=b);for(var n in m)k("raphael.attr."+n+"."+this.id,this,m[n]);for(n in this.paper.customAttributes)if(this.paper.customAttributes[a](n)&&m[a](n)&&c.is(this.paper.customAttributes[n],"function")){var o=this.paper.customAttributes[n].apply(this,[].concat(m[n]));this.attrs[n]=m[n];for(var p in o)o[a](p)&&(m[p]=o[p])}return w(this,m),this},B.toFront=function(){if(this.removed)return this;var a=z(this.node);a.parentNode.appendChild(a);var b=this.paper;return b.top!=this&&c._tofront(this,b),this},B.toBack=function(){if(this.removed)return this;var a=z(this.node),b=a.parentNode;b.insertBefore(a,b.firstChild),c._toback(this,this.paper);this.paper;return this},B.insertAfter=function(a){if(this.removed||!a)return this;var b=z(this.node),d=z(a.node||a[a.length-1].node);return d.nextSibling?d.parentNode.insertBefore(b,d.nextSibling):d.parentNode.appendChild(b),c._insertafter(this,a,this.paper),this},B.insertBefore=function(a){if(this.removed||!a)return this;var b=z(this.node),d=z(a.node||a[0].node);return d.parentNode.insertBefore(b,d),c._insertbefore(this,a,this.paper),this},B.blur=function(a){var b=this;if(0!==+a){var d=q("filter"),e=q("feGaussianBlur");b.attrs.blur=a,d.id=c.createUUID(),q(e,{stdDeviation:+a||1.5}),d.appendChild(e),b.paper.defs.appendChild(d),b._blur=d,q(b.node,{filter:"url(#"+d.id+")"})}else b._blur&&(b._blur.parentNode.removeChild(b._blur),delete b._blur,delete b.attrs.blur),b.node.removeAttribute("filter");return b},c._engine.circle=function(a,b,c,d){var e=q("circle");a.canvas&&a.canvas.appendChild(e);var f=new A(e,a);return f.attrs={cx:b,cy:c,r:d,fill:"none",stroke:"#000"},f.type="circle",q(e,f.attrs),f},c._engine.rect=function(a,b,c,d,e,f){var g=q("rect");a.canvas&&a.canvas.appendChild(g);var h=new A(g,a);return h.attrs={x:b,y:c,width:d,height:e,rx:f||0,ry:f||0,fill:"none",stroke:"#000"},h.type="rect",q(g,h.attrs),h},c._engine.ellipse=function(a,b,c,d,e){var f=q("ellipse");a.canvas&&a.canvas.appendChild(f);var g=new A(f,a);return g.attrs={cx:b,cy:c,rx:d,ry:e,fill:"none",stroke:"#000"},g.type="ellipse",q(f,g.attrs),g},c._engine.image=function(a,b,c,d,e,f){var g=q("image");q(g,{x:c,y:d,width:e,height:f,preserveAspectRatio:"none"}),g.setAttributeNS(n,"href",b),a.canvas&&a.canvas.appendChild(g);var h=new A(g,a);return h.attrs={x:c,y:d,width:e,height:f,src:b},h.type="image",h},c._engine.text=function(a,b,d,e){var f=q("text");a.canvas&&a.canvas.appendChild(f);var g=new A(f,a);return g.attrs={x:b,y:d,"text-anchor":"middle",text:e,"font-family":c._availableAttrs["font-family"],"font-size":c._availableAttrs["font-size"],stroke:"none",fill:"#000"},g.type="text",w(g,g.attrs),g},c._engine.setSize=function(a,b){return this.width=a||this.width,this.height=b||this.height,this.canvas.setAttribute("width",this.width),this.canvas.setAttribute("height",this.height),this._viewBox&&this.setViewBox.apply(this,this._viewBox),this},c._engine.create=function(){var a=c._getContainer.apply(0,arguments),b=a&&a.container,d=a.x,e=a.y,f=a.width,g=a.height;if(!b)throw new Error("SVG container not found.");var h,i=q("svg"),j="overflow:hidden;";return d=d||0,e=e||0,f=f||512,g=g||342,q(i,{height:g,version:1.1,width:f,xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"}),1==b?(i.style.cssText=j+"position:absolute;left:"+d+"px;top:"+e+"px",c._g.doc.body.appendChild(i),h=1):(i.style.cssText=j+"position:relative",b.firstChild?b.insertBefore(i,b.firstChild):b.appendChild(i)),b=new c._Paper,b.width=f,b.height=g,b.canvas=i,b.clear(),b._left=b._top=0,h&&(b.renderfix=function(){}),b.renderfix(),b},c._engine.setViewBox=function(a,b,c,d,e){k("raphael.setViewBox",this,this._viewBox,[a,b,c,d,e]);var f,h,i=this.getSize(),j=g(c/i.width,d/i.height),l=this.top,n=e?"xMidYMid meet":"xMinYMin";for(null==a?(this._vbSize&&(j=1),delete this._vbSize,f="0 0 "+this.width+m+this.height):(this._vbSize=j,f=a+m+b+m+c+m+d),q(this.canvas,{viewBox:f,preserveAspectRatio:n});j&&l;)h="stroke-width"in l.attrs?l.attrs["stroke-width"]:1,l.attr({"stroke-width":h}),l._.dirty=1,l._.dirtyT=1,l=l.prev;return this._viewBox=[a,b,c,d,!!e],this},c.prototype.renderfix=function(){var a,b=this.canvas,c=b.style;try{a=b.getScreenCTM()||b.createSVGMatrix()}catch(d){a=b.createSVGMatrix()}var e=-a.e%1,f=-a.f%1;(e||f)&&(e&&(this._left=(this._left+e)%1,c.left=this._left+"px"),f&&(this._top=(this._top+f)%1,c.top=this._top+"px"))},c.prototype.clear=function(){c.eve("raphael.clear",this);for(var a=this.canvas;a.firstChild;)a.removeChild(a.firstChild);this.bottom=this.top=null,(this.desc=q("desc")).appendChild(c._g.doc.createTextNode("Created with RaphaÃ«l "+c.version)),a.appendChild(this.desc),a.appendChild(this.defs=q("defs"))},c.prototype.remove=function(){k("raphael.remove",this),this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]="function"==typeof this[a]?c._removedFactory(a):null};var C=c.st;for(var D in B)B[a](D)&&!C[a](D)&&(C[D]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}(D))}}(),function(){if(c.vml){var a="hasOwnProperty",b=String,d=parseFloat,e=Math,f=e.round,g=e.max,h=e.min,i=e.abs,j="fill",k=/[, ]+/,l=c.eve,m=" progid:DXImageTransform.Microsoft",n=" ",o="",p={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},q=/([clmz]),?([^clmz]*)/gi,r=/ progid:\S+Blur\([^\)]+\)/g,s=/-?[^,\s-]+/g,t="position:absolute;left:0;top:0;width:1px;height:1px;behavior:url(#default#VML)",u=21600,v={path:1,rect:1,image:1},w={circle:1,ellipse:1},x=function(a){var d=/[ahqstv]/gi,e=c._pathToAbsolute;if(b(a).match(d)&&(e=c._path2curve),d=/[clmz]/g,e==c._pathToAbsolute&&!b(a).match(d)){var g=b(a).replace(q,function(a,b,c){var d=[],e="m"==b.toLowerCase(),g=p[b];return c.replace(s,function(a){e&&2==d.length&&(g+=d+p["m"==b?"l":"L"],d=[]),d.push(f(a*u))}),g+d});return g}var h,i,j=e(a);g=[];for(var k=0,l=j.length;l>k;k++){h=j[k],i=j[k][0].toLowerCase(),"z"==i&&(i="x");for(var m=1,r=h.length;r>m;m++)i+=f(h[m]*u)+(m!=r-1?",":o);g.push(i)}return g.join(n)},y=function(a,b,d){var e=c.matrix();return e.rotate(-a,.5,.5),{dx:e.x(b,d),dy:e.y(b,d)}},z=function(a,b,c,d,e,f){var g=a._,h=a.matrix,k=g.fillpos,l=a.node,m=l.style,o=1,p="",q=u/b,r=u/c;if(m.visibility="hidden",b&&c){if(l.coordsize=i(q)+n+i(r),m.rotation=f*(0>b*c?-1:1),f){var s=y(f,d,e);d=s.dx,e=s.dy}if(0>b&&(p+="x"),0>c&&(p+=" y")&&(o=-1),m.flip=p,l.coordorigin=d*-q+n+e*-r,k||g.fillsize){var t=l.getElementsByTagName(j);t=t&&t[0],l.removeChild(t),k&&(s=y(f,h.x(k[0],k[1]),h.y(k[0],k[1])),t.position=s.dx*o+n+s.dy*o),g.fillsize&&(t.size=g.fillsize[0]*i(b)+n+g.fillsize[1]*i(c)),l.appendChild(t)}m.visibility="visible"}};c.toString=function(){return"Your browser doesnât support SVG. Falling down to VML.\nYou are running RaphaÃ«l "+this.version};var A=function(a,c,d){for(var e=b(c).toLowerCase().split("-"),f=d?"end":"start",g=e.length,h="classic",i="medium",j="medium";g--;)switch(e[g]){case"block":case"classic":case"oval":case"diamond":case"open":case"none":h=e[g];break;case"wide":case"narrow":j=e[g];break;case"long":case"short":i=e[g]}var k=a.node.getElementsByTagName("stroke")[0];k[f+"arrow"]=h,k[f+"arrowlength"]=i,k[f+"arrowwidth"]=j},B=function(e,i){e.attrs=e.attrs||{};var l=e.node,m=e.attrs,p=l.style,q=v[e.type]&&(i.x!=m.x||i.y!=m.y||i.width!=m.width||i.height!=m.height||i.cx!=m.cx||i.cy!=m.cy||i.rx!=m.rx||i.ry!=m.ry||i.r!=m.r),r=w[e.type]&&(m.cx!=i.cx||m.cy!=i.cy||m.r!=i.r||m.rx!=i.rx||m.ry!=i.ry),s=e;for(var t in i)i[a](t)&&(m[t]=i[t]);if(q&&(m.path=c._getPath[e.type](e),e._.dirty=1),i.href&&(l.href=i.href),i.title&&(l.title=i.title),i.target&&(l.target=i.target),i.cursor&&(p.cursor=i.cursor),"blur"in i&&e.blur(i.blur),(i.path&&"path"==e.type||q)&&(l.path=x(~b(m.path).toLowerCase().indexOf("r")?c._pathToAbsolute(m.path):m.path),e._.dirty=1,"image"==e.type&&(e._.fillpos=[m.x,m.y],e._.fillsize=[m.width,m.height],z(e,1,1,0,0,0))),"transform"in i&&e.transform(i.transform),r){var y=+m.cx,B=+m.cy,D=+m.rx||+m.r||0,E=+m.ry||+m.r||0;l.path=c.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x",f((y-D)*u),f((B-E)*u),f((y+D)*u),f((B+E)*u),f(y*u)),e._.dirty=1}if("clip-rect"in i){var G=b(i["clip-rect"]).split(k);if(4==G.length){G[2]=+G[2]+ +G[0],G[3]=+G[3]+ +G[1];var H=l.clipRect||c._g.doc.createElement("div"),I=H.style;I.clip=c.format("rect({1}px {2}px {3}px {0}px)",G),l.clipRect||(I.position="absolute",I.top=0,I.left=0,I.width=e.paper.width+"px",I.height=e.paper.height+"px",l.parentNode.insertBefore(H,l),H.appendChild(l),l.clipRect=H)}i["clip-rect"]||l.clipRect&&(l.clipRect.style.clip="auto")}if(e.textpath){var J=e.textpath.style;i.font&&(J.font=i.font),i["font-family"]&&(J.fontFamily='"'+i["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g,o)+'"'),i["font-size"]&&(J.fontSize=i["font-size"]),i["font-weight"]&&(J.fontWeight=i["font-weight"]),i["font-style"]&&(J.fontStyle=i["font-style"])}if("arrow-start"in i&&A(s,i["arrow-start"]),"arrow-end"in i&&A(s,i["arrow-end"],1),null!=i.opacity||null!=i["stroke-width"]||null!=i.fill||null!=i.src||null!=i.stroke||null!=i["stroke-width"]||null!=i["stroke-opacity"]||null!=i["fill-opacity"]||null!=i["stroke-dasharray"]||null!=i["stroke-miterlimit"]||null!=i["stroke-linejoin"]||null!=i["stroke-linecap"]){var K=l.getElementsByTagName(j),L=!1;if(K=K&&K[0],!K&&(L=K=F(j)),"image"==e.type&&i.src&&(K.src=i.src),i.fill&&(K.on=!0),(null==K.on||"none"==i.fill||null===i.fill)&&(K.on=!1),K.on&&i.fill){var M=b(i.fill).match(c._ISURL);if(M){K.parentNode==l&&l.removeChild(K),K.rotate=!0,K.src=M[1],K.type="tile";var N=e.getBBox(1);K.position=N.x+n+N.y,e._.fillpos=[N.x,N.y],c._preload(M[1],function(){e._.fillsize=[this.offsetWidth,this.offsetHeight]})}else K.color=c.getRGB(i.fill).hex,K.src=o,K.type="solid",c.getRGB(i.fill).error&&(s.type in{circle:1,ellipse:1}||"r"!=b(i.fill).charAt())&&C(s,i.fill,K)&&(m.fill="none",m.gradient=i.fill,K.rotate=!1)}if("fill-opacity"in i||"opacity"in i){var O=((+m["fill-opacity"]+1||2)-1)*((+m.opacity+1||2)-1)*((+c.getRGB(i.fill).o+1||2)-1);O=h(g(O,0),1),K.opacity=O,K.src&&(K.color="none")}l.appendChild(K);var P=l.getElementsByTagName("stroke")&&l.getElementsByTagName("stroke")[0],Q=!1;!P&&(Q=P=F("stroke")),(i.stroke&&"none"!=i.stroke||i["stroke-width"]||null!=i["stroke-opacity"]||i["stroke-dasharray"]||i["stroke-miterlimit"]||i["stroke-linejoin"]||i["stroke-linecap"])&&(P.on=!0),("none"==i.stroke||null===i.stroke||null==P.on||0==i.stroke||0==i["stroke-width"])&&(P.on=!1);var R=c.getRGB(i.stroke);P.on&&i.stroke&&(P.color=R.hex),O=((+m["stroke-opacity"]+1||2)-1)*((+m.opacity+1||2)-1)*((+R.o+1||2)-1);var S=.75*(d(i["stroke-width"])||1);if(O=h(g(O,0),1),null==i["stroke-width"]&&(S=m["stroke-width"]),i["stroke-width"]&&(P.weight=S),S&&1>S&&(O*=S)&&(P.weight=1),P.opacity=O,i["stroke-linejoin"]&&(P.joinstyle=i["stroke-linejoin"]||"miter"),P.miterlimit=i["stroke-miterlimit"]||8,i["stroke-linecap"]&&(P.endcap="butt"==i["stroke-linecap"]?"flat":"square"==i["stroke-linecap"]?"square":"round"),"stroke-dasharray"in i){var T={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};P.dashstyle=T[a](i["stroke-dasharray"])?T[i["stroke-dasharray"]]:o}Q&&l.appendChild(P)}if("text"==s.type){s.paper.canvas.style.display=o;var U=s.paper.span,V=100,W=m.font&&m.font.match(/\d+(?:\.\d*)?(?=px)/);p=U.style,m.font&&(p.font=m.font),m["font-family"]&&(p.fontFamily=m["font-family"]),m["font-weight"]&&(p.fontWeight=m["font-weight"]),m["font-style"]&&(p.fontStyle=m["font-style"]),W=d(m["font-size"]||W&&W[0])||10,p.fontSize=W*V+"px",s.textpath.string&&(U.innerHTML=b(s.textpath.string).replace(/</g,"&#60;").replace(/&/g,"&#38;").replace(/\n/g,"<br>"));var X=U.getBoundingClientRect();s.W=m.w=(X.right-X.left)/V,s.H=m.h=(X.bottom-X.top)/V,s.X=m.x,s.Y=m.y+s.H/2,("x"in i||"y"in i)&&(s.path.v=c.format("m{0},{1}l{2},{1}",f(m.x*u),f(m.y*u),f(m.x*u)+1));for(var Y=["x","y","text","font","font-family","font-weight","font-style","font-size"],Z=0,$=Y.length;$>Z;Z++)if(Y[Z]in i){s._.dirty=1;break}switch(m["text-anchor"]){case"start":s.textpath.style["v-text-align"]="left",s.bbx=s.W/2;break;case"end":s.textpath.style["v-text-align"]="right",s.bbx=-s.W/2;break;default:s.textpath.style["v-text-align"]="center",s.bbx=0}s.textpath.style["v-text-kern"]=!0}},C=function(a,f,g){a.attrs=a.attrs||{};var h=(a.attrs,Math.pow),i="linear",j=".5 .5";if(a.attrs.gradient=f,f=b(f).replace(c._radial_gradient,function(a,b,c){return i="radial",b&&c&&(b=d(b),c=d(c),h(b-.5,2)+h(c-.5,2)>.25&&(c=e.sqrt(.25-h(b-.5,2))*(2*(c>.5)-1)+.5),j=b+n+c),o}),f=f.split(/\s*\-\s*/),"linear"==i){var k=f.shift();if(k=-d(k),isNaN(k))return null}var l=c._parseDots(f);if(!l)return null;if(a=a.shape||a.node,l.length){a.removeChild(g),g.on=!0,g.method="none",g.color=l[0].color,g.color2=l[l.length-1].color;for(var m=[],p=0,q=l.length;q>p;p++)l[p].offset&&m.push(l[p].offset+n+l[p].color);g.colors=m.length?m.join():"0% "+g.color,"radial"==i?(g.type="gradientTitle",g.focus="100%",g.focussize="0 0",g.focusposition=j,g.angle=0):(g.type="gradient",g.angle=(270-k)%360),a.appendChild(g)}return 1},D=function(a,b){this[0]=this.node=a,a.raphael=!0,this.id=c._oid++,a.raphaelid=this.id,this.X=0,this.Y=0,this.attrs={},this.paper=b,this.matrix=c.matrix(),this._={transform:[],sx:1,sy:1,dx:0,dy:0,deg:0,dirty:1,dirtyT:1},!b.bottom&&(b.bottom=this),this.prev=b.top,b.top&&(b.top.next=this),b.top=this,this.next=null},E=c.el;D.prototype=E,E.constructor=D,E.transform=function(a){if(null==a)return this._.transform;var d,e=this.paper._viewBoxShift,f=e?"s"+[e.scale,e.scale]+"-1-1t"+[e.dx,e.dy]:o;e&&(d=a=b(a).replace(/\.{3}|\u2026/g,this._.transform||o)),c._extractTransform(this,f+a);var g,h=this.matrix.clone(),i=this.skew,j=this.node,k=~b(this.attrs.fill).indexOf("-"),l=!b(this.attrs.fill).indexOf("url(");if(h.translate(1,1),l||k||"image"==this.type)if(i.matrix="1 0 0 1",i.offset="0 0",g=h.split(),k&&g.noRotation||!g.isSimple){j.style.filter=h.toFilter();var m=this.getBBox(),p=this.getBBox(1),q=m.x-p.x,r=m.y-p.y;j.coordorigin=q*-u+n+r*-u,z(this,1,1,q,r,0)}else j.style.filter=o,z(this,g.scalex,g.scaley,g.dx,g.dy,g.rotate);else j.style.filter=o,i.matrix=b(h),i.offset=h.offset();return null!==d&&(this._.transform=d,c._extractTransform(this,d)),this},E.rotate=function(a,c,e){if(this.removed)return this;if(null!=a){if(a=b(a).split(k),a.length-1&&(c=d(a[1]),e=d(a[2])),a=d(a[0]),null==e&&(c=e),null==c||null==e){var f=this.getBBox(1);c=f.x+f.width/2,e=f.y+f.height/2}return this._.dirtyT=1,this.transform(this._.transform.concat([["r",a,c,e]])),this}},E.translate=function(a,c){return this.removed?this:(a=b(a).split(k),a.length-1&&(c=d(a[1])),a=d(a[0])||0,c=+c||0,this._.bbox&&(this._.bbox.x+=a,this._.bbox.y+=c),this.transform(this._.transform.concat([["t",a,c]])),this)},E.scale=function(a,c,e,f){if(this.removed)return this;if(a=b(a).split(k),a.length-1&&(c=d(a[1]),e=d(a[2]),f=d(a[3]),isNaN(e)&&(e=null),isNaN(f)&&(f=null)),a=d(a[0]),null==c&&(c=a),null==f&&(e=f),null==e||null==f)var g=this.getBBox(1);return e=null==e?g.x+g.width/2:e,f=null==f?g.y+g.height/2:f,this.transform(this._.transform.concat([["s",a,c,e,f]])),this._.dirtyT=1,this},E.hide=function(){return!this.removed&&(this.node.style.display="none"),this},E.show=function(){return!this.removed&&(this.node.style.display=o),this},E.auxGetBBox=c.el.getBBox,E.getBBox=function(){var a=this.auxGetBBox();if(this.paper&&this.paper._viewBoxShift){var b={},c=1/this.paper._viewBoxShift.scale;return b.x=a.x-this.paper._viewBoxShift.dx,b.x*=c,b.y=a.y-this.paper._viewBoxShift.dy,b.y*=c,b.width=a.width*c,b.height=a.height*c,b.x2=b.x+b.width,b.y2=b.y+b.height,b}return a},E._getBBox=function(){return this.removed?{}:{x:this.X+(this.bbx||0)-this.W/2,y:this.Y-this.H,width:this.W,height:this.H}},E.remove=function(){if(!this.removed&&this.node.parentNode){this.paper.__set__&&this.paper.__set__.exclude(this),c.eve.unbind("raphael.*.*."+this.id),c._tear(this,this.paper),this.node.parentNode.removeChild(this.node),this.shape&&this.shape.parentNode.removeChild(this.shape);for(var a in this)this[a]="function"==typeof this[a]?c._removedFactory(a):null;this.removed=!0}},E.attr=function(b,d){if(this.removed)return this;if(null==b){var e={};for(var f in this.attrs)this.attrs[a](f)&&(e[f]=this.attrs[f]);return e.gradient&&"none"==e.fill&&(e.fill=e.gradient)&&delete e.gradient,e.transform=this._.transform,e}if(null==d&&c.is(b,"string")){if(b==j&&"none"==this.attrs.fill&&this.attrs.gradient)return this.attrs.gradient;for(var g=b.split(k),h={},i=0,m=g.length;m>i;i++)b=g[i],h[b]=b in this.attrs?this.attrs[b]:c.is(this.paper.customAttributes[b],"function")?this.paper.customAttributes[b].def:c._availableAttrs[b];return m-1?h:h[g[0]]}if(this.attrs&&null==d&&c.is(b,"array")){for(h={},i=0,m=b.length;m>i;i++)h[b[i]]=this.attr(b[i]);return h}var n;null!=d&&(n={},n[b]=d),null==d&&c.is(b,"object")&&(n=b);for(var o in n)l("raphael.attr."+o+"."+this.id,this,n[o]);if(n){for(o in this.paper.customAttributes)if(this.paper.customAttributes[a](o)&&n[a](o)&&c.is(this.paper.customAttributes[o],"function")){var p=this.paper.customAttributes[o].apply(this,[].concat(n[o]));this.attrs[o]=n[o];for(var q in p)p[a](q)&&(n[q]=p[q])}n.text&&"text"==this.type&&(this.textpath.string=n.text),B(this,n)}return this},E.toFront=function(){return!this.removed&&this.node.parentNode.appendChild(this.node),this.paper&&this.paper.top!=this&&c._tofront(this,this.paper),this},E.toBack=function(){return this.removed?this:(this.node.parentNode.firstChild!=this.node&&(this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild),c._toback(this,this.paper)),this)},E.insertAfter=function(a){return this.removed?this:(a.constructor==c.st.constructor&&(a=a[a.length-1]),a.node.nextSibling?a.node.parentNode.insertBefore(this.node,a.node.nextSibling):a.node.parentNode.appendChild(this.node),c._insertafter(this,a,this.paper),this)},E.insertBefore=function(a){return this.removed?this:(a.constructor==c.st.constructor&&(a=a[0]),a.node.parentNode.insertBefore(this.node,a.node),c._insertbefore(this,a,this.paper),this)},E.blur=function(a){var b=this.node.runtimeStyle,d=b.filter;return d=d.replace(r,o),0!==+a?(this.attrs.blur=a,b.filter=d+n+m+".Blur(pixelradius="+(+a||1.5)+")",b.margin=c.format("-{0}px 0 0 -{0}px",f(+a||1.5))):(b.filter=d,b.margin=0,delete this.attrs.blur),this},c._engine.path=function(a,b){var c=F("shape");c.style.cssText=t,c.coordsize=u+n+u,c.coordorigin=b.coordorigin;var d=new D(c,b),e={fill:"none",stroke:"#000"};a&&(e.path=a),d.type="path",d.path=[],d.Path=o,B(d,e),b.canvas.appendChild(c);var f=F("skew");return f.on=!0,c.appendChild(f),d.skew=f,d.transform(o),d},c._engine.rect=function(a,b,d,e,f,g){var h=c._rectPath(b,d,e,f,g),i=a.path(h),j=i.attrs;return i.X=j.x=b,i.Y=j.y=d,i.W=j.width=e,i.H=j.height=f,j.r=g,j.path=h,i.type="rect",i},c._engine.ellipse=function(a,b,c,d,e){{var f=a.path();f.attrs}return f.X=b-d,f.Y=c-e,f.W=2*d,f.H=2*e,f.type="ellipse",B(f,{cx:b,cy:c,rx:d,ry:e}),f},c._engine.circle=function(a,b,c,d){{var e=a.path();e.attrs}return e.X=b-d,e.Y=c-d,e.W=e.H=2*d,e.type="circle",B(e,{cx:b,cy:c,r:d}),e},c._engine.image=function(a,b,d,e,f,g){var h=c._rectPath(d,e,f,g),i=a.path(h).attr({stroke:"none"}),k=i.attrs,l=i.node,m=l.getElementsByTagName(j)[0];return k.src=b,i.X=k.x=d,i.Y=k.y=e,i.W=k.width=f,i.H=k.height=g,k.path=h,i.type="image",m.parentNode==l&&l.removeChild(m),m.rotate=!0,m.src=b,m.type="tile",i._.fillpos=[d,e],i._.fillsize=[f,g],l.appendChild(m),z(i,1,1,0,0,0),i},c._engine.text=function(a,d,e,g){var h=F("shape"),i=F("path"),j=F("textpath");d=d||0,e=e||0,g=g||"",i.v=c.format("m{0},{1}l{2},{1}",f(d*u),f(e*u),f(d*u)+1),i.textpathok=!0,j.string=b(g),j.on=!0,h.style.cssText=t,h.coordsize=u+n+u,h.coordorigin="0 0";var k=new D(h,a),l={fill:"#000",stroke:"none",font:c._availableAttrs.font,text:g};k.shape=h,k.path=i,k.textpath=j,k.type="text",k.attrs.text=b(g),k.attrs.x=d,k.attrs.y=e,k.attrs.w=1,k.attrs.h=1,B(k,l),h.appendChild(j),h.appendChild(i),a.canvas.appendChild(h);var m=F("skew");return m.on=!0,h.appendChild(m),k.skew=m,k.transform(o),k},c._engine.setSize=function(a,b){var d=this.canvas.style;return this.width=a,this.height=b,a==+a&&(a+="px"),b==+b&&(b+="px"),d.width=a,d.height=b,d.clip="rect(0 "+a+" "+b+" 0)",this._viewBox&&c._engine.setViewBox.apply(this,this._viewBox),this},c._engine.setViewBox=function(a,b,d,e,f){c.eve("raphael.setViewBox",this,this._viewBox,[a,b,d,e,f]);var g,h,i=this.getSize(),j=i.width,k=i.height;return f&&(g=k/e,h=j/d,j>d*g&&(a-=(j-d*g)/2/g),k>e*h&&(b-=(k-e*h)/2/h)),this._viewBox=[a,b,d,e,!!f],this._viewBoxShift={dx:-a,dy:-b,scale:i},this.forEach(function(a){a.transform("...")}),this};var F;c._engine.initWin=function(a){var b=a.document;b.styleSheets.length<31?b.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)"):b.styleSheets[0].addRule(".rvml","behavior:url(#default#VML)");try{!b.namespaces.rvml&&b.namespaces.add("rvml","urn:schemas-microsoft-com:vml"),F=function(a){return b.createElement("<rvml:"+a+' class="rvml">')}}catch(c){F=function(a){return b.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}},c._engine.initWin(c._g.win),c._engine.create=function(){var a=c._getContainer.apply(0,arguments),b=a.container,d=a.height,e=a.width,f=a.x,g=a.y;if(!b)throw new Error("VML container not found.");var h=new c._Paper,i=h.canvas=c._g.doc.createElement("div"),j=i.style;return f=f||0,g=g||0,e=e||512,d=d||342,h.width=e,h.height=d,e==+e&&(e+="px"),d==+d&&(d+="px"),h.coordsize=1e3*u+n+1e3*u,h.coordorigin="0 0",h.span=c._g.doc.createElement("span"),h.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;",i.appendChild(h.span),j.cssText=c.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",e,d),1==b?(c._g.doc.body.appendChild(i),j.left=f+"px",j.top=g+"px",j.position="absolute"):b.firstChild?b.insertBefore(i,b.firstChild):b.appendChild(i),h.renderfix=function(){},h},c.prototype.clear=function(){c.eve("raphael.clear",this),this.canvas.innerHTML=o,this.span=c._g.doc.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas.appendChild(this.span),this.bottom=this.top=null},c.prototype.remove=function(){c.eve("raphael.remove",this),this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]="function"==typeof this[a]?c._removedFactory(a):null;return!0};var G=c.st;for(var H in E)E[a](H)&&!G[a](H)&&(G[H]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}(H))}}(),B.was?A.win.Raphael=c:Raphael=c,"object"==typeof exports&&(module.exports=c),c});
/**
 * Raphael.Export https://github.com/ElbertF/Raphael.Export
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */

(function(R) {
	/**
	* Escapes string for XML interpolation
	* @param value string or number value to escape
	* @returns string escaped
	*/
	function escapeXML(s) {
		if ( typeof s === 'number' ) return s.toString();

		var replace = { '<': 'lt', '>': 'gt', '"': 'quot', '\'': 'apos' };

		for ( var entity in replace ) {
			s = s.replace(new RegExp(entity, 'g'), '&' + replace[entity] + ';');
		}

		return s;
	}

	/**
	* Generic map function
	* @param iterable the array or object to be mapped
	* @param callback the callback function(element, key)
	* @returns array
	*/
	function map(iterable, callback) {
		var mapped = [],
			undef = 'undefined',
			i;

		// use an index iteration if we're dealing with an array
		if( typeof iterable.unshift != 'undefined'){
			var l = iterable.length;
			for ( i = 0; i < l; i++ ) {
				if( typeof iterable[i] != undef ){
					var value = callback.call(this, iterable[i], i);
					if( value !== null ) mapped.push(value);
				}
			}
		} else {
			for ( i in iterable ) {
				if ( iterable.hasOwnProperty(i) ) {
					var value = callback.call(this, iterable[i], i);
					if ( value !== null ) mapped.push(value);
				}
			}
		}

		return mapped;
	}

	/**
	* Generic reduce function
	* @param iterable array or object to be reduced
	* @param callback the callback function(initial, element, i)
	* @param initial the initial value
	* @return the reduced value
	*/
	function reduce(iterable, callback, initial) {
		for ( var i in iterable ) {
			if ( iterable.hasOwnProperty(i) ) {
				initial = callback.call(this, initial, iterable[i], i);
			}
		}

		return initial;
	}

	/**
	* Utility method for creating a tag
	* @param name the tag name, e.g., 'text'
	* @param attrs the attribute string, e.g., name1="val1" name2="val2"
	* or attribute map, e.g., { name1 : 'val1', name2 : 'val2' }
	* @param content the content string inside the tag
	* @returns string of the tag
	*/
	function tag(name, attrs, matrix, content) {
		if ( typeof content === 'undefined' || content === null ) {
			content = '';
		}

		if ( typeof attrs === 'object' ) {
			attrs = map(attrs, function(element, name) {
				switch ( name ) {
					case 'transform':
						return;

					case 'fill':
						if ( element.match(/^hsb/) ) {
							var hsb = element.replace(/^hsb\(|\)$/g, '').split(',');

							if ( hsb.length === 3 ) {
								element = R.hsb2rgb(hsb[0], hsb[1], hsb[2]).toString();
							}
						}
				}

				return name + '="' + escapeXML(element) + '"';
			}).join(' ');
		}

		return '<' + name + ( matrix ? ' transform="matrix(' + matrix.toString().replace(/^matrix\(|\)$/g, '') + ')" ' : ' ' ) + attrs + '>' +  content + '</' + name + '>';
	}

	/**
	* @return object the style object
	*/
	function extractStyle(node) {
		return {
			font: {
				family: node.attrs.font.replace(/^.*?"(\w+)".*$/, '$1'),
				size:   typeof node.attrs['font-size'] === 'undefined' ? null : parseInt( node.attrs['font-size'] ),
				style: typeof node.attrs['font-style'] === 'undefined' ? null : node.attrs['font-style'],
				weight: typeof node.attrs['font-weight'] === 'undefined' ? null : node.attrs['font-weight']
			},
			anchor: typeof node.attrs['text-anchor'] === 'undefined' ? null : node.attrs['text-anchor']
		};
	}

	/**
	* @param style object from style()
	* @return string
	*/
	function styleToString(style) {
		// TODO figure out what is 'normal'
		// Tyler: it refers to the default inherited from CSS. Order of terms here:
		// 		  http://www.w3.org/TR/SVG/text.html#FontProperty
		var norm = 'normal',
			textAnchor = 'text-anchor: ' + ( style.anchor || 'middle' ) + '; ',
			font = style.font;
		// return 'font: normal normal normal 10px/normal ' + style.font.family + ( style.font.size === null ? '' : '; font-size: ' + style.font.size + 'px' );
		return textAnchor + [ 'font:',
		         (font.style || norm), // font-style (e.g. italic)
		         norm, // font-variant
		         (font.weight || norm), // font-weight (e.g. bold)
		         (font.size ? font.size + 'px' : '10px') + '/normal', // font-size/IGNORED line-height!
		         font.family ].join(' ');
	}
	
	/**
	 * repairs the hex color which missed the '#'
	 * @param any string
	 * @return hexvalue of rgb
	 */
	function convertToHexColor(value) {
		
		if(/^[0-9A-F]{6}$/i.test(value)){
			value = '#' + value;
		}
		
		return value;
	}

	/**
	* Computes tspan dy using font size. This formula was empircally determined
	* using a best-fit line. Works well in both VML and SVG browsers.
	* @param fontSize number
	* @return number
	*/
	function computeTSpanDy(fontSize, line, lines) {
		if ( fontSize === null ) fontSize = 10;

		//return fontSize * 4.5 / 13
		return fontSize * 4.5 / 13 * ( line - .2 - lines / 2 ) * 3.5;
	}

	var serializer = {
		'text': function(node) {
			var style = extractStyle(node),
				tags = new Array,
				textLines = node.attrs['text'].toString().split('\n'),
				totalLines = textLines.length;
			
			map(textLines, function(text, line) {
				var attrs = reduce(
					node.attrs,
					function(initial, value, name) {
						if ( name !== 'text' && name !== 'w' && name !== 'h' ) {
							if ( name === 'font-size') value = parseInt(value) + 'px';

							if( name === 'stroke'){
								value = convertToHexColor(value);
							}
							
							initial[name] = escapeXML(value.toString());
						}

						return initial;
					},
					{ style: styleToString(style) + ';' }
				);
				/**
				 * if text node has a class set, apply it to the attrs object
				*/
				if (node.node.className.baseVal != "") {
					attrs["class"] = node.node.className.baseVal;
				}
                tags.push(tag(
					'text',
					attrs,
					node.matrix,
					tag('tspan', { dy: computeTSpanDy(style.font.size, line + 1, totalLines) }, null, text.replace(/&/g, "&amp;"))
				));
			});

			return tags;
		},
		'path' : function(node) {
			var initial = ( node.matrix.a === 1 && node.matrix.d === 1 ) ? {} : { 'transform' : node.matrix.toString() };

			return tag(
				'path',
				reduce(
					node.attrs,
					function(initial, value, name) {
						if ( name === 'path' ) name = 'd';
						
						if( name === 'stroke'){
							value = convertToHexColor(value);
						}
						
						initial[name] = value.toString();

						return initial;
					},
					{}
				),
				node.matrix
				);
		}
		// Other serializers should go here
	};

	R.fn.toSVG = function() {
		var
			paper   = this,
			restore = { svg: R.svg, vml: R.vml },
			svg     = '<svg style="overflow: hidden; position: relative;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' + paper.width + '" version="1.1" height="' + paper.height + '">'
			;

		R.svg = true;
		R.vml = false;

		for ( var node = paper.bottom; node != null; node = node.next ) {
			if ( node.node.style.display === 'none' ) continue;

			var attrs = '';

			// Use serializer
			if ( typeof serializer[node.type] === 'function' ) {
				svg += serializer[node.type](node);

				continue;
			}

			switch ( node.type ) {
				case 'image':
					attrs += ' preserveAspectRatio="none"';
					break;
			}

			for ( i in node.attrs ) {
				var name = i;
				var value = '';

				switch ( i ) {
					case 'r':
						// see https://github.com/ElbertF/Raphael.Export/issues/40
						if (node.type != "rect") {
							break;
						}
						
						/**
						 * set 'rx' and 'ry' to 'r'
						*/
						value = node.attrs.r;
						node.attrs.rx = value;
						node.attrs.ry = value;
						
						/**
						 * skip adding the 'r' attribute
						*/
						continue;
					
					case 'src':
						name = 'xlink:href';

						break;
					case 'transform':
						name = '';

						break;

					case 'fill':
						//skip if there is any gradient
						if(node.attrs.gradient)
							continue;
						break;
					case 'gradient':
						//radial gradient
						var id = node.id;
						var gradient = node.attrs.gradient;
						var fx = 0.5, fy=0.5;
						gradient = String(gradient).replace(R._radial_gradient, function (all, _fx, _fy) {
			                type = "radial";
			                if (_fx && _fy) {
			                    fx = parseFloat(_fx);
			                    fy = parseFloat(_fy);
			                    var dir = ((fy > .5) * 2 - 1);
			                    Math.pow(fx - .5, 2) + Math.pow(fy - .5, 2) > .25 &&
			                        (fy = Math.sqrt(.25 - Math.pow(fx - .5, 2)) * dir + .5) &&
			                        fy != .5 &&
			                        (fy = fy.toFixed(5) - 1e-5 * dir);
			                }
			                return '';
			            });
						gradient = gradient.split(/\s*\-\s*/);
						if(node.attrs.gradient.match(/^r/g)){
							var dots = R._parseDots(gradient);
							if (!dots) {
				                continue;
				            }	
							svg += '<defs>';
							svg += '	    <radialGradient id="radialgradient'+id+'" fx="'+fx+'" fy="'+fy+'" >';

							for(var di = 0; di < dots.length; di++){
								var offset = (di/(dots.length-1) * 100)+'%';
								//if dot has an offset
								if(dots[di].offset)							
									offset = dots[di].offset;
								svg +=  '<stop stop-color="'+dots[di].color+'" offset="'+offset+'"/>';
							}
							svg += '    </radialGradient>';
							svg += '</defs>';

							name = 'fill';
							value = 'url(#radialgradient'+id+')';

						}else{//linear gradient

							//assuming gradient is validated already!!
							var angle = gradient.shift();
							angle = parseFloat(angle)*-1;
			                if (isNaN(angle)) {
			                   continue; 
			                }
							var dots = R._parseDots(gradient);
							if (!dots) {
				                continue;
				            }
				            var vector = [0, 0, Math.cos(R.rad(angle)), Math.sin(R.rad(angle))],
			                       max = 1 / (Math.max(Math.abs(vector[2]), Math.abs(vector[3])) || 1);
			                vector[2] *= max;
			                vector[3] *= max;
			                if (vector[2] < 0) {
			                    vector[0] = -vector[2];
			                    vector[2] = 0;
			                }
			                if (vector[3] < 0) {
			                    vector[1] = -vector[3];
			                    vector[3] = 0;
			                }

				            svg += '<defs>';
							svg += '	    <linearGradient id="lineargradient'+id+'" x1="'+vector[0]+'" y1="'+vector[1]+'" x2="'+vector[2]+'" y2="'+vector[3]+'">';

							for(var di = 0; di < dots.length; di++){
								var offset = (di/(dots.length-1) * 100)+'%';
								//if dot has an offset
								if(dots[di].offset)							
									offset = dots[di].offset;
								svg +=  '<stop stop-color="'+dots[di].color+'" offset="'+offset+'"/>';
							}
							svg += '    </linearGradient>';
							svg += '</defs>';

							name = 'fill';
							value = 'url(#lineargradient'+id+')';

						}
						break;
					case 'stroke':
						if(value){
							value = convertToHexColor(value);
						}else{
							value = convertToHexColor(node.attrs[i].toString());
						}
						break;
				}

				if ( name ) {
					if(value)
						attrs += ' ' + name + '="' + escapeXML(value) + '"';
					else
						attrs += ' ' + name + '="' + escapeXML(node.attrs[i].toString()) + '"';
				}
			}

			/**
			 * if node has a class set, append it to the attrs string
		    */
			if (node.node.className.baseVal != "") {
				attrs += ' ' + 'class="' + node.node.className.baseVal + '"';
			}

			svg += '<' + node.type + ' transform="matrix(' + node.matrix.toString().replace(/^matrix\(|\)$/g, '') + ')"' + attrs + '></' + node.type + '>';
		}

		svg += '</svg>';

		R.svg = restore.svg;
		R.vml = restore.vml;

		return svg;
	};
})(window.Raphael);
/*
 * Raphael 1.5.2 - JavaScript Vector Library
 *
 * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com)
 * Licensed under the MIT (http://raphaeljs.com/license.html) license.
 */
(function(){function a(){if(a.is(arguments[0],G)){var b=arguments[0],d=bV[m](a,b.splice(0,3+a.is(b[0],E))),e=d.set();for(var g=0,h=b[w];g<h;g++){var i=b[g]||{};c[f](i.type)&&e[L](d[i.type]().attr(i))}return e}return bV[m](a,arguments)}a.version="1.5.2";var b=/[, ]+/,c={circle:1,rect:1,path:1,ellipse:1,text:1,image:1},d=/\{(\d+)\}/g,e="prototype",f="hasOwnProperty",g=document,h=window,i={was:Object[e][f].call(h,"Raphael"),is:h.Raphael},j=function(){this.customAttributes={}},k,l="appendChild",m="apply",n="concat",o="createTouch"in g,p="",q=" ",r=String,s="split",t="click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend orientationchange touchcancel gesturestart gesturechange gestureend"[s](q),u={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},v="join",w="length",x=r[e].toLowerCase,y=Math,z=y.max,A=y.min,B=y.abs,C=y.pow,D=y.PI,E="number",F="string",G="array",H="toString",I="fill",J=Object[e][H],K={},L="push",M=/^url\(['"]?([^\)]+?)['"]?\)$/i,N=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,O={"NaN":1,Infinity:1,"-Infinity":1},P=/^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,Q=y.round,R="setAttribute",S=parseFloat,T=parseInt,U=" progid:DXImageTransform.Microsoft",V=r[e].toUpperCase,W={blur:0,"clip-rect":"0 0 1e9 1e9",cursor:"default",cx:0,cy:0,fill:"#fff","fill-opacity":1,font:"10px \"Arial\"","font-family":"\"Arial\"","font-size":"10","font-style":"normal","font-weight":400,gradient:0,height:0,href:"http://raphaeljs.com/",opacity:1,path:"M0,0",r:0,rotation:0,rx:0,ry:0,scale:"1 1",src:"",stroke:"#000","stroke-dasharray":"","stroke-linecap":"butt","stroke-linejoin":"butt","stroke-miterlimit":0,"stroke-opacity":1,"stroke-width":1,target:"_blank","text-anchor":"middle",title:"Raphael",translation:"0 0",width:0,x:0,y:0},X={along:"along",blur:E,"clip-rect":"csv",cx:E,cy:E,fill:"colour","fill-opacity":E,"font-size":E,height:E,opacity:E,path:"path",r:E,rotation:"csv",rx:E,ry:E,scale:"csv",stroke:"colour","stroke-opacity":E,"stroke-width":E,translation:"csv",width:E,x:E,y:E},Y="replace",Z=/^(from|to|\d+%?)$/,$=/\s*,\s*/,_={hs:1,rg:1},ba=/,?([achlmqrstvxz]),?/gi,bb=/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,bc=/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,bd=/^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/,be=function(a,b){return a.key-b.key};a.type=h.SVGAngle||g.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")?"SVG":"VML";if(a.type=="VML"){var bf=g.createElement("div"),bg;bf.innerHTML="<v:shape adj=\"1\"/>";bg=bf.firstChild;bg.style.behavior="url(#default#VML)";if(!(bg&&typeof bg.adj=="object"))return a.type=null;bf=null}a.svg=!(a.vml=a.type=="VML");j[e]=a[e];k=j[e];a._id=0;a._oid=0;a.fn={};a.is=function(a,b){b=x.call(b);if(b=="finite")return!O[f](+a);return b=="null"&&a===null||b==typeof a||b=="object"&&a===Object(a)||b=="array"&&Array.isArray&&Array.isArray(a)||J.call(a).slice(8,-1).toLowerCase()==b};a.angle=function(b,c,d,e,f,g){{if(f==null){var h=b-d,i=c-e;if(!h&&!i)return 0;return((h<0)*180+y.atan(-i/-h)*180/D+360)%360}return a.angle(b,c,f,g)-a.angle(d,e,f,g)}};a.rad=function(a){return a%360*D/180};a.deg=function(a){return a*180/D%360};a.snapTo=function(b,c,d){d=a.is(d,"finite")?d:10;if(a.is(b,G)){var e=b.length;while(e--)if(B(b[e]-c)<=d)return b[e]}else{b=+b;var f=c%b;if(f<d)return c-f;if(f>b-d)return c-f+b}return c};function bh(){var a=[],b=0;for(;b<32;b++)a[b]=(~(~(y.random()*16)))[H](16);a[12]=4;a[16]=(a[16]&3|8)[H](16);return"r-"+a[v]("")}a.setWindow=function(a){h=a;g=h.document};var bi=function(b){if(a.vml){var c=/^\s+|\s+$/g,d;try{var e=new ActiveXObject("htmlfile");e.write("<body>");e.close();d=e.body}catch(a){d=createPopup().document.body}var f=d.createTextRange();bi=bm(function(a){try{d.style.color=r(a)[Y](c,p);var b=f.queryCommandValue("ForeColor");b=(b&255)<<16|b&65280|(b&16711680)>>>16;return"#"+("000000"+b[H](16)).slice(-6)}catch(a){return"none"}})}else{var h=g.createElement("i");h.title="RaphaÃ«l Colour Picker";h.style.display="none";g.body[l](h);bi=bm(function(a){h.style.color=a;return g.defaultView.getComputedStyle(h,p).getPropertyValue("color")})}return bi(b)},bj=function(){return"hsb("+[this.h,this.s,this.b]+")"},bk=function(){return"hsl("+[this.h,this.s,this.l]+")"},bl=function(){return this.hex};a.hsb2rgb=function(b,c,d,e){if(a.is(b,"object")&&"h"in b&&"s"in b&&"b"in b){d=b.b;c=b.s;b=b.h;e=b.o}return a.hsl2rgb(b,c,d/2,e)};a.hsl2rgb=function(b,c,d,e){if(a.is(b,"object")&&"h"in b&&"s"in b&&"l"in b){d=b.l;c=b.s;b=b.h}if(b>1||c>1||d>1){b/=360;c/=100;d/=100}var f={},g=["r","g","b"],h,i,j,k,l,m;if(c){d<0.5?h=d*(1+c):h=d+c-d*c;i=2*d-h;for(var n=0;n<3;n++){j=b+1/3*-(n-1);j<0&&j++;j>1&&j--;j*6<1?f[g[n]]=i+(h-i)*6*j:j*2<1?f[g[n]]=h:j*3<2?f[g[n]]=i+(h-i)*(2/3-j)*6:f[g[n]]=i}}else f={r:d,g:d,b:d};f.r*=255;f.g*=255;f.b*=255;f.hex="#"+(16777216|f.b|f.g<<8|f.r<<16).toString(16).slice(1);a.is(e,"finite")&&(f.opacity=e);f.toString=bl;return f};a.rgb2hsb=function(b,c,d){if(c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b){d=b.b;c=b.g;b=b.r}if(c==null&&a.is(b,F)){var e=a.getRGB(b);b=e.r;c=e.g;d=e.b}if(b>1||c>1||d>1){b/=255;c/=255;d/=255}var f=z(b,c,d),g=A(b,c,d),h,i,j=f;{if(g==f)return{h:0,s:0,b:f,toString:bj};var k=f-g;i=k/f;b==f?h=(c-d)/k:c==f?h=2+(d-b)/k:h=4+(b-c)/k;h/=6;h<0&&h++;h>1&&h--}return{h:h,s:i,b:j,toString:bj}};a.rgb2hsl=function(b,c,d){if(c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b){d=b.b;c=b.g;b=b.r}if(c==null&&a.is(b,F)){var e=a.getRGB(b);b=e.r;c=e.g;d=e.b}if(b>1||c>1||d>1){b/=255;c/=255;d/=255}var f=z(b,c,d),g=A(b,c,d),h,i,j=(f+g)/2,k;if(g==f)k={h:0,s:0,l:j};else{var l=f-g;i=j<0.5?l/(f+g):l/(2-f-g);b==f?h=(c-d)/l:c==f?h=2+(d-b)/l:h=4+(b-c)/l;h/=6;h<0&&h++;h>1&&h--;k={h:h,s:i,l:j}}k.toString=bk;return k};a._path2string=function(){return this.join(",")[Y](ba,"$1")};function bm(a,b,c){function d(){var g=Array[e].slice.call(arguments,0),h=g[v]("âº"),i=d.cache=d.cache||{},j=d.count=d.count||[];if(i[f](h))return c?c(i[h]):i[h];j[w]>=1000&&delete i[j.shift()];j[L](h);i[h]=a[m](b,g);return c?c(i[h]):i[h]}return d}a.getRGB=bm(function(b){if(!b||!(!((b=r(b)).indexOf("-")+1)))return{r:-1,g:-1,b:-1,hex:"none",error:1};if(b=="none")return{r:-1,g:-1,b:-1,hex:"none"};!(_[f](b.toLowerCase().substring(0,2))||b.charAt()=="#")&&(b=bi(b));var c,d,e,g,h,i,j,k=b.match(N);if(k){if(k[2]){g=T(k[2].substring(5),16);e=T(k[2].substring(3,5),16);d=T(k[2].substring(1,3),16)}if(k[3]){g=T((i=k[3].charAt(3))+i,16);e=T((i=k[3].charAt(2))+i,16);d=T((i=k[3].charAt(1))+i,16)}if(k[4]){j=k[4][s]($);d=S(j[0]);j[0].slice(-1)=="%"&&(d*=2.55);e=S(j[1]);j[1].slice(-1)=="%"&&(e*=2.55);g=S(j[2]);j[2].slice(-1)=="%"&&(g*=2.55);k[1].toLowerCase().slice(0,4)=="rgba"&&(h=S(j[3]));j[3]&&j[3].slice(-1)=="%"&&(h/=100)}if(k[5]){j=k[5][s]($);d=S(j[0]);j[0].slice(-1)=="%"&&(d*=2.55);e=S(j[1]);j[1].slice(-1)=="%"&&(e*=2.55);g=S(j[2]);j[2].slice(-1)=="%"&&(g*=2.55);(j[0].slice(-3)=="deg"||j[0].slice(-1)=="Â°")&&(d/=360);k[1].toLowerCase().slice(0,4)=="hsba"&&(h=S(j[3]));j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsb2rgb(d,e,g,h)}if(k[6]){j=k[6][s]($);d=S(j[0]);j[0].slice(-1)=="%"&&(d*=2.55);e=S(j[1]);j[1].slice(-1)=="%"&&(e*=2.55);g=S(j[2]);j[2].slice(-1)=="%"&&(g*=2.55);(j[0].slice(-3)=="deg"||j[0].slice(-1)=="Â°")&&(d/=360);k[1].toLowerCase().slice(0,4)=="hsla"&&(h=S(j[3]));j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsl2rgb(d,e,g,h)}k={r:d,g:e,b:g};k.hex="#"+(16777216|g|e<<8|d<<16).toString(16).slice(1);a.is(h,"finite")&&(k.opacity=h);return k}return{r:-1,g:-1,b:-1,hex:"none",error:1}},a);a.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||0.75},c=this.hsb2rgb(b.h,b.s,b.b);b.h+=0.075;if(b.h>1){b.h=0;b.s-=0.2;b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b})}return c.hex};a.getColor.reset=function(){delete this.start};a.parsePathString=bm(function(b){if(!b)return null;var c={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},d=[];a.is(b,G)&&a.is(b[0],G)&&(d=bo(b));d[w]||r(b)[Y](bb,function(a,b,e){var f=[],g=x.call(b);e[Y](bc,function(a,b){b&&f[L](+b)});if(g=="m"&&f[w]>2){d[L]([b][n](f.splice(0,2)));g="l";b=b=="m"?"l":"L"}while(f[w]>=c[g]){d[L]([b][n](f.splice(0,c[g])));if(!c[g])break}});d[H]=a._path2string;return d});a.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=C(j,3)*a+C(j,2)*3*i*c+j*3*i*i*e+C(i,3)*g,l=C(j,3)*b+C(j,2)*3*i*d+j*3*i*i*f+C(i,3)*h,m=a+2*i*(c-a)+i*i*(e-2*c+a),n=b+2*i*(d-b)+i*i*(f-2*d+b),o=c+2*i*(e-c)+i*i*(g-2*e+c),p=d+2*i*(f-d)+i*i*(h-2*f+d),q=(1-i)*a+i*c,r=(1-i)*b+i*d,s=(1-i)*e+i*g,t=(1-i)*f+i*h,u=90-y.atan((m-o)/(n-p))*180/D;(m>o||n<p)&&(u+=180);return{x:k,y:l,m:{x:m,y:n},n:{x:o,y:p},start:{x:q,y:r},end:{x:s,y:t},alpha:u}};var bn=bm(function(a){if(!a)return{x:0,y:0,width:0,height:0};a=bw(a);var b=0,c=0,d=[],e=[],f;for(var g=0,h=a[w];g<h;g++){f=a[g];if(f[0]=="M"){b=f[1];c=f[2];d[L](b);e[L](c)}else{var i=bv(b,c,f[1],f[2],f[3],f[4],f[5],f[6]);d=d[n](i.min.x,i.max.x);e=e[n](i.min.y,i.max.y);b=f[5];c=f[6]}}var j=A[m](0,d),k=A[m](0,e);return{x:j,y:k,width:z[m](0,d)-j,height:z[m](0,e)-k}}),bo=function(b){var c=[];if(!a.is(b,G)||!a.is(b&&b[0],G))b=a.parsePathString(b);for(var d=0,e=b[w];d<e;d++){c[d]=[];for(var f=0,g=b[d][w];f<g;f++)c[d][f]=b[d][f]}c[H]=a._path2string;return c},bp=bm(function(b){if(!a.is(b,G)||!a.is(b&&b[0],G))b=a.parsePathString(b);var c=[],d=0,e=0,f=0,g=0,h=0;if(b[0][0]=="M"){d=b[0][1];e=b[0][2];f=d;g=e;h++;c[L](["M",d,e])}for(var i=h,j=b[w];i<j;i++){var k=c[i]=[],l=b[i];if(l[0]!=x.call(l[0])){k[0]=x.call(l[0]);switch(k[0]){case"a":k[1]=l[1];k[2]=l[2];k[3]=l[3];k[4]=l[4];k[5]=l[5];k[6]=+(l[6]-d).toFixed(3);k[7]=+(l[7]-e).toFixed(3);break;case"v":k[1]=+(l[1]-e).toFixed(3);break;case"m":f=l[1];g=l[2];default:for(var m=1,n=l[w];m<n;m++)k[m]=+(l[m]-(m%2?d:e)).toFixed(3)}}else{k=c[i]=[];if(l[0]=="m"){f=l[1]+d;g=l[2]+e}for(var o=0,p=l[w];o<p;o++)c[i][o]=l[o]}var q=c[i][w];switch(c[i][0]){case"z":d=f;e=g;break;case"h":d+=+c[i][q-1];break;case"v":e+=+c[i][q-1];break;default:d+=+c[i][q-2];e+=+c[i][q-1]}}c[H]=a._path2string;return c},0,bo),bq=bm(function(b){if(!a.is(b,G)||!a.is(b&&b[0],G))b=a.parsePathString(b);var c=[],d=0,e=0,f=0,g=0,h=0;if(b[0][0]=="M"){d=+b[0][1];e=+b[0][2];f=d;g=e;h++;c[0]=["M",d,e]}for(var i=h,j=b[w];i<j;i++){var k=c[i]=[],l=b[i];if(l[0]!=V.call(l[0])){k[0]=V.call(l[0]);switch(k[0]){case"A":k[1]=l[1];k[2]=l[2];k[3]=l[3];k[4]=l[4];k[5]=l[5];k[6]=+(l[6]+d);k[7]=+(l[7]+e);break;case"V":k[1]=+l[1]+e;break;case"H":k[1]=+l[1]+d;break;case"M":f=+l[1]+d;g=+l[2]+e;default:for(var m=1,n=l[w];m<n;m++)k[m]=+l[m]+(m%2?d:e)}}else for(var o=0,p=l[w];o<p;o++)c[i][o]=l[o];switch(k[0]){case"Z":d=f;e=g;break;case"H":d=k[1];break;case"V":e=k[1];break;case"M":f=c[i][c[i][w]-2];g=c[i][c[i][w]-1];default:d=c[i][c[i][w]-2];e=c[i][c[i][w]-1]}}c[H]=a._path2string;return c},null,bo),br=function(a,b,c,d){return[a,b,c,d,c,d]},bs=function(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]},bt=function(a,b,c,d,e,f,g,h,i,j){var k=D*120/180,l=D/180*(+e||0),m=[],o,p=bm(function(a,b,c){var d=a*y.cos(c)-b*y.sin(c),e=a*y.sin(c)+b*y.cos(c);return{x:d,y:e}});if(j){G=j[0];H=j[1];E=j[2];F=j[3]}else{o=p(a,b,-l);a=o.x;b=o.y;o=p(h,i,-l);h=o.x;i=o.y;var q=y.cos(D/180*e),r=y.sin(D/180*e),t=(a-h)/2,u=(b-i)/2,x=t*t/(c*c)+u*u/(d*d);if(x>1){x=y.sqrt(x);c=x*c;d=x*d}var z=c*c,A=d*d,C=(f==g?-1:1)*y.sqrt(B((z*A-z*u*u-A*t*t)/(z*u*u+A*t*t))),E=C*c*u/d+(a+h)/2,F=C*-d*t/c+(b+i)/2,G=y.asin(((b-F)/d).toFixed(9)),H=y.asin(((i-F)/d).toFixed(9));G=a<E?D-G:G;H=h<E?D-H:H;G<0&&(G=D*2+G);H<0&&(H=D*2+H);g&&G>H&&(G=G-D*2);!g&&H>G&&(H=H-D*2)}var I=H-G;if(B(I)>k){var J=H,K=h,L=i;H=G+k*(g&&H>G?1:-1);h=E+c*y.cos(H);i=F+d*y.sin(H);m=bt(h,i,c,d,e,0,g,K,L,[H,J,E,F])}I=H-G;var M=y.cos(G),N=y.sin(G),O=y.cos(H),P=y.sin(H),Q=y.tan(I/4),R=4/3*c*Q,S=4/3*d*Q,T=[a,b],U=[a+R*N,b-S*M],V=[h+R*P,i-S*O],W=[h,i];U[0]=2*T[0]-U[0];U[1]=2*T[1]-U[1];{if(j)return[U,V,W][n](m);m=[U,V,W][n](m)[v]()[s](",");var X=[];for(var Y=0,Z=m[w];Y<Z;Y++)X[Y]=Y%2?p(m[Y-1],m[Y],l).y:p(m[Y],m[Y+1],l).x;return X}},bu=function(a,b,c,d,e,f,g,h,i){var j=1-i;return{x:C(j,3)*a+C(j,2)*3*i*c+j*3*i*i*e+C(i,3)*g,y:C(j,3)*b+C(j,2)*3*i*d+j*3*i*i*f+C(i,3)*h}},bv=bm(function(a,b,c,d,e,f,g,h){var i=e-2*c+a-(g-2*e+c),j=2*(c-a)-2*(e-c),k=a-c,l=(-j+y.sqrt(j*j-4*i*k))/2/i,n=(-j-y.sqrt(j*j-4*i*k))/2/i,o=[b,h],p=[a,g],q;B(l)>"1e12"&&(l=0.5);B(n)>"1e12"&&(n=0.5);if(l>0&&l<1){q=bu(a,b,c,d,e,f,g,h,l);p[L](q.x);o[L](q.y)}if(n>0&&n<1){q=bu(a,b,c,d,e,f,g,h,n);p[L](q.x);o[L](q.y)}i=f-2*d+b-(h-2*f+d);j=2*(d-b)-2*(f-d);k=b-d;l=(-j+y.sqrt(j*j-4*i*k))/2/i;n=(-j-y.sqrt(j*j-4*i*k))/2/i;B(l)>"1e12"&&(l=0.5);B(n)>"1e12"&&(n=0.5);if(l>0&&l<1){q=bu(a,b,c,d,e,f,g,h,l);p[L](q.x);o[L](q.y)}if(n>0&&n<1){q=bu(a,b,c,d,e,f,g,h,n);p[L](q.x);o[L](q.y)}return{min:{x:A[m](0,p),y:A[m](0,o)},max:{x:z[m](0,p),y:z[m](0,o)}}}),bw=bm(function(a,b){var c=bq(a),d=b&&bq(b),e={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g=function(a,b){var c,d;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null);switch(a[0]){case"M":b.X=a[1];b.Y=a[2];break;case"A":a=["C"][n](bt[m](0,[b.x,b.y][n](a.slice(1))));break;case"S":c=b.x+(b.x-(b.bx||b.x));d=b.y+(b.y-(b.by||b.y));a=["C",c,d][n](a.slice(1));break;case"T":b.qx=b.x+(b.x-(b.qx||b.x));b.qy=b.y+(b.y-(b.qy||b.y));a=["C"][n](bs(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1];b.qy=a[2];a=["C"][n](bs(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][n](br(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][n](br(b.x,b.y,a[1],b.y));break;case"V":a=["C"][n](br(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][n](br(b.x,b.y,b.X,b.Y));break}return a},h=function(a,b){if(a[b][w]>7){a[b].shift();var e=a[b];while(e[w])a.splice(b++,0,["C"][n](e.splice(0,6)));a.splice(b,1);k=z(c[w],d&&d[w]||0)}},i=function(a,b,e,f,g){if(a&&b&&a[g][0]=="M"&&b[g][0]!="M"){b.splice(g,0,["M",f.x,f.y]);e.bx=0;e.by=0;e.x=a[g][1];e.y=a[g][2];k=z(c[w],d&&d[w]||0)}};for(var j=0,k=z(c[w],d&&d[w]||0);j<k;j++){c[j]=g(c[j],e);h(c,j);d&&(d[j]=g(d[j],f));d&&h(d,j);i(c,d,e,f,j);i(d,c,f,e,j);var l=c[j],o=d&&d[j],p=l[w],q=d&&o[w];e.x=l[p-2];e.y=l[p-1];e.bx=S(l[p-4])||e.x;e.by=S(l[p-3])||e.y;f.bx=d&&(S(o[q-4])||f.x);f.by=d&&(S(o[q-3])||f.y);f.x=d&&o[q-2];f.y=d&&o[q-1]}return d?[c,d]:c},null,bo),bx=bm(function(b){var c=[];for(var d=0,e=b[w];d<e;d++){var f={},g=b[d].match(/^([^:]*):?([\d\.]*)/);f.color=a.getRGB(g[1]);if(f.color.error)return null;f.color=f.color.hex;g[2]&&(f.offset=g[2]+"%");c[L](f)}for(d=1,e=c[w]-1;d<e;d++){if(!c[d].offset){var h=S(c[d-1].offset||0),i=0;for(var j=d+1;j<e;j++){if(c[j].offset){i=c[j].offset;break}}if(!i){i=100;j=e}i=S(i);var k=(i-h)/(j-d+1);for(;d<j;d++){h+=k;c[d].offset=h+"%"}}}return c}),by=function(b,c,d,e){var f;if(a.is(b,F)||a.is(b,"object")){f=a.is(b,F)?g.getElementById(b):b;if(f.tagName)return c==null?{container:f,width:f.style.pixelWidth||f.offsetWidth,height:f.style.pixelHeight||f.offsetHeight}:{container:f,width:c,height:d}}else return{container:1,x:b,y:c,width:d,height:e}},bz=function(a,b){var c=this;for(var d in b){if(b[f](d)&&!(d in a))switch(typeof b[d]){case"function":(function(b){a[d]=a===c?b:function(){return b[m](c,arguments)}})(b[d]);break;case"object":a[d]=a[d]||{};bz.call(this,a[d],b[d]);break;default:a[d]=b[d];break}}},bA=function(a,b){a==b.top&&(b.top=a.prev);a==b.bottom&&(b.bottom=a.next);a.next&&(a.next.prev=a.prev);a.prev&&(a.prev.next=a.next)},bB=function(a,b){if(b.top===a)return;bA(a,b);a.next=null;a.prev=b.top;b.top.next=a;b.top=a},bC=function(a,b){if(b.bottom===a)return;bA(a,b);a.next=b.bottom;a.prev=null;b.bottom.prev=a;b.bottom=a},bD=function(a,b,c){bA(a,c);b==c.top&&(c.top=a);b.next&&(b.next.prev=a);a.next=b.next;a.prev=b;b.next=a},bE=function(a,b,c){bA(a,c);b==c.bottom&&(c.bottom=a);b.prev&&(b.prev.next=a);a.prev=b.prev;b.prev=a;a.next=b},bF=function(a){return function(){throw new Error("RaphaÃ«l: you are calling to method â"+a+"â of removed object")}};a.pathToRelative=bp;if(a.svg){k.svgns="http://www.w3.org/2000/svg";k.xlink="http://www.w3.org/1999/xlink";Q=function(a){return+a+(~(~a)===a)*0.5};var bG=function(a,b){if(b)for(var c in b)b[f](c)&&a[R](c,r(b[c]));else{a=g.createElementNS(k.svgns,a);a.style.webkitTapHighlightColor="rgba(0,0,0,0)";return a}};a[H]=function(){return"Your browser supports SVG.\nYou are running RaphaÃ«l "+this.version};var bH=function(a,b){var c=bG("path");b.canvas&&b.canvas[l](c);var d=new bN(c,b);d.type="path";bK(d,{fill:"none",stroke:"#000",path:a});return d},bI=function(a,b,c){var d="linear",e=0.5,f=0.5,h=a.style;b=r(b)[Y](bd,function(a,b,c){d="radial";if(b&&c){e=S(b);f=S(c);var g=(f>0.5)*2-1;C(e-0.5,2)+C(f-0.5,2)>0.25&&(f=y.sqrt(0.25-C(e-0.5,2))*g+0.5)&&f!=0.5&&(f=f.toFixed(5)-0.00001*g)}return p});b=b[s](/\s*\-\s*/);if(d=="linear"){var i=b.shift();i=-S(i);if(isNaN(i))return null;var j=[0,0,y.cos(i*D/180),y.sin(i*D/180)],k=1/(z(B(j[2]),B(j[3]))||1);j[2]*=k;j[3]*=k;if(j[2]<0){j[0]=-j[2];j[2]=0}if(j[3]<0){j[1]=-j[3];j[3]=0}}var m=bx(b);if(!m)return null;var n=a.getAttribute(I);n=n.match(/^url\(#(.*)\)$/);n&&c.defs.removeChild(g.getElementById(n[1]));var o=bG(d+"Gradient");o.id=bh();bG(o,d=="radial"?{fx:e,fy:f}:{x1:j[0],y1:j[1],x2:j[2],y2:j[3]});c.defs[l](o);for(var q=0,t=m[w];q<t;q++){var u=bG("stop");bG(u,{offset:m[q].offset?m[q].offset:q?"100%":"0%","stop-color":m[q].color||"#fff"});o[l](u)}bG(a,{fill:"url(#"+o.id+")",opacity:1,"fill-opacity":1});h.fill=p;h.opacity=1;h.fillOpacity=1;return 1},bJ=function(b){var c=b.getBBox();bG(b.pattern,{patternTransform:a.format("translate({0},{1})",c.x,c.y)})},bK=function(c,d){var e={"":[0],none:[0],"-":[3,1],".":[1,1],"-.":[3,1,1,1],"-..":[3,1,1,1,1,1],". ":[1,3],"- ":[4,3],"--":[8,3],"- .":[4,3,1,3],"--.":[8,3,1,3],"--..":[8,3,1,3,1,3]},h=c.node,i=c.attrs,j=c.rotate(),k=function(a,b){b=e[x.call(b)];if(b){var c=a.attrs["stroke-width"]||"1",f=({round:c,square:c,butt:0})[a.attrs["stroke-linecap"]||d["stroke-linecap"]]||0,g=[],i=b[w];while(i--)g[i]=b[i]*c+(i%2?1:-1)*f;bG(h,{"stroke-dasharray":g[v](",")})}};d[f]("rotation")&&(j=d.rotation);var m=r(j)[s](b);if(m.length-1){m[1]=+m[1];m[2]=+m[2]}else m=null;S(j)&&c.rotate(0,true);for(var n in d){if(d[f](n)){if(!W[f](n))continue;var o=d[n];i[n]=o;switch(n){case"blur":c.blur(o);break;case"rotation":c.rotate(o,true);break;case"href":case"title":case"target":var t=h.parentNode;if(x.call(t.tagName)!="a"){var u=bG("a");t.insertBefore(u,h);u[l](h);t=u}n=="target"&&o=="blank"?t.setAttributeNS(c.paper.xlink,"show","new"):t.setAttributeNS(c.paper.xlink,n,o);break;case"cursor":h.style.cursor=o;break;case"clip-rect":var y=r(o)[s](b);if(y[w]==4){c.clip&&c.clip.parentNode.parentNode.removeChild(c.clip.parentNode);var z=bG("clipPath"),A=bG("rect");z.id=bh();bG(A,{x:y[0],y:y[1],width:y[2],height:y[3]});z[l](A);c.paper.defs[l](z);bG(h,{"clip-path":"url(#"+z.id+")"});c.clip=A}if(!o){var B=g.getElementById(h.getAttribute("clip-path")[Y](/(^url\(#|\)$)/g,p));B&&B.parentNode.removeChild(B);bG(h,{"clip-path":p});delete c.clip}break;case"path":c.type=="path"&&bG(h,{d:o?i.path=bq(o):"M0,0"});break;case"width":h[R](n,o);if(i.fx){n="x";o=i.x}else break;case"x":i.fx&&(o=-i.x-(i.width||0));case"rx":if(n=="rx"&&c.type=="rect")break;case"cx":m&&(n=="x"||n=="cx")&&(m[1]+=o-i[n]);h[R](n,o);c.pattern&&bJ(c);break;case"height":h[R](n,o);if(i.fy){n="y";o=i.y}else break;case"y":i.fy&&(o=-i.y-(i.height||0));case"ry":if(n=="ry"&&c.type=="rect")break;case"cy":m&&(n=="y"||n=="cy")&&(m[2]+=o-i[n]);h[R](n,o);c.pattern&&bJ(c);break;case"r":c.type=="rect"?bG(h,{rx:o,ry:o}):h[R](n,o);break;case"src":c.type=="image"&&h.setAttributeNS(c.paper.xlink,"href",o);break;case"stroke-width":h.style.strokeWidth=o;h[R](n,o);i["stroke-dasharray"]&&k(c,i["stroke-dasharray"]);break;case"stroke-dasharray":k(c,o);break;case"translation":var C=r(o)[s](b);C[0]=+C[0]||0;C[1]=+C[1]||0;if(m){m[1]+=C[0];m[2]+=C[1]}cz.call(c,C[0],C[1]);break;case"scale":C=r(o)[s](b);c.scale(+C[0]||1,+C[1]||+C[0]||1,isNaN(S(C[2]))?null:+C[2],isNaN(S(C[3]))?null:+C[3]);break;case I:var D=r(o).match(M);if(D){z=bG("pattern");var E=bG("image");z.id=bh();bG(z,{x:0,y:0,patternUnits:"userSpaceOnUse",height:1,width:1});bG(E,{x:0,y:0});E.setAttributeNS(c.paper.xlink,"href",D[1]);z[l](E);var F=g.createElement("img");F.style.cssText="position:absolute;left:-9999em;top-9999em";F.onload=function(){bG(z,{width:this.offsetWidth,height:this.offsetHeight});bG(E,{width:this.offsetWidth,height:this.offsetHeight});g.body.removeChild(this);c.paper.safari()};g.body[l](F);F.src=D[1];c.paper.defs[l](z);h.style.fill="url(#"+z.id+")";bG(h,{fill:"url(#"+z.id+")"});c.pattern=z;c.pattern&&bJ(c);break}var G=a.getRGB(o);if(G.error)if((({circle:1,ellipse:1})[f](c.type)||r(o).charAt()!="r")&&bI(h,o,c.paper)){i.gradient=o;i.fill="none";break}else{delete d.gradient;delete i.gradient;!a.is(i.opacity,"undefined")&&a.is(d.opacity,"undefined")&&bG(h,{opacity:i.opacity});!a.is(i["fill-opacity"],"undefined")&&a.is(d["fill-opacity"],"undefined")&&bG(h,{"fill-opacity":i["fill-opacity"]})}G[f]("opacity")&&bG(h,{"fill-opacity":G.opacity>1?G.opacity/100:G.opacity});case"stroke":G=a.getRGB(o);h[R](n,G.hex);n=="stroke"&&G[f]("opacity")&&bG(h,{"stroke-opacity":G.opacity>1?G.opacity/100:G.opacity});break;case"gradient":(({circle:1,ellipse:1})[f](c.type)||r(o).charAt()!="r")&&bI(h,o,c.paper);break;case"opacity":i.gradient&&!i[f]("stroke-opacity")&&bG(h,{"stroke-opacity":o>1?o/100:o});case"fill-opacity":if(i.gradient){var H=g.getElementById(h.getAttribute(I)[Y](/^url\(#|\)$/g,p));if(H){var J=H.getElementsByTagName("stop");J[J[w]-1][R]("stop-opacity",o)}break}default:n=="font-size"&&(o=T(o,10)+"px");var K=n[Y](/(\-.)/g,function(a){return V.call(a.substring(1))});h.style[K]=o;h[R](n,o);break}}}bM(c,d);m?c.rotate(m.join(q)):S(j)&&c.rotate(j,true)},bL=1.2,bM=function(b,c){if(b.type!="text"||!(c[f]("text")||c[f]("font")||c[f]("font-size")||c[f]("x")||c[f]("y")))return;var d=b.attrs,e=b.node,h=e.firstChild?T(g.defaultView.getComputedStyle(e.firstChild,p).getPropertyValue("font-size"),10):10;if(c[f]("text")){d.text=c.text;while(e.firstChild)e.removeChild(e.firstChild);var i=r(c.text)[s]("\n");for(var j=0,k=i[w];j<k;j++)if(i[j]){var m=bG("tspan");j&&bG(m,{dy:h*bL,x:d.x});m[l](g.createTextNode(i[j]));e[l](m)}}else{i=e.getElementsByTagName("tspan");for(j=0,k=i[w];j<k;j++)j&&bG(i[j],{dy:h*bL,x:d.x})}bG(e,{y:d.y});var n=b.getBBox(),o=d.y-(n.y+n.height/2);o&&a.is(o,"finite")&&bG(e,{y:d.y+o})},bN=function(b,c){var d=0,e=0;this[0]=b;this.id=a._oid++;this.node=b;b.raphael=this;this.paper=c;this.attrs=this.attrs||{};this.transformations=[];this._={tx:0,ty:0,rt:{deg:0,cx:0,cy:0},sx:1,sy:1};!c.bottom&&(c.bottom=this);this.prev=c.top;c.top&&(c.top.next=this);c.top=this;this.next=null},bO=bN[e];bN[e].rotate=function(c,d,e){if(this.removed)return this;if(c==null){if(this._.rt.cx)return[this._.rt.deg,this._.rt.cx,this._.rt.cy][v](q);return this._.rt.deg}var f=this.getBBox();c=r(c)[s](b);if(c[w]-1){d=S(c[1]);e=S(c[2])}c=S(c[0]);d!=null&&d!==false?this._.rt.deg=c:this._.rt.deg+=c;e==null&&(d=null);this._.rt.cx=d;this._.rt.cy=e;d=d==null?f.x+f.width/2:d;e=e==null?f.y+f.height/2:e;if(this._.rt.deg){this.transformations[0]=a.format("rotate({0} {1} {2})",this._.rt.deg,d,e);this.clip&&bG(this.clip,{transform:a.format("rotate({0} {1} {2})",-this._.rt.deg,d,e)})}else{this.transformations[0]=p;this.clip&&bG(this.clip,{transform:p})}bG(this.node,{transform:this.transformations[v](q)});return this};bN[e].hide=function(){!this.removed&&(this.node.style.display="none");return this};bN[e].show=function(){!this.removed&&(this.node.style.display="");return this};bN[e].remove=function(){if(this.removed)return;bA(this,this.paper);this.node.parentNode.removeChild(this.node);for(var a in this)delete this[a];this.removed=true};bN[e].getBBox=function(){if(this.removed)return this;if(this.type=="path")return bn(this.attrs.path);if(this.node.style.display=="none"){this.show();var a=true}var b={};try{b=this.node.getBBox()}catch(a){}finally{b=b||{}}if(this.type=="text"){b={x:b.x,y:Infinity,width:0,height:0};for(var c=0,d=this.node.getNumberOfChars();c<d;c++){var e=this.node.getExtentOfChar(c);e.y<b.y&&(b.y=e.y);e.y+e.height-b.y>b.height&&(b.height=e.y+e.height-b.y);e.x+e.width-b.x>b.width&&(b.width=e.x+e.width-b.x)}}a&&this.hide();return b};bN[e].attr=function(b,c){if(this.removed)return this;if(b==null){var d={};for(var e in this.attrs)this.attrs[f](e)&&(d[e]=this.attrs[e]);this._.rt.deg&&(d.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(d.scale=this.scale());d.gradient&&d.fill=="none"&&(d.fill=d.gradient)&&delete d.gradient;return d}if(c==null&&a.is(b,F)){if(b=="translation")return cz.call(this);if(b=="rotation")return this.rotate();if(b=="scale")return this.scale();if(b==I&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;return this.attrs[b]}if(c==null&&a.is(b,G)){var g={};for(var h=0,i=b.length;h<i;h++)g[b[h]]=this.attr(b[h]);return g}if(c!=null){var j={};j[b]=c}else b!=null&&a.is(b,"object")&&(j=b);for(var k in this.paper.customAttributes)if(this.paper.customAttributes[f](k)&&j[f](k)&&a.is(this.paper.customAttributes[k],"function")){var l=this.paper.customAttributes[k].apply(this,[][n](j[k]));this.attrs[k]=j[k];for(var m in l)l[f](m)&&(j[m]=l[m])}bK(this,j);return this};bN[e].toFront=function(){if(this.removed)return this;this.node.parentNode[l](this.node);var a=this.paper;a.top!=this&&bB(this,a);return this};bN[e].toBack=function(){if(this.removed)return this;if(this.node.parentNode.firstChild!=this.node){this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild);bC(this,this.paper);var a=this.paper}return this};bN[e].insertAfter=function(a){if(this.removed)return this;var b=a.node||a[a.length-1].node;b.nextSibling?b.parentNode.insertBefore(this.node,b.nextSibling):b.parentNode[l](this.node);bD(this,a,this.paper);return this};bN[e].insertBefore=function(a){if(this.removed)return this;var b=a.node||a[0].node;b.parentNode.insertBefore(this.node,b);bE(this,a,this.paper);return this};bN[e].blur=function(a){var b=this;if(+a!==0){var c=bG("filter"),d=bG("feGaussianBlur");b.attrs.blur=a;c.id=bh();bG(d,{stdDeviation:+a||1.5});c.appendChild(d);b.paper.defs.appendChild(c);b._blur=c;bG(b.node,{filter:"url(#"+c.id+")"})}else{if(b._blur){b._blur.parentNode.removeChild(b._blur);delete b._blur;delete b.attrs.blur}b.node.removeAttribute("filter")}};var bP=function(a,b,c,d){var e=bG("circle");a.canvas&&a.canvas[l](e);var f=new bN(e,a);f.attrs={cx:b,cy:c,r:d,fill:"none",stroke:"#000"};f.type="circle";bG(e,f.attrs);return f},bQ=function(a,b,c,d,e,f){var g=bG("rect");a.canvas&&a.canvas[l](g);var h=new bN(g,a);h.attrs={x:b,y:c,width:d,height:e,r:f||0,rx:f||0,ry:f||0,fill:"none",stroke:"#000"};h.type="rect";bG(g,h.attrs);return h},bR=function(a,b,c,d,e){var f=bG("ellipse");a.canvas&&a.canvas[l](f);var g=new bN(f,a);g.attrs={cx:b,cy:c,rx:d,ry:e,fill:"none",stroke:"#000"};g.type="ellipse";bG(f,g.attrs);return g},bS=function(a,b,c,d,e,f){var g=bG("image");bG(g,{x:c,y:d,width:e,height:f,preserveAspectRatio:"none"});g.setAttributeNS(a.xlink,"href",b);a.canvas&&a.canvas[l](g);var h=new bN(g,a);h.attrs={x:c,y:d,width:e,height:f,src:b};h.type="image";return h},bT=function(a,b,c,d){var e=bG("text");bG(e,{x:b,y:c,"text-anchor":"middle"});a.canvas&&a.canvas[l](e);var f=new bN(e,a);f.attrs={x:b,y:c,"text-anchor":"middle",text:d,font:W.font,stroke:"none",fill:"#000"};f.type="text";bK(f,f.attrs);return f},bU=function(a,b){this.width=a||this.width;this.height=b||this.height;this.canvas[R]("width",this.width);this.canvas[R]("height",this.height);return this},bV=function(){var b=by[m](0,arguments),c=b&&b.container,d=b.x,e=b.y,f=b.width,h=b.height;if(!c)throw new Error("SVG container not found.");var i=bG("svg");d=d||0;e=e||0;f=f||512;h=h||342;bG(i,{xmlns:"http://www.w3.org/2000/svg",version:1.1,width:f,height:h});if(c==1){i.style.cssText="position:absolute;left:"+d+"px;top:"+e+"px";g.body[l](i)}else c.firstChild?c.insertBefore(i,c.firstChild):c[l](i);c=new j;c.width=f;c.height=h;c.canvas=i;bz.call(c,c,a.fn);c.clear();return c};k.clear=function(){var a=this.canvas;while(a.firstChild)a.removeChild(a.firstChild);this.bottom=this.top=null;(this.desc=bG("desc"))[l](g.createTextNode("Created with RaphaÃ«l"));a[l](this.desc);a[l](this.defs=bG("defs"))};k.remove=function(){this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]=bF(a)}}if(a.vml){var bW={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},bX=/([clmz]),?([^clmz]*)/gi,bY=/ progid:\S+Blur\([^\)]+\)/g,bZ=/-?[^,\s-]+/g,b$=1000+q+1000,b_=10,ca={path:1,rect:1},cb=function(a){var b=/[ahqstv]/ig,c=bq;r(a).match(b)&&(c=bw);b=/[clmz]/g;if(c==bq&&!r(a).match(b)){var d=r(a)[Y](bX,function(a,b,c){var d=[],e=x.call(b)=="m",f=bW[b];c[Y](bZ,function(a){if(e&&d[w]==2){f+=d+bW[b=="m"?"l":"L"];d=[]}d[L](Q(a*b_))});return f+d});return d}var e=c(a),f,g;d=[];for(var h=0,i=e[w];h<i;h++){f=e[h];g=x.call(e[h][0]);g=="z"&&(g="x");for(var j=1,k=f[w];j<k;j++)g+=Q(f[j]*b_)+(j!=k-1?",":p);d[L](g)}return d[v](q)};a[H]=function(){return"Your browser doesnât support SVG. Falling down to VML.\nYou are running RaphaÃ«l "+this.version};bH=function(a,b){var c=cd("group");c.style.cssText="position:absolute;left:0;top:0;width:"+b.width+"px;height:"+b.height+"px";c.coordsize=b.coordsize;c.coordorigin=b.coordorigin;var d=cd("shape"),e=d.style;e.width=b.width+"px";e.height=b.height+"px";d.coordsize=b$;d.coordorigin=b.coordorigin;c[l](d);var f=new bN(d,c,b),g={fill:"none",stroke:"#000"};a&&(g.path=a);f.type="path";f.path=[];f.Path=p;bK(f,g);b.canvas[l](c);return f};bK=function(c,d){c.attrs=c.attrs||{};var e=c.node,h=c.attrs,i=e.style,j,k=(d.x!=h.x||d.y!=h.y||d.width!=h.width||d.height!=h.height||d.r!=h.r)&&c.type=="rect",m=c;for(var n in d)d[f](n)&&(h[n]=d[n]);if(k){h.path=cc(h.x,h.y,h.width,h.height,h.r);c.X=h.x;c.Y=h.y;c.W=h.width;c.H=h.height}d.href&&(e.href=d.href);d.title&&(e.title=d.title);d.target&&(e.target=d.target);d.cursor&&(i.cursor=d.cursor);"blur"in d&&c.blur(d.blur);if(d.path&&c.type=="path"||k)e.path=cb(h.path);d.rotation!=null&&c.rotate(d.rotation,true);if(d.translation){j=r(d.translation)[s](b);cz.call(c,j[0],j[1]);if(c._.rt.cx!=null){c._.rt.cx+=+j[0];c._.rt.cy+=+j[1];c.setBox(c.attrs,j[0],j[1])}}if(d.scale){j=r(d.scale)[s](b);c.scale(+j[0]||1,+j[1]||+j[0]||1,+j[2]||null,+j[3]||null)}if("clip-rect"in d){var o=r(d["clip-rect"])[s](b);if(o[w]==4){o[2]=+o[2]+ +o[0];o[3]=+o[3]+ +o[1];var q=e.clipRect||g.createElement("div"),t=q.style,u=e.parentNode;t.clip=a.format("rect({1}px {2}px {3}px {0}px)",o);if(!e.clipRect){t.position="absolute";t.top=0;t.left=0;t.width=c.paper.width+"px";t.height=c.paper.height+"px";u.parentNode.insertBefore(q,u);q[l](u);e.clipRect=q}}d["clip-rect"]||e.clipRect&&(e.clipRect.style.clip=p)}c.type=="image"&&d.src&&(e.src=d.src);if(c.type=="image"&&d.opacity){e.filterOpacity=U+".Alpha(opacity="+d.opacity*100+")";i.filter=(e.filterMatrix||p)+(e.filterOpacity||p)}d.font&&(i.font=d.font);d["font-family"]&&(i.fontFamily="\""+d["font-family"][s](",")[0][Y](/^['"]+|['"]+$/g,p)+"\"");d["font-size"]&&(i.fontSize=d["font-size"]);d["font-weight"]&&(i.fontWeight=d["font-weight"]);d["font-style"]&&(i.fontStyle=d["font-style"]);if(d.opacity!=null||d["stroke-width"]!=null||d.fill!=null||d.stroke!=null||d["stroke-width"]!=null||d["stroke-opacity"]!=null||d["fill-opacity"]!=null||d["stroke-dasharray"]!=null||d["stroke-miterlimit"]!=null||d["stroke-linejoin"]!=null||d["stroke-linecap"]!=null){e=c.shape||e;var v=e.getElementsByTagName(I)&&e.getElementsByTagName(I)[0],x=false;!v&&(x=v=cd(I));if("fill-opacity"in d||"opacity"in d){var y=((+h["fill-opacity"]+1||2)-1)*((+h.opacity+1||2)-1)*((+a.getRGB(d.fill).o+1||2)-1);y=A(z(y,0),1);v.opacity=y}d.fill&&(v.on=true);if(v.on==null||d.fill=="none")v.on=false;if(v.on&&d.fill){var B=d.fill.match(M);if(B){v.src=B[1];v.type="tile"}else{v.color=a.getRGB(d.fill).hex;v.src=p;v.type="solid";if(a.getRGB(d.fill).error&&(m.type in{circle:1,ellipse:1}||r(d.fill).charAt()!="r")&&bI(m,d.fill)){h.fill="none";h.gradient=d.fill}}}x&&e[l](v);var C=e.getElementsByTagName("stroke")&&e.getElementsByTagName("stroke")[0],D=false;!C&&(D=C=cd("stroke"));if(d.stroke&&d.stroke!="none"||d["stroke-width"]||d["stroke-opacity"]!=null||d["stroke-dasharray"]||d["stroke-miterlimit"]||d["stroke-linejoin"]||d["stroke-linecap"])C.on=true;(d.stroke=="none"||C.on==null||d.stroke==0||d["stroke-width"]==0)&&(C.on=false);var E=a.getRGB(d.stroke);C.on&&d.stroke&&(C.color=E.hex);y=((+h["stroke-opacity"]+1||2)-1)*((+h.opacity+1||2)-1)*((+E.o+1||2)-1);var F=(S(d["stroke-width"])||1)*0.75;y=A(z(y,0),1);d["stroke-width"]==null&&(F=h["stroke-width"]);d["stroke-width"]&&(C.weight=F);F&&F<1&&(y*=F)&&(C.weight=1);C.opacity=y;d["stroke-linejoin"]&&(C.joinstyle=d["stroke-linejoin"]||"miter");C.miterlimit=d["stroke-miterlimit"]||8;d["stroke-linecap"]&&(C.endcap=d["stroke-linecap"]=="butt"?"flat":d["stroke-linecap"]=="square"?"square":"round");if(d["stroke-dasharray"]){var G={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};C.dashstyle=G[f](d["stroke-dasharray"])?G[d["stroke-dasharray"]]:p}D&&e[l](C)}if(m.type=="text"){i=m.paper.span.style;h.font&&(i.font=h.font);h["font-family"]&&(i.fontFamily=h["font-family"]);h["font-size"]&&(i.fontSize=h["font-size"]);h["font-weight"]&&(i.fontWeight=h["font-weight"]);h["font-style"]&&(i.fontStyle=h["font-style"]);m.node.string&&(m.paper.span.innerHTML=r(m.node.string)[Y](/</g,"&#60;")[Y](/&/g,"&#38;")[Y](/\n/g,"<br>"));m.W=h.w=m.paper.span.offsetWidth;m.H=h.h=m.paper.span.offsetHeight;m.X=h.x;m.Y=h.y+Q(m.H/2);switch(h["text-anchor"]){case"start":m.node.style["v-text-align"]="left";m.bbx=Q(m.W/2);break;case"end":m.node.style["v-text-align"]="right";m.bbx=-Q(m.W/2);break;default:m.node.style["v-text-align"]="center";break}}};bI=function(a,b){a.attrs=a.attrs||{};var c=a.attrs,d,e="linear",f=".5 .5";a.attrs.gradient=b;b=r(b)[Y](bd,function(a,b,c){e="radial";if(b&&c){b=S(b);c=S(c);C(b-0.5,2)+C(c-0.5,2)>0.25&&(c=y.sqrt(0.25-C(b-0.5,2))*((c>0.5)*2-1)+0.5);f=b+q+c}return p});b=b[s](/\s*\-\s*/);if(e=="linear"){var g=b.shift();g=-S(g);if(isNaN(g))return null}var h=bx(b);if(!h)return null;a=a.shape||a.node;d=a.getElementsByTagName(I)[0]||cd(I);!d.parentNode&&a.appendChild(d);if(h[w]){d.on=true;d.method="none";d.color=h[0].color;d.color2=h[h[w]-1].color;var i=[];for(var j=0,k=h[w];j<k;j++)h[j].offset&&i[L](h[j].offset+q+h[j].color);d.colors&&(d.colors.value=i[w]?i[v]():"0% "+d.color);if(e=="radial"){d.type="gradientradial";d.focus="100%";d.focussize=f;d.focusposition=f}else{d.type="gradient";d.angle=(270-g)%360}}return 1};bN=function(b,c,d){var e=0,f=0,g=0,h=1;this[0]=b;this.id=a._oid++;this.node=b;b.raphael=this;this.X=0;this.Y=0;this.attrs={};this.Group=c;this.paper=d;this._={tx:0,ty:0,rt:{deg:0},sx:1,sy:1};!d.bottom&&(d.bottom=this);this.prev=d.top;d.top&&(d.top.next=this);d.top=this;this.next=null};bO=bN[e];bO.rotate=function(a,c,d){if(this.removed)return this;if(a==null){if(this._.rt.cx)return[this._.rt.deg,this._.rt.cx,this._.rt.cy][v](q);return this._.rt.deg}a=r(a)[s](b);if(a[w]-1){c=S(a[1]);d=S(a[2])}a=S(a[0]);c!=null?this._.rt.deg=a:this._.rt.deg+=a;d==null&&(c=null);this._.rt.cx=c;this._.rt.cy=d;this.setBox(this.attrs,c,d);this.Group.style.rotation=this._.rt.deg;return this};bO.setBox=function(a,b,c){if(this.removed)return this;var d=this.Group.style,e=this.shape&&this.shape.style||this.node.style;a=a||{};for(var g in a)a[f](g)&&(this.attrs[g]=a[g]);b=b||this._.rt.cx;c=c||this._.rt.cy;var h=this.attrs,i,j,k,l;switch(this.type){case"circle":i=h.cx-h.r;j=h.cy-h.r;k=l=h.r*2;break;case"ellipse":i=h.cx-h.rx;j=h.cy-h.ry;k=h.rx*2;l=h.ry*2;break;case"image":i=+h.x;j=+h.y;k=h.width||0;l=h.height||0;break;case"text":this.textpath.v=["m",Q(h.x),", ",Q(h.y-2),"l",Q(h.x)+1,", ",Q(h.y-2)][v](p);i=h.x-Q(this.W/2);j=h.y-this.H/2;k=this.W;l=this.H;break;case"rect":case"path":if(this.attrs.path){var m=bn(this.attrs.path);i=m.x;j=m.y;k=m.width;l=m.height}else{i=0;j=0;k=this.paper.width;l=this.paper.height}break;default:i=0;j=0;k=this.paper.width;l=this.paper.height;break}b=b==null?i+k/2:b;c=c==null?j+l/2:c;var n=b-this.paper.width/2,o=c-this.paper.height/2,q;d.left!=(q=n+"px")&&(d.left=q);d.top!=(q=o+"px")&&(d.top=q);this.X=ca[f](this.type)?-n:i;this.Y=ca[f](this.type)?-o:j;this.W=k;this.H=l;if(ca[f](this.type)){e.left!=(q=-n*b_+"px")&&(e.left=q);e.top!=(q=-o*b_+"px")&&(e.top=q)}else if(this.type=="text"){e.left!=(q=-n+"px")&&(e.left=q);e.top!=(q=-o+"px")&&(e.top=q)}else{d.width!=(q=this.paper.width+"px")&&(d.width=q);d.height!=(q=this.paper.height+"px")&&(d.height=q);e.left!=(q=i-n+"px")&&(e.left=q);e.top!=(q=j-o+"px")&&(e.top=q);e.width!=(q=k+"px")&&(e.width=q);e.height!=(q=l+"px")&&(e.height=q)}};bO.hide=function(){!this.removed&&(this.Group.style.display="none");return this};bO.show=function(){!this.removed&&(this.Group.style.display="block");return this};bO.getBBox=function(){if(this.removed)return this;if(ca[f](this.type))return bn(this.attrs.path);return{x:this.X+(this.bbx||0),y:this.Y,width:this.W,height:this.H}};bO.remove=function(){if(this.removed)return;bA(this,this.paper);this.node.parentNode.removeChild(this.node);this.Group.parentNode.removeChild(this.Group);this.shape&&this.shape.parentNode.removeChild(this.shape);for(var a in this)delete this[a];this.removed=true};bO.attr=function(b,c){if(this.removed)return this;if(b==null){var d={};for(var e in this.attrs)this.attrs[f](e)&&(d[e]=this.attrs[e]);this._.rt.deg&&(d.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(d.scale=this.scale());d.gradient&&d.fill=="none"&&(d.fill=d.gradient)&&delete d.gradient;return d}if(c==null&&a.is(b,"string")){if(b=="translation")return cz.call(this);if(b=="rotation")return this.rotate();if(b=="scale")return this.scale();if(b==I&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;return this.attrs[b]}if(this.attrs&&c==null&&a.is(b,G)){var g,h={};for(e=0,g=b[w];e<g;e++)h[b[e]]=this.attr(b[e]);return h}var i;if(c!=null){i={};i[b]=c}c==null&&a.is(b,"object")&&(i=b);if(i){for(var j in this.paper.customAttributes)if(this.paper.customAttributes[f](j)&&i[f](j)&&a.is(this.paper.customAttributes[j],"function")){var k=this.paper.customAttributes[j].apply(this,[][n](i[j]));this.attrs[j]=i[j];for(var l in k)k[f](l)&&(i[l]=k[l])}i.text&&this.type=="text"&&(this.node.string=i.text);bK(this,i);i.gradient&&(({circle:1,ellipse:1})[f](this.type)||r(i.gradient).charAt()!="r")&&bI(this,i.gradient);(!ca[f](this.type)||this._.rt.deg)&&this.setBox(this.attrs)}return this};bO.toFront=function(){!this.removed&&this.Group.parentNode[l](this.Group);this.paper.top!=this&&bB(this,this.paper);return this};bO.toBack=function(){if(this.removed)return this;if(this.Group.parentNode.firstChild!=this.Group){this.Group.parentNode.insertBefore(this.Group,this.Group.parentNode.firstChild);bC(this,this.paper)}return this};bO.insertAfter=function(a){if(this.removed)return this;a.constructor==cC&&(a=a[a.length-1]);a.Group.nextSibling?a.Group.parentNode.insertBefore(this.Group,a.Group.nextSibling):a.Group.parentNode[l](this.Group);bD(this,a,this.paper);return this};bO.insertBefore=function(a){if(this.removed)return this;a.constructor==cC&&(a=a[0]);a.Group.parentNode.insertBefore(this.Group,a.Group);bE(this,a,this.paper);return this};bO.blur=function(b){var c=this.node.runtimeStyle,d=c.filter;d=d.replace(bY,p);if(+b!==0){this.attrs.blur=b;c.filter=d+q+U+".Blur(pixelradius="+(+b||1.5)+")";c.margin=a.format("-{0}px 0 0 -{0}px",Q(+b||1.5))}else{c.filter=d;c.margin=0;delete this.attrs.blur}};bP=function(a,b,c,d){var e=cd("group"),f=cd("oval"),g=f.style;e.style.cssText="position:absolute;left:0;top:0;width:"+a.width+"px;height:"+a.height+"px";e.coordsize=b$;e.coordorigin=a.coordorigin;e[l](f);var h=new bN(f,e,a);h.type="circle";bK(h,{stroke:"#000",fill:"none"});h.attrs.cx=b;h.attrs.cy=c;h.attrs.r=d;h.setBox({x:b-d,y:c-d,width:d*2,height:d*2});a.canvas[l](e);return h};function cc(b,c,d,e,f){return f?a.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z",b+f,c,d-f*2,f,-f,e-f*2,f*2-d,f*2-e):a.format("M{0},{1}l{2},0,0,{3},{4},0z",b,c,d,e,-d)}bQ=function(a,b,c,d,e,f){var g=cc(b,c,d,e,f),h=a.path(g),i=h.attrs;h.X=i.x=b;h.Y=i.y=c;h.W=i.width=d;h.H=i.height=e;i.r=f;i.path=g;h.type="rect";return h};bR=function(a,b,c,d,e){var f=cd("group"),g=cd("oval"),h=g.style;f.style.cssText="position:absolute;left:0;top:0;width:"+a.width+"px;height:"+a.height+"px";f.coordsize=b$;f.coordorigin=a.coordorigin;f[l](g);var i=new bN(g,f,a);i.type="ellipse";bK(i,{stroke:"#000"});i.attrs.cx=b;i.attrs.cy=c;i.attrs.rx=d;i.attrs.ry=e;i.setBox({x:b-d,y:c-e,width:d*2,height:e*2});a.canvas[l](f);return i};bS=function(a,b,c,d,e,f){var g=cd("group"),h=cd("image");g.style.cssText="position:absolute;left:0;top:0;width:"+a.width+"px;height:"+a.height+"px";g.coordsize=b$;g.coordorigin=a.coordorigin;h.src=b;g[l](h);var i=new bN(h,g,a);i.type="image";i.attrs.src=b;i.attrs.x=c;i.attrs.y=d;i.attrs.w=e;i.attrs.h=f;i.setBox({x:c,y:d,width:e,height:f});a.canvas[l](g);return i};bT=function(b,c,d,e){var f=cd("group"),g=cd("shape"),h=g.style,i=cd("path"),j=i.style,k=cd("textpath");f.style.cssText="position:absolute;left:0;top:0;width:"+b.width+"px;height:"+b.height+"px";f.coordsize=b$;f.coordorigin=b.coordorigin;i.v=a.format("m{0},{1}l{2},{1}",Q(c*10),Q(d*10),Q(c*10)+1);i.textpathok=true;h.width=b.width;h.height=b.height;k.string=r(e);k.on=true;g[l](k);g[l](i);f[l](g);var m=new bN(k,f,b);m.shape=g;m.textpath=i;m.type="text";m.attrs.text=e;m.attrs.x=c;m.attrs.y=d;m.attrs.w=1;m.attrs.h=1;bK(m,{font:W.font,stroke:"none",fill:"#000"});m.setBox();b.canvas[l](f);return m};bU=function(a,b){var c=this.canvas.style;a==+a&&(a+="px");b==+b&&(b+="px");c.width=a;c.height=b;c.clip="rect(0 "+a+" "+b+" 0)";return this};var cd;g.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)");try{!g.namespaces.rvml&&g.namespaces.add("rvml","urn:schemas-microsoft-com:vml");cd=function(a){return g.createElement("<rvml:"+a+" class=\"rvml\">")}}catch(a){cd=function(a){return g.createElement("<"+a+" xmlns=\"urn:schemas-microsoft.com:vml\" class=\"rvml\">")}}bV=function(){var b=by[m](0,arguments),c=b.container,d=b.height,e,f=b.width,h=b.x,i=b.y;if(!c)throw new Error("VML container not found.");var k=new j,n=k.canvas=g.createElement("div"),o=n.style;h=h||0;i=i||0;f=f||512;d=d||342;f==+f&&(f+="px");d==+d&&(d+="px");k.width=1000;k.height=1000;k.coordsize=b_*1000+q+b_*1000;k.coordorigin="0 0";k.span=g.createElement("span");k.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";n[l](k.span);o.cssText=a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",f,d);if(c==1){g.body[l](n);o.left=h+"px";o.top=i+"px";o.position="absolute"}else c.firstChild?c.insertBefore(n,c.firstChild):c[l](n);bz.call(k,k,a.fn);return k};k.clear=function(){this.canvas.innerHTML=p;this.span=g.createElement("span");this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";this.canvas[l](this.span);this.bottom=this.top=null};k.remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var a in this)this[a]=bF(a);return true}}var ce=navigator.userAgent.match(/Version\\x2f(.*?)\s/);navigator.vendor=="Apple Computer, Inc."&&(ce&&ce[1]<4||navigator.platform.slice(0,2)=="iP")?k.safari=function(){var a=this.rect(-99,-99,this.width+99,this.height+99).attr({stroke:"none"});h.setTimeout(function(){a.remove()})}:k.safari=function(){};var cf=function(){this.returnValue=false},cg=function(){return this.originalEvent.preventDefault()},ch=function(){this.cancelBubble=true},ci=function(){return this.originalEvent.stopPropagation()},cj=(function(){{if(g.addEventListener)return function(a,b,c,d){var e=o&&u[b]?u[b]:b,g=function(e){if(o&&u[f](b))for(var g=0,h=e.targetTouches&&e.targetTouches.length;g<h;g++){if(e.targetTouches[g].target==a){var i=e;e=e.targetTouches[g];e.originalEvent=i;e.preventDefault=cg;e.stopPropagation=ci;break}}return c.call(d,e)};a.addEventListener(e,g,false);return function(){a.removeEventListener(e,g,false);return true}};if(g.attachEvent)return function(a,b,c,d){var e=function(a){a=a||h.event;a.preventDefault=a.preventDefault||cf;a.stopPropagation=a.stopPropagation||ch;return c.call(d,a)};a.attachEvent("on"+b,e);var f=function(){a.detachEvent("on"+b,e);return true};return f}}})(),ck=[],cl=function(a){var b=a.clientX,c=a.clientY,d=g.documentElement.scrollTop||g.body.scrollTop,e=g.documentElement.scrollLeft||g.body.scrollLeft,f,h=ck.length;while(h--){f=ck[h];if(o){var i=a.touches.length,j;while(i--){j=a.touches[i];if(j.identifier==f.el._drag.id){b=j.clientX;c=j.clientY;(a.originalEvent?a.originalEvent:a).preventDefault();break}}}else a.preventDefault();b+=e;c+=d;f.move&&f.move.call(f.move_scope||f.el,b-f.el._drag.x,c-f.el._drag.y,b,c,a)}},cm=function(b){a.unmousemove(cl).unmouseup(cm);var c=ck.length,d;while(c--){d=ck[c];d.el._drag={};d.end&&d.end.call(d.end_scope||d.start_scope||d.move_scope||d.el,b)}ck=[]};for(var cn=t[w];cn--;)(function(b){a[b]=bN[e][b]=function(c,d){if(a.is(c,"function")){this.events=this.events||[];this.events.push({name:b,f:c,unbind:cj(this.shape||this.node||g,b,c,d||this)})}return this};a["un"+b]=bN[e]["un"+b]=function(a){var c=this.events,d=c[w];while(d--)if(c[d].name==b&&c[d].f==a){c[d].unbind();c.splice(d,1);!c.length&&delete this.events;return this}return this}})(t[cn]);bO.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)};bO.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};bO.drag=function(b,c,d,e,f,h){this._drag={};this.mousedown(function(i){(i.originalEvent||i).preventDefault();var j=g.documentElement.scrollTop||g.body.scrollTop,k=g.documentElement.scrollLeft||g.body.scrollLeft;this._drag.x=i.clientX+k;this._drag.y=i.clientY+j;this._drag.id=i.identifier;c&&c.call(f||e||this,i.clientX+k,i.clientY+j,i);!ck.length&&a.mousemove(cl).mouseup(cm);ck.push({el:this,move:b,end:d,move_scope:e,start_scope:f,end_scope:h})});return this};bO.undrag=function(b,c,d){var e=ck.length;while(e--)ck[e].el==this&&(ck[e].move==b&&ck[e].end==d)&&ck.splice(e++,1);!ck.length&&a.unmousemove(cl).unmouseup(cm)};k.circle=function(a,b,c){return bP(this,a||0,b||0,c||0)};k.rect=function(a,b,c,d,e){return bQ(this,a||0,b||0,c||0,d||0,e||0)};k.ellipse=function(a,b,c,d){return bR(this,a||0,b||0,c||0,d||0)};k.path=function(b){b&&!a.is(b,F)&&!a.is(b[0],G)&&(b+=p);return bH(a.format[m](a,arguments),this)};k.image=function(a,b,c,d,e){return bS(this,a||"about:blank",b||0,c||0,d||0,e||0)};k.text=function(a,b,c){return bT(this,a||0,b||0,r(c))};k.set=function(a){arguments[w]>1&&(a=Array[e].splice.call(arguments,0,arguments[w]));return new cC(a)};k.setSize=bU;k.top=k.bottom=null;k.raphael=a;function co(){return this.x+q+this.y}bO.resetScale=function(){if(this.removed)return this;this._.sx=1;this._.sy=1;this.attrs.scale="1 1"};bO.scale=function(a,b,c,d){if(this.removed)return this;if(a==null&&b==null)return{x:this._.sx,y:this._.sy,toString:co};b=b||a;!(+b)&&(b=a);var e,f,g,h,i=this.attrs;if(a!=0){var j=this.getBBox(),k=j.x+j.width/2,l=j.y+j.height/2,m=B(a/this._.sx),o=B(b/this._.sy);c=+c||c==0?c:k;d=+d||d==0?d:l;var r=this._.sx>0,s=this._.sy>0,t=~(~(a/B(a))),u=~(~(b/B(b))),x=m*t,y=o*u,z=this.node.style,A=c+B(k-c)*x*(k>c==r?1:-1),C=d+B(l-d)*y*(l>d==s?1:-1),D=a*t>b*u?o:m;switch(this.type){case"rect":case"image":var E=i.width*m,F=i.height*o;this.attr({height:F,r:i.r*D,width:E,x:A-E/2,y:C-F/2});break;case"circle":case"ellipse":this.attr({rx:i.rx*m,ry:i.ry*o,r:i.r*D,cx:A,cy:C});break;case"text":this.attr({x:A,y:C});break;case"path":var G=bp(i.path),H=true,I=r?x:m,J=s?y:o;for(var K=0,L=G[w];K<L;K++){var M=G[K],N=V.call(M[0]);{if(N=="M"&&H)continue;H=false}if(N=="A"){M[G[K][w]-2]*=I;M[G[K][w]-1]*=J;M[1]*=m;M[2]*=o;M[5]=+(t+u?!(!(+M[5])):!(+M[5]))}else if(N=="H")for(var O=1,P=M[w];O<P;O++)M[O]*=I;else if(N=="V")for(O=1,P=M[w];O<P;O++)M[O]*=J;else for(O=1,P=M[w];O<P;O++)M[O]*=O%2?I:J}var Q=bn(G);e=A-Q.x-Q.width/2;f=C-Q.y-Q.height/2;G[0][1]+=e;G[0][2]+=f;this.attr({path:G});break}if(this.type in{text:1,image:1}&&(t!=1||u!=1))if(this.transformations){this.transformations[2]="scale("[n](t,",",u,")");this.node[R]("transform",this.transformations[v](q));e=t==-1?-i.x-(E||0):i.x;f=u==-1?-i.y-(F||0):i.y;this.attr({x:e,y:f});i.fx=t-1;i.fy=u-1}else{this.node.filterMatrix=U+".Matrix(M11="[n](t,", M12=0, M21=0, M22=",u,", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')");z.filter=(this.node.filterMatrix||p)+(this.node.filterOpacity||p)}else if(this.transformations){this.transformations[2]=p;this.node[R]("transform",this.transformations[v](q));i.fx=0;i.fy=0}else{this.node.filterMatrix=p;z.filter=(this.node.filterMatrix||p)+(this.node.filterOpacity||p)}i.scale=[a,b,c,d][v](q);this._.sx=a;this._.sy=b}return this};bO.clone=function(){if(this.removed)return null;var a=this.attr();delete a.scale;delete a.translation;return this.paper[this.type]().attr(a)};var cp={},cq=function(b,c,d,e,f,g,h,i,j){var k=0,l=100,m=[b,c,d,e,f,g,h,i].join(),n=cp[m],o,p;!n&&(cp[m]=n={data:[]});n.timer&&clearTimeout(n.timer);n.timer=setTimeout(function(){delete cp[m]},2000);if(j!=null){var q=cq(b,c,d,e,f,g,h,i);l=~(~q)*10}for(var r=0;r<l+1;r++){if(n.data[j]>r)p=n.data[r*l];else{p=a.findDotsAtSegment(b,c,d,e,f,g,h,i,r/l);n.data[r]=p}r&&(k+=C(C(o.x-p.x,2)+C(o.y-p.y,2),0.5));if(j!=null&&k>=j)return p;o=p}if(j==null)return k},cr=function(b,c){return function(d,e,f){d=bw(d);var g,h,i,j,k="",l={},m,n=0;for(var o=0,p=d.length;o<p;o++){i=d[o];if(i[0]=="M"){g=+i[1];h=+i[2]}else{j=cq(g,h,i[1],i[2],i[3],i[4],i[5],i[6]);if(n+j>e){if(c&&!l.start){m=cq(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);k+=["C",m.start.x,m.start.y,m.m.x,m.m.y,m.x,m.y];if(f)return k;l.start=k;k=["M",m.x,m.y+"C",m.n.x,m.n.y,m.end.x,m.end.y,i[5],i[6]][v]();n+=j;g=+i[5];h=+i[6];continue}if(!b&&!c){m=cq(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);return{x:m.x,y:m.y,alpha:m.alpha}}}n+=j;g=+i[5];h=+i[6]}k+=i}l.end=k;m=b?n:c?l:a.findDotsAtSegment(g,h,i[1],i[2],i[3],i[4],i[5],i[6],1);m.alpha&&(m={x:m.x,y:m.y,alpha:m.alpha});return m}},cs=cr(1),ct=cr(),cu=cr(0,1);bO.getTotalLength=function(){if(this.type!="path")return;if(this.node.getTotalLength)return this.node.getTotalLength();return cs(this.attrs.path)};bO.getPointAtLength=function(a){if(this.type!="path")return;return ct(this.attrs.path,a)};bO.getSubpath=function(a,b){if(this.type!="path")return;if(B(this.getTotalLength()-b)<"1e-6")return cu(this.attrs.path,a).end;var c=cu(this.attrs.path,b,1);return a?cu(c,a).end:c};a.easing_formulas={linear:function(a){return a},"<":function(a){return C(a,3)},">":function(a){return C(a-1,3)+1},"<>":function(a){a=a*2;if(a<1)return C(a,3)/2;a-=2;return(C(a,3)+2)/2},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a=a-1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a==0||a==1)return a;var b=0.3,c=b/4;return C(2,-10*a)*y.sin((a-c)*(2*D)/b)+1},bounce:function(a){var b=7.5625,c=2.75,d;if(a<1/c)d=b*a*a;else if(a<2/c){a-=1.5/c;d=b*a*a+0.75}else if(a<2.5/c){a-=2.25/c;d=b*a*a+0.9375}else{a-=2.625/c;d=b*a*a+0.984375}return d}};var cv=[],cw=function(){var b=+(new Date);for(var c=0;c<cv[w];c++){var d=cv[c];if(d.stop||d.el.removed)continue;var e=b-d.start,g=d.ms,h=d.easing,i=d.from,j=d.diff,k=d.to,l=d.t,m=d.el,n={},o;if(e<g){var r=h(e/g);for(var s in i)if(i[f](s)){switch(X[s]){case"along":o=r*g*j[s];k.back&&(o=k.len-o);var t=ct(k[s],o);m.translate(j.sx-j.x||0,j.sy-j.y||0);j.x=t.x;j.y=t.y;m.translate(t.x-j.sx,t.y-j.sy);k.rot&&m.rotate(j.r+t.alpha,t.x,t.y);break;case E:o=+i[s]+r*g*j[s];break;case"colour":o="rgb("+[cy(Q(i[s].r+r*g*j[s].r)),cy(Q(i[s].g+r*g*j[s].g)),cy(Q(i[s].b+r*g*j[s].b))][v](",")+")";break;case"path":o=[];for(var u=0,x=i[s][w];u<x;u++){o[u]=[i[s][u][0]];for(var y=1,z=i[s][u][w];y<z;y++)o[u][y]=+i[s][u][y]+r*g*j[s][u][y];o[u]=o[u][v](q)}o=o[v](q);break;case"csv":switch(s){case"translation":var A=r*g*j[s][0]-l.x,B=r*g*j[s][1]-l.y;l.x+=A;l.y+=B;o=A+q+B;break;case"rotation":o=+i[s][0]+r*g*j[s][0];i[s][1]&&(o+=","+i[s][1]+","+i[s][2]);break;case"scale":o=[+i[s][0]+r*g*j[s][0],+i[s][1]+r*g*j[s][1],2 in k[s]?k[s][2]:p,3 in k[s]?k[s][3]:p][v](q);break;case"clip-rect":o=[];u=4;while(u--)o[u]=+i[s][u]+r*g*j[s][u];break}break;default:var C=[].concat(i[s]);o=[];u=m.paper.customAttributes[s].length;while(u--)o[u]=+C[u]+r*g*j[s][u];break}n[s]=o}m.attr(n);m._run&&m._run.call(m)}else{if(k.along){t=ct(k.along,k.len*!k.back);m.translate(j.sx-(j.x||0)+t.x-j.sx,j.sy-(j.y||0)+t.y-j.sy);k.rot&&m.rotate(j.r+t.alpha,t.x,t.y)}(l.x||l.y)&&m.translate(-l.x,-l.y);k.scale&&(k.scale+=p);m.attr(k);cv.splice(c--,1)}}a.svg&&m&&m.paper&&m.paper.safari();cv[w]&&setTimeout(cw)},cx=function(b,c,d,e,f){var g=d-e;c.timeouts.push(setTimeout(function(){a.is(f,"function")&&f.call(c);c.animate(b,g,b.easing)},e))},cy=function(a){return z(A(a,255),0)},cz=function(a,b){if(a==null)return{x:this._.tx,y:this._.ty,toString:co};this._.tx+=+a;this._.ty+=+b;switch(this.type){case"circle":case"ellipse":this.attr({cx:+a+this.attrs.cx,cy:+b+this.attrs.cy});break;case"rect":case"image":case"text":this.attr({x:+a+this.attrs.x,y:+b+this.attrs.y});break;case"path":var c=bp(this.attrs.path);c[0][1]+=+a;c[0][2]+=+b;this.attr({path:c});break}return this};bO.animateWith=function(a,b,c,d,e){for(var f=0,g=cv.length;f<g;f++)cv[f].el.id==a.id&&(b.start=cv[f].start);return this.animate(b,c,d,e)};bO.animateAlong=cA();bO.animateAlongBack=cA(1);function cA(b){return function(c,d,e,f){var g={back:b};a.is(e,"function")?f=e:g.rot=e;c&&c.constructor==bN&&(c=c.attrs.path);c&&(g.along=c);return this.animate(g,d,f)}}function cB(a,b,c,d,e,f){var g=3*b,h=3*(d-b)-g,i=1-g-h,j=3*c,k=3*(e-c)-j,l=1-j-k;function m(a){return((i*a+h)*a+g)*a}function n(a,b){var c=o(a,b);return((l*c+k)*c+j)*c}function o(a,b){var c,d,e,f,j,k;for(e=a,k=0;k<8;k++){f=m(e)-a;if(B(f)<b)return e;j=(3*i*e+2*h)*e+g;if(B(j)<0.000001)break;e=e-f/j}c=0;d=1;e=a;if(e<c)return c;if(e>d)return d;while(c<d){f=m(e);if(B(f-a)<b)return e;a>f?c=e:d=e;e=(d-c)/2+c}return e}return n(a,1/(200*f))}bO.onAnimation=function(a){this._run=a||0;return this};bO.animate=function(c,d,e,g){var h=this;h.timeouts=h.timeouts||[];if(a.is(e,"function")||!e)g=e||null;if(h.removed){g&&g.call(h);return h}var i={},j={},k=false,l={};for(var m in c)if(c[f](m)){if(X[f](m)||h.paper.customAttributes[f](m)){k=true;i[m]=h.attr(m);i[m]==null&&(i[m]=W[m]);j[m]=c[m];switch(X[m]){case"along":var n=cs(c[m]),o=ct(c[m],n*!(!c.back)),p=h.getBBox();l[m]=n/d;l.tx=p.x;l.ty=p.y;l.sx=o.x;l.sy=o.y;j.rot=c.rot;j.back=c.back;j.len=n;c.rot&&(l.r=S(h.rotate())||0);break;case E:l[m]=(j[m]-i[m])/d;break;case"colour":i[m]=a.getRGB(i[m]);var q=a.getRGB(j[m]);l[m]={r:(q.r-i[m].r)/d,g:(q.g-i[m].g)/d,b:(q.b-i[m].b)/d};break;case"path":var t=bw(i[m],j[m]);i[m]=t[0];var u=t[1];l[m]=[];for(var v=0,x=i[m][w];v<x;v++){l[m][v]=[0];for(var y=1,z=i[m][v][w];y<z;y++)l[m][v][y]=(u[v][y]-i[m][v][y])/d}break;case"csv":var A=r(c[m])[s](b),B=r(i[m])[s](b);switch(m){case"translation":i[m]=[0,0];l[m]=[A[0]/d,A[1]/d];break;case"rotation":i[m]=B[1]==A[1]&&B[2]==A[2]?B:[0,A[1],A[2]];l[m]=[(A[0]-i[m][0])/d,0,0];break;case"scale":c[m]=A;i[m]=r(i[m])[s](b);l[m]=[(A[0]-i[m][0])/d,(A[1]-i[m][1])/d,0,0];break;case"clip-rect":i[m]=r(i[m])[s](b);l[m]=[];v=4;while(v--)l[m][v]=(A[v]-i[m][v])/d;break}j[m]=A;break;default:A=[].concat(c[m]);B=[].concat(i[m]);l[m]=[];v=h.paper.customAttributes[m][w];while(v--)l[m][v]=((A[v]||0)-(B[v]||0))/d;break}}}if(k){var G=a.easing_formulas[e];if(!G){G=r(e).match(P);if(G&&G[w]==5){var H=G;G=function(a){return cB(a,+H[1],+H[2],+H[3],+H[4],d)}}else G=function(a){return a}}cv.push({start:c.start||+(new Date),ms:d,easing:G,from:i,diff:l,to:j,el:h,t:{x:0,y:0}});a.is(g,"function")&&(h._ac=setTimeout(function(){g.call(h)},d));cv[w]==1&&setTimeout(cw)}else{var C=[],D;for(var F in c)if(c[f](F)&&Z.test(F)){m={value:c[F]};F=="from"&&(F=0);F=="to"&&(F=100);m.key=T(F,10);C.push(m)}C.sort(be);C[0].key&&C.unshift({key:0,value:h.attrs});for(v=0,x=C[w];v<x;v++)cx(C[v].value,h,d/100*C[v].key,d/100*(C[v-1]&&C[v-1].key||0),C[v-1]&&C[v-1].value.callback);D=C[C[w]-1].value.callback;D&&h.timeouts.push(setTimeout(function(){D.call(h)},d))}return this};bO.stop=function(){for(var a=0;a<cv.length;a++)cv[a].el.id==this.id&&cv.splice(a--,1);for(a=0,ii=this.timeouts&&this.timeouts.length;a<ii;a++)clearTimeout(this.timeouts[a]);this.timeouts=[];clearTimeout(this._ac);delete this._ac;return this};bO.translate=function(a,b){return this.attr({translation:a+" "+b})};bO[H]=function(){return"RaphaÃ«lâs object"};a.ae=cv;var cC=function(a){this.items=[];this[w]=0;this.type="set";if(a)for(var b=0,c=a[w];b<c;b++){if(a[b]&&(a[b].constructor==bN||a[b].constructor==cC)){this[this.items[w]]=this.items[this.items[w]]=a[b];this[w]++}}};cC[e][L]=function(){var a,b;for(var c=0,d=arguments[w];c<d;c++){a=arguments[c];if(a&&(a.constructor==bN||a.constructor==cC)){b=this.items[w];this[b]=this.items[b]=a;this[w]++}}return this};cC[e].pop=function(){delete this[this[w]--];return this.items.pop()};for(var cD in bO)bO[f](cD)&&(cC[e][cD]=(function(a){return function(){for(var b=0,c=this.items[w];b<c;b++)this.items[b][a][m](this.items[b],arguments);return this}})(cD));cC[e].attr=function(b,c){if(b&&a.is(b,G)&&a.is(b[0],"object"))for(var d=0,e=b[w];d<e;d++)this.items[d].attr(b[d]);else for(var f=0,g=this.items[w];f<g;f++)this.items[f].attr(b,c);return this};cC[e].animate=function(b,c,d,e){(a.is(d,"function")||!d)&&(e=d||null);var f=this.items[w],g=f,h,i=this,j;e&&(j=function(){!(--f)&&e.call(i)});d=a.is(d,F)?d:j;h=this.items[--g].animate(b,c,d,j);while(g--)this.items[g]&&!this.items[g].removed&&this.items[g].animateWith(h,b,c,d,j);return this};cC[e].insertAfter=function(a){var b=this.items[w];while(b--)this.items[b].insertAfter(a);return this};cC[e].getBBox=function(){var a=[],b=[],c=[],d=[];for(var e=this.items[w];e--;){var f=this.items[e].getBBox();a[L](f.x);b[L](f.y);c[L](f.x+f.width);d[L](f.y+f.height)}a=A[m](0,a);b=A[m](0,b);return{x:a,y:b,width:z[m](0,c)-a,height:z[m](0,d)-b}};cC[e].clone=function(a){a=new cC;for(var b=0,c=this.items[w];b<c;b++)a[L](this.items[b].clone());return a};a.registerFont=function(a){if(!a.face)return a;this.fonts=this.fonts||{};var b={w:a.w,face:{},glyphs:{}},c=a.face["font-family"];for(var d in a.face)a.face[f](d)&&(b.face[d]=a.face[d]);this.fonts[c]?this.fonts[c][L](b):this.fonts[c]=[b];if(!a.svg){b.face["units-per-em"]=T(a.face["units-per-em"],10);for(var e in a.glyphs)if(a.glyphs[f](e)){var g=a.glyphs[e];b.glyphs[e]={w:g.w,k:{},d:g.d&&"M"+g.d[Y](/[mlcxtrv]/g,function(a){return({l:"L",c:"C",x:"z",t:"m",r:"l",v:"c"})[a]||"M"})+"z"};if(g.k)for(var h in g.k)g[f](h)&&(b.glyphs[e].k[h]=g.k[h])}}return a};k.getFont=function(b,c,d,e){e=e||"normal";d=d||"normal";c=+c||({normal:400,bold:700,lighter:300,bolder:800})[c]||400;if(!a.fonts)return;var g=a.fonts[b];if(!g){var h=new RegExp("(^|\\s)"+b[Y](/[^\w\d\s+!~.:_-]/g,p)+"(\\s|$)","i");for(var i in a.fonts)if(a.fonts[f](i)){if(h.test(i)){g=a.fonts[i];break}}}var j;if(g)for(var k=0,l=g[w];k<l;k++){j=g[k];if(j.face["font-weight"]==c&&(j.face["font-style"]==d||!j.face["font-style"])&&j.face["font-stretch"]==e)break}return j};k.print=function(c,d,e,f,g,h,i){h=h||"middle";i=z(A(i||0,1),-1);var j=this.set(),k=r(e)[s](p),l=0,m=p,n;a.is(f,e)&&(f=this.getFont(f));if(f){n=(g||16)/f.face["units-per-em"];var o=f.face.bbox.split(b),q=+o[0],t=+o[1]+(h=="baseline"?o[3]-o[1]+ +f.face.descent:(o[3]-o[1])/2);for(var u=0,v=k[w];u<v;u++){var x=u&&f.glyphs[k[u-1]]||{},y=f.glyphs[k[u]];l+=u?(x.w||f.w)+(x.k&&x.k[k[u]]||0)+f.w*i:0;y&&y.d&&j[L](this.path(y.d).attr({fill:"#000",stroke:"none",translation:[l,0]}))}j.scale(n,n,q,t).translate(c-q,d-t)}return j};a.format=function(b,c){var e=a.is(c,G)?[0][n](c):arguments;b&&a.is(b,F)&&e[w]-1&&(b=b[Y](d,function(a,b){return e[++b]==null?p:e[b]}));return b||p};a.ninja=function(){i.was?h.Raphael=i.is:delete Raphael;return a};a.el=bO;a.st=cC[e];i.was?h.Raphael=a:Raphael=a})()
/*
 * jQuery.splitter.js - two-pane splitter window plugin
 *
 * version 1.6 (2010/01/03) 
 * 
 * Dual licensed under the MIT and GPL licenses: 
 *   http://www.opensource.org/licenses/mit-license.php 
 *   http://www.gnu.org/licenses/gpl.html 
 */
 
/**
 * The splitter() plugin implements a two-pane resizable splitter window.
 * The selected elements in the jQuery object are converted to a splitter;
 * each selected element should have two child elements, used for the panes
 * of the splitter. The plugin adds a third child element for the splitbar.
 * 
 * For more details see: http://methvin.com/jquery/splitter/
 *
 *
 * @example $('#MySplitter').splitter();
 * @desc Create a vertical splitter with default settings 
 *
 * @example $('#MySplitter').splitter({type: 'h', accessKey: 'M'});
 * @desc Create a horizontal splitter resizable via Alt+Shift+M
 *
 * @name splitter
 * @type jQuery
 * @param Object options Options for the splitter (not required)
 * @cat Plugins/Splitter
 * @return jQuery
 * @author Dave Methvin (dave.methvin@gmail.com)
 */
 ;(function($){

var splitterCounter = 0;

 $.fn.splitter = function(args){
	args = args || {};
	return this.each(function() {
		if ( $(this).is(".splitter") )	// already a splitter
			return;
		var zombie;		// left-behind splitbar for outline resizes
		function setBarState(state) {
			bar.removeClass(opts.barStateClasses).addClass(state);
		}
		function startSplitMouse(evt) {
			if ( evt.which != 1 )
				return;		// left button only
			bar.removeClass(opts.barHoverClass);
			if ( opts.outline ) {
				zombie = zombie || bar.clone(false).insertAfter(A);
				bar.removeClass(opts.barDockedClass);
			}
			setBarState(opts.barActiveClass)
			// Safari selects A/B text on a move; iframes capture mouse events so hide them
			panes.css("-webkit-user-select", "none").find("iframe").addClass(opts.iframeClass);
			A._posSplit = A[0][opts.pxSplit] - evt[opts.eventPos];
			$(document)
				.bind("mousemove"+opts.eventNamespace, doSplitMouse)
				.bind("mouseup"+opts.eventNamespace, endSplitMouse);
		}
		function doSplitMouse(evt) {
			var pos = A._posSplit+evt[opts.eventPos],
				range = Math.max(0, Math.min(pos, splitter._DA - bar._DA)),
				limit = Math.max(A._min, splitter._DA - B._max, 
						Math.min(pos, A._max, splitter._DA - bar._DA - B._min));
			if ( opts.outline ) {
				// Let docking splitbar be dragged to the dock position, even if min width applies
				if ( (opts.dockPane == A && pos < Math.max(A._min, bar._DA))  || 
					 (opts.dockPane == B && pos > Math.min(pos, A._max, splitter._DA - bar._DA - B._min)) ) {
					bar.addClass(opts.barDockedClass).css(opts.origin, range);
				}
				else {
					bar.removeClass(opts.barDockedClass).css(opts.origin, limit);
				}
				bar._DA = bar[0][opts.pxSplit];
			} else 
				resplit(pos);
			setBarState(pos == limit? opts.barActiveClass : opts.barLimitClass);
		}
		function endSplitMouse(evt) {
			setBarState(opts.barNormalClass);
			bar.addClass(opts.barHoverClass);
			var pos = A._posSplit+evt[opts.eventPos];
			if ( opts.outline ) {
				zombie.remove(); zombie = null;
				resplit(pos);
			}
			panes.css("-webkit-user-select", "text").find("iframe").removeClass(opts.iframeClass);
			$(document)
				.unbind("mousemove"+opts.eventNamespace+" mouseup"+opts.eventNamespace);
		}
		function resplit(pos) {
			bar._DA = bar[0][opts.pxSplit];		// bar size may change during dock
			// Constrain new splitbar position to fit pane size and docking limits
			if ( (opts.dockPane == A && pos < Math.max(A._min, bar._DA)) ||
				 (opts.dockPane == B && pos > Math.min(pos, A._max, splitter._DA - bar._DA - B._min)) ) {
				bar.addClass(opts.barDockedClass);
				bar._DA = bar[0][opts.pxSplit];
				pos = opts.dockPane == A? 0 : splitter._DA - bar._DA;
				if ( bar._pos == null )
					bar._pos = A[0][opts.pxSplit];
			}
			else {
				bar.removeClass(opts.barDockedClass);
				bar._DA = bar[0][opts.pxSplit];
				bar._pos = null;
				pos = Math.max(A._min, splitter._DA - B._max, 
						Math.min(pos, A._max, splitter._DA - bar._DA - B._min));
			}
			// Resize/position the two panes
			bar.css(opts.origin, pos).css(opts.fixed, splitter._DF);
			A.css(opts.origin, 0).css(opts.split, pos).css(opts.fixed,  splitter._DF);
			B.css(opts.origin, pos+bar._DA)
				.css(opts.split, splitter._DA-bar._DA-pos).css(opts.fixed,  splitter._DF);
			// IE fires resize for us; all others pay cash
			if ( !$.browser.msie )
				panes.trigger("resize");
		}
		function dimSum(jq, dims) {
			// Opera returns -1 for missing min/max width, turn into 0
			var sum = 0;
			for ( var i=1; i < arguments.length; i++ )
				sum += Math.max(parseInt(jq.css(arguments[i]),10) || 0, 0);
			return sum;
		}
		
		// Determine settings based on incoming opts, element classes, and defaults
		var vh = (args.splitHorizontal? 'h' : args.splitVertical? 'v' : args.type) || 'v';
		var opts = $.extend({
			// Defaults here allow easy use with ThemeRoller
			splitterClass:	"splitter ui-widget ui-widget-content",
			paneClass:		"splitter-pane",
			barClass:		"splitter-bar",
			barNormalClass: "ui-state-default",			// splitbar normal
			barHoverClass:  "ui-state-hover",			// splitbar mouse hover
			barActiveClass: "ui-state-highlight",		// splitbar being moved
			barLimitClass:  "ui-state-error",			// splitbar at limit
			iframeClass:	"splitter-iframe-hide",		// hide iframes during split
			eventNamespace:	".splitter"+(++splitterCounter),
			pxPerKey: 8,			// splitter px moved per keypress
			tabIndex: 0,			// tab order indicator
			accessKey: ''			// accessKey for splitbar
		},{
			// user can override
			v: {					// Vertical splitters:
				keyLeft: 39, keyRight: 37, cursor: "e-resize",
				barStateClass: "splitter-bar-vertical", 
				barDockedClass: "splitter-bar-vertical-docked"
			},
			h: {					// Horizontal splitters:
				keyTop: 40, keyBottom: 38,  cursor: "n-resize",
				barStateClass: "splitter-bar-horizontal",
				barDockedClass: "splitter-bar-horizontal-docked"
			}
		}[vh], args, {
			// user cannot override
			v: {					// Vertical splitters:
				type: 'v', eventPos: "pageX", origin: "left",
				split: "width",  pxSplit: "offsetWidth",  side1: "Left", side2: "Right",
				fixed: "height", pxFixed: "offsetHeight", side3: "Top",  side4: "Bottom"
			},
			h: {					// Horizontal splitters:
				type: 'h', eventPos: "pageY", origin: "top",
				split: "height", pxSplit: "offsetHeight", side1: "Top",  side2: "Bottom",
				fixed: "width",  pxFixed: "offsetWidth",  side3: "Left", side4: "Right"
			}
		}[vh]);
		opts.barStateClasses = [opts.barNormalClass, opts.barHoverClass, opts.barActiveClass, opts.barLimitClass].join(' ');

		// Create jQuery object closures for splitter and both panes
		var splitter = $(this).css({position: "relative"}).addClass(opts.splitterClass);
		var panes = $(">*", splitter[0]).addClass(opts.paneClass).css({
			position: "absolute", 			// positioned inside splitter container
			"z-index": "1",					// splitbar is positioned above
			"-moz-outline-style": "none"	// don't show dotted outline
		});
		var A = $(panes[0]), B = $(panes[1]);	// A = left/top, B = right/bottom
		opts.dockPane = opts.dock && (/right|bottom/.test(opts.dock)? B:A);

		// Focuser element, provides keyboard support; title is shown by Opera accessKeys
		var focuser = $('<a href="javascript:void(0)"></a>')
			.attr({accessKey: opts.accessKey, tabIndex: opts.tabIndex, title: opts.splitbarClass})
			.bind(($.browser.opera?"click":"focus")+opts.eventNamespace, 
				function(){ this.focus(); bar.addClass(opts.barActiveClass) })
			.bind("keydown"+opts.eventNamespace, function(e){
				var key = e.which || e.keyCode;
				var dir = key==opts["key"+opts.side1]? 1 : key==opts["key"+opts.side2]? -1 : 0;
				if ( dir )
					resplit(A[0][opts.pxSplit]+dir*opts.pxPerKey, false);
			})
			.bind("blur"+opts.eventNamespace, 
				function(){ bar.removeClass(opts.barActiveClass) });
			
		// Splitbar element
		var bar = $('<div></div>')
			.insertAfter(A).addClass(opts.barClass).addClass(opts.barStateClass)
			.append(focuser).attr({unselectable: "on"})
			.css({position: "absolute",	"user-select": "none", "-webkit-user-select": "none",
				"-khtml-user-select": "none", "-moz-user-select": "none", "z-index": "100"})
			.bind("mousedown"+opts.eventNamespace, startSplitMouse)
			.bind("mouseover"+opts.eventNamespace, function(){
				$(this).addClass(opts.barHoverClass);
			})
			.bind("mouseout"+opts.eventNamespace, function(){
				$(this).removeClass(opts.barHoverClass);
			});
		// Use our cursor unless the style specifies a non-default cursor
		if ( /^(auto|default|)$/.test(bar.css("cursor")) )
			bar.css("cursor", opts.cursor);

		// Cache several dimensions for speed, rather than re-querying constantly
		// These are saved on the A/B/bar/splitter jQuery vars, which are themselves cached
		// DA=dimension adjustable direction, PBF=padding/border fixed, PBA=padding/border adjustable
		bar._DA = bar[0][opts.pxSplit];
		splitter._PBF = dimSum(splitter, "border"+opts.side3+"Width", "border"+opts.side4+"Width");
		splitter._PBA = dimSum(splitter, "border"+opts.side1+"Width", "border"+opts.side2+"Width");
		A._pane = opts.side1;
		B._pane = opts.side2;
		$.each([A,B], function(){
			this._splitter_style = this.style;
			this._min = opts["min"+this._pane] || dimSum(this, "min-"+opts.split);
			this._max = opts["max"+this._pane] || dimSum(this, "max-"+opts.split) || 9999;
			this._init = opts["size"+this._pane]===true ?
				parseInt($.curCSS(this[0],opts.split),10) : opts["size"+this._pane];
		});
		
		// Determine initial position, get from cookie if specified
		var initPos = A._init;
		if ( !isNaN(B._init) )	// recalc initial B size as an offset from the top or left side
			initPos = splitter[0][opts.pxSplit] - splitter._PBA - B._init - bar._DA;
		if ( opts.cookie ) {
			if ( !$.cookie )
				alert('jQuery.splitter(): jQuery cookie plugin required');
			initPos = parseInt($.cookie(opts.cookie),10);
			$(window).bind("unload"+opts.eventNamespace, function(){
				var state = String(bar.css(opts.origin));	// current location of splitbar
				$.cookie(opts.cookie, state, {expires: opts.cookieExpires || 365, 
					path: opts.cookiePath || document.location.pathname});
			});
		}
		if ( isNaN(initPos) )	// King Solomon's algorithm
			initPos = Math.round((splitter[0][opts.pxSplit] - splitter._PBA - bar._DA)/2);

		// Resize event propagation and splitter sizing
		if ( opts.anchorToWindow )
			opts.resizeTo = window;
		if ( opts.resizeTo ) {
			splitter._hadjust = dimSum(splitter, "borderTopWidth", "borderBottomWidth", "marginBottom");
			splitter._hmin = Math.max(dimSum(splitter, "minHeight"), 20);
			$(window).bind("resize"+opts.eventNamespace, function(){
				var top = splitter.offset().top;
				var eh = $(opts.resizeTo).height();
				splitter.css("height", Math.max(eh-top-splitter._hadjust, splitter._hmin)+"px");
				if ( !$.browser.msie ) splitter.trigger("resize");
			}).trigger("resize"+opts.eventNamespace);
		}
		else if ( opts.resizeToWidth && !$.browser.msie ) {
			$(window).bind("resize"+opts.eventNamespace, function(){
				splitter.trigger("resize");
			});
		}

		// Docking support
		if ( opts.dock ) {
			
			splitter
				.bind("toggleDock"+opts.eventNamespace, function() {
					var pw = opts.dockPane[0][opts.pxSplit];
					splitter.trigger(pw?"dock":"undock");
				})
				
				.bind("dock"+opts.eventNamespace, function(){
					var pw = A[0][opts.pxSplit]; 
					if ( !pw ) return;
					bar._pos = pw;
					var x={}; 
					x[opts.origin] = opts.dockPane==A? 0 :
						splitter[0][opts.pxSplit] - splitter._PBA - bar[0][opts.pxSplit];
					bar.animate(x, opts.dockSpeed||1, opts.dockEasing, function(){
						bar.addClass(opts.barDockedClass);
						resplit(x[opts.origin]);
					});
				})
				.bind("undock"+opts.eventNamespace, function(){
					var pw = opts.dockPane[0][opts.pxSplit];
					if ( pw ) return;
					var x={}; x[opts.origin]=bar._pos+"px";
					bar.removeClass(opts.barDockedClass)
						.animate(x, opts.undockSpeed||opts.dockSpeed||1, opts.undockEasing||opts.dockEasing, function(){
							resplit(bar._pos);
							bar._pos = null;
						});
				});
			if ( opts.dockKey )
				$('<a title="'+opts.splitbarClass+' toggle dock" href="javascript:void(0)"></a>')
					.attr({accessKey: opts.dockKey, tabIndex: -1}).appendTo(bar)
					.bind($.browser.opera?"click":"focus", function(){ 
						splitter.trigger("toggleDock"); this.blur();
					});
			bar.bind("dblclick", function(){ splitter.trigger("toggleDock"); })
		}

		
		// Resize event handler; triggered immediately to set initial position
		splitter
			.bind("destroy"+opts.eventNamespace, function(){
				$([window, document]).unbind(opts.eventNamespace);
				bar.unbind().remove();
				panes.removeClass(opts.paneClass);
				splitter
					.removeClass(opts.splitterClass)
					.add(panes)
						.unbind(opts.eventNamespace)
						.attr("style", function(el){
							return this._splitter_style||"";	//TODO: save style
						});
				splitter = bar = focuser = panes = A = B = opts = args = null;
			})
			.bind("resize"+opts.eventNamespace, function(e, size){
				// Custom events bubble in jQuery 1.3; avoid recursion
				if ( e.target != this ) return;
				// Determine new width/height of splitter container
				splitter._DF = splitter[0][opts.pxFixed] - splitter._PBF;
				splitter._DA = splitter[0][opts.pxSplit] - splitter._PBA;
				// Bail if splitter isn't visible or content isn't there yet
				if ( splitter._DF <= 0 || splitter._DA <= 0 ) return;
				// Re-divvy the adjustable dimension; maintain size of the preferred pane
				resplit(!isNaN(size)? size : (!(opts.sizeRight||opts.sizeBottom)? A[0][opts.pxSplit] :
					splitter._DA-B[0][opts.pxSplit]-bar._DA));
				setBarState(opts.barNormalClass);
			})
			.trigger("resize" , [initPos]);
	});
};

})(jQuery);
ï»¿formatFileSize = function (bytes) {
            if (typeof bytes !== 'number' || bytes === null) {
                return '';
            }
            if (bytes >= 1000000000) {
                return (bytes / 1000000000).toFixed(2) + ' GB';
            }
            if (bytes >= 1000000) {
                return (bytes / 1000000).toFixed(2) + ' MB';
            }
            return (bytes / 1000).toFixed(2) + ' KB';
        };

$(function() {
	$("input[name=type]").change(function () {
		if ($("#standardtext").attr('checked')) { $("input.standardtext").removeAttr("disabled");}
		else {$("input.standardtext").attr("disabled", true);}
	}).change();
});

	
$('#file_upload').fileUploadUI({
//     url: '/path/to/upload/handler.json'
	
	uploadTable: $('#files'),
	downloadTable: $('#files'),
	buildUploadRow: function (files, index, handler)
	{
		
            return $('<tr><td>' + files[index].name + '<\/td>' +
                    '<td class="file_upload_progress"><div><\/div><\/td>' +
                    '<td class="file_upload_cancel">' +
                    '<button class="ui-state-default ui-corner-all" title="Cancel">' +
                    '<span class="ui-icon ui-icon-cancel">Cancel<\/span>' +
                    '<\/button><\/td><\/tr>');
        },
	buildDownloadRow: function (file, handler)
	{

//             alert(file+"uuu"+downloadRow);
		var downloadRow = handler.downloadTable.find('.file_download_template:last')
			.clone().removeAttr('id').removeClass("file_download_template");
		    
		downloadRow.attr('data-id', file.id || file.name);
		downloadRow.attr('data-type', file.filetype);
		downloadRow.find('.file_name a:last')
			.text(file.name);
		downloadRow.find('.file_size:last')
			.text(formatFileSize(file.size));
		downloadRow.find('.file_sentencenumber:last')
			.text(file.sentences+" sentences.");
		if (file.thumbnail) {
			downloadRow.find('.file_download_preview:last').append(
				$('<a/>').append($('<img/>').attr('src', file.thumbnail || null))
				);
			downloadRow.find('a:last').attr('target', '_blank');
			}
		downloadRow.find('a:last')
			.attr('href', file.url || null);
		downloadRow.find('.file_view button:last')
			.button({icons: {primary: 'ui-icon-upload'}, text: false}).click(function() {
				var clicked = $(this).parent().parent().attr("data-id");
// 				alert('click'+clicked);
				enterDB(this);
			});
		return downloadRow;
		 
// 		$('.file_view')
		
        }		       
});



viewer = function(that) {
	var row = $(that).parent().parent();
	var filename = row.attr("data-id");
	var filetype = row.attr("data-type");
	$("#filename").attr("value", filename );
	$("#filetype").attr("value", filetype );
// 	$("#username").attr("value", unam );
	$("#fileform").attr("action", "viewer.cgi" );
	$("#fileform").submit();
}

enterDB = function(that) {
	var row = $(that).parent().parent();
	var filename = row.attr("data-id");
	var filetype = row.attr("data-type");
	$("#filename").attr("value", filename );
	$("#filetype").attr("value", filetype );
// 	$("#username").attr("value", unam );
	$("#fileform").attr("action", "project.cgi" );
	$("#fileform").submit();
	console.log($("#fileform"))
}

tempStorageConll = function() {
	
	
}

/*
 
var fileName = handler.formatFileName(file.name),
                fileUrl = handler.getFileUrl(file, handler),
                thumbnailUrl = handler.getThumbnailUrl(file, handler),
                downloadRow = handler.downloadTemplate
                    .clone().removeAttr('id');*/

//             return $('<tr><td>' + file.name + 'mmm<\/td><\/tr>');

/*
$(function () {
    $('#file_upload').fileUploadUI({
        uploadTable: $('#files'),
        buildUploadRow: function (files, index, handler) {
            return $('<tr><td>' + files[index].name + '<\/td>' +
                    '<td class="file_upload_progress"><div><\/div><\/td>' +
                    '<td class="file_upload_cancel">' +
                    '<button class="ui-state-default ui-corner-all" title="Cancel">' +
                    '<span class="ui-icon ui-icon-cancel">Cancel<\/span>' +
                    '<\/button><\/td><\/tr>');
        },
        buildDownloadRow: function (file, handler) {
		
            return $('<tr><td>' + file.name + 'mmm<\/td><\/tr>');
        }
    });
});*/

// $(function () {
//     // Initialize jQuery File Upload (Extended User Interface Version):
//     $('#file_upload').fileUploadUIX();
//     
//     // Load existing files:
//     $.getJSON($('#file_upload').fileUploadUIX('option', 'url'), function (files) {
//         var fileUploadOptions = $('#file_upload').fileUploadUIX('option');
//         $.each(files, function (index, file) {
//             fileUploadOptions.buildDownloadRow(file, fileUploadOptions)
//                 .appendTo(fileUploadOptions.downloadTable).fadeIn();
//         });
//     });
// });
#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys,cgi,cgitb,json
sys.path.append('modules')
from logintools import isloggedin
from lib import database
cgitb.enable()

form = cgi.FieldStorage()

project = form.getvalue('project',None)
treeid = form.getvalue('treeid',None)
textid = form.getvalue('textid',None)
snr = form.getvalue('snr',None)
userid = form.getvalue('userid',None)
username = form.getvalue("username","unknown")
sid = form.getvalue('sid',None)

test = isloggedin("users/")
if not test:sys.exit()
admin = test[0]["admin"]

#print "Content-Type: text/html\n" # blank line: end of headers

sql=database.SQL(project)
if treeid:
	answer = sql.statustree(int(sid),int(snr),int(treeid),int(userid),username,int(textid),int(admin))
else:
	answer = sql.statustext(int(textid),int(userid))
print "Content-Type: application/json\r\n\r\n"
print json.dumps(answer)

#print 'Content-type: text/html\n\n'
#print answer
{# base.html #}
<!DOCTYPE html>
<html>
    <head>
        <title>Authomatic Flask Example</title>
    </head>
    <body>
        {% block body %}{% endblock %}
    </body>
</html> 
{# index.html #}
{% extends "base.html" %}
{% block body %}

    <!--Links to the login handler-->
    Login with <a href="login/gg">Google</a>.<br />
    Login with <a href="login/fb">Facebook</a>.<br />
    Login with <a href="login/tw">Twitter</a>.<br />
    
    <!--OpenID form-->
    <form action="login/oi">
        <input type="text" name="id" value="me.yahoo.com" />
        <input type="submit" value="Authenticate With OpenID">
    </form>

{% endblock %}
{# index.html #}
{% extends "base.html" %}

{% block body %}

    <a href="/">Home</a>
    
    {# Check for errors. #}
    {% if result.error %}
        <h2>Damn that error: {{ result.error.message }}</h2>
    {% endif %}
    
    {# Welcome the user. #}
    {% if result.user %}
        <h1>Hi {{ result.user.name }}</h1>
        <h2>Your id is: {{ result.user.id }}</h2>
        <h2>Your email is: {{ result.user.email }}</h2>
    {% endif %}
    
    {# If the user has credentials, we can access his/her protected resources. #}
    {% if result.user.credentials %}
        
        {# Let's get the user's 5 most recent statuses. #}
        {% if result.provider.name == 'fb' %}
            Your are logged in with Facebook.<br />
            
            {# Insert the user's ID to the URL and access the resource. #}
            {% set url = 'https://graph.facebook.com/{0}?fields=feed.limit(5)'.format(result.user.id) %}
            {% set response = result.provider.access(url) %}
            
            {# Parse the response. #}
            {% if response.status == 200 %}
                
                {% if response.data.error %}
                    Damn that error: {{ response.data.error }}!
                {% endif %}
                
                {% if response.data.feed.data %}
                    Your 5 most recent statuses:<br />
                    
                    {% for status in response.data.feed.data %}
                        <h3>{{ status.message or status.name or status.story }}</h3>
                        Posted on: {{ status.created_time }}
                    {% endfor %}
                {% endif %}
                
            {% endif %}{# response.status == 200 #}
            
        {% endif %}{# result.provider.name == 'fb' #}
        
        {# Do the same for Twitter. #}
        {% if result.provider.name == 'tw' %}
            Your are logged in with Twitter.<br />
            
            {% set url = 'https://api.twitter.com/1.1/statuses/user_timeline.json?count=5' %}
            {% set response = result.provider.access(url) %}
            
            {% if response.status == 200 %}
                
                {% if response.data.errors %}
                    Damn that error: {{ response.data.errors }}!
                {% endif %}
                
                {% if response.data %}
                    Your 5 most recent tweets:<br />
                    
                    {% for tweet in response.data %}
                        <h3>{{ tweet.text }}</h3>
                        Posted on: {{ tweet.created_at }}
                    {% endfor %}
                {% endif %}
                
            {% endif %}
            
        {% endif %}{# result.provider.name == 'tw' #}
    {% endif %}{# result.user.credentials #}

{% endblock body %} 
#!/usr/bin/python
# -*- coding: utf-8 -*-

from os import sys, listdir, makedirs, path
import argparse
import datetime

# --------------------------------
# options and arguments

parser = argparse.ArgumentParser(description=sys.argv[0])

parser.add_argument('--micro','-micro', help=u"Folder containing microsyntactic tabular files", action='store', dest='dos_in', metavar=('MICRO'), required=True)
parser.add_argument('--all','-all', help=u"Folder containing aligned tabular files", action='store', dest='dos_add', metavar=('ALL'), required=True)

args = parser.parse_args()  

dos_in = args.dos_in
if dos_in[-1]=="/": dos_in = dos_in[:-1]

dos_add = args.dos_add
if dos_add[-1]=="/": dos_add = dos_add[:-1]


# columns in aligned files (here speaker (in column 4) and layer (in column 26)
speaker_col = 4 # when it has already been moved!!
layer_col = 26


for fic in (listdir(dos_in)):

#    base_dos = dos_add.rsplit("/", 1)[0]
    base_name = fic.rsplit(".", 2)[0]
    print "\nProcessing "+base_name
    if "~" in fic or "Rhapsodie" in fic: continue #skip global file (done after)

    pt=open(dos_in+"/"+fic, "r")
    contents=pt.readlines()
    pt.close()

    pt=open(dos_add+"/"+base_name+".micro_macro_prosody.tabular", "r")
    contents_add=pt.readlines()
    pt.close()

    all_pt = (dos_add+"/"+fic)

    pt=open(dos_in+"/"+fic, "w")  

    for i, line in enumerate(contents):

        line = line.strip("\n")
        line = line.split("\t")

        #print "length = ", len(line)  #check length

        contents[i] = line

        # get info to be added
        speaker = contents_add[i].split("\t")[speaker_col]
        layer = contents_add[i].split("\t")[layer_col]

        # print directly by putting speaker in column 4 (so the same as in the
        # original aligned files and layer at the end)
        print>>pt, "\t".join(line[:speaker_col]) + "\t" + speaker + "\t" + "\t".join(line[speaker_col:])+ layer

    pt.close()

# do global file
ncols = 27
pt=open(dos_add+"/Rhapsodie.micro_macro_prosody.tabular", "r") 
contents = pt.readlines()
pt.close()

pt=open(dos_in+"/Rhapsodie.micro.tabular", "w")  

for i, line in enumerate(contents):

    line = line.strip("\n")
    line = line.split("\t")

    #print "length = ", len(line)  #check length

    contents[i] = line

    print>>pt, "\t".join(line[:27])


pt.close()




        
    


#!/usr/bin/python
# -*- coding: utf-8 -*-
#from __future__ import print_function

import glob, os, conll, codecs, pinyin #, json #, goslate
sys.path.insert(0, '../lib')
from conll import conllFile2trees, trees2conllFile
from googleapiclient.discovery import build

def translate(q,source='zh',target='en', maxiAtOnce=100):
	
	service = build('translate', 'v2', developerKey='AIzaSyBES6QPcKqvY4pHsaMkr8piFsS2Mwe029w')
	translations=[]
	
	for wordgroup in [q[i:i+maxiAtOnce] for i in xrange(0, len(q), maxiAtOnce)]:
		res = service.translations().list(source=source, target=target, q=wordgroup ).execute()
		translations+=[td["translatedText"] for td in res["translations"]]
		
	return translations

#translate(u"åè®¸ ä¸ ä½ äººå£« å¥å¢ ç æå".split())


for conllinfile in glob.glob(os.path.join("corpus/conll/", 'CONV*.*')):
	
	print conllinfile
	trees=conllFile2trees(conllinfile)
	path,base = os.path.split(conllinfile)
	translateDic={}
	counter=0
	for tree in trees:
		for i,node in tree.iteritems():
			node["tag2"]=pinyin.get(node["t"])
			translateDic[node["t"]]=None
		counter+=1
		if not counter%100: print counter,"trees"
	
	words = sorted(translateDic)
	print len(words), "words"
	trads = translate(words)
	translateDic = dict(zip(words, trads))
	print len(translateDic),"translations"
	for tree in trees:
		for i,node in tree.iteritems():
			node["gloss"]=translateDic[node["t"]]
		counter+=1
		if not counter%100: print counter,"trees"
			#lines+=[u" ".join(words+[u"ï¼"])]
	trees2conllFile(trees,path+"/"+"UD-"+base[len("CONV-CORREC-"):])
	





1319 c' ce CLS
1276 il il CLS
949 je je CLS
777 on on CLS
724 qui qui PRQ
475 vous vous CLS
413 j' je CLS
384 Ã§a Ã§a PRO
378 elle il CLS
328 ils il CLS
313 tu tu CLS
93 nous nous CLS
59 ce ce CLS
41 elles il CLS
19 gens gens NOM
16 soldat soldat NOM
15 tout tout PRO
13 art art NOM
11 des de+le PRE
11 l'on on CLS
10 XXX XXX NOM
10 j~ j~ CLI
10 HÃ©lÃ¨ne HÃ©lÃ¨ne NOM
10 tout le monde tout le monde PRO
9 diable diable NOM
8 France France NOM
8 -vous vous CLS
8 vÃ©hicule vÃ©hicule NOM
7 pÃ¨re pÃ¨re NOM
7 livre livre NOM
7 boulanger boulanger NOM
7 -il il CLS
6 femme femme NOM
6 -ce ce CLS
6 gouvernement gouvernement NOM
6 filles fille NOM
5 cela cela PRO
5 fille fille NOM
5 personne personne PRO
5 chacun chacun PRO
4 vie vie NOM
4 e~ e~ CLS
4 c~ c~ X
4 colombes colombe NOM
4 ballon ballon NOM
4 problÃ¨mes problÃ¨me NOM
4 nombre nombre NOM
4 eux lui PRO
4 Ã©quipe Ã©quipe NOM
4 prÃªtre prÃªtre NOM
4 Charlot Charlot NOM
4 prÃ©sident prÃ©sident NOM
4 JÃ©sus JÃ©sus NOM
4 parents parent NOM
4 -tu tu CLS
4 char char NOM
3 rouge-gorge rouge-gorge NOM
3 grippe grippe NOM
3 immobilier immobilier NOM
3 autres autre PRO
3 quelqu'un quelqu'un PRO
3 policiers policier NOM
3 Louis Louis NOM
3 Ã©cole Ã©cole NOM
3 Ã©crivains Ã©crivain NOM
3 gaillard gaillard NOM
3 -on on CLS
3 probabilitÃ© probabilitÃ© NOM
3 professeur professeur NOM
3 programme programme NOM
3 ceci ceci PRO
3 point point NOM
3 Charlie Charlie NOM
3 rien rien PRO
3 impur impur NOM
3 conditions condition NOM
3 enfant enfant NOM
3 oiseau oiseau NOM
3 ventes vente NOM
3 d' de PRE
3 vÃ©ritÃ© vÃ©ritÃ© NOM
3 policier policier NOM
2 dame dame NOM
2 mÃ¨re mÃ¨re NOM
2 disparition disparition NOM
2 A A DET
2 criminalitÃ© criminalitÃ© NOM
2 Ãtienne Ãtienne NOM
2 monde monde NOM
2 Japonais Japonais NOM
2 vidanges vidange NOM
2 Marcel Marcel NOM
2 parole parole NOM
2 homme homme NOM
2 portable portable NOM
2 conseil conseil NOM
2 Nietzsche Nietzsche NOM
2 histoire histoire NOM
2 Argentins Argentin NOM
2 langue langue NOM
2 Job Job NOM
2 chef chef NOM
2 assurance assurance NOM
2 Edith Edith NOM
2 FranÃ§ais FranÃ§ais NOM
2 Szymanoski Szymanoski NOM
2 Lucas Lucas NOM
2 acte acte NOM
2 Magalie Magalie NOM
2 et puis et puis COO
2 armoire armoire NOM
2 retraite retraite NOM
2 encadrants encadrant NOM
2 siÃ¨cle siÃ¨cle NOM
2 ce ce PRO
2 communautÃ© communautÃ© NOM
2 verdict verdict NOM
2 dÃ©ficit dÃ©ficit NOM
2 avocats avocat NOM
2 s~ s~ NOM
2 groupe groupe NOM
2 Marseille Marseille NOM
2 Beauceron Beauceron NOM
2 tissu tissu NOM
2 aiguille aiguille NOM
2 projets projet NOM
2 elle elle CLS
2 mÃ©tier mÃ©tier NOM
2 sociÃ©tÃ© sociÃ©tÃ© NOM
2 souche souche NOM
2 Ã©tudiants Ã©tudiant NOM
2 avenir avenir NOM
2 certains certain PRO
2 quelque chose quelque chose PRO
2 boule boule NOM
2 Laurent Laurent NOM
2 action action NOM
2 mamans maman NOM
2 souris souris NOM
2 cours cours NOM
2 linge linge NOM
2 un un PRO
2 Ã§~ Ã§~ PRO
2 passante passant NOM
2 famille famille NOM
2 ligue ligue NOM
2 beaucoup beaucoup ADV
2 personne personne NOM
2 ministre ministre NOM
2 arbitre arbitre NOM
2 jeunes jeune NOM
2 nation nation NOM
2 progrÃ¨s progrÃ¨s NOM
2 dimension dimension NOM
2 autres autre NOM
2 une un PRO
2 Belgique Belgique NOM
2 circulation circulation NOM
2 postes poste NOM
2 -ils il CLS
2 hommes homme NOM
2 Conseil Conseil NOM
2 valeur valeur NOM
2 programmes programme NOM
2 monsieur Monsieur NOM
2 procureur procureur NOM
2 situation situation NOM
2 Ã©coles Ã©cole NOM
2 virus virus NOM
1 soutenir soutenir VNF
1 formations formation NOM
1 formule formule NOM
1 Ã©glise Ã©glise NOM
1 camarades camarade NOM
1 loyers loyer NOM
1 composante composante NOM
1 accent accent NOM
1 notion notion NOM
1 consensus consensus NOM
1 nuit nuit NOM
1 formation formation NOM
1 apÃ´tre apÃ´tre NOM
1 mythe mythe NOM
1 discothÃ¨que discothÃ¨que NOM
1 baguette baguette NOM
1 Ã©lÃ¨ves Ã©lÃ¨ve NOM
1 nations nation NOM
1 mÃ©decin mÃ©decin NOM
1 Valenciennes Valenciennes NOM
1 escadrille escadrille NOM
1 a~ a~ NOM
1 majoritÃ© majoritÃ© NOM
1 machine machine NOM
1 foi foi NOM
1 Yves Yves NOM
1 GÃ¢tinais GÃ¢tinais NOM
1 musulmans musulman NOM
1 tribunal tribunal NOM
1 camÃ©ra camÃ©ra NOM
1 -t-il il CLS
1 continuez continuer VRB
1 biens bien NOM
1 foule foule NOM
1 dix-neuf-cent-nonante-deux dix-neuf-cent-nonante-deux NUM
1 amÃ©lioration amÃ©lioration NOM
1 trou trou NOM
1 bataille bataille NOM
1 attachement attachement NOM
1 bleus bleu NOM
1 sÃ©rie sÃ©rie NOM
1 AcadÃ©mie AcadÃ©mie NOM
1 travail travail NOM
1 dÃ©couverte dÃ©couverte NOM
1 bonheur bonheur NOM
1 lycÃ©e lycÃ©e NOM
1 doctrine doctrine NOM
1 responsable responsable NOM
1 mise mise NOM
1 beaucoup beaucoup PRO
1 Christian Christian NOM
1 tempÃ©rament tempÃ©rament NOM
1 batterie batterie NOM
1 UMP UMP NOM
1 puissance puissance NOM
1 cri cri NOM
1 observateurs observateur NOM
1 parti parti NOM
1 son son NOM
1 linges linge NOM
1 FranÃ§ois FranÃ§ois NOM
1 Premier Premier NOM
1 Madame Madame NOM
1 serveur serveur NOM
1 servir servir VNF
1 Famibook Famibook NOM
1 attitude attitude NOM
1 actions action NOM
1 -t-il t-il CLS
1 Francis Francis NOM
1 Nice Nice NOM
1 com com NOM
1 lui lui PRO
1 avancÃ©es avancÃ©e NOM
1 internet internet NOM
1 littÃ©rature littÃ©rature NOM
1 Europe Europe NOM
1 intÃ©ressÃ©s intÃ©ressÃ© NOM
1 garage garage NOM
1 responsables responsable NOM
1 pinces pince NOM
1 maman maman NOM
1 ouverture ouverture NOM
1 fÃªtes fÃªte NOM
1 G G NOM
1 dis~ dis~ NOM
1 plan plan NOM
1 livreur livreur NOM
1 Neuhaus Neuhaus NOM
1 prÃ©tendant prÃ©tendant NOM
1 gare gare NOM
1 comte comte NOM
1 lavalliÃ¨re lavalliÃ¨re NOM
1 horaires horaire NOM
1 argent argent NOM
1 morale morale NOM
1 enjeux enjeu NOM
1 Rozyski Rozyski NOM
1 rÃ©actions rÃ©action NOM
1 mal mal NOM
1 ceux celui PRO
1 LKP LKP NOM
1 c'~ c'~ CLS
1 prÃ©dÃ©cesseurs prÃ©dÃ©cesseur NOM
1 collectivitÃ© collectivitÃ© NOM
1 peu peu ADV
1 ordre ordre NOM
1 profession profession NOM
1 fils fils NOM
1 pompe pompe NOM
1 Nouvelles Nouvelles NOM
1 Taylor Taylor NOM
1 Jacques Jacques NOM
1 curÃ©s curÃ© NOM
1 classes classe NOM
1 gendarmes gendarme NOM
1 Ã©lÃ©gance Ã©lÃ©gance NOM
1 avantage avantage NOM
1 Ã©vangile Ã©vangile NOM
1 cas cas NOM
1 mot mot NOM
1 constructeurs constructeur NOM
1 anniversaire anniversaire NOM
1 incident incident NOM
1 vigilance vigilance NOM
1 blanc blanc ADJ
1 cercle cercle NOM
1 L L NOM
1 au lieu de au lieu de PRE
1 rÃ©union rÃ©union NOM
1 Ã§~ Ã§~ X
1 entretien entretien NOM
1 mains main NOM
1 j~ j~ CLS
1 rÃ©seaux rÃ©seau NOM
1 tÃ©moins tÃ©moin NOM
1 rÃ©ponse rÃ©ponse NOM
1 siÃ¨ge siÃ¨ge NOM
1 Alsace Alsace NOM
1 lecture lecture NOM
1 tour tour NOM
1 niveau niveau NOM
1 talents talent NOM
1 placÃ©s placer VPP
1 royaume royaume NOM
1 ex ex NOM
1 exigence exigence NOM
1 site site NOM
1 Alain Alain NOM
1 Pascale Pascale NOM
1 millier millier NOM
1 responsabilitÃ© responsabilitÃ© NOM
1 cour cour NOM
1 habitant habitant NOM
1 matÃ©riaux matÃ©rial NOM
1 pieds pied NOM
1 Monsieur Monsieur NOM
1 mÃ©nage mÃ©nage NOM
1 difficultÃ©s difficultÃ© NOM
1 affaire affaire NOM
1 deux deux NUM
1 contestation contestation NOM
1 porte porte NOM
1 tension tension NOM
1 gauche gauche NOM
1 numÃ©ro numÃ©ro NOM
1 La le DET
1 engagement engagement NOM
1 passÃ© passÃ© NOM
1 aisthesis aisthesis NOM
1 mÃ©lange mÃ©lange NOM
1 fleuve fleuve NOM
1 observateur observateur NOM
1 diable diable ADJ
1 madame madame NOM
1 projet projet NOM
1 niÃ¨ce niÃ¨ce NOM
1 Ã©tat Ã©tat NOM
1 classe classe NOM
1 pneumatiques pneumatique NOM
1 prÃ©sidente prÃ©sidente NOM
1 Loire Loire NOM
1 photographie photographie NOM
1 maisons maison NOM
1 radiateur radiateur NOM
1 Mitterand Mitterand NOM
1 Jean Jean NOM
1 assurances assurance NOM
1 message message NOM
1 faire faire VNF
1 comportement comportement NOM
1 BÃ©rÃ©govoy BÃ©rÃ©govoy NOM
1 la plupart la plupart PRO
1 incendie incendie NOM
1 Gourcuff Gourcuff NOM
1 trains train NOM
1 pour pour NOM
1 tu~ tu~ CLS
1 partie partie NOM
1 maximum maximum NOM
1 terme terme NOM
1 appel appel NOM
1 chÃ¢teau chÃ¢teau NOM
1 aucun aucun PRO
1 travaux travaux NOM
1 base base NOM
1 autres autre ADJ
1 fauteuil fauteuil NOM
1 Mark Mark NOM
1 eau eau NOM
1 si si CSU
1 Organisation Organisation NOM
1 but but NOM
1 festival festival NOM
1 LiÃ¨ge LiÃ¨ge NOM
1 Commission Commission NOM
1 compatriotes compatriote NOM
1 General General NOM
1 squelette squelette NOM
1 clartÃ© clartÃ© NOM
1 ProgrÃ¨s ProgrÃ¨s NOM
1 delta delta NOM
1 population population NOM
1 accusÃ©s accusÃ© NOM
1 couleurs couleur NOM
1 police police NOM
1 syndicats syndicat NOM
1 cinÃ©ma cinÃ©ma NOM
1 parties party NOM
1 est-ce que est-ce que CSU
1 avions avion NOM
1 groupes groupe NOM
1 v~ v~ X
1 rayons rayon NOM
1 second second ADJ
1 fait fait NOM
1 est Ãªtre VRB
1 perte perte NOM
1 naÃ¯fs naÃ¯f NOM
1 table table NOM
1 RenÃ© RenÃ© NOM
1 membres membre NOM
1 Chine Chine NOM
1 officiers officier NOM
1 salaire salaire NOM
1 arrivÃ©e arrivÃ© NOM
1 Voix Voix NOM
1 maires maire NOM
1 vous lui CLS
1 coup coup NOM
1 peut pouvoir VRB
1 combats combat NOM
1 chiffres chiffre NOM
1 souvenirs souvenir NOM
1 tendance tendance NOM
1 Christophe Christophe NOM
1 compagne compagne NOM
1 aspect aspect NOM
1 Heinze Heinze NOM
1 baiser baiser NOM
1 prÃªtres prÃªtre NOM
1 images image NOM
1 les le DET
1 acteurs acteur NOM
1 modernitÃ© modernitÃ© NOM
1 paille paille NOM
1 culture culture NOM
1 places place NOM
1 Mandanda Mandanda NOM
1 page page NOM
1 artistes artiste NOM
1 points point NOM
1 automobilistes automobiliste NOM
1 PrÃ©fecture PrÃ©fecture NOM
1 question question NOM
1 peuples peuple NOM
1 enquÃªteurs enquÃªteur NOM
1 grands-parents grand-parent NOM
1 scÃ©narios scÃ©nario NOM
1 ils~ ils~ CLS
1 embouteillages embouteillage NOM
1 v~ v~ CLS
1 abstentionnisme abstentionnisme NOM
1 Ã©poque Ã©poque NOM
1 mortalitÃ© mortalitÃ© NOM
1 tourmente tourmente NOM
1 bien bien NOM
1 bases base NOM
1 mouche mouche NOM
1 guerre guerre NOM
1 tout tout ADV
1 succÃ¨s succÃ¨s NOM
1 philosophes philosophe NOM
1 exceptionnel exceptionnel ADJ
1 avantages avantage NOM
1 disons dire VRB
1 tÃªtes tÃªte NOM
1 poÃ¯Ã©sis poÃ¯Ã©sis NOM
1 chauss~ chauss~ NOM
1 dirigeants dirigeant NOM
1 oeuvre oeuvre NOM
1 herbe herbe NOM
1 expÃ©rience expÃ©rience NOM
1 contours contour NOM
1 utilisation utilisation NOM
1 MexÃ¨s MexÃ¨s NOM
1 justice justice NOM
1 prof prof NOM
1 Dieu Dieu NOM
1 taux taux NOM
1 dÃ©tenus dÃ©tenu NOM
1 crÃ©dits crÃ©dit NOM
1 Antoine Antoine NOM
1 servantes servante NOM
1 prix prix NOM
1 traiteur traiteur NOM
1 papa papa NOM
1 ministÃ¨res ministÃ¨re NOM
1 collÃ¨ge collÃ¨ge NOM
1 opinion opinion NOM
1 parcmÃ¨tres parcmÃ¨tre NOM
1 test test NOM
1 Ã©change Ã©change NOM
1 Paul Paul NOM
1 direction direction NOM
1 consÃ©quences consÃ©quence NOM
1 coopÃ©ration coopÃ©ration NOM
1 tsar tsar NOM
1 Nicolas Nicolas NOM
1 peinture peinture NOM
1 vacances vacance NOM
1 journÃ©e journÃ©e NOM
1 quel quel PRQ
1 ambiance ambiance NOM
1 Arts-Loi Arts-Loi NOM
1 activitÃ© activitÃ© NOM
1 Gallas Gallas NOM
1 l' une l' une PRO
1 quels quel PRQ
1 chorale chorale NOM
1 dÃ©clarations dÃ©claration NOM
1 LibÃ©ration LibÃ©ration NOM
1 ville ville NOM
1 malheureux malheureux ADJ
1 Ã©lysÃ©ologues Ã©lysÃ©ologue NOM
1 histoires histoire NOM
1 Ã©pidÃ©mies Ã©pidÃ©mie NOM
1 tables table NOM
1 confiance confiance NOM
1 OMS OMS NOM
1 Roger Roger NOM
1 agents agent NOM
1 contact contact NOM
1 gouverneur gouverneur NOM
1 loisir loisir NOM
1 dÃ©fense dÃ©fense NOM
1 ceux ceux-lÃ  PRO
1 tartines tartine NOM
1 procÃ¨s procÃ¨s NOM
1 Messi Messi NOM
1 rÃ©gion rÃ©gion NOM
1 AllÃ©gret AllÃ©gret NOM
1 informatique informatique NOM
1 sensibilitÃ© sensibilitÃ© NOM
1 autoritÃ©s autoritÃ© NOM
1 dossiers dossier NOM
1 lectures lecture NOM
1 l' le DET
1 mÃ©tro mÃ©tro NOM
1 stage stage NOM
1 organisme organisme NOM
1 poids poids NOM
1 film film NOM
1 penser penser VNF
1 accomplissement accomplissement NOM
1 moitiÃ© moitiÃ© NOM
1 bourgeoise bourgeois NOM
1 fiÃ¨vre fiÃ¨vre NOM
1 ont avoir VRB
1 tÃ©lÃ©visions tÃ©lÃ©vision NOM
1 Chaplin Chaplin NOM
1 livres livre NOM
1 univers univers NOM
1 doigt doigt NOM
1 les uns l'un PRO
1 inconvÃ©nients inconvÃ©nient NOM
1 tiers tiers NOM
1 mouvement mouvement NOM
1 incidents incident NOM
1 aspects aspect NOM
1 course course NOM
1 forum forum NOM
1 aigle aigle NOM
1 n'importe n'importe PRO
1 Barack Barack NOM
1 effectifs effectif NOM
1 Rozycki Rozycki NOM
1 regards regard NOM
1 Ãtat Ãtat NOM
1 restent rester VRB
1 combat combat NOM
1 signature signature NOM
1 elles elle CLS
1 Michel Michel NOM
1 croquis croquis NOM
1 croisent croiser VRB
1 GutiÃ©rrez GutiÃ©rrez NOM
1 lit lit NOM
1 i~ i~ CLS
1 intervention intervention NOM
1 Ã©diteur Ã©diteur NOM
1 premier premier ADJ
1 fÃªte fÃªte NOM
1 verbe verbe NOM
1 d~ d~ NOM
1 cycle cycle NOM
1 personnalitÃ© personnalitÃ© NOM
total of 8492 cases
#!/usr/bin/python
# -*- coding: utf-8 -*-

#######################################################################################
# this file allows to systematically correct a complete annotation project, 
# directly in the database
#######################################################################################


debug=False
debug=True
#debug=5



import traceback, copy, sys
from time	import mktime, time, asctime, localtime
from copy 	import deepcopy
sys.path.insert(0, '../lib')
from database 	import SQL
from goldPlatinumTransformConll import splice


##################################### bulkcorrection directly in the database ####################
		
def OLDcomplexbulkcorrectdic(dic):
	changed,newdic=False,False
	
	for i,node in dic.iteritems():
		print i, node["t"],node
		
		if node.get("t"," ") in ["$","L1","L2","L3"]:
			print i, node["t"],node
			print "found false token"
			newdic=integrate(dic,i,{}) # kicking out the token
			newdic, newchanged = complexbulkcorrectdic(newdic)
			changed = True
		
	if newdic:return newdic, changed
	else:return dic,changed
	
def OLDsimplebulkcorrectdic(dic):
	changed,newdic=False,False
	
	for i,node in dic.iteritems():
		print i, node["t"],node
		
		continue
	
	
		if node["cat"]=="Cl":		node["lemma"]=node["t"]
		
		if node["lemma"]=="ils":	node["lemma"]="il"
		elif node["lemma"]=="elles":	node["lemma"]="elle"
		elif node["lemma"]=="l'":	node["lemma"]="le"
		elif node["lemma"]=="la":	node["lemma"]="le"
		elif node["lemma"]=="les":	node["lemma"]="le"
		elif node["lemma"]=="c'":	node["lemma"]="ce"
		elif node["lemma"]=="m'":	node["lemma"]="me"
		elif node["lemma"]=="t'":	node["lemma"]="te"
		elif node["lemma"]=="s'":	node["lemma"]="se"
		elif node["lemma"]=="j'":	node["lemma"]="je"
		elif node["lemma"]=="n'":	node["lemma"]="ne"
		
	return dic,True	

	
	
	

def OLDcorrectfeatures(sentencetree, enterNr=None,enterDic=None):
	"""
	takes a dictionary structure: node number => feature structure
	applies changes
	gives back the new dictionary and the comparison to the old structure
	"""
	
	newsentencetree=deepcopy(sentencetree)
	
	
	if enterNr:
		newsentencetree=splice(newsentencetree,enterNr,enterDic)
	
	
	#return None,None
	
	govcats={}
	for i, node in sentencetree.iteritems():
		govcats[i]=[sentencetree[j].get("cat") for j in node.get("gov",{}) if j in sentencetree and "cat" in sentencetree[j] ]
	
	for i, node in newsentencetree.iteritems():
	
		#get short forms:
		cat = node.get("cat",None)
		t = node.get("t", None)
		token = node.get("token",None) # 
		lemma = node.get("lemma")
		mode = node.get("mode", None)
		
		
		
		#ajouter mode="indicative" et tense="present" pour tous les V qui n'ont pas de trait mode instanciÃ©
		if cat=="V" and not "mode" in node:
			node["mode"]="indicative"
			node["tense"]="present"
			
		#faire mode="infinitive" pour tous les V dont la forme se termine par er, ir, oir
		if cat=="V" and t[-2:]=="er" or t[-2:]=="ir" or t[-3:]=="oir" or t==u"rÃ©pondre" or t=="dire" or t=="reprendre" or t=="prendre" or t=="conduire" :
			node["mode"]="infinitive"
		
		#faire mode="infinitive" pour tous les V dont le lemme et la forme sont identique et se termine par re
		if cat=="V" and lemma==t and lemma[-2:]=="re":
			node["mode"]="infinitive"
		#changer mode= "gerundive" en mode="present_participle"
		if cat=="V" and mode=="gerundive":
			node["mode"]="present_participle"
		
		# faire mode="past_participle" et supprimer le trait tense pour tous les V qui dÃ©pendent d'un lemme="avoir" ou lemme="Ãªtre" par une relation pred
		# kim: changÃ© en : si verbe et dÃ©pend d'un verbe par pred
		if cat=="V" and "V" in govcats[i] and "pred" in node["gov"].values():
			if "tense" in node : del node["tense"]
			node["mode"]="present_participle"
		
		#faire mode="past_participle" et supprimer le trait tense pour tous les V dont la forme se termine par Ã©, i, u, Ã©e, Ã©s, Ã©es (on fait pas ie, ies, is parce que ca peut Ã©tre d'autres formes)
		if cat=="V" and (t[-1:]==u"Ã©" or t[-1:]==u"i" or t[-1:]==u"u" or t[-2:]==u"Ã©e" or t[-2:]==u"Ã©s" or t[-3:]==u"Ã©es"):
			if "tense" in node : del node["tense"]
			node["mode"]="past_participle"
		
		#faire mode="present_participle" et supprimer le trait tense pour tous les V dont la forme se termine par ant
		if cat=="V" and (t[-3:]==u"ant"):
			if "tense" in node : del node["tense"]
			node["mode"]="present_participle"
		
		
		#2) il y a aussi qq lemmes Ã  changer
		#faire lemme="Ã§a" quand forme="Ã§a"
		if t==u"Ã§a":
			node["lemma"]=u"Ã§a"
			
		#et faire forme="n'" quand token="n'" (IMPORTANT)
		if token=="n' " or token=="n'":  # TODO: regarder si c'est utile (et si on veut l'espace
			node["t"]=u"n' "
		
		
		#faire forme="qu' " quand token="qu' "
		if token=="qu' " or token=="qu'":
			node["t"]=u"qu' "
			
		
		
		
		if cat=="V" and mode=="conditional":
			node["mode"]="indicative"
			node["tense"]="conditional"
		
		
		#Pour les V il faudrait le faire une fois maintenant et une fois que tout aura Ã©tÃ© corrigÃ© (avant l'export) ????
		

		#3) supprimer les traits qui servent Ã  rien :
		dumbfeatures="""
		token
		que
		aux
		aux_req
		attention
		attentionFeature
		Vadj
		countable
		def
		dem
		det
		poss
		numberposs
		index
		wh
		control
		extraction
		neg
		sat
		enum
		hum
		nb
		sat
		time
		inv
		width
		features
		pcas
		real
		position""".strip().split()
		for df in dumbfeatures:
			if df in node: del node[df]
			#node.hasAttribute(df) : node.removeAttribute(df)
			
		#si catâ V supprimer trait mode
		if cat!="V" :
			if mode : del node["mode"]
		#pour les V
		else:
			#si modeâ past_particple|present_participle supprimer trait gender
			if mode!="past_particple":
				if "gender" in node : del node["gender"]
			#si modeâ  indicative supprimer trait tense, person
			if mode!="indicative":
				if "tense" in node :del node["tense"]
				if "person" in node :del node["person"]
			#si mode= infinitive supprimer trait numberposs
			if mode=="infinitive":
				if "numberposs" in node : del node["numberposs"]
		

		if cat=="CS" or cat=="I":
			if "gender" in node : del node["gender"]
			if "number" in node : del node["number"]
			if "person" in node : del node["person"]
			if "tense" in node : del node["tense"]
			
		if cat!="V":
			if "tense" in node : del node["tense"]
			
		if lemma and lemma[0]=="-": lemma=lemma[1:]
		if cat=="Adj" and not "gender" in node  : node["gender"]="masc/fem"
		
		#test:
		#lemma="xxx_"+lemma
		#lemma=lemma[4:]
		
		
		#rewrite values into the node
		if cat: node["cat"]=cat
		if t: node["t"]=t
		if token: node["token"]=token
		if lemma: node["lemma"]=lemma
		if mode: node["mode"]=mode
		

	return newsentencetree,(newsentencetree!=sentencetree or enterNr)


	#newdic, changed = complexbulkcorrectdic(sentencetree)
			#newdic, changed = simplebulkcorrectdic(sentencetree)
			#newdic, changed = correctfeatures(sentencetree)
			#newdic, changed = correctfeatures(sentencetree,4,{})
			#newdic, changed = correctfeatures(sentencetree,4,{	
				#'i1':{ u'person': u'3', u'number': u'sg', u'cat': u'V', u'lemma': u'\xeatre', u'token': u'est', u'tense': u'present', u'mode': u'indicative', 'gov': {'i0':'root'}, u't': u'A'},
				#'i2':{u'cat': u'Cl', u'lemma': u'c', u'token': u'-ce', u't': u'B', 'gov': {'i1': u'sub'}, 'child':None}
			#})
			
	
			
			#break



def correctLowerProperNouns(tree):
	changed={}
	for i, node in tree.iteritems():
		t,l,ca=node["t"],node["lemma"],node["tag"]
		if t!=t.lower() and l==l.lower() and i>1: # some upper case
			node["lemma"]=t
			changed[i]=(t,l)
			
	
	return tree, changed


def NNAAMMEE(tree):
	"""
	NNAAMMEE -> XXX
	"""
	for i, node in tree.iteritems():
		#if node["t"]=="XXX":
			#node["t"]="YYY"
			#node["lemma"]="YYY"
		if node["t"]=="NNAAMMEE":
			node["t"]="XXX"
			node["lemma"]="XXX"
			changed[i]="NNAAMMEE"
	return tree, changed


def bulkcorrectDB(project, treeids=[],commit=True):
	"""
	bulk correction of a whole project! very slow!
	
	better to do directly in sql, for example:
	#change all functions:
		#update links set function='dep' where function='det';

	"""
	
	sql = SQL(project)
	db,cursor=sql.open()
	
	if treeids:	a,v=["rowid"],treeids
	else:		a,v=[],[]
	
	allt=list(sql.getall(cursor, "trees",a,v))
	print "nb trees:",len(allt)
	ti = time()
	
	for nr, (treeid,sid,uid,annotype,status,comment,timestamp) in enumerate(allt):
		
		
		
		dic=sql.gettree(None,None,treeid, indb=db,incursor=cursor)
		if dic:
			tree=dic["tree"]
			newtree, changed=correctLowerProperNouns(tree)
			
			if changed:
				print "________________________________\n"
				ws,sentence,_ = sql.enterTree(cursor, newtree, sid, uid,tokensChanged=True)
				print sentence
				print "changed",changed
				if not nr%100:
					print "committing..."
					if commit:db.commit()
			if not nr%100:
				print "_____ treeid",treeid,"nr",nr+1,"/",len(allt),"---",int(float(nr+1)/(time()-ti)),"trees per second",int(float(len(allt)-nr+1)/(float(nr+1)/(time()-ti))),"seconds (",round(float(len(allt)-nr+1)/(float(nr+1)/(time()-ti))/60,1),"minutes) to go"
	if commit: db.commit()
	db.close()
	
		
	
if __name__ == "__main__":
	print "bonjour"
	#bulkcorrectDB("Rhapsodie")
	bulkcorrectDB("Platinum", [], commit=False)
	
	
#! usr/bin/python
#! coding: utf-8

import datetime, codecs, time, sys, os, glob, copy, re
from goldPlatinumTransformConll import splice
sys.path.insert(0, '../lib')
import conll



def lireToutLex(returnLex=False):
	"""
	fonction qui lit le lexique en mÃ©moire
	pour des testes de consistence de la lemmatisation et de l'Ã©quitetage
	"""
	
	allLex={} # tag -> { t->[lem1, lem2, ..], ... }
	allLem={} # lem -> tag
	
	for lexfile in glob.glob('../../../../Orfeo/lexique/*.sfplm'):
		
		# Lecture des fichiers .sfplm		
		
		catt = os.path.basename(lexfile)[:3]
		print "je lis",lexfile,catt
		#print "allLex",allLex
		dic=allLex.get(catt,{})
		#print dic
		# Ouverture d'un fichier de lexique
		with codecs.open(lexfile, "r", "utf8") as f:
			
			for ligne in f:
				if len(ligne) and "\t" in ligne:
					# correct errors:
					ligne=ligne.replace(u"â","'")
					ligne=ligne.replace(u"..",".")
					ligne=ligne.replace(u" -","-")
					ligne=ligne.replace(u"' ","'") # pour corriger l'entrÃ©e "qu' est -ce que" etc.
					ligne = ligne.replace("\t\t","\t")
					ligne = ligne.replace(" \t","\t")
					ligne = ligne.replace("\t ","\t")
					
					#print ligne.strip()
					cols = ligne.split("\t")
					if len(cols)==5: deco,t,cat,lem,morph = cols
					elif len(cols)==6: deco,t,cat,lem,morph,_ = cols
					else: qsdf # violent break. problem with dictionary!!!
					# specific corrections for french dictionary
					if lem[:2]=="s'":
						lem=lem[2:]
					elif lem[:3]=="se ":
						lem=lem[3:]
											
					#if deco not in ["S","P","X", "M", "MX"]:
						
						#if (not onlyLetters.match(t)):
							#if lexfile[:3]!="PCT" or len(t)>1: # virer qqs poncutation seule
					
					#if t in dic:
						#print "oh",ligne
						#print t,dic[t]
						#print t,lem
					if deco not in ["S"]:	
						dic[t]=dic.get(t,[])+[(deco,lem)]
					#print t,lem
					allLem[lem]=allLem.get(lem,[])+[cat]
		allLex[catt]=dic
		#print "MWE","MWE" in allLex,catt in allLex,catt=="MWE"
		#print len(dic),"entries"
		
		
	#for li in u"""
	#du de+le PRE
	#aux Ã +le PRE
	#au Ã +le PRE
	#j~ j~ CLS
	#c~ c~ CLS
	#""".strip().split("\n"):
		#cols=li.strip().split()
		#allLem[cols[1]] = allLem.get(cols[1],[])+[cols[2]]  
	
	
		#
		#print lexfile[:3],"a",len(allLex[lexfile[:3]]),u"entrÃ©es"
		
	allLex["ADV"][u"-mÃªme"]=[("A",u"mÃªme")]
	#print "MWE","MWE" in allLex
	if returnLex: 	return allLex
	else:		return allLem


	
def lireLexiqueCorr(infile="lexiqueCorr.txt", allLex={}):
	"""
	
	"""
	instructs=["L","LS","T","R","TM","M","LM","?","TT"]

	renum=re.compile(ur"\s\d+\s")
	treebankinstr=[]
	
	with codecs.open(infile, "r", "utf8") as f:
		for line in f:
			line=line.strip()
			if line and line[0]!="#":
				print line
				instr, rest = renum.split(line)
				ins = instr.strip().split()[0]
				entries,lexcat = rest.split("lexique: ")
				try:t,l,c=entries.split()
				except:t,l,c=entries.split("\t")
				c=c.strip()
				if ins=="L":
					print "j'ajoute au dict:",t,l,c
					allLex[c][t]=allLex[c].get(t,[])+[("A",l)]
				elif ins=="LM":
					newlem=" ".join(instr.strip().split()[1:])
					print "j'ajoute au dict:",t,"newlem:",newlem,c
					allLex[c][t]=allLex[c].get(t,[])+[("A",newlem)]
				elif ins=="LS":
					print "j'ajoute au dict:",t,l,c
					allLex[c][t]=allLex[c].get(t,[])+[("A",l)]
					print "je supprime du dict:",t,l,lexcat
					try:
						del allLex[lexcat][t]
					except:
						print "----- no entry to delete",t,l,lexcat
					if len(lexcat.split())>1:qsdf
					print "trebank: tous les",t,l,lexcat,u"doivent Ãªtre de cat",c 
					treebankinstr+=["\t".join(["LS trebank: tous les",t,l,lexcat,u"doivent Ãªtre de cat",c ])]
				elif ins=="T":
					newcat=" ".join(instr.strip().split()[1:]).strip()
					if not newcat:	qsdf
					print "j'ajoute au dict:",t,l,"newcat:",newcat
					allLex[newcat][t]=allLex[newcat].get(t,[])+[("A",l)]
					print "trebank: tous les",t,l,c,u"doivent Ãªtre de cat",newcat 
					treebankinstr+=["\t".join(["T trebank: tous les",t,l,c,u"doivent Ãªtre de cat",newcat ])]
				elif ins=="TM":
					newlem=" ".join(instr.strip().split()[1:])
					print "trebank: tous les:",t,l,c,u"doivent obtenir newlem:",newlem
					treebankinstr+=["\t".join(["TM trebank: tous les:",t,l,c,"doivent obtenir newlem:",newlem ])]
				elif ins=="TT":	
					newlem,newcat=" ".join(instr.strip().split()[1:]).split()
					print "trebank: tous les:",t,"doivent obtenir newlem:",newlem,newcat
					treebankinstr+=["\t".join(["TT trebank: tous les:",t,"doivent obtenir newlem:",newlem,newcat])]
	#print "\n"*11
	with codecs.open('corrinst.txt', "w", "utf8") as f:
		f.write( "\n".join(treebankinstr) )
	
	if False: # write new lexique
	#if True: # write new lexique
		for cat in allLex:
			print "___________",cat
			with codecs.open('../../../../Orfeo/lexique/lexiqueCorr/'+cat+".sfplm", "w", "utf8") as f:
				for t in sorted(allLex[cat]):
					for deco,l in sorted(set(allLex[cat][t])):
						f.write( "\t".join([t,l,deco])+"\n" )
						#if len(sorted(set(allLex[cat][t])))>1:
							#print sorted(set(allLex[cat][t]))
					
			
def funcsearch(infolder):
	errors={}
	numerrors=0
	for infile in sorted(glob.glob(os.path.join(infolder,"*"))): # .conll
		if not os.path.isfile(infile): continue
		basename=os.path.basename(infile)
		print "reading",basename
		trees = conll.conllFile2trees(infile)
		
		for tree in trees:
			for i, node in tree.iteritems():
				
				if node["gov"].values()[0] in ["dm"] and node["tag"] not in ["INT"] :
					key= " ".join([node["t"],node["lemma"],node["tag"]])
					#print key,"!!!"
					errors[key ]=errors.get(key,0)+1
					numerrors+=1
	with codecs.open("dm.txt","w","utf-8") as outf:
		for key in sorted(errors, key=errors.get, reverse=True):
			print errors[key],key
			outf.write(str(errors[key])+" "+key+"\n")
		print "total of",numerrors,"cases"
		outf.write(" ".join(["total of",str(numerrors),"cases"]))
	



			
def search(infolder):
	allLem=lireToutLex()
	errors={}
	numerrors=0
	for infile in sorted(glob.glob(os.path.join(infolder,"*"))): # .conll
		if not os.path.isfile(infile): continue
		basename=os.path.basename(infile)
		print "reading",basename
		trees = conll.conllFile2trees(infile)
		
		for tree in trees:
			#if hasVerbalDm(tree):
			#if isNonProjective(tree):
			for i, node in tree.iteritems():
				#if (node["lemma"] in allLem and node["tag"] in allLem[node["lemma"]]) and node["tag"] not in ["NOM"] and allLem.get(node["lemma"],None):
					#pass
					##print node["t"],node["lemma"],node["tag"],"ok"
				#else:
				if node["tag"] in ["NOM"] and node["tag"] not in allLem.get(node["lemma"],[]) and allLem.get(node["lemma"],[])==[]:
					key= " ".join([node["t"],node["lemma"],node["tag"],"lexique:"," ".join(sorted(set(allLem.get(node["lemma"],["pas dans le lexique"]))))])
					#print key,"!!!"
					errors[key ]=errors.get(key,0)+1
					numerrors+=1
	with codecs.open("problems.txt","w","utf-8") as outf:
		for key in sorted(errors, key=errors.get, reverse=True):
			print errors[key],key
			outf.write(str(errors[key])+" "+key+"\n")
		print "total of",numerrors,"errors"
		outf.write(" ".join(["total of",str(numerrors),"errors"]))
	

def fixOutname(name):
	if name.startswith("validated."): 
		name=name.split(".")[1]
		#name=name[len("validated."):]
	if name.endswith("lastHuman.conll"): name=name[:-len(".lastHuman.conll")]
	if name.endswith(".conll"): name=name[:-len(".conll")]
	return name

def createNonExistingFolders(path):
	head, tail = os.path.split(path)
	if head=="":
		if tail and not os.path.exists(tail): os.makedirs(tail)
	else:
		createNonExistingFolders(head)
	if head and not os.path.exists(head): os.makedirs(head)


def correct(tree,corrinst=[]):
	for i, node in tree.iteritems():
		t,l,ca=node["t"],node["lemma"],node["tag"]
		if t=="aujourd' hui":
			node["t"]="aujourd'hui"
			node["lemma"]="aujourd'hui"
		if t=="quelqu' un":
			node["t"]="quelqu'un"
			node["lemma"]="quelqu'un"	
		if t==u"c' est-Ã -dire":
			node["t"]=u"c'est-Ã -dire"
			node["lemma"]=u"c'est-Ã -dire"	
		if t==u"c' est-Ã -dire que":
			node["t"]=u"c'est-Ã -dire que"
			node["lemma"]=u"c'est-Ã -dire que"	
		if t==u"n' importe quoi":
			node["t"]=u"n'importe quoi"
			node["lemma"]=u"n'importe quoi"	
		if l=="La":node["lemma"]="le"
		if l=="Le":node["lemma"]="le"
		if l=="Les":node["lemma"]="le"
		if l=="L'":node["lemma"]="le"
		if l=="Aujourd'hui":node["lemma"]="aujourd'hui"
		
		if l=="il":node["tag"]="CLS"
		if l=="se":node["tag"]="CLI"
		
		for ty,a,b,c,d in corrinst:
			if ty=="TLS" and t==a and l==b and c==ca:
				print t,l,ca,"correcting",ty,a,b,c,d,"into",d
				node["tag"]=d
			elif ty=="TM" and t==a and l==b and c==ca:
				print t,l,ca,"correcting",ty,a,b,c,d,"into",d
				node["lemma"]=d
			elif ty=="TT" and t==a:
				if l!=b or c!=ca:
					print t,l,ca,"correcting",ty,a,b,c,d,"into",b,c
					node["lemma"]=b
					node["tag"]=c
				
		if l=="accord" and i-1 in tree and tree[i-1]["lemma"]=="de": # TODO: repair! doesn't redirect dependents of d'accord!!!
			#print node
			tree[i-1]["t"]=tree[i-1]["t"][0]+u"'accord"
			tree[i-1]["lemma"]=u"d'accord"	
			tree[i-1]["tag"]=u"INT"
			tree=splice(tree,i,{})
			tree=correct(tree)
			return tree
		
	return tree

def compil(infile):
	instr=[]
	with codecs.open(infile, "r", "utf8") as f:
		for li in f:
			col=li.strip().split("\t")
			
			if li.split()[0] in ["T","LS"]:
				instr+=[("TLS",col[1],col[2],col[3],col[5])]
			elif li.split()[0] in ["TM"]:
				instr+=[("TM",col[1],col[2],col[3],col[5])]
			elif li.split()[0] in ["TT"]:
				instr+=[("TT",col[1],col[3],col[4],"_")]
	return instr

def transform(infolder,outfolder,mixOldNew=False):
	createNonExistingFolders(outfolder)
	
	corrinst=compil('corrinst.txt')
	print len(corrinst),"rules"
	for infile in sorted(glob.glob(os.path.join(infolder,"*"))):
		if not os.path.isfile(infile): continue
		basename=os.path.basename(infile)
		print "reading",basename
		trees = conll.conllFile2trees(infile)
		newtrees=[]
		for tree in trees:
			if mixOldNew: newtrees+=[tree]
			newtree=copy.deepcopy(tree)
			newtree=correct(newtree,corrinst)
			newtrees+=[newtree]
			
			
		conll.trees2conllFile(newtrees,os.path.join(outfolder,fixOutname(basename)))
	

def searches():			
	
	#search("../projects/Platinum/export/")
	
	transform("../projects/Platinum/export/","../projects/Platinum/exportcorrected/")
	#search("../projects/Platinum/exportcorrected/")
	#funcsearch("../projects/Platinum/exportcorrected/")

if __name__ == "__main__":
	searches()
	#lireLexiqueCorr(allLex=lireToutLex(returnLex=True))
	#
# -*- coding: utf-8 -*-

import subprocess, re

gre=re.compile(r"\((\w+) (\w+)\)",re.M+re.U+re.I)

def zhparse(filename):
	""" parse utf8 encoded file
	give back couples word, pos
	"""
	a = subprocess.check_output('java -mx1500m -cp parser/stanford-parser.jar edu.stanford.nlp.parser.lexparser.LexicalizedParser -encoding utf-8 parser/xinhuaFactoredSegmenting.ser.gz {filename}'.format(filename=filename), shell=True, stderr=subprocess.STDOUT).decode("utf-8")
	for aa in a.split("\n\n"):
		for m in gre.finditer(aa):
			print "o"
			yield (m.group(1),m.group(2))
		yield (None,None)
		



#a=u"""(ROOT
  #(IP
    #(IP
      #(NP (NR æ°å))
      #(VP (VV ç½åäº¬)
        #(NP
          #(NP (NT ï¼æ) (NT ï¼ï¼æ¥))
          #(NP (NN çµ) (NN ç¹ç¨¿)))))
    #(PU ï¼)
    #(IP
      #(LCP
        #(NP (NN åé¦) (NN ä¼æ¤å¼é¢))
        #(LC ä¸­))
      #(NP
        #(NP (NR ç¾))
        #(NP (NN å³ç³»)))
      #(VP
        #(NP (NT æªæ¥))
        #(VP (VV ()
          #(NP (NN è®°è) (NN åå°æ) (NN èµµåæ) (NN åå¥) (NN éé) (NN )))
          #(QP
            #(NP (NT ï¼ï¼ï¼ï¼å¹´) (NT ï¼æ))
            #(NP (NT ï¼æ¥)
              #(CC è³)
              #(NT ï¼æ¥))))))
    #(PU ï¼)
    #(IP
      #(NP
        #(NP (NR ä¸­) (NR ç¾))
        #(NP (NN å³ç³»)))
      #(VP
        #(ADVP (AD å°))
        #(VP (VV è¿æ¥)
          #(NP
            #(QP (CD ä¸)
              #(CLP (M ä¸ª)))
            #(ADJP (JJ åå²æ§))
            #(NP (NN æ¶å»))))))
    #(PU ââ)
    #(IP
      #(NP (NR ä¸­å½) (NN å½å®¶) (NN ä¸»å¸­) (NN ä¹ è¿å¹³))
      #(VP
        #(ADVP (AD å°))
        #(PP (P å)
          #(NP
            #(NP (NR ç¾å½))
            #(NP (NN æ»ç») (NN å¥¥å·´é©¬))))
        #(PP (P å¨)
          #(NP
            #(NP (NR ç¾å½))
            #(NP (NN å å©ç¦å°¼äºå·))))
        #(VP (VV ä¸¾è¡)
          #(NP (NN ä¼æ¤)))))
    #(PU ã)))

#(ROOT
  #(IP
    #(NP (PN è¿))
    #(VP (VC æ¯)
      #(UCP
        #(IP
          #(NP
            #(NP (NR ä¸­) (NR ç¾))
            #(QP (CD ä¸¤))
            #(NP (NN å½)))
          #(VP
            #(VP (VV å®æ)
              #(NP (NN é¢å¯¼) (NN å±)))
            #(VP (VV æ¢å±))))
        #(PU ã)
        #(NP
          #(DNP
            #(LCP
              #(IP
                #(NP
                  #(NP
                    #(QP (CD ä¸¤))
                    #(NP (NN å½)))
                  #(NP (NN å³ç³»)))
                #(VP (VV è¿å¥)
                  #(NP
                    #(ADJP (JJ æ°))
                    #(NP (NN æ¶æ)))))
              #(LC å))
            #(DEG ç))
          #(QP (OD é¦)
            #(CLP (M æ¬¡)))
          #(NP (NN å½å®¶) (NN åé¦) (NN ä¼æ¤)))))
    #(PU ã)))
#"""


#if already segmented:
#run("java -mx500m -cp stanford-parser.jar edu.stanford.nlp.parser.lexparser.LexicalizedParser -encoding utf-8 chineseFactored.ser.gz data/chinese-onesent-unseg-utf8.txt")
# if not
#run("java -mx500m -cp stanford-parser.jar edu.stanford.nlp.parser.lexparser.LexicalizedParser -encoding utf-8 xinhuaFactoredSegmenting.ser.gz zh.utf-8")

if __name__ == "__main__":
	#zhparse("zh.utf-8")
	for (w,c) in zhparse("zh.utf-8"):
		print w,c
#!/usr/bin/python
# -*- coding: utf-8 -*-

#import cgi
#from xml.dom import minidom
#from xml.dom.ext import PrettyPrint
#from datetime import date
#from time     import mktime, time, asctime, localtime
from sqlite3   import connect
#from os       import environ
#from sys      import exit

#import traceback,
#import sys, os, re
#import makeXML
#import codecs

sys.path.insert(0, '../lib')
from config import *

#sys.path.append('modules')

debug=False

	
def open():
	db = connect(dbpath)
	cursor = db.cursor()	
	return db, cursor


### stuff only needed for importing xml to corpus 

def xml2SentenceListe(filename,minWords=3,letterMinQuota=0.7,upperMaxQuota=0.3, minSentPerArticle=3):
	""" xml input file: 	type est rÃ©publicain CNRTL
		output: 	artiList: list of articles: one line per sentence, double new line for paragraphs
			sentences: list of good sentences: (sentenceNumber, articleNumber, sentence)
	"""
	
	#maxartis=1
	
	repoint = re.compile(ur"((?<! [0-9A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÄÃÃÃÃÅÃÃÃÃÃÃÃÃ])([\.\?\!â¦] +[Â»\" ]*)(?=[Â« ]*?[A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÄÃÃÃÃÅÃÃÃÃÃÃÃÃ])|_)")
	#(?<!\n[Â«Â»\-\" ])[Â«Â»\-\" ]*
	startWords=ur"(Il|Elle|Ils|Elles|Ã|Et|Si|On|C'est|Sur) "
	#reStartWords = re.compile(ur"(?<!\n) "+startWords,re.U)
	reFindStartWords = re.compile(ur"(\w[Â»\" ]*)([\- ]*?"+startWords+")",re.U)
	
	respace= re.compile(ur"\s+",re.U+re.M)
	reellipsis= re.compile(ur"\.\.\.")
	renotLetter=re.compile(ur"[\W0-9]",re.U)
	reSentEnd=re.compile(ur"([\.\?\!â¦][Â»\" ]*)$")
	reSentStart=re.compile(ur"^[Â«Â»\-\" ]*[0-9A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÄÃÃÃÃÅÃÃÃÃÃÃÃÃ]")
	reUpper=re.compile(ur"[A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÄÃÃÃÃÅÃÃÃÃÃÃÃÃ]")
	
	doc = minidom.parse(filename)
	sentences=[]
	badSent=[]
	articles = doc.getElementsByTagName("div")
	artiList=[]
	articount=0
	#totalphrasecount=0
	cc=0
	for arti in articles:
		
		keys = arti.attributes.keys()
		if "type" in keys and  arti.attributes["type"].value == "article":
			
			
			#print "___________________",
			#print arti.toprettyxml()
			sennb=0
			artigoodsents,artibadsents = [],[]
			#for h in arti.getElementsByTagName("head") :
				#print "!!!!",h.nodeName, h.attributes
			headers=[h for h in arti.getElementsByTagName("head") if not h.attributes ]
			
			parags=arti.getElementsByTagName("p")
			#print arti.childNodes,headers, parags
			if len(headers)==1 and len(parags)>0: # only if the article has a unique title and at least a paragraph
				#print "title", unicode(headers[0].childNodes[0].nodeValue)
				#print "number parags",len(parags)		
				artitext= respace.sub(u" ", unicode(headers[0].childNodes[0].nodeValue))+"\n\n"
				
				for p in parags:
					
					mapar = unicode(p.childNodes[0].nodeValue)
					# make text nice:					
					mapar = respace.sub(u" ",mapar)
					mapar = reellipsis.sub(u"â¦",mapar).strip()
					# sentences in a line:
					mapar = unicode(repoint.sub(ur"\1*\n",mapar).strip())
					# some sentence startes that are not preceded by correct punctuation
					if reFindStartWords.search(mapar):
						#print "\n\navant===================="
						#print mapar.encode("utf-8")
						#print "aprÃ¨s===================="
						mapar = reFindStartWords.sub(r"\1*\n\2",mapar)#.encode("utf-8")
						cc+=1
					#adding paragraph to artitext:
					artitext+=mapar.replace("*\n","\n")+"\n\n"	
					#print mapar.encode("utf-8")
					
					#looking at eache potential sentence:
					phrlist = mapar.split("*\n")
					for s in phrlist:
						bloed=renotLetter.sub("",s)
						noUpper=reUpper.sub("",s)
						#totalphrasecount+=1
						sennb+=1
						# testing if
						# 1. enough letters, 2. not too many upper cases 
						# 3. sentence ends and 4. starts correctly
						# 5. enough words
						#if len(s.split())<minWords: print "!!!!!!!!!!!!",s,len(s.split())
						if float(len(bloed))>letterMinQuota*float(len(s)) and float(len(noUpper))>upperMaxQuota*float(len(s)) and reSentEnd.search(s) and reSentStart.search(s) and len(s.split())>=minWords:
							
							artigoodsents+=[(sennb,articount,s.strip())]
							
							
						else: 
							artibadsents+=[(sennb,articount,s.strip())]
			
			if len(artigoodsents)>=minSentPerArticle:
				if not articount%100 : print articount,"articles"
				artiList+=[artitext.strip()]
				sentences+=artigoodsents
				badSent+=artibadsents
				articount+=1
		#if articount>=maxartis: 
			#print "breaking::::::::"
			#break

	print "nb articles",len(artiList)
	#print cc,"remplacement d'urgence"
	#print sennb,"total"#,mapar.split("\n")
	print len(badSent),"bad"
	
	print len(sentences),"good"
	
	#print badSent
	#print sentences
	
	return artiList,sentences


def corpus2db(sentences, articles, corpusName):
	
	#sentences: list of good sentences: (sentenceNumber, articleNumber, sentence)
	
	db,cursor=open()
	cursor.execute("INSERT OR IGNORE INTO corpora (name) VALUES (?) ",(corpusName,))
	cursor.execute('select rowid from corpora where name=?', (corpusName,))
	corpusid, = cursor.fetchone()
	
	for i,article in enumerate(articles):	
		cursor.execute("INSERT OR IGNORE INTO articles (text, corpusid, articlenb) VALUES (?,?,?) ",(article,corpusid,i,))
	
	
	# nb of words, nb of chars, sentence, article number, phrasenumber
	for sentenceNumber, articleNumber, sentence in sentences:	
		cursor.execute("INSERT OR IGNORE INTO sentences (sentence,corpusid,articlenb,sentencenb) VALUES (?,?,?,?) ",(sentence,corpusid,articleNumber,sentenceNumber,))
	
		
	db.commit()
	db.close()
	
	
	
	
def xmldb(xmlfile, corpusName):
	
	artis,ses=xml2SentenceListe(xmlfile)
	print "artis:",len(artis)
	print "sentences:",len(ses)
	corpus2db(ses,artis,corpusName)
	

### end: stuff only needed for importing xml to corpus 

############################# distributing sentences ###############################

def addASentence(annoexoid,userid,corpusid,nbAnnos,nbWordsPers,common):
	if debug:	print"** adding a sentence"
	nbWords=0
	foundSentence=False
	db,cursor=open()
	# get half finished annotation sentences
	sqlcom="""select annoexoattribution.userid,sentences.rowid,sentences.sentence
			from
			(select rowid as sentid from
				(select  sentences.rowid, annoexoattribution.userid
					from sentences,  annoexoattribution 
					where annoexoattribution.annoexoid=?
					and sentences.rowid=annoexoattribution.sentenceid
					) group by rowid having  COUNT( userid) <?
				), sentences, annoexoattribution
			where sentid=sentences.rowid
			and annoexoattribution.annoexoid=?
			and sentences.rowid=annoexoattribution.sentenceid
					;
			"""
	u2s,s2u,s2sent={},{},{}
	slist=[]
	u2s[userid]=[]
	for u,s,sent in db.execute(sqlcom, (annoexoid,nbAnnos,annoexoid,)):
		#print u,s,sent
		u2s[u] =	u2s.get(u,[])+[s]
		s2u[s] = 	s2u.get(s,[])+[u]
		slist+=[s]
		s2sent[s]=sent
	# compute correspondance
	#print "u2s,s2u",u2s,s2u
	for s in slist: # try all half annototed sentences
		#sent=s2sent[s]
		if s not in u2s[userid]: # s not already attributed to userid
			bad=False
			if debug:	print "trying sentence",s
			for u in s2u[s]: # try all users that have this sentence
				if u!=userid:
					#computeCorrespondance
					corr=computeCommons(1,nbWordsPers,u,userid,s)
					if debug:	print "corr",corr,"with user",u
					#commonw=0
					#for commonsent in list(set(u2s[u]) & set(u2s[userid]))+[s]: # all common sentences including the hypthetical s
						
						#commonw+=len(s2sent[commonsent].split())
					#print "commonsent",list(set(u2s[u]) & set(u2s[userid])),"user",u
					#if (float(commonw)/nbWordsPers)*100 > common:
					if corr*100>common:
						bad=True
						if debug:	print "bad user:",u,
						if debug:	print "thus, bad sentence",s
						break
				if bad==True:
					if debug:	print "next sentence"
					break
			if not bad:
				foundSentence=True
				if debug:	print "found",s
				sentid,sentence=s,s2sent[s]
				break
	
	
	if not foundSentence:
		sqlcom="""SELECT sentences.rowid, sentences.sentence  FROM sentences LEFT JOIN annoexoattribution ON (sentences.rowid = annoexoattribution.sentenceid ) 
			WHERE annoexoattribution.rowid IS NULL 
			and sentences.corpusid=?"""
		sentid,sentence, = cursor.execute(sqlcom,(corpusid,)).fetchone()
	
	cursor.execute("INSERT OR IGNORE INTO annoexoattribution (annoexoid,userid,sentenceid) VALUES (?,?,?) ",(annoexoid,userid,sentid,))
	if debug:	print "added",sentid#,sentence
		
	db.commit()
	db.close()
	
def getAllAttributions(annoexoid):
	db,cursor=open()
	sqlcom="""select annoexoattribution.userid,sentences.rowid,sentences.sentence
			from sentences, annoexoattribution
			where annoexoattribution.sentenceid=sentences.rowid
			and annoexoattribution.annoexoid=?
			and sentences.rowid=annoexoattribution.sentenceid
			;
			"""
	treesqlcom="""select distinct users.realname, users.user, trees.rowid
		from trees, words, users
			where trees.sentenceid=?
			and trees.userid=?
			and words.treeid=trees.rowid
			and users.rowid=trees.userid
			;
			"""
	u2s,s2u,s2sent,s2treeuser={},{},{},{}
	slist=[]
	
	for u,s,sent in db.execute(sqlcom, (annoexoid,)):
		# for each user id, sentence id, sentence that was attributed
		#print u,s,sent
		u2s[u] =	u2s.get(u,[])+[s]
		s2u[s] = 	s2u.get(s,[])+[u]
		slist+=[s]
		s2sent[s]=sent
		treeid = cursor.execute(treesqlcom,(s,u,)).fetchone()
		if treeid: s2treeuser[s]=[(treeid[0],treeid[1],treeid[2])]
		else: s2treeuser[s]=[]
	#print "s2treeuser",s2treeuser	
	#db.commit()
	db.close()
	
	#print "getAllAttributions",u2s
	return u2s,s2u,s2sent,s2treeuser


	
	
def printAllCommons(annoexoid,nbWordsPers):
	u2s,s2u,s2sent,s2treeuser=getAllAttributions(annoexoid)
	s2wl={}
	for s in s2u.keys():
		s2wl[s]=float(len(s2sent[s].split())) 
	
	for u,s in u2s.iteritems():
		#wl=s2wl[s]
		for uu,ss in u2s.iteritems():
			if u<uu:
				#wwl=s2wl[ss]
				c=list(set(s)&set(ss))
				#if debug:	print "u",u,"uu",uu,":",s,ss,c,"common",
				cwl=0
				for sss in c:
					cwl+=s2wl[sss]
				#if debug:	print cwl,cwl/nbWordsPers

def computeCommons(annoexoid,nbWordsPers,u1=1,u2=2,hyposen=None):
	u2s,s2u,s2sent,s2treeuser=getAllAttributions(annoexoid)
	s2wl={}
	for s in s2u.keys():
		s2wl[s]=float(len(s2sent[s].split())) 
	
	
	u2s[u1]=u2s.get(u1,[])
	u2s[u2]=u2s.get(u2,[])
	c=list(set(u2s[u1])&set(u2s[u2]))
	if hyposen: c+=[hyposen]
	#print "u",u,"uu",uu,":",s,ss,c,"common",float(len(c))/len(s),
	cwl=0.0
	for sss in c:
		cwl+=s2wl[sss]
	#print cwl,cwl/nbWordsPers
	return cwl/nbWordsPers
	

def removeAttributions(annoexoid,userid):
	db,cursor=open()
	rc=cursor.execute("delete from annoexoattribution where annoexoid=? and userid=? ",(annoexoid,userid,) ).rowcount
	if debug:	print "removeAttributions: I just deleted", rc, "rows"
	db.commit()
	db.close()
	
def searchAnnoexo(annoexoid,userid):
	"""
	serch for all the annotations of userid in the annoexe with id annoexeid
	gives back loads of information 
	"""
	nbWords=0
	db,cursor=open()
	
	sqlcom="""select annoexo.corpusid, annoexo.nbAnnos, annoexo.nbWordsPers, annoexo.common
			from annoexo 
				where annoexo.rowid=?
			;
			"""
	re=cursor.execute(sqlcom,(annoexoid,)).fetchone()
	if re:
		corpusid, nbAnnos, nbWordsPers, common, = re
	else: 
		print "error"
		return 
	
	sids,sentences,articles=[],{},[]
	sqlcom="""select sentences.rowid,sentences.sentence, articles.text, annoexo.corpusid, annoexo.nbAnnos, annoexo.nbWordsPers, annoexo.common
			from sentences, articles, annoexo, annoexoattribution 
				where annoexoattribution.userid=?
				and annoexo.rowid=?
				and annoexoattribution.annoexoid=annoexo.rowid
				and sentences.rowid=annoexoattribution.sentenceid
				and articles.corpusid=annoexo.corpusid
				and sentences.articlenb=articles.articlenb
			;
			"""
	
	for sid, sentence, article, corpusid, nbAnnos, nbWordsPers, common in db.execute(sqlcom, (userid,annoexoid,)):
		l= len(sentence.split())
		nbWords+=l
		sids+=[sid]
		sentences[sid]=sentence
		articles+=[article]
	db.commit()
	db.close()
	return sids,sentences,nbWords,articles, corpusid, nbAnnos, nbWordsPers, common

def getSentencesAnnoexo(annoexoid,userid):
	"""
	get all the sentences for userid for the annoexe annotation exercise
	
	"""
	sids,sentences,nbWords,articles, corpusid, nbAnnos, nbWordsPers, common=searchAnnoexo(annoexoid,userid)
	if debug:	print "user",userid,"____________start nbWords,nbWordsPers",nbWords,nbWordsPers
	while nbWords<nbWordsPers:
		if debug:	print "________________________ nbWords,nbWordsPers",nbWords,nbWordsPers
		
		
		addASentence(annoexoid,userid,corpusid,nbAnnos,nbWordsPers,common)
		sids,sentences,nbWords,articles, corpusid, nbAnnos, nbWordsPers, common=searchAnnoexo(annoexoid,userid)
	#nbWords OK	
	print  "You got",nbWords,"words to annotate, the minimum for this exercise being",nbWordsPers,"words. Hopefully you like the sentences. Enjoy annotating!"
	return sids,sentences
		#print article

def addAnnoTrees(userid, sentences={},s2treeuser={},exos={},total=0):
	db,cursor=open()

	#print userid+"***"
	sqlcom="""select distinct sentences.rowid,sentences.sentence, users.realname, users.user ,  
			trees.rowid, exos.rowid , exos.exo
				from trees, words, users, sentences, annoexoattribution, exos, annoexo
				where trees.userid=?
				and words.treeid=trees.rowid
				and users.rowid=trees.userid
				and annoexoattribution.sentenceid=sentences.rowid
				and trees.sentenceid=sentences.rowid
				and exos.rowid=annoexo.exoid
				and annoexoattribution.annoexoid=annoexo.rowid
				
			;
			"""
	for sid,sentence, realname, user, treeid, exoid, exoname in db.execute(sqlcom, (userid,)):		
		sentences[sid]=sentence
		s2treeuser[sid]=[(realname, user, treeid)]
		exos[sid]=[(exoid, exoname)]
		total+=1
		
	#print sentences
	db.close()
	return sentences,s2treeuser,exos,total
	

def getSentences(exoid,userid,onlyUser,start,number,adminlevel):
	
	#TODO adminlevel: show all annotated sentences
	#TODO start, number

	# TODO OOOOOOOOOO: reintroduce exo!!!!!!!
	
	exos={}
	#print "***<br>***<br>***<br>***<br>",exo
	db,cursor=open()
	if exo:
		sqlcom="""select annoexo.rowid
				from annoexo 
					where annoexo.exoid=?
				;
				"""
		re=cursor.execute(sqlcom,(exoid,)).fetchone()
		if re:
			annoexoid, = re
		else: 
			print "error"
			return 
		if debug:	print "annoexoid,userid",annoexoid,userid
		sids,sentences = getSentencesAnnoexo(annoexoid,userid)
		
		u2s,s2u,s2sent,s2treeuser = getAllAttributions(annoexoid)
		for s in sids:
			
			exos[s]=[(exoid,exo)]
	else:
		sentences,s2treeuser,exos=addAnnoTrees(userid)
		sids=sentences.keys()
	
	#print sentences
	return sentences,s2treeuser,exos,len(sids)
	
	
	
#def attributeSentences
	

if __name__ == "__main__":
	print "bonjour"
	
	# insert in corpus !!!!!!!!!!!!!! important !!!!!!!
	#xmldb("corpus/Annee2003/2003-02-24.xml","EstRep-2003-02-24")
	####################################################"
	for us in [5,6,7,8]: removeAttributions(1,us)
	#removeAttributions(1,6)
	#removeAttributions(1,7)
	#removeAttributions(1,4)
	##si,se = getSentencesAnnoexo(1,1)
	#print si#,se
	#print len(si),"sentences"
	#print" __________________"
	#si,se = getSentencesAnnoexo(1,2)
	#print si#,se
	#print len(si),"sentences"
	#print" __________________"
	#si,se = getSentencesAnnoexo(1,8)
	#print si#,se
	#print len(si),"sentences"
	#removeAttributions(1,3)
	print "========="
	a,b,c = getAllAttributions(1)
	
	print "(partly) distributed sentences",len(b)
	print b
	di={}
	for s,ul in b.iteritems():
		di[len(ul)]=di.get(len(ul),0)+1
	
	print "annotation number to sentences",di
	#computeCommons(1,1,2)
	printAllCommons(1,300)
	
	
	#c=0
	#for s in se:
		#for w in s.split():
			#c+=1
			#print c,w
	
	#addASentence(1,1,1,3,300,33)
	
	
ï»¿#!/usr/bin/python
# -*- coding: utf-8 -*-

from xml.dom import minidom
#from xml.dom.ext import PrettyPrint
import os,codecs,urllib,hashlib

sys.path.insert(0, '../lib')
from config import *

debug=True
debug=False

emptyeric="""<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<dependencies>
</dependencies>"""

def parseric(text, correction):
	if debug:print "xxxparsing:",text.encode("utf-8")
	cache= path+'cache/'
	
	md5hex = hashlib.md5(text.encode("utf-8")).hexdigest()
	analysisname= cache+text.encode("utf-8")[:10]+md5hex+'.eric.xml'
	
	if not os.path.isfile(analysisname):# if the sentence has never been analyzed:
		params={"grammar":"frmgtel","forest":"xmldep","view":"none","disambiguate":"yes", "sentence":text.encode("iso-8859-1"),"history":"<none>","hidden":"<none>","filename":"","Action":"Submit", ".cgifields":"save_history",".cgifields":"disambiguate",".cgifields":"examples"}
		params=urllib.urlencode(params)
		url="http://alpage.inria.fr/perl/parser.pl"
		try:	
			xmlcontent=urllib.urlopen(url, params).read().decode("iso-8859-1")
		except:
			print "<br><h1>no connection to",url,"</h1>"
			return 0,0
		#print params,xmlcontent.encode("utf-8")
		if xmlcontent.strip()==emptyeric: return -111,-111
		analysisfile=codecs.open(analysisname,"w","iso-8859-1")
		analysisfile.write(xmlcontent)
		analysisfile.close()
	doc = minidom.parse(analysisname)
	wordList=[]
	words={}
	links={}
	
	lastcluster={}
	goodNodeAttris=["form","lemma","cat","cluster"]
	goodEdgeAttris=["source","target","label","type"]
	
	for n in doc.getElementsByTagName("dependencies"):
		for nn in n.childNodes:
			if nn.nodeName=="node":
				id = nn.getAttribute("id")
				#print "node",id
				attris={}
				for k in nn.attributes.keys():
					if k in goodNodeAttris:
						attris[k]= nn.getAttribute(k)
						if k=="cluster":
							attris["idnum"]= int(nn.getAttribute(k).split("_")[1])
						elif k=="form": 
							if not attris[k]: attris[k]="---"
							elif attris[k]=="_NUMBER" :
								attris[k]=lastcluster["token"]
								attris["cat"]="det"
							elif attris[k]=="_LOCATION" :
								attris[k]=lastcluster["token"]
								attris["cat"]="np"
							elif attris[k]=="_ADRESSE" :
								attris[k]=lastcluster["token"]
								attris["cat"]="np"
							elif attris[k]=="_COMPANY" :
								attris[k]=lastcluster["token"]
								attris["cat"]="np"
							elif attris[k]=="_HEURE" :
								attris[k]=lastcluster["token"]
								attris["cat"]="np"
							elif attris[k].startswith("_DATE" ): # reason: _DATE_artf
								attris[k]=lastcluster["token"]
								attris["cat"]="np"
							elif attris[k].startswith("_PERSON" ): # reason: _PERSON_f
								attris[k]=lastcluster["token"]
								#print "___",attris[k]
								attris["cat"]="np"
								#attris[u"lemma"]="uuuuuuuuu"#attris[k] # TODO: why why why...
								#print "mmmmmmmmmmmmmm",k,attris[k]
							#TODO: other things to change here???
					
				words[id]=attris
				wordList+=[(attris["idnum"],id)]
			elif nn.nodeName=="edge":
				#print "edge"
				attris={}
				for k in nn.attributes.keys():
					if k in goodEdgeAttris: attris[k]= nn.getAttribute(k)
				links[nn.getAttribute("id")]=attris
			elif nn.nodeName=="cluster":
				#lastcluster["id"]=nn.getAttribute("id")
				lastcluster["token"]=nn.getAttribute("token")
				

	
	#print wordList
	#print words
	wordList.sort()
	wordList=[(i,n[1]) for i,n in enumerate(wordList)]  # n[1]: first char of eric's id code
	#print wordList
	lw = [(id,i) for i,id in wordList]
	for n,(i,id) in enumerate(wordList):
		words[id]["idnum"]=i
		words[id]["nnn"]=n # TODO: use this!!!!!
	
	for l in links:
		#print "target",words[links[l]["target"]], "source",words[links[l]["source"]],links[l]["label"]
		words[links[l]["target"]]["gov"]='"'+str(words[links[l]["source"]]["idnum"])+'":"'+links[l]["label"]+'"'
		words[links[l]["target"]]["gg"]=(words[links[l]["source"]]["idnum"],links[l]["label"])
		#words[links[l]["target"]]["gov"]=links[l]["source"]+':'+links[l]["label"]

	
	
	
	wordfeatures=[words[n] for i,n in wordList]
	indeces,ws=[],{}
	for i,n in wordList:
		indeces+=[i]
		ws[i]=words[n]
		
	####################### here:
	if correction: 
		wordfeatures=eric2rhapsodie(indeces, ws)
		sentence = " ".join([words[w[1]]["form"] for w in wordList if words[w[1]]["form"]!="---"]) #
	else:
		sentence = " ".join([words[w[1]]["form"] for w in wordList ])
	if debug:	print "sentence",sentence.encode("utf-8"),wordfeatures
	##############################
	
	return sentence,wordfeatures
	
	#print "________wordfeatures",wordfeatures
	
	


def eric2rhapsodie(indeces, ws):
	"""
	example
	indeces:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] 
	ws: {
	0: {'nnn': 0, u'form': u'une', u'lemma': u'un', 'idnum': 0, u'cat': u'det', 'gg': (5, u'det'), u'cluster': u'1c_0_1', 'gov': u'"5":"det"'}, 
	1: {'nnn': 1, u'form': '---', u'lemma': u'', 'idnum': 1, u'cat': u'comp', 'gg': (3, u'comp'), u'cluster': u'1c_0_0', 'gov': u'"3":"comp"'}, 
	2: {'nnn': 2, u'form': '---', u'lemma': u'', 'idnum': 2, u'cat': u'start', 'gg': (3, u'start'), u'cluster': u'1c_0_0', 'gov': u'"3":"start"'}, 
	3: {'nnn': 3, u'form': '---', u'lemma': u'', 'idnum': 3, u'cat': u'S', u'cluster': u'1c_0_0'}, 
	4: {'nnn': 4, u'form': u'petite', u'lemma': u'petit', 'idnum': 4, u'cat': u'adj', 'gg': (5, u'N'), u'cluster': u'1c_1_2', 'gov': u'"5":"N"'}, 
	5: {'nnn': 5, u'form': u'rue', u'lemma': u'rue', 'idnum': 5, u'cat': u'nc', 'gg': (1, u'N2'), u'cluster': u'1c_2_3', 'gov': u'"1":"N2"'}, 
	6: {'nnn': 6, u'form': u'qui', u'lemma': u'qui', 'idnum': 6, u'cat': u'prel', 'gg': (8, u'subject'), u'cluster': u'1c_3_4', 'gov': u'"8":"subject"'}, 
	7: {'nnn': 7, u'form': '---', u'lemma': u'', 'idnum': 7, u'cat': u'N2', 'gg': (5, u'N2'), u'cluster': u'1c_3_3', 'gov': u'"5":"N2"'}, 
	8: {'nnn': 8, u'form': u'part', u'lemma': u'partir', 'idnum': 8, u'cat': u'v', 'gg': (7, u'SRel'), u'cluster': u'1c_4_5', 'gov': u'"7":"SRel"'}, 
	9: {'nnn': 9, u'form': u'\xe0', u'lemma': u'\xe0', 'idnum': 9, u'cat': u'prep', 'gg': (8, u'preparg'), u'cluster': u'1c_5_6', 'gov': u'"8":"preparg"'},
	10: {'nnn': 10, u'form': u'droite', u'lemma': u'droite', 'idnum': 10, u'cat': u'nc', 'gg': (9, u'N2'), u'cluster': u'1c_6_7', 'gov': u'"9":"N2"'}, 
	11: {'nnn': 11, u'form': u'de', u'lemma': u'de', 'idnum': 11, u'cat': u'prep', 'gg': (12, u'PP'), u'cluster': u'1c_7_8', 'gov': u'"12":"PP"'}, 
	12: {'nnn': 12, u'form': '---', u'lemma': u'', 'idnum': 12, u'cat': u'VMod', 'gg': (8, u'vmod'), u'cluster': u'1c_7_7', 'gov': u'"8":"vmod"'}, 
	13: {'nnn': 13, u'form': u'le', u'lemma': u'le', 'idnum': 13, u'cat': u'det', 'gg': (14, u'det'), u'cluster': u'1c_8_9', 'gov': u'"14":"det"'}, 
	14: {'nnn': 14, u'form': u'cin\xe9ma', u'lemma': u'cin\xe9ma', 'idnum': 14, u'cat': u'nc', 'gg': (11, u'N2'), u'cluster': u'1c_9_10', 'gov': u'"11":"N2"'}, 
	15: {'nnn': 15, u'form': '---', u'lemma': u'', 'idnum': 15, u'cat': u'end', 'gg': (16, u'Punct'), u'cluster': u'1c_10_10', 'gov': u'"16":"Punct"'}, 
	16: {'nnn': 16, u'form': '---', u'lemma': u'', 'idnum': 16, u'cat': u'S', 'gg': (3, u'S'), u'cluster': u'1c_10_10', 'gov': u'"3":"S"'}}
	
	example
	Pierre a perdu --- --- 
	[{'nnn': 0, u'form': u'Pierre', u'lemma': u'_PERSON', 'idnum': 0, 'cat': u'np', 'gg': (2, u'subject'), u'cluster': u'1c_0_1', 'gov': u'"2":"subject"'}, {'nnn': 1, u'form': u'a', u'lemma': u'avoir', 'idnum': 1, u'cat': u'aux', 'gg': (2, u'Infl'), u'cluster': u'1c_1_2', 'gov': u'"2":"Infl"'}, {'nnn': 2, u'form': u'perdu', u'lemma': u'perdre', 'idnum': 2, u'cat': u'v', u'cluster': u'1c_2_3'}, {'nnn': 3, u'form': '---', u'lemma': u'', 'idnum': 3, u'cat': u'end', 'gg': (4, u'Punct'), u'cluster': u'1c_3_3', 'gov': u'"4":"Punct"'}, {'nnn': 4, u'form': '---', u'lemma': u'', 'idnum': 4, u'cat': u'S', 'gg': (2, u'S'), u'cluster': u'1c_3_3', 'gov': u'"2":"S"'}]

	"""
	
	
	if debug:print "<br>",indeces,"<br>",ws,"<br>","<br>"
	for i in indeces:
		w=ws[i]
		if debug: print "<br>***** before modification:",w
		w["cat"]=ericcats.get(w["cat"],w["cat"]) # correct categories
		if "gg" in w.keys(): # ggs looks like this : (2, u'subject') => index of governor, function
			#w["gg"]=(w["gg"][0],ericfuncs.get(w["gg"][1],w["gg"][1])) # correct functions
			
			# problem of auxiliaries: auxs are deps of main verb, main verb has subject
			if w["gg"][1] in ["V" ,"Infl"]: # we found an auxiliary: w 
				oldgov=w["gg"][0] # oldgov: the index of the main verb (participle or infinitive)
				#newgov=w["idnum"] # newgov: 
				#w["gg"] = (-1,"root")
				
				w["gg"] =  ws[oldgov].get("gg",(-1,"root")) # if the main verb doesn't have a governor, the aux becomes root
				ws[oldgov]["gg"]=(i,"aux")
				for j in indeces: # check all words
					ww=ws[j]
					if "gg" in ww.keys() :
						if ww["gg"][0]==oldgov : # we found a dependant of the participle
							if ww["gg"][1] in ["sub"]: # move the subject to the auxiliary	
								ww["gg"]= (i,ww["gg"][1])
						#if ww["idnum"]==oldgov:
							
			# problem of jumping over --- : pps and rels
			if w["gg"][1]in ["PP", "SRel", "prel", "N2", "audience"]: # w's gov link of this type goes to ---
				govn = ws[w["gg"][0]] # let's call the gov "govn"
				if hyph["form"]=="---":
					w["gg"]=govn["gg"] # take ---'s governor and put it on w
					if ws[w["gg"][0]]["form"]=="---":w["gg"]=(-1,"root")
			
			# problem of N2
			if w["gg"][1] in ["N2"]:
				
				#for ww in words:
				for j in indeces:
					ww=ws[j]
					if ww["idnum"]== w["gg"][0] and ww["cat"]== "prep":w["gg"]=(w["gg"][0],"obj")

				if w["cat"]in ["adj","verb","prep"]: w["gg"]=(w["gg"][0],"mod")
			
			
			w["gg"]=(w["gg"][0],ericfuncs.get(w["gg"][1],w["gg"][1])) # correct functions
				
				
		else:
			w["gg"] = (-1,"root")
		
			
	words=[ws[j] for j in indeces if ws[j]["form"]!="---"]				

	#for w in words:
	for i in indeces:
		w=ws[i]	
		if "gg" in w: 
			if w["gg"][1]in ["csu", "prep"] or (w["gg"][1]in ["obj"] and w["cat"]== "QU"):# found a bad complementizer/preposition
				govverb=w["gg"][0] # get the comp/prep's governor
				#print "!!!!!!!!!!!!"
				for j in indeces: # check all following verbs
					ww=ws[j]
					#print i,j,ww,"<br>"
					if j>i and "gg" in ww and ww["cat"]=="VB" and ww["gg"][0]==govverb: # found the first following verb with the good gov
						#print "!!!!!!!!!!!!",ww
						ww["gg"]=i,"gov" # the embedded verb gets the comp/prep as gov
						w["gg"]=govverb,"obj" # the comp/prep keeps the verb as gov and calls it "obj"
						break
			
			
			w["gov"]='"'+str(w["gg"][0])+'":"'+w["gg"][1]+'"'
		else:
			w["gg"] = (-1,"root")
	
	return words


#! usr/bin/python
#! coding: utf-8

import datetime, codecs, time, sys, os, glob, copy
sys.path.insert(0, '../lib')
import conll

debug=3
debug=False



def tagset(arbre):
	"""
	
	Transformation des Ã©tiquettes morphosyntaxiques

	"""
	for i, node in arbre.iteritems(): #pour chaque position et noeud de l'arbre
		if node["lemma"] in ["ne", "pas", "jamais", "plus"]:
                    node["tag"] = "ADN"
                    #print "apres",node["tag"]
		elif node["tag"] == "CL" and node["tag2"] == "CLS":
                    node["tag"] = "CLS"
                    #print "apres",node["tag"]
		elif node["tag"] == "CL" and node["tag2"] == "CLR":
                    node["tag"] = "CLI"
                    #print "apres", node["tag"]                    
		elif node["tag"] == "CL" and node["tag2"] == "CLO":
                    node["tag"] = "CLI"
                    #print "apres", node["tag"]
		elif node["tag"] == "V" and node["tag2"] == "V":
                    node["tag"] = "VRB"
                    #print "apres", node["tag"]
		elif node["tag"] == "V" and node["tag2"] == "VS":
                    node["tag"] = "VRB"
                    #print "apres", node["tag"]
		elif node["tag"] == "V" and node["tag2"] == "VIMP":
                    node["tag"] = "VRB"
                    #print "apres", node["tag"]
		elif node["tag"] == "V" and node["tag2"] == "VINF":
                    node["tag"] = "VNF"
                    #print "apres", node["tag"]
		elif node["tag"] == "V" and node["tag2"] == "VPP":
                    node["tag"] = "VPP"
                    #print "apres", node["tag"]
		elif node["tag"] == "V" and node["tag2"] == "VPR":
                    node["tag"] = "VPR"
                    #print "apres", node["tag"]
		elif node["tag"] == "N" and node["tag2"] == "NC":
                    node["tag"] = "NOM"
                    #print "apres", node["tag"]
		elif node["tag"] == "N" and node["tag2"] == "NPP":
                    node["tag"] = "NOM"
		elif node["tag"] == "C" and node["tag2"] == "CC":
                    node["tag"] = "COO"
                    #print "apres", node["tag"]
		elif node["tag"] == "C" and node["tag2"] == "CS":
                    node["tag"] = "CSU"
                    #print "apres", node["tag"]
		elif node["tag"] == "A" and node["tag2"] == "ADJ":
                    node["tag"] = "ADJ"
                    #print "apres", node["tag"]
		elif node["tag"] == "A" and node["tag2"] == "ADJWH":
                    node["tag"] = "PRQ"
                    #print "apres", node["tag"]
		elif node["tag"] == "ADV" and node["tag2"] == "ADV":
                    node["tag"] = "ADV"
		elif node["tag"] == "ADV" and node["tag2"] == "ADVWH":
                    node["tag"] = "PRQ"
                    #print "apres", node["tag"]
		elif node["tag"] == "PRO" and node["tag2"] == "PRO":
                    node["tag"] = "PRO"
                    #print "apres", node["tag"]
		elif node["tag"] == "PRO" and node["tag2"] == "PROREL":
                    node["tag"] = "PRQ"
                    #print "apres", node["tag"]
		elif node["tag"] == "PRO" and node["tag2"] == "PROWH":
                    node["tag"] = "PRQ"
                    #print "apres", node["tag"]
		elif node["tag"] == "D" and node["tag2"] == "DET":
                    node["tag"] = "DET"
                    #print "apres", node["tag"]
		elif node["tag"] == "D" and node["tag2"] == "DETWH":
                    node["tag"] = "PRQ"
                    #print "apres", node["tag"]
		elif node["tag"] == "P" and node["tag2"] == "P":
                    node["tag"] = "PRE"
                    #print "apres", node["tag"]
		elif node["tag"] == "P+D" and node["tag2"] == "P+D":
                    node["tag"] = "PRE"
                    #print "apres", node["tag"]
		elif node["tag"] == "P+PRO":
                    node["tag"] = "PRQ"
                    #print "apres", node["tag"]
		elif node["tag"] == "I" and node["tag2"] == "I":
                    node["tag"] = "INT"
		elif node["tag"] == "ET" and node["tag2"] == "ET":
                    node["tag"] = "X"
                    #print "apres", node["tag"]
	return arbre
    
#Modifier les liens de dÃ©pendance

def liensAux(arbre):
	"""
	transformation des liens aux_tps, aux_pass et aux_caus -> aux
	"""
	for i, node in arbre.iteritems(): #pour chaque position et noeud de l'arbre
		#print i, node
		if node["gov"] and node["gov"].values()[0] in ["aux_tps", "aux_pass", "aux_caus"]:
			#print node["gov"].keys()[0], "et", node["gov"].values()[0]
			node["gov"] = {node["gov"].keys()[0]:"aux"} 
	return arbre

def liensSpe(arbre):
	"""
	transformation des liens det -> spe
	"""
	for i, node in arbre.iteritems(): #pour chaque position et noeud de l'arbre
		if node["tag"] == "DET":
			node["gov"] = {node["gov"].keys()[0]:"spe"} 
	return arbre

def desEnTete(arbre):
	"""
	des est toujours tÃªte comme une prÃ©position
	"""
	for i, node in arbre.iteritems(): #pour chaque position et noeud de l'arbre
		if node["tag"] == "DET" and node["t"] in ["des", "du", "de", "d'"]:
			if node["gov"]:
				govid = node["gov"].keys()[0]
				govnode=arbre[govid]
				if govnode["gov"]:
					ggovid = govnode["gov"].keys()[0]
					node["gov"]={ggovid:govnode.get("gov",{}).get(ggovid,0)}
					govnode["gov"]={i:"dep"}
					
	return arbre



def liensDep(arbre):
	"""
	transformation des liens mod_rel, obj et mod -> dep
	"""
	for i, node in arbre.iteritems(): #pour chaque position et noeud de l'arbre
		if node["gov"] and node["gov"].values()[0] in ["mod_rel"]:
			node["gov"] = {node["gov"].keys()[0]:"dep"}
		elif node["tag"] not in ["VRB", "VNF", "VPP", "VPR"]: #lien obj lorqu'il ne dÃ©pend pas d'un verbe
			head = node["id"]
			for i, node in arbre.iteritems():
				if node["gov"] and node["gov"].values()[0] in ["obj"] and node["gov"].keys()[0] == head:
					node["gov"] = {node["gov"].keys()[0]:"dep"}
				elif node["gov"] and node["gov"].values()[0] in ["mod"] and node["gov"].keys()[0] == head:
					node["gov"] = {node["gov"].keys()[0]:"dep"}
	return arbre

def liensComp(arbre):
	"""
	transformation des liens ats, ato, a_obj, de_obj, p_obj, obj et mod ->comp
	"""
	for i, node in arbre.iteritems(): #pour chaque position et noeud de l'arbre #
		#print "avant",node["gov"]
		if node["gov"] and node["gov"].values()[0] in ["ats", "ato", "a_obj", "de_obj", "p_obj"]:
			node["gov"] = {node["gov"].keys()[0]:"comp"}
		elif node["tag"] in ["VRB", "VNF", "VPP", "VPR"]:#lien obj / mod lorsqu'il dÃ©pend d'un verbe
			headComp = node["id"]
			for i, node in arbre.iteritems():
				if node["gov"] and node["gov"].keys()[0] == headComp:
					if node["gov"] and node["gov"].values()[0] in ["obj"]:
						node["gov"] = {node["gov"].keys()[0]:"comp"}
						#print "Success, obj -> comp"
					elif node["gov"] and node["gov"].values()[0] in ["mod"] and node["lemma"] in [u"Ã ", "au", "aux", "de"]:
						node["gov"] = {node["gov"].keys()[0]:"comp"}
						#print "Success, mod -> comp"
	return arbre

def liensPunct(arbre):
	"""
	Si le signe de ponctuation est final, il dÃ©pend de la racine, sinon il dÃ©pend du mot qui prÃ©cÃ¨de.
	"""
	for i, node in arbre.iteritems(): #pour chaque position et noeud de l'arbre
		#print "avant",node["gov"]
		if node["gov"] == {0:"root"}:
			head = node["id"] 
			for i, node in arbre.iteritems():
				if node["tag"] == "PONCT" and node["lemma"] in [u".", u"!", u"?", u";",u":"]:
					node["gov"] = {head:"ponct"}
				elif node["tag"] == "PONCT":
					node["gov"] = {i-1:"ponct"}

		#print "apres",node["gov"]
	return arbre

def liensAd(arbre):
	"""
	transformation des liens mod -> ad
	"""
	for i, node in arbre.iteritems():
		if node["tag"] in ["VRB", "VPP", "VNF", "VPR"]:#lien mod quand il dÃ©pend d'un verbe et n'est pas comp
			headVerbale = node["id"]
			for i, node in arbre.iteritems():
				if node["gov"] and node["gov"].values()[0] in ["mod"] and node["gov"].keys()[0] == headVerbale:
					Ad = node["id"]
					#print Ad, "ad"
					if headVerbale < Ad:
						node["gov"] = {node["gov"].keys()[0]:"ad"}
						#print "Success mod -> ad"
					elif node["tag"] in ["ADN", "PRQ"]:
						node["gov"] = {node["gov"].keys()[0]:"ad"}
						#print "Success Ad"
					elif Ad == headVerbale-1:
						node["gov"] = {node["gov"].keys()[0]:"ad"}
						#print "Success ad exception"
	return arbre

def liensPeriph(arbre):
	"""
	transformation des liens mod -> periph
	"""
	for i, node in arbre.iteritems():
		if node["tag"] in ["VRB", "VPP", "VNF", "VPR"]:
			headMod = node["id"]
			for i, node in arbre.iteritems():
				if node["tag"] not in ["ADN","PRQ"]:
					headPeriph = node["id"]
					if node["gov"] and node["gov"].values()[0] in ["mod"] and node["gov"].keys()[0] == headMod and headPeriph < headMod:
						node["gov"] = {node["gov"].keys()[0]:"periph"}
						#print "Success mod -> periph"
	return arbre

def liensPara(arbre):
	"""
	Transformation des liens coord + changement de dÃ©pendant -> lien para
	"""
	headPara = ""
	endPara = ""
	for i, node in arbre.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["coord"]:
			headPara = node["gov"].keys()[0]
		#	endMark = node["id"]
		elif node["gov"] and node["gov"].values()[0] in ["dep_coord"]:
			endPara = node["id"]
			if headPara < endPara:
				node["gov"] = {headPara:"para"}
	return arbre


def liensMark(arbre):
	"""
	Transformation des liens dep_coord + inversion tÃªte-dÃ©pendant -> lien mark
	"""
	headMark = ""
	endMark = ""
	for i, node in arbre.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["para"]:
			headMark = node["id"]
			#print node["t"]
			for i, node in arbre.iteritems():
				if node["gov"] and node["gov"].values()[0] in ["coord"]:
					endMark = node["id"]
					if headMark > endMark:
						node["gov"] = {headMark:"mark"}
		elif node["gov"] and node["gov"].values()[0] in ["dep_coord"]:
			headMark = node["id"]
			for i, node in arbre.iteritems():
				if node["tag"] == "COO" and node["gov"] and node["gov"].values()[0] in ["root"]:
					endMark = node["id"]
					if headMark > endMark:
						node["gov"] = {headMark:"mark"}
						for i, node in arbre.iteritems():
							if node["gov"] and node["gov"].values()[0] in ["dep_coord"]:
								node["gov"] = {0:"root"}
				elif node["tag"] == "ADV":
					node["gov"] = {node["gov"].keys()[0]:"dep"}
	return arbre



#def NNAAMMEE(tree):
	#"""
	#NNAAMMEE -> XXX
	#"""
	#for i, node in tree.iteritems():
		#if node["t"]=="XXX":
			#node["t"]="YYY"
			#node["lemma"]="YYY"
		#elif node["t"]=="NNAAMMEE":
			#node["t"]="XXX"
			#node["lemma"]="XXX"
	#return tree


#def removeTag2(tree):
	#for i, node in tree.iteritems():
		#del node["tag2"]
	

#def emptyErrors(tree):
	#for i, node in tree.iteritems():
		#if node["t"]=="":
			#if not node["gov"]: 
				#pprint(tree)
				#print i,node
				#print "_______________"
				
			#else:
				#gi,gf = node["gov"].items()[0]
				#for ci,cf in node["children"].iteritems():#normalement un seul enfant
					#cnode=tree[ci]
					#cnode["gov"]={gi:gf}
			#tree=splice(tree,i,{})
			#addinfototree(tree)
			#tree=emptyErrors(tree)
			#break
			##print i, "removed empty node in"
			##pprint(tree)
	#return tree
	
#def kesk(tree):
	#for i, node in tree.iteritems():
		#if node["lemma"]=="est-ce":
			#gi,gf = node["gov"].items()[0]
			#if gi in tree and tree[gi]["lemma"]=="que": # qu'est-ce que
				#govnode=tree[gi]
				##print
				##print i-1,tree[i-1]["t"],govnode
				##print i,tree[i]["t"],node
				##print i+1,tree[i+1]["t"], tree[i+1]
				##print i+2,tree[i+2]["t"], tree[i+2]
				##print "_______________"
				
				#gov={}
				#for ii in [i-1,i,i+1]: # for each node of qu'est-ce que
					#ggnode=tree[ii]
					#for ggi,ggf in ggnode["gov"].iteritems(): # collect each exterior governor
						#if ggi<i-1 or i+1<ggi:
							#gov[ggi]=ggf
					#for ci,cf in ggnode["children"].iteritems():# change each exterior kid
						#if ci<i-1 or i+1<ci:
							#cnode=tree[ci]
							#cnode["gov"]={gi:cf}
				#if i+2 in tree and tree[i+2]["lemma"][0] in "aeiouy" and tree[i+2]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					#t=u"qu'est-ce qu'"
					##pprint(tree)
					##print i
					##if  tree[i+2]["lemma"] not in ["on","un"] : azer
				#else:
					#t=u"qu'est-ce que"
				#tree[gi]={'lemma': u"qu'est-ce que", 'tag': u'CSU', 't':t , 'gov': gov, 'id': gi}
				#tree=splice(tree,i,{})
				#tree=splice(tree,i,{})
				#addinfototree(tree)
				#tree=kesk(tree)
				#break
				##print "***"
				##pprint(tree)
			#else: 
				##print
				###print i-1,tree[i-1]["t"],govnode
				##print i,tree[i]["t"],node
				##print i+1,tree[i+1]["t"], tree[i+1]
				##print i+2,tree[i+2]["t"], tree[i+2]
				##print "_______________"
				#gov={}
				#for ii in [i,i+1]: # for each node of est-ce que
					#ggnode=tree[ii]
					#for ggi,ggf in ggnode["gov"].iteritems(): # collect each exterior governor
						#if ggi<i or i+1<ggi:
							#gov[ggi]=ggf
					#for ci,cf in ggnode["children"].iteritems():# change each exterior kid
						#if ci<i or i+1<ci:
							#cnode=tree[ci]
							#cnode["gov"]={i:cf}
				#if i+2 in tree and tree[i+2]["lemma"][0] in "aeiouy" and tree[i+2]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					#t=u"est-ce qu'"
					##pprint(tree)
					##print i
					##if  tree[i+2]["lemma"] not in ["on","un"] : qsdf
				#else:
					#t=u"est-ce que"
				#tree[i]={'lemma': u"est-ce que", 'tag': u'CSU', 't':t , 'gov': gov, 'id': gi}
				#tree=splice(tree,i+1,{})
				#addinfototree(tree)
				#tree=kesk(tree)
				#break
	#return tree


#def rendezvous(tree):
	##z=False
	#for i, node in tree.iteritems():
		#if node["lemma"]=="rendez-vous" and i+1 in tree and tree[i+1]["t"]=="-vous":
			#node["t"]="rendez-vous"
			#tree=splice(tree,i+1,{})
			#addinfototree(tree)
			#pprint(tree)
			#print "******"
			#tree=rendezvous(tree)
			##z=True
			#break
	##if z:
		##print "mmmmm"
		##pprint(tree)
		##print "nnnnnnnnnnnnn"
	#return tree
		
#def cls(tree):
	#for i, node in tree.iteritems():
		#if node["tag"]=="CLS":
			#gi,gf = node["gov"].items()[0]
			#if gi in tree and tree[gi]["tag"]!="VRB" and gf not in ["para","disflink"]: # CLS whose governor is not a verb
				#govnode=tree[gi]
				
				#pprint(tree)
				#print "******",i
			
			
	#return tree


#def desEnTete(tree):
	#"""
	#des est toujours tÃªte comme une prÃ©position
	#"""
	#for i, node in tree.iteritems():
		#if node["tag"] == "DET" and node["t"] in ["des", "du", "de", "d'"]:
			#if node["gov"]:
				#govid = node["gov"].keys()[0]
				#if govid in tree:
					#govnode=tree[govid]
					#if govnode["gov"]:
						#ggovid = govnode["gov"].keys()[0]
						#node["gov"]={ggovid:govnode.get("gov",{}).get(ggovid,0)}
						#govnode["gov"]={i:"dep"}
		#node["tag"] == "PRE"			
	#return tree


##V disfl-> des Ã  -->  comp
##V disfl->  "du", "de", "d'" --> comp
##V disfl-> 

#def auxEnTete(tree):
	#"""
	#aux est la tÃªte
	#"""
	#for i, node in tree.iteritems():
		#if node["gov"] and node["gov"].values()[0] in ["aux"] and node["gov"].keys()[0]>i: # found a node that is a dependent aux
			##print "found a node that is a dependent aux",i,node
			#gi = node["gov"].keys()[0]
			#gnode=tree[gi]
			#if gnode["gov"] and gnode["gov"].values()[0] in ["aux"] and gnode["gov"].keys()[0]>gi: # found a double aux
				##print "found a double aux",gi,node
				#ggi = gnode["gov"].keys()[0]
				#ggnode=tree[ggi]
				#for ci,cf in ggnode["children"].iteritems():
					#if cf in ["suj","periph","insert","mark"]: # toutes ces fonctions montent sur 
						#cnode=tree[ci]
						#cnode["gov"]={i:cf}
					#elif cf=="dm" and ci<i: # dm seulement quand ils sont Ã  gauche de l'auxiliaire
						#cnode=tree[ci]	
						#cnode["gov"]={i:"dm"}
					#elif cf=="ad":
						#cnode=tree[ci]
						#if cnode["t"] in ["ne","n'"]:
							#cnode["gov"]={i:cf}
				#node["gov"]=ggnode["gov"].copy() 
				#gnode["gov"]={i:"aux"}
				#ggnode["gov"]={gi:"aux"}
			#else: # only simple aux
				#for ci,cf in gnode["children"].iteritems():
					#if cf in ["suj","periph","insert","mark"]: # toutes ces fonctions montent sur 
						#cnode=tree[ci]
						#cnode["gov"]={i:cf}
					#elif cf=="dm" and ci<i: # dm seulement quand ils sont Ã  gauche de l'auxiliaire
						#cnode=tree[ci]	
						#cnode["gov"]={i:"dm"}
					#elif cf=="ad":
						#cnode=tree[ci]
						#if cnode["t"] in ["ne","n'"]:
							#cnode["gov"]={i:cf}
				#node["gov"]=gnode["gov"].copy()
				#gnode["gov"]={i:"aux"}
			
			#addinfototree(tree)
	#return  tree		
			##pprint(tree)
			
#from searchConll import  hasEmptyNode
			
def goldplatinum(tree):
	removeTag2(tree)
	addinfototree(tree)
	tree=auxEnTete(tree)
	tree=desEnTete(tree)
	tree=NNAAMMEE(tree)

	tree=emptyErrors(tree)
	
	tree=kesk(tree)
	tree=rendezvous(tree)
	tree=cls(tree)
	return tree

def ftbplatinum(tree):
	
	addinfototree(tree)
	
	tree=tagset(tree)
	tree=auxEnTete(tree)
	tree=desEnTete(tree)
	tree=NNAAMMEE(tree)

	tree=emptyErrors(tree)
	
	tree=kesk(tree)
	tree=rendezvous(tree)
	tree=cls(tree)
	return tree





def pprint(tree):
	for i in tree:
		print i, tree[i]["t"],tree[i]
	print


def splice(sentencedic,nodenum,intdic,copiedfeatures=['t','token','lemma','cat','tag','beg','end']):
		"""
		integrates the intdic into sentencedic at position nodenum
		an empty intdic kicks out the node in a proper manner (other dependencies are still ok)

		format of intdic:
			indices start with 'i', for example 'i2'
			last node that carries 'child' feature will receive the children of the former nodenum node
			last node that has governor i0 will receive the governors of the former nodenum node
			if no node has a 'i0' governor, the first node will receive the governors
			WATCH OUT: the ix will be ordered. so i10 will be before i9!!!
			example pour est-ce, remplacÃ© par ce sous-arbre :
			{
				'i1':{ u'person': u'3', u'number': u'sg', u'cat': u'V', u'lemma': u'\xeatre', u'token': u'est', u'tense': u'present', u'mode': u'indicative', 'gov': {'i0':'root'}, u't': u'A', 'child':None},
				'i2':{u'cat': u'Cl', u'lemma': u'c', u'token': u'-ce', u't': u'B', 'gov': {'i1': u'sub'}}
			}

		gives back new sentencedic

		"""
		intgov,intchi=None,None
		if debug>2: 
			print "================== integrating"
			for i in sorted(intdic):
				print i,intdic[i]
			print "into ((((((((((((("
			for i in sorted(sentencedic):
				print i,sentencedic[i].get("t","-"),sentencedic[i]
			print "))))))))))))))) at ",nodenum
		#try:
		l = [0]+sorted(sentencedic)[:nodenum-1]+sorted(intdic)+sorted(sentencedic)[nodenum:]
		if debug>2: print "lllll",l


		for i in sorted(intdic):

			sentencedic[i]=sentencedic[nodenum].copy() # l'ancien noeud qui va Ãªtre virer est copier pour chaque ix
			for at in copiedfeatures:
				if at in intdic[i]:
					sentencedic[i][at]=intdic[i][at]
			sentencedic[i]['gov']=intdic[i].get('gov',{})

			if 'i0' in intdic[i].get('gov',{}).keys(): # found the gov: the one that has a root link
				sentencedic[i]['gov']=sentencedic[nodenum]['gov']
				intgov=i
			if "child" in intdic[i]: # found the child bearer
				intchi = i
				del intdic[i]['child']
		if not intgov:# if no governor is indicated, i guess it's the first word
			intgov="i1"
			if intgov in sentencedic: sentencedic[intgov]['gov']=sentencedic[nodenum]['gov']
		if not intchi: intchi=intgov

		# correct wrong gov links
		for i in sorted(sentencedic):
			if nodenum in sentencedic[i]['gov'].keys():
				sentencedic[i]['gov'][intchi]=sentencedic[i]['gov'][nodenum]
				del sentencedic[i]['gov'][nodenum]

		# bring all in a new list with new indeces based on l
		newsentencedic={}
		for newi,oldi in enumerate(l):
			if newi:# not 0
				newsentencedic[newi]=sentencedic[oldi]
				govdic={}
				for govi,dep in sentencedic[oldi]['gov'].iteritems():
					if govi>=0 and govi in l: govdic[l.index(govi)]=dep
				newsentencedic[newi]['gov']=govdic
				newsentencedic[newi]['id']=newi
				#print type(newsentencedic)


		if debug>2:
			print "result:((((((((((((("
			for i in sorted(newsentencedic):
				print i,newsentencedic[i].get("t",""),newsentencedic[i]
			print ")))))))))))))))"

		return newsentencedic


def addinfototree(tree, reset=True):
	"""
	add to each node:
	children: dico: childi -> func 
	govtags: dico: tag of governor --> [(govi,func), ...]
	"""
	if reset:
		for i,node in tree.items():
			tree[i]["children"]={}
			tree[i]["govtags"]={}
	
	
	rootindeces=[]

	for i,node in tree.items(): # getting some basic additional features: children and govtags
		tree[i]["children"]=tree[i].get("children",{})
		tree[i]["govtags"]={}
		
		for j,func in node["gov"].items():
			if j in tree: # in tree: has governor j
				tree[j]["children"]=tree[j].get("children",{})
				tree[j]["children"][i]=func
				tree[i]["govtags"][tree[j]["tag"]]=tree[i]["govtags"].get(tree[j]["tag"],[])+[ (j,func) ]
			else: # not in tree: roots
				rootindeces+=[i]
	return rootindeces

def fixOutname(name):
	if name.startswith("validated."): 
		name=name.split(".")[1]
		#name=name[len("validated."):]
	if name.endswith("lastHuman.conll"): name=name[:-len(".lastHuman.conll")]
	if name.endswith(".conll"): name=name[:-len(".conll")]
	return name

def createNonExistingFolders(path):
	head, tail = os.path.split(path)
	if head=="":
		if tail and not os.path.exists(tail): os.makedirs(tail)
	else:
		createNonExistingFolders(head)
	if head and not os.path.exists(head): os.makedirs(head)

def transform(infolder,outfolder,mixOldNew=True):
	createNonExistingFolders(outfolder)
	#for infile in sorted(glob.glob(os.path.join(infolder,"test.conll"))):
	for infile in sorted(glob.glob(os.path.join(infolder,"*.conll"))):
		basename=os.path.basename(infile)
		print "reading",basename
		trees = conll.conllFile2trees(infile)
		newtrees=[]
		for tree in trees:
			if mixOldNew: newtrees+=[tree]
			newtree=copy.deepcopy(tree)
			newtree=platinum(newtree)
			newtrees+=[newtree]
		conll.trees2conllFile(newtrees,os.path.join(outfolder,fixOutname(basename)))
		
			
transform("projects/OrfeoGold2016/export/","projects/OrfeoGold2016/platinum/",mixOldNew=False)		 
#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys,cgi,cgitb,codecs
sys.path.insert(0, '../lib')
from database import SQL

#cgitb.enable()
#project = form.getvalue('project',"").decode("utf-8")
project="linguistiqueCorpus-Exo"
#form = cgi.FieldStorage()
#uid = form.getvalue('uid',None)
uid = 1 # admin
#print 'Content-type: text/html\n\n'

sql=SQL(project)
	
#print project.encode('utf-8'),"uuuuuuuuuuuuuuuuuuuuu",uid

tid=13

uids=[uid for uid, in sql.uidForText(tid)]
print uids
with codecs.open(project+str(tid)+".results.html","w","utf-8") as out:
	for i,uid in enumerate(uids):
		print "__________________",uid,i,float(i)/len(uids)*100,"%"
		out.write(sql.evaluateUser(uid, tid, consolidateLine=True)+"\n")
# -*- coding: utf-8 -*-
#!/usr/bin/python

import os,subprocess,sys,glob,datetime, codecs
sys.path.insert(0, '../lib')
import conll, trees2train, treebankfiles, updateTrees, newconvert
from database 	import SQL

def extractConllFiles(project,outfolder):
	"""
	creates the empty files with a word per line as a first step to mate parsing
	"""
	if outfolder[-1]!="/": outfolder=outfolder+"/"
	texts={}
	sql = SQL(project)
	db, cursor = sql.open()
	command = """select distinct texts.textname, sentences.nr, features.nr, features.value 
		from features, trees, texts, sentences, users
		where attr = "t" and trees.rowid=features.treeid --and sentences.textid=13 
		and trees.sentenceid=sentences.rowid and sentences.textid=texts.rowid and users.user="parser";"""
	cursor.execute(command)
	a = cursor.fetchall()
	#print a
	for nr, (textname, snr, num, token) in enumerate(a):
		#sql.exportAnnotations(textid, textname, "lastconll")
		texts[textname]= texts.get(textname,[])+[(snr, num,token)]
	newfiles=[]
	for textname in texts:
		print "processing",textname
		f=codecs.open(outfolder+textname, "w", "utf-8")
		for c, (snr, num, tok) in enumerate(sorted(texts[textname])):
			if num == 1 and c > 0:
				f.write('\n')
			f.write("\t".join([str(num), tok]+["_"]*12)+'\n')
		print c+1, "tokens"
		f.close()
		newfiles+=[outfolder+textname]
	return newfiles

def getEmptyConlls(project,outfolder, lemma):
	newfiles=[]
	path="projects/"+project+"/export/"
	for conllfile in glob.glob(path+"*.most.recent.trees.conll10"):
		newfilename=conll.makeEmpty(conllfile, outfolder=outfolder, lemma=lemma)
		newfiles+=[newfilename]
	return newfiles


def main(project, outfolder):
	extractConllFiles(project)
	
	return toparse


if __name__ == '__main__':
	#toparse=extractConllFiles("OrfeoGold2016", "./mate/test_trees/")
	toparse=extractConllFiles("HongKongTVMandarin", "./mate/")
	
#! usr/bin/python
#! coding: utf-8

import datetime, codecs, time, sys, os, glob, copy
sys.path.insert(0, '../lib')
import conll
from searchConll import  hasEmptyNode, searches

debug=3
debug=False


def NNAAMMEE(tree):
	"""
	NNAAMMEE -> XXX
	"""
	for i, node in tree.iteritems():
		if node["t"]=="XXX":
			node["t"]="YYY"
			node["lemma"]="YYY"
		elif node["t"]=="NNAAMMEE":
			node["t"]="XXX"
			node["lemma"]="XXX"
	return tree

def cela(tree):
	"""
	Ã§a: lemme = Ã§a
	"""
	for i, node in tree.iteritems():
		if node["t"]==u"Ã§a":
			node["lemma"]=u"Ã§a"
	return tree

def tapostrophe(tree):
	"""
	t' CLS Ã  changer en CLI + lemme = te
	"""
	for i, node in tree.iteritems():
		if node["t"]==u"t'":
			node["lemma"]="te"
			node["tag"]="CLI"
	return tree



def sujEnSubj(tree):
	"""
	suj -> subj
	"""
	for i, node in tree.iteritems():
		for gi,gf in node["gov"].iteritems():
			if gf=="suj": tree[i]["gov"][gi]="subj"
	addinfototree(tree)
	return tree


def removeTag2(tree):
	for i, node in tree.iteritems():
		if "tag2" in node:
			del node["tag2"]
	

def emptyErrors(tree):
	for i, node in tree.iteritems():
		if node["t"]=="":
			if not node["gov"]: 
				pass
				#pprint(tree)
				#print i,node
				#print "_______________"
				
			else:
				gi,gf = node["gov"].items()[0]
				for ci,cf in node["children"].iteritems():#normalement un seul enfant
					cnode=tree[ci]
					cnode["gov"]={gi:gf}
			tree=splice(tree,i,{})
			addinfototree(tree)
			tree=emptyErrors(tree)
			break
			#print i, "removed empty node in"
			#pprint(tree)
	return tree
	
def kesk(tree):
	"""
	correct segmentation horrors
	"""
	for i, node in tree.iteritems():
		if node["lemma"]=="est-ce":
			gi,gf = node["gov"].items()[0]
			if gi in tree and tree[gi]["lemma"]=="que": # qu'est-ce que
				govnode=tree[gi]
							
				gov={}
				for ii in [i-1,i,i+1]: # for each node of qu'est-ce que
					ggnode=tree[ii]
					for ggi,ggf in ggnode["gov"].iteritems(): # collect each exterior governor
						if ggi<i-1 or i+1<ggi:
							gov[ggi]=ggf
					for ci,cf in ggnode["children"].iteritems():# change each exterior kid
						if ci<i-1 or i+1<ci:
							cnode=tree[ci]
							cnode["gov"]={gi:cf}
				if i+2 in tree and tree[i+2]["lemma"][0] in "aeiouy" and tree[i+2]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					t=u"qu'est-ce qu'"
					#pprint(tree)
					#print i
					#if  tree[i+2]["lemma"] not in ["on","un"] : azer
				else:
					t=u"qu'est-ce que"
				tree[gi]={'lemma': u"qu'est-ce que", 'tag': u'PRQ', 't':t , 'gov': gov, 'id': gi}
				tree=splice(tree,i,{})
				tree=splice(tree,i,{})
				addinfototree(tree)
				tree=kesk(tree)
				break
				#print "***"
				#pprint(tree)
			else: 
				gov={}
				for ii in [i,i+1]: # for each node of est-ce que
					ggnode=tree[ii]
					for ggi,ggf in ggnode["gov"].iteritems(): # collect each exterior governor
						if ggi<i or i+1<ggi:
							gov[ggi]=ggf
					for ci,cf in ggnode["children"].iteritems():# change each exterior kid
						if ci<i or i+1<ci:
							cnode=tree[ci]
							cnode["gov"]={i:cf}
				if i+2 in tree and tree[i+2]["lemma"][0] in "aeiouy" and tree[i+2]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					t=u"est-ce qu'"
					#pprint(tree)
					#print i
					#if  tree[i+2]["lemma"] not in ["on","un"] : qsdf
				else:
					t=u"est-ce que"
				tree[i]={'lemma': u"est-ce que", 'tag': u'CSU', 't':t , 'gov': gov, 'id': gi}
				tree=splice(tree,i+1,{})
				addinfototree(tree)
				tree=kesk(tree)
				break
			
		elif node["lemma"]=="qu'est-ce" and i+3 in tree:
			
			nnode = tree[i+1]
			nnnode = tree[i+2]
			if nnode["lemma"]=="ce" and  nnnode["lemma"]=="que":
				
				if tree[i+3]["lemma"][0] in "aeiouy" and tree[i+3]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					t=u"qu'est-ce qu'"
				else:
					t=u"qu'est-ce que"
				node["t"]=t
				node["lemma"]=u"qu'est-ce que"
				node["tag"]=u"PRQ"
				tree=splice(tree,i+1,{})
				tree=splice(tree,i+1,{})
				addinfototree(tree)
				tree=kesk(tree)
				break
			
		elif node["lemma"]=="est-ce que" and i-1 in tree:
			qnode = tree[i-1]
			if qnode["lemma"]=="que":
				qnode["lemma"]="qu'est-ce que"
				if qnode["t"] in ["est-ce que", "est-ce qu'"]:
					qnode["t"]="qu'"+qnode["t"] # pour garder l'info qu' ou que
				elif i+1 in tree and tree[i+1]["lemma"][0] in "aeiouy" and tree[i+1]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					qnode["t"]="qu'est-ce qu'"
				else:
					qnode["t"]="qu'est-ce que"
				tree=splice(tree,i,{})
				addinfototree(tree)
				#pprint(tree)
				
				tree=kesk(tree)
				break
			
	return tree


def rendezvous(tree):
	#z=False
	for i, node in tree.iteritems():
		if node["lemma"]=="rendez-vous" and i+1 in tree and tree[i+1]["t"]=="-vous":
			node["t"]="rendez-vous"
			tree=splice(tree,i+1,{})
			addinfototree(tree)
			#pprint(tree)
			#print "******"
			tree=rendezvous(tree)
			#z=True
			break
	#if z:
		#print "mmmmm"
		#pprint(tree)
		#print "nnnnnnnnnnnnn"
	return tree

def pasgrandchose(tree):
	"""
	X -rrr-> pas grand-chose PRO
	en
	X -ad-> pas ADN lemme: pas
	X -rrr-> pas grand-chose PRO lemme:grand-chose
	
	et si on avait
	dit souvent pas grand-chose
	??
	"""
	for i, node in tree.iteritems():
		if node["lemma"]=="pas grand-chose" and node["gov"] and node["gov"].keys()[0] in tree:
			gi = node["gov"].keys()[0]
			gnode=tree[gi]
			#pprint(tree)
			#print
			node["lemma"]="grand-chose"
			node["t"]="grand-chose"
			i1= gnode.copy()
			i1.update({'child':None})
			i3=node.copy()
			i3.update({'gov': {'i1': node["gov"].values()[0]}})
			chis=i3["children"]
			tree=splice(tree,i,{})
			tree=splice(tree,gi,
			{
				'i1':i1,
				'i2':{u'tag': u'ADN', u'lemma': u'pas', u't': u'pas', 'gov': {'i1': u'ad'}},
				'i3':i3
			})
			for chi in chis:
				tree[chi]["gov"]={gi+2:tree[chi]["gov"].values()[0]}
			addinfototree(tree)
			#pprint(tree)
			#print "******"
			tree=pasgrandchose(tree)
			break
	
	return tree




def cestadire(tree):
	"""
	c' + est-Ã -dire : un seul token COO
	"""
	for i, node in tree.iteritems():
		if node["t"]=="c'" and i+1 in tree and (tree[i+1]["t"]==u"est-Ã -dire" or tree[i+1]["t"]==u"est Ã  dire"):
			for ci,cf in tree[i+1]["children"].iteritems():
				tree[ci]["gov"]={i:cf}
			node["t"]=u"c'est-Ã -dire"
			node["lemma"]=u"c'est-Ã -dire"
			node["tag"]=u"COO"
			tree=splice(tree,i+1,{})
			addinfototree(tree)
			tree=cestadire(tree)
			break
		if node["t"]==u"est-Ã -dire":
			node["tag"]="COO"
	return tree

		
def prons(tree):
	"""
	je|tu|il|ils|on -> CLS
	elle|elles|nous|vous ce qui sont suj -> CLS
	si "ce" a un dep VRB alors ce -> PRO
	si un cls est dep d'une prÃ©position -> PRO
	tous les CLI dep autre que "y" doivent Ãªtre comp
	"""
	for i, node in tree.iteritems():
		if node["lemma"] in "je tu il ils on".split():
			node["tag"]="CLS"
		if node["lemma"] in "elle elles nous vous ce".split() and "subj" in node["gov"].values():
			node["tag"]="CLS"
		if node["lemma"] == "ce" and "VRB" in [tree[ci]["tag"] for ci in node["children"]]:
			node["tag"]="PRO"
		if node["tag"] == "CLS" and "dep" in node["gov"].values() and "PRE" in node["govtags"]:
			node["tag"]="PRO"
			addinfototree(tree)
		if node["tag"] == "CLI" and "dep" in node["gov"].values() and node["t"]!="y":
			node["gov"]={node["gov"].keys()[0]:"comp"}
			addinfototree(tree)
	return tree	

def disflink(tree):

	"""
	si une PRE est disflink et n'a pas de dÃ©pendant,
	disflink -> ad si le gouv est un VRB|VNF|VPP
	disflink -> dep sinon (sauf si le gouv est dm ou mark)
	on peut Ã©ventuellement faire la mÃªme rÃ¨gle pour les DET
	
	tout disflink entre entre prep et nom est en fait une dÃ©p
	"""
	for i, node in tree.iteritems():
		if node["tag"]in ["PRE","DET"] and "disflink" in node["gov"].values():
			if "VRB" in node["govtags"] or "VNF" in node["govtags"] or "VPP" in node["govtags"]:
				node["gov"]={node["gov"].keys()[0]:"ad"}
			else:
				node["gov"]={node["gov"].keys()[0]:"dep"}
		elif node["tag"]in ["NOM"] and "disflink" in node["gov"].values() and "PRE" in node["govtags"]:
			node["gov"]={node["gov"].keys()[0]:"dep"}
	return tree

def vrb(tree):
	"""
	dit|dites|fait|faites|Ã©crit avec un suj -> VRB
	dites|faites qui est root -> VRB
	"""
	for i, node in tree.iteritems():
		if node["t"] in u"dit dites fait faites Ã©crit".split() and "subj" in node["children"].values():
			node["tag"]="VRB"
		if node["t"] in "dites faites".split() and 0 in node["gov"]:
			node["tag"]="VRB"
		
	return tree



def desEnTete(tree):
	"""
	des est toujours tÃªte comme une prÃ©position
	"""
	for i, node in tree.iteritems():
		if node["tag"] == "DET" and node["t"] in ["des", "du", "de", "d'"]:
			if node["gov"]:
				govid = node["gov"].keys()[0]
				if govid in tree:
					govnode=tree[govid]
					if govnode["gov"]:
						ggovid = govnode["gov"].keys()[0]
						node["gov"]={ggovid:govnode.get("gov",{}).get(ggovid,0)}
						govnode["gov"]={i:"dep"}
		node["tag"] == "PRE"			
	return tree


#V disfl-> des Ã  -->  comp
#V disfl->  "du", "de", "d'" --> comp
#V disfl-> 

def auxEnTete(tree):
	"""
	aux est la tÃªte
	"""
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["aux"] and node["gov"].keys()[0]>i: # found an aux
			#print "found a node that is a dependent aux",i,node
			gi = node["gov"].keys()[0]
			gnode=tree[gi]
			if gnode["gov"] and gnode["gov"].values()[0] in ["aux"] and gnode["gov"].keys()[0]>gi: # found a double aux
				#print "found a double aux",gi,node
				ggi = gnode["gov"].keys()[0]
				ggnode=tree[ggi]
				for ci,cf in ggnode["children"].iteritems():
					if cf in ["subj","periph","insert","mark"]: # toutes ces fonctions montent sur 
						cnode=tree[ci]
						cnode["gov"]={i:cf}
					elif cf=="dm" and ci<i: # dm seulement quand ils sont Ã  gauche de l'auxiliaire
						cnode=tree[ci]	
						cnode["gov"]={i:cf}
					elif cf=="ad":
						cnode=tree[ci]
						if cnode["t"] in ["ne","n'"]:
							cnode["gov"]={i:cf}
				node["gov"]=ggnode["gov"].copy() 
				gnode["gov"]={i:"aux"}
				ggnode["gov"]={gi:"aux"}
			else: # only simple aux
				#print node
				for ci,cf in gnode["children"].iteritems(): # look at all the siblings of the aux
					if cf in ["subj","periph","insert","mark"]: # toutes ces fonctions montent sur l'auxiliaire
						cnode=tree[ci]
						cnode["gov"]={i:cf}
					elif cf=="dm" and ci<i: # dm seulement quand ils sont Ã  gauche de l'auxiliaire
						cnode=tree[ci]	
						cnode["gov"]={i:cf}
					elif cf=="ad":
						cnode=tree[ci]
						if cnode["t"] in ["ne","n'"]:
							cnode["gov"]={i:cf}
				node["gov"]=gnode["gov"].copy()
				gnode["gov"]={i:"aux"}
				#print node
				#pprint(tree)
				#qsdf
			addinfototree(tree)
	return  tree		
			#pprint(tree)
			

def auxpara(tree):
	"""
	#si X-para-> Y et X-aux-> Z, alors Y-aux-> Z
	"""
	for i, node in tree.iteritems():
		if "para" in node["children"].values() and "aux" in node["children"].values():
			yparai=[ci for ci in node["children"] if node["children"][ci]=="para"][0]
			zauxi =[ci for ci in node["children"] if node["children"][ci]=="aux" ][0]
			tree[zauxi]["gov"]={yparai:"aux"}
			addinfototree(tree)
			#pprint(tree)
			#qsdf
	return  tree	

def adperiph(tree):
	"""
	1) si X <-ad|comp- Y et il y a un Z suj tel que X < Z < Y, alors X <-periph- Y
	"""
	for i, node in tree.iteritems():
		if ("ad" in node["children"].values() or "comp" in node["children"].values()) and "suj" in node["children"].values():
			zsubji =[ci for ci in node["children"] if node["children"][ci]=="suj" ][0]
			for xadci in [ci for ci in node["children"] if node["children"][ci] in ["ad","comp"]]:
				if xadci<zsubji<i:
					tree[xadci]["gov"]={i:"periph"}
					addinfototree(tree)
	return  tree	

def montperiph(tree):
	"""
	2) (Ã  appliquer ensuite) si X <-periph- Y et W-aux-> Y, alors  X <-periph- Y	
	"""
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["periph"]:
			ygi = node["gov"].keys()[0]
			ygnode=tree[ygi]
			if ygnode["gov"] and ygnode["gov"].values()[0] in ["aux"]:
				wggi=ygnode["gov"].keys()[0]
				node["gov"]={wggi:"periph"}
				addinfototree(tree)
	return  tree

def rootperiph(tree):
	"""
	beaucoup de periph qui se balladent
	si un periph a 0 comme gouv, lui attribuer le nÅud root comme gouv

	"""
	for i, node in tree.iteritems():
		if node["gov"] and 0 in node["gov"] and  node["gov"][0] == "periph":
			for j, rnode in tree.iteritems():
				if rnode["gov"] and 0 in rnode["gov"] and  rnode["gov"][0] == "root":
					node["gov"]={j:"periph"}
					break 
				
	return  tree	


def affcomp(tree):
	"""
	tout aff devient comp

	"""
	for i, node in tree.iteritems():
		if node["gov"]:
			for govi, func in node["gov"].iteritems():
				if func=="aff":
					node["gov"][govi]="comp"				
				
	return  tree	

def csuperiph(tree):
	"""
	autre pb avec les periph : si X est un periph qui dÃ©pend d'un CSU, X doit dÃ©pendre du VRB dÃ©pendant de la CSU
	ct faux !
	meilleure version
	si Y est CSU et Y-periph-> X et Y n'est pas root, alors X <-periph- Z <-dep - Y
	"""
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["periph"] and "CSU" in node["govtags"]:
			csui,f=node["govtags"]["CSU"][0]
			csunode=tree[csui]
			if "root" not in csunode["gov"].values():
				cis=[ci for ci in csunode["children"] if tree[ci]["tag"][0]=="V"]
				if cis:
					vi=cis[0]
					node["gov"]={vi:"periph"}
					addinfototree(tree)
					#pprint( tree)
				
				
	return  tree	


		
		

def pprint(tree):
	for i in tree:
		print i, tree[i]["t"],tree[i]
	print
	
def corrigerNumerotation(arbre):
	indexcorrection = {0:0} # ancien indice --> nouvel indice
	problem = False
	for compteur, ind in enumerate(sorted(arbre.keys())):
		indexcorrection[ind]=compteur+1
		if compteur+1 != ind:
			problem = True
	if problem:
		print indexcorrection
		pprint(arbre)
		arbrecorrige = {}
		for i, node in arbre.iteritems():
			node["id"]=indexcorrection[i]
			newgov={}
			for gi,f in node["gov"].iteritems():
				newgov[indexcorrection[gi]]=f
			node["gov"]=newgov
			arbrecorrige[indexcorrection[i]]=node
		return arbrecorrige
	else:
		return arbre

def splice(sentencedic,nodenum,intdic,copiedfeatures=['t','token','lemma','cat','tag','beg','end']):
		"""
		integrates the intdic into sentencedic at position nodenum
		an empty intdic kicks out the node in a proper manner (other dependencies are still ok)

		format of intdic:
			indices start with 'i', for example 'i2'
			last node that carries 'child' feature will receive the children of the former nodenum node
			last node that has governor i0 will receive the governors of the former nodenum node
			if no node has a 'i0' governor, the first node will receive the governors
			WATCH OUT: the ix will be ordered. so i10 will be before i9!!!
			example pour est-ce, remplacÃ© par ce sous-arbre :
			{
				'i1':{ u'person': u'3', u'number': u'sg', u'cat': u'V', u'lemma': u'\xeatre', u'token': u'est', u'tense': u'present', u'mode': u'indicative', 'gov': {'i0':'root'}, u't': u'A', 'child':None},
				'i2':{u'cat': u'Cl', u'lemma': u'c', u'token': u'-ce', u't': u'B', 'gov': {'i1': u'sub'}}
			}

		gives back new sentencedic

		"""
		intgov,intchi=None,None
		if debug>2: 
			print "================== integrating"
			for i in sorted(intdic):
				print i,intdic[i]
			print "into ((((((((((((("
			for i in sorted(sentencedic):
				print i,sentencedic[i].get("t","-"),sentencedic[i]
			print "))))))))))))))) at ",nodenum
		#try:
		l = [0]+sorted(sentencedic)[:nodenum-1]+sorted(intdic)+sorted(sentencedic)[nodenum:]
		if debug>2: print "lllll",l


		for i in sorted(intdic):

			sentencedic[i]=sentencedic[nodenum].copy() # l'ancien noeud qui va Ãªtre virÃ© et copiÃ© pour chaque ix
			for at in copiedfeatures:
				if at in intdic[i]:
					sentencedic[i][at]=intdic[i][at]
			sentencedic[i]['gov']=intdic[i].get('gov',{})

			if 'i0' in intdic[i].get('gov',{}).keys(): # found the gov: the one that has a root link
				sentencedic[i]['gov']=sentencedic[nodenum]['gov']
				intgov=i
			if "child" in intdic[i]: # found the child bearer
				intchi = i
				del intdic[i]['child']
		if not intgov:# if no governor is indicated, i guess it's the first word
			intgov="i1"
			if intgov in sentencedic: sentencedic[intgov]['gov']=sentencedic[nodenum]['gov']
		if not intchi: intchi=intgov

		# correct wrong gov links
		for i in sorted(sentencedic):
			if nodenum in sentencedic[i]['gov'].keys():
				sentencedic[i]['gov'][intchi]=sentencedic[i]['gov'][nodenum]
				del sentencedic[i]['gov'][nodenum]

		# bring all in a new list with new indeces based on l
		newsentencedic={}
		for newi,oldi in enumerate(l):
			if newi:# not 0
				newsentencedic[newi]=sentencedic[oldi]
				govdic={}
				for govi,dep in sentencedic[oldi]['gov'].iteritems():
					if govi>=0 and govi in l: govdic[l.index(govi)]=dep
				newsentencedic[newi]['gov']=govdic
				newsentencedic[newi]['id']=newi
				#print type(newsentencedic)


		if debug>2:
			print "result:((((((((((((("
			for i in sorted(newsentencedic):
				print i,newsentencedic[i].get("t",""),newsentencedic[i]
			print ")))))))))))))))"

		return newsentencedic


def addinfototree(tree, reset=True):
	"""
	add to each node:
	children: dico: childi -> func 
	govtags: dico: tag of governor --> [(govi,func), ...]
	"""
	if reset:
		for i,node in tree.items():
			tree[i]["children"]={}
			tree[i]["govtags"]={}
	
	
	rootindeces=[]

	for i,node in tree.items(): # getting some basic additional features: children and govtags
		tree[i]["children"]=tree[i].get("children",{})
		tree[i]["govtags"]={}
		
		for j,func in node["gov"].items():
			if j in tree: # in tree: has governor j
				tree[j]["children"]=tree[j].get("children",{})
				tree[j]["children"][i]=func
				tree[i]["govtags"][tree[j]["tag"]]=tree[i]["govtags"].get(tree[j]["tag"],[])+[ (j,func) ]
			else: # not in tree: roots
				rootindeces+=[i]
	tree=corrigerNumerotation(tree)
	return rootindeces

	

def fixOutname(name):
	if name.startswith("validated."): 
		name=name.split(".")[1]
		#name=name[len("validated."):]
	if name.endswith("lastHuman.conll"): name=name[:-len(".lastHuman.conll")]
	if name.endswith(".conll"): name=name[:-len(".conll")]
	return name

def createNonExistingFolders(path):
	head, tail = os.path.split(path)
	if head=="":
		if tail and not os.path.exists(tail): os.makedirs(tail)
	else:
		createNonExistingFolders(head)
	if head and not os.path.exists(head): os.makedirs(head)


def platinum(tree):
	removeTag2(tree)
	addinfototree(tree)
	#pprint (tree)
	
	tree=NNAAMMEE(tree)
	tree=cela(tree)
	tree=tapostrophe(tree)
	tree=sujEnSubj(tree)
	
	

	tree=emptyErrors(tree)
	if hasEmptyNode(tree): # testing whether all empty nodes have been removed
		print tree
		qsdf
	tree=kesk(tree)
	tree=rendezvous(tree)
	tree=pasgrandchose(tree)
	
	tree=auxEnTete(tree)
	tree=auxpara(tree)
	#print "after aux:"
	#pprint (tree)
	tree=desEnTete(tree)
	tree=adperiph(tree)
	#print "before aux:"
	#pprint (tree)
	tree=montperiph(tree)
	#print "after montperiph:"
	#pprint (tree)
	tree=rootperiph(tree)
	#pprint (tree)
	tree=csuperiph(tree)
	
	
	tree=prons(tree)
	tree=disflink(tree)
	tree=vrb(tree)
	tree=cestadire(tree)
	tree=kesk(tree)
	tree=affcomp(tree)
	
	#pprint (tree)
	
	return tree


def directDatabaseChangeForForgottenCorrection():
	from database import SQL
	sql=SQL("Platinum")
	db,cursor=sql.open()
	cursor.execute('update links set function="comp" where function="aff";')
	db.commit()	
	db.close()
	print "changed"



def transform(infolder,outfolder,mixOldNew=True):
	createNonExistingFolders(outfolder)
	spaceToks={}
	#for infile in sorted(glob.glob(os.path.join(infolder,"test.conll"))):
	for infile in sorted(glob.glob(os.path.join(infolder,"*.conll"))):
		if not os.path.isfile(infile): continue
		basename=os.path.basename(infile)
		print "reading",basename
		trees = conll.conllFile2trees(infile)
		newtrees=[]
		for tree in trees:
			if mixOldNew: newtrees+=[tree]
			newtree=copy.deepcopy(tree)
			newtree=platinum(newtree)
			newtrees+=[newtree]
			
			
		conll.trees2conllFile(newtrees,os.path.join(outfolder,fixOutname(basename)))
	
	
		
if __name__ == "__main__":

	transform("../projects/OrfeoGold2016/export/","../projects/OrfeoGold2016/platinum/",mixOldNew=False)		 
	#searches()
	#directDatabaseChangeForForgottenCorrection()
22 donc donc ADV
14 parce que parce que CSU
4 plus plus ADV
3 ensuite ensuite ADV
2 aussi aussi ADV
2 Ã  Ã  PRE
2 comme comme PRE
1 en tout cas en tout cas ADV
1 plus plus NOM
1 parce qu' parce que CSU
1 hm hm INT
1 sauf sauf PRE
1 au Ã +le PRE
1 en en PRE
1 mais mai NOM
1 pas pas ADN
1 sinon sinon ADV
1 soit Ãªtre VRB
1 notamment notamment ADV
1 Ã©galement Ã©galement NOM
1 si si CSU
1 comme comme CSU
total of 64 cases
# -*- coding: utf-8 -*-
#!/usr/bin/python

import os, subprocess, sys, glob, datetime, random, time, unicodedata, re, shutil, codecs, argparse

sys.path.insert(0, '../lib')
sys.path.insert(0, 'lib')

import conll, trees2train, updateTrees, getTextsForParsing
#from database import SQL
#import retokenisation

verbose=True

def checkIntegrity(infile):
	"""
	checks size of file
	if empty returns False
	else returns True
	"""
	b = os.path.getsize(infile)
	if b > 0:
		return True
	else:
		return False

def backupOldDatabase(project, outpath):
	oldpath = "projects/"+project
	newpath=outpath+project+"_"+datetime.datetime.now().strftime('%Y-%m-%d_%H:%M')
	command="cp -ar "+oldpath+" "+newpath
	p1 = subprocess.Popen([command],shell=True, stdout=subprocess.PIPE)
	out, err = p1.communicate()
	return newpath

def slugify(value):
	"""
	Normalizes string, converts to lowercase, removes non-alpha characters,
	and converts spaces to hyphens.
	"""
	
	value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
	value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
	return re.sub('[-\s]+', '-', value)

def createDailyPath(path, project):
	"""
	creates timestamped directory
	takes path as argument
	returns timestamped path
	"""
	if path[-1]!="/": path=path+"/"
	newpath=path+slugify(project)+"."+datetime.datetime.now().strftime('%Y-%m-%d_%H:%M')+"/"
	try:
		os.mkdir(newpath)
	except OSError:
		pass
	return newpath

def createDirectory(path):
	if path[-1]!="/": path=path+"/"
	try:
		os.mkdir(path)
	except OSError:
		pass
	return path

def getClasses(lang="graph"):
	"""
	returns the names of the classes to be used for parsing
	mind that the parseclass used for parsing and for evaluation are different
	"""
	#anna = "mate/mate_components/anna-3.3.jar" # in case the file is called from some other cwd:
	anna = os.path.abspath(os.path.join(os.path.dirname(__file__), 'mate/mate_components/anna-3.3.jar'))
	
	lemclass = "is2.lemmatizer.Lemmatizer"
	tagclass = "is2.tag.Tagger"

	if lang=="graph":
		parseclass = "is2.parser.Parser"

	elif lang=="transition":
		"""NOTE : problem probably caused by parser itself. 
		if transition-based is used for training, it adds "-tag" at the end of filename
		so transition is here just used for evaluation"""
		parseclass = "is2.transitionR6j.Parser"

	return anna, lemclass, tagclass, parseclass

def makeTrainingModels(basepath, lemtagin, depin, outfolder="mate/models", memory="4G", testfile="", evalfile="", lemma=None, parserType="graph"):
	"""
	training of mate
	creation of models used for parsing
	evaluation if testfile and evalfile are given
	returns names of models
	
	TODO: parserType is ignored for the time being!!!
	
	"""
	
	print "makeTrainingModels parameters:",basepath, lemtagin, depin,outfolder, memory, testfile, evalfile, lemma, parserType
	
	if outfolder[-1]!="/": outfolder=outfolder+"/"
	anna,lemclass, tagclass,parseclass = getClasses("graph")
	if evalfile=="":
		lemodel=outfolder+"LemModel"
		tagmodel=outfolder+"TagModel"
		parsemodel=outfolder+"ParseModel"
	else:
		lemodel=outfolder+"LemModelEval"
		tagmodel=outfolder+"TagModelEval"
		parsemodel=outfolder+"ParseModelEval"
		evalfile="-eval "+evalfile
	
	if testfile:
		lemtest="-test "+testfile
		if lemma: tagtest="-test "+testfile+"_lem"
		else: tagtest="-test "+testfile
		lemout="-out "+testfile+"_lem"
		tagout="-out "+testfile+"_tag"
	else:
		lemtest=""
		tagtest=""
		lemout=""
		tagout=""
	
	if verbose: print "\n\n========== Start of Training"
	
	lemcommand = "java -Xmx{memory} -cp {anna} {lemclass} -train {lemtagin} -model {lemodel} {lemtest} {lemout} {evalfile}".format(memory=memory, anna=anna, lemclass=lemclass, lemtagin=lemtagin, lemodel=lemodel, lemtest=lemtest, lemout=lemout, evalfile=evalfile)
	
	tagcommand = "java -Xmx{memory} -cp {anna} {tagclass} -train {lemtagin} -model {tagmodel} {tagtest} {tagout} {evalfile}".format(memory=memory, anna=anna, tagclass=tagclass,lemtagin=lemtagin, tagmodel=tagmodel, tagtest=tagtest, tagout=tagout, evalfile=evalfile)

	if evalfile=="":
		parsecommand = "java -Xmx{memory} -cp {anna} {parseclass} -train {depin} -model {parsemodel}".format(memory=memory, anna=anna, parseclass=parseclass, depin=depin, parsemodel=parsemodel)
	else:
		parsecommand="java -Xmx{memory} -cp {anna} {parseclass} -train {depin} -model {parsemodel} -test {testfile}_tag -out {testfile}_parse {evalfile}".format(memory=memory, anna=anna, parseclass=parseclass, parsemodel=parsemodel, depin=depin, testfile=testfile, evalfile=evalfile)

	if lemma:
		#if "trainlex" in lemma:
			#lemcommand = "java -Xmx{memory} -cp {anna} {lemclass} -train {infile} -model {lemodel} {lemtest} {lemout} {evalfile}".format(memory=memory, anna=anna, lemclass=lemclass, infile=lemma, lemodel=lemodel, lemtest=lemtest, lemout=lemout, evalfile=evalfile)
		if verbose: print "\n\n========== lemma training...", lemcommand
		p1 = subprocess.Popen([lemcommand],shell=True, stdout=subprocess.PIPE)
		out, err = p1.communicate()
		if verbose: print out, err
	else: lemodel=None
	if verbose: print "\n\n========== tag training...", tagcommand
	p1 = subprocess.Popen([tagcommand],shell=True, stdout=subprocess.PIPE)
	out, err = p1.communicate()
	if verbose: print out, err
	if verbose: print "\n\n========== dep training...", parsecommand
	p1 = subprocess.Popen([parsecommand],shell=True,  stdout=subprocess.PIPE)
	print  p1.stdout.read()
	if verbose: print "\n\n========== models done"
	
	return lemodel, tagmodel, parsemodel

def parsing(infile, lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", outfolder="parses", memory="4G", depparse=True, parserType="graph", lemmatized=False):
	"""
	parsing function
	TODO: parserType is ignored
	"""
	
	print "parsing now i'm at os.getcwd()",os.getcwd(),os.path.dirname(__file__),__file__
	
	if outfolder[-1]!="/": outfolder=outfolder+"/"
	outfile = outfolder+os.path.basename(infile)

	anna, lemclass, tagclass, parseclass = getClasses("graph")
	
	#anna = ".:./mate/mate_components/anna-3.3.jar"
	#anna = os.path.join(os.path.dirname(__file__), 'mate/mate_components/anna-3.3.jar')
	#print anna
	#print "/home/kim/Dropbox/programmation/arborator/trunk/tools/mate/mate_components/anna-3.3.jar"
	#assert anna=="/home/kim/Dropbox/programmation/arborator/trunk/tools/mate/mate_components/anna-3.3.jar"
	#print anna
	#anna = "anna-3.3.jar"
	#anna =        "/home/kim/Dropbox/programmation/arborator/trunk/tools/mate/mate_components/anna-3.3.jar"
	#print anna
	#lemodel="../platinum.2017-03-26_03:10/models/LemModel"
	#print lemodel
	#print "/home/kim/Dropbox/programmation/arborator/trunk/tools/mate/platinum.2016-11-29_00:22/models/LemModel"
	#assert lemodel=="/home/kim/Dropbox/programmation/arborator/trunk/tools/mate/platinum.2016-11-29_00:22/models/LemModel"
	#lemodel="/home/kim/Dropbox/programmation/arborator/trunk/tools/mate/platinum.2016-11-29_00:22/models/LemModel"
	#infile="/home/kim/Dropbox/programmation/arborator/trunk/tools/parses/upload.fr.txt.sents.conll"
	
	lemcommand="java -Xmx{memory} -cp {anna} {lemclass} -model {lemodel} -test {infile} -out {outfile}_lem".format(memory=memory, anna=anna, lemclass=lemclass, infile=infile, lemodel=lemodel, outfile=outfile)
	tagcommand="java -Xmx{memory} -cp {anna} {tagclass} -model {tagmodel} -test {outfile}_lem -out {outfile}_tag".format(memory=memory, anna=anna, tagclass=tagclass, tagmodel=tagmodel, outfile=outfile)
	parsecommand="java -Xmx{memory} -cp {anna} {parseclass} -model {parsemodel} -test {outfile}_tag -out {outfile}_parse".format(memory=memory, anna=anna, parseclass=parseclass, parsemodel=parsemodel, outfile=outfile)
	
	if lemodel and lemodel[-1]!="/":
		if verbose:print "\n\n========== lemmatizing...", lemcommand
		p1 = subprocess.Popen([lemcommand], shell=True, stdout=subprocess.PIPE, 
			cwd='/home/kim/Dropbox/programmation/arborator/trunk/tools/mate/mate_components')
		out, err = p1.communicate()
		if verbose:
			print out, err
	else:
		if lemmatized:
			print "copying",outfolder+os.path.basename(infile),"as lemma file"
			shutil.copyfile(outfolder+os.path.basename(infile), outfolder+os.path.basename(infile)+"_lem")
		else:
			print "adding toks as lems",outfolder+os.path.basename(infile)
			trees=conll.conllFile2trees(infile)
			with codecs.open(outfolder+os.path.basename(infile)+"_lem","w","utf-8") as lemf:
				for tree in trees:
					lemf.write(newconvert.treeToEmptyConll14Text(tree, lemma=False)+"\n")
	if verbose:
		print "\n\n========== tagging...", tagcommand
	p1 = subprocess.Popen([tagcommand],shell=True, stdout=subprocess.PIPE)
	out, err = p1.communicate()
	if verbose: print out, err
	if depparse:
			if verbose: print "\n\n========== dep analysis...", parsecommand
			p1 = subprocess.Popen([parsecommand],shell=True,  stdout=subprocess.PIPE)
			print  p1.stdout.read()
			if verbose: print "\n\n========== parsed"
			#java -Xmx40G -cp mate/mate_components/anna-3.3.jar is2.parser.Parser -model ParseModel -test annotatedCorpora.corr.conll_tag -out naija_parse
						#anna = ""
				#lemclass = "is2.lemmatizer.Lemmatizer"
				#tagclass = "is2.tag.Tagger"

				#if lang=="graph":
					#parseclass = ""

	if checkIntegrity(outfile+'_parse') == False:
		print "*********ERROR IN FILE", outfile+"_parse", "Please Try again*********"
	return outfile+"_parse"


def detailedEvaluation(parserType="graph", memory="4G", testfile="", evalfile="", path="mate/evaluation", evaluationPercent=10):
	"""
	takes a mate-parsed file (testfile) and compares it to a gold file (evalfile)
	writes detailed evaluation in timestamped logfile
	returns name of logfile
	"""
	f=open(path+"evaluation_"+str(evaluationPercent)+"."+datetime.datetime.now().strftime('%Y-%m-%d_%H:%M')+"transition_based_anna.3.3.txt", "w")

	anna,lemclass, tagclass,parseclass = getClasses("transition")
	
	evalcommand="java -Xmx4G -cp {anna} {parseclass} -eval {evalfile} -out {testfile}".format(anna=anna, parseclass=parseclass, evalfile=evalfile, testfile=testfile)

	if verbose: print "evaluation\n", evalcommand
	p1 = subprocess.Popen([evalcommand],shell=True,  stdout=subprocess.PIPE)
	f.write(p1.stdout.read())
	#print p1.stdout.read()
	return f.name


def mateLogs(text, mode="w"):
	with codecs.open("./mate/parse.log", mode, "utf-8") as f:
		f.write(text)
	if verbose:
		print "\n\n==========",text


"""
STEPS
	1. copier BDD dans dossier sauvegardesOrfeo
	2. extraire arbres validÃ©s (trees2train.getnewtrees())
	3. entrainer mate
		3a. sur 90% pour evaluation
		3b. sur 100% pour modÃ¨le
	3. extraire textes Orfeo + conll.makeEmpty
	4. parser textes Orfeo
	5. remettre en ligne sous user = parser

Ne pas oublier :
	fichiers logs
	revoir arborescence projet
		un dossier horodatÃ© (Jour+heure)
			Un sous dossier avec les modÃ¨les (full/partiel)
			un sous dossier avec les arbres parsÃ©s
			un sous dossier avec les diffÃ©rents fichiers d'entrainement
"""
def treeToConll14Text(tree):
	outtext=""
	for i in sorted(tree.keys()):
		node = tree[i]
		gov = node.get("gov",{}).items()
		govid = -1
		func = "_"
		
		if gov:
			for govid,func in gov:
				if govid!=-1:
					outtext+="\t".join([str(i),node.get("t","_"), node.get("lemma",""), node.get("lemma",""), node.get("tag","_"), node.get("tag","_"), "_","_", str(govid), str(govid),func,func,"_","_"])+"\n"
		else:
			outtext+="\t".join([str(i),node.get("t","_"), node.get("lemma",""), node.get("lemma",""), node.get("tag","_"), node.get("tag","_"), "_","_", str(govid), str(govid),func,func,"_","_"])+"\n"
	return outtext

def treeToEmptyConll14Text(tree, lemma=True):
	outtext=""
	for i in sorted(tree.keys()):
		node = tree[i]
		lem="_"
		if not lemma: lem=node.get("t","_")
		outtext+="\t".join([str(i),node.get("t","_"), node.get("t","_"), lem]+["_"]*10)+"\n"
	return outtext

def makeTrainTestSets(infolder,pattern="*conll",train="train",test="test",empty="emptytest",testsize=10, lemma=True):
	"""
	chooses testsize % for test file, rest for train file
	"""
	tottec,tottrc,toks=0,0,0
	traintrees=[]
	with codecs.open(os.path.join(infolder, test),"w","utf-8") as testf, codecs.open(os.path.join(infolder, empty),"w","utf-8") as emptyf, codecs.open(os.path.join(infolder, train),"w","utf-8") as trainf:
		for infilename in glob.glob(os.path.join(infolder, pattern)):
			print "newconvert: looking at",infilename
			
			tec,trc=0,0
			allsentences=conll.conllFile2trees(infilename)
			print len(allsentences),"sentences"
			testselection=random.sample(range(len(allsentences)),len(allsentences)*testsize/100)
			
			for i,s in enumerate(allsentences):
				toks+=len(s)
				if i in testselection:
					testf.write(treeToConll14Text(s)+"\n")
					emptyf.write(treeToEmptyConll14Text(s,lemma)+"\n")
					tec+=1
				else:
					traintrees+=[s]
					trainf.write(treeToConll14Text(s)+"\n")
					trc+=1
						
			print "testing with",tec,"sentences. training with",trc,"sentences"
			tottec+=tec
			tottrc+=trc
			if not lemma: shutil.copyfile(os.path.join(infolder, empty), os.path.join(infolder, empty)+"_lem")
	print "tottec,tottrc,toks=",tottec,tottrc,toks
	return traintrees

			
def splitForTraining(infilename="Rhapsodie.micro_simple.conll.oldnum"):
	"""
	+ splitting for parsing
	"""
	count=0
	with codecs.open("bonne.Rhapsodie1000","w","utf-8") as outfile, codecs.open("bonne.Rhapsodie600","w","utf-8") as testfile, codecs.open("bonne.Rhapsodie600empty","w","utf-8") as emptyfile:
		
		for tree in conllFile2trees(infilename):
			count+=1
			treecorrdic={0:0}
			for i,tokenid in enumerate(sorted(tree)):
				treecorrdic[tokenid]=i+1
			print treecorrdic
			for i,tokenid in enumerate(sorted(tree)):
				node=tree[tokenid]
				gov = node.get("gov",{}).items()
				govid,func= gov[0]
				#govid
				print "____",i+1,tokenid,node,govid,func,treecorrdic,treecorrdic[tokenid],treecorrdic[govid]
				
				if count<=1000:
					outfile.write("\t".join([str(treecorrdic[tokenid]),node.get("t","_"), node.get("lemma","_"), node.get("lemma","_"), node.get("tag","_"), node.get("tag","_"), node.get("morph","_"), node.get("morph","_"), str(treecorrdic[govid]),str(treecorrdic[govid]),func,func,"_","_"])+"\n")
				else:
				
					testfile.write("\t".join([str(treecorrdic[tokenid]),node.get("t","_"), node.get("lemma","_"), node.get("lemma","_"), node.get("tag","_"), node.get("tag","_"), node.get("morph","_"), node.get("morph","_"), str(treecorrdic[govid]),str(treecorrdic[govid]),func,func,"_","_"])+"\n")
					emptyfile.write("\t".join([str(treecorrdic[tokenid]),node.get("t","_"), "_", "_", "_", "_", "_", "_", "_","_","_","_","_","_"])+"\n")
			if count<=1000:outfile.write("\n")
			else:
				testfile.write("\n")
				emptyfile.write("\n")

def trainingEvaluationParsing(project=u"OrfeoGold2016", parserType="graph", whoseTrees="validator", evaluationPercent=10, additionnalLexicon=None, resultAnnotator="mate", getFromFolder=False, parseDB=False, memory="40G", stopOnError=False):
	"""
	if additionnalLexicon is given, it is joined to the training file for lemmatization and tagging.
	change memory here!
	todo :
		- add function to choose parser type (lang=)
		- creer mate.log pour progression (fin = "Ready.")
	"""
	mateLogs("Begin")
	ti = time.time()
	
	if getFromFolder: parseDB=False # TODO: correct this so that all options are available
	parserType=(parserType or "graph")
	whoseTrees=whoseTrees or "validator"
	evaluationPercent=evaluationPercent or 10
	resultAnnotator=resultAnnotator or "mate"
	
	try:os.chmod("mate/parse.log", 0666) # just in case...
	except:pass
	
	timestamp=datetime.datetime.now().strftime('%Y-%m-%d_%H:%M')


	#####
	#defining project and creation of saves directories
	#####
	
	basepath= createDailyPath("./mate/", project)
	if parseDB:
		backupbase=backupOldDatabase(project,basepath)
		mateLogs("A copy of the database has been stored in {backupbase}. Getting validated trees...".format(backupbase=backupbase) )
	traindir=createDirectory(basepath+"training")
	modeldir=createDirectory(basepath+"models")
	logdir=createDirectory(basepath+"logs")
	parsedir=createDirectory(basepath+"parses")


	#####
	#getting gold trees for training
	#####
	
	if getFromFolder: # getFromFolder contains folder name containing only conll files
		error=False
		goldtrees=[]
		for infile in glob.glob(os.path.join(getFromFolder, '*')):
			if os.path.isfile(infile):
				print "reading",infile
				gtrees=conll.conllFile2trees(infile)
				for tree in gtrees:
					problemkeys=[]
					for i in tree:
						for gi in tree[i]["gov"]:
							if not 0<=gi<=len(tree):
								print infile
								print tree
								print "has a problematic governor:",gi
								error=True
								problemkeys+=[i]
					for problemk in problemkeys:
						del tree[problemk]
				goldtrees+=gtrees
		if error and stopOnError: sys.exit()
	else:
		goldtrees = trees2train.getValidatedTrees(project, basepath, whoseTrees)
	mateLogs( u"{nrtrees} validated trees extracted".format(nrtrees=len(goldtrees)) ) 
		
	lemma = None
	if goldtrees: # see whether the first token of the first tree has a lemma. if lemma==None: we'll skip lemmatization
		lemma=goldtrees[0][sorted(goldtrees[0])[0]].get("lemma",None) # just trying to get the first lemma value
		if lemma=="_": lemma=None
		print "found lemma in first tree:",lemma
		#TODO: do something here: double tokens as lemmas for chinese, see function makeTrainTestSets
	else:
		print "no trees from:",getFromFolder
		sys.exit()
	print "goldtrees:",len(goldtrees)
	
	#####
	#creating trainingfiles
	#####
	
	alldeptraining=traindir+"alldeptraining.conll"
	conll.trees2conllFile(goldtrees, alldeptraining, columns=14)
	traintrees=makeTrainTestSets(traindir,pattern=os.path.basename(alldeptraining),train="partialdeptrain.conll",test="test.conll",empty="emptytest.conll",testsize=int(evaluationPercent), lemma=lemma)
	print "traintrees:",len(traintrees)
	if additionnalLexicon:
		lexicontrees = conll.conllFile2trees(additionnalLexicon)
		print "lexicontrees:",len(lexicontrees)
		alllemtagtrain=traindir+"alllemtagtrain.conll"
		conll.trees2conllFile(goldtrees+lexicontrees, alllemtagtrain, columns=14)
		partiallemtagtrain=traindir+"partiallemtagtrain.conll"
		conll.trees2conllFile(traintrees+lexicontrees, partiallemtagtrain, columns=14)
	else:
		alllemtagtrain=alldeptraining
		partiallemtagtrain=traindir+"partialdeptrain.conll"
	
	#creating files used for evaluation
	#if isinstance(evaluationPercent, str): evaluationPercent = int(evaluationPercent)
	mateLogs("trainfiles created")
	if verbose:
		print "just testing whether i can load them..."
		conll.conllFile2trees(traindir+"partialdeptrain.conll")
		conll.conllFile2trees(traindir+"emptytest.conll")
		conll.conllFile2trees(traindir+"test.conll")
	
	
	mateLogs("training of partial tree file for evaluation... ====")
	lemodelpartial, tagmodelpartial, parsemodelpartial=makeTrainingModels(basepath, lemtagin=partiallemtagtrain, depin=traindir+"partialdeptrain.conll",outfolder=modeldir, memory=memory, testfile=traindir+"emptytest.conll", evalfile=traindir+"test.conll", lemma=lemma, parserType=parserType)
	mateLogs("evaluation...")
	#evaluation
	evaluFileName = detailedEvaluation(parserType=parserType, memory=memory, testfile=traindir+"emptytest.conll_parse", evalfile=traindir+"test.conll", path=logdir, evaluationPercent=evaluationPercent)
	
	evalu=unicode(evaluFileName)+"\n"
	with codecs.open(evaluFileName,"r","utf-8") as f:
		evalu+=f.read()
	
	#full training
	mateLogs("training of full tree file for parsing... ====")
	lemodel, tagmodel, parsemodel=makeTrainingModels(basepath, lemtagin=alllemtagtrain, depin=alldeptraining,outfolder=modeldir, memory=memory,lemma=lemma, parserType=parserType)
	#getting texts to parse
	mateLogs("training and evaluation complete. Starting the parse...\n\n{evalu}".format(evalu=evalu))
	#filenames=getTextsForParsing.main(project, parsedir)
	if parseDB:
		filenames=getTextsForParsing.extractConllFiles(project, parsedir)
		#parsing
		for infile in filenames:
			#mateLogs("Training and evaluation complete. Starting the parse of {infile}\n\n{evalu}".format(infile=infile, evalu=evalu))
			mateLogs("Training and evaluation complete. Starting the parse of {}\n\n".format(infile))
			parsedfile=parsing(infile, lemodel=lemodel, tagmodel=tagmodel, parsemodel=parsemodel, outfolder=parsedir, parserType=parserType, memory=memory)
			#update on base
			newname=os.path.basename(parsedfile)
			updateTrees.updateParseResult(project, parsedir, filepattern=newname, annotatorName=resultAnnotator, removeToGetDB="_parse")

	
	
	# make it easy for everyone to erase all this stuff:
	for root, dirs, files in os.walk(basepath):
		for momo in dirs:
			try:	os.chmod(os.path.join(root, momo), 0777)
			except:	pass
		for momo in files:
			try:	os.chmod(os.path.join(root, momo), 0666)
			except:	pass
	
	totaltime=(time.time()-ti)/60
	mateLogs("Ready. It took {totaltime} minutes for the whole process\n\n{evalu}".format(totaltime=round(totaltime,1), evalu=evalu) )


if __name__ == "__main__":
	""" 
	example usage:
	python mate.py Platinum --getFromFolder ../projects/Platinum/export/
	python mate.py Platinum -f projects/Platinum/punc/
	python mate.py Platinum -a mate/VRB.conll14 -f projects/Platinum/punc/
	python mate.py Platinum -a mate/fr/lex.conll14 -f projects/Platinum/punc/
	python mate.py Platinum -f ../projects/Platinum/punc/
	python mate.py Platinum -f ../projects/Platinum/exportcool/
	python mate.py Platinum -f ../projects/Platinum/exportcorrected/
	
	python mate.py HKUD -f mate/testEvalMate/training/HongKongTVMandarin.UD_ZH%.conll14
	python mate.py HKUD -f mate/testEvalMate/training/alltrees/

	python mate.py Platinum -f enpetit/
	python mate.py Platinum -f en/
	"""	
	parser = argparse.ArgumentParser(description='Trains Mate, evaluates, and parses the complete project')
	parser.add_argument('project', type=lambda s: unicode(s, 'utf8'), help='project name')
	parser.add_argument('-t','--parserType', help='parser type: graph or transition', type=lambda s: unicode(s, 'utf8'), required=False)
	parser.add_argument('-w','--whoseTrees', help='take validated trees from: all or validator', type=lambda s: unicode(s, 'utf8'), required=False)
	parser.add_argument('-e','--evaluationPercent', help='percentage of trees taken for evaluation', type=lambda s: unicode(s, 'utf8'), required=False)
	parser.add_argument('-a','--additionnalLexicon', help='name of additional lexicon', type=lambda s: unicode(s, 'utf8'), required=False)
	parser.add_argument('-r','--resultAnnotator', help='username to be used for the newly parsed trees', type=lambda s: unicode(s, 'utf8'), required=False)
	parser.add_argument('-f','--getFromFolder', help='name of folder containing the gold conlls', type=lambda s: unicode(s, 'utf8'), required=False)
	parser.add_argument('-testRun', help="don't actually parse", action='store_true')
	args = vars(parser.parse_args())
	
	#trainingEvaluationParsing(project=u"OrfeoGold2016")
	if args.get("testRun",None):
		mateLogs("Ready. It took {totaltime} seconds for the whole process".format(totaltime=1) )
		print "ok so far", args["testRun"]
	else:
		#print 'args.get("whoseTrees","validator")',args.get("whoseTrees","validator")
		trainingEvaluationParsing(project=args["project"], parserType=args.get("parserType","graph"), whoseTrees=args.get("whoseTrees","validator"), evaluationPercent=int(args.get("evaluationPercent",None) or 10), additionnalLexicon=args.get("additionnalLexicon",None), resultAnnotator=args.get("resultAnnotator","mate"), getFromFolder=args.get("getFromFolder",None))
		
	
	



#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys, json, codecs, copy, collections, re, hashlib, os, glob
from itertools import izip

#d = defaultdict(int)

def readconll(gold,parse,attached=True):
	"""
	attached: only count relation if governor is correctly predicted
	"""
	actual={} # actual relation : number
	predic={} # predicted relation : number
	actualpredic={} # actual relation to dic { predicted relation : number }
	with codecs.open(gold,"r","utf-8") as goldf, codecs.open(parse,"r","utf-8") as parsef:
		for g, p in izip(goldf, parsef):
			gcells = g.split('\t')
			pcells = p.split('\t')
			if len(gcells)==14:
				nr, t, lemma, lemma2, tag, tag2, morph, morph2, head, ghead, rel, grel, _, _ = gcells
				nr, t, lemma, lemma2, tag, tag2, morph, morph2, head, phead, rel, prel, _, _ = pcells
				if (not attached) or (attached and ghead==phead):
					#print ghead,phead,grel,prel
					actualpredic[grel]=actualpredic.get(grel,{})
					actualpredic[grel][prel]=actualpredic[grel].get(prel,0)+1
					predic[prel]=predic.get(prel,0)+1
				actual[grel]=actual.get(grel,0)+1
	return actual, predic, actualpredic

def writeTable(conf,actual, predic, actualpredic, info):
	conf.write("\t".join([info,"#"]+sorted(actualpredic))+"\n")
	for grel in sorted(actualpredic):
		conf.write("\t".join([grel,str(actual[grel])]+[str(actualpredic[grel].get(prel,0)) for prel in sorted(actualpredic)])+"\n")
	conf.write("\n")
	
	ftoline={}
	alltp=0
	allpredic=0
	allactual=0
	conf.write("\t".join(["",	"#",		"tp",		"found",		"precision",		"recall",		"fscore"])+"\n")
	for grel in sorted(actualpredic):
		tp=actualpredic[grel].get(grel,0)
		precision=float(tp)/predic[grel]
		recall=float(tp)/actual[grel]
		alltp+=tp
		allpredic+=predic[grel]
		allactual+=actual[grel]
		
		if precision+recall:	fscore=round(2.0*precision*recall/(precision+recall),2)
		else: fscore=0.0
		line="\t".join([grel,	str(actual[grel]),str(tp),	str(predic[grel]), 	str(round(precision,2)),str(round(recall,2)),	str(fscore)])+"\n"
		ftoline[fscore]=ftoline.get(fscore,[])+[line]
		conf.write(line)
	conf.write("\n")
	
	conf.write("\t".join([info,	"#",		"tp",		"found",		"precision",		"recall",		"fscore"])+"\n")
	for fscore in sorted(ftoline,reverse=True):
		conf.write("".join(ftoline[fscore]))
	conf.write("\n")
	
	conf.write("\t".join(["#",		"tp",		"found",		"precision",		"recall",		"fscore"])+"\n")
	precision=float(alltp)/allpredic
	recall=float(alltp)/allactual
	if precision+recall:	fscore=round(2.0*precision*recall/(precision+recall),2)
	else: fscore=0.0
	line="\t".join([str(allactual),str(alltp),	str(allpredic), 	str(round(precision,2)),str(round(recall,2)),	str(fscore)])+"\n"
	conf.write(line)
	conf.write("\n")

def confusion(gold,parse):
	actual, predic, actualpredic= readconll(gold,parse,attached=False)
	with codecs.open(parse+".confusion.tsv","w","utf-8") as conf:
		writeTable(conf,actual, predic, actualpredic, "corr func")
		
	actual, predic, actualpredic= readconll(gold,parse,attached=True)
	with codecs.open(parse+".confusion.tsv","a","utf-8") as conf:
		writeTable(conf,actual, predic, actualpredic, "corr attach")
		
		
		
		
if __name__ == "__main__":
	confusion("test","emptytest_parse")

#! usr/bin/python
#! coding: utf-8

import codecs, glob, random, shutil, time, sys, os, psutil
import regex as re

from mate import parsing
sys.path.insert(0, '../lib')

import conll

verbose=False
#verbose=True

droporfeo='/home/kim/Dropbox/Orfeo/lexique/lexiqueCorrSimp/' # where we can find the lexique folder



def lireDictionnaires(dicofolder=None):
	"""
	Lecture des fichiers du lexique
	"""
	if not dicofolder: dicofolder = droporfeo
	specialCharDic={}
	onlyLetters=re.compile(ur'''^\w+$''',re.U+re.I)

	
	for lexfile in glob.glob(dicofolder+'*.sfplm'):
		# Lecture des fichiers .sfplm
		#print "reading",lexfile
		with codecs.open(lexfile, "r", "utf8") as f:
			for ligne in f:
				if len(ligne) and "\t" in ligne:
					t,lem=ligne.strip().split("\t")
					#if t=="~": print lexfile, qsdf
					#if (not onlyLetters.match(t[0])) or (not onlyLetters.match(t[1:-1].replace("-",""))) or not onlyLetters.match(t[-1]) :
					specialCharDic[t]=None
	if "~" in specialCharDic: del specialCharDic["~"]
	print len(specialCharDic),"special character words"
	specialCharWords = sorted(specialCharDic, key=len, reverse=True)
	#print "Fin de lecture du dictionnaire"
	#for w in specialCharWords: print w
	return specialCharWords

repoint=re.compile(ur'(?<![0-9A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ])([.ã]+)')
repointEnd=re.compile(ur'([.ã]+)$')
reponctWithNum=re.compile(ur'(?<![0-9])(\s*[;:,!\(\)Â§"]+)')
rehyph=re.compile(ur'(\s*[\/\']+)') # \- pas de segmentation des traits d'union
revirer=re.compile(ur'[`]+')
redoublespace=re.compile(ur'\s+',re.U)
reparenth=re.compile(ur'\([ \w]*\)',re.U)

def preparetokenize(text):
	text=text.strip()
	text=text.replace(u"â","'")
	text=revirer.sub(r" ",text)
	text=repointEnd.sub(r" \1 ",text)
	text=repoint.sub(r" \1 ",text)
	text=reponctWithNum.sub(r" \1 ",text)
	text=rehyph.sub(r"\1 ",text)
	return text

def textToSentences(text, outname, problemout=None, keepEndSent=False, maxlength=50, writeProblemBeforeSentence=True, includeProblemSentence=False):
	"""
	creates one sentence per line file in name outname
	"""
	#writeProblemFile=False, 
	endsent=re.compile(u'(?<=(?<=\w\w\s?|\s|~))([!?.]+([ Â ]*Â»+)*)',re.U) # TODO fix for xx~.
	
	#endsent=re.compile(r"(([\?\!ï¼](?![\?\!])|((?<!\s[A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ])\.ãï¼â¦â¦\s)|\s\|)\s*)", re.M+re.U)
	#print 777,text
	#text=preparetokenize(text).strip()
	text=text.strip()
	#print 777,text
	
	#for sent in text.split("."):
	#if keepEndSent: 	sents = endsent.sub(ur"\1\n",u"Ã§a va ? comment ! qsdf. oh!").split("\n")
	#else: 		sents = endsent.sub(ur"\n",u"Ã§a va ? comment ! qsdf. oh!").split("\n")
	if keepEndSent: 	sents = endsent.sub(ur"\1\n",text).split("\n")
	else: 			sents = endsent.sub(ur"\n",text).split("\n")
	
	#print endsent.sub(ur"\n",u"Si je devais parler d'un Ã©vÃ©nement marquant euh, je parlerais de l'annÃ©e 2010. En effet je faisais de la GRS Ã  haut niveau, j'ai commencÃ© Ã  l'Ã¢ge de cinq ans et euh en 2010 je faisais partie euh d'une Ã©quipe de cinq. Comment va le P.S.G. ? Et tout le reste ? pas trop mal !").split("\n")
	#qsdf
	
	sents=[s for s in sents if s.strip()]
	#print sents
	if problemout: longfile = codecs.open(problemout,"w","utf-8")
	
	with codecs.open(outname,"w","utf-8") as courtfile:
		
		for sent in sents:
			sent=preparetokenize(sent)
			sent=redoublespace.sub(" ",sent)
			sent=reparenth.sub(" ",sent).strip()
			
			sent=sent.replace("aujourd' hui","aujourd'hui")
			sent=sent.replace("quelqu' un","quelqu'un")
			
			#sent=sent.replace("parce qu","parce_qu")
			#sent=reinvers.sub(ur" \1",sent)
			
			
			if sent:
				if problemout: longfile.write(sent+"\n") # +" .\n"
				if keepEndSent: simp = sent
				else:		simp=reponct.sub(" ",sent)
				simp=redoublespace.sub(" ",simp).strip()
				if not maxlength or len(simp.split())<maxlength:
					courtfile.write(simp+"\n")
				else:
					#pass
					#courtfile.write(u"_______________phrase trÃ¨s longue:\n")
					print "_______________phrase trÃ¨s longue:"
					print simp
					#if writeProblemFile:
						#with codecs.open("sentences/"+simplename+".problem.txt","a","utf-8") as probfile:
							#probfile.write(u"_______________phrase trÃ¨s longue:\n"+simp+" \n")
					if includeProblemSentence:
						if writeProblemBeforeSentence:
							courtfile.write("long ! ")
						courtfile.write(simp+"\n")
						


	
def removePuncsFromConllfile(conllfile):
	with codecs.open(conllfile,"r","utf-8") as f:
		conlltext=f.read()
		conlltext=conlltext.replace("0	_	punc","-1	_	punc")
	with codecs.open(conllfile,"w","utf-8") as f:
		f.write(conlltext)

def degradeConllfile(conllfile, removeFuncs=["para"], removeDeps=0.4):
	trees=conll.conllFile2trees(conllfile)
	nbgovs=0
	for arbre in trees:
		for i, node in arbre.iteritems():
			if "gov" in node and node["gov"].keys()[0]!=-1 and node["gov"].values()[0] not in removeFuncs: 
				nbgovs+=1
	print int(nbgovs*removeDeps)
	tobeRemoved=sorted(random.sample(range(nbgovs),int(nbgovs*removeDeps)))
	print "nbgovs:", nbgovs, "tobeRemoved:", tobeRemoved
	nbgovs=0
	for arbre in trees:
		for i, node in arbre.iteritems():
			if "gov" in node and node["gov"].keys()[0]!=-1:
				if node["gov"].values()[0] in removeFuncs: 
					node["gov"]={}
				else:
					nbgovs+=1
					if nbgovs in tobeRemoved:
						node["gov"]={}
	newname=conllfile
	if conllfile.endswith(".conll"): newname=conllfile[:-len(".conll")]
	shutil.move(conllfile, newname+".orig")
	conll.trees2conllFile(trees, newname+".deg", columns=10)
	
	
	

def emptyFromSentence(sentencefile, specialCharWords=[],outfolder="."):
	"""
	file with one sentence per line --> conll14
	if specialCharWords not empty, it is used for tokenization
	"""
	outname=os.path.join(outfolder,os.path.basename(sentencefile)+".conll")
	print "emptyFromSentence:",sentencefile, outfolder, outname
	with codecs.open(sentencefile,"r","utf-8") as f, codecs.open(outname,"w","utf-8") as g:
		for li in f:
			if specialCharWords: 	toks = tokenize(li,specialCharWords)
			else:			toks = simpletokenize(li)
			for num,tok in enumerate( toks ):
				g.write("\t".join([str(num+1), tok ]+["_"]*12)+'\n')
			g.write("\n")

	return outname


def simpletokenize(text, returnMatchInfo=False):
	"""
	simple punctuation and space based tokenization
	
	specific for french (apostrophes are glued to the precedent word: d' un
	in english we'd need: do n't Mike 's
	"""
	if verbose: print "_____before simpletokenize",text
	reponct=re.compile(ur'''(\s*[.;:,!?\(\)Â§"'Â«Â»]+)''',re.U+re.M) # prepare for default punctuation matching. removed - \d from list
	renogroupponct=re.compile(ur'''(\s*[;:,Â«Â»\(\)"])''', re.U+re.M) # signs that have to be alone - they cannot be grouped
	
	
	
	## do the remaining simple token-based splitting
	toks=[]
	#for text,done in ntoks:
		#if done:
			#nntoks+=[(text,done)]
		#else:
	text=reponct.sub(r" \1 ",text).replace(" '","'") # spaces around punctuation, but not before hyphen (french specific!)
	text=renogroupponct.sub(r" \1 ",text).replace(" ~","~") # spaces around no group punctuation

	#nntoks += [(t,1) for t in text.split()] #
	if verbose: print "____after simpletokenize",text
	return text.split()
	
	
def numurltokenize(text, returnMatchInfo=False):
	"""
	number and url tokenization
	
	"""
	reurl=re.compile(ur'''(https?://|\w+@)?[\w\d\%\.]*\w\w\.\w\w[\w\d~/\%\#]*(\?[\w\d~/\%\#]+)*''', re.U+re.M+re.I)
	resignswithnumbers=re.compile(ur'''(?<![0-9A-ZÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ])\d+[\d,.\s]+''', re.U+re.M)
	
	if verbose: print "before numurltokenize",text
	
	# do the url recognition
	toks=[] # couples : (tok, todo=0, done=1)
	laststart=0
	for m in reurl.finditer(text):
		#print 'reurl:%02d-%02d: %s' % (m.start(), m.end(), m.group(0))
		toks += [(text[laststart:m.start()],0), (m.group(0).strip(),1)] #
		#print toks
		laststart=m.end()
	toks += [(text[laststart:],0)] #
	#print toks
	
	# do the number recognition
	ntoks=[]
	for text,done in toks:
		if done:
			ntoks+=[(text,done)]
		else:
			laststart=0
			for m in resignswithnumbers.finditer(text):
				#print 'resignswithnumbers:%02d-%02d: %s' % (m.start(), m.end(), m.group(0))
				ntoks += [(text[laststart:m.start()],0), (m.group(0).strip(),1)] #
				#print toks
				laststart=m.end()
			ntoks += [(text[laststart:],0)] #
	if verbose: print "after numurltokenize",ntoks
	
	if returnMatchInfo: 	return ntoks
	else:			return [t for t,done in ntoks]
	


def tokenize(line, specialCharWords):
	"""
	tokenization of line
	uses specialCharWords = list of words ordered by size (bigger first)
	returns list of tokens
	"""
	
	# prepare line:
	line=line.replace(u"â","'")
	respaces=re.compile(ur'\s+')
	line=respaces.sub(r" ",line)
	
	#print u"Ã  part entiÃ¨re" in specialCharWords
	
	# prepare multiword matching:
	multimatch = ur"(( |\b)"+    ur"( |\b)|( |\b)".join([re.escape(mm) for mm in specialCharWords])   +ur"( |\b))"
	#multimatch = ur"(\b"+    ur"\b|\b".join([re.escape(mm) for mm in specialCharWords])   +ur"\b)"
	remultimatch=re.compile(multimatch,re.U+re.I)
	# go!
	toks=[]
	for chunk, done in numurltokenize(line,returnMatchInfo=True):
		#print "tokenize chunk",chunk, done
		if done:
			toks+=[chunk]
		else:
			laststart=0
			for m in remultimatch.finditer(chunk):
				#print 'remultimatch:%02d-%02d: %s' % (m.start(), m.end(), m.group(0))
				#print "ppp",laststart,m.start()
				toks += simpletokenize(chunk[laststart:m.start()]) + [m.group(0).strip()] # strip is needed for cases like "etc. !"
				#print toks
				laststart=m.end()
			toks+=simpletokenize(chunk[laststart:])
	
	if verbose: print "after tokenize",toks
	return toks
	
			
def parseSentenceFile(sentencefile, modelfolder="mate/platinum.2016-10-20_01:34/models/", lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="4G", removePunct=False, specialCharWords=[], degrade=False):
	"""
	removed parsertype from options!
	"""
	print "\n\nparseSentenceFile",sentencefile
	emptyConll = emptyFromSentence(sentencefile, specialCharWords=specialCharWords, outfolder=outfolder)
	print "made empty",emptyConll
	parsedfile=parsing(emptyConll, lemodel=modelfolder+lemodel, tagmodel=modelfolder+tagmodel, parsemodel=modelfolder+parsemodel, outfolder=outfolder, memory=memory)
	print parsedfile
	if removePunct: removePuncsFromConllfile(parsedfile)
	if degrade: degradeConllfile(parsedfile)
	return parsedfile

def textToParseAll(simpletxtfile):
	
	#if not os.path.dirname(os.path.abspath(sys.argv[0])).endswith("tools"): # problem when importing from uploadFile via parSentences
		#os.chdir(os.path.dirname(os.path.abspath(sys.argv[0]))+"/tools")
		#print 444,os.path.dirname(os.path.abspath(sys.argv[0]))+"/tools"
		#print "now i'm at os.getcwd()",os.getcwd()
	
	#specialCharWords=lireDictionnaires(os.path.join(os.path.dirname(__file__),"fr/lexiqueCorrSimp/"))
	specialCharWords={}
	memory = str(psutil.virtual_memory().total/(1000000000)-1)+"G"
	textToSentences(codecs.open(simpletxtfile,'r','utf-8').read(),simpletxtfile+".sents" , maxlength=0, keepEndSent=True)
	
	parsedfile = parseSentenceFile(os.path.abspath(simpletxtfile+".sents"), 
		modelfolder=os.path.abspath(os.path.join(os.path.dirname(__file__),"mate/platinum.2016-11-29_00:22/models"))+"/", 
		lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", 
		outfolder=os.path.join(os.path.dirname(__file__),"parses"), memory=memory, removePunct=False, specialCharWords=specialCharWords, degrade=False)
	return parsedfile

if __name__ == "__main__":
	ti=time.time()
	specialCharWords=lireDictionnaires()
	#sent=u"D'abord, dit-il, c'est elle-mÃªme qui Â« regardeÂ» aujourd'hui le long-mÃ©trage Â«Â Â§218 etc.Â Â Â», long de 2,3m !!! Et c'est Â«Â avecÂ Â» le gars-mÃªme qui l'a vue sur Http://liberation.fr?!?! Allons-y pour voir l'Ã¢ge de/des Åuvre(s)... J'irais pour 100 000â¬ pour lui envoyer un mÃ¨l Ã  part entiÃ¨re au qsdf@sqfd3sqdf.com"
	#toks= simpletokenize(sent)
	#print toks
	#print " ".join( toks )
	#toks = tokenize(sent,specialCharWords=lireDictionnaires()) 
	#print toks
	#print "__".join( toks )
	#print re.sub(ur"\s+","",sent)==re.sub(ur"\s+","","".join(toks))
	#print re.sub(ur"\s+","",sent)
	#print re.sub(ur"\s+","","".join(toks))
	
	#emptyFromSentence("2a.txt",specialCharWords=lireDictionnaires())
	
	#parseSentenceFile("2a.txt", modelfolder="mate/platinum.2016-11-14_02:05/models/", lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="7G", removePunct=True, specialCharWords=specialCharWords, degrade=True)
	#parseSentenceFile("2b.txt", modelfolder="mate/platinum.2016-11-14_02:05/models/", lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="7G", removePunct=True, specialCharWords=specialCharWords, degrade=True)
	#parseSentenceFile("2c.txt", modelfolder="mate/platinum.2016-11-14_02:05/models/", lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="7G", removePunct=True, specialCharWords=specialCharWords, degrade=True)
	
	#degradeConllfile("/home/kim/Dropbox/programmation/arborator/trunk/projects/SyntaxeAlOuest/export/2a.conll")
	#degradeConllfile("/home/kim/Dropbox/programmation/arborator/trunk/projects/SyntaxeAlOuest/export/2b.conll")
	#degradeConllfile("/home/kim/Dropbox/programmation/arborator/trunk/projects/SyntaxeAlOuest/export/2c.conll")
	#degradeConllfile("/home/kim/Dropbox/programmation/arborator/trunk/projects/SyntaxeAlOuest/export/1a.conll")
	#degradeConllfile("/home/kim/Dropbox/programmation/arborator/trunk/projects/SyntaxeAlOuest/export/1b.conll")
	#degradeConllfile("/home/kim/Dropbox/programmation/arborator/trunk/projects/SyntaxeAlOuest/export/1c.conll")
	#degradeConllfile("2b.txt.conll_parse")
	#degradeConllfile("2c.txt.conll_parse")
	
	
	#parseSentenceFile("exo1sents.a")
	#parseSentenceFile("exo1sents.b")
	#parseSentenceFile("exo1sents.c")
	#parseSentenceFile("cantonese_sample_for_dict.txt", modelfolder="mate/hkud.2016-11-02_04:04/models/", lemodel="", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="4G", removePunct=False, useTokDic=False)
	#parseSentenceFile("cantonese_sample_for_dict.txt", modelfolder="mate/hkud.2016-11-02_11:03/models/", lemodel="", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="4G", removePunct=False, useTokDic=False)
	#parseSentenceFile("cantonese_sample_for_dict.txt.fakeMand", modelfolder="mate/hkud.2016-11-02_11:03/models/", lemodel="", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="4G", removePunct=False, useTokDic=False)
	for sentfile in glob.glob("sentences/*"):
		if ".problem." in sentfile: continue
	
		#print 
		#qsdf
	
		#parseSentenceFile(sentfile, modelfolder="mate/platinum.2016-11-29_00:22/models/", lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", outfolder="parses", memory="47G", removePunct=True, specialCharWords=specialCharWords, degrade=False)
		memory = str(psutil.virtual_memory().total/(1000000000)-1)+"G"
		parseSentenceFile(os.path.abspath(sentfile), 
			modelfolder=os.path.abspath(os.path.join(os.path.dirname(__file__),"mate/platinum.2016-11-29_00:22/models"))+"/", 
			lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", 
			outfolder=os.path.abspath(os.path.join(os.path.dirname(__file__),"parses")), memory=memory, removePunct=True, specialCharWords=specialCharWords, degrade=False)
		

	#parseSentenceFile("../corpus/upload.fr.txt", modelfolder="mate/platinum.2017-03-26_03:10/models/", lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", outfolder="parses", memory="47G", removePunct=False, specialCharWords=specialCharWords, degrade=False)
	
	#textToParseAll("/home/kim/gromoteur/export-bitcoin/www.numerama.com---tech---306898-une-etude-remet-en-cause-la-suprematie-du-bitcoin-sur-les-autres-crypto-monnaies.html.txt")
	
	
	print "it took",time.time()-ti,"seconds"

#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2015 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

from xml.dom import minidom
import argparse
import difflib, codecs, glob, os, re, glob
sys.path.insert(0, '../lib')
import conll
from nltk import Tree


verbose=False

def brandNewXml():
	############# basics
	doc=minidom.Document()
	sampleEl=doc.createElement("sample")
	sampleEl.setAttribute("style","Rhapsodie")
	doc.appendChild(sampleEl)
	
	return doc
	
	
def addinfototree(tree,deletePara=True):
	rootindeces=[]
		
		
	for i,node in tree.items(): # getting some basic additional features: children and govcats
		tree[i]["children"]=tree[i].get("children",[])
		tree[i]["govcats"]={}
		
		if "junc" in node["gov"].values() and len(node["gov"].values())>1:
			for j,func in node["gov"].items():
				#if deletePara and func.startswith("para"):
					#del node["gov"][j]
				if func!="junc":
					del node["gov"][j]
		
		for j,func in node["gov"].items():
			#print func,func.endswith("_inherited")
			if func.endswith("_inherited"):
				# if the node has other govs than the inherited, ignore the inherited link
				if [dep for g,dep in node["gov"].iteritems() if not dep.endswith("_inherited")]:
					continue
			if func=="attention":
				# if the node has other govs than the attention, ignore the attention link
				if [dep for g,dep in node["gov"].iteritems() if dep!="attention"]:
					continue
			if j in tree: # in tree: has governor
				tree[j]["children"]=tree[j].get("children",[])+[i]
				tree[i]["govcats"][tree[j]["cat"]]=tree[i]["govcats"].get(tree[j]["cat"],[])+[ (j,func) ]
			else: # not in tree: roots
				if [dep for g,dep in node["gov"].iteritems() if not dep.endswith("_inherited") and g!=j and dep!="attention"]:
					continue # we have other governors than the root node and inherited govs
				rootindeces+=[i]
	return rootindeces

def makeConstNode(i,node,constDic, rootindeces,tree):
	if node["cat"] in constDic:
		
		if node["cat"]=="V":
			if node.get("mode","")=="infinitive":
				ntree=Tree("(VP (V "+node["t"]+"))")
			elif "V" in node["govcats"]:
				j,func=node["govcats"]["V"][0]
				if func.startswith("para"): # copy governor's const
					gov=tree[j]
					if "const" not in gov:
						makeConstNode(j,gov,constDic, rootindeces,tree)	
					if unicode(gov["const"])[1]=="V": # governor's const is VP
						ntree=Tree("(VP (V "+node["t"]+"))")
					else:	# governor's const is S
						ntree=Tree("(S (V "+node["t"]+"))")
				else: # verb depends on verb
					ntree=Tree("(VP (V "+node["t"]+"))")			
			else:
				ntree=Tree("(S (V "+node["t"]+"))")
		else:
			ntree=constDic[node["cat"]].copy(True)
			ntree.insert(0,Tree("("+node["cat"]+" "+node["t"]+")"))
	else:
		if node["children"] or i in rootindeces or node["cat"] in node["govcats"]:
			ntree=Tree("("+node["cat"]+"P ("+node["cat"]+" "+node["t"]+"))")
		else:
			ntree=Tree("("+node["cat"]+" "+node["t"]+")")
			
	ntree.depindex=i
	ntree.function=unicode(node["gov"].items()[0][1])
	#ntree.t=node["t"]
	
	node["const"]=ntree
	for t in list(ntree.subtrees(lambda t: t.height() == 2)):
		t.depindex=i
		#t.lexid=node["lexid"]
		t.lexid=node["id"]
		t.t=node["t"]	
	

def makePhraseStructure(trees):
	constDic={"V":Tree("(S)"),"N":Tree("(NP)"),"I":Tree("(IP)"),"Adj":Tree("(AP)"),"Pre":Tree("(PP)"),"CS":Tree("(CP)") ,"Pro":Tree("(NP)")   }
	#left in automatic mode: D "Adv":Tree("(AdvP)"), "Cl":Tree("(NP)"),
	#sentences, fs = rhapsodie2Sentences(xmlfile)
	doc=brandNewXml()
	phraseStrus=doc.createElement("constrees")
	phraseStrus.setAttribute("type","phraseStructure")
	sample=doc.getElementsByTagName("sample")[0]
	sample.appendChild(phraseStrus)
	
	
	phrasestructurecounter=0
	#print sentences[0]
	
	allConstTrees=[]
	#print len(sentences)		
	for treeindex,tree in enumerate(trees): # [:33] [19:20]
		
		if verbose:
			print "_____________",treeindex
			print tree

		rootindeces=addinfototree(tree)
		
		for i,node in tree.items(): # creating the actual phrase structure
			makeConstNode(i,node,constDic,rootindeces,tree)
			
		alreadyconst=[]
		for i,node in tree.items(): # putting the const trees for each node together
			children=sorted(node["children"])

			for j,chi in enumerate(children):
				
				if chi in alreadyconst:
					continue
				
				if chi<i:
					node["const"].insert(j,tree[chi]["const"])
					alreadyconst+=[chi]
				elif chi>i: # necessary to kick out autochildren
					node["const"].insert(len(node["const"]),tree[chi]["const"])
					alreadyconst+=[chi]
				
		nodesshown=[]	
		for i in rootindeces: # output
			if i in nodesshown:continue
			ctree=tree[i]["const"]
			allConstTrees+=[ctree]
			
			nodesshown += [t.depindex for t in list(ctree.subtrees(lambda t: t.height() == 2))]
			
			phraseStru=doc.createElement("constree")
			phraseStru.setAttribute("id","phrasestruct"+str(phrasestructurecounter))
			phraseStru.appendChild(doc.createComment(" ".join(list(ctree.leaves()))))
			phraseStrus.appendChild(phraseStru)
			
			root=doc.createElement("const")
			root.setAttribute("ctype","phrase")
			root.setAttribute("cat",unicode(ctree.node))			
			root.setAttribute("func","root")
			phraseStru.appendChild(root)
			
			for child in ctree:
				traverse(child,doc,root)
			
			phrasestructurecounter+=1

	return allConstTrees, doc		
			
def traverse(t,doc, lastconst, plexid=None):
	
	try:
		t.node
	except AttributeError:
		#print unicode(t)
		pass
		#const=doc.createElement("const")
		#const.setAttribute("ctype","lexeme")
		##const.setAttribute("cat","lexeme")
		##const.setAttribute("shape","empty")
		##const.setAttribute("localreg","empty")
		##const.setAttribute("function","?")
		#const.setAttribute("func","head")
		##const.setAttribute("idref",str(plexid))
		##const.setAttribute("token",str(plexid))
		#lastconst.appendChild(const)
	else:
		# Now we know that t.node is defined
		#print '(', unicode(t.node),t.function
		const=doc.createElement("const")
		const.setAttribute("ctype","phrase")
		const.setAttribute("cat",unicode(t.node))
		try:const.setAttribute("t",unicode(t.t))
		except:pass
		#const.setAttribute("shape","empty")
		#const.setAttribute("localreg","empty")
		
		try: const.setAttribute("func",t.function)
		except: const.setAttribute("func","head")		
		
		lastconst.appendChild(const)
		try: 		plexid=t.lexid
		except: 	plexid=None
		if verbose : print "lexid",unicode(t),plexid
		for child in t:
			traverse(child,doc,const,plexid)




def pasteBracketing(trees,ctrees,conllinfilename, out):

	reword=re.compile(" ([^\(\)]+)(?=\))",re.U+re.M+re.DOTALL)
	reonlylett=re.compile("[\w\'\-\&\~]+",re.U+re.M+re.DOTALL)
	respace=re.compile("\s+",re.U+re.M+re.DOTALL)
	rebrack=re.compile("\s*(\)+)\s*",re.U+re.M+re.DOTALL)
	
		
	#################### making the phrase structure parts
	sentences=[]	
	for ctree in ctrees: 
		c = respace.sub(" ",ctree.pprint())
		#.replace("\n"," ")
		#out.write(c+"\n\n")
		
		#for match in reword.finditer(ctree.pprint()):
			#print match.group(1)
			
		sentence=[]
		starter=""
		for seg in reword.split(c): 
			#print "___",seg
			if reonlylett.match(seg):
				#print seg, "onlylett"
				sentence+=[ [starter,seg,""] ]
			else:
				if rebrack.search(seg):
					#print "sentence[-1]",sentence[-1]
					sentence[-1][-1]+=rebrack.search(seg).group(1)
					seg=rebrack.sub("",seg)
				starter=seg
			#print sentence
		sentences+=[sentence]
		#print
	#print sentences
	
	#################### tucking the phrase structure parts to the token as the "ba" (before after) featuer
	for tree in  trees:
		#print tree
		for i in sorted(tree): # for each word
			t = unicode(tree[i]["t"]).strip()
			#print "\n________",i,t
			ii=0
			starti=0
			while True:
				if ii>=10:
					#len(sentences):
					#print "error!!!!!!!!!!!!!!!",[t]
					#for s in sentences[:10]:
						#print s
					starti+=1
					ii=0
					#print "starti",starti
					if starti>=99:
						1/0
				#print "ooo",sentences[ii][0][1]
				if starti<len(sentences[ii]) and sentences[ii][starti][1]==t:
					#print "found it",ii,sentences[ii][starti]
					tree[i]["ba"]=sentences[ii].pop(starti)
					if sentences[ii]==[]:
						#print "popped"
						sentences.pop(ii)
					#print tree[i]
					break
				ii+=1
			
			
			#print tree[i]
			#print tree[i]["const"].pprint()
			#print "\n"
		#print "\n\n\n"

	#################### add to file
	
	#lines=[]
	treenr=0
	lastemptylinenr=-1
	after=""
	with codecs.open(conllinfilename,"r","utf-8") as f:
		lines=[f.readline()[:-1]+"\tPhrase Struc"]
		#print lines
		for li in f:
			li=li[:-1]
			print li
			
			mili=li.strip()
			if len(mili)>5:
				parts = mili.split("\t")
				if len(parts) in [3,5]:#,5
					if len(parts) ==3: lastemptylinenr=len(lines)
				else: # normal line
					tokid=int(parts[2])
					print "tokid",tokid,trees[treenr-1][tokid]["t"]
					print "treenr",treenr
					print trees[treenr-1]
					ba = trees[treenr-1][tokid]["ba"]
					#print "ba",ba
					if ba[1].strip() != trees[treenr-1][tokid]["t"].strip():
						1/0
					#print "adding b to",lines[lastemptylinenr]
					if len(lines[lastemptylinenr].split("\t"))==15:
						lines[lastemptylinenr]+="\t"
					lines[lastemptylinenr]+=ba[0]
					after = ba[2]
					
			else: 
				print "___________________________newtree",lastemptylinenr,len(lines)
				if lastemptylinenr<len(lines)-1:
					treenr+=1
					li+="\t"+after+" "
				lastemptylinenr=len(lines)
				#print "adding",after," to this line"
				#print len(li.split("\t"))
				
			lines+=[li]
				
	#with codecs.open(out,"w","utf-8") as g:
	for li in lines:
		if li and li[-1]==" ":li=li[:-1]
		out.write(li+"\n")

	





startlatex="""\documentclass[pdftex,8pt,a3paper,landscape]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{fullpage}
\usepackage{qtree}
\pagestyle{empty}
\\begin{document}
"""
endlatex="""\\end{document}
\\end"""

def conll2phrasestructure(conllinfilename, phrasestructureoutname, args):
	beginning=""
	rhaps=False
	with codecs.open(conllinfilename,"r","utf-8") as f: beginning = f.read(50)
	if beginning.startswith("Text ID	Tree ID	Token ID"): rhaps=True
	elif args.orfeo: rhaps="orfeo"
	trees=conll.conllFile2trees(conllinfilename, {"tag":"cat"}, rhaps=rhaps)
	#print len(trees),trees[0]
	ctrees,xmldoc=makePhraseStructure(trees)
	out=codecs.open(phrasestructureoutname,"w","utf-8")
	if args.bracketing:
		for ctree in ctrees: out.write(ctree.pprint()+"\n\n")
	elif args.pasteBracketing:
		pasteBracketing(trees,ctrees,conllinfilename, out)
	elif args.latex:
		out.write(startlatex)
		for ctree in ctrees: out.write(unicode(ctree.pprint_latex_qtree()).replace("#","\\#")+"\n\n")
		out.write(endlatex)
		try:
			import subprocess, webbrowser
			proc=subprocess.Popen(['pdflatex',phrasestructureoutname])
			proc.communicate()
			webbrowser.open_new_tab(os.path.abspath( ".".join(phrasestructureoutname.split(".")[:-1])+".pdf"))
		except:
			print "is pdflatex and qtree installed? try 'sudo apt-get install texlive-humanities'"
		
		
	else:
		out.write(xmldoc.toprettyxml())
	out.close()
	if args.graphs:
		for ctree in ctrees:
			ctree.draw()
	
	

	
if __name__ == "__main__":
	
	parser = argparse.ArgumentParser(description='make phrase structure file.')
	parser.add_argument('infile', type=str, nargs='*',  help='the CoNLL file with 10 columns')
	parser.add_argument('outfile', type=str,  help='The suffix of the phrase structure')
	parser.add_argument('--verbose', '-v', action='count')
	
	#parser.add_argument('--xml', '-x', action='store_true', help='Save as Rhapsodie XML, containing the features (default)')
	parser.add_argument('--bracketing', '-b', action='store_true', help='Save as simple bracketing')
	parser.add_argument('--pasteBracketing', '-p', action='store_true', help='Paste the bracketing at the next column of a Rhapsodie CoNLL file')
	parser.add_argument('--latex', '-l', action='store_true', help='A latex qtree representation of these trees. example usage: python phrasestruc.py -ol Pedi.orfeo .tex')
	parser.add_argument('--orfeo', '-o', action='store_true', help='Use the Orfeo Conll 10+3 format. example usage: python phrasestruc.py -bo *.orfeo .phs')
	
	
	parser.add_argument('--graphs', '-g', action='store_true', help='Show all created phrase structure trees graphically')
	
	args=parser.parse_args()
	verbose=args.verbose
	
	for infile in args.infile:
		conll2phrasestructure(infile, infile+args.outfile, args)		
				
				
#! usr/bin/python
#! coding: utf-8

import datetime, codecs, time, sys, os, glob, copy

sys.path.insert(0, '../lib')
import conll
from searchConll import  hasEmptyNode, searches

debug=3
debug=False


#def NNAAMMEE(tree):
	#"""
	#NNAAMMEE -> XXX
	#"""
	#for i, node in tree.iteritems():
		#if node["t"]=="XXX":
			#node["t"]="YYY"
			#node["lemma"]="YYY"
		#elif node["t"]=="NNAAMMEE":
			#node["t"]="XXX"
			#node["lemma"]="XXX"
	#return tree

#def cela(tree):
	#"""
	#Ã§a: lemme = Ã§a
	#"""
	#for i, node in tree.iteritems():
		#if node["t"]==u"Ã§a":
			#node["lemma"]=u"Ã§a"
	#return tree

#def tapostrophe(tree):
	#"""
	#t' CLS Ã  changer en CLI + lemme = te
	#"""
	#for i, node in tree.iteritems():
		#if node["t"]==u"t'":
			#node["lemma"]="te"
			#node["tag"]="CLI"
	#return tree



#def sujEnSubj(tree):
	#"""
	#suj -> subj
	#"""
	#for i, node in tree.iteritems():
		#for gi,gf in node["gov"].iteritems():
			#if gf=="suj": tree[i]["gov"][gi]="subj"
	#addinfototree(tree)
	#return tree


#def removeTag2(tree):
	#for i, node in tree.iteritems():
		#del node["tag2"]
	

#def emptyErrors(tree):
	#for i, node in tree.iteritems():
		#if node["t"]=="":
			#if not node["gov"]: 
				#pass
				##pprint(tree)
				##print i,node
				##print "_______________"
				
			#else:
				#gi,gf = node["gov"].items()[0]
				#for ci,cf in node["children"].iteritems():#normalement un seul enfant
					#cnode=tree[ci]
					#cnode["gov"]={gi:gf}
			#tree=splice(tree,i,{})
			#addinfototree(tree)
			#tree=emptyErrors(tree)
			#break
			##print i, "removed empty node in"
			##pprint(tree)
	#return tree
	
#def kesk(tree):
	#"""
	#correct segmentation horrors
	#"""
	#for i, node in tree.iteritems():
		#if node["lemma"]=="est-ce":
			#gi,gf = node["gov"].items()[0]
			#if gi in tree and tree[gi]["lemma"]=="que": # qu'est-ce que
				#govnode=tree[gi]
							
				#gov={}
				#for ii in [i-1,i,i+1]: # for each node of qu'est-ce que
					#ggnode=tree[ii]
					#for ggi,ggf in ggnode["gov"].iteritems(): # collect each exterior governor
						#if ggi<i-1 or i+1<ggi:
							#gov[ggi]=ggf
					#for ci,cf in ggnode["children"].iteritems():# change each exterior kid
						#if ci<i-1 or i+1<ci:
							#cnode=tree[ci]
							#cnode["gov"]={gi:cf}
				#if i+2 in tree and tree[i+2]["lemma"][0] in "aeiouy" and tree[i+2]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					#t=u"qu'est-ce qu'"
					##pprint(tree)
					##print i
					##if  tree[i+2]["lemma"] not in ["on","un"] : azer
				#else:
					#t=u"qu'est-ce que"
				#tree[gi]={'lemma': u"qu'est-ce que", 'tag': u'PRQ', 't':t , 'gov': gov, 'id': gi}
				#tree=splice(tree,i,{})
				#tree=splice(tree,i,{})
				#addinfototree(tree)
				#tree=kesk(tree)
				#break
				##print "***"
				##pprint(tree)
			#else: 
				#gov={}
				#for ii in [i,i+1]: # for each node of est-ce que
					#ggnode=tree[ii]
					#for ggi,ggf in ggnode["gov"].iteritems(): # collect each exterior governor
						#if ggi<i or i+1<ggi:
							#gov[ggi]=ggf
					#for ci,cf in ggnode["children"].iteritems():# change each exterior kid
						#if ci<i or i+1<ci:
							#cnode=tree[ci]
							#cnode["gov"]={i:cf}
				#if i+2 in tree and tree[i+2]["lemma"][0] in "aeiouy" and tree[i+2]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					#t=u"est-ce qu'"
					##pprint(tree)
					##print i
					##if  tree[i+2]["lemma"] not in ["on","un"] : qsdf
				#else:
					#t=u"est-ce que"
				#tree[i]={'lemma': u"est-ce que", 'tag': u'CSU', 't':t , 'gov': gov, 'id': gi}
				#tree=splice(tree,i+1,{})
				#addinfototree(tree)
				#tree=kesk(tree)
				#break
			
		#elif node["lemma"]=="qu'est-ce" and i+3 in tree:
			
			#nnode = tree[i+1]
			#nnnode = tree[i+2]
			#if nnode["lemma"]=="ce" and  nnnode["lemma"]=="que":
				
				#if tree[i+3]["lemma"][0] in "aeiouy" and tree[i+3]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					#t=u"qu'est-ce qu'"
				#else:
					#t=u"qu'est-ce que"
				#node["t"]=t
				#node["lemma"]=u"qu'est-ce que"
				#node["tag"]=u"PRQ"
				#tree=splice(tree,i+1,{})
				#tree=splice(tree,i+1,{})
				#addinfototree(tree)
				#tree=kesk(tree)
				#break
			
		#elif node["lemma"]=="est-ce que" and i-1 in tree:
			#qnode = tree[i-1]
			#if qnode["lemma"]=="que":
				#qnode["lemma"]="qu'est-ce que"
				#if qnode["t"] in ["est-ce que", "est-ce qu'"]:
					#qnode["t"]="qu'"+qnode["t"] # pour garder l'info qu' ou que
				#elif i+1 in tree and tree[i+1]["lemma"][0] in "aeiouy" and tree[i+1]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					#qnode["t"]="qu'est-ce qu'"
				#else:
					#qnode["t"]="qu'est-ce que"
				#tree=splice(tree,i,{})
				#addinfototree(tree)
				##pprint(tree)
				
				#tree=kesk(tree)
				#break
			
	#return tree


#def rendezvous(tree):
	##z=False
	#for i, node in tree.iteritems():
		#if node["lemma"]=="rendez-vous" and i+1 in tree and tree[i+1]["t"]=="-vous":
			#node["t"]="rendez-vous"
			#tree=splice(tree,i+1,{})
			#addinfototree(tree)
			##pprint(tree)
			##print "******"
			#tree=rendezvous(tree)
			##z=True
			#break
	##if z:
		##print "mmmmm"
		##pprint(tree)
		##print "nnnnnnnnnnnnn"
	#return tree

#def pasgrandchose(tree):
	#"""
	#X -rrr-> pas grand-chose PRO
	#en
	#X -ad-> pas ADN lemme: pas
	#X -rrr-> pas grand-chose PRO lemme:grand-chose
	
	#et si on avait
	#dit souvent pas grand-chose
	#??
	#"""
	#for i, node in tree.iteritems():
		#if node["lemma"]=="pas grand-chose" and node["gov"] and node["gov"].keys()[0] in tree:
			#gi = node["gov"].keys()[0]
			#gnode=tree[gi]
			##pprint(tree)
			##print
			#node["lemma"]="grand-chose"
			#node["t"]="grand-chose"
			#i1= gnode.copy()
			#i1.update({'child':None})
			#i3=node.copy()
			#i3.update({'gov': {'i1': node["gov"].values()[0]}})
			#chis=i3["children"]
			#tree=splice(tree,i,{})
			#tree=splice(tree,gi,
			#{
				#'i1':i1,
				#'i2':{u'tag': u'ADN', u'lemma': u'pas', u't': u'pas', 'gov': {'i1': u'ad'}},
				#'i3':i3
			#})
			#for chi in chis:
				#tree[chi]["gov"]={gi+2:tree[chi]["gov"].values()[0]}
			#addinfototree(tree)
			##pprint(tree)
			##print "******"
			#tree=pasgrandchose(tree)
			#break
	
	#return tree




#def cestadire(tree):
	#"""
	#c' + est-Ã -dire : un seul token COO
	#"""
	#for i, node in tree.iteritems():
		#if node["t"]=="c'" and i+1 in tree and (tree[i+1]["t"]==u"est-Ã -dire" or tree[i+1]["t"]==u"est Ã  dire"):
			#for ci,cf in tree[i+1]["children"].iteritems():
				#tree[ci]["gov"]={i:cf}
			#node["t"]=u"c'est-Ã -dire"
			#node["lemma"]=u"c'est-Ã -dire"
			#node["tag"]=u"COO"
			#tree=splice(tree,i+1,{})
			#addinfototree(tree)
			#tree=cestadire(tree)
			#break
		#if node["t"]==u"est-Ã -dire":
			#node["tag"]="COO"
	#return tree

		
#def prons(tree):
	#"""
	#je|tu|il|ils|on -> CLS
	#elle|elles|nous|vous ce qui sont suj -> CLS
	#si "ce" a un dep VRB alors ce -> PRO
	#si un cls est dep d'une prÃ©position -> PRO
	#tous les CLI dep autre que "y" doivent Ãªtre comp
	#"""
	#for i, node in tree.iteritems():
		#if node["lemma"] in "je tu il ils on".split():
			#node["tag"]="CLS"
		#if node["lemma"] in "elle elles nous vous ce".split() and "subj" in node["gov"].values():
			#node["tag"]="CLS"
		#if node["lemma"] == "ce" and "VRB" in [tree[ci]["tag"] for ci in node["children"]]:
			#node["tag"]="PRO"
		#if node["tag"] == "CLS" and "dep" in node["gov"].values() and "PRE" in node["govtags"]:
			#node["tag"]="PRO"
			#addinfototree(tree)
		#if node["tag"] == "CLI" and "dep" in node["gov"].values() and node["t"]!="y":
			#node["gov"]={node["gov"].keys()[0]:"comp"}
			#addinfototree(tree)
	#return tree	

#def disflink(tree):

	#"""
	#si une PRE est disflink et n'a pas de dÃ©pendant,
	#disflink -> ad si le gouv est un VRB|VNF|VPP
	#disflink -> dep sinon (sauf si le gouv est dm ou mark)
	#on peut Ã©ventuellement faire la mÃªme rÃ¨gle pour les DET
	
	#tout disflink entre entre prep et nom est en fait une dÃ©p
	#"""
	#for i, node in tree.iteritems():
		#if node["tag"]in ["PRE","DET"] and "disflink" in node["gov"].values():
			#if "VRB" in node["govtags"] or "VNF" in node["govtags"] or "VPP" in node["govtags"]:
				#node["gov"]={node["gov"].keys()[0]:"ad"}
			#else:
				#node["gov"]={node["gov"].keys()[0]:"dep"}
		#elif node["tag"]in ["NOM"] and "disflink" in node["gov"].values() and "PRE" in node["govtags"]:
			#node["gov"]={node["gov"].keys()[0]:"dep"}
	#return tree

#def vrb(tree):
	#"""
	#dit|dites|fait|faites|Ã©crit avec un suj -> VRB
	#dites|faites qui est root -> VRB
	#"""
	#for i, node in tree.iteritems():
		#if node["t"] in u"dit dites fait faites Ã©crit".split() and "subj" in node["children"].values():
			#node["tag"]="VRB"
		#if node["t"] in "dites faites".split() and 0 in node["gov"]:
			#node["tag"]="VRB"
		
	#return tree



#def desEnTete(tree):
	#"""
	#des est toujours tÃªte comme une prÃ©position
	#"""
	#for i, node in tree.iteritems():
		#if node["tag"] == "DET" and node["t"] in ["des", "du", "de", "d'"]:
			#if node["gov"]:
				#govid = node["gov"].keys()[0]
				#if govid in tree:
					#govnode=tree[govid]
					#if govnode["gov"]:
						#ggovid = govnode["gov"].keys()[0]
						#node["gov"]={ggovid:govnode.get("gov",{}).get(ggovid,0)}
						#govnode["gov"]={i:"dep"}
		#node["tag"] == "PRE"			
	#return tree


#V disfl-> des Ã  -->  comp
#V disfl->  "du", "de", "d'" --> comp
#V disfl-> 

def reverseauxEnTete(tree):
	"""
	aux est la tÃªte
	"""
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["aux"] and node["gov"].keys()[0]<i: # found a pp
			#print "found a node that is dependent on aux",i,node
			gi = node["gov"].keys()[0]
			gnode=tree[gi]
			#print "gnode",gi,gnode
				
			if gnode["gov"] and gnode["gov"].values()[0] in ["aux"]:# and gnode["gov"].keys()[0]<gi: # found a double aux
				ggi = gnode["gov"].keys()[0]
				if ggi:
					ggnode=tree[ggi]
					for ci,cf in ggnode["children"].iteritems():
						cnode=tree[ci]	
						cnode["gov"]={i:cf}
						
					node["gov"]=ggnode["gov"].copy() 
					ggnode["gov"]={gi:"aux"}
				gnode["gov"]={i:"aux"}
				#pprint(tree)
				#dsqsdsdf
			else: # only simple aux
				if "aux" not in node["children"].values():
					for ci,cf in gnode["children"].iteritems(): # look at all the siblings of the aux
						cnode=tree[ci]
						cnode["gov"]={i:cf}
					node["gov"]=gnode["gov"].copy()
					gnode["gov"]={i:"aux"}
					#print "transformed",node
				
				
			addinfototree(tree)
	return  tree		
			#pprint(tree)
			

def reverseauxpara(tree):
	"""
	#avant:si X-para-> Y et X-aux-> Z, alors Y-aux-> Z
	#si X-para-> Y et Y-aux-> Z, alors X-aux-> Z
	"""
	for i, node in tree.iteritems():
		if "para" in node["gov"].values() and "aux" in node["children"].values():
			#print "found a node X-para-> Y et Y-aux-> Z. Y:",i,node
			xparai=[ci for ci in node["gov"] if node["gov"][ci]=="para"][0]
			zauxi =[ci for ci in node["children"] if node["children"][ci]=="aux" ][0]
			tree[zauxi]["gov"]={i:"aux"}
			addinfototree(tree)
			#pprint(tree)
			#qsdf
	return  tree	


def reversecollapsecomps(tree):
	"""
	
	"""
	for i, node in tree.iteritems():
		for gi,f in node["gov"].iteritems():
			if f in "comp aff ad".split():
				node["gov"]={gi:"dep"}
			
			
	return  tree	
	
#def adperiph(tree):
	#"""
	#1) si X <-ad|comp- Y et il y a un Z suj tel que X < Z < Y, alors X <-periph- Y
	#"""
	#for i, node in tree.iteritems():
		#if ("ad" in node["children"].values() or "comp" in node["children"].values()) and "suj" in node["children"].values():
			#zsubji =[ci for ci in node["children"] if node["children"][ci]=="suj" ][0]
			#for xadci in [ci for ci in node["children"] if node["children"][ci] in ["ad","comp"]]:
				#if xadci<zsubji<i:
					#tree[xadci]["gov"]={i:"periph"}
					#addinfototree(tree)
	#return  tree	

#def montperiph(tree):
	#"""
	#2) (Ã  appliquer ensuite) si X <-periph- Y et W-aux-> Y, alors  X <-periph- Y	
	#"""
	#for i, node in tree.iteritems():
		#if node["gov"] and node["gov"].values()[0] in ["periph"]:
			#ygi = node["gov"].keys()[0]
			#ygnode=tree[ygi]
			#if ygnode["gov"] and ygnode["gov"].values()[0] in ["aux"]:
				#wggi=ygnode["gov"].keys()[0]
				#node["gov"]={wggi:"periph"}
				#addinfototree(tree)
	#return  tree

def rootperiph(tree):
	"""
	beaucoup de periph qui se balladent
	si un periph a 0 comme gouv, lui attribuer le nÅud root comme gouv

	"""
	for i, node in tree.iteritems():
		if node["gov"] and 0 in node["gov"] and  node["gov"][0] == "periph":
			for j, rnode in tree.iteritems():
				if rnode["gov"] and 0 in rnode["gov"] and  rnode["gov"][0] == "root":
					node["gov"]={j:"periph"}
					break 
				
	return  tree	

def csuperiph(tree):
	"""
	autre pb avec les periph : si X est un periph qui dÃ©pend d'un CSU, X doit dÃ©pendre du VRB dÃ©pendant de la CSU
	ct faux !
	meilleure version
	si Y est CSU et Y-periph-> X et Y n'est pas root, alors X <-periph- Z <-dep - Y
	"""
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["periph"] and "CSU" in node["govtags"]:
			csui,f=node["govtags"]["CSU"][0]
			csunode=tree[csui]
			if "root" not in csunode["gov"].values():
				cis=[ci for ci in csunode["children"] if tree[ci]["tag"][0]=="V"]
				if cis:
					vi=cis[0]
					node["gov"]={vi:"periph"}
					addinfototree(tree)
					#pprint( tree)
				
				
	return  tree	

		
		
	
		
		

def pprint(tree):
	for i in tree:
		print i, tree[i]["t"],tree[i]
	print
	
def corrigerNumerotation(arbre):
	indexcorrection = {0:0} # ancien indice --> nouvel indice
	problem = False
	for compteur, ind in enumerate(sorted(arbre.keys())):
		indexcorrection[ind]=compteur+1
		if compteur+1 != ind:
			problem = True
	if problem:
		print indexcorrection
		pprint(arbre)
		arbrecorrige = {}
		for i, node in arbre.iteritems():
			node["id"]=indexcorrection[i]
			newgov={}
			for gi,f in node["gov"].iteritems():
				newgov[indexcorrection[gi]]=f
			node["gov"]=newgov
			arbrecorrige[indexcorrection[i]]=node
		return arbrecorrige
	else:
		return arbre

def splice(sentencedic,nodenum,intdic,copiedfeatures=['t','token','lemma','cat','tag','beg','end']):
		"""
		integrates the intdic into sentencedic at position nodenum
		an empty intdic kicks out the node in a proper manner (other dependencies are still ok)

		format of intdic:
			indices start with 'i', for example 'i2'
			last node that carries 'child' feature will receive the children of the former nodenum node
			last node that has governor i0 will receive the governors of the former nodenum node
			if no node has a 'i0' governor, the first node will receive the governors
			WATCH OUT: the ix will be ordered. so i10 will be before i9!!!
			example pour est-ce, remplacÃ© par ce sous-arbre :
			{
				'i1':{ u'person': u'3', u'number': u'sg', u'cat': u'V', u'lemma': u'\xeatre', u'token': u'est', u'tense': u'present', u'mode': u'indicative', 'gov': {'i0':'root'}, u't': u'A', 'child':None},
				'i2':{u'cat': u'Cl', u'lemma': u'c', u'token': u'-ce', u't': u'B', 'gov': {'i1': u'sub'}}
			}

		gives back new sentencedic

		"""
		intgov,intchi=None,None
		if debug>2: 
			print "================== integrating"
			for i in sorted(intdic):
				print i,intdic[i]
			print "into ((((((((((((("
			for i in sorted(sentencedic):
				print i,sentencedic[i].get("t","-"),sentencedic[i]
			print "))))))))))))))) at ",nodenum
		#try:
		l = [0]+sorted(sentencedic)[:nodenum-1]+sorted(intdic)+sorted(sentencedic)[nodenum:]
		if debug>2: print "lllll",l


		for i in sorted(intdic):

			sentencedic[i]=sentencedic[nodenum].copy() # l'ancien noeud qui va Ãªtre virÃ© et copiÃ© pour chaque ix
			for at in copiedfeatures:
				if at in intdic[i]:
					sentencedic[i][at]=intdic[i][at]
			sentencedic[i]['gov']=intdic[i].get('gov',{})

			if 'i0' in intdic[i].get('gov',{}).keys(): # found the gov: the one that has a root link
				sentencedic[i]['gov']=sentencedic[nodenum]['gov']
				intgov=i
			if "child" in intdic[i]: # found the child bearer
				intchi = i
				del intdic[i]['child']
		if not intgov:# if no governor is indicated, i guess it's the first word
			intgov="i1"
			if intgov in sentencedic: sentencedic[intgov]['gov']=sentencedic[nodenum]['gov']
		if not intchi: intchi=intgov

		# correct wrong gov links
		for i in sorted(sentencedic):
			if nodenum in sentencedic[i]['gov'].keys():
				sentencedic[i]['gov'][intchi]=sentencedic[i]['gov'][nodenum]
				del sentencedic[i]['gov'][nodenum]

		# bring all in a new list with new indeces based on l
		newsentencedic={}
		for newi,oldi in enumerate(l):
			if newi:# not 0
				newsentencedic[newi]=sentencedic[oldi]
				govdic={}
				for govi,dep in sentencedic[oldi]['gov'].iteritems():
					if govi>=0 and govi in l: govdic[l.index(govi)]=dep
				newsentencedic[newi]['gov']=govdic
				newsentencedic[newi]['id']=newi
				#print type(newsentencedic)


		if debug>2:
			print "result:((((((((((((("
			for i in sorted(newsentencedic):
				print i,newsentencedic[i].get("t",""),newsentencedic[i]
			print ")))))))))))))))"

		return newsentencedic


def addinfototree(tree, reset=True):
	"""
	add to each node:
	children: dico: childi -> func 
	govtags: dico: tag of governor --> [(govi,func), ...]
	"""
	if reset:
		for i,node in tree.items():
			tree[i]["children"]={}
			tree[i]["govtags"]={}
	
	
	rootindeces=[]

	for i,node in tree.items(): # getting some basic additional features: children and govtags
		tree[i]["children"]=tree[i].get("children",{})
		tree[i]["govtags"]={}
		
		for j,func in node["gov"].items():
			if j in tree: # in tree: has governor j
				tree[j]["children"]=tree[j].get("children",{})
				tree[j]["children"][i]=func
				tree[i]["govtags"][tree[j]["tag"]]=tree[i]["govtags"].get(tree[j]["tag"],[])+[ (j,func) ]
			else: # not in tree: roots
				rootindeces+=[i]
	tree=corrigerNumerotation(tree)
	return rootindeces

	

def fixOutname(name):
	if name.startswith("validated."): 
		name=name.split(".")[1]
		#name=name[len("validated."):]
	if name.endswith(".lastHumanTrees.conll"): name=name[:-len(".lastHumanTrees.conll")]
	elif name.endswith("lastHuman.conll"): name=name[:-len(".lastHuman.conll")]
	elif name.endswith(".conll"): name=name[:-len(".conll")]
	return name

def createNonExistingFolders(path):
	head, tail = os.path.split(path)
	if head=="":
		if tail and not os.path.exists(tail): os.makedirs(tail)
	else:
		createNonExistingFolders(head)
	if head and not os.path.exists(head): os.makedirs(head)


def platinum(tree):
	#removeTag2(tree)
	addinfototree(tree)
	##pprint (tree)
	
	#tree=NNAAMMEE(tree)
	#tree=cela(tree)
	#tree=tapostrophe(tree)
	#tree=sujEnSubj(tree)
	
	

	#tree=emptyErrors(tree)
	#if hasEmptyNode(tree): # testing whether all empty nodes have been removed
		#print tree
		#qsdf
	#tree=kesk(tree)
	#tree=rendezvous(tree)
	#tree=pasgrandchose(tree)
	tree=reverseauxpara(tree)
	tree=reverseauxEnTete(tree)
	tree=reversecollapsecomps(tree)
	#print "after aux:"
	#pprint (tree)
	#tree=desEnTete(tree)
	#tree=adperiph(tree)
	#print "before aux:"
	#pprint (tree)
	#tree=montperiph(tree)
	#print "after montperiph:"
	#pprint (tree)
	#tree=rootperiph(tree)
	#pprint (tree)
	#tree=csuperiph(tree)
	
	
	#tree=prons(tree)
	#tree=disflink(tree)
	#tree=vrb(tree)
	#tree=cestadire(tree)
	#tree=kesk(tree)
	#pprint (tree)
	
	return tree



def transform(infolder,outfolder,mixOldNew=True, filepattern="*.conll"):
	createNonExistingFolders(outfolder)
	spaceToks={}
	#for infile in sorted(glob.glob(os.path.join(infolder,"test.conll"))):
	for infile in sorted(glob.glob(os.path.join(infolder,filepattern))):
		if not os.path.isfile(infile): continue
		basename=os.path.basename(infile)
		print "reading",basename
		trees = conll.conllFile2trees(infile)
		newtrees=[]
		for tree in trees:
			if mixOldNew: newtrees+=[tree]
			newtree=copy.deepcopy(tree)
			newtree=platinum(newtree)
			newtrees+=[newtree]
			
			
		conll.trees2conllFile(newtrees,os.path.join(outfolder,fixOutname(basename)))
		#qsdf
	
	
		

#transform("../projects/Platinum/eric/","../projects/Platinum/pyrite/",mixOldNew=False, filepattern="*")		 
transform("../projects/Platinum/export/","../projects/Platinum/pyrite/",mixOldNew=False, filepattern="*")		 
#transform("export/","exportpyrite/",mixOldNew=False, filepattern="*")		 
#transform("LAST_VERSION/eric_platinum_final/","LAST_VERSION/eric_platinum_final_pyrite/",mixOldNew=False, filepattern="*")		 
#searches()
#! usr/bin/python
#! coding: utf-8

import datetime, codecs, time, sys, os, glob, copy
sys.path.insert(0, '../lib')
import conll

debug=3
debug=False


def NNAAMMEE(tree):
	"""
	NNAAMMEE -> XXX
	"""
	for i, node in tree.iteritems():
		if node["t"]=="XXX":
			node["t"]="YYY"
			node["lemma"]="YYY"
		elif node["t"]=="NNAAMMEE":
			node["t"]="XXX"
			node["lemma"]="XXX"
	return tree


def removeTag2(tree):
	for i, node in tree.iteritems():
		del node["tag2"]
	

def emptyErrors(tree):
	for i, node in tree.iteritems():
		if node["t"]=="":
			if not node["gov"]: 
				pass
				#pprint(tree)
				#print i,node
				#print "_______________"
				
			else:
				gi,gf = node["gov"].items()[0]
				for ci,cf in node["children"].iteritems():#normalement un seul enfant
					cnode=tree[ci]
					cnode["gov"]={gi:gf}
			tree=splice(tree,i,{})
			addinfototree(tree)
			tree=emptyErrors(tree)
			break
			#print i, "removed empty node in"
			#pprint(tree)
	return tree
	
def kesk(tree):
	for i, node in tree.iteritems():
		if node["lemma"]=="est-ce":
			gi,gf = node["gov"].items()[0]
			if gi in tree and tree[gi]["lemma"]=="que": # qu'est-ce que
				govnode=tree[gi]
				#print
				#print i-1,tree[i-1]["t"],govnode
				#print i,tree[i]["t"],node
				#print i+1,tree[i+1]["t"], tree[i+1]
				#print i+2,tree[i+2]["t"], tree[i+2]
				#print "_______________"
				
				gov={}
				for ii in [i-1,i,i+1]: # for each node of qu'est-ce que
					ggnode=tree[ii]
					for ggi,ggf in ggnode["gov"].iteritems(): # collect each exterior governor
						if ggi<i-1 or i+1<ggi:
							gov[ggi]=ggf
					for ci,cf in ggnode["children"].iteritems():# change each exterior kid
						if ci<i-1 or i+1<ci:
							cnode=tree[ci]
							cnode["gov"]={gi:cf}
				if i+2 in tree and tree[i+2]["lemma"][0] in "aeiouy" and tree[i+2]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					t=u"qu'est-ce qu'"
					#pprint(tree)
					#print i
					#if  tree[i+2]["lemma"] not in ["on","un"] : azer
				else:
					t=u"qu'est-ce que"
				tree[gi]={'lemma': u"qu'est-ce que", 'tag': u'CSU', 't':t , 'gov': gov, 'id': gi}
				tree=splice(tree,i,{})
				tree=splice(tree,i,{})
				addinfototree(tree)
				tree=kesk(tree)
				break
				#print "***"
				#pprint(tree)
			else: 
				#print
				##print i-1,tree[i-1]["t"],govnode
				#print i,tree[i]["t"],node
				#print i+1,tree[i+1]["t"], tree[i+1]
				#print i+2,tree[i+2]["t"], tree[i+2]
				#print "_______________"
				gov={}
				for ii in [i,i+1]: # for each node of est-ce que
					ggnode=tree[ii]
					for ggi,ggf in ggnode["gov"].iteritems(): # collect each exterior governor
						if ggi<i or i+1<ggi:
							gov[ggi]=ggf
					for ci,cf in ggnode["children"].iteritems():# change each exterior kid
						if ci<i or i+1<ci:
							cnode=tree[ci]
							cnode["gov"]={i:cf}
				if i+2 in tree and tree[i+2]["lemma"][0] in "aeiouy" and tree[i+2]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					t=u"est-ce qu'"
					#pprint(tree)
					#print i
					#if  tree[i+2]["lemma"] not in ["on","un"] : qsdf
				else:
					t=u"est-ce que"
				tree[i]={'lemma': u"est-ce que", 'tag': u'CSU', 't':t , 'gov': gov, 'id': gi}
				tree=splice(tree,i+1,{})
				addinfototree(tree)
				tree=kesk(tree)
				break
	return tree


def rendezvous(tree):
	#z=False
	for i, node in tree.iteritems():
		if node["lemma"]=="rendez-vous" and i+1 in tree and tree[i+1]["t"]=="-vous":
			node["t"]="rendez-vous"
			tree=splice(tree,i+1,{})
			addinfototree(tree)
			#pprint(tree)
			#print "******"
			tree=rendezvous(tree)
			#z=True
			break
	#if z:
		#print "mmmmm"
		#pprint(tree)
		#print "nnnnnnnnnnnnn"
	return tree
		
def cls(tree):
	for i, node in tree.iteritems():
		if node["tag"]=="CLS":
			gi,gf = node["gov"].items()[0]
			if gi in tree and tree[gi]["tag"]!="VRB" and gf not in ["para","disflink"]: # CLS whose governor is not a verb
				govnode=tree[gi]
				
				#pprint(tree)
				#print "******",i
			
			
	return tree


def desEnTete(tree):
	"""
	des est toujours tÃªte comme une prÃ©position
	"""
	for i, node in tree.iteritems():
		if node["tag"] == "DET" and node["t"] in ["des", "du", "de", "d'"]:
			if node["gov"]:
				govid = node["gov"].keys()[0]
				if govid in tree:
					govnode=tree[govid]
					if govnode["gov"]:
						ggovid = govnode["gov"].keys()[0]
						node["gov"]={ggovid:govnode.get("gov",{}).get(ggovid,0)}
						govnode["gov"]={i:"dep"}
		node["tag"] == "PRE"			
	return tree


#V disfl-> des Ã  -->  comp
#V disfl->  "du", "de", "d'" --> comp
#V disfl-> 

def auxEnTete(tree):
	"""
	aux est la tÃªte
	"""
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["aux"] and node["gov"].keys()[0]>i: # found a node that is a dependent aux
			#print "found a node that is a dependent aux",i,node
			gi = node["gov"].keys()[0]
			gnode=tree[gi]
			if gnode["gov"] and gnode["gov"].values()[0] in ["aux"] and gnode["gov"].keys()[0]>gi: # found a double aux
				#print "found a double aux",gi,node
				ggi = gnode["gov"].keys()[0]
				ggnode=tree[ggi]
				for ci,cf in ggnode["children"].iteritems():
					if cf in ["suj","periph","insert","mark"]: # toutes ces fonctions montent sur 
						cnode=tree[ci]
						cnode["gov"]={i:cf}
					elif cf=="dm" and ci<i: # dm seulement quand ils sont Ã  gauche de l'auxiliaire
						cnode=tree[ci]	
						cnode["gov"]={i:"dm"}
					elif cf=="ad":
						cnode=tree[ci]
						if cnode["t"] in ["ne","n'"]:
							cnode["gov"]={i:cf}
				node["gov"]=ggnode["gov"].copy() 
				gnode["gov"]={i:"aux"}
				ggnode["gov"]={gi:"aux"}
			else: # only simple aux
				for ci,cf in gnode["children"].iteritems():
					if cf in ["suj","periph","insert","mark"]: # toutes ces fonctions montent sur 
						cnode=tree[ci]
						cnode["gov"]={i:cf}
					elif cf=="dm" and ci<i: # dm seulement quand ils sont Ã  gauche de l'auxiliaire
						cnode=tree[ci]	
						cnode["gov"]={i:"dm"}
					elif cf=="ad":
						cnode=tree[ci]
						if cnode["t"] in ["ne","n'"]:
							cnode["gov"]={i:cf}
				node["gov"]=gnode["gov"].copy()
				gnode["gov"]={i:"aux"}
			
			addinfototree(tree)
	return  tree		
			#pprint(tree)
			
from searchConll import  hasEmptyNode
			
def platinum(tree):
	removeTag2(tree)
	addinfototree(tree)
	tree=auxEnTete(tree)
	tree=desEnTete(tree)
	tree=NNAAMMEE(tree)

	tree=emptyErrors(tree)
	if hasEmptyNode(tree):
		print tree
		qsdf
	tree=kesk(tree)
	tree=rendezvous(tree)
	tree=cls(tree)
	return tree


def pprint(tree):
	for i in tree:
		print i, tree[i]["t"],tree[i]
	print


def splice(sentencedic,nodenum,intdic,copiedfeatures=['t','token','lemma','cat','tag','beg','end']):
		"""
		integrates the intdic into sentencedic at position nodenum
		an empty intdic kicks out the node in a proper manner (other dependencies are still ok)

		format of intdic:
			indices start with 'i', for example 'i2'
			last node that carries 'child' feature will receive the children of the former nodenum node
			last node that has governor i0 will receive the governors of the former nodenum node
			if no node has a 'i0' governor, the first node will receive the governors
			WATCH OUT: the ix will be ordered. so i10 will be before i9!!!
			example pour est-ce, remplacÃ© par ce sous-arbre :
			{
				'i1':{ u'person': u'3', u'number': u'sg', u'cat': u'V', u'lemma': u'\xeatre', u'token': u'est', u'tense': u'present', u'mode': u'indicative', 'gov': {'i0':'root'}, u't': u'A', 'child':None},
				'i2':{u'cat': u'Cl', u'lemma': u'c', u'token': u'-ce', u't': u'B', 'gov': {'i1': u'sub'}}
			}

		gives back new sentencedic

		"""
		intgov,intchi=None,None
		if debug>2: 
			print "================== integrating"
			for i in sorted(intdic):
				print i,intdic[i]
			print "into ((((((((((((("
			for i in sorted(sentencedic):
				print i,sentencedic[i].get("t","-"),sentencedic[i]
			print "))))))))))))))) at ",nodenum
		#try:
		l = [0]+sorted(sentencedic)[:nodenum-1]+sorted(intdic)+sorted(sentencedic)[nodenum:]
		if debug>2: print "lllll",l


		for i in sorted(intdic):

			sentencedic[i]=sentencedic[nodenum].copy() # l'ancien noeud qui va Ãªtre virer est copier pour chaque ix
			for at in copiedfeatures:
				if at in intdic[i]:
					sentencedic[i][at]=intdic[i][at]
			sentencedic[i]['gov']=intdic[i].get('gov',{})

			if 'i0' in intdic[i].get('gov',{}).keys(): # found the gov: the one that has a root link
				sentencedic[i]['gov']=sentencedic[nodenum]['gov']
				intgov=i
			if "child" in intdic[i]: # found the child bearer
				intchi = i
				del intdic[i]['child']
		if not intgov:# if no governor is indicated, i guess it's the first word
			intgov="i1"
			if intgov in sentencedic: sentencedic[intgov]['gov']=sentencedic[nodenum]['gov']
		if not intchi: intchi=intgov

		# correct wrong gov links
		for i in sorted(sentencedic):
			if nodenum in sentencedic[i]['gov'].keys():
				sentencedic[i]['gov'][intchi]=sentencedic[i]['gov'][nodenum]
				del sentencedic[i]['gov'][nodenum]

		# bring all in a new list with new indeces based on l
		newsentencedic={}
		for newi,oldi in enumerate(l):
			if newi:# not 0
				newsentencedic[newi]=sentencedic[oldi]
				govdic={}
				for govi,dep in sentencedic[oldi]['gov'].iteritems():
					if govi>=0 and govi in l: govdic[l.index(govi)]=dep
				newsentencedic[newi]['gov']=govdic
				newsentencedic[newi]['id']=newi
				#print type(newsentencedic)


		if debug>2:
			print "result:((((((((((((("
			for i in sorted(newsentencedic):
				print i,newsentencedic[i].get("t",""),newsentencedic[i]
			print ")))))))))))))))"

		return newsentencedic


def addinfototree(tree, reset=True):
	"""
	add to each node:
	children: dico: childi -> func 
	govtags: dico: tag of governor --> [(govi,func), ...]
	"""
	if reset:
		for i,node in tree.items():
			tree[i]["children"]={}
			tree[i]["govtags"]={}
	
	
	rootindeces=[]

	for i,node in tree.items(): # getting some basic additional features: children and govtags
		tree[i]["children"]=tree[i].get("children",{})
		tree[i]["govtags"]={}
		
		for j,func in node["gov"].items():
			if j in tree: # in tree: has governor j
				tree[j]["children"]=tree[j].get("children",{})
				tree[j]["children"][i]=func
				tree[i]["govtags"][tree[j]["tag"]]=tree[i]["govtags"].get(tree[j]["tag"],[])+[ (j,func) ]
			else: # not in tree: roots
				rootindeces+=[i]
	return rootindeces

	
def correcttree2nodedic(tree,catname="cat"):
		""" 
		takes the conll string representation of a single tree and creates a dictionary for it
		here special format: spacelemmas
		"""
	
		nodedic={}
		for line in tree.split('\n'):
			line=line.strip()
			
			#print "___",line
			if line:
				cells = line.split('\t')
				nrCells = len(cells)
				if nrCells in [6,7]:
					if nrCells == 6:
						nr, t, lemma, tag, head, rel = cells
						#nr,head = int(nr), int(head)
						if int(head)>=0:nodedic['i'+nr]={'id':'i'+nr,"t": t,'lemma': lemma, catname: tag, 'gov':{'i'+head: rel} }
						else:nodedic['i'+nr]={'id':'i'+nr,"t": t,'lemma': lemma, catname: tag, 'gov':{} }
					elif nrCells == 7:
						nr, t, lemma, tag, head, rel, child = cells
						
						#nr,head = int(nr), int(head)
						#if lemma2!="_": lemma=lemma2 # TODO: find out what the difference between these colons is!!!!!!
						if int(head)>=0:nodedic['i'+nr]={'id':'i'+nr,"t": t,'lemma': lemma, catname: tag, 'gov':{'i'+head: rel} }
						else:nodedic['i'+nr]={'id':'i'+nr,"t": t,'lemma': lemma, catname: tag, 'gov':{} }
						
						
						if child.strip()=="-":
							nodedic['i'+nr]["child"]=True
						else:
							print child,cells,"--- strange line:"
							print line
				else:
					print "strange line"
					print line
					print "in:"
					print tree
			
					
		return nodedic
	
	
def correctionDics(correctionfilename):
	
	if debug: print "reading the correction file",correctionfilename
	corrf=codecs.open(correctionfilename,"r","utf-8")
			
	corrlist=[]
	#currtoken=None
	currdic={}
	currconlltree=""
	
	for line in corrf:
		line=line.split("#")[0] # comments
		#if (line.strip()+" ")[0]!="#":
		if line.strip():
			#if "#" in line: 
			if currdic: # second or further line of a block
				if line.startswith("<-"):
					corrlist+=[(currdic,-1)]
					currconlltree=""
					currdic={}
				elif line.startswith("->"):
					#gluedic[currtoken]=1
					corrlist+=[(currdic,1)]
					currconlltree=""
					currdic={}
				else:
					currconlltree+=line
			else: # first line of a block
				trucs=line.split("\t")
				if len(trucs)>3:
					_,currtoken,currlemma,currcat=trucs[:4] # [0].strip()
					currdic={}
					for (a,v) in [("t",currtoken),("lemma",currlemma),("cat",currcat)]:
						v=v.strip()
						if v: currdic[a]=v
					
				else:
					print "strange line:",line
					currconlltree=""
					currdic={}	
		else: # empty line
			if currconlltree.strip():
				corrlist+=[(currdic,correcttree2nodedic(currconlltree))]
				#corrdic[currtoken]=correcttree2nodedic(currconlltree)			
			
			currconlltree=""
			currdic={}		
	if debug:
		for matchdic,insdic in corrlist:
			print matchdic
			if isinstance(insdic, int):
				print insdic
			else:
			
				for i in sorted(insdic):
					print i,insdic[i]
			print "_______________"
	
			
	return corrlist
	


def findSpaces(spaceDic, tree, spc=" '-"):
	for i,node in tree.items():
		for s in spc:
			if s in node["t"] or s in node["lemma"]:
				spaceDic[node["lemma"]]=spaceDic.get(node["lemma"],0)+1


def fixOutname(name):
	if name.startswith("validated."): 
		name=name.split(".")[1]
		#name=name[len("validated."):]
	if name.endswith("lastHuman.conll"): name=name[:-len(".lastHuman.conll")]
	if name.endswith(".conll"): name=name[:-len(".conll")]
	return name

def createNonExistingFolders(path):
	head, tail = os.path.split(path)
	if head=="":
		if tail and not os.path.exists(tail): os.makedirs(tail)
	else:
		createNonExistingFolders(head)
	if head and not os.path.exists(head): os.makedirs(head)

def transform(infolder,outfolder,mixOldNew=True):
	createNonExistingFolders(outfolder)
	spaceToks={}
	#for infile in sorted(glob.glob(os.path.join(infolder,"test.conll"))):
	for infile in sorted(glob.glob(os.path.join(infolder,"*.conll"))):
		basename=os.path.basename(infile)
		print "reading",basename
		trees = conll.conllFile2trees(infile)
		newtrees=[]
		for tree in trees:
			if mixOldNew: newtrees+=[tree]
			newtree=copy.deepcopy(tree)
			newtree=platinum(newtree)
			newtrees+=[newtree]
			
			findSpaces(spaceToks,tree)
			
		conll.trees2conllFile(newtrees,os.path.join(outfolder,fixOutname(basename)))
	
	
	#corrdic = correctionDics("corrConll.txt")
	#for c in corrdic:
		#print c
	#qsdf
	for i,tok in enumerate(sorted(spaceToks)):
		print i,tok,spaceToks[tok]
		
		
	
transform("projects/OrfeoGold2016/export/","projects/OrfeoGold2016/platinum/",mixOldNew=False)		 
#! usr/bin/python
#! coding: utf-8

import codecs, re, copy, conll, glob, os, random, shutil
sys.path.insert(0, '../lib')
from num2words import num2words
from pprint import pprint
from mate import parsing

debug=False

droporfeo='/home/clover/Dropbox/Orfeo/' # where we can find the lexique folder
droporfeo='/home/kim/Dropbox/Orfeo/' # where we can find the lexique folder

global digitsandnumbers
#digitsandnumbers=None
#if not digitsandnumbers:
#TODO: find a way it's not executed when the module is imported
try: digitsandnumbers=codecs.open(droporfeo+"lexique/gg", "r", "utf-8").read().split('\n')
except: digitsandnumbers=[]

def lireDictionnaires(dico,forme2cat,expressions_multimots,multim):
	"""
	Lecture des fichiers du lexique
	Le dictionnaire se prÃ©sente sous la forme {forme: (type, pos, lemme)}
	"""
	#print "Lecture du dictionnaire"
	
	#l = glob.glob("/home/clover/Dropbox/Orfeo//home/clover/Dropbox/Orfeo/lexique/*sfplm")  
	#print l		droporfeo+'lexique/VRB.sfplm.old',

	for lexfile in [
		droporfeo+'lexique/INT.sfplm', 
		droporfeo+'lexique/COO.sfplm',
		droporfeo+'lexique/CLS.sfplm',  
		droporfeo+'lexique/CLN.sfplm',
		droporfeo+'lexique/CLI.sfplm', 
		droporfeo+'lexique/ADN.sfplm',
		droporfeo+'lexique/ADV.sfplm',
		droporfeo+'lexique/PRO.sfplm', 
		droporfeo+'lexique/PRQ.sfplm', 
		droporfeo+'lexique/CSU.sfplm',
		droporfeo+'lexique/VPR.sfplm', 
		droporfeo+'lexique/VPP.sfplm',
		droporfeo+'lexique/VNF.sfplm',  
		droporfeo+'lexique/PRE.sfplm',
		droporfeo+'lexique/DET.sfplm'
	]:
		# Lecture des fichiers .sfplm
		#if ".sfplm" in lexfile and "~" not in lexfile and not lexfile.endswith("ADVJA.sfplm") and not lexfile.endswith("VRB.sfplm.old"):

			# Ouverture d'un fichier de lexique
			with codecs.open(lexfile, "r", "utf8") as f:
				for ligne in f:
					if len(ligne) and "\t" in ligne:
						ligne = ligne.replace("\t\t","\t")
						ligne = ligne.replace(" \t","\t")
						ligne = ligne.replace("\t ","\t")
						
						#print ligne.strip()
						cols = ligne.split("\t")
						if len(cols)==5: deco,t,cat,lem,morph = cols
						elif len(cols)==6: deco,t,cat,lem,morph,_ = cols
						else:
							qsdf
						if lem[:2]=="s'":
							lem=lem[2:]
						elif lem[:3]=="se ":
							lem=lem[3:]
						#if (t,cat) in dico and dico[(t,cat)]!=(deco,lem):
							#print "dÃ©jÃ ",(t,cat)
							#print dico[(t,cat)]
							#print "nouveau:",deco,t,cat,lem,morph
						dico[(t,cat)]=(deco,lem)
						
						forme2cat[t]=forme2cat.get(t,[])+[cat]
						
						if " " in t and deco not in ["S","P","X", "M", "MX"]: # CrÃ©ation d'une liste des expressions multimots
								expressions_multimots+=[ligne]
								multim[t] = cols
	#print "Fin de lecture du dictionnaire"



def dicoTiretsRhapsodie():
	dico={}
	trees=conll.conllFile2trees("mate/fr/Rhaps.gold.conll14")
	for arbre in trees:
		for i, node in arbre.iteritems():
			if node["t"][0]=="-":
				dico[node["t"]]=dico.get(node["t"], node["tag"])
	return dico



def splice(sentencedic,nodenum,intdic,copiedfeatures=['t','token','lemma','cat','tag','beg','end']):
		"""
		integrates the intdic into sentencedic at position nodenum
		an empty intdic kicks out the node in a proper manner (other dependencies are still ok)

		format of intdic:
			indices start with 'i', for example 'i2'
			last node that carries 'child' feature will receive the children of the former nodenum node
			last node that has governor i0 will receive the governors of the former nodenum node
			if no node has a 'i0' governor, the first node will receive the governors
			WATCH OUT: the ix will be ordered. so i10 will be before i9!!!
			example pour est-ce, remplacÃ© par ce sous-arbre :
			{
				'i1':{ u'person': u'3', u'number': u'sg', u'cat': u'V', u'lemma': u'\xeatre', u'token': u'est', u'tense': u'present', u'mode': u'indicative', 'gov': {'i0':'root'}, u't': u'A', 'child':None},
				'i2':{u'cat': u'Cl', u'lemma': u'c', u'token': u'-ce', u't': u'B', 'gov': {'i1': u'sub'}}
			}

		gives back new sentencedic

		"""
		intgov,intchi=None,None
		if debug>2: 
			print "================== integrating"
			for i in sorted(intdic):
				print i,intdic[i]
			print "into ((((((((((((("
			for i in sorted(sentencedic):
				print i,sentencedic[i].get("t","-"),sentencedic[i]
			print "))))))))))))))) at ",nodenum
		#try:
		l = [0]+sorted(sentencedic)[:nodenum-1]+sorted(intdic)+sorted(sentencedic)[nodenum:]
		if debug>2: print "lllll",l


		for i in sorted(intdic):

			sentencedic[i]=sentencedic[nodenum].copy() # l'ancien noeud qui va Ãªtre virer est copier pour chaque ix
			for at in copiedfeatures:
				if at in intdic[i]:
					sentencedic[i][at]=intdic[i][at]
			sentencedic[i]['gov']=intdic[i].get('gov',{})

			if 'i0' in intdic[i].get('gov',{}).keys(): # found the gov: the one that has a root link
				sentencedic[i]['gov']=sentencedic[nodenum]['gov']
				intgov=i
			if "child" in intdic[i]: # found the child bearer
				intchi = i
				del intdic[i]['child']
		if not intgov:# if no governor is indicated, i guess it's the first word
			intgov="i1"
			if intgov in sentencedic: sentencedic[intgov]['gov']=sentencedic[nodenum]['gov']
		if not intchi: intchi=intgov

		# correct wrong gov links
		for i in sorted(sentencedic):
			if nodenum in sentencedic[i]['gov'].keys():
				sentencedic[i]['gov'][intchi]=sentencedic[i]['gov'][nodenum]
				del sentencedic[i]['gov'][nodenum]

		# bring all in a new list with new indeces based on l
		newsentencedic={}
		for newi,oldi in enumerate(l):
			if newi:# not 0
				newsentencedic[newi]=sentencedic[oldi]
				govdic={}
				for govi,dep in sentencedic[oldi]['gov'].iteritems():
					if govi>=0 and govi in l: govdic[l.index(govi)]=dep
				newsentencedic[newi]['gov']=govdic
				newsentencedic[newi]['id']=newi
				#print type(newsentencedic)


		if debug>2:
			print "result:((((((((((((("
			for i in sorted(newsentencedic):
				print i,newsentencedic[i].get("t",""),newsentencedic[i]
			print ")))))))))))))))"

		return newsentencedic

def corrigerNumerotation(arbre):
	indexcorrection = {0:0} # ancien indice --> nouvel indice
	problem = False
	for compteur, ind in enumerate(sorted(arbre.keys())):
		indexcorrection[ind]=compteur+1
		if compteur+1 != ind:
			problem = True
	if problem:
		arbrecorrige = {}
		for i, node in arbre.iteritems():
			print"before", node["gov"]
			node["id"]=indexcorrection[i]
			newgov={}
			for gi,f in node["gov"].iteritems():
				newgov[indexcorrection[gi]]=f
			node["gov"]=newgov
			print "after",node["gov"]
			arbrecorrige[indexcorrection[i]]=node
		return arbrecorrige
	return arbre


def corrigerArbreCompos(arbre): # DÃ©composition des mots composÃ©s
	for i, node in arbre.iteritems():
		tok = node["t"]
		if node["tag"] != "NUM":
			#print (tok,node["tag"])
			if re.match("jusqu'.+",tok):
				tok = tok.replace("'", "' ")
			corrtype= dico.get( (node["t"],node["tag"]),"P")[0] # P comme pas trouvÃ©
			if corrtype in ["S","P","X", "M", "MX"] and " " in tok:
				if corrtype in ["M", "X"]:
					nomFonc='MORPH'
				else:
					nomFonc="DEP"
				lem=node["lemma"]
				tag=node["tag"]
				arbIns = {}
				toksplit=tok.split()
				for ii,mot in enumerate(toksplit):
					if tok in ["de la", "de l'"]:
						nlem=dico.get((mot,forme2cat.get(mot,"?")[0]), "???")[1]
						ntag=forme2cat.get(mot,"?")[0]
						nnode={"lemma": nlem, "t":mot, 'id':'i1', 'tag':ntag, 'gov':{'i0':"SPE"}}
						node["gov"]={node["gov"].keys()[0]: "DEP"}
						#print node
					else:
						if mot !='A':
							nlem=dico.get((mot.lower(),forme2cat.get(mot.lower(),"?")[0]), "???")[1]
							ntag=forme2cat.get(mot.lower(),"?")[0]
						else:
							nlem=mot
							ntag='NOM'
						if ntag=='?' and nlem=='?':
							ntag=node['tag']
							nlem=mot.lower()
						nnode={"lemma": nlem, "t":mot, 'id':'i1', 'tag':ntag, 'gov':{'i0':nomFonc}}
					if ii==0:
						nnode['child']=None
					arbIns['i1']=nnode
				nouvelarb = splice(arbre,i,arbIns)
				nouvelarb=corrigerNumerotationSplice(nouvelarb)
				nouvelarb = corrigerArbreCompos(nouvelarb)
				return nouvelarb
	return arbre

def retoken(arbre):
	nouvelarb = copy.deepcopy(arbre)
	for i, node in arbre.iteritems():
		if node["gov"] == {}:
			node["gov"]={0:"!attention"}
		if (node["t"] == "est" and i+3 < len(arbre)) and (" ".join([arbre[x]["t"] for x in range(i,i+3)]) in ["est -ce que", "est -ce qu'"]):
			#print arbre
			#print i
			nouvelarb[i]["t"] = "est-ce "+arbre[i+2]["t"]
			nouvelarb[i]["gov"]={0:"root"}
			nouvelarb[i]["lemma"]= "est-ce que"
			nouvelarb[i]["tag"]= "CSU"
			nouvelarb = splice(nouvelarb, i+1, {})
			nouvelarb = splice(nouvelarb, i+1, {})
			#print nouvelarb
	if nouvelarb == {}:
		print arbre
	return nouvelarb

def corrigerNumerotationSplice(arbre, listeId=[]):
	if listeId:	newgovid=min(listeId)
	index={}
	node2tag={}
	for node in arbre.keys():
		index[arbre[node]["id"]]=node
		node2tag[arbre[node]["tag"]]=node2tag.get(arbre[node]["tag"], [])+[node]
	for i, node in arbre.iteritems():
		if node["gov"]=={}:
			if node["govtags"] == {}:
						node["gov"]={0:'ROOT'}
			else:
				govtag=node["govtags"].keys()[0]
				newgov= dict(node["govtags"].values()[0])
				if newgov.keys()[0] in listeId:
					#print newgov
					node["gov"]={newgovid:newgov.values()[0]}
					#print node["gov"]
				elif newgov.keys()[0] in index.keys():
					node["gov"]={index[newgov.keys()[0]]:newgov.values()[0]}
					if node["gov"].keys()[0] == i:
						node["gov"]={i-1:newgov.values()[0]}
				else:
					if govtag in node2tag.keys():
						for x in node2tag[govtag]:
							if newgov.keys()[0]-x <=2:
								node["gov"]={x:newgov.values()[0]}
					else:
						node['gov']={0:'root'}
	return arbre

def addinfototree(tree,deletePara=True):
	"""
	add to each node:
	children: list of indeces that are children
	
	"""
	rootindeces=[]

	for i,node in tree.items(): # getting some basic additional features: children and govtags
		tree[i]["children"]=tree[i].get("children",[])
		tree[i]["govtags"]={}
		
		if "junc" in node["gov"].values() and len(node["gov"].values())>1:
			for j,func in node["gov"].items():
				#if deletePara and func.startswith("para"):
					#del node["gov"][j]
				if func!="junc":
					del node["gov"][j]
		
		for j,func in node["gov"].items():
			#print func,func.endswith("_inherited")
			if func.endswith("_inherited"):
				# if the node has other govs than the inherited, ignore the inherited link
				if [dep for g,dep in node["gov"].iteritems() if not dep.endswith("_inherited")]:
					continue
			if func=="attention":
				# if the node has other govs than the attention, ignore the attention link
				if [dep for g,dep in node["gov"].iteritems() if dep!="attention"]:
					continue
			if j in tree: # in tree: has governor
				tree[j]["children"]=tree[j].get("children",[])+[i]
				tree[i]["govtags"][tree[j]["tag"]]=tree[i]["govtags"].get(tree[j]["tag"],[])+[ (j,func) ]
			else: # not in tree: roots
				if [dep for g,dep in node["gov"].iteritems() if not dep.endswith("_inherited") and g!=j and dep!="attention"]:
					continue # we have other governors than the root node and inherited govs
				rootindeces+=[i]
	return rootindeces


def recomposerMultimots(arbre, expressions_multimots):
	nouvelarbre=copy.deepcopy(arbre)
	correspondance=False
	for ligne in expressions_multimots:
		motCompose = ligne.split("\t")
		token=motCompose[1]
		token = token.replace("-"," -")
		termesMotCompose = token.split()
		#print termesMotCompose
		length = len(termesMotCompose)
		for i, node in nouvelarbre.iteritems():
			if i+(length-1)<=len(nouvelarbre):
				if node["t"] == termesMotCompose[0]:
					index=i
					if " ".join([nouvelarbre[i+x]["t"] for x in range(length)]) == token:
						#try:
						node["t"]=token.replace(" -", "-")
						node["lemma"]=motCompose[3]
						node["tag"]=motCompose[2]
						#print node["gov"], i
						if node["gov"].keys()[0] >i:
							for c in range(length):
								#print "  ", arbre[i+c]["gov"].keys()[0], i+c
								if arbre[i+c]["gov"].keys()[0] not in [x for x in range(i, i+c+1)]:
									node["gov"]=arbre[i+c]["gov"]
									#print "    ", node["gov"]
						listeId=[]
						for x in range(1,length):
							listeId += [i+x]
						#print listeId
						for indice in listeId:
							#print indice # On supprime les children des termes s'il sont dans l'expression
							if node["gov"].keys() not in listeId: # S'il y a des children
								nouvelarbre=splice(nouvelarbre, i+1, {})
							#print i, x, " ".join([node["t"] for i, node in nouvelarbre.iteritems()])
						copielisteId=listeId+[i]
						nouvelarbre=corrigerNumerotationSplice(nouvelarbre, copielisteId)
						listeId=[]
						
						nouvelarbre=recomposerMultimots(nouvelarbre, expressions_multimots)
						
	return nouvelarbre



def digits(arbre, digitsandnumbers):
	for i, node in arbre.iteritems():
		for number in digitsandnumbers:
			if number == node["t"] or number+"~" == node["t"]:
				node["lemma"] = node["t"]
				node["tag"]= "NUM"
				node["tag2"]="NUM"
	return arbre

def corrigerSegmentationClitiques(arbre, dico_clitiques):
	nouvelarb=copy.deepcopy(arbre)
	#print "before", len(arbre)
	for clitique in dico_clitiques.keys():
		for i, node in arbre.iteritems():
			if clitique in node["t"] and "#" not in node["t"]:
				#print "\n\n****",clitique
				arbIns={}
				nnode={"lemma": clitique[1:], "t":clitique, 'id':'i'+str(i+1), 'tag':dico_clitiques[clitique], 'gov':{'i1':"dep"}}
				verbe={"lemma": node["lemma"][:-len(clitique)], "t":node["t"][:-len(clitique)], 'id':'i1', 'tag':node["tag"], 'gov':node["gov"]}
				arbIns={ 'i1':verbe,'i2':nnode}
				#print arbIns
				nouvelarb = splice(nouvelarb,i,arbIns)
				nouvelarb=corrigerNumerotationSplice(nouvelarb)
				nouvelarb = corrigerArbreCompos(nouvelarb)
				#print arbre
				#print 
				#print nouvelarb
				#print "___"
				#qsdf
				"""
				probleme avec dernier arbre composÃ© de jeanne mallet (A DEBUG)
				"""
	return nouvelarb

def nombresComposes(arbre):
	for i,node in arbre.iteritems():
		if re.match(ur"[0-9]+", node["t"]):
			#print node["t"], i
			if node["t"]== u"000":
				node["t"]="1000"
			node["t"]= num2words(int(node["t"]), lang='fr')
			#print node["t"]
			arbre = nombresComposes(arbre)
	return arbre

def corrigerInaudibles(arbre):
	for i, node in arbre.iteritems():
		if node["t"].endswith("-~"):
			#print node["t"]
			node["t"]=node["t"][:-2]+"~"
			node["lemma"]= node["t"]
		elif node["t"].endswith("-"):
			#print node["t"]
			node["t"]=node["t"][:-1]+"~"
			node["lemma"]= node["t"]
	return arbre

def corrigerClitiques(arbre):
	for i, node in arbre.iteritems():
		if node["t"] in ["nous","vous"]:
			if node["gov"].values()[0] == "suj":
				node["tag"]= "CLS"
			elif node["gov"].values()[0] == "comp":
				node["tag"] = "CLI"
			else:
				node["tag"]="PRO"
		elif node["t"] == "elle":
			if node["gov"].values()[0] == "suj":
				node["tag"] = "CLS"
			else:
				node["tag"]="PRO"
		elif node["t"] == "il":
			if node["gov"].values()[0] == "comp":
				node["tag"] = "CLI"
			else:
				node["tag"]="PRO"
		if re.match(ur"[A-Z]", node["t"]) and not re.match(ur"[A-Z]", node["lemma"]):
			node["lemma"]=node["t"]
		if node["t"] == "NNAAMMEE":
			node["lemma"]= "_"
	return arbre

def retokeniser(nomdufichier, path="",addtoout=""):
	if not path: path,_= os.path.split(nomdufichier) # take the same path as the nomdufichier
	if path and path[-1]!="/": path=path+"/"		
	trees = conll.conllFile2trees(nomdufichier) # on lit le fichier
	print "le fichier",nomdufichier,"a",len(trees),"arbres"
	#newtrees, alltrees=[], []
	newtrees=[]
	digitsandnumbers=codecs.open(droporfeo+"lexique/gg", "r", "utf-8").read().split('\n')
	for i, arbre in enumerate(trees): # on boucle sur les arbres
		#alltrees+=[copy.deepcopy(arbre)]
		#oldtree=copy.deepcopy(arbre)
		racines = addinfototree(arbre)
		oldtree=copy.deepcopy(arbre)
		arbre = corrigerNumerotation(arbre)
		arbre = nombresComposes(arbre)
		arbre = digits(arbre, digitsandnumbers)
		arbre=corrigerArbreCompos(arbre) # DÃ©composition des expressions multimots
		#for i, node in arbre.items(): # Reconfiguration des enfants
			#if node["gov"] == {}:
			#print "crap"

		arbre=recomposerMultimots(arbre, expressions_multimots)
		arbre=corrigerNumerotationSplice(arbre)

		arbre=corrigerSegmentationClitiques(arbre, dico_clitiques)
		arbre=corrigerInaudibles(arbre)
		arbre=corrigerClitiques(arbre)
		arbre= retoken(arbre)
		if arbre!=oldtree:
			print i
			for ii in arbre:
				if arbre[ii]!=oldtree.get(ii,None):
					print ii,arbre[ii]['t'],arbre[ii],oldtree.get(ii,None)
		newtrees.append(arbre)
	newname=path+os.path.basename(nomdufichier+addtoout)
	conll.trees2conllFile(newtrees, newname, columns=10)
	return newname

def segmentationChiffres(arbre):
	for i,node in arbre.iteritems():
		if node["tag"] == "NUM" and " " in node["t"]:
			toksplit=node["t"].split()
			for ii,mot in enumerate(toksplit):
				nlem=mot
				ntag="NUM"
				nnode={"lemma": nlem, "t":mot, 'id':'i1', 'tag':ntag, 'gov':{'i0':nomFonc}}
				if ii==0:
					nnode['child']=None
				arbIns['i1']=nnode
				nouvelarb = splice(arbre,i,arbIns)
				nouvelarb=corrigerNumerotationSplice(nouvelarb)
				nouvelarb = corrigerArbreCompos(nouvelarb)
				return nouvelarb
	return arbre

def retokenizeTree(arbre):
	for i,node in arbre.iteritems():
		if "gov" not in arbre[i]:
			arbre[i]["gov"]={0:"!attention"}

	racines = addinfototree(arbre)
	#print racines
	arbre = corrigerNumerotation(arbre)
	arbre = nombresComposes(arbre)
	

	arbre = digits(arbre, digitsandnumbers)
	arbre=corrigerArbreCompos(arbre) # DÃ©composition des expressions multimots
	#for i, node in arbre.items(): # Reconfiguration des enfants
		#if node["gov"] == {}:
		#print "crap"

	arbre=recomposerMultimots(arbre, expressions_multimots)
	arbre=corrigerNumerotationSplice(arbre)

	arbre=corrigerSegmentationClitiques(arbre, dico_clitiques)
	arbre=corrigerInaudibles(arbre)
	arbre=corrigerClitiques(arbre)
	arbre= retoken(arbre)

	for i,node in arbre.iteritems():
		del node["govtags"]
		del node["children"]

	return arbre


def doIt():
	dico={} # (forme,pos) -> (decomposinfo,lemme)
	forme2cat={} # forme -> [cats]
	expressions_multimots=[]
	multim={}
	lireDictionnaires(dico,forme2cat,expressions_multimots, multim)

	print dico
	dico_clitiques=dicoTiretsRhapsodie()
	print dico_clitiques
	
def removePuncsFromConllfile(conllfile):
	with codecs.open(conllfile,"r","utf-8") as f:
		conlltext=f.read()
		conlltext=conlltext.replace("0	_	punc","-1	_	punc")
	with codecs.open(conllfile,"w","utf-8") as f:
		f.write(conlltext)

def degradeConllfile(conllfile, removeFuncs=["para"], removeDeps=0.2):
	trees=conll.conllFile2trees(conllfile)
	nbgovs=0
	for arbre in trees:
		for i, node in arbre.iteritems():
			if "gov" in node and node["gov"].keys()[0]!=-1 and node["gov"].values()[0] not in removeFuncs: 
				nbgovs+=1
	print int(nbgovs*removeDeps)
	tobeRemoved=sorted(random.sample(range(nbgovs),int(nbgovs*removeDeps)))
	print nbgovs, tobeRemoved
	nbgovs=0
	for arbre in trees:
		for i, node in arbre.iteritems():
			if "gov" in node and node["gov"].keys()[0]!=-1 and node["gov"].values()[0] not in removeFuncs: 
				nbgovs+=1
				if nbgovs in tobeRemoved:
					node["gov"]={}
	shutil.move(conllfile, conllfile+".orig")
	conll.trees2conllFile(trees, conllfile, columns=10)
	
	
	
reponct=re.compile(ur'''(\s*[-.;:,!\(\)Â§"']+)''')
#rehyph=re.compile(ur'(\s*\-+)')
respaces=re.compile(ur'\s+')
removespaces=re.compile(ur''' +([']+)''')
removespacesl=re.compile(ur''' +([-,\(\)Â§"']+)''')
removespacesr=re.compile(ur'''([-,\(\)Â§"']+) +''')

def tokenize(text):
	text=text.replace(u"â","'")
	text=reponct.sub(r" \1 ",text)
	#text=rehyph.sub(r"\1 ",text)
	text=respaces.sub(r" ",text)
	
	return text


def emptyFromSentence(sentencefile, useTokDic=True):
	"""
	file one sentence per line --> conll14
	specific for french (apostrophes are glued to the precedent word: d' un
	in english we'd need: do n't Mike 's
	"""
	outname=sentencefile+".conll"
	if useTokDic:
		dico={} # (forme,pos) -> (decomposinfo,lemme)
		forme2cat={} # forme -> [cats]
		expressions_multimots=[]
		multim={}
		lireDictionnaires(dico,forme2cat,expressions_multimots, multim)
		multimatch = tokenize(ur"(\b"+ur"\b|\b".join(sorted(multim))+ur"\b)").replace("+","\\+").strip()
		remultimatch=re.compile(multimatch,re.U+re.I)
		
		with codecs.open(sentencefile,"r","utf-8") as f, codecs.open(outname,"w","utf-8") as g:
			for li in f:
				multitoks=[]
				tokli = tokenize(li)
				for m in remultimatch.finditer(tokli):
					multitoks+=[m.group(0)]
				for mt in multitoks:
					tokli=tokli.replace(mt,mt.replace(" ","_"))
				#print tokli
				tokli=removespaces.sub(ur"\1",tokli) # remove spaces in front of ' : d ' un --> d' un
				#print tokli
				for num,tok in enumerate( tokli.split() ):
					tok = removespacesr.sub(ur"\1",removespacesl.sub(ur"\1",tok.replace("_"," "))) # glues together spaces to obtain a regular spelling
					g.write("\t".join([str(num+1), tok ]+["_"]*12)+'\n')
				g.write("\n")
	else:
		with codecs.open(sentencefile,"r","utf-8") as f, codecs.open(outname,"w","utf-8") as g:
			for li in f:
				tokli = tokenize(li)
				
				tokli=removespaces.sub(ur"\1",tokli) # remove spaces in front of ' : d ' un --> d' un
				#print tokli
				for num,tok in enumerate( tokli.split() ):
					tok = removespacesr.sub(ur"\1",removespacesl.sub(ur"\1",tok.replace("_"," "))) # glues together spaces to obtain a regular spelling
					g.write("\t".join([str(num+1), tok ]+["_"]*12)+'\n')
				g.write("\n")
	return outname


			
def parseSentenceFile(sentencefile, modelfolder="mate/platinum.2016-10-20_01:34/models/", lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="40G", removePunct=False, useTokDic=False, degrade=False):
	"""
	removed parsertype from options!
	"""
	emptyConll = emptyFromSentence(sentencefile, useTokDic=useTokDic)
	parsedfile=parsing(emptyConll, lemodel=modelfolder+lemodel, tagmodel=modelfolder+tagmodel, parsemodel=modelfolder+parsemodel, outfolder=outfolder, memory=memory)
	print parsedfile
	if removePunct: removePuncsFromConllfile(parsedfile)
	if degrade: degradeConllfile(parsedfile)


if __name__ == "__main__":
	
	#doIt()
	#parseSentenceFile("exo1sents.a")
	#parseSentenceFile("exo1sents.b")
	#parseSentenceFile("exo1sents.c")
	#parseSentenceFile("cantonese_sample_for_dict.txt", modelfolder="mate/hkud.2016-11-02_04:04/models/", lemodel="", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="4G", removePunct=False, useTokDic=False)
	#parseSentenceFile("cantonese_sample_for_dict.txt", modelfolder="mate/hkud.2016-11-02_11:03/models/", lemodel="", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="4G", removePunct=False, useTokDic=False)
	#parseSentenceFile("cantonese_sample_for_dict.txt.fakeMand", modelfolder="mate/hkud.2016-11-02_11:03/models/", lemodel="", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="4G", removePunct=False, useTokDic=False)
	
	#parseSentenceFile("2a.txt", modelfolder="mate/platinum.2016-10-20_01:34/models/", lemodel="LemModel", tagmodel="TagModel", parsemodel="ParseModel", outfolder=".", memory="40G", removePunct=True, useTokDic=True)
	
	
	emptyFromSentence("2a.txt")
	#degradeConllfile("2a.txt.conll_parse")
	
	#path="mate/parses/"
	#try:
		#os.mkdir(path)
	#except OSError:
		#pass
	#retokeniser("./mate/test_200816.conll", path)
	#retokeniser("./mate/parses/test/test.conll", path)
	"""for infile in glob.glob("mate/parses/originals/*.orfeo"):
					print infile
					retokeniser(infile, path)
					kjbk"""
	#conll.trees2conllFile(alltrees, "mate/parses/Tscha_cha_reu_ass_08.trs.lif.w+p.orfeo"+".conll10")
#! usr/bin/python
#! coding: utf-8

sys.path.insert(0, '../lib')
import database, retokenisation, newmate, pprint, sys

def modifyAllTreesFromAllUsers(project):
	sql = database.SQL(project)
	db, cursor = sql.open()
	command = "select sentences.rowid, sentences.nr, sentences.textid, trees.userid, trees.rowid from sentences, texts, trees where sentences.textid=texts.rowid and trees.sentenceid = sentences.rowid;"
	cursor.execute(command)
	answer = cursor.fetchall()
	print len(answer), "trees to update"
	lastpourc=-1
	for nr, (sid, snr, tid, uid, treeid) in enumerate(answer):
		pourc = int(float(nr)/len(answer)*100)
		if pourc!=lastpourc: sys.stdout.write( "{pourc}%\r".format(pourc=pourc) )
		sys.stdout.flush()
		dic=sql.gettree(None,None,treeid, indb=db,incursor=cursor)
		if dic and dic["tree"]:
			sentencetree=dic["tree"]
			#print "Resgmentation en cours de ", " ".join([node["t"] for i, node in sentencetree.iteritems()]), "id=", treeid, "by user", uid
			#newtree=retokenisation.retokenizeTree(sentencetree)
			newtree=retokenisation.segmentationChiffres(sentencetree)
			#pprint.pprint(newtree)
			sql.enterTree(cursor, newtree, sid, uid, tokensChanged=True)
			#print "Tree", treeid," successfully updated."
	print "Done."
	db.commit()
	db.close()


if __name__ == "__main__":
	project="OrfeoGold2016"
	newpath=newmate.backupOldDatabase(project, "./oldprojects/")
	print "copie de la base dans", newpath
	modifyAllTreesFromAllUsers(project)


#! usr/bin/python
#! coding: utf-8

import datetime, codecs, time, sys, os, glob
sys.path.insert(0, '../lib')
import conll

def hasVerbalDm(tree):
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["dm"] and node["tag"]=="VRB":
			return True
	return False

def hasVRBPeriph(tree):
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["periph"] and node["tag"]=="VRB":
			return True
	return False

def hasVerbalPeriph(tree):
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["periph"] and node["tag"][0]=="V":
			return True
	return False

def hasAux(tree):
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["aux"]:
			return True
	return False

def hasMorph(tree):
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["morph"]:
			return True
	return False

def estce(tree):
	for i, node in tree.iteritems():
		if "est-ce" in node["t"]:
			return True
	return False

def lalala(tree):
	for i, node in tree.iteritems():
		if u"-lÃ " in node["t"]:
			return True
	return False

def hasNoGov(tree):
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].keys()[0] in [-1]:
			return True
	return False

def disflink(tree):
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["disflink"]:
			return True
	return False

def hasEmptyNode(tree):
	for i, node in tree.iteritems():
		if node["t"]=="":
			return True
	return False

def isNonProjective(tree):
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].keys()[0] in tree and node["tag"] not in ["PRQ", "CLI"]:
			j=node["gov"].keys()[0]
			for ii, nnode in tree.iteritems(): # look again at each node
				if i<ii<j: # between
					for jj in nnode["gov"]:
						if jj<i or jj>j:
							if jj==0 or (ii in tree and tree[ii]["tag"] not in ["PRQ", "CLI"]):
								return True
				else:
					for jj in nnode["gov"]:
						if i<jj<j: 
							if jj==0 or (ii in tree and tree[ii]["tag"] not in ["PRQ", "CLI"]):
								return True
	return False

def cls(tree):
	for i, node in tree.iteritems():
		if node["tag"]=="CLS":
			gi,gf = node["gov"].items()[0]
			if gi in tree and tree[gi]["tag"] not in ["VRB"] and gf not in ["para","disflink"]: # CLS whose governor is not a verb
				return True
				#govnode=tree[gi]
				
				#pprint(tree)
				#print "******",i
			
			
	return False

	

def lemmaContains(tree, char="-"):
	
	lemmacorrection=dict( [(a.split(":")[0],a.split(":")[1]) for a in u"""
c' est-Ã -dire que:c'est-Ã -dire que
est-ce-que:est-ce que
quelques-un:quelques-uns
Ax-~~:Ax~
festiv-Ã©tÃ©:Festiv-Ã©tÃ©
t-il:il
ouvre-moi:ouvrir
trouve-moi:trouver
rappelle-moi:rappeler
dÃ©brouille-toi:dÃ©brouiller
met-il:mettre
es-tu:Ãªtre
doit-on:devoir
moment-lÃ :moment
gars-lÃ :gars
jour-lÃ :jour
bruit-lÃ :bruit
mois-lÃ :mois
festival-lÃ :festival
cÃ´tÃ©-lÃ :cÃ´tÃ©
jeunes-lÃ :jeunes
niveau-lÃ :niveau
celui-lÃ :celui
lui-mÃªme:lui
sapeurs-pompier:sapeur-pompier
proposition-lÃ :proposition
petit-Ã -petit:petit Ã  petit
groupe-lÃ :groupe
titre-lÃ :titre
stylo-lÃ :stylo
pays-lÃ :pays
sens-lÃ :sens
faut-il:falloir
passe-moi:passer
appelez-le:appeler

""".strip().split("\n")])
	print lemmacorrection
	
	
	motstraits=u"""
anti-littÃ©rature
prÃ©-fait
six-dix
arriÃ¨re-plan
anti-concertation
contre-littÃ©rature
arriÃ¨re-grand-mÃ¨re
Reuilly-Diderot
Saint-Germain
grands-parents
outre-mer
qu'est-ce que
est-ce que
vice-versa
peut-Ãªtre
sous-terrain
tout-Ã -fait
entre-temps
au-dehors
rouge-gorge
c'est-Ã -dire
au-delÃ 
essuie-glace
lÃ -haut
petit-ami
Alsace-Lorraine
Hauts-de-Seine
rond-point
centre-ville
Notre-Dame
Sainte-Claire
Grand-Bigard
Ghlin-Baudour
Point-rouge
Mbuji-Mayi
ex-gouverneur
lÃ -bas
lÃ -dedans
dix-neuviÃ¨me
dix-septiÃ¨me
dix-huitiÃ¨me
vingt-et-uniÃ¨me
rendez-vous
baby-sitter
quand-mÃªme
aprÃ¨s-midi
grand-parent
Pointe-Ã -Pitre
Saint-Trop
Villeneuve-Loubet
Bruxelles-ville
Arts-Loi
enseignant-chercheur
mathÃ©maticien-Ã©crivain
pÃ¢tissier-boulanger
Jean-FranÃ§ois
Jean-Baptiste
Jean-Louis
Jean-Paul
Jean-Christophe
week-end
lunch-packet
Saint-Sauveur
Proche-Orient
grand-chose
lÃ -dessus
au-dessus
lÃ -haut-dessus
top-model
Saint-Jean
Saint-Jean-de-Maurienne
c'est-Ã -dire que
anti-viral
JT-derniÃ¨re
Festiv-Ã©tÃ©
sous-marin
septante-sept
mille-neuf-cent-nonant
mitterando-mitterandien
savoir-vivre
sapeur-pompier
tout-petit
vis-Ã -vis de
mÃ©dico-social
belle-mÃ¨re
mi-temps
micro-processeur
contre-attaque
fois-ci
celui-ci
celle-ci
moi-mÃªme
ceux-lÃ 
vis-Ã -vis
Nord-Africains
Royaume-Uni
Grande-Bretagne
Ãtats-Unis d'AmÃ©rique
quelques-uns
vu-t-une
	""".strip().split("\n")
	for i, node in tree.iteritems():
		if char in node["lemma"]:
			if 1<node["lemma"].find(char)<len(node["lemma"]):
				if node["lemma"] not in motstraits and node["lemma"] not in lemmacorrection:
					if node["tag"] != "NUM":
						#print i,node["lemma"].find(char)
						return True
					
			
			
def search(infolder,fun):
	goodtrees=[]
	print "doing", fun.__name__
	#try: os.mkdir(outdir)	
	#except OSError: pass
	for infile in sorted(glob.glob(os.path.join(infolder,"*"))): # .conll
		if not os.path.isfile(infile): continue
		basename=os.path.basename(infile)
		print "reading",basename
		trees = conll.conllFile2trees(infile)
		
		for tree in trees:
			#if hasVerbalDm(tree):
			#if isNonProjective(tree):
			if fun(tree):
				goodtrees+=[tree]
	print "found",len(goodtrees)
	if goodtrees:
		conll.trees2conllFile(goodtrees,fun.__name__+".conll")

def searches():			
	#search("projects/OrfeoGold2016/platinum/", hasVerbalDm)		
	search("projects/OrfeoGold2016/platinum/", isNonProjective)
	#search("projects/OrfeoGold2016/platinum/", hasEmptyNode)
	#search("projects/OrfeoGold2016/platinum/", cls)
	#search("projects/OrfeoGold2016/platinum/", hasAux)
	#search("projects/OrfeoGold2016/platinum/", estce)
	#search("projects/OrfeoGold2016/platinum/", lalala)
	#search("projects/OrfeoGold2016/platinum/", hasNoGov)
	#search("projects/OrfeoGold2016/platinum/", disflink)
	#search("projects/OrfeoGold2016/platinum/", hasVRBPeriph)
	#search("projects/OrfeoGold2016/platinum/", hasVerbalPeriph)
	#search("projects/OrfeoGold2016/platinum/", hasMorph)
	#search("../projects/Platinum/exportcool/", lemmaContains)
	#search("../projects/Platinum/export/", lemmaContains)

if __name__ == "__main__":
	searches()
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2012 Kim Gerdes
# kim AT gerdes. fr
# http://arborator.ilpga.fr/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 3 of the GNU Affero General Public License (the "License")
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of of version 3 of the GNU Affero General Public License
# from http://www.gnu.org/licenses/agpl-3.0.html 
# For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

import re, os, codecs, sys
#import config, xmlsqlite, traceback

sys.path.insert(0, '../lib')
from conll import conllFile2trees, update, trees2conllFile

debug=True
debug=False

lettre=re.compile("\w",re.U)


def tree2nodedic(tree, correctiondic={}):
	""" 
	takes the conll string (or malt) representation of a single tree and creates a dictionary for it
	correctiondic of the form {"tag":"cat"} (old to new) corrects feature names
	"""
	
	treedic={}
	bonnr=0
	for line in tree.split('\n'):
		#print "$$$$",line
		line=line.strip()
		if line:
			cells = line.split('\t')
			nrCells = len(cells)
			if nrCells > 10 and cells[3]!="":
				# il faut sÃ©parer les cas avec 11 cellules des cas avec 21, car on a les "&".
				codeetexte,_, nr, t, lemma, tag, morph,_,_,_,_,g0,f0 =  cells[:13]
				if nrCells > 20:
					codeetexte,_, nr, t, lemma, tag, morph,_,_,_,_,g0,f0,g1,f1,g2,f2,g3,f3,g4,f4 =  cells[:21]
				if debug:print cells[:20]
				nr=int(nr)
				if tag[:2]=="I_":
					if lettre.search(t[0]) and lettre.search(derniert[0]):treedic[bonnr]["t"]+=" "
					#if nr-bonnr>1:treedic[bonnr]["t"]+=" "
					treedic[bonnr]["t"]+=t
				else:
					bonnr=nr
					
					features={'id':nr,'t': t,'lemma': lemma, 'tag': tag,  'gov':{}, 'morph':morph}
					if g0:	features["gov"][int(g0)]=f0
					else:
						if g3:	
							try:
								features["gov"][int(g3)]=f3
							except:
								print "problem int"
								out=codecs.open("problem.txt","a","utf-8")
								out.write("\t".join(cells[:21])+"\n")
								out.close()
								features["gov"][int(g3.split(",")[0])]=f3
						else:
							if g1:	features["gov"][int(g1)]=f1
							
							else:
								
								
								print "problem"
								out=codecs.open("problem.txt","a","utf-8")
								out.write("\t".join(cells[:21])+"\n")
								out.close()
							
					#if features['gov']== {23: u'sub'} and features['t']=='qui' and codeetexte=='M0023 ':	
						##raw_input("iii")
						#print "kkkkkkkkkkkkkkk"
						#print tree
					treedic[int(nr)]=features
				derniert=t
				
	#print nodedic
	return treedic
		
	
def specialconllFile2trees(path, correctiondic={}):
	"""
	important function!
	
	called from enterConll and uploadConll in treebankfiles.cgi
	
	"""
	f=codecs.open(path,"r","utf-8")
	tree=""
	trees=[]
	for li in f:
		li=li.strip()
		if li:
			elements = li.split("\t")
			#print li
			if elements[0]=="TextID": continue
			if len(elements)>1: nr = elements[1]
			else: nr = ""
			
			if nr: 	tree+=li+"\n"
			else: # emptyline, sentence is finished copy.deepcopy(nodedic)
				treedic=tree2nodedic(tree,correctiondic)
				trees+=[treedic]
				#del nd
				tree=""
	f.close()
	if tree.strip(): # last tree may not be followed by empty line
		treedic=tree2nodedic(tree,correctiondic)
		trees+=[treedic]
	return trees


	
def completeInformation(tree):
	for id in sorted(tree):
		node=tree[id]
		
		if node["gov"]!={}:
			tgov=node["gov"].keys()[0]
			if tgov!=0:
				tfonc=node["gov"][tgov]
				if debug:print node
				tree[tgov]['kid']=tree[tgov].get('kid',{})
				tree[tgov]['kid'][id]=tfonc
	return tree
	
	

def passcomp(tree):
	#print tree
	for id in sorted(tree):
		node=tree[id]
		if 'past_participle' in node.get('morph',""):
			tgov=node["gov"].keys()[0]
			tfonc=node["gov"][tgov]
			if tfonc=="pred" and tree[tgov]["tag"]=="V":
				
				
				node['gov']=tree[tgov]['gov']
				#print "kkk",tree[tgov]
				for i,f in tree[tgov]['kid'].iteritems():
					if f!="pred":
						tree[i]['gov']={id:f}
						#print "---",tree[i]
				tree[tgov]['gov']={id:'aux'}
				print "____",node
				
	return tree


def simpleConll(inputfilename):
	"""
	fonction qui lit rhapsodie tok
	et choisit la fonction unique par token
	resort un format conll standard
	"""
	trees=conllFile2trees(inputfilename)
	#1/0
	with codecs.open(inputfilename+'.simpl', 'w','utf-8') as f: 
		for treedic in trees: 
			#print "\n"
			
			#for id in sorted(treedic):
				#node=treedic[id]
				#print "____",id, node
			#print "ooo"
			treedic=completeInformation(treedic)
			ntreedic=passcomp(treedic)
			
			
			# treedic ressemble Ã  {1: {'lemma': u'bonjour', 'gov': {0: u'root'}, 'tag': u'B_I', 'id': 1, 't': u'bonjour'}, 3: {'lemma': u'Eric', 'gov': {0: u'root'}, 'tag': u'B_N', 'id': 3, 't': u'Eric'}}
			for tokid in sorted(treedic):
				
				tdic=treedic[tokid]
				#print tokid,tdic
				if tdic["gov"]!={}:
					tgov=tdic["gov"].keys()[0]
					tfonc=tdic["gov"][tgov]
				else: tgov,tfonc="",""
				f.write("\t".join([str(tdic["id"]),tdic["t"],tdic["lemma"],tdic["tag"][2:],"","",str(tgov),tfonc])+"\n")
			f.write("\n")
			


def passcompUp(infilename, outfilename):
	trees=conllFile2trees(infilename)
	print "read trees"
	with codecs.open(outfilename,"w","utf-8")  as outfile:
		for tree in trees: 
			tree=completeInformation(tree)
			tree=passcomp(tree)
			for i,tokenid in enumerate(sorted(tree)):
				node=tree[tokenid]
				gov = node.get("gov",{}).items()
				govid,func= gov[0]
				outfile.write("\t".join([str(tokenid),node.get("t","_"), node.get("lemma","_"), node.get("lemma","_"), node.get("tag","_"), node.get("tag","_"), node.get("morph","_"), node.get("morph","_"), str(govid),str(govid),func,func,"_","_"])+"\n")
				
			outfile.write("\n")

def makeConllUfromNaijaFile(infilename):
	trees=[]
	tree={}
	with codecs.open(infilename,"r","utf-8") as infile:
		for line in infile:
		#print "$$$$",line
			line=line.strip()
			if line and line[0]!="#":
				cells = line.split('\t')
				nrCells = len(cells)
				if nrCells != 10 :
					print line
					continue
				nr,t,x,tag=cells[:4]
				nr = int(nr)
				newf={'id':nr,'t': t,'tag': tag}
				x=x.strip()	
				if "=" in x:
					mf=dict([(av.split("=")[0],av.split("=")[-1]) for av in x.split("|")])
					newf=update({"features":mf},newf)
					
				elif x!=".":
					newf=update({"lemma":x},newf)
				if nr==1:
					trees+=[tree.copy()]
					tree={}
				tree[nr]=update(tree.get(nr,{}),newf)
	print len(trees),"trees"
	trees2conllFile(trees,os.path.basename(infilename).split(".")[0]+".conllu",columns="u")
	
if __name__ == "__main__":
	
	#simpleConll("Rhap-M2006-Aligned_data.tok")
	#simpleConll("Rhapsodie.tok")
	#passcompUp("Rhapsodie.conll","Rhapsodie.passcomp.conll")
	makeConllUfromNaijaFile("../corpus/annotatedCorpora.csv")
	
	
	
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2014 Kim Gerdes
# kim AT gerdes. fr
# http://arborator.ilpga.fr/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 3 of the GNU Affero General Public License (the "License")
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of of version 3 of the GNU Affero General Public License
# from http://www.gnu.org/licenses/agpl-3.0.html 
# For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

import sys, json, codecs, copy, collections, re, hashlib, os, glob, subprocess, fnmatch
from datetime import datetime
#import config, xmlsqlite, traceback
from parseSentences import textToSentences
debug=True

#print codecs.open("textgrids/Chloe Guyard_23953_assignsubmission_file_guyard2411.TextGrid","r","utf-16").read()






requotes=re.compile(ur'\"(.*)\"$',re.U)
#reinvers=re.compile('(-je|-tu|-t-il|-il|-elle|-t-elle|-nous|-vous|-t-ils|-ils)',re.U)

incompleteTextLine=re.compile(ur'^ *text \= \"\s*\r?\n?',re.U+re.M)
lonelyQuote=re.compile(ur'\s*\"\s*$',re.U+re.M)

            


verbose=False
verbose=True

def textgridToSentences(filter="*", keepEndSent=True, maxlength=100, writeProblemBeforeSentence=False, skipBefore=None):
	
	
	skipnext=False
	skiptier=False
	#for infilename in glob.glob(os.path.join("textgrids", filter)):
	for root, dirnames, filenames in os.walk('textgrids'):
		for filename in fnmatch.filter(filenames, filter):
			infilename=os.path.join(root, filename)
			
			
			#print datetime.fromtimestamp(os.path.getmtime(infilename)) 
			if skipBefore and str( datetime.fromtimestamp(os.path.getmtime(infilename))) < skipBefore: 
				print infilename,"too old"
				continue
			
			print  "\n\n",infilename,root, dirnames, filenames
			simplename = infilename.split("/")[1].split("_")[0].replace(" ","_") 
			print "-->",simplename
			
			problem=True
			for enc in ["utf-8","utf-16","iso-8859-1"]:
				try:
					alltext=codecs.open(infilename,"r",enc).read()
					#print enc
					problem=False
					break
				except Exception,e: 
					pass
					#print str(e)
			
			if problem:
				print "can't decode",infilename
				break
			else:
				nbtiers=alltext.count('class = "IntervalTier"')
				if nbtiers!=1:
					print "the file",infilename,"has",nbtiers,"tiers!"
			with codecs.open(infilename,"r",enc) as infile:
				#c=infile.read()
				#print c
				#continue
				first=infile.readline().strip()
				if first and ord(first[0])==65279: first=first[1:] # fucking boms!
				text=""
				if first.startswith("File type") or '"IntervalTier"' in alltext:
					print "reading textgrid",infilename
					
					intext=infile.read()
					
					intext = incompleteTextLine.sub('  text = "', intext) # remove new line after incompleteTextLine
					intext = lonelyQuote.sub('"', intext) # move lonelyQuote to previous line
					
					
					#print intext
					for line in intext.split("\n"):
						line=line.strip()
						if skipnext:
							if line==u'"phonÃ¨mes"' or line==u'"Fonctions"':
								skiptier=line
							skipnext=False
							continue
						
						if line=='"IntervalTier"' or line=='"TextTier"':
							skipnext=True
							skiptier=False
							continue
						else: skipnext= False
						
						if skiptier:
							continue
						if line.startswith("class =") or line.startswith("File type =") or line.startswith("Object class =") or line.startswith("name ="): continue
						
						m = requotes.search(line)
						if m:
							text+=m.group(1)+" "
							if verbose:print m.group(1)
						else:
							pass
							if verbose:print "no",line
						
				else:
					print "textfile",infilename
					text+=first
					for line in infile:
						line=line.strip()
						line=line.replace('"',' ')
						text+=line+" "
				text=text.replace("...",".")
				text=text.replace("/","~")
				if verbose:
					print "___",text,"___"
				
			textToSentences(text, outname="sentences/"+simplename, problemout="longsentences/"+simplename, keepEndSent=keepEndSent, maxlength=maxlength, writeProblemBeforeSentence=writeProblemBeforeSentence)	
				
			#outfile=codecs.open(outfolder+"/"+infile.split("/")[-1],"w","utf-8")




def mate(outfolder="parses", lang="fr", memory="12G", sentenceFolder=u"sentences", filter="*",depparse=True):
	
	for sentencefile in glob.glob(os.path.join(sentenceFolder, filter)):
		sentencefile=unicode(sentencefile)
		if outfolder[-1]!="/":outfolder=outfolder+"/"

		filebase=outfolder+os.path.basename(sentencefile)
		print "java -Dfile.encoding=UTF-8 -cp anna-3.6.jar is2.util.Split "+sentencefile+" > "+filebase+"-one-word-per-line.txt"
		p1 = subprocess.Popen(["java -Dfile.encoding=UTF-8 -cp anna-3.6.jar is2.util.Split "+sentencefile+" > "+filebase+"-one-word-per-line.txt"],shell=True, stdout=subprocess.PIPE)
		print p1.stdout.read()
		print "ooo",sentencefile
		########### franÃ§ais
		if lang=="fr":

			p1 = subprocess.Popen(["java -Xmx"+memory+" -cp anna-3.6.jar is2.lemmatizer.Lemmatizer -model lemModelAll -test  "+filebase+"-one-word-per-line.txt -out   "+filebase+"-lemmatized.txt"],shell=True)
			out, err = p1.communicate()
			print out, err
			p1 = subprocess.Popen(["java -Xmx"+memory+" -cp anna-3.6.jar is2.tag.Tagger  -model tagModelAll -test  "+filebase+"-lemmatized.txt -out   "+filebase+"-tagged.txt"],shell=True,  stdout=subprocess.PIPE) # is2.tag3.Tagger or is2.tag.Tagger ???
			out, err = p1.communicate()
			print out, err
			p1 = subprocess.Popen(["java -Xmx"+memory+" -cp anna-3.6.jar is2.mtag.Tagger  -model morphModelAll -test  "+filebase+"-tagged.txt -out   "+filebase+"-mtagged.txt"],shell=True,  stdout=subprocess.PIPE) # is2.tag3.Tagger or is2.tag.Tagger ???
			out, err = p1.communicate()
			print out, err
			p1 = subprocess.Popen(["java -Xmx"+memory+" -classpath anna-3.6.jar is2.parser.Parser -model faaModelAll -test  "+filebase+"-mtagged.txt -out  "+filebase+".trees.conll14"],shell=True,  stdout=subprocess.PIPE)
			print p1.stdout.read()


#print glob.glob(os.path.join("textgrids", 'Alexia*.*'))

#lis=""
#with codecs.open("textgrids/Alexia Murano_23945_assignsubmission_file_Text_gridmuranoalexia","r","utf-8") as infile:
	#for line in infile:
		#if line and line[0]=='"':lis+=line
#with codecs.open("textgrids/Alexia Murano_23945_assignsubmission_file_Text_gridmuranoalexia","w","utf-8") as outfile:
	#outfile.write(lis)





def rename():
	for fil in glob.glob(os.path.join(u"parses", '*-dependanaly.txt')):
		os.rename(fil, fil[:-16]+".trees.conll14")






if __name__ == "__main__":
	pass
	
	#rename()
	#textgridToSentences("*", keepEndSent=True)
	#textgridToSentences("*", keepEndSent=False, writeProblemFile=True)
	#textgridToSentences("*", keepEndSent=False, writeProblemFile=False)
	#textgridToSentences("*Lipo*")
	
	#mate(sentenceFolder="rafael")
	
	#textgridToSentences("*")
	textgridToSentences("*", keepEndSent=True, maxlength=100, writeProblemBeforeSentence=False, skipBefore="2017-11-16")
	#mate(filter="*Fum*")
	
	#for fil in glob.glob(os.path.join(u"sentences", '*')):
		#if "?" in codecs.open(fil,"r","utf-8").read():
			#print fil
#! usr/bin/python
#! coding: utf-8

import datetime, codecs, time, sys, os, glob, copy
sys.path.insert(0, '../lib')
import conll

debug=3
debug=False


def NNAAMMEE(tree):
	"""
	NNAAMMEE -> XXX
	"""
	for i, node in tree.iteritems():
		if node["t"]=="XXX":
			node["t"]="YYY"
			node["lemma"]="YYY"
		elif node["t"]=="NNAAMMEE":
			node["t"]="XXX"
			node["lemma"]="XXX"
	return tree


def removeTag2(tree):
	for i, node in tree.iteritems():
		del node["tag2"]
	

def emptyErrors(tree):
	for i, node in tree.iteritems():
		if node["t"]=="":
			if not node["gov"]: 
				pprint(tree)
				print i,node
				print "_______________"
				
			else:
				gi,gf = node["gov"].items()[0]
				for ci,cf in node["children"].iteritems():#normalement un seul enfant
					cnode=tree[ci]
					cnode["gov"]={gi:gf}
			tree=splice(tree,i,{})
			addinfototree(tree)
			tree=emptyErrors(tree)
			break
			#print i, "removed empty node in"
			#pprint(tree)
	return tree
	
def kesk(tree):
	for i, node in tree.iteritems():
		if node["lemma"]=="est-ce":
			gi,gf = node["gov"].items()[0]
			if gi in tree and tree[gi]["lemma"]=="que": # qu'est-ce que
				govnode=tree[gi]
				#print
				#print i-1,tree[i-1]["t"],govnode
				#print i,tree[i]["t"],node
				#print i+1,tree[i+1]["t"], tree[i+1]
				#print i+2,tree[i+2]["t"], tree[i+2]
				#print "_______________"
				
				gov={}
				for ii in [i-1,i,i+1]: # for each node of qu'est-ce que
					ggnode=tree[ii]
					for ggi,ggf in ggnode["gov"].iteritems(): # collect each exterior governor
						if ggi<i-1 or i+1<ggi:
							gov[ggi]=ggf
					for ci,cf in ggnode["children"].iteritems():# change each exterior kid
						if ci<i-1 or i+1<ci:
							cnode=tree[ci]
							cnode["gov"]={gi:cf}
				if i+2 in tree and tree[i+2]["lemma"][0] in "aeiouy" and tree[i+2]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					t=u"qu'est-ce qu'"
					#pprint(tree)
					#print i
					#if  tree[i+2]["lemma"] not in ["on","un"] : azer
				else:
					t=u"qu'est-ce que"
				tree[gi]={'lemma': u"qu'est-ce que", 'tag': u'CSU', 't':t , 'gov': gov, 'id': gi}
				tree=splice(tree,i,{})
				tree=splice(tree,i,{})
				addinfototree(tree)
				tree=kesk(tree)
				break
				#print "***"
				#pprint(tree)
			else: 
				#print
				##print i-1,tree[i-1]["t"],govnode
				#print i,tree[i]["t"],node
				#print i+1,tree[i+1]["t"], tree[i+1]
				#print i+2,tree[i+2]["t"], tree[i+2]
				#print "_______________"
				gov={}
				for ii in [i,i+1]: # for each node of est-ce que
					ggnode=tree[ii]
					for ggi,ggf in ggnode["gov"].iteritems(): # collect each exterior governor
						if ggi<i or i+1<ggi:
							gov[ggi]=ggf
					for ci,cf in ggnode["children"].iteritems():# change each exterior kid
						if ci<i or i+1<ci:
							cnode=tree[ci]
							cnode["gov"]={i:cf}
				if i+2 in tree and tree[i+2]["lemma"][0] in "aeiouy" and tree[i+2]["lemma"] not in ["euh","est-ce",u"est-ce que",u"est-ce-que"]:
					t=u"est-ce qu'"
					#pprint(tree)
					#print i
					#if  tree[i+2]["lemma"] not in ["on","un"] : qsdf
				else:
					t=u"est-ce que"
				tree[i]={'lemma': u"est-ce que", 'tag': u'CSU', 't':t , 'gov': gov, 'id': gi}
				tree=splice(tree,i+1,{})
				addinfototree(tree)
				tree=kesk(tree)
				break
	return tree


def rendezvous(tree):
	#z=False
	for i, node in tree.iteritems():
		if node["lemma"]=="rendez-vous" and i+1 in tree and tree[i+1]["t"]=="-vous":
			node["t"]="rendez-vous"
			tree=splice(tree,i+1,{})
			addinfototree(tree)
			pprint(tree)
			print "******"
			tree=rendezvous(tree)
			#z=True
			break
	#if z:
		#print "mmmmm"
		#pprint(tree)
		#print "nnnnnnnnnnnnn"
	return tree
		
def cls(tree):
	for i, node in tree.iteritems():
		if node["tag"]=="CLS":
			gi,gf = node["gov"].items()[0]
			if gi in tree and tree[gi]["tag"]!="VRB" and gf not in ["para","disflink"]: # CLS whose governor is not a verb
				govnode=tree[gi]
				
				pprint(tree)
				print "******",i
			
			
	return tree


def desEnTete(tree):
	"""
	des est toujours tÃªte comme une prÃ©position
	"""
	for i, node in tree.iteritems():
		if node["tag"] == "DET" and node["t"] in ["des", "du", "de", "d'"]:
			if node["gov"]:
				govid = node["gov"].keys()[0]
				if govid in tree:
					govnode=tree[govid]
					if govnode["gov"]:
						ggovid = govnode["gov"].keys()[0]
						node["gov"]={ggovid:govnode.get("gov",{}).get(ggovid,0)}
						govnode["gov"]={i:"dep"}
		node["tag"] == "PRE"			
	return tree


#V disfl-> des Ã  -->  comp
#V disfl->  "du", "de", "d'" --> comp
#V disfl-> 

def auxEnTete(tree):
	"""
	aux est la tÃªte
	"""
	for i, node in tree.iteritems():
		if node["gov"] and node["gov"].values()[0] in ["aux"] and node["gov"].keys()[0]>i: # found a node that is a dependent aux
			#print "found a node that is a dependent aux",i,node
			gi = node["gov"].keys()[0]
			gnode=tree[gi]
			if gnode["gov"] and gnode["gov"].values()[0] in ["aux"] and gnode["gov"].keys()[0]>gi: # found a double aux
				#print "found a double aux",gi,node
				ggi = gnode["gov"].keys()[0]
				ggnode=tree[ggi]
				for ci,cf in ggnode["children"].iteritems():
					if cf in ["suj","periph","insert","mark"]: # toutes ces fonctions montent sur 
						cnode=tree[ci]
						cnode["gov"]={i:cf}
					elif cf=="dm" and ci<i: # dm seulement quand ils sont Ã  gauche de l'auxiliaire
						cnode=tree[ci]	
						cnode["gov"]={i:"dm"}
					elif cf=="ad":
						cnode=tree[ci]
						if cnode["t"] in ["ne","n'"]:
							cnode["gov"]={i:cf}
				node["gov"]=ggnode["gov"].copy() 
				gnode["gov"]={i:"aux"}
				ggnode["gov"]={gi:"aux"}
			else: # only simple aux
				for ci,cf in gnode["children"].iteritems():
					if cf in ["suj","periph","insert","mark"]: # toutes ces fonctions montent sur 
						cnode=tree[ci]
						cnode["gov"]={i:cf}
					elif cf=="dm" and ci<i: # dm seulement quand ils sont Ã  gauche de l'auxiliaire
						cnode=tree[ci]	
						cnode["gov"]={i:"dm"}
					elif cf=="ad":
						cnode=tree[ci]
						if cnode["t"] in ["ne","n'"]:
							cnode["gov"]={i:cf}
				node["gov"]=gnode["gov"].copy()
				gnode["gov"]={i:"aux"}
			
			addinfototree(tree)
	return  tree		
			#pprint(tree)
			
from searchConll import  hasEmptyNode
			
def platinum(tree):
	removeTag2(tree)
	addinfototree(tree)
	tree=auxEnTete(tree)
	tree=desEnTete(tree)
	tree=NNAAMMEE(tree)

	tree=emptyErrors(tree)
	if hasEmptyNode(tree):
		print tree
		qsdf
	tree=kesk(tree)
	tree=rendezvous(tree)
	tree=cls(tree)
	return tree


def pprint(tree):
	for i in tree:
		print i, tree[i]["t"],tree[i]
	print


def splice(sentencedic,nodenum,intdic,copiedfeatures=['t','token','lemma','cat','tag','beg','end']):
		"""
		integrates the intdic into sentencedic at position nodenum
		an empty intdic kicks out the node in a proper manner (other dependencies are still ok)

		format of intdic:
			indices start with 'i', for example 'i2'
			last node that carries 'child' feature will receive the children of the former nodenum node
			last node that has governor i0 will receive the governors of the former nodenum node
			if no node has a 'i0' governor, the first node will receive the governors
			WATCH OUT: the ix will be ordered. so i10 will be before i9!!!
			example pour est-ce, remplacÃ© par ce sous-arbre :
			{
				'i1':{ u'person': u'3', u'number': u'sg', u'cat': u'V', u'lemma': u'\xeatre', u'token': u'est', u'tense': u'present', u'mode': u'indicative', 'gov': {'i0':'root'}, u't': u'A', 'child':None},
				'i2':{u'cat': u'Cl', u'lemma': u'c', u'token': u'-ce', u't': u'B', 'gov': {'i1': u'sub'}}
			}

		gives back new sentencedic

		"""
		intgov,intchi=None,None
		if debug>2: 
			print "================== integrating"
			for i in sorted(intdic):
				print i,intdic[i]
			print "into ((((((((((((("
			for i in sorted(sentencedic):
				print i,sentencedic[i].get("t","-"),sentencedic[i]
			print "))))))))))))))) at ",nodenum
		#try:
		l = [0]+sorted(sentencedic)[:nodenum-1]+sorted(intdic)+sorted(sentencedic)[nodenum:]
		if debug>2: print "lllll",l


		for i in sorted(intdic):

			sentencedic[i]=sentencedic[nodenum].copy() # l'ancien noeud qui va Ãªtre virer est copier pour chaque ix
			for at in copiedfeatures:
				if at in intdic[i]:
					sentencedic[i][at]=intdic[i][at]
			sentencedic[i]['gov']=intdic[i].get('gov',{})

			if 'i0' in intdic[i].get('gov',{}).keys(): # found the gov: the one that has a root link
				sentencedic[i]['gov']=sentencedic[nodenum]['gov']
				intgov=i
			if "child" in intdic[i]: # found the child bearer
				intchi = i
				del intdic[i]['child']
		if not intgov:# if no governor is indicated, i guess it's the first word
			intgov="i1"
			if intgov in sentencedic: sentencedic[intgov]['gov']=sentencedic[nodenum]['gov']
		if not intchi: intchi=intgov

		# correct wrong gov links
		for i in sorted(sentencedic):
			if nodenum in sentencedic[i]['gov'].keys():
				sentencedic[i]['gov'][intchi]=sentencedic[i]['gov'][nodenum]
				del sentencedic[i]['gov'][nodenum]

		# bring all in a new list with new indeces based on l
		newsentencedic={}
		for newi,oldi in enumerate(l):
			if newi:# not 0
				newsentencedic[newi]=sentencedic[oldi]
				govdic={}
				for govi,dep in sentencedic[oldi]['gov'].iteritems():
					if govi>=0 and govi in l: govdic[l.index(govi)]=dep
				newsentencedic[newi]['gov']=govdic
				newsentencedic[newi]['id']=newi
				#print type(newsentencedic)


		if debug>2:
			print "result:((((((((((((("
			for i in sorted(newsentencedic):
				print i,newsentencedic[i].get("t",""),newsentencedic[i]
			print ")))))))))))))))"

		return newsentencedic


def addinfototree(tree, reset=True):
	"""
	add to each node:
	children: dico: childi -> func 
	govtags: dico: tag of governor --> [(govi,func), ...]
	"""
	if reset:
		for i,node in tree.items():
			tree[i]["children"]={}
			tree[i]["govtags"]={}
	
	
	rootindeces=[]

	for i,node in tree.items(): # getting some basic additional features: children and govtags
		tree[i]["children"]=tree[i].get("children",{})
		tree[i]["govtags"]={}
		
		for j,func in node["gov"].items():
			if j in tree: # in tree: has governor j
				tree[j]["children"]=tree[j].get("children",{})
				tree[j]["children"][i]=func
				tree[i]["govtags"][tree[j]["tag"]]=tree[i]["govtags"].get(tree[j]["tag"],[])+[ (j,func) ]
			else: # not in tree: roots
				rootindeces+=[i]
	return rootindeces

def fixOutname(name):
	if name.startswith("validated."): 
		name=name.split(".")[1]
		#name=name[len("validated."):]
	if name.endswith("lastHuman.conll"): name=name[:-len(".lastHuman.conll")]
	if name.endswith(".conll"): name=name[:-len(".conll")]
	return name

def createNonExistingFolders(path):
	head, tail = os.path.split(path)
	if head=="":
		if tail and not os.path.exists(tail): os.makedirs(tail)
	else:
		createNonExistingFolders(head)
	if head and not os.path.exists(head): os.makedirs(head)

def transform(infolder,outfolder,mixOldNew=True):
	createNonExistingFolders(outfolder)
	#for infile in sorted(glob.glob(os.path.join(infolder,"test.conll"))):
	for infile in sorted(glob.glob(os.path.join(infolder,"*.conll"))):
		basename=os.path.basename(infile)
		print "reading",basename
		trees = conll.conllFile2trees(infile)
		newtrees=[]
		for tree in trees:
			if mixOldNew: newtrees+=[tree]
			newtree=copy.deepcopy(tree)
			newtree=platinum(newtree)
			newtrees+=[newtree]
		conll.trees2conllFile(newtrees,os.path.join(outfolder,fixOutname(basename)))
		
			
transform("projects/OrfeoGold2016/export/","projects/OrfeoGold2016/platinum/",mixOldNew=False)		 
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2012 Kim Gerdes
# kim AT gerdes. fr
# http://arborator.ilpga.fr/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 3 of the GNU Affero General Public License (the "License")
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of of version 3 of the GNU Affero General Public License
# from http://www.gnu.org/licenses/agpl-3.0.html 
# For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####

import cgi, os, shutil, codecs, re, sys, cgitb

from lib import config

#print "Content-Type: application/json\n"
cgitb.enable()
print "Content-Type: text/html\r\n\r\nxxxxxxxx"
method = os.environ.get( 'REQUEST_METHOD' )
if method == "POST":
	form = cgi.FieldStorage()
	project=form.getvalue("project",None).decode("utf-8")
	filetype=form.getvalue("type",None)
	analyze=form.getvalue("analyze",None)
	filecontent=form.getvalue("files[]")
	thefile = form["files[]"].filename.decode("utf-8")
	
	
	if filetype=="conll":
		ns=1
		conll=None
		for li in filecontent.split("\n"):
			if not li.strip():ns+=1
			elif not conll and "\t" in li:
				n = len(li.split("\t"))
				#print n,li.encode("utf-8"),"<br>"
				if n==4:conll=4
				if n==10:conll=10
				elif n==13:conll=10 # orfeo file format: conll10 + begin, end, speaker
				elif n==14:conll=14
		if not li.strip():ns-=1 # if last line empty, we counted one too much
		#if not conll:print "???",filename,"strange datatype<br>"
		
		
		
		
		# TODO: upload XML
		if conll:
		
			foutname=os.path.join(config.configProject(project)["configuration"]["corpusfolder"]+"/conll",thefile) # 
			out = open(foutname, "wb")
			out.write(filecontent)
			out.close()
		else:
			print "Content-Type: text/html\r\n\r\nProblem analyzing the CoNLL file. Are you sure the file {fn} is in CoNLL format?".format(fn=thefile)
			sys.exit()
	elif filetype=="standard":
		conll=10
		foutname=os.path.join(config.configProject(project)["configuration"]["corpusfolder"]+"/conll",thefile+".conll10") # 
		out = codecs.open(foutname, "w","utf-8")
		ns=0
		if analyze=='english':
	
			from pattern.en import parse
			
			reslash=re.compile("\/",re.U+re.I+re.M)
			reslashinv=re.compile("&slash;",re.U+re.I+re.M)
			reponct=re.compile(ur"((</span>)? (<span class='\w+'>)|(</span>) (<span class='\w+'>)?| )(?P<punct>[.,;()])",re.U+re.I+re.M)
			
			filecontent = reslash.sub("&slash;",filecontent)
			
			pc = parse(filecontent, tokenize=True, tags=True, chunks=True, relations=True, lemmata=True, light=False)
			
			
			
			for li in pc.split("\n"):
				#print li
				li=li.strip()
				if li:
					for nb,t in enumerate(li.split()):
						w,cat,ch,vp,php,l = t.split("/")
						out.write("\t".join([str(nb+1), reslashinv.sub("/",w), reslashinv.sub("/",l), cat, "_", "_","_", "_", "_", "_"])+"\n")
						
					out.write("\n")
					ns+=1						
						
		elif analyze=='chinese':
			from lib.parse import zhparse
			temp=open("corpus/zh.utf-8","wb")
			temp.write(filecontent)
			temp.close()
			nb=1
			nix=True # avoiding multiple empty lines
			for (cat,w) in zhparse("corpus/zh.utf-8"):
				if w:
					out.write("\t".join([str(nb), w, w, cat, "_", "_","_", "_", "_", "_"])+"\n")
					nb+=1
					nix=False
				else:
					if not nix:
						out.write("\n")
						ns+=1	
					nb=1
					nix=True
		
		elif analyze=='french':
			from tools import parseSentences
			temp=open("corpus/upload.fr.txt","wb")
			temp.write(filecontent)
			temp.close()
			
			parsedfile = parseSentences.textToParseAll(os.path.abspath("corpus/upload.fr.txt"))
			
			os.rename(parsedfile, foutname)
			
			
		
		
		elif analyze=='no':
			renl=re.compile("\n+",re.U+re.M)
			rew=re.compile("(\W+)",re.U+re.M)
			for line in renl.split(filecontent.strip()):
				nb=0
				for t in rew.split(line.strip()):
					t=t.strip()
					if t:
						nb+=1
						out.write("\t".join([str(nb), t, t, "_", "_", "_","_", "_", "_", "_"])+"\n")
						
			
				out.write("\n")
			
			
		#markuptext = reponct.sub(r"\g<punct>",reslashinv.sub("/",markuptext))
	else: # filetype=sentline
			conll=10
			foutname=os.path.join(config.configProject(project)["configuration"]["corpusfolder"]+"/conll",thefile+".conll10").encode("utf-8") # 
			ns=0
			out = codecs.open(foutname, "w","utf-8")
			
			if analyze=='chinese':
				from lib.parse import zhparse
				temp=open("corpus/zh.utf-8","wb")
				temp.write(filecontent)
				temp.close()
				nb=1
				nix=True # avoiding multiple empty lines
				for (cat,w) in zhparse("corpus/zh.utf-8"):
					if w:
						out.write("\t".join([str(nb), w, w, cat, "_", "_","_", "_", "_", "_"])+"\n")
						nb+=1
						nix=False
					else:
						if not nix:
							out.write("\n")
							ns+=1	
						nb=1
						nix=True
			else:
				nl=re.compile(ur"\s*\r?\n+\r?\s*",re.U+re.M+re.I)
				#wsplit=re.compile(ur"(\W+)",re.U+re.M+re.I) # to be used for untokenized text
				wsplit=re.compile(ur"\s+",re.U+re.M+re.I)
				space=re.compile(ur"^\s*$",re.U+re.M+re.I)
				for li in nl.split(filecontent.decode("utf-8")):
					#out.write("___"+repr(wsplit.split(li))+"___\n")
					
					nb=1
					for t in wsplit.split(li.strip()):
						t=t.strip()
						if space.match(t):continue
						out.write("\t".join([str(nb), t, t, "?", "_", "_","_", "_", "_", "_"])+"\n")
						nb+=1
					out.write("\n")	
						
			out.close()	
		#print "Content-Type: text/json\r\n\r\n{}"
		#sys.exit(1)
	
	filesize=os.path.getsize(foutname)

	filewithpath="corpus/conll/"+thefile
	thumbnail="images/conll"+str(conll)+".png"
	
	#print "Content-Type: application/json\r\n\r\n"
	#print "Content-Type: text/plain\n\n"
	#print "Content-Type: application/json\n\n"
	#print thefile.encode("utf-8")
	print (''' {"files":[{"name":"'''+thefile+'''",	
	"id":"'''+filewithpath+'''",
	"filetype":"'''+"conll"+str(conll)+'''",
	"size":'''+str(filesize)+''',
	"sentences":'''+str(ns)+''',
	"url":"'''+config.configProject(project)["configuration"]["url"]+filewithpath+'''",
	"thumbnail":"'''+thumbnail+'''"
	}]}''').encode("utf-8")
	
	
  
else:
	print "Content-Type: text/html\r\n\r\nxxxxxxxx"
#!/usr/bin/python
# -*- coding: utf-8 -*-

####
# Copyright (C) 2009-2012 Kim Gerdes
# kim AT gerdes. fr
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
# See the GNU General Public License (www.gnu.org) for more details.
#
# You can retrieve a copy of the GNU General Public License
# from http://www.gnu.org/.  For a copy via US Mail, write to the
#     Free Software Foundation, Inc.
#     59 Temple Place - Suite 330,
#     Boston, MA  02111-1307
#     USA
####


import os, cgitb, cgi, time, sys, glob, codecs
sys.path.append('modules')
from logintools import login
from logintools import isloggedin
from logintools import logout
from lib import config


verbose=False
######################### functions

def sizeofFile(num):
    for x in ['bytes','KB','MB','GB','TB']:
        if num < 1024.0:
            return "%3.1f%s" % (num, x)
        num /= 1024.0


##############################################"




########################## default values

##########################

cgitb.enable()
form = cgi.FieldStorage()
project = form.getvalue("project",None)
if project: project =project.decode("utf-8")
if not project:
	try:project = config.projects[0]
	except:print "something seriously wrong: can't find any projects"
projectconfig = config.configProject(project) # read in the settings of the current project
cachedir = projectconfig["configuration"]["corpusfolder"]+"/"

#thisscript = os.environ.get('SCRIPT_NAME', '')

thisfile = os.environ.get('SCRIPT_NAME',".")
#.split("/")[-1]

action = None
userdir = 'users/'
#action, userconfig = login(form, userdir, thisfile, action)
#adminLevel,username = int(userconfig["admin"]),userconfig["username"]
#userid = xmlsqlite.username2userid(username)
#adminLevel,username = 0,"guest"

test = isloggedin("users/")
logint = form.getvalue("login",None)
if logint == "logout":
	cookieheader = logout(userdir)
	print cookieheader
else: cookieheader="rien"

print "Content-Type: text/html\n" # blank line: end of headers
#<script type="text/javascript" src="script/raphael.js"></script>

######################   head    ###########################


print """
<html>
<head><title>Arborator: File uploader</title>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<script type="text/javascript" src="script/jquery.js"></script>
<script type="text/javascript" src="script/jquery-ui-1.8.18.custom.min.js"></script>
<script type="text/javascript" src="script/jquery.fileupload-ui.js"></script>
<script type="text/javascript" src="script/jquery.fileupload.js"></script>
<link href="css/arborator.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.8.18.custom.css" media="screen" />
<link rel="stylesheet" type="text/css" href="css/jquery.fileupload-ui.css" media="screen" />


<script type="text/javascript"></script>
</head>
"""


########################################### body #####################################
print '<body id="body">'
print """<div id="navigation" style="width:100%;margin:0px;border:0px;" class="arbortitle  ui-widget-header ui-helper-clearfix">
		<a href='.' style='position: fixed;left:1px;top:1px'><img src="images/arboratorNano.png" border="0"></a>
		<a href='project.cgi?project={project}' style='position: fixed;left:120px;top:5px'>{project} Annotation Project</a>
			<span style='margin:5 auto;position: relative;top:5px;' id='sentinfo'>Uploader</span>
		</div>
		<div id="center" class="center" style="width:100%;font-size:1em">
		""".format(project=project.encode("utf-8"))


################################ reactions after clicks/uploads



#####################################  printing always:


print """
<form method="post" action="viewer.cgi" style="display: none;" id="fileform" >
        <input type="hidden" id="filename" name="filename" value="">
        <input type="hidden" id="filetype" name="filetype" value="">
        <input type="hidden" id="project" name="project" value="{project}">
</form>""".format(project=project.encode("utf-8"))

print """<div class="ui-widget ui-widget-content ui-corner-all box">
<div id="file_upload" class="file_upload">
	<table id="files">
	<tbody>"""
	
print """	<tr>
	<div class="ui-widget ui-widget-content ui-corner-all box">	<h3> Upload analysis (CoNLL, text, ...)</h3>
		<form enctype="multipart/form-data" method="POST" action="uploadFile.cgi" class="file_upload">
			<input type="hidden" id="project" name="project" value="{project}">
			<input type="file" multiple="" name="files[]">
			<button type="submit">Upload</button>
			
			<br/><br/>
		
	
			<label><input name="type" type="radio" checked="checked" value="conll">Analysis Conll style</label>
			<label><input name="type" type="radio" value="sentline">A sentence per line</label>
			<label><input name="type" type="radio" value="standard" id="standardtext">Standard text:</label>
			
			<label class="standardtext"><input name="analyze" type="radio" value="no" class="standardtext">Don't analyze</label>
			<label class="standardtext"><input name="analyze" type="radio" value="english" class="standardtext">Analyze English</label>
			<label class="standardtext"><input name="analyze" type="radio" value="chinese" class="standardtext">Analyze Chinese</label>
			<label class="standardtext"><input name="analyze" type="radio" value="french" class="standardtext">Analyze French</label>
		</form>
	</div>	
</tr>
		
	
""".format(project=project.encode("utf-8"))
	
for infile in sorted(glob.glob(os.path.join("corpus/conll/", '*.*'))): #  os.path.join(foldername, '*.*')
	filename=os.path.basename(infile).decode("utf-8")
	try:
		f=codecs.open(infile,"r","utf-8")
	except Exception as e: 
		print "can't read",filename,"!\n <br>",e,"<br><br>"
		continue
	ns=1
	conll=None
	try:
		for li in f:
			if not li.strip():ns+=1
			elif not conll and "\t" in li:
				n = len(li.split("\t"))
				#print n,li.encode("utf-8"),"<br>"
				if n==4:	conll=4
				if n==10:	conll=10
				elif n==12:	conll=10 # naija file format: conll10 + begin, end
				elif n==14:	conll=14
		if not li.strip():ns-=1 # if last line empty, we counted one too much
	except:
		pass
	f.close()
	if conll:
		thumbnail="conll"+str(conll)+".png"
		filesize=sizeofFile(os.path.getsize(infile))
		
		
		href=projectconfig["configuration"]["url"]+u"corpus/conll/"+filename
		#print (u"Ã©{r}".format(r=href)).encode("utf-8")
		print (u"""
		<tr data-id="{filename}" data-type="{datatype}">
			<td class="file_download_preview"><a target="_blank" href="{href}"><img src="images/{thumbnail}"></a></td>
			<td class="file_name"><a target="_blank" title="Table view of the original file" href="{href}">{simplefilename}</a></td>
			<td class="file_size">{size}</td>
			<td class="file_sentencenumber" colspan="2">{numbersentences} sentences.</td>
			<td class="file_view"><button onclick="enterDB(this)" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only" role="button" aria-disabled="false" title="Add to the database" style="height: 1.4em;width: 1.4em;"><span class="ui-button-icon-primary ui-icon ui-icon-upload"></span></button></td>
		</tr>""".format(filename=infile.decode("utf-8") ,datatype="conll"+str(conll) ,simplefilename=filename, href=href , size=filesize , thumbnail=thumbnail , numbersentences=ns )).encode("utf-8")
		

	else:	print "<tr>???",filename.encode("utf-8"),"couldn't read or strange datatype<br></tr>"
	#<td class="file_view"><button onclick="viewer(this)" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only" role="button" aria-disabled="false" title="Graphic view of this file" style="height: 1.4em;width: 1.4em;"><span class="ui-button-icon-primary ui-icon ui-icon-image"></span></button></td>

	# TODO: implement trashing files:
	#<td class="file_view"><button onclick="trash(this)" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only" role="button" aria-disabled="false" title="Trash the file" style="height: 1.4em;width: 1.4em;"><span class="ui-button-icon-primary ui-icon ui-icon-trash"></span><span class="ui-button-text">Start</span></button></td>
		
		
print """

        <tr style="display: none;" class="file_upload_template">
            <td class="file_upload_preview"></td>
            <td class="file_name"></td>
            <td class="file_size"></td>
            <td class="file_upload_progress"><div></div></td>
            <td class="file_upload_start"><button>Start</button></td>
            <td class="file_upload_cancel"><button>Cancel</button></td>
        </tr>
        <tr style="display: none;" class="file_download_template">
            <td class="file_download_preview"></td>
            <td class="file_name"><a></a></td>
            <td class="file_size"></td>
            <td class="file_sentencenumber"  colspan="2"></td>
            <td class="file_import"><button style="height: 1.4em;width: 1.4em;" title="Graphic view of this file" aria-disabled="false" role="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only" onclick="viewer(this)"><span class="ui-button-icon-primary ui-icon ui-icon-image"></span><span class="ui-button-text">Start</span></button></td>
            <td class="file_view"><button style="height: 1.4em;width: 1.4em;" title="Add to the database" aria-disabled="false" role="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only" onclick="enterDB(this)"><span class="ui-button-icon-primary ui-icon ui-icon-upload"></span><span class="ui-button-text">Start</span></button></td>

        </tr>
    </tbody>
    </table>
<script type="text/javascript" src="script/upload.js"></script>
"""
#<td colspan="2" class="file_sentencenumber"></td>






print "</div></div>"














if test != False and logint != "logout":
	userconfig, cookieheader = test
	print '<div class="arborfoot fg-toolbar ui-widget-header ui-helper-clearfix">'
	print "logged in as",userconfig["realname"]
	print '<a href="'+thisfile+'?login=editaccount">edit the account '+userconfig["username"]+'</a>'
	print '<a href="'+thisfile+'?login=logout">logout</a>'
	if userconfig["username"] == "admin":print '<a href="'+thisfile+'?login=admin">Administration</a>'
	print "</div>"
	
print '</body></html>'










admin = 5# admin level, above 0 means user has admin rights 
password = V7X__-faTZhKAp-XRjwzHnl6_# 'pass1'
max-age = 0# 0 means no limit, otherwise is max-age of cookie in seconds (since last login)
realname = Administrator
username = admin
email = kim@gerdes.fr
lastused = 1503403001.21# Time the login was last used
created = 0# Time the login was created
numlogins = 69# Number of times the user has logged in - if max-age is 0 this will probably be the number of user sessions
editable = Yes# Can the user edit their name, email and password ?
numused = 956# Number of protected pages accessed by this user....
action = project_Rhapsodie
login = doeditaccountnojs
templatedir = 'logintemplates/'             # path to the login template directory (absolute or relatve from script *calling* login.py)
cookiepath=''                # the 'super-url' of the scripts - for the cookie. Can be '' if no other script in your domain uses cookies
adminuser='admin'                           # the login name who is the *main* administrator account. This one cannot be deleted.

# login page
newloginlink='Yes'                          # Do you want a link to the 'create new user' page on your login page ?
                                            # saying no means only the admin can create new user (using the create/invite feature)
adminmail = kim@gerdes.fr# This address will be mailed whenever a new user is confirmed - set to '' to switch off

email_subject = New Arborator Login# The subject line for emails sent when a new login is created
email_message = '''You have signed up for, or been invited to join the Arborator community.
http://arborator.ilpga.fr/
Clicking on the link at the end of this message will confirm your new login.
_________________________________________________
Vous vous Ãªtes inscrit(e) ou vous avez Ã©tÃ© invitÃ©(e) Ã  rejoindre la communautÃ© Arborator.
http://arborator.ilpga.fr/
Vous confirmez votre inscription en cliquant sur le lien ci-dessous.

'''# The message sent for new logins   


# The values of this that are editable from the admin login are :
# newloginlink, adminmail, email_subject, email_message
# This contains default values that aren't currently collected from the user
# The two relevant ones are 'admin' and 'max-age'

admin=0                                             # admin level, currently should be 0 or 3 
password=''                                         # An obfuscated version of the password
max-age=0                                           # 0 means no limit, otherwise is max-age of cookie in seconds (since last login)
email=''                                            # email address
realname=''                                         # real name
username=''                                     # should be the same as the filename ! Here for convenience
lastused=0                                      # Time the login was last used
created=0                                       # Time the login was created
numlogins=0                                     # Number of times the user has logged in - if max-age is 0 this will probably be the number of user sessions
editable='Yes'                                  # Can the user edit their name, email and password ?
numused=0                                       # Number of protected pages accessed by this user....
admin = 0# admin level, currently should be 0 or 3 
password = JmX__-jszkjWElbv3bh75ivXE2DjVn1T_# 'pass1'
max-age = 0# 0 means no limit, otherwise is max-age of cookie in seconds (since last login)
realname = Guest User
username = guest
email = guest@nowhere.com
lastused = 1474581220.93# Time the login was last used
created = 0# Time the login was created
numlogins = 18# Number of times the user has logged in - if max-age is 0 this will probably be the number of user sessions
editable = No# Can the user edit their name, email and password ?
numused = 93# Number of protected pages accessed by this user....
#!/usr/bin/python
# -*- coding: utf-8 -*-

import cgi,sys,codecs
from os import environ
sys.path.append('modules')
from logintools import isloggedin

test = isloggedin("users/")
if not test:sys.exit()
admin = int(test[0]["admin"])

method = environ.get( 'REQUEST_METHOD' )
if method == "POST" : #or True
	data = cgi.FieldStorage()
	
	project = data.getvalue("project",None).decode("utf-8")
	#project = data.getlist("project")
	svg = data.getvalue("svg",None)
	treeid = data.getvalue("treeid",None)
	
	if treeid:
		svgfile=codecs.open("svg/"+project+"."+treeid,"w","utf-8")
		svgfile.write(svg)
		svgfile.close()
	
	
<blink><br><br><br><br><div class="ui-state-error ui-corner-all" style="padding:10;text-align:center;margin:50;">Achtung!<br> The site is currently under maintenance! Please come back tomorrow.</div></blink>