changeset 1495:fb74ed5d8c22

initial commit of the Stone Web viewer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 25 Jun 2020 16:51:10 +0200
parents 5a3ef478deb6
children d450653b46d3
files StoneWebViewer/COPYING StoneWebViewer/Plugin/CMakeLists.txt StoneWebViewer/Plugin/OrthancExplorer.js StoneWebViewer/Plugin/Plugin.cpp StoneWebViewer/Resources/GenerateImages.py StoneWebViewer/Resources/NOTES.txt StoneWebViewer/Resources/Styles/_button.scss StoneWebViewer/Resources/Styles/_exitButton.scss StoneWebViewer/Resources/Styles/_helpers.scss StoneWebViewer/Resources/Styles/_layout.scss StoneWebViewer/Resources/Styles/_notice.scss StoneWebViewer/Resources/Styles/_print.scss StoneWebViewer/Resources/Styles/_selectionActionlist.scss StoneWebViewer/Resources/Styles/_serieslist.scss StoneWebViewer/Resources/Styles/_studyInformationBreadcrumb.scss StoneWebViewer/Resources/Styles/_studyIsland.scss StoneWebViewer/Resources/Styles/_toolbar.scss StoneWebViewer/Resources/Styles/_variable.scss StoneWebViewer/Resources/Styles/_video.scss StoneWebViewer/Resources/Styles/styles.scss StoneWebViewer/Resources/Styles/tb-group.scss StoneWebViewer/Resources/Styles/webviewer.components.scss StoneWebViewer/Resources/Styles/webviewer.main.scss StoneWebViewer/Resources/Styles/wv-disclaimer.scss StoneWebViewer/Resources/Styles/wv-loadingbar.scss StoneWebViewer/Resources/Styles/wv-overlay.scss StoneWebViewer/Resources/Styles/wv-pdf-viewer.scss StoneWebViewer/Resources/Styles/wv-splitpane.scss StoneWebViewer/Resources/Styles/wv-timeline-controls.scss StoneWebViewer/Resources/Styles/wv-timeline.scss StoneWebViewer/WebApplication/app.css StoneWebViewer/WebApplication/app.js StoneWebViewer/WebApplication/img/grid1x1.png StoneWebViewer/WebApplication/img/grid1x2.png StoneWebViewer/WebApplication/img/grid2x1.png StoneWebViewer/WebApplication/img/grid2x2.png StoneWebViewer/WebApplication/img/loading.gif StoneWebViewer/WebApplication/index.html StoneWebViewer/WebAssembly/CMakeLists.txt StoneWebViewer/WebAssembly/NOTES.txt StoneWebViewer/WebAssembly/ParseWebAssemblyExports.py StoneWebViewer/WebAssembly/Test.cpp StoneWebViewer/WebAssembly/docker-build.sh StoneWebViewer/WebAssembly/docker-internal.sh
diffstat 44 files changed, 14515 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/COPYING	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,661 @@
+                    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/>.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Plugin/CMakeLists.txt	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 2.8.3)
+
+project(StoneWebViewerPlugin)
+
+set(ORTHANC_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../orthanc CACHE PATH "")
+set(STONE_BINARIES CACHE PATH "")
+
+include(${ORTHANC_ROOT}/Resources/CMake/OrthancFrameworkParameters.cmake)
+set(ENABLE_MODULE_IMAGES OFF)
+set(ENABLE_MODULE_JOBS OFF)
+set(ENABLE_MODULE_DICOM OFF)
+include(${ORTHANC_ROOT}/Resources/CMake/OrthancFrameworkConfiguration.cmake)
+
+include_directories(
+  ${ORTHANC_ROOT}/Core
+  ${ORTHANC_ROOT}/Plugins/Samples/Common
+  ${ORTHANC_ROOT}/Plugins/Include  # TODO => Fix a version
+  )
+
+add_definitions(
+  -DHAS_ORTHANC_EXCEPTION=1
+  -DPLUGIN_VERSION="mainline"  # TODO
+  -DPLUGIN_NAME="stone-webviewer"
+  )
+
+EmbedResources(
+  ORTHANC_EXPLORER       ${CMAKE_SOURCE_DIR}/OrthancExplorer.js
+  STONE_WRAPPER          ${STONE_BINARIES}/stone.js
+  STONE_WEB_VIEWER_JS    ${STONE_BINARIES}/StoneWebViewer.js
+  STONE_WEB_VIEWER_WASM  ${STONE_BINARIES}/StoneWebViewer.wasm
+  WEB_APPLICATION        ${CMAKE_SOURCE_DIR}/../WebApplication
+  IMAGES                 ${STONE_BINARIES}/img/
+  )
+
+add_library(StoneWebViewer SHARED
+  Plugin.cpp
+  ${AUTOGENERATED_SOURCES}
+  ${ORTHANC_ROOT}/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
+  ${ORTHANC_CORE_SOURCES}
+  )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Plugin/OrthancExplorer.js	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,57 @@
+$('#study').live('pagebeforecreate', function() {
+  var b = $('<a>')
+      .attr('data-role', 'button')
+      .attr('href', '#')
+      .attr('data-icon', 'search')
+      .attr('data-theme', 'e')
+      .text('Stone Web Viewer');
+
+  b.insertBefore($('#study-delete').parent().parent());
+  b.click(function() {
+    if ($.mobile.pageData) {
+      $.ajax({
+        url: '../studies/' + $.mobile.pageData.uuid,
+        dataType: 'json',
+        cache: false,
+        success: function(study) {
+          var studyInstanceUid = study.MainDicomTags.StudyInstanceUID;
+          window.open('../stone-webviewer/index.html?study=' + studyInstanceUid);
+        }
+      });      
+    }
+  });
+});
+
+
+$('#series').live('pagebeforecreate', function() {
+  var b = $('<a>')
+      .attr('data-role', 'button')
+      .attr('href', '#')
+      .attr('data-icon', 'search')
+      .attr('data-theme', 'e')
+      .text('Stone Web Viewer');
+
+  b.insertBefore($('#series-delete').parent().parent());
+  b.click(function() {
+    if ($.mobile.pageData) {
+      $.ajax({
+        url: '../series/' + $.mobile.pageData.uuid,
+        dataType: 'json',
+        cache: false,
+        success: function(series) {
+          $.ajax({
+            url: '../studies/' + series.ParentStudy,
+            dataType: 'json',
+            cache: false,
+            success: function(study) {
+              var studyInstanceUid = study.MainDicomTags.StudyInstanceUID;
+              var seriesInstanceUid = series.MainDicomTags.SeriesInstanceUID;
+              window.open('../stone-webviewer/index.html?study=' + studyInstanceUid +
+                          '&series=' + seriesInstanceUid);
+            }
+          });      
+        }
+      });      
+    }
+  });
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Plugin/Plugin.cpp	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,235 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * 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/>.
+ **/
+
+
+#include <OrthancPluginCppWrapper.h>
+#include <EmbeddedResources.h>
+
+#include <SystemToolbox.h>
+#include <Toolbox.h>
+
+OrthancPluginErrorCode OnChangeCallback(OrthancPluginChangeType changeType,
+                                        OrthancPluginResourceType resourceType,
+                                        const char* resourceId)
+{
+  try
+  {
+    if (changeType == OrthancPluginChangeType_OrthancStarted)
+    {
+      Json::Value info;
+      if (!OrthancPlugins::RestApiGet(info, "/plugins/dicom-web", false))
+      {
+        throw Orthanc::OrthancException(
+          Orthanc::ErrorCode_InternalError,
+          "The Stone Web viewer requires the DICOMweb plugin to be installed");
+      }
+
+      if (info.type() != Json::objectValue ||
+          !info.isMember("ID") ||
+          !info.isMember("Version") ||
+          info["ID"].type() != Json::stringValue ||
+          info["Version"].type() != Json::stringValue ||
+          info["ID"].asString() != "dicom-web")
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                        "The DICOMweb plugin is not properly installed");
+      }
+
+      std::string version = info["Version"].asString();
+      if (version != "mainline")
+      {
+        std::vector<std::string> tokens;
+        Orthanc::Toolbox::TokenizeString(tokens, version, '.');
+        if (tokens.size() != 2)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                          "Bad version of the DICOMweb plugin: " + version);
+        }
+
+        int major, minor;
+        
+        try
+        {
+          major = boost::lexical_cast<int>(tokens[0]);
+          minor = boost::lexical_cast<int>(tokens[1]);
+        }
+        catch (boost::bad_lexical_cast&)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                          "Bad version of the DICOMweb plugin: " + version);
+        }
+
+        if (major <= 0 ||
+            (major == 1 && minor <= 1))
+        {
+          throw Orthanc::OrthancException(
+            Orthanc::ErrorCode_InternalError,
+            "The Stone Web viewer requires DICOMweb plugin with version >= 1.2, found: " + version);
+        }
+
+        if (major <= 0 ||
+            (major == 1 && minor == 2))
+        {
+          /**
+           * DICOMweb 1.3 is better than 1.2 for 2 reasons: (1)
+           * MONOCHROME1 images are not properly rendered in DICOMweb
+           * 1.2, and (2) DICOMweb 1.2 cannot transcode images (this
+           * causes issues on JPEG2k images).
+           **/
+          LOG(WARNING) << "The Stone Web viewer has some incompatibilities "
+                       << "with DICOMweb plugin 1.2, consider upgrading the DICOMweb plugin";
+        }
+      }
+    }
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "Exception: " << e.What();
+    return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
+  }
+
+  return OrthancPluginErrorCode_Success;
+}
+    
+
+template <enum Orthanc::EmbeddedResources::DirectoryResourceId folder>
+void ServeEmbeddedFolder(OrthancPluginRestOutput* output,
+                         const char* url,
+                         const OrthancPluginHttpRequest* request)
+{
+  OrthancPluginContext* context = OrthancPlugins::GetGlobalContext();
+
+  if (request->method != OrthancPluginHttpMethod_Get)
+  {
+    OrthancPluginSendMethodNotAllowed(context, output, "GET");
+  }
+  else
+  {
+    std::string path = "/" + std::string(request->groups[0]);
+    const char* mime = Orthanc::EnumerationToString(Orthanc::SystemToolbox::AutodetectMimeType(path));
+
+    std::string s;
+    Orthanc::EmbeddedResources::GetDirectoryResource(s, folder, path.c_str());
+
+    const char* resource = s.size() ? s.c_str() : NULL;
+    OrthancPluginAnswerBuffer(context, output, resource, s.size(), mime);
+  }
+}
+
+
+template <enum Orthanc::EmbeddedResources::FileResourceId file>
+void ServeEmbeddedFile(OrthancPluginRestOutput* output,
+                       const char* url,
+                       const OrthancPluginHttpRequest* request)
+{
+  OrthancPluginContext* context = OrthancPlugins::GetGlobalContext();
+
+  if (request->method != OrthancPluginHttpMethod_Get)
+  {
+    OrthancPluginSendMethodNotAllowed(context, output, "GET");
+  }
+  else
+  {
+    const char* mime = Orthanc::EnumerationToString(Orthanc::SystemToolbox::AutodetectMimeType(url));
+
+    std::string s;
+    Orthanc::EmbeddedResources::GetFileResource(s, file);
+
+    const char* resource = s.size() ? s.c_str() : NULL;
+    OrthancPluginAnswerBuffer(context, output, resource, s.size(), mime);
+  }
+}
+
+
+extern "C"
+{
+  ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
+  {
+    OrthancPlugins::SetGlobalContext(context);
+    Orthanc::Logging::InitializePluginContext(context);
+
+    /* Check the version of the Orthanc core */
+    if (OrthancPluginCheckVersion(context) == 0)
+    {
+      char info[1024];
+      sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin",
+              context->orthancVersion,
+              ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
+              ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER,
+              ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER);
+      OrthancPluginLogError(context, info);
+      return -1;
+    }
+
+    try
+    {
+      std::string explorer;
+      Orthanc::EmbeddedResources::GetFileResource(
+        explorer, Orthanc::EmbeddedResources::ORTHANC_EXPLORER);
+      OrthancPluginExtendOrthancExplorer(OrthancPlugins::GetGlobalContext(), explorer.c_str());
+      
+      OrthancPlugins::RegisterRestCallback
+        <ServeEmbeddedFile<Orthanc::EmbeddedResources::STONE_WEB_VIEWER_WASM> >
+        ("/stone-webviewer/StoneWebViewer.wasm", true);
+      
+      OrthancPlugins::RegisterRestCallback
+        <ServeEmbeddedFile<Orthanc::EmbeddedResources::STONE_WEB_VIEWER_JS> >
+        ("/stone-webviewer/StoneWebViewer.js", true);
+      
+      OrthancPlugins::RegisterRestCallback
+        <ServeEmbeddedFile<Orthanc::EmbeddedResources::STONE_WRAPPER> >
+        ("/stone-webviewer/stone.js", true);
+      
+      OrthancPlugins::RegisterRestCallback
+        <ServeEmbeddedFolder<Orthanc::EmbeddedResources::IMAGES> >
+        ("/stone-webviewer/img/(.*)", true);
+
+      OrthancPlugins::RegisterRestCallback
+        <ServeEmbeddedFolder<Orthanc::EmbeddedResources::WEB_APPLICATION> >
+        ("/stone-webviewer/(.*)", true);
+
+      OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback);
+    }
+    catch (...)
+    {
+      OrthancPlugins::LogError("Exception while initializing the Stone Web viewer plugin");
+      return -1;
+    }
+
+    return 0;
+  }
+
+
+  ORTHANC_PLUGINS_API void OrthancPluginFinalize()
+  {
+  }
+
+
+  ORTHANC_PLUGINS_API const char* OrthancPluginGetName()
+  {
+    return PLUGIN_NAME;
+  }
+
+
+  ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion()
+  {
+    return PLUGIN_VERSION;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/GenerateImages.py	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+
+# Stone of Orthanc
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017-2020 Osimis S.A., Belgium
+#
+# 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/>.
+
+
+import os
+from PIL import Image
+
+SOURCE = os.path.dirname(os.path.abspath(__file__))
+TARGET = os.path.join(SOURCE, '..', 'WebApplication', 'img')
+
+try:
+    os.makedirs(TARGET)
+except:  # Directory already exists
+    pass
+    
+color = (217, 217, 217, 255)
+border = 3
+width = 32
+height = 32
+
+
+
+image = Image.new('RGBA', (width, height))
+
+for x in range(0, width):
+    for y in range(0, height):
+        image.putpixel((x, y), color)
+
+image.save(os.path.join(TARGET, 'grid1x1.png'), 'PNG')
+
+
+
+image = Image.new('RGBA', (width, height))
+
+for x in range(0, width / 2 - border):
+    for y in range(0, height / 2 - border):
+        image.putpixel((x, y), color)
+    for y in range(height / 2 + border, height):
+        image.putpixel((x, y), color)
+
+for x in range(width / 2 + border, width):
+    for y in range(0, height / 2 - border):
+        image.putpixel((x, y), color)
+    for y in range(height / 2 + border, height):
+        image.putpixel((x, y), color)
+
+image.save(os.path.join(TARGET, 'grid2x2.png'), 'PNG')
+
+
+
+image = Image.new('RGBA', (width, height))
+
+for y in range(0, height):
+    for x in range(0, width / 2 - border):
+        image.putpixel((x, y), color)
+    for x in range(width / 2 + border, width):
+        image.putpixel((x, y), color)
+
+image.save(os.path.join(TARGET, 'grid2x1.png'), 'PNG')
+
+
+
+image = Image.new('RGBA', (width, height))
+
+for x in range(0, width):
+    for y in range(0, height / 2 - border):
+        image.putpixel((x, y), color)
+    for y in range(height / 2 + border, height):
+        image.putpixel((x, y), color)
+
+image.save(os.path.join(TARGET, 'grid1x2.png'), 'PNG')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/NOTES.txt	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,15 @@
+
+Origin of SCSS
+==============
+
+The "Styles" folder is a copy of:
+https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/styles/
+
+
+
+Generation of CSS from the SCSS
+===============================
+
+$ npm install node-sass
+$ ./node_modules/node-sass/bin/node-sass ./Styles/styles.scss > ../WebApplication/app.css
+$ ./GenerateImages.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_button.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,205 @@
+// clean icon buttons
+.wvButton {
+    // Remove <a> default styles. Take care - this class may either be used
+    // with <a> or <button>.
+    &:hover {
+        text-decoration: none;
+        color: white;
+    }
+
+    // Remove <button> default styles.
+    outline: none;
+    background-color: transparent;
+    border: none;
+    border-radius: 0;
+
+    // Set relative to position button absolutely
+    position: relative;
+
+    // Style button
+    display: inline-block;
+    cursor: pointer;
+    font-variant: small-caps;
+    text-transform: lowercase;
+    text-align: center;
+    font-size: 1.3rem;
+    font-weight: 400;
+    color: hsl(0, 0%, 85%);
+    transition: 0.3s text-decoration ease, 0.3s border ease, 0.3s opacity ease;
+
+    // Position button
+    margin: 0;
+    min-width: 3rem;
+    padding: 0 10px;
+    line-height: 3.6rem;
+    &.wvLargeButton{
+        font-size: 2rem;
+        line-height: 6.2rem;
+        padding: 0 20px;
+    }
+}
+
+.wvButton--rotate {
+    @extend .wvButton;
+    // Rotate only the icon
+    &:before, &:after{
+        transform: rotate(90deg);
+        display: inline-block;
+    }
+
+}
+
+.wvButton--vflip {
+    @extend .wvButton;
+    // flip only the icon
+    &:before, &:after{
+        transform: scaleX(-1);
+        display: inline-block;
+    }
+
+}
+
+// button w/ blue underline
+.wvButton--underline, .fa.wvButton--underline {
+    @extend .wvButton;
+
+    position: relative;
+
+    background-color:inherit;
+    text-decoration: none;
+    text-align:left;
+    font-size: 1.2rem;
+    &.wvLargeButton{
+        font-size: 2rem;
+        width: 6.4rem;
+    }
+    * {
+        pointer-events: none;
+    }
+    &:hover, &:active, &:focus{
+        outline:0;
+    }
+
+    width: 3.2rem;
+    vertical-align: middle;
+    color:white;
+    opacity: 0.75;
+    border:none;
+    border-bottom: 2px solid rgba(255,255,255,0.1);
+    &:hover, &:focus{
+        border-color: rgba(255,255,255,1);
+        opacity:1;
+        .wvButton__bottomTriangle{
+            border-left-color: rgba(255,255,255,1);
+            &.toggled{
+                // border-color: rgba(255, 255, 255, 1);
+            }
+        }
+    }
+    &.active{
+        opacity: 1;
+        border-color: $primary;
+    }
+
+    // Make sure the 2px border is not hidden by viewports and other parts of
+    // the layout (.glyphicon class sets top to 1px)
+    top: 0px;
+
+    // Compensate glyphicon whitespace
+    &::before {
+        position: relative;
+        top: -1px;
+    }
+
+    // Adapt font-awesome icon to glyphicon styles
+    &.fa {
+        top: 0px;
+        font-weight: 800;
+    }
+}
+
+.wvButton__bottomTriangle{
+    transition: 0.3s border ease, 0.3s opacity ease;
+
+    display:block;
+    position: absolute;
+    bottom:0;
+    left:0;
+
+    width: 0;
+    height: 0;
+    border-style: solid;
+    border-width: 10px 0 0 10px;
+    border-color: transparent transparent transparent rgba(255,255,255,0.1);
+    &.active{
+        border-color: transparent transparent transparent $primary !important;
+        &.toggled{
+            border-left-color: $primary !important;
+        }
+    }
+    &.toggled{
+        // border-width: 15px 0 0 15px;
+    }
+}
+
+// button w/ border
+.wvButton--border {
+    @extend .wvButton;
+
+    // Prevent multi line buttons.
+    max-height: calc(2.8rem - 3px);
+    max-width: 100%;
+    overflow: hidden;
+
+    // Set margin
+    margin: 0.6rem;
+    margin-left: 0rem;
+    margin-right: 0rem;
+    & + & {
+        margin-left: 0.7rem;
+    }
+
+    // Set button size
+    line-height: 2rem;
+
+    // Align text
+    padding-top: 0.1rem;
+    padding-bottom: 0.5rem;
+
+    // Style button
+    font-size: 1.4rem;
+    border: 1px solid hsl(0, 0%, 27%);
+
+    // Set best looking font with small-caps.
+    font-family: Arial;
+
+    // Change background on hover
+    background-color: hsl(0, 0%, 0%);
+    &:hover {
+        background-color: hsl(0, 0%, 10%);
+    }
+
+    & > .glyphicon { // used with the same element as glyphicons
+        // Position button
+        position: relative;
+        display: inline-block;
+        top: 3px;
+        margin-right: 4px;
+    }
+}
+
+// button w/ border + white modifier to use when the background is white.
+.wvButton--borderAndWhite {
+    @extend .wvButton--border;
+
+    // Text color
+    color: hsl(0, 0%, 10%);
+    border: 1px solid hsl(0, 0%, 73%);
+
+    // Change background on hover
+    background-color: hsl(0, 0%, 100%);
+    &:hover {
+        color: hsl(0, 0%, 10%);
+        background-color: hsl(0, 0%, 90%);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_exitButton.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,28 @@
+.wvExitButton {
+    margin-left: 1rem;
+    margin-top: .25rem;
+    font-size: 1.25em;
+    color: white;
+    opacity: .66;
+
+    transition: .3s opacity ease;
+    background-color: inherit;
+    border: none;
+    text-decoration: none;
+    text-align: left;
+    padding: 0;
+    cursor: pointer;
+    font-family: inherit;
+    line-height: inherit;
+
+    &:hover, &:focus {
+        opacity: 1;
+        outline: 0;
+    }
+}
+
+.wvExitButton__text {
+    // Align text with glyph-icon.
+    position: relative;
+    top: -1px;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_helpers.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,1115 @@
+/*
+ *  Source code taken from private Osimis' frontend toolbox 3.2.1.
+ */
+
+/**
+    _overlay.scss
+ */
+.overlay__transparent{
+    position:fixed;
+    top:0;
+    left:0;
+    width:100%;
+    height:100%;
+    z-index: 1;
+}
+
+/** _transition.scss **/
+.transition,
+%transition{
+    &{
+        transition: 0.3s all ease;
+    }
+}
+
+.transition--long,
+%transition--long{
+    &{
+        transition: 0.6s all ease;
+    }
+}
+
+
+/** _list.scss **/
+// This is used in the study information panel.
+dd+dt{
+    clear:both;
+}
+.listDefinition{
+    width:100%;
+    line-height: 1.3;
+}
+.listDefinition__term{
+    clear:both;
+    float:left;
+    text-align: right;
+    padding-right:10px;
+    width:50%;
+    @extend .font__light;
+
+}
+.listDefinition__data{
+    text-align: left;
+    padding-left:10px;
+    float:right;
+    width:50%;
+    @extend .font__normal;
+
+}
+
+
+/** _animation.scss **/
+@keyframes blink__primary {
+    0% { color: $primary; }
+    100% { color: $text-color; }
+}
+
+.blink__primary{
+  animation: blink__primary 0.8s linear infinite;
+}
+
+[translate-cloak]{
+    transition: 0.3s all ease;
+    opacity: 1;
+    &.translate-cloak{
+        opacity: 0;
+    }
+}
+
+/** _button.scss **/
+.button__unstyled,
+%button__unstyled{
+    background-color:inherit;
+    border:none;
+    text-decoration: none;
+    text-align:left;
+    padding:0;
+    cursor: pointer;
+    *{
+        pointer-events: none;
+    }
+    &:hover, &:active, &:focus{
+        outline:0;
+    }
+}
+.button__base,
+%button__base{
+    @extend .button__unstyled;
+    transition: 0.3s all ease;
+    //& *,& *:before,& *:after{
+    //  @extend .transition;
+    //}
+}
+.button__state--active{
+    @extend .button__base;
+    opacity:1;
+    &:hover{
+        opacity:0.9;
+        color:$primary;
+    }
+}
+.button__state--inactive{
+    @extend .button__base;
+    opacity:0.333;
+    &:hover{
+        opacity:0.4333;
+        color:$primary;
+    }
+}
+.button__iconed{
+    @extend .button__base;
+    opacity: 1;
+    &:hover, &:focus, &.active{
+        opacity: 0.75;
+        color:$primary;
+    }
+}
+.button__switch--base,
+%button__switch--base{
+    @extend .button__base;
+    padding:5px 0px;
+    display:inline-block;
+    text-align: center;
+    border-top: 1px solid;
+    border-bottom: 1px solid;
+    border-color: $primary;
+    background-color: white;
+    overflow: hidden;
+    &:hover, &:focus{
+        background-color: lighten($primary, 10);
+        color:white;
+    }
+    &.active{
+        background-color:$primary;
+        color:white;
+    }
+}
+.button__switch{
+    @extend .button__switch--base;
+}
+.button__switch--first{
+    @extend .button__switch--base;
+    border-left: 1px solid $primary;
+    border-radius: 5px 0 0 5px;
+}
+.button__switch--last{
+    @extend .button__switch--base;
+    border-right: 1px solid $primary;
+    border-radius: 0 5px 5px 0;
+}
+.button__lightgrey--hover{
+    @extend .button__base;
+    &:hover, &:focus, &.active{
+        background-color: $lightGrey;
+    }
+}
+.button__text-danger--hover{
+    @extend .button__base;
+    &:hover, &:focus, &.active{
+        color: $dangerColor;
+    }
+}
+.button__text-primary--hover{
+    @extend .button__base;
+    &:hover, &:focus, &.active{
+        color: $primary;
+    }
+}
+.button__danger--hover{
+    @extend .button__base;
+    &:hover, &:focus, &.active{
+        background-color: $dangerColor;
+        color:white;
+    }
+}
+.button__text{
+    @extend .button__base;
+    opacity:0.66;
+    &:hover, &.active, &:focus{
+        opacity:1;
+    }
+}
+.button__text--underlined{
+    @extend .button__base;
+    text-decoration: underline;
+    &:hover, &.active, &:focus{
+        text-decoration:none;
+    }
+}
+.button__bordered{
+    @extend .button__base;
+    border-bottom: 2px solid $text-color;
+    &:hover, &:focus, &.active{
+        border-color: $primary;
+    }
+}
+.button__bordered--inverted{
+    @extend .button__base;
+    border-bottom: 2px solid white;
+}
+.button__close{
+    @extend .button__base;
+    position:absolute;
+    top:0;
+    right:0;
+    opacity:0.6;
+    z-index:10;
+    &:hover, &:focus{
+        opacity:1;
+    }
+}
+
+
+/** _block.scss **/
+.block{
+    display: block !important;
+}
+
+
+/** _boxsizing.scss **/
+.boxsizing__borderbox{
+    box-sizing:border-box;
+}
+.boxsizing__contentbox{
+    box-sizing:content-box;
+}
+
+
+/** _scrollable.scss **/
+.scrollable{
+    overflow-y:auto;
+}
+.scrollable--x{
+    overflow-x:auto;
+}
+.no-scroll{
+    overflow:hidden;
+}
+
+
+/** _float.scss **/
+.float__right,
+%float__right{
+    float:right;
+}
+.float__left,
+%float__left{
+    float:left;
+}
+
+
+/** _fonts.scss **/
+.font__bold,
+%font__bold{
+    font-weight:600;
+}
+.font__normal,
+%font__normal{
+    font-weight: 400;
+}
+.font__light,
+%font__light{
+    font-weight: 200;
+}
+.fontColor__primary{
+    color:$primary;
+}
+.fontColor__lightGrey{
+    color:$lightGrey;
+}
+.fontColor__normal{
+    color:$text-color;
+}
+.fontColor__darker{
+    color:$darkGrey;
+}
+.fontColor__white{
+    color:white;
+}
+
+
+/** _forms.scss **/
+.textarea__unstyled{
+    border:none;
+    outline:none;
+    background-color:transparent;
+    color:inherit;
+    resize:none;
+    padding:0;
+    margin:0;
+    width:100%;
+}
+
+
+/** _position.scss **/
+.position__relative{
+    position: relative;
+}
+
+
+/** _margin.scss **/
+.margin__auto{
+    margin:auto;
+}
+
+
+/** _helpers.scss **/
+$steps: (
+    0,5,8,10,
+    11,12,13,14,15,16,17,18,19,20,
+    21,22,23,24,25,26,27,28,29,30,
+    31,32,33,34,35,36,37,38,39,40,
+    41,42,43,44,45,46,47,48,49,50,
+    55,60,64,65,70,72,75,80,85,90,95,96,100,
+    110,120,130,140,150,160,170,180,190,200,
+    210,220,230,240,250,260,270,280,290,300,
+    310,320,330,340,350,360,370,380,390,400,
+    410,420,430,440,450,460,470,480,490,500
+);
+
+
+/*************HELPERS**************/
+/**********************************/
+
+/*** identical width and height ***/
+@each $step in $steps {
+    .wh__#{$step} {
+        width: $step + px !important;
+        height: $step + px !important;
+    }
+    .lh__#{$step} {
+        line-height: $step + px !important;
+    }
+}
+.lh__1{
+    line-height: 1 !important;
+}
+.no-wrap{
+    white-space: nowrap;
+}
+
+.ov-h{
+    overflow: hidden;
+}
+.va-m{
+    vertical-align: middle;
+}
+.bg-inherit{
+    background-color:inherit;
+}
+.bg-black{
+    background-color: black;
+}
+.v-center{
+    &:before {
+        content: '';
+        display: inline-block;
+        height: 100%;
+        vertical-align: middle;
+        margin-top: -0.25em; /* Adjusts for spacing */
+    }
+}
+.fluid-height{
+    height:100%;
+}
+.visibility__hidden{
+    visibility: hidden;
+}
+
+.pointerEvents__none{
+    pointer-events: none;
+}
+
+/* Padding Helpers */
+.pn {
+  padding: 0 !important;
+}
+.p1 {
+  padding: 1px !important;
+}
+.p2 {
+  padding: 2px !important;
+}
+.p3 {
+  padding: 3px !important;
+}
+.p4 {
+  padding: 4px !important;
+}
+.p5 {
+  padding: 5px !important;
+}
+.p6 {
+  padding: 6px !important;
+}
+.p7 {
+  padding: 7px !important;
+}
+.p8 {
+  padding: 8px !important;
+}
+.p10 {
+  padding: 10px !important;
+}
+.p12 {
+  padding: 12px !important;
+}
+.p15 {
+  padding: 15px !important;
+}
+.p20 {
+  padding: 20px !important;
+}
+.p25 {
+  padding: 25px !important;
+}
+.p30 {
+  padding: 30px !important;
+}
+.p35 {
+  padding: 35px !important;
+}
+.p40 {
+  padding: 40px !important;
+}
+.p50 {
+  padding: 50px !important;
+}
+.ptn {
+  padding-top: 0 !important;
+}
+.pt5 {
+  padding-top: 5px !important;
+}
+.pt10 {
+  padding-top: 10px !important;
+}
+.pt15 {
+  padding-top: 15px !important;
+}
+.pt20 {
+  padding-top: 20px !important;
+}
+.pt25 {
+  padding-top: 25px !important;
+}
+.pt30 {
+  padding-top: 30px !important;
+}
+.pt35 {
+  padding-top: 35px !important;
+}
+.pt40 {
+  padding-top: 40px !important;
+}
+.pt50 {
+  padding-top: 50px !important;
+}
+.prn {
+  padding-right: 0 !important;
+}
+.pr5 {
+  padding-right: 5px !important;
+}
+.pr10 {
+  padding-right: 10px !important;
+}
+.pr15 {
+  padding-right: 15px !important;
+}
+.pr20 {
+  padding-right: 20px !important;
+}
+.pr25 {
+  padding-right: 25px !important;
+}
+.pr30 {
+  padding-right: 30px !important;
+}
+.pr35 {
+  padding-right: 35px !important;
+}
+.pr40 {
+  padding-right: 40px !important;
+}
+.pr50 {
+  padding-right: 50px !important;
+}
+.pbn {
+  padding-bottom: 0 !important;
+}
+.pb5 {
+  padding-bottom: 5px !important;
+}
+.pb10 {
+  padding-bottom: 10px !important;
+}
+.pb15 {
+  padding-bottom: 15px !important;
+}
+.pb20 {
+  padding-bottom: 20px !important;
+}
+.pb25 {
+  padding-bottom: 25px !important;
+}
+.pb30 {
+  padding-bottom: 30px !important;
+}
+.pb35 {
+  padding-bottom: 35px !important;
+}
+.pb40 {
+  padding-bottom: 40px !important;
+}
+.pb50 {
+  padding-bottom: 50px !important;
+}
+.pln {
+  padding-left: 0 !important;
+}
+.pl5 {
+  padding-left: 5px !important;
+}
+.pl10 {
+  padding-left: 10px !important;
+}
+.pl15 {
+  padding-left: 15px !important;
+}
+.pl20 {
+  padding-left: 20px !important;
+}
+.pl25 {
+  padding-left: 25px !important;
+}
+.pl30 {
+  padding-left: 30px !important;
+}
+.pl35 {
+  padding-left: 35px !important;
+}
+.pl40 {
+  padding-left: 40px !important;
+}
+.pl50 {
+  padding-left: 50px !important;
+}
+
+/* Axis Padding (both top/bottom or left/right) */
+.pv5 {
+  padding-top: 5px !important;
+  padding-bottom: 5px !important;
+}
+.pv8 {
+  padding-top: 8px !important;
+  padding-bottom: 8px !important;
+}
+.pv10 {
+  padding-top: 10px !important;
+  padding-bottom: 10px !important;
+}
+.pv15 {
+  padding-top: 15px !important;
+  padding-bottom: 15px !important;
+}
+.pv20 {
+  padding-top: 20px !important;
+  padding-bottom: 20px !important;
+}
+.pv25 {
+  padding-top: 25px !important;
+  padding-bottom: 25px !important;
+}
+.pv30 {
+  padding-top: 30px !important;
+  padding-bottom: 30px !important;
+}
+.pv40 {
+  padding-top: 40px !important;
+  padding-bottom: 40px !important;
+}
+.pv50 {
+  padding-top: 50px !important;
+  padding-bottom: 50px !important;
+}
+.ph5 {
+  padding-left: 5px !important;
+  padding-right: 5px !important;
+}
+.ph8 {
+  padding-left: 8px !important;
+  padding-right: 8px !important;
+}
+.ph10 {
+  padding-left: 10px !important;
+  padding-right: 10px !important;
+}
+.ph15 {
+  padding-left: 15px !important;
+  padding-right: 15px !important;
+}
+.ph20 {
+  padding-left: 20px !important;
+  padding-right: 20px !important;
+}
+.ph25 {
+  padding-left: 25px !important;
+  padding-right: 25px !important;
+}
+.ph30 {
+  padding-left: 30px !important;
+  padding-right: 30px !important;
+}
+.ph40 {
+  padding-left: 40px !important;
+  padding-right: 40px !important;
+}
+.ph50 {
+  padding-left: 50px !important;
+  padding-right: 50px !important;
+}
+
+/* margin center helper */
+.mauto {
+  margin-left: auto;
+  margin-right: auto;
+}
+.mn {
+  margin: 0 !important;
+}
+.m1 {
+  margin: 1px !important;
+}
+.m2 {
+  margin: 2px !important;
+}
+.m3 {
+  margin: 3px !important;
+}
+.m4 {
+  margin: 4px !important;
+}
+.m5 {
+  margin: 5px !important;
+}
+.m8 {
+  margin: 8px !important;
+}
+.m10 {
+  margin: 10px !important;
+}
+.m15 {
+  margin: 15px !important;
+}
+.m20 {
+  margin: 20px !important;
+}
+.m25 {
+  margin: 25px !important;
+}
+.m30 {
+  margin: 30px !important;
+}
+.m35 {
+  margin: 35px !important;
+}
+.m40 {
+  margin: 40px !important;
+}
+.m50 {
+  margin: 50px !important;
+}
+.mtn {
+  margin-top: 0 !important;
+}
+.mt5 {
+  margin-top: 5px !important;
+}
+.mt10 {
+  margin-top: 10px !important;
+}
+.mt15 {
+  margin-top: 15px !important;
+}
+.mt20 {
+  margin-top: 20px !important;
+}
+.mt25 {
+  margin-top: 25px !important;
+}
+.mt30 {
+  margin-top: 30px !important;
+}
+.mt35 {
+  margin-top: 35px !important;
+}
+.mt40 {
+  margin-top: 40px !important;
+}
+.mt50 {
+  margin-top: 50px !important;
+}
+.mt70 {
+  margin-top: 70px !important;
+}
+.mrn {
+  margin-right: 0 !important;
+}
+.mr5 {
+  margin-right: 5px !important;
+}
+.mr10 {
+  margin-right: 10px !important;
+}
+.mr15 {
+  margin-right: 15px !important;
+}
+.mr20 {
+  margin-right: 20px !important;
+}
+.mr25 {
+  margin-right: 25px !important;
+}
+.mr30 {
+  margin-right: 30px !important;
+}
+.mr35 {
+  margin-right: 35px !important;
+}
+.mr40 {
+  margin-right: 40px !important;
+}
+.mr50 {
+  margin-right: 50px !important;
+}
+.mbn {
+  margin-bottom: 0 !important;
+}
+.mb5 {
+  margin-bottom: 5px !important;
+}
+.mb10 {
+  margin-bottom: 10px !important;
+}
+.mb15 {
+  margin-bottom: 15px !important;
+}
+.mb20 {
+  margin-bottom: 20px !important;
+}
+.mb25 {
+  margin-bottom: 25px !important;
+}
+.mb30 {
+  margin-bottom: 30px !important;
+}
+.mb35 {
+  margin-bottom: 35px !important;
+}
+.mb40 {
+  margin-bottom: 40px !important;
+}
+.mb50 {
+  margin-bottom: 50px !important;
+}
+.mb70 {
+  margin-bottom: 70px !important;
+}
+.mln {
+  margin-left: 0 !important;
+}
+.ml5 {
+  margin-left: 5px !important;
+}
+.ml10 {
+  margin-left: 10px !important;
+}
+.ml15 {
+  margin-left: 15px !important;
+}
+.ml20 {
+  margin-left: 20px !important;
+}
+.ml25 {
+  margin-left: 25px !important;
+}
+.ml30 {
+  margin-left: 30px !important;
+}
+.ml35 {
+  margin-left: 35px !important;
+}
+.ml40 {
+  margin-left: 40px !important;
+}
+.ml50 {
+  margin-left: 50px !important;
+}
+
+/* Axis Margins (both top/bottom or left/right) */
+.mv5 {
+  margin-top: 5px !important;
+  margin-bottom: 5px !important;
+}
+.mv10 {
+  margin-top: 10px !important;
+  margin-bottom: 10px !important;
+}
+.mv15 {
+  margin-top: 15px !important;
+  margin-bottom: 15px !important;
+}
+.mv20 {
+  margin-top: 20px !important;
+  margin-bottom: 20px !important;
+}
+.mv25 {
+  margin-top: 25px !important;
+  margin-bottom: 25px !important;
+}
+.mv30 {
+  margin-top: 30px !important;
+  margin-bottom: 30px !important;
+}
+.mv40 {
+  margin-top: 40px !important;
+  margin-bottom: 40px !important;
+}
+.mv50 {
+  margin-top: 50px !important;
+  margin-bottom: 50px !important;
+}
+.mv70 {
+  margin-top: 70px !important;
+  margin-bottom: 70px !important;
+}
+.mh5 {
+  margin-left: 5px !important;
+  margin-right: 5px !important;
+}
+.mh10 {
+  margin-left: 10px !important;
+  margin-right: 10px !important;
+}
+.mh15 {
+  margin-left: 15px !important;
+  margin-right: 15px !important;
+}
+.mh20 {
+  margin-left: 20px !important;
+  margin-right: 20px !important;
+}
+.mh25 {
+  margin-left: 25px !important;
+  margin-right: 25px !important;
+}
+.mh30 {
+  margin-left: 30px !important;
+  margin-right: 30px !important;
+}
+.mh40 {
+  margin-left: 40px !important;
+  margin-right: 40px !important;
+}
+.mh50 {
+  margin-left: 50px !important;
+  margin-right: 50px !important;
+}
+.mh70 {
+  margin-left: 70px !important;
+  margin-right: 70px !important;
+}
+
+/* Negative Margin Helpers */
+.mtn5 {
+  margin-top: -5px !important;
+}
+.mtn10 {
+  margin-top: -10px !important;
+}
+.mtn15 {
+  margin-top: -15px !important;
+}
+.mtn20 {
+  margin-top: -20px !important;
+}
+.mtn30 {
+  margin-top: -30px !important;
+}
+.mrn5 {
+  margin-right: -5px !important;
+}
+.mrn10 {
+  margin-right: -10px !important;
+}
+.mrn15 {
+  margin-right: -15px !important;
+}
+.mrn20 {
+  margin-right: -20px !important;
+}
+.mrn30 {
+  margin-right: -30px !important;
+}
+.mbn5 {
+  margin-bottom: -5px !important;
+}
+.mbn10 {
+  margin-bottom: -10px !important;
+}
+.mbn15 {
+  margin-bottom: -15px !important;
+}
+.mbn20 {
+  margin-bottom: -20px !important;
+}
+.mbn30 {
+  margin-bottom: -30px !important;
+}
+.mln5 {
+  margin-left: -5px !important;
+}
+.mln10 {
+  margin-left: -10px !important;
+}
+.mln15 {
+  margin-left: -15px !important;
+}
+.mln20 {
+  margin-left: -20px !important;
+}
+.mln30 {
+  margin-left: -30px !important;
+}
+
+/* Vertical Negative Margin "mv" + "n" + "x" */
+.mvn5 {
+  margin-top: -5px !important;
+  margin-bottom: -5px !important;
+}
+.mvn10 {
+  margin-top: -10px !important;
+  margin-bottom: -10px !important;
+}
+.mvn15 {
+  margin-top: -15px !important;
+  margin-bottom: -15px !important;
+}
+.mvn20 {
+  margin-top: -20px !important;
+  margin-bottom: -20px !important;
+}
+.mvn30 {
+  margin-top: -30px !important;
+  margin-bottom: -30px !important;
+}
+
+/* Horizontal Negative Margin "mh" + "n" + "x" */
+.mhn5 {
+  margin-left: -5px !important;
+  margin-right: -5px !important;
+}
+.mhn10 {
+  margin-left: -10px !important;
+  margin-right: -10px !important;
+}
+.mhn15 {
+  margin-left: -15px !important;
+  margin-right: -15px !important;
+}
+.mhn20 {
+  margin-left: -20px !important;
+  margin-right: -20px !important;
+}
+.mhn30 {
+  margin-left: -30px !important;
+  margin-right: -30px !important;
+}
+
+/* Vertical Align Helpers */
+.va-t {
+  vertical-align: top !important;
+}
+.va-m {
+  vertical-align: middle !important;
+}
+.va-b {
+  vertical-align: bottom !important;
+}
+.va-s {
+  vertical-align: super !important;
+}
+
+/* Text Helpers */
+.text-left {
+  text-align: left !important;
+}
+.text-right {
+  text-align: right !important;
+}
+.text-center {
+  text-align: center !important;
+}
+.text-justify {
+  text-align: justify !important;
+}
+.text-nowrap {
+  white-space: nowrap !important;
+}
+
+/* Inline Block Helper */
+.ib,
+.inline-object {
+  display: inline-block !important;
+}
+
+.clear {
+  clear: both;
+}
+
+// warning popup
+
+.wvWarning {
+  position: relative;
+  width: 320px;
+  min-height: 130px;
+  z-index: 999;
+  left: calc(50% - 160px);
+  border: #000 solid 1px;
+  -webkit-border-radius: 7px;
+  -moz-border-radius: 7px;
+  border-radius: 7px;
+  color: #FF5722;
+  box-shadow: 0px 3px 23px #ff980078;
+  -webkit-animation-name: example;  /* Safari 4.0 - 8.0 */
+  -webkit-animation-duration: 3s;  /* Safari 4.0 - 8.0 */    
+  -webkit-animation-fill-mode: both; /* Safari 4.0 - 8.0 */
+  animation-name: example;
+  animation-duration: 2s;    
+  animation-fill-mode: both;
+  animation-timing-function: ease-out;
+}
+
+@-webkit-keyframes example {
+    from {top: 0vh;opacity: 0;background: #868686}
+    to {top: 10vh;opacity: 1;background: #ffffff}
+}
+
+@keyframes example {
+  from {top: 0vh;opacity: 0;background: #868686}
+    to {top: 10vh;opacity: 1;background: #ffffff}
+}
+
+.wvWarning-content{
+  position: relative;
+  width: 190px;
+  min-height: 88px;
+  max-height: 80vh;
+  margin: auto;
+}
+.wvWarning-icon{
+  font-size: 32px;
+}
+.wvWarning-text{
+  position: relative;
+}
+.wvWarning-button{
+  background-color: #f1ededcc;
+  color: #607D8B;
+  width: 50px;
+  font-weight: 600;
+  margin-top: 2px;
+  margin-right: 30px;
+}
+
+.wvScreenToSmallWarning {
+  position: fixed;
+  display: block;
+  top: 0;
+  left: 0;
+  background-color: white;
+  color: #333;
+  width: 100%;
+  height: 100%;
+  z-index: 1000;
+}
+
+.wvScreenToSmallWarning-content {
+  padding: 10px;
+  text-align: center;
+}
+
+/* on some mobile devices, the size returned for the "screen" is actually the viewport size so 360x640 is actually equal to 280x560 */
+@media only screen and (min-width: 550px) and (min-height: 280px) { 
+  .wvScreenToSmallWarning {
+    display: none;
+  }
+}
+
+/* on some mobile devices, the size returned for the "screen" is actually the viewport size so 360x640 is actually equal to 280x560 */
+@media only screen and (min-width: 280px) and (min-height: 550px)  {
+  .wvScreenToSmallWarning {
+    display: none;
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_layout.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,498 @@
+$topHeight: 42px;
+$bottomHeightSmall: 7rem; // On small width, we provide two-lines bottom zone to compensate the smaller width
+$bottomHeightLarge: 5rem; // On large width, we provide one-line bottom zone
+
+$asideWidth: 32rem;
+$asideMinifyWidth: 12rem;
+
+$asideRightMinifyWidth: 85px; // eq. 7.5rem * 12px - ( $asideRightPadding / 2 )
+$asideRightPadding: 10px;
+
+/* layout: left section */
+
+// Adapt left aside based on state (opened/closed).
+.wvLayoutLeft {
+    // Set general properties.
+    position:absolute;
+    z-index:2;
+    background-color:black;
+    width: $asideWidth;
+
+    // Position the left side below the top zone if it is shown
+    &.wvLayoutLeft--toppadding {
+        top: $topHeight;
+    }
+    &:not(.wvLayoutLeft--toppadding) {
+        top: 0;
+    }
+
+    // Position the left section over the bottom one if the latter is shown
+    &.wvLayoutLeft--bottompadding {
+        @media screen and (max-device-width: 374px) {
+            bottom: $bottomHeightSmall;
+        }
+        @media screen and (min-device-width: 375px) {
+            bottom: $bottomHeightLarge;
+        }
+    }
+    &:not(.wvLayoutLeft--bottompadding) {
+        bottom: 0;
+    }
+
+    // Position the left side on the left
+    left: 0;
+
+    // When layout left is shown, nothing special happens (default state)
+    &:not(.wvLayoutLeft--closed) {
+    }
+
+    // When layout left is closed, move it aside
+    &.wvLayoutLeft--closed {
+        transform: translateX(- $asideWidth); // Move all out of the screen
+        &.wvLayoutLeft--small {
+            transform: translateX(-$asideMinifyWidth);
+        }
+    }
+    &.wvLayoutLeft--small{
+        width: $asideMinifyWidth;
+        & .wvLayoutLeft__contentTop, & .wvLayoutLeft__contentMiddle, & .wvLayoutLeft__contentBottom{
+            width: 100%;
+        }
+    }
+}
+
+// Layout-Left Flexbox containers for the content.
+.wvLayoutLeft__content {
+    border-right: 1px solid #AAA;
+
+    // Display flex mode so optional actions can be positionned at the bottom
+    // side.
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+
+    // Make it scrollable.
+    overflow-y: auto;
+    height: 100%;
+}
+
+.wvLayoutLeft__contentTop {
+    // We have to set a static height since we use floating to make white space
+    // disapear in nested content.
+    // note: could be deactivate with the clearfix so we can have a dynamic height
+    // max-height: 6rem;
+    padding: 0rem 1rem 0rem 1rem;
+
+    // Enforce width even if there is a scrollbar on win/IE11 (-1 px for
+    // border).
+    width: $asideWidth - 0.1rem;
+
+    &:after{
+        content: "";
+        display:block;
+        height:0;
+        width:0;
+        clear:both;
+    }
+}
+
+.wvLayoutLeft__contentMiddle {
+    // Let the middle zone take as much space as required.
+    flex: 1 0 auto;
+
+    // Enforce width even if there is a scrollbar on win/IE11 (-1 px for
+    // border).
+    width: $asideWidth - 0.1rem;
+
+}
+
+.wvLayoutLeft__contentBottom {
+    // Enforce width even if there is a scrollbar on win/IE11 (-1 px for
+    // border).
+    width: $asideWidth - 0.1rem;
+}
+.wvLayout__leftBottom.wvLayout__leftBottom--enabled {
+    border-top: 1px solid hsla(0, 0%, 100%, 0.2);
+    margin-top: 1rem;
+    padding: 1rem;
+
+    // Prevent from taking more space than intended.
+    // flex-grow: 0;
+}
+
+.wvLayoutLeft__actions,
+%wvLayoutLeft__actions{
+    display:block;
+    position:absolute;
+    right:1px; // border
+    top: 50%;
+    transform: translateY(-50%);
+    width:25px;
+}
+.wvLayoutLeft__actions--outside{
+    @extend.wvLayoutLeft__actions;
+    right:-25px; // width + border
+}
+.wvLayoutLeft__action{
+    background-color:$primary;
+    opacity: 0.5;
+    color:white;
+    transition: none;
+    &:hover, &:focus{
+        opacity: 1;
+    }
+}
+
+
+/* layout: right section */
+
+// Adapt right aside based on state (opened/closed).
+.wvLayout__right {
+    display:block;
+    position:absolute;
+    z-index:2;
+    background-color:black;
+    width: $asideRightMinifyWidth;
+
+    // Position the left side below the top zone if it is shown
+    &.wvLayout__right--toppadding {
+        top: $topHeight;
+    }
+    &:not(.wvLayout__right--toppadding) {
+        top: 0;
+    }
+
+    // Position the right section over the bottom one if the latter is shown
+    &.wvLayout__right--bottompadding {
+        @media screen and (max-device-width: 374px) {
+            bottom: $bottomHeightSmall;
+        }
+        @media screen and (min-device-width: 375px) {
+            bottom: $bottomHeightLarge;
+        }
+    }
+    &:not(.wvLayout__right--bottompadding) {
+        bottom: 0;
+    }
+
+    // Position the right side on the right
+    right: 0;
+
+    // When layout right is shown, nothing special happens (default state)
+    &:not(.wvLayout__right--closed) {
+    }
+
+    // When layout right is closed, move it aside
+    &.wvLayout__right--closed {
+        transform: translateX(+ $asideRightMinifyWidth); // Move all out of the screen
+    }
+
+    // Set childrens to full height (so border-left appears at 100% height)
+    & > wv-layout-right,
+    & > wv-layout-right > .wvViewer__asideRight
+    {
+        display: block;
+        height: 100%;
+        width: 100%;
+    }
+}
+
+.wvAsideRight__content {
+    height: 100%;
+    float: left;
+
+    border-left: 1px solid #AAA;
+
+    padding: 0 $asideRightPadding/2;
+    width: $asideWidth;
+}
+
+.wvAsideRight__actions,
+%wvAsideRight__actions{
+    display:block;
+    position:absolute;
+    left:1px; // border
+    top: 50%;
+    transform: translateY(-50%);
+    width:25px;
+
+    // Compensate aside z-index to let user click on button when another button
+    // is behind the actions.
+    z-index: 3;
+}
+.wvAsideRight__actions--outside{
+    @extend.wvAsideRight__actions;
+    left:-25px; // width + border
+}
+.wvAsideRight__action{
+    background-color:$primary;
+    opacity: 0.5;
+    color:white;
+    transition: none;
+    &:hover, &:focus{
+        opacity: 1;
+    }
+}
+.wvAsideRight__fixOpenFullyTooltip + .tooltip { // Fix the "open fully" bad tooltip placement of the asideRight
+    left: -6.633em !important;
+    top: 1px !important;
+}
+
+
+/* layout: bottom section */
+
+// Set bottom section size & position
+.wvLayout__bottom {
+    position: absolute;
+
+    // Display the bottom bar in the bottom side
+    @media screen and (max-device-width: 374px) {
+        height: $bottomHeightSmall;
+    }
+    @media screen and (min-device-width: 375px) {
+        height: $bottomHeightLarge;
+    }
+
+    left: 0;
+    bottom: 0;
+    right: 0;
+
+    // Set grey background color (as it is only used to display notices)
+    background-color: hsl(0, 0%, 10%);
+}
+
+
+/* layout: main section */
+
+// Set main section size & position
+.wvLayout__main {
+    position: absolute;
+
+    // Align content (such as toolbar)
+    text-align: center;
+
+    // Position splitpane considering the toolbar when toolbar is present.
+    & .wvLayout__splitpane--toolbarAtTop {
+        display: block;
+        height: calc(100% - #{$toolbarHeight});
+        width: 100%;
+
+        position: relative;
+        top: $toolbarHeight;
+    }
+    & .wvLayout__splitpane--toolbarAtRight {
+        display: block;
+        height: 100%;
+        width: calc(100% - #{$toolbarHeight});
+    }
+
+    & .wvLayout__splitpane--bigToolbarAtTop {
+        display: block;
+        height: calc(100% - 68px);
+        width: 100%;
+
+        position: relative;
+        top: 68px;
+    }
+    & .wvLayout__splitpane--bigToolbarAtRight {
+        display: block;
+        height: 100%;
+        width: calc(100% - 68px);
+    }
+
+    // Position the main section below the top zone if the latter is shown
+    &.wvLayout__main--toppadding {
+        top: $topHeight;
+    }
+    &:not(.wvLayout__main--toppadding) {
+        top: 0;
+    }
+
+    // Position the main section over the bottom one if the latter is shown
+    &.wvLayout__main--bottompadding {
+        bottom:440px;
+        @media screen and (max-device-width: 374px) {
+            bottom: $bottomHeightSmall;
+        }
+        @media screen and (min-device-width: 375px) {
+            bottom: $bottomHeightLarge;
+        }
+    }
+    &:not(.wvLayout__main--bottompadding) {
+        bottom: 0;
+    }
+
+    // Make the main content fill the screen by default
+    // Depending on the browser, the left and right attributes are more
+    // optimized than padding's ones. The reason is that they based upon
+    // absolute positioning. The require no contextual positioning calculation
+    // and are less performance intensive, especially concidering transition
+    // animation.
+    right: 0;
+    left: 0;
+
+    // transition: 0.6s left ease, 0.6s right ease;
+
+    // Adapt main section's size based on aside left's state (opened/closed)
+    // 1. When aside left is not hidden , move the main section 300 pixel to
+    //   the right
+    &.wvLayout__main--leftpadding {
+        left: $asideWidth;
+    }
+    // 2. When aside left is hidden, let the main take 100% of the place
+    &:not(.wvLayout__main--leftpadding, .wvLayout__main--smallleftpadding) {
+        left: 0px;
+    }
+    &.wvLayout__main--smallleftpadding {
+        left: $asideMinifyWidth;
+    }
+
+    // Adapt main section's size based on aside right's state (opened/closed)
+    // 1. When aside right is not hidden , move the main section 84 pixel to
+    //   the left
+    &.wvLayout__main--rightpadding {
+        right: $asideRightMinifyWidth;
+    }
+    // 2. When aside right is hidden, let the main take 100% of the place
+    &:not(.wvLayout__main--rightpadding) {
+        right: 0px;
+    }
+}
+
+/* global */
+.popover {
+    // Set back black as default popover text color
+    color: black;
+}
+
+.wvViewer__editor--full{
+    position:absolute;
+    top:0;
+    right:0;
+    z-index:10;
+    opacity:0;
+    transform: translateX(100%);
+    width:100%;
+    height:100%;
+    background-color:white;
+    color:$text-color;
+    &.opened{
+        opacity:1;
+        transform: translateX(0);
+    }
+}
+
+.wvViewer__topBar{
+    width:100%;
+    // margin-top: 0.5rem;
+
+    // Allow user to scroll through the toolbar if screen is too small. Note we
+    // can't use z-index properly to show buttons on top of the viewer, as any
+    // popover will appear behind them (even with higher z-index) due to an
+    // overflow property hidden somewhere.
+    overflow-y: auto;
+    white-space: nowrap;
+    max-width: 100%;
+}
+.wvViewer__buttonGroup{
+    display:inline-block;
+}
+.wvViewer__buttonGroup--asideWidth{
+    width: $asideWidth;
+    padding-right: 1rem;
+}
+.wvViewer__buttonGroup--contentWidth{
+    width: calc(100% - #{$asideWidth});
+    padding-left: 1rem;
+    max-height: 4.2rem; // Make sure mobile keeps the menubar below a certain size
+}
+.wvViewer__iframe{
+    position:absolute;
+    left:0;
+    top:0;
+}
+
+/* bottom bar */
+.wvViewer__bottomBar,
+%wvViewer__bottomBar{
+    position:absolute;
+    left:0;
+    bottom:0;
+    width:100%;
+    background-color:#111111;
+}
+
+.wvViewer__bottomBar--expanded{
+    @extend .wvViewer__bottomBar;
+    height: 80px; //total height of the last serieList cell (64 + 10(margin bottom previous item) + item padding bottom +1 border-width (top item)
+    //border-top: 1px solid rgba(255,255,255,0.1);
+    color:white;
+
+    .wvViewer__timeline{
+        width: calc(100% - 80px);
+    }
+    .wvTimeline__hotspots{
+        bottom: -40px;
+    }
+}
+
+.wvViewer__bottomBar--minimized{
+    @extend .wvViewer__bottomBar;
+    color: white;
+
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+    padding-left: 2.5rem;
+
+    .wvTimeline__hotspot{
+        top: -40px;
+        opacity:0;
+        visibility:hidden;
+        z-index:-1;
+        // transition: all 0.3s ease 0.6s; //adding a delay when mouse leave
+        // transition-property: opacity, visibility, z-index;
+    }
+    &:hover .wvTimeline__hotspot{
+        opacity:1;
+        visibility: visible;
+        z-index:5;
+        transition-delay: 0s;
+    }
+}
+
+.wvViewer__timeline{
+    height:24px;
+    //background-color:rgba(1,1,1,0.2);
+    line-height: 24px;
+    vertical-align: middle;
+    width:100%;
+}
+
+.wvViewer__trademark{
+    display:inline-block;
+    float:right;
+    width:80px;
+    height:80px;
+    float:right;
+    line-height: 80px;
+    vertical-align: middle;
+    text-align: center;
+}
+.wvTimeline__action--text{
+
+}
+.wvTimeline__input{
+    border-radius: 3px;
+    &:focus{
+        outline:none;
+    }
+    margin-top:2px;
+    border: 1px solid $border-color;
+}
+
+.wvTimeline__actions{
+    display:inline-block;
+    border-right: 1px solid $border-color;
+}
+.wvTimeline__wrapper{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_notice.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,55 @@
+wv-notice {
+    display: block;
+    height: 100%;
+    width: 100%;
+}
+
+.wvNotice {
+    // Set padding
+    padding: 0.5rem 0.25rem;
+
+    // Fill parent element so text can be centered
+    height: 100%;
+}
+
+.wvNotice__text {
+    // Center text 
+    position: relative;
+    top: 50%;
+    transform: translateY(-50%);
+    text-align: center;
+    margin-left: 1rem;
+
+    // Text style
+    font-weight: 400;
+    color: hsl(0, 0%, 70%);
+
+    // Keep space for button
+    float: left;
+    width: calc(100% - 7rem); // 3.5 rem + 3 rem margin (incl. button margin + text margin)
+}
+
+.wvNotice__closeButton {
+    // Position button on right
+    float: right;
+    margin-right: 0.5em;
+
+    // Center button vertically
+    position: relative;
+    top: 50%;
+    transform: translateY(-50%);
+
+    // Set button size
+    width: 3.5rem;
+    height: 2.5rem; // half the bottom zone height
+
+    // Configure button icon
+    text-align: center;
+    font-size: 1em;
+    font-weight: 100;
+    line-height: 2.2rem;
+
+    // Set button style
+    cursor: pointer;
+    border: 1px solid hsl(0, 0%, 27%);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_print.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,56 @@
+.wvPrintExclude{
+    display:none;
+}
+
+.wvPrintFullPage{
+    width: 100% !important;
+    height: 100% !important;
+    position: absolute !important;
+    top: 0 !important;
+    left: 0 !important;
+    display:block !important;
+}
+
+.wvLayout__main{
+    top: 0 !important;
+    right: 0 !important;
+    left: 0 !important;
+    bottom: 0 !important;
+}
+
+.wvPrintViewer{
+    width: 100%;
+    height:100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.wvPrintViewer canvas{
+    max-width: 100% !important;
+    max-height: 100% !important;
+    margin:auto;
+}
+
+.wv-overlay-topleft, .wv-overlay-topright, .wv-overlay-bottomright, .wv-overlay-bottomleft{
+    &, & * {
+        background-color: black !important;
+        -webkit-print-color-adjust: exact !important; 
+        color-adjust: exact !important;
+        color: orange !important;
+    }
+}
+.tooltip{
+    display: none !important;
+}
+body{
+    margin: 0;
+    padding: 0;
+    position: relative;
+    &, *{
+        background-color: black !important;
+        -webkit-print-color-adjust: exact !important; 
+    }
+    width: 8.5in;
+    height: 11in;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_selectionActionlist.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,9 @@
+.wvSelectionActionlist {
+    display: block;
+
+    text-align: center;
+}
+
+.wvSelectionActionlist__bottom {
+    
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_serieslist.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,182 @@
+$gray: gray;
+$blue: hsla(204, 70%, 53%, 0.7);
+$red: rgba(206, 0, 0, 0.7);
+$green: rgba(0, 160, 27, .7);
+$yellow: rgba(220, 200  , 0, .9);
+$violet: rgba(255, 31, 255, .7);
+
+$borderColor: rgba(255, 255, 255, 0.8);
+$borderColorActive: rgba(255, 255, 255, 0.6);
+$borderColorHighlighted: rgba(255, 255, 255, 1);
+$pictureSize: 6.5rem;
+
+.wvSerieslist {
+    margin: 0;
+    padding: 0;
+    list-style: none;
+}
+
+.wvSerieslist__seriesItem--selectable {
+    // Pointer cursor (for `ng-click`)
+    cursor: pointer !important;
+
+    // Lighten up the icon on hover
+    &:hover {
+        color: white;
+    }
+}
+
+.wvSerieslist__placeholderIcon, .wvSerieslist__placeholderIcon.fa { // Make sure it has precedence over .fa class {
+    position: absolute;
+
+    // Fill the li element
+    width: 100%;
+    height: 100%;
+
+    // Fill the li element with the fontawesome icon
+    font-size: $pictureSize/2;
+    line-height: $pictureSize;
+    text-align: center;
+}
+
+.wvSerieslist__placeholderIcon--strikeout, .wvSerieslist__placeholderIcon--strikeout.fa { // Make sure it has precedence over .fa class
+    // Grey out (since no report is available)
+    color: #c3c3c3;
+
+    // Diagonal line crossing report icon (to tell none are available)
+    // position: relative;
+
+    &::after { // use after to not conflicts with font-awesome :before
+        position: absolute;
+
+        left: 0;
+        top: 50%;
+        right: 0;
+
+        transform: rotate(-45deg) scaleX(0.9);
+
+        border-top: 5px solid;
+        border-color: inherit;
+
+        content: "";
+    }
+}
+
+.wvSerieslist__picture{
+    display: inline-block;
+    font-size: 14px;
+    width: $pictureSize;
+    height: $pictureSize;
+    position: relative;
+
+    // Move picture behind the `toggle layout@ left` button.
+    z-index: -1;
+}
+.wvSerieslist__badge {
+    position: absolute;
+    bottom:5px;
+    right:5px;
+    font-size:10px;
+    line-height:15px;
+    width:15px;
+    height:15px;
+    border-radius: 100%;
+    background-color: $gray;
+    vertical-align: middle;
+    text-align: center;
+    font-weight: bold;
+}
+.wvSerieslist__information{
+    font-size: 14px;
+    float: right;
+    padding-left: 1rem;
+    width: calc(100% - #{$pictureSize});
+    height: $pictureSize;
+}
+.wvSerieslist__label{
+    white-space: nowrap;
+    width:calc(100% - 10px);
+    overflow:hidden;
+    height:$pictureSize/2;
+    line-height:$pictureSize/2;
+    vertical-align: middle;
+}
+.wvSerieslist__timeline{
+    //border-top: 0.1rem solid rgba(255,255,255,0.2);
+    height:$pictureSize/2;
+    line-height:$pictureSize/2;
+    vertical-align: middle;
+}
+
+.wvSerieslist__seriesItem {
+    // anchor
+    position: relative;
+
+    // unstyle list
+    padding-left: 0;
+    list-style: none;
+    font-size: 0;
+
+    // mimic on hover border for draggable
+    border-right: 0.2rem solid transparent;
+    border-left: 0.2rem solid transparent;
+    border-top: 0.2rem solid transparent;
+    border-bottom: 0.2rem solid transparent;
+    border-corner-shape: notch;
+
+    line-height: 0px;
+    margin: 0.1rem;
+
+    &.active{
+        border-color: $borderColorActive;
+        border-style: solid;
+    }
+
+    &.highlighted{
+        border-color: $borderColorHighlighted;
+        border-style: solid;
+    }
+
+    &:hover, &:focus, &.focused{
+        border-style: dashed;
+        border-color: $borderColor;
+    }
+}
+
+.wvSerieslist__seriesItem--list {
+    display: block;
+}
+.wvSerieslist__seriesItem--grid {
+    display: inline-block;
+}
+.wvSerieslist__seriesItem--oneCol{
+    text-align: center;
+}
+
+.wvSerieslist__seriesItem--activated,
+.wvSerieslist__videoItem--activated,
+.wvSerieslist__pdfItem--activated {
+    border: 0.2rem solid hsla(204, 70%, 53%, 1) !important;
+}
+
+// Color related modifiers
+.wvSerieslist__badge--blue {
+    @extend .wvSerieslist__badge;
+    background-color: $blue;
+}
+.wvSerieslist__badge--red {
+    @extend .wvSerieslist__badge;
+    background-color: $red;
+}
+.wvSerieslist__badge--green {
+    @extend .wvSerieslist__badge;
+    background-color: $green;
+}
+.wvSerieslist__badge--yellow {
+    @extend .wvSerieslist__badge;
+    background-color: $yellow;
+}
+.wvSerieslist__badge--violet {
+    @extend .wvSerieslist__badge;
+    background-color: $violet;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_studyInformationBreadcrumb.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,39 @@
+.wvStudyInformationBreadcrumb {
+}
+
+.wvStudyInformationBreadcrumb__patient {
+    display: inline-block;
+    background-color: rgba(255, 255, 255, 0.15);
+    padding: 0.2rem 1rem 0.3rem 1rem;
+    text-align: center;
+    font-size: 1em;
+    margin: 0.6rem;
+    font-weight: 400;
+    line-height: 2.3rem;
+
+    // Prevent doubled margin with the next item
+    margin-right: 0;
+}
+.wvStudyInformationBreadcrumb__patientName {
+
+}
+.wvStudyInformationBreadcrumb__patientBirthDate {
+
+}
+
+.wvStudyInformationBreadcrumb__study {
+    display: inline-block;
+    background-color: rgba(255, 255, 255, 0.15);
+    padding: 0.2rem 1rem 0.3rem 1rem;
+    text-align: center;
+    font-size: 1em;
+    margin: 0.6rem;
+    font-weight: 400;
+    line-height: 2.3rem;
+}
+.wvStudyInformationBreadcrumb__studyDescription {
+
+}
+.wvStudyInformationBreadcrumb__studyDate {
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_studyIsland.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,90 @@
+$gray: gray;
+$blue: hsla(204, 70%, 53%, 0.7);
+$red: rgba(206, 0, 0, 0.7);
+$green: rgba(0, 160, 27, .7);
+$yellow: rgba(220, 200  , 0, .9);
+$violet: rgba(255, 31, 255, .7);
+
+
+%wvStudyIsland {
+    margin: 1rem 1rem 1rem 1rem;
+    border: 0.3rem solid $gray;
+}
+
+%wvStudyIsland__header {
+    background-color: $gray;
+    padding: 0.5rem 0.5rem 0.8rem 0.5rem;
+    line-height: 1.35rem;
+    width: 100%;
+}
+.wvStudyIsland__actions {
+    float: right;
+
+    // Compensate header padding (since the inner download study button
+    // actually has margin).
+    margin-top: -0.8rem;
+    margin-right: -0.8rem;
+}
+
+.wvStudyIsland__actions--oneCol {
+    float: none;
+    text-align: center;
+}
+
+.wvStudyIsland__main {
+    padding: 0.4rem; // 0.7rem - 0.3rem (2px transparent border + 1px margin)
+    color: hsl(0, 0%, 100%);
+    width: 100%; // let some space for the 4-columns based items
+}
+
+
+// Color modifiers
+.wvStudyIsland--blue {
+    @extend %wvStudyIsland;
+    border-color: $blue;
+}
+
+.wvStudyIsland__header--blue {
+    @extend %wvStudyIsland__header;
+    background-color: $blue;
+}
+
+.wvStudyIsland--red {
+    @extend %wvStudyIsland;
+    border-color: $red;
+}
+
+.wvStudyIsland__header--red {
+    @extend %wvStudyIsland__header;
+    background-color: $red;
+}
+
+.wvStudyIsland--green {
+    @extend %wvStudyIsland;
+    border-color: $green;
+}
+
+.wvStudyIsland__header--green {
+    @extend %wvStudyIsland__header;
+    background-color: $green;
+}
+
+.wvStudyIsland--yellow {
+    @extend %wvStudyIsland;
+    border-color: $yellow;
+}
+
+.wvStudyIsland__header--yellow {
+    @extend %wvStudyIsland__header;
+    background-color: $yellow;
+}
+
+.wvStudyIsland--violet {
+    @extend %wvStudyIsland;
+    border-color: $violet;
+}
+
+.wvStudyIsland__header--violet {
+    @extend %wvStudyIsland__header;
+    background-color: $violet;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_toolbar.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,178 @@
+.wvToolbar {
+    position: absolute;
+}
+
+.wvToolbar--top {
+    top: 0;
+    height: $toolbarHeight;
+
+    // Position the toolbar to the right (even if it's positioned
+    // horizontally).
+    right: 0;
+    text-align: right;
+
+    // Allow user to scroll through the toolbar if screen is too small. Note we
+    // can't use z-index properly to show buttons on top of the viewer, as any
+    // popover will appear behind them (even with higher z-index) due to an
+    // overflow property hidden somewhere.
+    // overflow-y: auto;
+    white-space: nowrap;
+    max-width: 100%;
+}
+
+.wvToolbar--right {
+    right: 0;
+    width: 42px; // != $toolbarHeight since we're in the reverse order.
+
+    // Allow user to scroll through the toolbar if screen is too small.
+    // overflow-x: auto;
+    height: 100%;
+    z-index: 2;
+    &.wvToolbar--big{
+        width: 68px;
+    }
+}
+
+/* Splitpane Grid Configuration */
+
+.wvToolbar__splitpaneConfigPopover {
+    // Prevent white space between buttons.
+    font-size: 0;
+}
+
+.wvToolbar__splitpaneConfigNotice {
+    font-size: 1.25rem;
+    font-style: italic;
+    text-align: center;
+
+    color: #333;
+}
+
+input[type="radio"].wvToolbar__splitpaneConfigButtonInput {
+    // Hide the radio input, but make it fit the label, so we can rely on its
+    // html caracteristics without having to suffer from its design.
+    position: absolute;
+    width: 0;
+    height: 0;
+    left: 0;
+    top: 0;
+    bottom: 2px;
+    right: 0;
+    opacity: 0;
+}
+
+/* Windowing Preset */
+
+.wvToolbar__windowingPresetConfigPopover {
+
+}
+.wvToolbar__windowingPresetConfigNotice {
+    font-size: 1.25rem;
+    font-style: italic;
+    text-align: center;
+
+    color: #333;
+}
+
+.wvToolbar__windowingPresetList {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+
+    font-size: 1.5rem;
+}
+.wvToolbar__windowingPresetListItem {
+    // Remove <a> default styles. Take care - this class may either be used
+    // with <a> or <button>.
+    &:hover {
+        text-decoration: none;
+        color: white;
+    }
+
+    // Remove <button> default styles.
+    outline: none;
+    background-color: transparent;
+    border: none;
+
+    // Set relative to position button absolutely
+    position: relative;
+
+    // Style button
+    display: inline-block;
+    cursor: pointer;
+    font-variant: small-caps;
+    text-transform: lowercase;
+    text-align: center;
+    font-size: 1.3rem;
+    font-weight: 400;
+    line-height: 2.2rem;
+    color: hsl(0, 0%, 85%);
+    transition: 0.3s text-decoration ease, 0.3s border ease, 0.3s opacity ease;
+
+    // Position button
+    margin: 0;
+    min-width: 3rem;
+    padding: 0 10px;
+    line-height: 3.6rem;
+
+
+
+    // Prevent multi line buttons.
+    max-height: 2.8rem;
+    max-width: 100%;
+    overflow: hidden;
+
+    // Set margin
+    margin: 0.6rem;
+    margin-left: 0rem;
+    margin-right: 0rem;
+    & + & {
+        margin-left: 0.7rem;
+    }
+
+    // Set button size
+    line-height: 2rem;
+
+    // Align text
+    padding-top: 0.1rem;
+    padding-bottom: 0.5rem;
+
+    // Style button
+    font-size: 1.4rem;
+    border: 1px solid hsl(0, 0%, 27%);
+
+    // Set best looking font with small-caps.
+    font-family: Arial;
+
+    // Change background on hover
+    background-color: hsl(0, 0%, 0%);
+    &:hover {
+        background-color: hsl(0, 0%, 10%);
+    }
+
+    & > .glyphicon { // used with the same element as glyphicons
+        // Position button
+        position: relative;
+        display: inline-block;
+        top: 3px;
+        margin-right: 4px;
+    }
+
+    // Text color
+    color: hsl(0, 0%, 10%);
+    border: 1px solid hsl(0, 0%, 73%);
+
+    // Change background on hover
+    background-color: hsl(0, 0%, 100%);
+    &:hover {
+        color: hsl(0, 0%, 10%);
+        background-color: hsl(0, 0%, 90%);
+    }
+
+
+    width: 100%;
+    margin: 0;
+    margin-left: 0 !important;
+    border-top: none;
+    border-bottom: none;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_variable.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,14 @@
+$primary-lighten: #57aae1;
+$primary: #3498db;
+$dangerColor: #E63F24;
+
+$panel-header: #fafafa;
+$border-color: #e7e7e7;
+$lightGrey: #cccccc;
+$text-color: #666666;
+
+$darkGrey: #333333;
+$darkBlue: #203A6F;
+$blueGrey: #303E4D;
+
+$toolbarHeight: 42px;
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/_video.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,9 @@
+.wvVideo {
+    // Align component vertically & horizontally
+    position: absolute;
+    top:50%;
+    left:0;
+    width: 100%;
+    height: auto;
+    transform: translateY(-50%);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/styles.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,32 @@
+// bower:scss
+// endbower
+
+@import "webviewer.main.scss";
+@import "webviewer.components.scss";
+
+
+@media print {
+    @import "print";
+}
+
+.closePrintButton{
+    display:none;
+}
+
+body.print{
+    @import "print";
+
+    @media screen {
+        .closePrintButton{
+            display:block;
+            position: fixed;
+            top: 0;
+            right: 0;
+            padding: 10px;
+            font-size: 24px;
+            background-color: black;
+            color: white;
+            border: none;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/tb-group.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,36 @@
+.tbGroup{
+    position:relative;
+}
+.tbGroup__buttons--base,
+%tbGroup__buttons--base{
+    z-index: 5;
+    background-color: black;
+    position: absolute;
+}
+
+.tbGroup__buttons--bottom{
+    @extend .tbGroup__buttons--base;
+    right:0;  // let the element at it's initial position but align him in right (natural position is below the toggl button element)
+    display:block;
+}
+.tbGroup__buttons--left{
+    @extend .tbGroup__buttons--base;
+    right:100%;
+    top:0;
+    display:block;
+}
+.tbGroup__icon{
+    display:block;
+    position: absolute;
+    bottom:0;
+    left:0;
+
+    width: 0;
+    height: 0;
+    border-style: solid;
+    border-width: 10px 0 0 10px;
+    border-color: transparent transparent transparent rgba(255,255,255,0.1);
+    &.active{
+        border-color: transparent transparent transparent $primary;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/webviewer.components.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,59 @@
+/* wvp-ui stuffs */
+wv-webviewer {
+    display: block;
+    height: 100%;
+    overflow: hidden;
+}
+
+@import "variable";
+@import "button";
+@import "exitButton";
+@import "studyIsland";
+@import "helpers";
+@import "notice";
+@import "layout";
+@import "serieslist";
+@import "toolbar";
+@import "video";
+@import "studyInformationBreadcrumb";
+@import "selectionActionlist";
+
+/* wvb-ui stuffs */
+@import "wv-overlay.scss";
+@import "wv-pdf-viewer.scss";
+@import "wv-splitpane.scss";
+@import "wv-timeline.scss";
+@import "wv-timeline-controls.scss";
+@import "wv-loadingbar.scss";
+@import "wv-disclaimer";
+@import "tb-group";
+
+wv-viewport { // make sure the element is sized when using with drag & drop
+  display: inline-block;
+  width: 100%;
+  height: 100%;
+
+  > div {
+    position: relative;
+    width: 100%;
+    height: 100%;
+  }
+
+  // We don't set 100% width/height to the canvas element, as it would stretch
+  // the pixels. Instead, we center it for more fluid transition when pane's 
+  // width changes (at least the content is kept centered even if the js hasn't
+  // yet reacted to layout reflow).
+  > div > .wv-cornerstone-enabled-image {
+    width: 100%;
+    height: 100%;
+    text-align: center;
+  }
+}
+
+.wv-draggable-clone {
+  width: 150px;
+  height: 150px;
+  background-color: rgba(255,255,255,0.25);
+
+  // No need to set z-index (already done by jquery ui lib).
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/webviewer.main.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,90 @@
+.browsehappy {
+  margin: 0.2em 0;
+  background: #ccc;
+  color: #000;
+  padding: 0.2em 0;
+}
+
+.wv-html, .wv-body {
+    height: 100%;
+    width: 100%;
+
+    margin: 0;
+    padding: 0;
+    
+    overflow: hidden;
+}
+.wv-body {
+    background-color: black;
+    color: white;
+    position: relative;
+    overflow: hidden;
+
+    font-family: "Open Sans", Helvetica, Arial, sans-serif;
+    -webkit-tap-highlight-color: hsla(0, 0%, 0%, 0);
+    font-size: 13px;
+    font-weight: 400;
+    line-height: 1.49;
+    font-size-adjust: 100%;
+
+    // Smooth text
+    -moz-osx-font-smoothing: grayscale !important;
+    font-smoothing: antialiased !important;
+    -webkit-font-smoothing: antialiased !important;
+}
+
+.wvLoadingScreen {
+    width: 100%;
+    height: 100%;
+    background-color: black;
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 9999;
+
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.wvLoadingSpinner {
+    margin: 100px auto 0;
+    width: 70px;
+    text-align: center;
+}
+  
+.wvLoadingSpinner > div {
+    width: 18px;
+    height: 18px;
+    background-color: #FFF;
+
+    border-radius: 100%;
+    display: inline-block;
+    -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+    animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+}
+
+.wvLoadingSpinner .bounce1 {
+    -webkit-animation-delay: -0.32s;
+    animation-delay: -0.32s;
+}
+
+.wvLoadingSpinner .bounce2 {
+    -webkit-animation-delay: -0.16s;
+    animation-delay: -0.16s;
+}
+
+@-webkit-keyframes sk-bouncedelay {
+    0%, 80%, 100% { -webkit-transform: scale(0) }
+    40% { -webkit-transform: scale(1.0) }
+}
+
+@keyframes sk-bouncedelay {
+    0%, 80%, 100% { 
+        -webkit-transform: scale(0);
+        transform: scale(0);
+    } 40% { 
+        -webkit-transform: scale(1.0);
+        transform: scale(1.0);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/wv-disclaimer.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,7 @@
+.disclaimer{
+    color: $dangerColor;
+    background-color: #303030;
+    padding:5px;
+    text-align: center;
+    font-weight: bold;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/wv-loadingbar.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,33 @@
+.wv-loadingbar-image-bar {
+    cursor: pointer;
+}
+.wv-loadingbar-not-loaded {
+    fill: rgba(255, 255, 255, 0.1);
+}
+.wv-loadingbar-not-loaded, .wv-loadingbar-LOW-quality {
+    transition: none;
+}
+.wv-loadingbar-not-loaded:hover {
+    fill: rgba(255, 255, 255, 0.2);
+}
+.wv-loadingbar-LOSSLESS-quality, .wv-loadingbar-PIXELDATA-quality {
+    fill:rgba(0, 255, 0, 0.7);
+}
+.wv-loadingbar-LOSSLESS-quality:hover,
+.wv-loadingbar-LOSSLESS-quality.wv-loadingbar-active,
+.wv-loadingbar-PIXELDATA-quality:hover,
+.wv-loadingbar-PIXELDATA-quality.wv-loadingbar-active {
+    fill:rgba(0, 255, 0, 1);
+}
+.wv-loadingbar-LOW-quality {
+    fill:rgba(255, 0, 0, 0.7);
+}
+.wv-loadingbar-LOW-quality:hover, .wv-loadingbar-LOW-quality.wv-loadingbar-active {
+    fill:rgba(255, 0, 0, 1);
+}
+.wv-loadingbar-MEDIUM-quality {
+    fill:rgba(255, 95, 0, 0.7);
+}
+.wv-loadingbar-MEDIUM-quality:hover, .wv-loadingbar-MEDIUM-quality.wv-loadingbar-active {
+    fill:rgba(255, 95, 0, 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/wv-overlay.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,135 @@
+$gray: gray;
+$blue: hsla(204, 70%, 53%, 0.7);
+$red: rgba(206, 0, 0, 0.7);
+$green: rgba(0, 160, 27, .7);
+$yellow: rgba(220, 200  , 0, .9);
+$violet: rgba(255, 31, 255, .7);
+
+.wv-overlay {
+    // width&height is 0x0 to avoid capturing viewport events
+    color: orange;
+}
+
+.wv-overlay-icon {
+    width: 64px;    
+}
+
+.wvOverlay__studyBadge {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 1.5rem;
+    height: 1.5rem;
+    background-color: $gray;
+    z-index: 1;
+}
+
+.wv-overlay-topleft {
+    position: absolute;
+    top: 0rem;
+    left: 0rem;
+    text-align: left;
+}
+
+.wv-overlay-topright {
+    position: absolute;
+    top: 0rem;
+    right: 0rem;
+    text-align: right;
+}
+
+.wv-overlay-bottomright {
+    position: absolute;
+    bottom: 2em; // save 2em for the timeline
+    right: 0rem;
+    text-align: right;
+}
+
+.wv-overlay-bottomleft {
+    position: absolute;
+    bottom: 2em; // save 2em for the timeline
+    left: 0rem;
+    text-align: left;
+}
+
+.wv-overlay-timeline-wrapper {
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 1; // Make sure the representation of the selected image on the timeline appear on top of other overlay panels
+}
+
+.wv-overlay-topleft, .wv-overlay-topright, .wv-overlay-bottomright, .wv-overlay-bottomleft {
+    padding: 2rem;
+    transition: color 500ms, background-color 500ms;
+    background-color: rgba(0, 0, 0, 0.66);
+}
+
+.wv-overlay-topleft:hover, .wv-overlay-topright:hover, .wv-overlay-bottomright:hover, .wv-overlay-bottomleft:hover {
+    background-color: rgba(0, 0, 0, 0.9);
+}
+
+.wvPaneOverlay {
+    position: absolute;
+    top: 50%;
+    width: 100%;
+    transform: translateY(-50%);
+
+    font-weight: 100;
+    text-align: center;
+    color: white;
+    font-size: 2rem;
+}
+
+.wv-overlay-scrollbar-loaded {
+    position: absolute;
+    bottom:0;
+    left:0;
+    height: 5px;
+    background-color: red;
+    will-change: right;
+    transform-origin: 0% 50%;
+}
+
+.wv-overlay-scrollbar-loading {
+    position: absolute;
+    bottom:0;
+    left:0;
+    height: 5px;
+    background-color: #660000;
+    will-change: right;
+    transform-origin: 0% 50%;
+}
+
+.wv-overlay-scrollbar-text {
+    position: absolute;
+    bottom: calc(1em + 5px);
+    left: 5px;
+    height: 1em;
+    color: red;
+    font-size: 0.8em;
+    font-family: helvetica;
+}
+
+// Color related modifiers
+.wvOverlay__studyBadge--blue {
+    @extend .wvOverlay__studyBadge;
+    background-color: $blue;
+}
+.wvOverlay__studyBadge--red {
+    @extend .wvOverlay__studyBadge;
+    background-color: $red;
+}
+.wvOverlay__studyBadge--green {
+    @extend .wvOverlay__studyBadge;
+    background-color: $green;
+}
+.wvOverlay__studyBadge--yellow {
+    @extend .wvOverlay__studyBadge;
+    background-color: $yellow;
+}
+.wvOverlay__studyBadge--violet {
+    @extend .wvOverlay__studyBadge;
+    background-color: $violet;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/wv-pdf-viewer.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,1761 @@
+wv-pdf-viewer {
+    display: block;
+    width: 100%;
+    height: 100%;
+}
+
+#toolbarContainer > #toolbarViewer > #toolbarViewerLeft > .wv-pdf-viewer-closebutton { // We need high priority, !important keywords don't work
+    background-color: inherit;
+    color: hsl(0, 0%, 100%);
+    border: none;
+
+    padding: 2px;
+    margin-left: 4px;
+    margin-right: 2px;
+
+    &:hover {
+        color: black;
+    }
+}
+
+.fa.fa-window-close.wv-pdf-viewer-closebuttonicon { // We need high priority
+    font-size: 2rem;
+    line-height: 28px; // pdf.js toolbar size (- closebutton margin)
+}
+
+// The following code has been generated via:
+// 
+// ```bash
+// cd bower_components/pdf.js-viewer/
+// lessc --global-var='pdfjsImagePath="../images/pdf.js-viewer"' viewer.less viewer.css
+// ```
+
+.pdfjs .textLayer {
+  position: absolute;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  overflow: hidden;
+  opacity: 0.2;
+}
+.pdfjs .textLayer > div {
+  color: transparent;
+  position: absolute;
+  white-space: pre;
+  cursor: text;
+  -webkit-transform-origin: 0 0;
+  -moz-transform-origin: 0 0;
+  -o-transform-origin: 0 0;
+  -ms-transform-origin: 0 0;
+  transform-origin: 0 0;
+}
+.pdfjs .textLayer .highlight {
+  margin: -1px;
+  padding: 1px;
+  background-color: #b400aa;
+  border-radius: 4px;
+}
+.pdfjs .textLayer .highlight.begin {
+  border-radius: 4px 0 0 4px;
+}
+.pdfjs .textLayer .highlight.end {
+  border-radius: 0 4px 4px 0;
+}
+.pdfjs .textLayer .highlight.middle {
+  border-radius: 0;
+}
+.pdfjs .textLayer .highlight.selected {
+  background-color: #006400;
+}
+.pdfjs .textLayer ::selection {
+  background: #00f;
+}
+.pdfjs .textLayer ::-moz-selection {
+  background: #00f;
+}
+.pdfjs .pdfViewer .canvasWrapper {
+  overflow: hidden;
+}
+.pdfjs .pdfViewer .page {
+  direction: ltr;
+  width: 816px;
+  height: 1056px;
+  margin: 1px auto -8px;
+  position: relative;
+  overflow: visible;
+  border: 9px solid transparent;
+  background-clip: content-box;
+  border-image: url('../images/pdf.js-viewer/shadow.png') 9 9 repeat;
+  background-color: #fff;
+}
+body {
+  height: 100%;
+}
+.pdfjs .pdfViewer.removePageBorders .page {
+  margin: 0 auto 10px;
+  border: none;
+}
+.pdfjs .pdfViewer .page canvas {
+  margin: 0;
+  display: block;
+}
+.pdfjs .pdfViewer .page .loadingIcon {
+  position: absolute;
+  display: block;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  background: url('../images/pdf.js-viewer/loading-icon.gif') center no-repeat;
+}
+.pdfjs .pdfViewer .page .annotLink > a:hover {
+  opacity: .2;
+  background: #ff0;
+  box-shadow: 0 2px 10px #ff0;
+}
+.pdfjs .pdfPresentationMode:-webkit-full-screen .pdfViewer .page {
+  margin-bottom: 100%;
+  border: 0;
+}
+.pdfjs .pdfPresentationMode:-moz-full-screen .pdfViewer .page {
+  margin-bottom: 100%;
+  border: 0;
+}
+.pdfjs .pdfPresentationMode:-ms-fullscreen .pdfViewer .page {
+  margin-bottom: 100%!important;
+  border: 0;
+}
+.pdfjs .pdfPresentationMode:fullscreen .pdfViewer .page {
+  margin-bottom: 100%;
+  border: 0;
+}
+.pdfjs .pdfViewer .page .annotText > img {
+  position: absolute;
+  cursor: pointer;
+}
+.pdfjs .pdfViewer .page .annotTextContentWrapper {
+  position: absolute;
+  width: 20em;
+}
+.pdfjs .pdfViewer .page .annotTextContent {
+  z-index: 200;
+  float: left;
+  max-width: 20em;
+  background-color: #FF9;
+  box-shadow: 0 2px 5px #333;
+  border-radius: 2px;
+  padding: .6em;
+  cursor: pointer;
+}
+.pdfjs .pdfViewer .page .annotTextContent > h1 {
+  font-size: 1em;
+  border-bottom: 1px solid #000;
+  padding-bottom: 0.2em;
+}
+.pdfjs .pdfViewer .page .annotTextContent > p {
+  padding-top: 0.2em;
+}
+.pdfjs .pdfViewer .page .annotLink > a {
+  position: absolute;
+  font-size: 1em;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+}
+.pdfjs .pdfViewer .page .annotLink > a {
+  background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAA\ LAAAAAABAAEAAAIBRAA7") 0 0 repeat;
+}
+.pdfjs * {
+  padding: 0;
+  margin: 0;
+}
+html {
+  height: 100%;
+  font-size: 10px;
+}
+.pdfjs input,
+.pdfjs button,
+.pdfjs select {
+  font: message-box;
+  outline: none;
+}
+.pdfjs .hidden {
+  display: none !important;
+}
+.pdfjs [hidden] {
+  display: none !important;
+}
+.pdfjs #viewerContainer.pdfPresentationMode:-webkit-full-screen {
+  top: 0;
+  border-top: 2px solid transparent;
+  background-color: #000;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  cursor: none;
+  -webkit-user-select: none;
+}
+.pdfjs #viewerContainer.pdfPresentationMode:-moz-full-screen {
+  top: 0;
+  border-top: 2px solid transparent;
+  background-color: #000;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  cursor: none;
+  -moz-user-select: none;
+}
+.pdfjs #viewerContainer.pdfPresentationMode:-ms-fullscreen {
+  top: 0!important;
+  border-top: 2px solid transparent;
+  width: 100%;
+  height: 100%;
+  overflow: hidden!important;
+  cursor: none;
+  -ms-user-select: none;
+}
+.pdfjs #viewerContainer.pdfPresentationMode:-ms-fullscreen::-ms-backdrop {
+  background-color: #000;
+}
+.pdfjs #viewerContainer.pdfPresentationMode:fullscreen {
+  top: 0;
+  border-top: 2px solid transparent;
+  background-color: #000;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  cursor: none;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+}
+.pdfjs .pdfPresentationMode:-webkit-full-screen a:not(.internalLink) {
+  display: none;
+}
+.pdfjs .pdfPresentationMode:-moz-full-screen a:not(.internalLink) {
+  display: none;
+}
+.pdfjs .pdfPresentationMode:-ms-fullscreen a:not(.internalLink) {
+  display: none !important;
+}
+.pdfjs .pdfPresentationMode:fullscreen a:not(.internalLink) {
+  display: none;
+}
+.pdfjs .pdfPresentationMode:-webkit-full-screen .textLayer > div {
+  cursor: none;
+}
+.pdfjs .pdfPresentationMode:-moz-full-screen .textLayer > div {
+  cursor: none;
+}
+.pdfjs .pdfPresentationMode:-ms-fullscreen .textLayer > div {
+  cursor: none;
+}
+.pdfjs .pdfPresentationMode:fullscreen .textLayer > div {
+  cursor: none;
+}
+.pdfjs .pdfPresentationMode.pdfPresentationModeControls > *,
+.pdfjs .pdfPresentationMode.pdfPresentationModeControls .textLayer > div {
+  cursor: default;
+}
+.pdfjs .outerCenter {
+  pointer-events: none;
+  position: relative;
+}
+html[dir='ltr'] .pdfjs .outerCenter {
+  float: right;
+  right: 50%;
+}
+html[dir='rtl'] .pdfjs .outerCenter {
+  float: left;
+  left: 50%;
+}
+.pdfjs .innerCenter {
+  pointer-events: auto;
+  position: relative;
+}
+html[dir='ltr'] .pdfjs .innerCenter {
+  float: right;
+  right: -50%;
+}
+html[dir='rtl'] .pdfjs .innerCenter {
+  float: left;
+  left: -50%;
+}
+.pdfjs #outerContainer {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  background-color: #404040;
+  background-image: url('../images/pdf.js-viewer/texture.png');
+}
+.pdfjs #sidebarContainer {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  width: 200px;
+  visibility: hidden;
+  -webkit-transition-duration: 200ms;
+  -webkit-transition-timing-function: ease;
+  transition-duration: 200ms;
+  transition-timing-function: ease;
+}
+html[dir='ltr'] .pdfjs #sidebarContainer {
+  -webkit-transition-property: left;
+  transition-property: left;
+  left: -200px;
+}
+html[dir='rtl'] .pdfjs #sidebarContainer {
+  -webkit-transition-property: right;
+  transition-property: right;
+  right: -200px;
+}
+.pdfjs #outerContainer.sidebarMoving > #sidebarContainer,
+.pdfjs #outerContainer.sidebarOpen > #sidebarContainer {
+  visibility: visible;
+}
+html[dir='ltr'] .pdfjs #outerContainer.sidebarOpen > #sidebarContainer {
+  left: 0;
+}
+html[dir='rtl'] .pdfjs #outerContainer.sidebarOpen > #sidebarContainer {
+  right: 0;
+}
+.pdfjs #mainContainer {
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  min-width: 320px;
+  -webkit-transition-duration: 200ms;
+  -webkit-transition-timing-function: ease;
+  transition-duration: 200ms;
+  transition-timing-function: ease;
+}
+html[dir='ltr'] .pdfjs #outerContainer.sidebarOpen > #mainContainer {
+  -webkit-transition-property: left;
+  transition-property: left;
+  left: 200px;
+}
+html[dir='rtl'] .pdfjs #outerContainer.sidebarOpen > #mainContainer {
+  -webkit-transition-property: right;
+  transition-property: right;
+  right: 200px;
+}
+.pdfjs #sidebarContent {
+  top: 32px;
+  bottom: 0;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+  position: absolute;
+  width: 200px;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+html[dir='ltr'] .pdfjs #sidebarContent {
+  left: 0;
+  box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.25);
+}
+html[dir='rtl'] .pdfjs #sidebarContent {
+  right: 0;
+  box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.25);
+}
+.pdfjs #viewerContainer {
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+  position: absolute;
+  top: 32px;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  outline: none;
+}
+html[dir='ltr'] .pdfjs #viewerContainer {
+  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.05);
+}
+html[dir='rtl'] .pdfjs #viewerContainer {
+  box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.05);
+}
+.pdfjs .toolbar {
+  position: relative;
+  left: 0;
+  right: 0;
+  // z-index: 9999;
+  cursor: default;
+}
+.pdfjs #toolbarContainer {
+  width: 100%;
+}
+.pdfjs #toolbarSidebar {
+  width: 200px;
+  height: 32px;
+  background-color: #424242;
+  background-image: url('../images/pdf.js-viewer/texture.png'), linear-gradient(rgba(77, 77, 77, 0.99), rgba(64, 64, 64, 0.95));
+}
+html[dir='ltr'] .pdfjs #toolbarSidebar {
+  box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1);
+}
+html[dir='rtl'] .pdfjs #toolbarSidebar {
+  box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1);
+}
+.pdfjs #toolbarContainer,
+.pdfjs .findbar,
+.pdfjs .secondaryToolbar {
+  position: relative;
+  height: 32px;
+  background-color: #474747;
+  background-image: url('../images/pdf.js-viewer/texture.png'), linear-gradient(rgba(82, 82, 82, 0.99), rgba(69, 69, 69, 0.95));
+}
+html[dir='ltr'] .pdfjs #toolbarContainer,
+.pdfjs .findbar,
+.pdfjs .secondaryToolbar {
+  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.08), inset 0 1px 1px rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(0, 0, 0, 0.15), 0 1px 1px rgba(0, 0, 0, 0.1);
+}
+html[dir='rtl'] .pdfjs #toolbarContainer,
+.pdfjs .findbar,
+.pdfjs .secondaryToolbar {
+  box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.08), inset 0 1px 1px rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(0, 0, 0, 0.15), 0 1px 1px rgba(0, 0, 0, 0.1);
+}
+.pdfjs #toolbarViewer {
+  height: 32px;
+}
+.pdfjs #loadingBar {
+  position: relative;
+  width: 100%;
+  height: 4px;
+  background-color: #333;
+  border-bottom: 1px solid #333;
+}
+.pdfjs #loadingBar .progress {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 0;
+  height: 100%;
+  background-color: #ddd;
+  overflow: hidden;
+  -webkit-transition: width 200ms;
+  transition: width 200ms;
+}
+@-webkit-keyframes progressIndeterminate {
+  0% {
+    left: 0;
+  }
+  50% {
+    left: 100%;
+  }
+  100% {
+    left: 100%;
+  }
+}
+@keyframes progressIndeterminate {
+  0% {
+    left: 0;
+  }
+  50% {
+    left: 100%;
+  }
+  100% {
+    left: 100%;
+  }
+}
+.pdfjs #loadingBar .progress.indeterminate {
+  background-color: #999;
+  -webkit-transition: none;
+  transition: none;
+}
+.pdfjs #loadingBar .indeterminate .glimmer {
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  width: 50px;
+  background-image: linear-gradient(to right, #999 0%, #fff 50%, #999 100%);
+  background-size: 100% 100%;
+  background-repeat: no-repeat;
+  -webkit-animation: progressIndeterminate 2s linear infinite;
+  animation: progressIndeterminate 2s linear infinite;
+}
+.pdfjs .findbar,
+.pdfjs .secondaryToolbar {
+  top: 32px;
+  position: absolute;
+  z-index: 10000;
+  height: 32px;
+  min-width: 16px;
+  padding: 0 6px;
+  margin: 4px 2px;
+  color: #d9d9d9;
+  font-size: 12px;
+  line-height: 14px;
+  text-align: left;
+  cursor: default;
+}
+html[dir='ltr'] .pdfjs .findbar {
+  left: 68px;
+}
+html[dir='rtl'] .pdfjs .findbar {
+  right: 68px;
+}
+.pdfjs .findbar label {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+}
+.pdfjs #findInput[data-status="pending"] {
+  background-image: url('../images/pdf.js-viewer/loading-small.png');
+  background-repeat: no-repeat;
+  background-position: right;
+}
+html[dir='rtl'] .pdfjs #findInput[data-status="pending"] {
+  background-position: left;
+}
+.pdfjs .secondaryToolbar {
+  padding: 6px;
+  height: auto;
+  z-index: 30000;
+}
+html[dir='ltr'] .pdfjs .secondaryToolbar {
+  right: 4px;
+}
+html[dir='rtl'] .pdfjs .secondaryToolbar {
+  left: 4px;
+}
+.pdfjs #secondaryToolbarButtonContainer {
+  max-width: 200px;
+  max-height: 400px;
+  overflow-y: auto;
+  -webkit-overflow-scrolling: touch;
+  margin-bottom: -4px;
+}
+.pdfjs .doorHanger,
+.pdfjs .doorHangerRight {
+  border: 1px solid rgba(0, 0, 0, 0.5);
+  border-radius: 2px;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+}
+.pdfjs .doorHanger:after,
+.pdfjs .doorHanger:before,
+.pdfjs .doorHangerRight:after,
+.pdfjs .doorHangerRight:before {
+  bottom: 100%;
+  border: solid transparent;
+  content: " ";
+  height: 0;
+  width: 0;
+  position: absolute;
+  pointer-events: none;
+}
+.pdfjs .doorHanger:after,
+.pdfjs .doorHangerRight:after {
+  border-bottom-color: rgba(82, 82, 82, 0.99);
+  border-width: 8px;
+}
+.pdfjs .doorHanger:before,
+.pdfjs .doorHangerRight:before {
+  border-bottom-color: rgba(0, 0, 0, 0.5);
+  border-width: 9px;
+}
+html[dir='ltr'] .pdfjs .doorHanger:after,
+html[dir='rtl'] .pdfjs .doorHangerRight:after {
+  left: 13px;
+  margin-left: -8px;
+}
+html[dir='ltr'] .pdfjs .doorHanger:before,
+html[dir='rtl'] .pdfjs .doorHangerRight:before {
+  left: 13px;
+  margin-left: -9px;
+}
+html[dir='rtl'] .pdfjs .doorHanger:after,
+html[dir='ltr'] .pdfjs .doorHangerRight:after {
+  right: 13px;
+  margin-right: -8px;
+}
+html[dir='rtl'] .pdfjs .doorHanger:before,
+html[dir='ltr'] .pdfjs .doorHangerRight:before {
+  right: 13px;
+  margin-right: -9px;
+}
+.pdfjs #findMsg {
+  font-style: italic;
+  color: #A6B7D0;
+}
+.pdfjs #findInput.notFound {
+  background-color: #f66;
+}
+html[dir='ltr'] .pdfjs #toolbarViewerLeft {
+  margin-left: -1px;
+}
+html[dir='rtl'] .pdfjs #toolbarViewerRight {
+  margin-right: -1px;
+}
+html[dir='ltr'] .pdfjs #toolbarViewerLeft,
+html[dir='rtl'] .pdfjs #toolbarViewerRight {
+  position: absolute;
+  top: 0;
+  left: 0;
+}
+html[dir='ltr'] .pdfjs #toolbarViewerRight,
+html[dir='rtl'] .pdfjs #toolbarViewerLeft {
+  position: absolute;
+  top: 0;
+  right: 0;
+}
+html[dir='ltr'] .pdfjs #toolbarViewerLeft > *,
+html[dir='ltr'] .pdfjs #toolbarViewerMiddle > *,
+html[dir='ltr'] .pdfjs #toolbarViewerRight > *,
+html[dir='ltr'] .pdfjs .findbar > * {
+  position: relative;
+  float: left;
+}
+html[dir='rtl'] .pdfjs #toolbarViewerLeft > *,
+html[dir='rtl'] .pdfjs #toolbarViewerMiddle > *,
+html[dir='rtl'] .pdfjs #toolbarViewerRight > *,
+html[dir='rtl'] .pdfjs .findbar > * {
+  position: relative;
+  float: right;
+}
+html[dir='ltr'] .pdfjs .splitToolbarButton {
+  margin: 3px 2px 4px 0;
+  display: inline-block;
+}
+html[dir='rtl'] .pdfjs .splitToolbarButton {
+  margin: 3px 0 4px 2px;
+  display: inline-block;
+}
+html[dir='ltr'] .pdfjs .splitToolbarButton > .toolbarButton {
+  border-radius: 0;
+  float: left;
+}
+html[dir='rtl'] .pdfjs .splitToolbarButton > .toolbarButton {
+  border-radius: 0;
+  float: right;
+}
+.pdfjs .toolbarButton,
+.pdfjs .secondaryToolbarButton,
+.pdfjs .overlayButton {
+  border: 0 none;
+  background: none;
+  width: 32px;
+  height: 25px;
+}
+.pdfjs .toolbarButton > span {
+  display: inline-block;
+  width: 0;
+  height: 0;
+  overflow: hidden;
+}
+.pdfjs .toolbarButton[disabled],
+.pdfjs .secondaryToolbarButton[disabled],
+.pdfjs .overlayButton[disabled] {
+  opacity: 0.5;
+}
+.pdfjs .toolbarButton.group {
+  margin-right: 0;
+}
+.pdfjs .splitToolbarButton.toggled .toolbarButton {
+  margin: 0;
+}
+.pdfjs .splitToolbarButton:hover > .toolbarButton,
+.pdfjs .splitToolbarButton:focus > .toolbarButton,
+.pdfjs .splitToolbarButton.toggled > .toolbarButton,
+.pdfjs .toolbarButton.textButton {
+  background-color: rgba(0, 0, 0, 0.12);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  border: 1px solid rgba(0, 0, 0, 0.35);
+  border-color: rgba(0, 0, 0, 0.32) rgba(0, 0, 0, 0.38) rgba(0, 0, 0, 0.42);
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.15) inset, 0 1px 0 rgba(255, 255, 255, 0.05);
+  -webkit-transition-property: background-color, border-color, box-shadow;
+  -webkit-transition-duration: 150ms;
+  -webkit-transition-timing-function: ease;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
+  transition-timing-function: ease;
+}
+.pdfjs .splitToolbarButton > .toolbarButton:hover,
+.pdfjs .splitToolbarButton > .toolbarButton:focus,
+.pdfjs .dropdownToolbarButton:hover,
+.pdfjs .overlayButton:hover,
+.pdfjs .toolbarButton.textButton:hover,
+.pdfjs .toolbarButton.textButton:focus {
+  background-color: rgba(0, 0, 0, 0.2);
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.15) inset, 0 0 1px rgba(0, 0, 0, 0.05);
+  z-index: 199;
+}
+.pdfjs .splitToolbarButton > .toolbarButton {
+  position: relative;
+}
+html[dir='ltr'] .pdfjs .splitToolbarButton > .toolbarButton:first-child,
+html[dir='rtl'] .pdfjs .splitToolbarButton > .toolbarButton:last-child {
+  position: relative;
+  margin: 0;
+  margin-right: -1px;
+  border-top-left-radius: 2px;
+  border-bottom-left-radius: 2px;
+  border-right-color: transparent;
+}
+html[dir='ltr'] .pdfjs .splitToolbarButton > .toolbarButton:last-child,
+html[dir='rtl'] .pdfjs .splitToolbarButton > .toolbarButton:first-child {
+  position: relative;
+  margin: 0;
+  margin-left: -1px;
+  border-top-right-radius: 2px;
+  border-bottom-right-radius: 2px;
+  border-left-color: transparent;
+}
+.pdfjs .splitToolbarButtonSeparator {
+  padding: 8px 0;
+  width: 1px;
+  background-color: rgba(0, 0, 0, 0.5);
+  z-index: 99;
+  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
+  display: inline-block;
+  margin: 5px 0;
+}
+html[dir='ltr'] .pdfjs .splitToolbarButtonSeparator {
+  float: left;
+}
+html[dir='rtl'] .pdfjs .splitToolbarButtonSeparator {
+  float: right;
+}
+.pdfjs .splitToolbarButton:hover > .splitToolbarButtonSeparator,
+.pdfjs .splitToolbarButton.toggled > .splitToolbarButtonSeparator {
+  padding: 12px 0;
+  margin: 1px 0;
+  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.03);
+  -webkit-transition-property: padding;
+  -webkit-transition-duration: 10ms;
+  -webkit-transition-timing-function: ease;
+  transition-property: padding;
+  transition-duration: 10ms;
+  transition-timing-function: ease;
+}
+.pdfjs .toolbarButton,
+.pdfjs .dropdownToolbarButton,
+.pdfjs .secondaryToolbarButton,
+.pdfjs .overlayButton {
+  min-width: 16px;
+  padding: 2px 6px 0;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  color: rgba(255, 255, 255, 0.8);
+  font-size: 12px;
+  line-height: 14px;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  cursor: default;
+  -webkit-transition-property: background-color, border-color, box-shadow;
+  -webkit-transition-duration: 150ms;
+  -webkit-transition-timing-function: ease;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
+  transition-timing-function: ease;
+}
+html[dir='ltr'] .pdfjs .toolbarButton,
+html[dir='ltr'] .pdfjs .overlayButton,
+html[dir='ltr'] .pdfjs .dropdownToolbarButton {
+  margin: 3px 2px 4px 0;
+}
+html[dir='rtl'] .pdfjs .toolbarButton,
+html[dir='rtl'] .pdfjs .overlayButton,
+html[dir='rtl'] .pdfjs .dropdownToolbarButton {
+  margin: 3px 0 4px 2px;
+}
+.pdfjs .toolbarButton:hover,
+.pdfjs .toolbarButton:focus,
+.pdfjs .dropdownToolbarButton,
+.pdfjs .overlayButton,
+.pdfjs .secondaryToolbarButton:hover,
+.pdfjs .secondaryToolbarButton:focus {
+  background-color: rgba(0, 0, 0, 0.12);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  border: 1px solid rgba(0, 0, 0, 0.35);
+  border-color: rgba(0, 0, 0, 0.32) rgba(0, 0, 0, 0.38) rgba(0, 0, 0, 0.42);
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.15) inset, 0 1px 0 rgba(255, 255, 255, 0.05);
+}
+.pdfjs .toolbarButton:hover:active,
+.pdfjs .overlayButton:hover:active,
+.pdfjs .dropdownToolbarButton:hover:active,
+.pdfjs .secondaryToolbarButton:hover:active {
+  background-color: rgba(0, 0, 0, 0.2);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  border-color: rgba(0, 0, 0, 0.35) rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.45);
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1) inset, 0 0 1px rgba(0, 0, 0, 0.2) inset, 0 1px 0 rgba(255, 255, 255, 0.05);
+  -webkit-transition-property: background-color, border-color, box-shadow;
+  -webkit-transition-duration: 10ms;
+  -webkit-transition-timing-function: linear;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 10ms;
+  transition-timing-function: linear;
+}
+.pdfjs .toolbarButton.toggled,
+.pdfjs .splitToolbarButton.toggled > .toolbarButton.toggled,
+.pdfjs .secondaryToolbarButton.toggled {
+  background-color: rgba(0, 0, 0, 0.3);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  border-color: rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.45) rgba(0, 0, 0, 0.5);
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1) inset, 0 0 1px rgba(0, 0, 0, 0.2) inset, 0 1px 0 rgba(255, 255, 255, 0.05);
+  -webkit-transition-property: background-color, border-color, box-shadow;
+  -webkit-transition-duration: 10ms;
+  -webkit-transition-timing-function: linear;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 10ms;
+  transition-timing-function: linear;
+}
+.pdfjs .toolbarButton.toggled:hover:active,
+.pdfjs .splitToolbarButton.toggled > .toolbarButton.toggled:hover:active,
+.pdfjs .secondaryToolbarButton.toggled:hover:active {
+  background-color: rgba(0, 0, 0, 0.4);
+  border-color: rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.5) rgba(0, 0, 0, 0.55);
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2) inset, 0 0 1px rgba(0, 0, 0, 0.3) inset, 0 1px 0 rgba(255, 255, 255, 0.05);
+}
+.pdfjs .dropdownToolbarButton {
+  width: 120px;
+  max-width: 120px;
+  padding: 0;
+  overflow: hidden;
+  background: url('../images/pdf.js-viewer/toolbarButton-menuArrows.png') no-repeat;
+}
+html[dir='ltr'] .pdfjs .dropdownToolbarButton {
+  background-position: 95%;
+}
+html[dir='rtl'] .pdfjs .dropdownToolbarButton {
+  background-position: 5%;
+}
+.pdfjs .dropdownToolbarButton > select {
+  min-width: 140px;
+  font-size: 12px;
+  color: #f2f2f2;
+  margin: 0;
+  padding: 3px 2px 2px;
+  border: none;
+  background: rgba(0, 0, 0, 0);
+}
+.pdfjs .dropdownToolbarButton > select > option {
+  background: #3d3d3d;
+}
+.pdfjs #customScaleOption {
+  display: none;
+}
+.pdfjs #pageWidthOption {
+  border-bottom: 1px rgba(255, 255, 255, 0.5) solid;
+}
+html[dir='ltr'] .pdfjs .splitToolbarButton:first-child,
+html[dir='ltr'] .pdfjs .toolbarButton:first-child,
+html[dir='rtl'] .pdfjs .splitToolbarButton:last-child,
+html[dir='rtl'] .pdfjs .toolbarButton:last-child {
+  margin-left: 4px;
+}
+html[dir='ltr'] .pdfjs .splitToolbarButton:last-child,
+html[dir='ltr'] .pdfjs .toolbarButton:last-child,
+html[dir='rtl'] .pdfjs .splitToolbarButton:first-child,
+html[dir='rtl'] .pdfjs .toolbarButton:first-child {
+  margin-right: 4px;
+}
+.pdfjs .toolbarButtonSpacer {
+  width: 30px;
+  display: inline-block;
+  height: 1px;
+}
+.pdfjs .toolbarButtonFlexibleSpacer {
+  -webkit-box-flex: 1;
+  -moz-box-flex: 1;
+  min-width: 30px;
+}
+html[dir='ltr'] .pdfjs #findPrevious {
+  margin-left: 3px;
+}
+html[dir='ltr'] .pdfjs #findNext {
+  margin-right: 3px;
+}
+html[dir='rtl'] .pdfjs #findPrevious {
+  margin-right: 3px;
+}
+html[dir='rtl'] .pdfjs #findNext {
+  margin-left: 3px;
+}
+.pdfjs .toolbarButton::before,
+.pdfjs .secondaryToolbarButton::before {
+  position: absolute;
+  display: inline-block;
+  top: 4px;
+  left: 7px;
+}
+html[dir="ltr"] .pdfjs .secondaryToolbarButton::before {
+  left: 4px;
+}
+html[dir="rtl"] .pdfjs .secondaryToolbarButton::before {
+  right: 4px;
+}
+html[dir='ltr'] .pdfjs .toolbarButton#sidebarToggle::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-sidebarToggle.png');
+}
+html[dir='rtl'] .pdfjs .toolbarButton#sidebarToggle::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-sidebarToggle-rtl.png');
+}
+html[dir='ltr'] .pdfjs .toolbarButton#secondaryToolbarToggle::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-secondaryToolbarToggle.png');
+}
+html[dir='rtl'] .pdfjs .toolbarButton#secondaryToolbarToggle::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-secondaryToolbarToggle-rtl.png');
+}
+html[dir='ltr'] .pdfjs .toolbarButton.findPrevious::before {
+  content: url('../images/pdf.js-viewer/findbarButton-previous.png');
+}
+html[dir='rtl'] .pdfjs .toolbarButton.findPrevious::before {
+  content: url('../images/pdf.js-viewer/findbarButton-previous-rtl.png');
+}
+html[dir='ltr'] .pdfjs .toolbarButton.findNext::before {
+  content: url('../images/pdf.js-viewer/findbarButton-next.png');
+}
+html[dir='rtl'] .pdfjs .toolbarButton.findNext::before {
+  content: url('../images/pdf.js-viewer/findbarButton-next-rtl.png');
+}
+html[dir='ltr'] .pdfjs .toolbarButton.pageUp::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-pageUp.png');
+}
+html[dir='rtl'] .pdfjs .toolbarButton.pageUp::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-pageUp-rtl.png');
+}
+html[dir='ltr'] .pdfjs .toolbarButton.pageDown::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-pageDown.png');
+}
+html[dir='rtl'] .pdfjs .toolbarButton.pageDown::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-pageDown-rtl.png');
+}
+.pdfjs .toolbarButton.zoomOut::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-zoomOut.png');
+}
+.pdfjs .toolbarButton.zoomIn::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-zoomIn.png');
+}
+.pdfjs .toolbarButton.presentationMode::before,
+.pdfjs .secondaryToolbarButton.presentationMode::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-presentationMode.png');
+}
+.pdfjs .toolbarButton.print::before,
+.pdfjs .secondaryToolbarButton.print::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-print.png');
+}
+.pdfjs .toolbarButton.openFile::before,
+.pdfjs .secondaryToolbarButton.openFile::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-openFile.png');
+}
+.pdfjs .toolbarButton.download::before,
+.pdfjs .secondaryToolbarButton.download::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-download.png');
+}
+.pdfjs .toolbarButton.bookmark,
+.pdfjs .secondaryToolbarButton.bookmark {
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  outline: none;
+  padding-top: 4px;
+  text-decoration: none;
+}
+.pdfjs .secondaryToolbarButton.bookmark {
+  padding-top: 5px;
+}
+.pdfjs .bookmark[href='#'] {
+  opacity: .5;
+  pointer-events: none;
+}
+.pdfjs .toolbarButton.bookmark::before,
+.pdfjs .secondaryToolbarButton.bookmark::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-bookmark.png');
+}
+.pdfjs #viewThumbnail.toolbarButton::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-viewThumbnail.png');
+}
+html[dir="ltr"] .pdfjs #viewOutline.toolbarButton::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-viewOutline.png');
+}
+html[dir="rtl"] .pdfjs #viewOutline.toolbarButton::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-viewOutline-rtl.png');
+}
+.pdfjs #viewAttachments.toolbarButton::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-viewAttachments.png');
+}
+.pdfjs #viewFind.toolbarButton::before {
+  content: url('../images/pdf.js-viewer/toolbarButton-search.png');
+}
+.pdfjs .secondaryToolbarButton {
+  position: relative;
+  margin: 0 0 4px;
+  padding: 3px 0 1px;
+  height: auto;
+  min-height: 25px;
+  width: auto;
+  min-width: 100%;
+  white-space: normal;
+}
+html[dir="ltr"] .pdfjs .secondaryToolbarButton {
+  padding-left: 24px;
+  text-align: left;
+}
+html[dir="rtl"] .pdfjs .secondaryToolbarButton {
+  padding-right: 24px;
+  text-align: right;
+}
+html[dir="ltr"] .pdfjs .secondaryToolbarButton.bookmark {
+  padding-left: 27px;
+}
+html[dir="rtl"] .pdfjs .secondaryToolbarButton.bookmark {
+  padding-right: 27px;
+}
+html[dir="ltr"] .pdfjs .secondaryToolbarButton > span {
+  padding-right: 4px;
+}
+html[dir="rtl"] .pdfjs .secondaryToolbarButton > span {
+  padding-left: 4px;
+}
+.pdfjs .secondaryToolbarButton.firstPage::before {
+  content: url('../images/pdf.js-viewer/secondaryToolbarButton-firstPage.png');
+}
+.pdfjs .secondaryToolbarButton.lastPage::before {
+  content: url('../images/pdf.js-viewer/secondaryToolbarButton-lastPage.png');
+}
+.pdfjs .secondaryToolbarButton.rotateCcw::before {
+  content: url('../images/pdf.js-viewer/secondaryToolbarButton-rotateCcw.png');
+}
+.pdfjs .secondaryToolbarButton.rotateCw::before {
+  content: url('../images/pdf.js-viewer/secondaryToolbarButton-rotateCw.png');
+}
+.pdfjs .secondaryToolbarButton.handTool::before {
+  content: url('../images/pdf.js-viewer/secondaryToolbarButton-handTool.png');
+}
+.pdfjs .secondaryToolbarButton.documentProperties::before {
+  content: url('../images/pdf.js-viewer/secondaryToolbarButton-documentProperties.png');
+}
+.pdfjs .verticalToolbarSeparator {
+  display: block;
+  padding: 8px 0;
+  margin: 8px 4px;
+  width: 1px;
+  background-color: rgba(0, 0, 0, 0.5);
+  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
+}
+html[dir='ltr'] .pdfjs .verticalToolbarSeparator {
+  margin-left: 2px;
+}
+html[dir='rtl'] .pdfjs .verticalToolbarSeparator {
+  margin-right: 2px;
+}
+.pdfjs .horizontalToolbarSeparator {
+  display: block;
+  margin: 0 0 4px;
+  height: 1px;
+  width: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
+}
+.pdfjs .toolbarField {
+  padding: 3px 6px;
+  margin: 4px 0;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  background-color: rgba(255, 255, 255, 0.09);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  border: 1px solid rgba(0, 0, 0, 0.35);
+  border-color: rgba(0, 0, 0, 0.32) rgba(0, 0, 0, 0.38) rgba(0, 0, 0, 0.42);
+  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.05) inset, 0 1px 0 rgba(255, 255, 255, 0.05);
+  color: #f2f2f2;
+  font-size: 12px;
+  line-height: 14px;
+  outline-style: none;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
+  transition-timing-function: ease;
+}
+.pdfjs .toolbarField[type=checkbox] {
+  display: inline-block;
+  margin: 8px 0;
+}
+.pdfjs .toolbarField.pageNumber {
+  -moz-appearance: textfield;
+  min-width: 16px;
+  text-align: right;
+  width: 40px;
+}
+.pdfjs .toolbarField.pageNumber.visiblePageIsLoading {
+  background-image: url('../images/pdf.js-viewer/loading-small.png');
+  background-repeat: no-repeat;
+  background-position: 1px;
+}
+.pdfjs .toolbarField.pageNumber::-webkit-inner-spin-button,
+.pdfjs .toolbarField.pageNumber::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
+.pdfjs .toolbarField:hover {
+  background-color: rgba(255, 255, 255, 0.11);
+  border-color: rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.43) rgba(0, 0, 0, 0.45);
+}
+.pdfjs .toolbarField:focus {
+  background-color: rgba(255, 255, 255, 0.15);
+  border-color: rgba(77, 184, 255, 0.8) rgba(77, 184, 255, 0.85) rgba(77, 184, 255, 0.9);
+}
+.pdfjs .toolbarLabel {
+  min-width: 16px;
+  padding: 3px 6px 3px 2px;
+  margin: 4px 2px 4px 0;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  color: #d9d9d9;
+  font-size: 12px;
+  line-height: 14px;
+  text-align: left;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  cursor: default;
+}
+.pdfjs #thumbnailView {
+  position: absolute;
+  width: 120px;
+  top: 0;
+  bottom: 0;
+  padding: 10px 40px 0;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+}
+.pdfjs .thumbnail {
+  float: left;
+  margin-bottom: 5px;
+}
+.pdfjs #thumbnailView > a:last-of-type > .thumbnail {
+  margin-bottom: 10px;
+}
+.pdfjs #thumbnailView > a:last-of-type > .thumbnail:not([data-loaded]) {
+  margin-bottom: 9px;
+}
+.pdfjs .thumbnail:not([data-loaded]) {
+  border: 1px dashed rgba(255, 255, 255, 0.5);
+  margin: -1px -1px 4px;
+}
+.pdfjs .thumbnailImage {
+  border: 1px solid transparent;
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);
+  opacity: .8;
+  z-index: 99;
+  background-color: #fff;
+  background-clip: content-box;
+}
+.pdfjs .thumbnailSelectionRing {
+  border-radius: 2px;
+  padding: 7px;
+}
+.pdfjs a:focus > .thumbnail > .thumbnailSelectionRing > .thumbnailImage,
+.pdfjs .thumbnail:hover > .thumbnailSelectionRing > .thumbnailImage {
+  opacity: 0.9;
+}
+.pdfjs a:focus > .thumbnail > .thumbnailSelectionRing,
+.pdfjs .thumbnail:hover > .thumbnailSelectionRing {
+  background-color: rgba(255, 255, 255, 0.15);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.2) inset, 0 0 1px rgba(0, 0, 0, 0.2);
+  color: rgba(255, 255, 255, 0.9);
+}
+.pdfjs .thumbnail.selected > .thumbnailSelectionRing > .thumbnailImage {
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
+  opacity: 1;
+}
+.pdfjs .thumbnail.selected > .thumbnailSelectionRing {
+  background-color: rgba(255, 255, 255, 0.3);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.1) inset, 0 0 1px rgba(0, 0, 0, 0.2);
+  color: #ffffff;
+}
+.pdfjs #outlineView,
+.pdfjs #attachmentsView {
+  position: absolute;
+  width: 192px;
+  top: 0;
+  bottom: 0;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+}
+.pdfjs #outlineView {
+  padding: 4px 4px 0;
+}
+.pdfjs #attachmentsView {
+  padding: 3px 4px 0;
+}
+html[dir='ltr'] .pdfjs .outlineItem > .outlineItems {
+  margin-left: 20px;
+}
+html[dir='rtl'] .pdfjs .outlineItem > .outlineItems {
+  margin-right: 20px;
+}
+.pdfjs .outlineItem > a,
+.pdfjs .attachmentsItem > button {
+  text-decoration: none;
+  display: inline-block;
+  min-width: 95%;
+  height: auto;
+  margin-bottom: 1px;
+  border-radius: 2px;
+  color: rgba(255, 255, 255, 0.8);
+  font-size: 13px;
+  line-height: 15px;
+  -moz-user-select: none;
+  white-space: normal;
+}
+.pdfjs .attachmentsItem > button {
+  border: 0 none;
+  background: none;
+  cursor: pointer;
+  width: 100%;
+}
+html[dir='ltr'] .pdfjs .outlineItem > a {
+  padding: 2px 0 5px 10px;
+}
+html[dir='ltr'] .pdfjs .attachmentsItem > button {
+  padding: 2px 0 3px 7px;
+  text-align: left;
+}
+html[dir='rtl'] .pdfjs .outlineItem > a {
+  padding: 2px 10px 5px 0;
+}
+html[dir='rtl'] .pdfjs .attachmentsItem > button {
+  padding: 2px 7px 3px 0;
+  text-align: right;
+}
+.pdfjs .outlineItem > a:hover,
+.pdfjs .attachmentsItem > button:hover {
+  background-color: rgba(255, 255, 255, 0.02);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.2) inset, 0 0 1px rgba(0, 0, 0, 0.2);
+  color: rgba(255, 255, 255, 0.9);
+}
+.pdfjs .outlineItem.selected {
+  background-color: rgba(255, 255, 255, 0.08);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.1) inset, 0 0 1px rgba(0, 0, 0, 0.2);
+  color: #ffffff;
+}
+.pdfjs .noResults {
+  font-size: 12px;
+  color: rgba(255, 255, 255, 0.8);
+  font-style: italic;
+  cursor: default;
+}
+.pdfjs ::selection {
+  background: rgba(0, 0, 255, 0.3);
+}
+.pdfjs ::-moz-selection {
+  background: rgba(0, 0, 255, 0.3);
+}
+.pdfjs #errorWrapper {
+  background: none repeat scroll 0 0 #F55;
+  color: #fff;
+  left: 0;
+  position: absolute;
+  right: 0;
+  z-index: 1000;
+  padding: 3px;
+  font-size: 0.8em;
+}
+.pdfjs .loadingInProgress #errorWrapper {
+  top: 37px;
+}
+.pdfjs #errorMessageLeft {
+  float: left;
+}
+.pdfjs #errorMessageRight {
+  float: right;
+}
+.pdfjs #errorMoreInfo {
+  background-color: #FFF;
+  color: #000;
+  padding: 3px;
+  margin: 3px;
+  width: 98%;
+}
+.pdfjs .overlayButton {
+  width: auto;
+  margin: 3px 4px 2px!important;
+  padding: 2px 6px 3px;
+}
+.pdfjs #overlayContainer {
+  display: table;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.2);
+  z-index: 40000;
+}
+.pdfjs #overlayContainer > * {
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+}
+.pdfjs #overlayContainer > .container {
+  display: table-cell;
+  vertical-align: middle;
+  text-align: center;
+}
+.pdfjs #overlayContainer > .container > .dialog {
+  display: inline-block;
+  padding: 15px;
+  border-spacing: 4px;
+  color: #d9d9d9;
+  font-size: 12px;
+  line-height: 14px;
+  background-color: #474747;
+  background-image: url('../images/pdf.js-viewer/texture.png'), linear-gradient(rgba(82, 82, 82, 0.99), rgba(69, 69, 69, 0.95));
+  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.08), inset 0 1px 1px rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(0, 0, 0, 0.15), 0 1px 1px rgba(0, 0, 0, 0.1);
+  border: 1px solid rgba(0, 0, 0, 0.5);
+  border-radius: 4px;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+}
+.pdfjs .dialog > .row {
+  display: table-row;
+}
+.pdfjs .dialog > .row > * {
+  display: table-cell;
+}
+.pdfjs .dialog .toolbarField {
+  margin: 5px 0;
+}
+.pdfjs .dialog .separator {
+  display: block;
+  margin: 4px 0;
+  height: 1px;
+  width: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
+}
+.pdfjs .dialog .buttonRow {
+  text-align: center;
+  vertical-align: middle;
+}
+.pdfjs #passwordOverlay > .dialog {
+  text-align: center;
+}
+.pdfjs #passwordOverlay .toolbarField {
+  width: 200px;
+}
+.pdfjs #documentPropertiesOverlay > .dialog {
+  text-align: left;
+}
+.pdfjs #documentPropertiesOverlay .row > * {
+  min-width: 100px;
+}
+html[dir='ltr'] .pdfjs #documentPropertiesOverlay .row > * {
+  text-align: left;
+}
+html[dir='rtl'] .pdfjs #documentPropertiesOverlay .row > * {
+  text-align: right;
+}
+.pdfjs #documentPropertiesOverlay .row > span {
+  width: 125px;
+  word-wrap: break-word;
+}
+.pdfjs #documentPropertiesOverlay .row > p {
+  max-width: 225px;
+  word-wrap: break-word;
+}
+.pdfjs #documentPropertiesOverlay .buttonRow {
+  margin-top: 10px;
+}
+.pdfjs .clearBoth {
+  clear: both;
+}
+.pdfjs .fileInput {
+  background: #fff;
+  color: #000;
+  margin-top: 5px;
+  visibility: hidden;
+  position: fixed;
+  right: 0;
+  top: 0;
+}
+.pdfjs #PDFBug {
+  background: none repeat scroll 0 0 #fff;
+  border: 1px solid #666;
+  position: fixed;
+  top: 32px;
+  right: 0;
+  bottom: 0;
+  font-size: 10px;
+  padding: 0;
+  width: 300px;
+}
+.pdfjs #PDFBug .controls {
+  background: #EEE;
+  border-bottom: 1px solid #666;
+  padding: 3px;
+}
+.pdfjs #PDFBug .panels {
+  bottom: 0;
+  left: 0;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+  position: absolute;
+  right: 0;
+  top: 27px;
+}
+.pdfjs #PDFBug button.active {
+  font-weight: 700;
+}
+.pdfjs .debuggerShowText {
+  background: none repeat scroll 0 0 #ff0;
+  color: blue;
+}
+.pdfjs .debuggerHideText:hover {
+  background: none repeat scroll 0 0 #ff0;
+}
+.pdfjs #PDFBug .stats {
+  font-family: courier;
+  font-size: 10px;
+  white-space: pre;
+}
+.pdfjs #PDFBug .stats .title {
+  font-weight: 700;
+}
+.pdfjs #PDFBug table {
+  font-size: 10px;
+}
+.pdfjs #viewer.textLayer-visible .textLayer > div,
+.pdfjs #viewer.textLayer-hover .textLayer > div:hover {
+  background-color: #fff;
+  color: #000;
+}
+.pdfjs #viewer.textLayer-shadow .textLayer > div {
+  background-color: rgba(255, 255, 255, 0.6);
+  color: #000;
+}
+.pdfjs .grab-to-pan-grab {
+  cursor: url('../images/pdf.js-viewer/grab.cur'), move !important;
+  cursor: -webkit-grab !important;
+  cursor: -moz-grab !important;
+  cursor: grab !important;
+}
+.pdfjs .grab-to-pan-grab :not(input):not(textarea):not(button):not(select):not(:link) {
+  cursor: inherit !important;
+}
+.pdfjs .grab-to-pan-grab:active,
+.pdfjs .grab-to-pan-grabbing {
+  cursor: url('../images/pdf.js-viewer/grabbing.cur'), move !important;
+  cursor: -webkit-grabbing !important;
+  cursor: -moz-grabbing !important;
+  cursor: grabbing!important;
+  position: fixed;
+  background: transparent;
+  display: block;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  overflow: hidden;
+  z-index: 50000;
+}
+@page {
+  margin: 0;
+}
+.pdfjs #printContainer {
+  display: none;
+}
+@media screen and (min-resolution: 2dppx) {
+  .pdfjs .toolbarButton::before {
+    -webkit-transform: scale(0.5);
+    transform: scale(0.5);
+    top: -5px;
+  }
+  .pdfjs .secondaryToolbarButton::before {
+    -webkit-transform: scale(0.5);
+    transform: scale(0.5);
+    top: -4px;
+  }
+  html[dir='ltr'] .pdfjs .toolbarButton::before,
+  html[dir='rtl'] .pdfjs .toolbarButton::before {
+    left: -1px;
+  }
+  html[dir='ltr'] .pdfjs .secondaryToolbarButton::before {
+    left: -2px;
+  }
+  html[dir='rtl'] .pdfjs .secondaryToolbarButton::before {
+    left: 186px;
+  }
+  .pdfjs .toolbarField.pageNumber.visiblePageIsLoading,
+  .pdfjs #findInput[data-status="pending"] {
+    background-image: url('../images/pdf.js-viewer/loading-small@2x.png');
+    background-size: 16px 17px;
+  }
+  .pdfjs .dropdownToolbarButton {
+    background: url('../images/pdf.js-viewer/toolbarButton-menuArrows@2x.png') no-repeat;
+    background-size: 7px 16px;
+  }
+  html[dir='ltr'] .pdfjs .toolbarButton#sidebarToggle::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-sidebarToggle@2x.png');
+  }
+  html[dir='rtl'] .pdfjs .toolbarButton#sidebarToggle::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-sidebarToggle-rtl@2x.png');
+  }
+  html[dir='ltr'] .pdfjs .toolbarButton#secondaryToolbarToggle::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-secondaryToolbarToggle@2x.png');
+  }
+  html[dir='rtl'] .pdfjs .toolbarButton#secondaryToolbarToggle::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-secondaryToolbarToggle-rtl@2x.png');
+  }
+  html[dir='ltr'] .pdfjs .toolbarButton.findPrevious::before {
+    content: url('../images/pdf.js-viewer/findbarButton-previous@2x.png');
+  }
+  html[dir='rtl'] .pdfjs .toolbarButton.findPrevious::before {
+    content: url('../images/pdf.js-viewer/findbarButton-previous-rtl@2x.png');
+  }
+  html[dir='ltr'] .pdfjs .toolbarButton.findNext::before {
+    content: url('../images/pdf.js-viewer/findbarButton-next@2x.png');
+  }
+  html[dir='rtl'] .pdfjs .toolbarButton.findNext::before {
+    content: url('../images/pdf.js-viewer/findbarButton-next-rtl@2x.png');
+  }
+  html[dir='ltr'] .pdfjs .toolbarButton.pageUp::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-pageUp@2x.png');
+  }
+  html[dir='rtl'] .pdfjs .toolbarButton.pageUp::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-pageUp-rtl@2x.png');
+  }
+  html[dir='ltr'] .pdfjs .toolbarButton.pageDown::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-pageDown@2x.png');
+  }
+  html[dir='rtl'] .pdfjs .toolbarButton.pageDown::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-pageDown-rtl@2x.png');
+  }
+  .pdfjs .toolbarButton.zoomIn::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-zoomIn@2x.png');
+  }
+  .pdfjs .toolbarButton.zoomOut::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-zoomOut@2x.png');
+  }
+  .pdfjs .toolbarButton.presentationMode::before,
+  .pdfjs .secondaryToolbarButton.presentationMode::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-presentationMode@2x.png');
+  }
+  .pdfjs .toolbarButton.print::before,
+  .pdfjs .secondaryToolbarButton.print::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-print@2x.png');
+  }
+  .pdfjs .toolbarButton.openFile::before,
+  .pdfjs .secondaryToolbarButton.openFile::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-openFile@2x.png');
+  }
+  .pdfjs .toolbarButton.download::before,
+  .pdfjs .secondaryToolbarButton.download::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-download@2x.png');
+  }
+  .pdfjs .toolbarButton.bookmark::before,
+  .pdfjs .secondaryToolbarButton.bookmark::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-bookmark@2x.png');
+  }
+  .pdfjs #viewThumbnail.toolbarButton::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-viewThumbnail@2x.png');
+  }
+  html[dir="ltr"] .pdfjs #viewOutline.toolbarButton::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-viewOutline@2x.png');
+  }
+  html[dir="rtl"] .pdfjs #viewOutline.toolbarButton::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-viewOutline-rtl@2x.png');
+  }
+  .pdfjs #viewAttachments.toolbarButton::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-viewAttachments@2x.png');
+  }
+  .pdfjs #viewFind.toolbarButton::before {
+    content: url('../images/pdf.js-viewer/toolbarButton-search@2x.png');
+  }
+  .pdfjs .secondaryToolbarButton.firstPage::before {
+    content: url('../images/pdf.js-viewer/secondaryToolbarButton-firstPage@2x.png');
+  }
+  .pdfjs .secondaryToolbarButton.lastPage::before {
+    content: url('../images/pdf.js-viewer/secondaryToolbarButton-lastPage@2x.png');
+  }
+  .pdfjs .secondaryToolbarButton.rotateCcw::before {
+    content: url('../images/pdf.js-viewer/secondaryToolbarButton-rotateCcw@2x.png');
+  }
+  .pdfjs .secondaryToolbarButton.rotateCw::before {
+    content: url('../images/pdf.js-viewer/secondaryToolbarButton-rotateCw@2x.png');
+  }
+  .pdfjs .secondaryToolbarButton.handTool::before {
+    content: url('../images/pdf.js-viewer/secondaryToolbarButton-handTool@2x.png');
+  }
+  .pdfjs .secondaryToolbarButton.documentProperties::before {
+    content: url('../images/pdf.js-viewer/secondaryToolbarButton-documentProperties@2x.png');
+  }
+}
+@media print {
+  body {
+    background: transparent none;
+  }
+  .pdfjs #sidebarContainer,
+  .pdfjs #secondaryToolbar,
+  .pdfjs .toolbar,
+  .pdfjs #loadingBox,
+  .pdfjs #errorWrapper,
+  .pdfjs .textLayer {
+    display: none;
+  }
+  .pdfjs #viewerContainer {
+    overflow: visible;
+  }
+  .pdfjs #mainContainer,
+  .pdfjs #viewerContainer,
+  .pdfjs .page,
+  .pdfjs .page canvas {
+    position: static;
+    padding: 0;
+    margin: 0;
+  }
+  .pdfjs .page {
+    float: left;
+    display: none;
+    border: none;
+    box-shadow: none;
+    background-clip: content-box;
+    background-color: #fff;
+  }
+  .pdfjs .page[data-loaded] {
+    display: block;
+  }
+  .pdfjs .fileInput {
+    display: none;
+  }
+  body[data-mozPrintCallback] .pdfjs #outerContainer {
+    display: none;
+  }
+  body[data-mozPrintCallback] .pdfjs #printContainer {
+    display: block;
+  }
+  .pdfjs #printContainer > div {
+    position: relative;
+    top: 0;
+    left: 0;
+    overflow: hidden;
+  }
+  .pdfjs #printContainer canvas {
+    display: block;
+  }
+}
+.pdfjs .visibleLargeView,
+.pdfjs .visibleMediumView,
+.pdfjs .visibleSmallView {
+  display: none;
+}
+@media all and (max-width: 960px) {
+  html[dir='ltr'] .pdfjs #outerContainer.sidebarMoving .outerCenter,
+  html[dir='ltr'] .pdfjs #outerContainer.sidebarOpen .outerCenter {
+    float: left;
+    left: 205px;
+  }
+  html[dir='rtl'] .pdfjs #outerContainer.sidebarMoving .outerCenter,
+  html[dir='rtl'] .pdfjs #outerContainer.sidebarOpen .outerCenter {
+    float: right;
+    right: 205px;
+  }
+}
+@media all and (max-width: 900px) {
+  .pdfjs .sidebarOpen .hiddenLargeView {
+    display: none;
+  }
+  .pdfjs .sidebarOpen .visibleLargeView {
+    display: inherit;
+  }
+}
+@media all and (max-width: 860px) {
+  .pdfjs .sidebarOpen .hiddenMediumView {
+    display: none;
+  }
+  .pdfjs .sidebarOpen .visibleMediumView {
+    display: inherit;
+  }
+}
+@media all and (max-width: 770px) {
+  .pdfjs #sidebarContainer {
+    top: 32px;
+    z-index: 100;
+  }
+  .pdfjs .loadingInProgress #sidebarContainer {
+    top: 37px;
+  }
+  .pdfjs #sidebarContent {
+    top: 32px;
+    background-color: rgba(0, 0, 0, 0.7);
+  }
+  html[dir='ltr'] .pdfjs #outerContainer.sidebarOpen > #mainContainer {
+    left: 0;
+  }
+  html[dir='rtl'] .pdfjs #outerContainer.sidebarOpen > #mainContainer {
+    right: 0;
+  }
+  html[dir='ltr'] .pdfjs .outerCenter {
+    float: left;
+    left: 205px;
+  }
+  html[dir='rtl'] .pdfjs .outerCenter {
+    float: right;
+    right: 205px;
+  }
+  .pdfjs #outerContainer .hiddenLargeView,
+  .pdfjs #outerContainer .hiddenMediumView {
+    display: inherit;
+  }
+  .pdfjs #outerContainer .visibleLargeView,
+  .pdfjs #outerContainer .visibleMediumView {
+    display: none;
+  }
+}
+@media all and (max-width: 700px) {
+  .pdfjs #outerContainer .hiddenLargeView {
+    display: none;
+  }
+  .pdfjs #outerContainer .visibleLargeView {
+    display: inherit;
+  }
+}
+@media all and (max-width: 660px) {
+  .pdfjs #outerContainer .hiddenMediumView {
+    display: none;
+  }
+  .pdfjs #outerContainer .visibleMediumView {
+    display: inherit;
+  }
+}
+@media all and (max-width: 600px) {
+  .pdfjs .hiddenSmallView {
+    display: none;
+  }
+  .pdfjs .visibleSmallView {
+    display: inherit;
+  }
+  html[dir='ltr'] .pdfjs #outerContainer.sidebarMoving .outerCenter,
+  html[dir='ltr'] .pdfjs #outerContainer.sidebarOpen .outerCenter,
+  html[dir='ltr'] .pdfjs .outerCenter {
+    left: 156px;
+  }
+  html[dir='rtl'] .pdfjs #outerContainer.sidebarMoving .outerCenter,
+  html[dir='rtl'] .pdfjs #outerContainer.sidebarOpen .outerCenter,
+  html[dir='rtl'] .pdfjs .outerCenter {
+    right: 156px;
+  }
+  .pdfjs .toolbarButtonSpacer {
+    width: 0;
+  }
+}
+@media all and (max-width: 510px) {
+  .pdfjs #scaleSelectContainer,
+  .pdfjs #pageNumberLabel {
+    display: none;
+  }
+}
+/* should be hidden differently */
+#fileInput.fileInput {
+  display: none;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/wv-splitpane.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,80 @@
+$gray: gray;
+$blue: hsla(204, 70%, 53%, 0.7);
+$red: rgba(206, 0, 0, 0.7);
+$green: rgba(0, 160, 27, .7);
+$yellow: rgba(220, 200  , 0, .9);
+$violet: rgba(255, 31, 255, .7);
+
+.wvSplitpane {
+    height: 100%;
+    padding: 7px 2px 2px 2px;
+
+    // Anchor
+    position: relative;
+}
+.wvSplitpane__cell {
+    display: inline-block;
+    float: left;
+    height: 100%;
+    width: 100%;
+
+    // Anchor
+    position: relative;
+}
+.wvSplitpane__cellBorder,
+%wvSplitpane__cellBorder {
+    display: inline-block;
+    float: left;
+    height: calc(100% - 2px);
+    width: calc(100% - 2px);
+
+    border: 2px dashed transparent;
+
+    padding: 2px;
+    margin: 1px;
+}
+.wvSplitpane__cellBorder--selected {
+    @extend .wvSplitpane__cellBorder;
+
+    // Add border
+    border: 2px solid $blue;
+}
+
+// Color modifiers
+.wvSplitpane__cellBorder--blue {
+    @extend .wvSplitpane__cellBorder;
+    border-color: $blue;
+}
+
+.wvSplitpane__cellBorder--red {
+    @extend .wvSplitpane__cellBorder;
+    border-color: $red;
+}
+
+.wvSplitpane__cellBorder--green {
+    @extend .wvSplitpane__cellBorder;
+    border-color: $green;
+}
+
+.wvSplitpane__cellBorder--yellow {
+    @extend .wvSplitpane__cellBorder;
+    border-color: $yellow;
+}
+
+.wvSplitpane__cellBorder--violet {
+    @extend .wvSplitpane__cellBorder;
+    border-color: $violet;
+}
+
+// Make sure the pane keeps its size
+wv-pane-policy {
+    display: block;
+    width: 100%;
+    height: 100%;
+
+    > div[ng-transclude] {
+        display: block;
+        width: 100%;
+        height: 100%;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/wv-timeline-controls.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,194 @@
+/* wv-timeline-controls directive */
+.wv-timeline-controls {
+    padding: 0.5em 0.5em 0.5em 0.5em;
+    line-height: 1em;
+    background-color: rgba(0, 0, 0, 0.66);
+
+    text-align: center;
+
+    transition: color 500ms, background-color 500ms;
+}
+
+.wv-timeline-controls:hover {
+    background-color: rgba(0, 0, 0, 0.9);
+}
+
+// Used to make sure buttons doesn't break the style
+.wv-timeline-controls-vertical-sizing {
+    display: inline-block; 
+    line-height: 1em;
+    font-size: 1em;
+}
+
+.wv-timeline-controls-vflip {
+    // flip only the icon
+    &:before, &:after{
+        transform: scaleX(-1);
+        display: inline-block;
+    }
+}
+
+.wv-timeline-controls-button {
+    display: inline-block;
+    height: 1em;
+    width: 1em;
+    line-height: 1em;
+    font-size: 1em;
+    margin: 0;
+
+    user-select: none;
+    cursor: pointer;
+}
+
+.wv-timeline-controls-input {
+    height: 1em;
+    width: 3em;
+    padding: 0;
+    padding-bottom: 1px;
+    box-sizing: content-box;
+
+    border: none;
+    border-bottom: 1px solid hsla(35, 100%, 75%, 0.24);
+    background-color: transparent;
+    
+    text-align: right;
+}
+
+// Display play button on the right side
+.wv-timeline-controls-play-button-wrapper {
+    float: right;
+}
+
+/* wv-play-button directive */
+.wv-play-button {
+    display: inline-block;
+    position: relative;
+    line-height: 1em;
+
+    // This is for the boxing box
+    height: 3em;
+    width: 6em;
+    padding-bottom: 1em;
+    padding-left: 0.25em;
+    padding-right: 0.25em;
+}
+
+.wv-play-button:hover .wv-play-button-config-position-handler {
+    visibility: visible;
+}
+
+// This is a 0x0 div to set the position
+.wv-play-button-config-position-handler {
+    visibility: hidden;
+    position: absolute;
+    bottom: 3em;
+    left: 1em;
+    right: 0.5em;
+    // z-index: 2;
+}
+
+// The layout of play configuration
+.wv-play-button-config {
+    position: absolute;
+    bottom: 0;
+    left: -6em;
+    width: 12em;
+    padding: 1em;
+    background-color: hsla(0,1,0, 0.5);
+}
+
+/* Style range input (see http://brennaobrien.com/blog/2014/05/style-input-type-range-in-every-browser.html) */
+
+.wv-play-button-config-framerate-wrapper {
+    display: inline-block;
+    margin: 0.25em 0 0.5em 0;
+}
+input[type="range"].wv-play-button-config-framerate {
+    /*removes default webkit styles*/
+    -webkit-appearance: none;
+    
+    /*fix for FF unable to apply focus style bug */
+    border: 1px solid white;
+    
+    /*required for proper track sizing in FF*/
+    width: 10em;
+}
+input[type="range"].wv-play-button-config-framerate::-webkit-slider-runnable-track {
+    width: 10em;
+    height: 5px;
+    background: #ddd;
+    border: none;
+    border-radius: 3px;
+}
+input[type="range"].wv-play-button-config-framerate::-webkit-slider-thumb {
+    -webkit-appearance: none;
+    border: none;
+    height: 16px;
+    width: 16px;
+    border-radius: 50%;
+    background: goldenrod;
+    margin-top: -4px;
+}
+input[type="range"].wv-play-button-config-framerate:focus {
+    outline: none;
+}
+input[type="range"].wv-play-button-config-framerate:focus::-webkit-slider-runnable-track {
+    background: #ccc;
+}
+
+input[type="range"].wv-play-button-config-framerate::-moz-range-track {
+    width: 10em;
+    height: 5px;
+    background: #ddd;
+    border: none;
+    border-radius: 3px;
+}
+input[type="range"].wv-play-button-config-framerate::-moz-range-thumb {
+    border: none;
+    height: 16px;
+    width: 16px;
+    border-radius: 50%;
+    background: goldenrod;
+}
+
+/*hide the outline behind the border*/
+input[type="range"].wv-play-button-config-framerate:-moz-focusring{
+    outline: 1px solid white;
+    outline-offset: -1px;
+}
+
+input[type="range"].wv-play-button-config-framerate::-ms-track {
+    width: 10em;
+    height: 5px;
+    
+    /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */
+    background: transparent;
+    
+    /*leave room for the larger thumb to overflow with a transparent border */
+    border-color: transparent;
+    border-width: 6px 0;
+
+    /*remove default tick marks*/
+    color: transparent;
+}
+input[type="range"].wv-play-button-config-framerate::-ms-fill-lower {
+    background: #777;
+    border-radius: 10px;
+}
+input[type="range"].wv-play-button-config-framerate::-ms-fill-upper {
+    background: #ddd;
+    border-radius: 10px;
+}
+input[type="range"].wv-play-button-config-framerate::-ms-thumb {
+    border: none;
+    height: 16px;
+    width: 16px;
+    border-radius: 50%;
+    background: goldenrod;
+}
+input[type="range"].wv-play-button-config-framerate:focus::-ms-fill-lower {
+    background: #888;
+}
+input[type="range"].wv-play-button-config-framerate:focus::-ms-fill-upper {
+    background: #ccc;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/Resources/Styles/wv-timeline.scss	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,34 @@
+.wv-timeline {
+    position: relative;
+    height: 2em; // save 2px to display the "upper part of the currently selected image on the timeline"
+    &.reduced{
+        height: 5px;
+        .wv-timeline-loading-bar-wrapper{
+            width: 100%;
+            height: 100%;
+        }
+    }
+}
+
+.wv-timeline-controls-wrapper {
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    width: 16em;
+    height: 100%; 
+    color: white;
+}
+
+.wv-timeline-loading-bar-wrapper {
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    width: calc(100% - 16em);
+    height: calc(100% + 2px); 
+    
+    svg{
+        position:absolute;
+        left:0;
+        top:0;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/WebApplication/app.css	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,4752 @@
+.browsehappy {
+  margin: 0.2em 0;
+  background: #ccc;
+  color: #000;
+  padding: 0.2em 0; }
+
+.wv-html, .wv-body {
+  height: 100%;
+  width: 100%;
+  margin: 0;
+  padding: 0;
+  overflow: hidden; }
+
+.wv-body {
+  background-color: black;
+  color: white;
+  position: relative;
+  overflow: hidden;
+  font-family: "Open Sans", Helvetica, Arial, sans-serif;
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+  font-size: 13px;
+  font-weight: 400;
+  line-height: 1.49;
+  font-size-adjust: 100%;
+  -moz-osx-font-smoothing: grayscale !important;
+  font-smoothing: antialiased !important;
+  -webkit-font-smoothing: antialiased !important; }
+
+.wvLoadingScreen {
+  width: 100%;
+  height: 100%;
+  background-color: black;
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 9999;
+  display: flex;
+  align-items: center;
+  justify-content: center; }
+
+.wvLoadingSpinner {
+  margin: 100px auto 0;
+  width: 70px;
+  text-align: center; }
+
+.wvLoadingSpinner > div {
+  width: 18px;
+  height: 18px;
+  background-color: #FFF;
+  border-radius: 100%;
+  display: inline-block;
+  -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
+  animation: sk-bouncedelay 1.4s infinite ease-in-out both; }
+
+.wvLoadingSpinner .bounce1 {
+  -webkit-animation-delay: -0.32s;
+  animation-delay: -0.32s; }
+
+.wvLoadingSpinner .bounce2 {
+  -webkit-animation-delay: -0.16s;
+  animation-delay: -0.16s; }
+
+@-webkit-keyframes sk-bouncedelay {
+  0%, 80%, 100% {
+    -webkit-transform: scale(0); }
+  40% {
+    -webkit-transform: scale(1); } }
+
+@keyframes sk-bouncedelay {
+  0%, 80%, 100% {
+    -webkit-transform: scale(0);
+    transform: scale(0); }
+  40% {
+    -webkit-transform: scale(1);
+    transform: scale(1); } }
+
+/* wvp-ui stuffs */
+wv-webviewer {
+  display: block;
+  height: 100%;
+  overflow: hidden; }
+
+.wvButton, .wvButton--rotate, .wvButton--vflip, .wvButton--underline, .fa.wvButton--underline, .wvButton--border, .wvButton--borderAndWhite {
+  outline: none;
+  background-color: transparent;
+  border: none;
+  border-radius: 0;
+  position: relative;
+  display: inline-block;
+  cursor: pointer;
+  font-variant: small-caps;
+  text-transform: lowercase;
+  text-align: center;
+  font-size: 1.3rem;
+  font-weight: 400;
+  color: #d9d9d9;
+  transition: 0.3s text-decoration ease, 0.3s border ease, 0.3s opacity ease;
+  margin: 0;
+  min-width: 3rem;
+  padding: 0 10px;
+  line-height: 3.6rem; }
+  .wvButton:hover, .wvButton--rotate:hover, .wvButton--vflip:hover, .wvButton--underline:hover, .wvButton--border:hover, .wvButton--borderAndWhite:hover {
+    text-decoration: none;
+    color: white; }
+  .wvButton.wvLargeButton, .wvLargeButton.wvButton--rotate, .wvLargeButton.wvButton--vflip, .wvLargeButton.wvButton--underline, .wvLargeButton.wvButton--border, .wvLargeButton.wvButton--borderAndWhite {
+    font-size: 2rem;
+    line-height: 6.2rem;
+    padding: 0 20px; }
+
+.wvButton--rotate:before, .wvButton--rotate:after {
+  transform: rotate(90deg);
+  display: inline-block; }
+
+.wvButton--vflip:before, .wvButton--vflip:after {
+  transform: scaleX(-1);
+  display: inline-block; }
+
+.wvButton--underline, .fa.wvButton--underline {
+  position: relative;
+  background-color: inherit;
+  text-decoration: none;
+  text-align: left;
+  font-size: 1.2rem;
+  width: 3.2rem;
+  vertical-align: middle;
+  color: white;
+  opacity: 0.75;
+  border: none;
+  border-bottom: 2px solid rgba(255, 255, 255, 0.1);
+  top: 0px; }
+  .wvButton--underline.wvLargeButton, .fa.wvButton--underline.wvLargeButton {
+    font-size: 2rem;
+    width: 6.4rem; }
+  .wvButton--underline *, .fa.wvButton--underline * {
+    pointer-events: none; }
+  .wvButton--underline:hover, .wvButton--underline:active, .wvButton--underline:focus, .fa.wvButton--underline:hover, .fa.wvButton--underline:active, .fa.wvButton--underline:focus {
+    outline: 0; }
+  .wvButton--underline:hover, .wvButton--underline:focus, .fa.wvButton--underline:hover, .fa.wvButton--underline:focus {
+    border-color: white;
+    opacity: 1; }
+    .wvButton--underline:hover .wvButton__bottomTriangle, .wvButton--underline:focus .wvButton__bottomTriangle, .fa.wvButton--underline:hover .wvButton__bottomTriangle, .fa.wvButton--underline:focus .wvButton__bottomTriangle {
+      border-left-color: white; }
+  .wvButton--underline.active, .fa.wvButton--underline.active {
+    opacity: 1;
+    border-color: #3498db; }
+  .wvButton--underline::before, .fa.wvButton--underline::before {
+    position: relative;
+    top: -1px; }
+  .wvButton--underline.fa, .fa.wvButton--underline.fa {
+    top: 0px;
+    font-weight: 800; }
+
+.wvButton__bottomTriangle {
+  transition: 0.3s border ease, 0.3s opacity ease;
+  display: block;
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  width: 0;
+  height: 0;
+  border-style: solid;
+  border-width: 10px 0 0 10px;
+  border-color: transparent transparent transparent rgba(255, 255, 255, 0.1); }
+  .wvButton__bottomTriangle.active {
+    border-color: transparent transparent transparent #3498db !important; }
+    .wvButton__bottomTriangle.active.toggled {
+      border-left-color: #3498db !important; }
+
+.wvButton--border, .wvButton--borderAndWhite {
+  max-height: calc(2.8rem - 3px);
+  max-width: 100%;
+  overflow: hidden;
+  margin: 0.6rem;
+  margin-left: 0rem;
+  margin-right: 0rem;
+  line-height: 2rem;
+  padding-top: 0.1rem;
+  padding-bottom: 0.5rem;
+  font-size: 1.4rem;
+  border: 1px solid #454545;
+  font-family: Arial;
+  background-color: black; }
+  .wvButton--border + .wvButton--border, .wvButton--borderAndWhite + .wvButton--border, .wvButton--border + .wvButton--borderAndWhite, .wvButton--borderAndWhite + .wvButton--borderAndWhite {
+    margin-left: 0.7rem; }
+  .wvButton--border:hover, .wvButton--borderAndWhite:hover {
+    background-color: #1a1a1a; }
+  .wvButton--border > .glyphicon, .wvButton--borderAndWhite > .glyphicon {
+    position: relative;
+    display: inline-block;
+    top: 3px;
+    margin-right: 4px; }
+
+.wvButton--borderAndWhite {
+  color: #1a1a1a;
+  border: 1px solid #bababa;
+  background-color: white; }
+  .wvButton--borderAndWhite:hover {
+    color: #1a1a1a;
+    background-color: #e6e6e6; }
+
+.wvExitButton {
+  margin-left: 1rem;
+  margin-top: .25rem;
+  font-size: 1.25em;
+  color: white;
+  opacity: .66;
+  transition: .3s opacity ease;
+  background-color: inherit;
+  border: none;
+  text-decoration: none;
+  text-align: left;
+  padding: 0;
+  cursor: pointer;
+  font-family: inherit;
+  line-height: inherit; }
+  .wvExitButton:hover, .wvExitButton:focus {
+    opacity: 1;
+    outline: 0; }
+
+.wvExitButton__text {
+  position: relative;
+  top: -1px; }
+
+.wvStudyIsland--blue, .wvStudyIsland--red, .wvStudyIsland--green, .wvStudyIsland--yellow, .wvStudyIsland--violet {
+  margin: 1rem 1rem 1rem 1rem;
+  border: 0.3rem solid gray; }
+
+.wvStudyIsland__header--blue, .wvStudyIsland__header--red, .wvStudyIsland__header--green, .wvStudyIsland__header--yellow, .wvStudyIsland__header--violet {
+  background-color: gray;
+  padding: 0.5rem 0.5rem 0.8rem 0.5rem;
+  line-height: 1.35rem;
+  width: 100%; }
+
+.wvStudyIsland__actions {
+  float: right;
+  margin-top: -0.8rem;
+  margin-right: -0.8rem; }
+
+.wvStudyIsland__actions--oneCol {
+  float: none;
+  text-align: center; }
+
+.wvStudyIsland__main {
+  padding: 0.4rem;
+  color: white;
+  width: 100%; }
+
+.wvStudyIsland--blue {
+  border-color: rgba(51, 152, 219, 0.7); }
+
+.wvStudyIsland__header--blue {
+  background-color: rgba(51, 152, 219, 0.7); }
+
+.wvStudyIsland--red {
+  border-color: rgba(206, 0, 0, 0.7); }
+
+.wvStudyIsland__header--red {
+  background-color: rgba(206, 0, 0, 0.7); }
+
+.wvStudyIsland--green {
+  border-color: rgba(0, 160, 27, 0.7); }
+
+.wvStudyIsland__header--green {
+  background-color: rgba(0, 160, 27, 0.7); }
+
+.wvStudyIsland--yellow {
+  border-color: rgba(220, 200, 0, 0.9); }
+
+.wvStudyIsland__header--yellow {
+  background-color: rgba(220, 200, 0, 0.9); }
+
+.wvStudyIsland--violet {
+  border-color: rgba(255, 31, 255, 0.7); }
+
+.wvStudyIsland__header--violet {
+  background-color: rgba(255, 31, 255, 0.7); }
+
+/*
+ *  Source code taken from private Osimis' frontend toolbox 3.2.1.
+ */
+/**
+    _overlay.scss
+ */
+.overlay__transparent {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1; }
+
+/** _transition.scss **/
+.transition {
+  transition: 0.3s all ease; }
+
+.transition--long {
+  transition: 0.6s all ease; }
+
+/** _list.scss **/
+dd + dt {
+  clear: both; }
+
+.listDefinition {
+  width: 100%;
+  line-height: 1.3; }
+
+.listDefinition__term {
+  clear: both;
+  float: left;
+  text-align: right;
+  padding-right: 10px;
+  width: 50%; }
+
+.listDefinition__data {
+  text-align: left;
+  padding-left: 10px;
+  float: right;
+  width: 50%; }
+
+/** _animation.scss **/
+@keyframes blink__primary {
+  0% {
+    color: #3498db; }
+  100% {
+    color: #666666; } }
+
+.blink__primary {
+  animation: blink__primary 0.8s linear infinite; }
+
+[translate-cloak] {
+  transition: 0.3s all ease;
+  opacity: 1; }
+  [translate-cloak].translate-cloak {
+    opacity: 0; }
+
+/** _button.scss **/
+.button__unstyled, .button__base, .button__state--active, .button__state--inactive, .button__iconed, .button__switch--base, .button__switch, .button__switch--first, .button__switch--last, .button__lightgrey--hover, .button__text-danger--hover, .button__text-primary--hover, .button__danger--hover, .button__text, .button__text--underlined, .button__bordered, .button__bordered--inverted, .button__close {
+  background-color: inherit;
+  border: none;
+  text-decoration: none;
+  text-align: left;
+  padding: 0;
+  cursor: pointer; }
+  .button__unstyled *, .button__base *, .button__state--active *, .button__state--inactive *, .button__iconed *, .button__switch--base *, .button__switch *, .button__switch--first *, .button__switch--last *, .button__lightgrey--hover *, .button__text-danger--hover *, .button__text-primary--hover *, .button__danger--hover *, .button__text *, .button__text--underlined *, .button__bordered *, .button__bordered--inverted *, .button__close * {
+    pointer-events: none; }
+  .button__unstyled:hover, .button__base:hover, .button__state--active:hover, .button__state--inactive:hover, .button__iconed:hover, .button__switch--base:hover, .button__switch:hover, .button__switch--first:hover, .button__switch--last:hover, .button__lightgrey--hover:hover, .button__text-danger--hover:hover, .button__text-primary--hover:hover, .button__danger--hover:hover, .button__text:hover, .button__text--underlined:hover, .button__bordered:hover, .button__bordered--inverted:hover, .button__close:hover, .button__unstyled:active, .button__base:active, .button__state--active:active, .button__state--inactive:active, .button__iconed:active, .button__switch--base:active, .button__switch:active, .button__switch--first:active, .button__switch--last:active, .button__lightgrey--hover:active, .button__text-danger--hover:active, .button__text-primary--hover:active, .button__danger--hover:active, .button__text:active, .button__text--underlined:active, .button__bordered:active, .button__bordered--inverted:active, .button__close:active, .button__unstyled:focus, .button__base:focus, .button__state--active:focus, .button__state--inactive:focus, .button__iconed:focus, .button__switch--base:focus, .button__switch:focus, .button__switch--first:focus, .button__switch--last:focus, .button__lightgrey--hover:focus, .button__text-danger--hover:focus, .button__text-primary--hover:focus, .button__danger--hover:focus, .button__text:focus, .button__text--underlined:focus, .button__bordered:focus, .button__bordered--inverted:focus, .button__close:focus {
+    outline: 0; }
+
+.button__base, .button__state--active, .button__state--inactive, .button__iconed, .button__switch--base, .button__switch, .button__switch--first, .button__switch--last, .button__lightgrey--hover, .button__text-danger--hover, .button__text-primary--hover, .button__danger--hover, .button__text, .button__text--underlined, .button__bordered, .button__bordered--inverted, .button__close {
+  transition: 0.3s all ease; }
+
+.button__state--active {
+  opacity: 1; }
+  .button__state--active:hover {
+    opacity: 0.9;
+    color: #3498db; }
+
+.button__state--inactive {
+  opacity: 0.333; }
+  .button__state--inactive:hover {
+    opacity: 0.4333;
+    color: #3498db; }
+
+.button__iconed {
+  opacity: 1; }
+  .button__iconed:hover, .button__iconed:focus, .button__iconed.active {
+    opacity: 0.75;
+    color: #3498db; }
+
+.button__switch--base, .button__switch, .button__switch--first, .button__switch--last {
+  padding: 5px 0px;
+  display: inline-block;
+  text-align: center;
+  border-top: 1px solid;
+  border-bottom: 1px solid;
+  border-color: #3498db;
+  background-color: white;
+  overflow: hidden; }
+  .button__switch--base:hover, .button__switch:hover, .button__switch--first:hover, .button__switch--last:hover, .button__switch--base:focus, .button__switch:focus, .button__switch--first:focus, .button__switch--last:focus {
+    background-color: #5faee3;
+    color: white; }
+  .button__switch--base.active, .active.button__switch, .active.button__switch--first, .active.button__switch--last {
+    background-color: #3498db;
+    color: white; }
+
+.button__switch--first {
+  border-left: 1px solid #3498db;
+  border-radius: 5px 0 0 5px; }
+
+.button__switch--last {
+  border-right: 1px solid #3498db;
+  border-radius: 0 5px 5px 0; }
+
+.button__lightgrey--hover:hover, .button__lightgrey--hover:focus, .button__lightgrey--hover.active {
+  background-color: #cccccc; }
+
+.button__text-danger--hover:hover, .button__text-danger--hover:focus, .button__text-danger--hover.active {
+  color: #E63F24; }
+
+.button__text-primary--hover:hover, .button__text-primary--hover:focus, .button__text-primary--hover.active {
+  color: #3498db; }
+
+.button__danger--hover:hover, .button__danger--hover:focus, .button__danger--hover.active {
+  background-color: #E63F24;
+  color: white; }
+
+.button__text {
+  opacity: 0.66; }
+  .button__text:hover, .button__text.active, .button__text:focus {
+    opacity: 1; }
+
+.button__text--underlined {
+  text-decoration: underline; }
+  .button__text--underlined:hover, .button__text--underlined.active, .button__text--underlined:focus {
+    text-decoration: none; }
+
+.button__bordered {
+  border-bottom: 2px solid #666666; }
+  .button__bordered:hover, .button__bordered:focus, .button__bordered.active {
+    border-color: #3498db; }
+
+.button__bordered--inverted {
+  border-bottom: 2px solid white; }
+
+.button__close {
+  position: absolute;
+  top: 0;
+  right: 0;
+  opacity: 0.6;
+  z-index: 10; }
+  .button__close:hover, .button__close:focus {
+    opacity: 1; }
+
+/** _block.scss **/
+.block {
+  display: block !important; }
+
+/** _boxsizing.scss **/
+.boxsizing__borderbox {
+  box-sizing: border-box; }
+
+.boxsizing__contentbox {
+  box-sizing: content-box; }
+
+/** _scrollable.scss **/
+.scrollable {
+  overflow-y: auto; }
+
+.scrollable--x {
+  overflow-x: auto; }
+
+.no-scroll {
+  overflow: hidden; }
+
+/** _float.scss **/
+.float__right {
+  float: right; }
+
+.float__left {
+  float: left; }
+
+/** _fonts.scss **/
+.font__bold {
+  font-weight: 600; }
+
+.font__normal, .listDefinition__data {
+  font-weight: 400; }
+
+.font__light, .listDefinition__term {
+  font-weight: 200; }
+
+.fontColor__primary {
+  color: #3498db; }
+
+.fontColor__lightGrey {
+  color: #cccccc; }
+
+.fontColor__normal {
+  color: #666666; }
+
+.fontColor__darker {
+  color: #333333; }
+
+.fontColor__white {
+  color: white; }
+
+/** _forms.scss **/
+.textarea__unstyled {
+  border: none;
+  outline: none;
+  background-color: transparent;
+  color: inherit;
+  resize: none;
+  padding: 0;
+  margin: 0;
+  width: 100%; }
+
+/** _position.scss **/
+.position__relative {
+  position: relative; }
+
+/** _margin.scss **/
+.margin__auto {
+  margin: auto; }
+
+/** _helpers.scss **/
+/*************HELPERS**************/
+/**********************************/
+/*** identical width and height ***/
+.wh__0 {
+  width: 0px !important;
+  height: 0px !important; }
+
+.lh__0 {
+  line-height: 0px !important; }
+
+.wh__5 {
+  width: 5px !important;
+  height: 5px !important; }
+
+.lh__5 {
+  line-height: 5px !important; }
+
+.wh__8 {
+  width: 8px !important;
+  height: 8px !important; }
+
+.lh__8 {
+  line-height: 8px !important; }
+
+.wh__10 {
+  width: 10px !important;
+  height: 10px !important; }
+
+.lh__10 {
+  line-height: 10px !important; }
+
+.wh__11 {
+  width: 11px !important;
+  height: 11px !important; }
+
+.lh__11 {
+  line-height: 11px !important; }
+
+.wh__12 {
+  width: 12px !important;
+  height: 12px !important; }
+
+.lh__12 {
+  line-height: 12px !important; }
+
+.wh__13 {
+  width: 13px !important;
+  height: 13px !important; }
+
+.lh__13 {
+  line-height: 13px !important; }
+
+.wh__14 {
+  width: 14px !important;
+  height: 14px !important; }
+
+.lh__14 {
+  line-height: 14px !important; }
+
+.wh__15 {
+  width: 15px !important;
+  height: 15px !important; }
+
+.lh__15 {
+  line-height: 15px !important; }
+
+.wh__16 {
+  width: 16px !important;
+  height: 16px !important; }
+
+.lh__16 {
+  line-height: 16px !important; }
+
+.wh__17 {
+  width: 17px !important;
+  height: 17px !important; }
+
+.lh__17 {
+  line-height: 17px !important; }
+
+.wh__18 {
+  width: 18px !important;
+  height: 18px !important; }
+
+.lh__18 {
+  line-height: 18px !important; }
+
+.wh__19 {
+  width: 19px !important;
+  height: 19px !important; }
+
+.lh__19 {
+  line-height: 19px !important; }
+
+.wh__20 {
+  width: 20px !important;
+  height: 20px !important; }
+
+.lh__20 {
+  line-height: 20px !important; }
+
+.wh__21 {
+  width: 21px !important;
+  height: 21px !important; }
+
+.lh__21 {
+  line-height: 21px !important; }
+
+.wh__22 {
+  width: 22px !important;
+  height: 22px !important; }
+
+.lh__22 {
+  line-height: 22px !important; }
+
+.wh__23 {
+  width: 23px !important;
+  height: 23px !important; }
+
+.lh__23 {
+  line-height: 23px !important; }
+
+.wh__24 {
+  width: 24px !important;
+  height: 24px !important; }
+
+.lh__24 {
+  line-height: 24px !important; }
+
+.wh__25 {
+  width: 25px !important;
+  height: 25px !important; }
+
+.lh__25 {
+  line-height: 25px !important; }
+
+.wh__26 {
+  width: 26px !important;
+  height: 26px !important; }
+
+.lh__26 {
+  line-height: 26px !important; }
+
+.wh__27 {
+  width: 27px !important;
+  height: 27px !important; }
+
+.lh__27 {
+  line-height: 27px !important; }
+
+.wh__28 {
+  width: 28px !important;
+  height: 28px !important; }
+
+.lh__28 {
+  line-height: 28px !important; }
+
+.wh__29 {
+  width: 29px !important;
+  height: 29px !important; }
+
+.lh__29 {
+  line-height: 29px !important; }
+
+.wh__30 {
+  width: 30px !important;
+  height: 30px !important; }
+
+.lh__30 {
+  line-height: 30px !important; }
+
+.wh__31 {
+  width: 31px !important;
+  height: 31px !important; }
+
+.lh__31 {
+  line-height: 31px !important; }
+
+.wh__32 {
+  width: 32px !important;
+  height: 32px !important; }
+
+.lh__32 {
+  line-height: 32px !important; }
+
+.wh__33 {
+  width: 33px !important;
+  height: 33px !important; }
+
+.lh__33 {
+  line-height: 33px !important; }
+
+.wh__34 {
+  width: 34px !important;
+  height: 34px !important; }
+
+.lh__34 {
+  line-height: 34px !important; }
+
+.wh__35 {
+  width: 35px !important;
+  height: 35px !important; }
+
+.lh__35 {
+  line-height: 35px !important; }
+
+.wh__36 {
+  width: 36px !important;
+  height: 36px !important; }
+
+.lh__36 {
+  line-height: 36px !important; }
+
+.wh__37 {
+  width: 37px !important;
+  height: 37px !important; }
+
+.lh__37 {
+  line-height: 37px !important; }
+
+.wh__38 {
+  width: 38px !important;
+  height: 38px !important; }
+
+.lh__38 {
+  line-height: 38px !important; }
+
+.wh__39 {
+  width: 39px !important;
+  height: 39px !important; }
+
+.lh__39 {
+  line-height: 39px !important; }
+
+.wh__40 {
+  width: 40px !important;
+  height: 40px !important; }
+
+.lh__40 {
+  line-height: 40px !important; }
+
+.wh__41 {
+  width: 41px !important;
+  height: 41px !important; }
+
+.lh__41 {
+  line-height: 41px !important; }
+
+.wh__42 {
+  width: 42px !important;
+  height: 42px !important; }
+
+.lh__42 {
+  line-height: 42px !important; }
+
+.wh__43 {
+  width: 43px !important;
+  height: 43px !important; }
+
+.lh__43 {
+  line-height: 43px !important; }
+
+.wh__44 {
+  width: 44px !important;
+  height: 44px !important; }
+
+.lh__44 {
+  line-height: 44px !important; }
+
+.wh__45 {
+  width: 45px !important;
+  height: 45px !important; }
+
+.lh__45 {
+  line-height: 45px !important; }
+
+.wh__46 {
+  width: 46px !important;
+  height: 46px !important; }
+
+.lh__46 {
+  line-height: 46px !important; }
+
+.wh__47 {
+  width: 47px !important;
+  height: 47px !important; }
+
+.lh__47 {
+  line-height: 47px !important; }
+
+.wh__48 {
+  width: 48px !important;
+  height: 48px !important; }
+
+.lh__48 {
+  line-height: 48px !important; }
+
+.wh__49 {
+  width: 49px !important;
+  height: 49px !important; }
+
+.lh__49 {
+  line-height: 49px !important; }
+
+.wh__50 {
+  width: 50px !important;
+  height: 50px !important; }
+
+.lh__50 {
+  line-height: 50px !important; }
+
+.wh__55 {
+  width: 55px !important;
+  height: 55px !important; }
+
+.lh__55 {
+  line-height: 55px !important; }
+
+.wh__60 {
+  width: 60px !important;
+  height: 60px !important; }
+
+.lh__60 {
+  line-height: 60px !important; }
+
+.wh__64 {
+  width: 64px !important;
+  height: 64px !important; }
+
+.lh__64 {
+  line-height: 64px !important; }
+
+.wh__65 {
+  width: 65px !important;
+  height: 65px !important; }
+
+.lh__65 {
+  line-height: 65px !important; }
+
+.wh__70 {
+  width: 70px !important;
+  height: 70px !important; }
+
+.lh__70 {
+  line-height: 70px !important; }
+
+.wh__72 {
+  width: 72px !important;
+  height: 72px !important; }
+
+.lh__72 {
+  line-height: 72px !important; }
+
+.wh__75 {
+  width: 75px !important;
+  height: 75px !important; }
+
+.lh__75 {
+  line-height: 75px !important; }
+
+.wh__80 {
+  width: 80px !important;
+  height: 80px !important; }
+
+.lh__80 {
+  line-height: 80px !important; }
+
+.wh__85 {
+  width: 85px !important;
+  height: 85px !important; }
+
+.lh__85 {
+  line-height: 85px !important; }
+
+.wh__90 {
+  width: 90px !important;
+  height: 90px !important; }
+
+.lh__90 {
+  line-height: 90px !important; }
+
+.wh__95 {
+  width: 95px !important;
+  height: 95px !important; }
+
+.lh__95 {
+  line-height: 95px !important; }
+
+.wh__96 {
+  width: 96px !important;
+  height: 96px !important; }
+
+.lh__96 {
+  line-height: 96px !important; }
+
+.wh__100 {
+  width: 100px !important;
+  height: 100px !important; }
+
+.lh__100 {
+  line-height: 100px !important; }
+
+.wh__110 {
+  width: 110px !important;
+  height: 110px !important; }
+
+.lh__110 {
+  line-height: 110px !important; }
+
+.wh__120 {
+  width: 120px !important;
+  height: 120px !important; }
+
+.lh__120 {
+  line-height: 120px !important; }
+
+.wh__130 {
+  width: 130px !important;
+  height: 130px !important; }
+
+.lh__130 {
+  line-height: 130px !important; }
+
+.wh__140 {
+  width: 140px !important;
+  height: 140px !important; }
+
+.lh__140 {
+  line-height: 140px !important; }
+
+.wh__150 {
+  width: 150px !important;
+  height: 150px !important; }
+
+.lh__150 {
+  line-height: 150px !important; }
+
+.wh__160 {
+  width: 160px !important;
+  height: 160px !important; }
+
+.lh__160 {
+  line-height: 160px !important; }
+
+.wh__170 {
+  width: 170px !important;
+  height: 170px !important; }
+
+.lh__170 {
+  line-height: 170px !important; }
+
+.wh__180 {
+  width: 180px !important;
+  height: 180px !important; }
+
+.lh__180 {
+  line-height: 180px !important; }
+
+.wh__190 {
+  width: 190px !important;
+  height: 190px !important; }
+
+.lh__190 {
+  line-height: 190px !important; }
+
+.wh__200 {
+  width: 200px !important;
+  height: 200px !important; }
+
+.lh__200 {
+  line-height: 200px !important; }
+
+.wh__210 {
+  width: 210px !important;
+  height: 210px !important; }
+
+.lh__210 {
+  line-height: 210px !important; }
+
+.wh__220 {
+  width: 220px !important;
+  height: 220px !important; }
+
+.lh__220 {
+  line-height: 220px !important; }
+
+.wh__230 {
+  width: 230px !important;
+  height: 230px !important; }
+
+.lh__230 {
+  line-height: 230px !important; }
+
+.wh__240 {
+  width: 240px !important;
+  height: 240px !important; }
+
+.lh__240 {
+  line-height: 240px !important; }
+
+.wh__250 {
+  width: 250px !important;
+  height: 250px !important; }
+
+.lh__250 {
+  line-height: 250px !important; }
+
+.wh__260 {
+  width: 260px !important;
+  height: 260px !important; }
+
+.lh__260 {
+  line-height: 260px !important; }
+
+.wh__270 {
+  width: 270px !important;
+  height: 270px !important; }
+
+.lh__270 {
+  line-height: 270px !important; }
+
+.wh__280 {
+  width: 280px !important;
+  height: 280px !important; }
+
+.lh__280 {
+  line-height: 280px !important; }
+
+.wh__290 {
+  width: 290px !important;
+  height: 290px !important; }
+
+.lh__290 {
+  line-height: 290px !important; }
+
+.wh__300 {
+  width: 300px !important;
+  height: 300px !important; }
+
+.lh__300 {
+  line-height: 300px !important; }
+
+.wh__310 {
+  width: 310px !important;
+  height: 310px !important; }
+
+.lh__310 {
+  line-height: 310px !important; }
+
+.wh__320 {
+  width: 320px !important;
+  height: 320px !important; }
+
+.lh__320 {
+  line-height: 320px !important; }
+
+.wh__330 {
+  width: 330px !important;
+  height: 330px !important; }
+
+.lh__330 {
+  line-height: 330px !important; }
+
+.wh__340 {
+  width: 340px !important;
+  height: 340px !important; }
+
+.lh__340 {
+  line-height: 340px !important; }
+
+.wh__350 {
+  width: 350px !important;
+  height: 350px !important; }
+
+.lh__350 {
+  line-height: 350px !important; }
+
+.wh__360 {
+  width: 360px !important;
+  height: 360px !important; }
+
+.lh__360 {
+  line-height: 360px !important; }
+
+.wh__370 {
+  width: 370px !important;
+  height: 370px !important; }
+
+.lh__370 {
+  line-height: 370px !important; }
+
+.wh__380 {
+  width: 380px !important;
+  height: 380px !important; }
+
+.lh__380 {
+  line-height: 380px !important; }
+
+.wh__390 {
+  width: 390px !important;
+  height: 390px !important; }
+
+.lh__390 {
+  line-height: 390px !important; }
+
+.wh__400 {
+  width: 400px !important;
+  height: 400px !important; }
+
+.lh__400 {
+  line-height: 400px !important; }
+
+.wh__410 {
+  width: 410px !important;
+  height: 410px !important; }
+
+.lh__410 {
+  line-height: 410px !important; }
+
+.wh__420 {
+  width: 420px !important;
+  height: 420px !important; }
+
+.lh__420 {
+  line-height: 420px !important; }
+
+.wh__430 {
+  width: 430px !important;
+  height: 430px !important; }
+
+.lh__430 {
+  line-height: 430px !important; }
+
+.wh__440 {
+  width: 440px !important;
+  height: 440px !important; }
+
+.lh__440 {
+  line-height: 440px !important; }
+
+.wh__450 {
+  width: 450px !important;
+  height: 450px !important; }
+
+.lh__450 {
+  line-height: 450px !important; }
+
+.wh__460 {
+  width: 460px !important;
+  height: 460px !important; }
+
+.lh__460 {
+  line-height: 460px !important; }
+
+.wh__470 {
+  width: 470px !important;
+  height: 470px !important; }
+
+.lh__470 {
+  line-height: 470px !important; }
+
+.wh__480 {
+  width: 480px !important;
+  height: 480px !important; }
+
+.lh__480 {
+  line-height: 480px !important; }
+
+.wh__490 {
+  width: 490px !important;
+  height: 490px !important; }
+
+.lh__490 {
+  line-height: 490px !important; }
+
+.wh__500 {
+  width: 500px !important;
+  height: 500px !important; }
+
+.lh__500 {
+  line-height: 500px !important; }
+
+.lh__1 {
+  line-height: 1 !important; }
+
+.no-wrap {
+  white-space: nowrap; }
+
+.ov-h {
+  overflow: hidden; }
+
+.va-m {
+  vertical-align: middle; }
+
+.bg-inherit {
+  background-color: inherit; }
+
+.bg-black {
+  background-color: black; }
+
+.v-center:before {
+  content: '';
+  display: inline-block;
+  height: 100%;
+  vertical-align: middle;
+  margin-top: -0.25em;
+  /* Adjusts for spacing */ }
+
+.fluid-height {
+  height: 100%; }
+
+.visibility__hidden {
+  visibility: hidden; }
+
+.pointerEvents__none {
+  pointer-events: none; }
+
+/* Padding Helpers */
+.pn {
+  padding: 0 !important; }
+
+.p1 {
+  padding: 1px !important; }
+
+.p2 {
+  padding: 2px !important; }
+
+.p3 {
+  padding: 3px !important; }
+
+.p4 {
+  padding: 4px !important; }
+
+.p5 {
+  padding: 5px !important; }
+
+.p6 {
+  padding: 6px !important; }
+
+.p7 {
+  padding: 7px !important; }
+
+.p8 {
+  padding: 8px !important; }
+
+.p10 {
+  padding: 10px !important; }
+
+.p12 {
+  padding: 12px !important; }
+
+.p15 {
+  padding: 15px !important; }
+
+.p20 {
+  padding: 20px !important; }
+
+.p25 {
+  padding: 25px !important; }
+
+.p30 {
+  padding: 30px !important; }
+
+.p35 {
+  padding: 35px !important; }
+
+.p40 {
+  padding: 40px !important; }
+
+.p50 {
+  padding: 50px !important; }
+
+.ptn {
+  padding-top: 0 !important; }
+
+.pt5 {
+  padding-top: 5px !important; }
+
+.pt10 {
+  padding-top: 10px !important; }
+
+.pt15 {
+  padding-top: 15px !important; }
+
+.pt20 {
+  padding-top: 20px !important; }
+
+.pt25 {
+  padding-top: 25px !important; }
+
+.pt30 {
+  padding-top: 30px !important; }
+
+.pt35 {
+  padding-top: 35px !important; }
+
+.pt40 {
+  padding-top: 40px !important; }
+
+.pt50 {
+  padding-top: 50px !important; }
+
+.prn {
+  padding-right: 0 !important; }
+
+.pr5 {
+  padding-right: 5px !important; }
+
+.pr10 {
+  padding-right: 10px !important; }
+
+.pr15 {
+  padding-right: 15px !important; }
+
+.pr20 {
+  padding-right: 20px !important; }
+
+.pr25 {
+  padding-right: 25px !important; }
+
+.pr30 {
+  padding-right: 30px !important; }
+
+.pr35 {
+  padding-right: 35px !important; }
+
+.pr40 {
+  padding-right: 40px !important; }
+
+.pr50 {
+  padding-right: 50px !important; }
+
+.pbn {
+  padding-bottom: 0 !important; }
+
+.pb5 {
+  padding-bottom: 5px !important; }
+
+.pb10 {
+  padding-bottom: 10px !important; }
+
+.pb15 {
+  padding-bottom: 15px !important; }
+
+.pb20 {
+  padding-bottom: 20px !important; }
+
+.pb25 {
+  padding-bottom: 25px !important; }
+
+.pb30 {
+  padding-bottom: 30px !important; }
+
+.pb35 {
+  padding-bottom: 35px !important; }
+
+.pb40 {
+  padding-bottom: 40px !important; }
+
+.pb50 {
+  padding-bottom: 50px !important; }
+
+.pln {
+  padding-left: 0 !important; }
+
+.pl5 {
+  padding-left: 5px !important; }
+
+.pl10 {
+  padding-left: 10px !important; }
+
+.pl15 {
+  padding-left: 15px !important; }
+
+.pl20 {
+  padding-left: 20px !important; }
+
+.pl25 {
+  padding-left: 25px !important; }
+
+.pl30 {
+  padding-left: 30px !important; }
+
+.pl35 {
+  padding-left: 35px !important; }
+
+.pl40 {
+  padding-left: 40px !important; }
+
+.pl50 {
+  padding-left: 50px !important; }
+
+/* Axis Padding (both top/bottom or left/right) */
+.pv5 {
+  padding-top: 5px !important;
+  padding-bottom: 5px !important; }
+
+.pv8 {
+  padding-top: 8px !important;
+  padding-bottom: 8px !important; }
+
+.pv10 {
+  padding-top: 10px !important;
+  padding-bottom: 10px !important; }
+
+.pv15 {
+  padding-top: 15px !important;
+  padding-bottom: 15px !important; }
+
+.pv20 {
+  padding-top: 20px !important;
+  padding-bottom: 20px !important; }
+
+.pv25 {
+  padding-top: 25px !important;
+  padding-bottom: 25px !important; }
+
+.pv30 {
+  padding-top: 30px !important;
+  padding-bottom: 30px !important; }
+
+.pv40 {
+  padding-top: 40px !important;
+  padding-bottom: 40px !important; }
+
+.pv50 {
+  padding-top: 50px !important;
+  padding-bottom: 50px !important; }
+
+.ph5 {
+  padding-left: 5px !important;
+  padding-right: 5px !important; }
+
+.ph8 {
+  padding-left: 8px !important;
+  padding-right: 8px !important; }
+
+.ph10 {
+  padding-left: 10px !important;
+  padding-right: 10px !important; }
+
+.ph15 {
+  padding-left: 15px !important;
+  padding-right: 15px !important; }
+
+.ph20 {
+  padding-left: 20px !important;
+  padding-right: 20px !important; }
+
+.ph25 {
+  padding-left: 25px !important;
+  padding-right: 25px !important; }
+
+.ph30 {
+  padding-left: 30px !important;
+  padding-right: 30px !important; }
+
+.ph40 {
+  padding-left: 40px !important;
+  padding-right: 40px !important; }
+
+.ph50 {
+  padding-left: 50px !important;
+  padding-right: 50px !important; }
+
+/* margin center helper */
+.mauto {
+  margin-left: auto;
+  margin-right: auto; }
+
+.mn {
+  margin: 0 !important; }
+
+.m1 {
+  margin: 1px !important; }
+
+.m2 {
+  margin: 2px !important; }
+
+.m3 {
+  margin: 3px !important; }
+
+.m4 {
+  margin: 4px !important; }
+
+.m5 {
+  margin: 5px !important; }
+
+.m8 {
+  margin: 8px !important; }
+
+.m10 {
+  margin: 10px !important; }
+
+.m15 {
+  margin: 15px !important; }
+
+.m20 {
+  margin: 20px !important; }
+
+.m25 {
+  margin: 25px !important; }
+
+.m30 {
+  margin: 30px !important; }
+
+.m35 {
+  margin: 35px !important; }
+
+.m40 {
+  margin: 40px !important; }
+
+.m50 {
+  margin: 50px !important; }
+
+.mtn {
+  margin-top: 0 !important; }
+
+.mt5 {
+  margin-top: 5px !important; }
+
+.mt10 {
+  margin-top: 10px !important; }
+
+.mt15 {
+  margin-top: 15px !important; }
+
+.mt20 {
+  margin-top: 20px !important; }
+
+.mt25 {
+  margin-top: 25px !important; }
+
+.mt30 {
+  margin-top: 30px !important; }
+
+.mt35 {
+  margin-top: 35px !important; }
+
+.mt40 {
+  margin-top: 40px !important; }
+
+.mt50 {
+  margin-top: 50px !important; }
+
+.mt70 {
+  margin-top: 70px !important; }
+
+.mrn {
+  margin-right: 0 !important; }
+
+.mr5 {
+  margin-right: 5px !important; }
+
+.mr10 {
+  margin-right: 10px !important; }
+
+.mr15 {
+  margin-right: 15px !important; }
+
+.mr20 {
+  margin-right: 20px !important; }
+
+.mr25 {
+  margin-right: 25px !important; }
+
+.mr30 {
+  margin-right: 30px !important; }
+
+.mr35 {
+  margin-right: 35px !important; }
+
+.mr40 {
+  margin-right: 40px !important; }
+
+.mr50 {
+  margin-right: 50px !important; }
+
+.mbn {
+  margin-bottom: 0 !important; }
+
+.mb5 {
+  margin-bottom: 5px !important; }
+
+.mb10 {
+  margin-bottom: 10px !important; }
+
+.mb15 {
+  margin-bottom: 15px !important; }
+
+.mb20 {
+  margin-bottom: 20px !important; }
+
+.mb25 {
+  margin-bottom: 25px !important; }
+
+.mb30 {
+  margin-bottom: 30px !important; }
+
+.mb35 {
+  margin-bottom: 35px !important; }
+
+.mb40 {
+  margin-bottom: 40px !important; }
+
+.mb50 {
+  margin-bottom: 50px !important; }
+
+.mb70 {
+  margin-bottom: 70px !important; }
+
+.mln {
+  margin-left: 0 !important; }
+
+.ml5 {
+  margin-left: 5px !important; }
+
+.ml10 {
+  margin-left: 10px !important; }
+
+.ml15 {
+  margin-left: 15px !important; }
+
+.ml20 {
+  margin-left: 20px !important; }
+
+.ml25 {
+  margin-left: 25px !important; }
+
+.ml30 {
+  margin-left: 30px !important; }
+
+.ml35 {
+  margin-left: 35px !important; }
+
+.ml40 {
+  margin-left: 40px !important; }
+
+.ml50 {
+  margin-left: 50px !important; }
+
+/* Axis Margins (both top/bottom or left/right) */
+.mv5 {
+  margin-top: 5px !important;
+  margin-bottom: 5px !important; }
+
+.mv10 {
+  margin-top: 10px !important;
+  margin-bottom: 10px !important; }
+
+.mv15 {
+  margin-top: 15px !important;
+  margin-bottom: 15px !important; }
+
+.mv20 {
+  margin-top: 20px !important;
+  margin-bottom: 20px !important; }
+
+.mv25 {
+  margin-top: 25px !important;
+  margin-bottom: 25px !important; }
+
+.mv30 {
+  margin-top: 30px !important;
+  margin-bottom: 30px !important; }
+
+.mv40 {
+  margin-top: 40px !important;
+  margin-bottom: 40px !important; }
+
+.mv50 {
+  margin-top: 50px !important;
+  margin-bottom: 50px !important; }
+
+.mv70 {
+  margin-top: 70px !important;
+  margin-bottom: 70px !important; }
+
+.mh5 {
+  margin-left: 5px !important;
+  margin-right: 5px !important; }
+
+.mh10 {
+  margin-left: 10px !important;
+  margin-right: 10px !important; }
+
+.mh15 {
+  margin-left: 15px !important;
+  margin-right: 15px !important; }
+
+.mh20 {
+  margin-left: 20px !important;
+  margin-right: 20px !important; }
+
+.mh25 {
+  margin-left: 25px !important;
+  margin-right: 25px !important; }
+
+.mh30 {
+  margin-left: 30px !important;
+  margin-right: 30px !important; }
+
+.mh40 {
+  margin-left: 40px !important;
+  margin-right: 40px !important; }
+
+.mh50 {
+  margin-left: 50px !important;
+  margin-right: 50px !important; }
+
+.mh70 {
+  margin-left: 70px !important;
+  margin-right: 70px !important; }
+
+/* Negative Margin Helpers */
+.mtn5 {
+  margin-top: -5px !important; }
+
+.mtn10 {
+  margin-top: -10px !important; }
+
+.mtn15 {
+  margin-top: -15px !important; }
+
+.mtn20 {
+  margin-top: -20px !important; }
+
+.mtn30 {
+  margin-top: -30px !important; }
+
+.mrn5 {
+  margin-right: -5px !important; }
+
+.mrn10 {
+  margin-right: -10px !important; }
+
+.mrn15 {
+  margin-right: -15px !important; }
+
+.mrn20 {
+  margin-right: -20px !important; }
+
+.mrn30 {
+  margin-right: -30px !important; }
+
+.mbn5 {
+  margin-bottom: -5px !important; }
+
+.mbn10 {
+  margin-bottom: -10px !important; }
+
+.mbn15 {
+  margin-bottom: -15px !important; }
+
+.mbn20 {
+  margin-bottom: -20px !important; }
+
+.mbn30 {
+  margin-bottom: -30px !important; }
+
+.mln5 {
+  margin-left: -5px !important; }
+
+.mln10 {
+  margin-left: -10px !important; }
+
+.mln15 {
+  margin-left: -15px !important; }
+
+.mln20 {
+  margin-left: -20px !important; }
+
+.mln30 {
+  margin-left: -30px !important; }
+
+/* Vertical Negative Margin "mv" + "n" + "x" */
+.mvn5 {
+  margin-top: -5px !important;
+  margin-bottom: -5px !important; }
+
+.mvn10 {
+  margin-top: -10px !important;
+  margin-bottom: -10px !important; }
+
+.mvn15 {
+  margin-top: -15px !important;
+  margin-bottom: -15px !important; }
+
+.mvn20 {
+  margin-top: -20px !important;
+  margin-bottom: -20px !important; }
+
+.mvn30 {
+  margin-top: -30px !important;
+  margin-bottom: -30px !important; }
+
+/* Horizontal Negative Margin "mh" + "n" + "x" */
+.mhn5 {
+  margin-left: -5px !important;
+  margin-right: -5px !important; }
+
+.mhn10 {
+  margin-left: -10px !important;
+  margin-right: -10px !important; }
+
+.mhn15 {
+  margin-left: -15px !important;
+  margin-right: -15px !important; }
+
+.mhn20 {
+  margin-left: -20px !important;
+  margin-right: -20px !important; }
+
+.mhn30 {
+  margin-left: -30px !important;
+  margin-right: -30px !important; }
+
+/* Vertical Align Helpers */
+.va-t {
+  vertical-align: top !important; }
+
+.va-m {
+  vertical-align: middle !important; }
+
+.va-b {
+  vertical-align: bottom !important; }
+
+.va-s {
+  vertical-align: super !important; }
+
+/* Text Helpers */
+.text-left {
+  text-align: left !important; }
+
+.text-right {
+  text-align: right !important; }
+
+.text-center {
+  text-align: center !important; }
+
+.text-justify {
+  text-align: justify !important; }
+
+.text-nowrap {
+  white-space: nowrap !important; }
+
+/* Inline Block Helper */
+.ib,
+.inline-object {
+  display: inline-block !important; }
+
+.clear {
+  clear: both; }
+
+.wvWarning {
+  position: relative;
+  width: 320px;
+  min-height: 130px;
+  z-index: 999;
+  left: calc(50% - 160px);
+  border: #000 solid 1px;
+  -webkit-border-radius: 7px;
+  -moz-border-radius: 7px;
+  border-radius: 7px;
+  color: #FF5722;
+  box-shadow: 0px 3px 23px #ff980078;
+  -webkit-animation-name: example;
+  /* Safari 4.0 - 8.0 */
+  -webkit-animation-duration: 3s;
+  /* Safari 4.0 - 8.0 */
+  -webkit-animation-fill-mode: both;
+  /* Safari 4.0 - 8.0 */
+  animation-name: example;
+  animation-duration: 2s;
+  animation-fill-mode: both;
+  animation-timing-function: ease-out; }
+
+@-webkit-keyframes example {
+  from {
+    top: 0vh;
+    opacity: 0;
+    background: #868686; }
+  to {
+    top: 10vh;
+    opacity: 1;
+    background: #ffffff; } }
+
+@keyframes example {
+  from {
+    top: 0vh;
+    opacity: 0;
+    background: #868686; }
+  to {
+    top: 10vh;
+    opacity: 1;
+    background: #ffffff; } }
+
+.wvWarning-content {
+  position: relative;
+  width: 190px;
+  min-height: 88px;
+  max-height: 80vh;
+  margin: auto; }
+
+.wvWarning-icon {
+  font-size: 32px; }
+
+.wvWarning-text {
+  position: relative; }
+
+.wvWarning-button {
+  background-color: #f1ededcc;
+  color: #607D8B;
+  width: 50px;
+  font-weight: 600;
+  margin-top: 2px;
+  margin-right: 30px; }
+
+.wvScreenToSmallWarning {
+  position: fixed;
+  display: block;
+  top: 0;
+  left: 0;
+  background-color: white;
+  color: #333;
+  width: 100%;
+  height: 100%;
+  z-index: 1000; }
+
+.wvScreenToSmallWarning-content {
+  padding: 10px;
+  text-align: center; }
+
+/* on some mobile devices, the size returned for the "screen" is actually the viewport size so 360x640 is actually equal to 280x560 */
+@media only screen and (min-width: 550px) and (min-height: 280px) {
+  .wvScreenToSmallWarning {
+    display: none; } }
+
+/* on some mobile devices, the size returned for the "screen" is actually the viewport size so 360x640 is actually equal to 280x560 */
+@media only screen and (min-width: 280px) and (min-height: 550px) {
+  .wvScreenToSmallWarning {
+    display: none; } }
+
+wv-notice {
+  display: block;
+  height: 100%;
+  width: 100%; }
+
+.wvNotice {
+  padding: 0.5rem 0.25rem;
+  height: 100%; }
+
+.wvNotice__text {
+  position: relative;
+  top: 50%;
+  transform: translateY(-50%);
+  text-align: center;
+  margin-left: 1rem;
+  font-weight: 400;
+  color: #b3b3b3;
+  float: left;
+  width: calc(100% - 7rem); }
+
+.wvNotice__closeButton {
+  float: right;
+  margin-right: 0.5em;
+  position: relative;
+  top: 50%;
+  transform: translateY(-50%);
+  width: 3.5rem;
+  height: 2.5rem;
+  text-align: center;
+  font-size: 1em;
+  font-weight: 100;
+  line-height: 2.2rem;
+  cursor: pointer;
+  border: 1px solid #454545; }
+
+/* layout: left section */
+.wvLayoutLeft {
+  position: absolute;
+  z-index: 2;
+  background-color: black;
+  width: 32rem;
+  left: 0; }
+  .wvLayoutLeft.wvLayoutLeft--toppadding {
+    top: 42px; }
+  .wvLayoutLeft:not(.wvLayoutLeft--toppadding) {
+    top: 0; }
+  @media screen and (max-device-width: 374px) {
+    .wvLayoutLeft.wvLayoutLeft--bottompadding {
+      bottom: 7rem; } }
+  @media screen and (min-device-width: 375px) {
+    .wvLayoutLeft.wvLayoutLeft--bottompadding {
+      bottom: 5rem; } }
+  .wvLayoutLeft:not(.wvLayoutLeft--bottompadding) {
+    bottom: 0; }
+  .wvLayoutLeft.wvLayoutLeft--closed {
+    transform: translateX(-32rem); }
+    .wvLayoutLeft.wvLayoutLeft--closed.wvLayoutLeft--small {
+      transform: translateX(-12rem); }
+  .wvLayoutLeft.wvLayoutLeft--small {
+    width: 12rem; }
+    .wvLayoutLeft.wvLayoutLeft--small .wvLayoutLeft__contentTop, .wvLayoutLeft.wvLayoutLeft--small .wvLayoutLeft__contentMiddle, .wvLayoutLeft.wvLayoutLeft--small .wvLayoutLeft__contentBottom {
+      width: 100%; }
+
+.wvLayoutLeft__content {
+  border-right: 1px solid #AAA;
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  overflow-y: auto;
+  height: 100%; }
+
+.wvLayoutLeft__contentTop {
+  padding: 0rem 1rem 0rem 1rem;
+  width: 31.9rem; }
+  .wvLayoutLeft__contentTop:after {
+    content: "";
+    display: block;
+    height: 0;
+    width: 0;
+    clear: both; }
+
+.wvLayoutLeft__contentMiddle {
+  flex: 1 0 auto;
+  width: 31.9rem; }
+
+.wvLayoutLeft__contentBottom {
+  width: 31.9rem; }
+
+.wvLayout__leftBottom.wvLayout__leftBottom--enabled {
+  border-top: 1px solid rgba(255, 255, 255, 0.2);
+  margin-top: 1rem;
+  padding: 1rem; }
+
+.wvLayoutLeft__actions, .wvLayoutLeft__actions--outside {
+  display: block;
+  position: absolute;
+  right: 1px;
+  top: 50%;
+  transform: translateY(-50%);
+  width: 25px; }
+
+.wvLayoutLeft__actions--outside {
+  right: -25px; }
+
+.wvLayoutLeft__action {
+  background-color: #3498db;
+  opacity: 0.5;
+  color: white;
+  transition: none; }
+  .wvLayoutLeft__action:hover, .wvLayoutLeft__action:focus {
+    opacity: 1; }
+
+/* layout: right section */
+.wvLayout__right {
+  display: block;
+  position: absolute;
+  z-index: 2;
+  background-color: black;
+  width: 85px;
+  right: 0; }
+  .wvLayout__right.wvLayout__right--toppadding {
+    top: 42px; }
+  .wvLayout__right:not(.wvLayout__right--toppadding) {
+    top: 0; }
+  @media screen and (max-device-width: 374px) {
+    .wvLayout__right.wvLayout__right--bottompadding {
+      bottom: 7rem; } }
+  @media screen and (min-device-width: 375px) {
+    .wvLayout__right.wvLayout__right--bottompadding {
+      bottom: 5rem; } }
+  .wvLayout__right:not(.wvLayout__right--bottompadding) {
+    bottom: 0; }
+  .wvLayout__right.wvLayout__right--closed {
+    transform: translateX(85px); }
+  .wvLayout__right > wv-layout-right,
+  .wvLayout__right > wv-layout-right > .wvViewer__asideRight {
+    display: block;
+    height: 100%;
+    width: 100%; }
+
+.wvAsideRight__content {
+  height: 100%;
+  float: left;
+  border-left: 1px solid #AAA;
+  padding: 0 5px;
+  width: 32rem; }
+
+.wvAsideRight__actions, .wvAsideRight__actions--outside {
+  display: block;
+  position: absolute;
+  left: 1px;
+  top: 50%;
+  transform: translateY(-50%);
+  width: 25px;
+  z-index: 3; }
+
+.wvAsideRight__actions--outside {
+  left: -25px; }
+
+.wvAsideRight__action {
+  background-color: #3498db;
+  opacity: 0.5;
+  color: white;
+  transition: none; }
+  .wvAsideRight__action:hover, .wvAsideRight__action:focus {
+    opacity: 1; }
+
+.wvAsideRight__fixOpenFullyTooltip + .tooltip {
+  left: -6.633em !important;
+  top: 1px !important; }
+
+/* layout: bottom section */
+.wvLayout__bottom {
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  background-color: #1a1a1a; }
+  @media screen and (max-device-width: 374px) {
+    .wvLayout__bottom {
+      height: 7rem; } }
+  @media screen and (min-device-width: 375px) {
+    .wvLayout__bottom {
+      height: 5rem; } }
+
+/* layout: main section */
+.wvLayout__main {
+  position: absolute;
+  text-align: center;
+  right: 0;
+  left: 0; }
+  .wvLayout__main .wvLayout__splitpane--toolbarAtTop {
+    display: block;
+    height: calc(100% - 42px);
+    width: 100%;
+    position: relative;
+    top: 42px; }
+  .wvLayout__main .wvLayout__splitpane--toolbarAtRight {
+    display: block;
+    height: 100%;
+    width: calc(100% - 42px); }
+  .wvLayout__main .wvLayout__splitpane--bigToolbarAtTop {
+    display: block;
+    height: calc(100% - 68px);
+    width: 100%;
+    position: relative;
+    top: 68px; }
+  .wvLayout__main .wvLayout__splitpane--bigToolbarAtRight {
+    display: block;
+    height: 100%;
+    width: calc(100% - 68px); }
+  .wvLayout__main.wvLayout__main--toppadding {
+    top: 42px; }
+  .wvLayout__main:not(.wvLayout__main--toppadding) {
+    top: 0; }
+  .wvLayout__main.wvLayout__main--bottompadding {
+    bottom: 440px; }
+    @media screen and (max-device-width: 374px) {
+      .wvLayout__main.wvLayout__main--bottompadding {
+        bottom: 7rem; } }
+    @media screen and (min-device-width: 375px) {
+      .wvLayout__main.wvLayout__main--bottompadding {
+        bottom: 5rem; } }
+  .wvLayout__main:not(.wvLayout__main--bottompadding) {
+    bottom: 0; }
+  .wvLayout__main.wvLayout__main--leftpadding {
+    left: 32rem; }
+  .wvLayout__main {
+    left: 0px; }
+  .wvLayout__main.wvLayout__main--smallleftpadding {
+    left: 12rem; }
+  .wvLayout__main.wvLayout__main--rightpadding {
+    right: 85px; }
+  .wvLayout__main:not(.wvLayout__main--rightpadding) {
+    right: 0px; }
+
+/* global */
+.popover {
+  color: black; }
+
+.wvViewer__editor--full {
+  position: absolute;
+  top: 0;
+  right: 0;
+  z-index: 10;
+  opacity: 0;
+  transform: translateX(100%);
+  width: 100%;
+  height: 100%;
+  background-color: white;
+  color: #666666; }
+  .wvViewer__editor--full.opened {
+    opacity: 1;
+    transform: translateX(0); }
+
+.wvViewer__topBar {
+  width: 100%;
+  overflow-y: auto;
+  white-space: nowrap;
+  max-width: 100%; }
+
+.wvViewer__buttonGroup {
+  display: inline-block; }
+
+.wvViewer__buttonGroup--asideWidth {
+  width: 32rem;
+  padding-right: 1rem; }
+
+.wvViewer__buttonGroup--contentWidth {
+  width: calc(100% - 32rem);
+  padding-left: 1rem;
+  max-height: 4.2rem; }
+
+.wvViewer__iframe {
+  position: absolute;
+  left: 0;
+  top: 0; }
+
+/* bottom bar */
+.wvViewer__bottomBar, .wvViewer__bottomBar--expanded, .wvViewer__bottomBar--minimized {
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  background-color: #111111; }
+
+.wvViewer__bottomBar--expanded {
+  height: 80px;
+  color: white; }
+  .wvViewer__bottomBar--expanded .wvViewer__timeline {
+    width: calc(100% - 80px); }
+  .wvViewer__bottomBar--expanded .wvTimeline__hotspots {
+    bottom: -40px; }
+
+.wvViewer__bottomBar--minimized {
+  color: white;
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+  padding-left: 2.5rem; }
+  .wvViewer__bottomBar--minimized .wvTimeline__hotspot {
+    top: -40px;
+    opacity: 0;
+    visibility: hidden;
+    z-index: -1; }
+  .wvViewer__bottomBar--minimized:hover .wvTimeline__hotspot {
+    opacity: 1;
+    visibility: visible;
+    z-index: 5;
+    transition-delay: 0s; }
+
+.wvViewer__timeline {
+  height: 24px;
+  line-height: 24px;
+  vertical-align: middle;
+  width: 100%; }
+
+.wvViewer__trademark {
+  display: inline-block;
+  float: right;
+  width: 80px;
+  height: 80px;
+  float: right;
+  line-height: 80px;
+  vertical-align: middle;
+  text-align: center; }
+
+.wvTimeline__input {
+  border-radius: 3px;
+  margin-top: 2px;
+  border: 1px solid #e7e7e7; }
+  .wvTimeline__input:focus {
+    outline: none; }
+
+.wvTimeline__actions {
+  display: inline-block;
+  border-right: 1px solid #e7e7e7; }
+
+.wvSerieslist {
+  margin: 0;
+  padding: 0;
+  list-style: none; }
+
+.wvSerieslist__seriesItem--selectable {
+  cursor: pointer !important; }
+  .wvSerieslist__seriesItem--selectable:hover {
+    color: white; }
+
+.wvSerieslist__placeholderIcon, .wvSerieslist__placeholderIcon.fa {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  font-size: 3.25rem;
+  line-height: 6.5rem;
+  text-align: center; }
+
+.wvSerieslist__placeholderIcon--strikeout, .wvSerieslist__placeholderIcon--strikeout.fa {
+  color: #c3c3c3; }
+  .wvSerieslist__placeholderIcon--strikeout::after, .wvSerieslist__placeholderIcon--strikeout.fa::after {
+    position: absolute;
+    left: 0;
+    top: 50%;
+    right: 0;
+    transform: rotate(-45deg) scaleX(0.9);
+    border-top: 5px solid;
+    border-color: inherit;
+    content: ""; }
+
+.wvSerieslist__picture {
+  display: inline-block;
+  font-size: 14px;
+  width: 6.5rem;
+  height: 6.5rem;
+  position: relative;
+  z-index: -1; }
+
+.wvSerieslist__badge, .wvSerieslist__badge--blue, .wvSerieslist__badge--red, .wvSerieslist__badge--green, .wvSerieslist__badge--yellow, .wvSerieslist__badge--violet {
+  position: absolute;
+  bottom: 5px;
+  right: 5px;
+  font-size: 10px;
+  line-height: 15px;
+  width: 15px;
+  height: 15px;
+  border-radius: 100%;
+  background-color: gray;
+  vertical-align: middle;
+  text-align: center;
+  font-weight: bold; }
+
+.wvSerieslist__information {
+  font-size: 14px;
+  float: right;
+  padding-left: 1rem;
+  width: calc(100% - 6.5rem);
+  height: 6.5rem; }
+
+.wvSerieslist__label {
+  white-space: nowrap;
+  width: calc(100% - 10px);
+  overflow: hidden;
+  height: 3.25rem;
+  line-height: 3.25rem;
+  vertical-align: middle; }
+
+.wvSerieslist__timeline {
+  height: 3.25rem;
+  line-height: 3.25rem;
+  vertical-align: middle; }
+
+.wvSerieslist__seriesItem {
+  position: relative;
+  padding-left: 0;
+  list-style: none;
+  font-size: 0;
+  border-right: 0.2rem solid transparent;
+  border-left: 0.2rem solid transparent;
+  border-top: 0.2rem solid transparent;
+  border-bottom: 0.2rem solid transparent;
+  border-corner-shape: notch;
+  line-height: 0px;
+  margin: 0.1rem; }
+  .wvSerieslist__seriesItem.active {
+    border-color: rgba(255, 255, 255, 0.6);
+    border-style: solid; }
+  .wvSerieslist__seriesItem.highlighted {
+    border-color: white;
+    border-style: solid; }
+  .wvSerieslist__seriesItem:hover, .wvSerieslist__seriesItem:focus, .wvSerieslist__seriesItem.focused {
+    border-style: dashed;
+    border-color: rgba(255, 255, 255, 0.8); }
+
+.wvSerieslist__seriesItem--list {
+  display: block; }
+
+.wvSerieslist__seriesItem--grid {
+  display: inline-block; }
+
+.wvSerieslist__seriesItem--oneCol {
+  text-align: center; }
+
+.wvSerieslist__seriesItem--activated,
+.wvSerieslist__videoItem--activated,
+.wvSerieslist__pdfItem--activated {
+  border: 0.2rem solid #3398db !important; }
+
+.wvSerieslist__badge--blue {
+  background-color: rgba(51, 152, 219, 0.7); }
+
+.wvSerieslist__badge--red {
+  background-color: rgba(206, 0, 0, 0.7); }
+
+.wvSerieslist__badge--green {
+  background-color: rgba(0, 160, 27, 0.7); }
+
+.wvSerieslist__badge--yellow {
+  background-color: rgba(220, 200, 0, 0.9); }
+
+.wvSerieslist__badge--violet {
+  background-color: rgba(255, 31, 255, 0.7); }
+
+.wvToolbar {
+  position: absolute; }
+
+.wvToolbar--top {
+  top: 0;
+  height: 42px;
+  right: 0;
+  text-align: right;
+  white-space: nowrap;
+  max-width: 100%; }
+
+.wvToolbar--right {
+  right: 0;
+  width: 42px;
+  height: 100%;
+  z-index: 2; }
+  .wvToolbar--right.wvToolbar--big {
+    width: 68px; }
+
+/* Splitpane Grid Configuration */
+.wvToolbar__splitpaneConfigPopover {
+  font-size: 0; }
+
+.wvToolbar__splitpaneConfigNotice {
+  font-size: 1.25rem;
+  font-style: italic;
+  text-align: center;
+  color: #333; }
+
+input[type="radio"].wvToolbar__splitpaneConfigButtonInput {
+  position: absolute;
+  width: 0;
+  height: 0;
+  left: 0;
+  top: 0;
+  bottom: 2px;
+  right: 0;
+  opacity: 0; }
+
+/* Windowing Preset */
+.wvToolbar__windowingPresetConfigNotice {
+  font-size: 1.25rem;
+  font-style: italic;
+  text-align: center;
+  color: #333; }
+
+.wvToolbar__windowingPresetList {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+  font-size: 1.5rem; }
+
+.wvToolbar__windowingPresetListItem {
+  outline: none;
+  background-color: transparent;
+  border: none;
+  position: relative;
+  display: inline-block;
+  cursor: pointer;
+  font-variant: small-caps;
+  text-transform: lowercase;
+  text-align: center;
+  font-size: 1.3rem;
+  font-weight: 400;
+  line-height: 2.2rem;
+  color: #d9d9d9;
+  transition: 0.3s text-decoration ease, 0.3s border ease, 0.3s opacity ease;
+  margin: 0;
+  min-width: 3rem;
+  padding: 0 10px;
+  line-height: 3.6rem;
+  max-height: 2.8rem;
+  max-width: 100%;
+  overflow: hidden;
+  margin: 0.6rem;
+  margin-left: 0rem;
+  margin-right: 0rem;
+  line-height: 2rem;
+  padding-top: 0.1rem;
+  padding-bottom: 0.5rem;
+  font-size: 1.4rem;
+  border: 1px solid #454545;
+  font-family: Arial;
+  background-color: black;
+  color: #1a1a1a;
+  border: 1px solid #bababa;
+  background-color: white;
+  width: 100%;
+  margin: 0;
+  margin-left: 0 !important;
+  border-top: none;
+  border-bottom: none; }
+  .wvToolbar__windowingPresetListItem:hover {
+    text-decoration: none;
+    color: white; }
+  .wvToolbar__windowingPresetListItem + .wvToolbar__windowingPresetListItem {
+    margin-left: 0.7rem; }
+  .wvToolbar__windowingPresetListItem:hover {
+    background-color: #1a1a1a; }
+  .wvToolbar__windowingPresetListItem > .glyphicon {
+    position: relative;
+    display: inline-block;
+    top: 3px;
+    margin-right: 4px; }
+  .wvToolbar__windowingPresetListItem:hover {
+    color: #1a1a1a;
+    background-color: #e6e6e6; }
+
+.wvVideo {
+  position: absolute;
+  top: 50%;
+  left: 0;
+  width: 100%;
+  height: auto;
+  transform: translateY(-50%); }
+
+.wvStudyInformationBreadcrumb__patient {
+  display: inline-block;
+  background-color: rgba(255, 255, 255, 0.15);
+  padding: 0.2rem 1rem 0.3rem 1rem;
+  text-align: center;
+  font-size: 1em;
+  margin: 0.6rem;
+  font-weight: 400;
+  line-height: 2.3rem;
+  margin-right: 0; }
+
+.wvStudyInformationBreadcrumb__study {
+  display: inline-block;
+  background-color: rgba(255, 255, 255, 0.15);
+  padding: 0.2rem 1rem 0.3rem 1rem;
+  text-align: center;
+  font-size: 1em;
+  margin: 0.6rem;
+  font-weight: 400;
+  line-height: 2.3rem; }
+
+.wvSelectionActionlist {
+  display: block;
+  text-align: center; }
+
+/* wvb-ui stuffs */
+.wv-overlay {
+  color: orange; }
+
+.wv-overlay-icon {
+  width: 64px; }
+
+.wvOverlay__studyBadge, .wvOverlay__studyBadge--blue, .wvOverlay__studyBadge--red, .wvOverlay__studyBadge--green, .wvOverlay__studyBadge--yellow, .wvOverlay__studyBadge--violet {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 1.5rem;
+  height: 1.5rem;
+  background-color: gray;
+  z-index: 1; }
+
+.wv-overlay-topleft {
+  position: absolute;
+  top: 0rem;
+  left: 0rem;
+  text-align: left; }
+
+.wv-overlay-topright {
+  position: absolute;
+  top: 0rem;
+  right: 0rem;
+  text-align: right; }
+
+.wv-overlay-bottomright {
+  position: absolute;
+  bottom: 2em;
+  right: 0rem;
+  text-align: right; }
+
+.wv-overlay-bottomleft {
+  position: absolute;
+  bottom: 2em;
+  left: 0rem;
+  text-align: left; }
+
+.wv-overlay-timeline-wrapper {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1; }
+
+.wv-overlay-topleft, .wv-overlay-topright, .wv-overlay-bottomright, .wv-overlay-bottomleft {
+  padding: 2rem;
+  transition: color 500ms, background-color 500ms;
+  background-color: rgba(0, 0, 0, 0.66); }
+
+.wv-overlay-topleft:hover, .wv-overlay-topright:hover, .wv-overlay-bottomright:hover, .wv-overlay-bottomleft:hover {
+  background-color: rgba(0, 0, 0, 0.9); }
+
+.wvPaneOverlay {
+  position: absolute;
+  top: 50%;
+  width: 100%;
+  transform: translateY(-50%);
+  font-weight: 100;
+  text-align: center;
+  color: white;
+  font-size: 2rem; }
+
+.wv-overlay-scrollbar-loaded {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  height: 5px;
+  background-color: red;
+  will-change: right;
+  transform-origin: 0% 50%; }
+
+.wv-overlay-scrollbar-loading {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  height: 5px;
+  background-color: #660000;
+  will-change: right;
+  transform-origin: 0% 50%; }
+
+.wv-overlay-scrollbar-text {
+  position: absolute;
+  bottom: calc(1em + 5px);
+  left: 5px;
+  height: 1em;
+  color: red;
+  font-size: 0.8em;
+  font-family: helvetica; }
+
+.wvOverlay__studyBadge--blue {
+  background-color: rgba(51, 152, 219, 0.7); }
+
+.wvOverlay__studyBadge--red {
+  background-color: rgba(206, 0, 0, 0.7); }
+
+.wvOverlay__studyBadge--green {
+  background-color: rgba(0, 160, 27, 0.7); }
+
+.wvOverlay__studyBadge--yellow {
+  background-color: rgba(220, 200, 0, 0.9); }
+
+.wvOverlay__studyBadge--violet {
+  background-color: rgba(255, 31, 255, 0.7); }
+
+wv-pdf-viewer {
+  display: block;
+  width: 100%;
+  height: 100%; }
+
+#toolbarContainer > #toolbarViewer > #toolbarViewerLeft > .wv-pdf-viewer-closebutton {
+  background-color: inherit;
+  color: white;
+  border: none;
+  padding: 2px;
+  margin-left: 4px;
+  margin-right: 2px; }
+  #toolbarContainer > #toolbarViewer > #toolbarViewerLeft > .wv-pdf-viewer-closebutton:hover {
+    color: black; }
+
+.fa.fa-window-close.wv-pdf-viewer-closebuttonicon {
+  font-size: 2rem;
+  line-height: 28px; }
+
+.pdfjs .textLayer {
+  position: absolute;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  overflow: hidden;
+  opacity: 0.2; }
+
+.pdfjs .textLayer > div {
+  color: transparent;
+  position: absolute;
+  white-space: pre;
+  cursor: text;
+  -webkit-transform-origin: 0 0;
+  -moz-transform-origin: 0 0;
+  -o-transform-origin: 0 0;
+  -ms-transform-origin: 0 0;
+  transform-origin: 0 0; }
+
+.pdfjs .textLayer .highlight {
+  margin: -1px;
+  padding: 1px;
+  background-color: #b400aa;
+  border-radius: 4px; }
+
+.pdfjs .textLayer .highlight.begin {
+  border-radius: 4px 0 0 4px; }
+
+.pdfjs .textLayer .highlight.end {
+  border-radius: 0 4px 4px 0; }
+
+.pdfjs .textLayer .highlight.middle {
+  border-radius: 0; }
+
+.pdfjs .textLayer .highlight.selected {
+  background-color: #006400; }
+
+.pdfjs .textLayer ::selection {
+  background: #00f; }
+
+.pdfjs .textLayer ::-moz-selection {
+  background: #00f; }
+
+.pdfjs .pdfViewer .canvasWrapper {
+  overflow: hidden; }
+
+.pdfjs .pdfViewer .page {
+  direction: ltr;
+  width: 816px;
+  height: 1056px;
+  margin: 1px auto -8px;
+  position: relative;
+  overflow: visible;
+  border: 9px solid transparent;
+  background-clip: content-box;
+  border-image: url("../images/pdf.js-viewer/shadow.png") 9 9 repeat;
+  background-color: #fff; }
+
+body {
+  height: 100%; }
+
+.pdfjs .pdfViewer.removePageBorders .page {
+  margin: 0 auto 10px;
+  border: none; }
+
+.pdfjs .pdfViewer .page canvas {
+  margin: 0;
+  display: block; }
+
+.pdfjs .pdfViewer .page .loadingIcon {
+  position: absolute;
+  display: block;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  background: url("../images/pdf.js-viewer/loading-icon.gif") center no-repeat; }
+
+.pdfjs .pdfViewer .page .annotLink > a:hover {
+  opacity: .2;
+  background: #ff0;
+  box-shadow: 0 2px 10px #ff0; }
+
+.pdfjs .pdfPresentationMode:-webkit-full-screen .pdfViewer .page {
+  margin-bottom: 100%;
+  border: 0; }
+
+.pdfjs .pdfPresentationMode:-moz-full-screen .pdfViewer .page {
+  margin-bottom: 100%;
+  border: 0; }
+
+.pdfjs .pdfPresentationMode:-ms-fullscreen .pdfViewer .page {
+  margin-bottom: 100% !important;
+  border: 0; }
+
+.pdfjs .pdfPresentationMode:fullscreen .pdfViewer .page {
+  margin-bottom: 100%;
+  border: 0; }
+
+.pdfjs .pdfViewer .page .annotText > img {
+  position: absolute;
+  cursor: pointer; }
+
+.pdfjs .pdfViewer .page .annotTextContentWrapper {
+  position: absolute;
+  width: 20em; }
+
+.pdfjs .pdfViewer .page .annotTextContent {
+  z-index: 200;
+  float: left;
+  max-width: 20em;
+  background-color: #FF9;
+  box-shadow: 0 2px 5px #333;
+  border-radius: 2px;
+  padding: .6em;
+  cursor: pointer; }
+
+.pdfjs .pdfViewer .page .annotTextContent > h1 {
+  font-size: 1em;
+  border-bottom: 1px solid #000;
+  padding-bottom: 0.2em; }
+
+.pdfjs .pdfViewer .page .annotTextContent > p {
+  padding-top: 0.2em; }
+
+.pdfjs .pdfViewer .page .annotLink > a {
+  position: absolute;
+  font-size: 1em;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%; }
+
+.pdfjs .pdfViewer .page .annotLink > a {
+  background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAA LAAAAAABAAEAAAIBRAA7") 0 0 repeat; }
+
+.pdfjs * {
+  padding: 0;
+  margin: 0; }
+
+html {
+  height: 100%;
+  font-size: 10px; }
+
+.pdfjs input,
+.pdfjs button,
+.pdfjs select {
+  font: message-box;
+  outline: none; }
+
+.pdfjs .hidden {
+  display: none !important; }
+
+.pdfjs [hidden] {
+  display: none !important; }
+
+.pdfjs #viewerContainer.pdfPresentationMode:-webkit-full-screen {
+  top: 0;
+  border-top: 2px solid transparent;
+  background-color: #000;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  cursor: none;
+  -webkit-user-select: none; }
+
+.pdfjs #viewerContainer.pdfPresentationMode:-moz-full-screen {
+  top: 0;
+  border-top: 2px solid transparent;
+  background-color: #000;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  cursor: none;
+  -moz-user-select: none; }
+
+.pdfjs #viewerContainer.pdfPresentationMode:-ms-fullscreen {
+  top: 0 !important;
+  border-top: 2px solid transparent;
+  width: 100%;
+  height: 100%;
+  overflow: hidden !important;
+  cursor: none;
+  -ms-user-select: none; }
+
+.pdfjs #viewerContainer.pdfPresentationMode:-ms-fullscreen::-ms-backdrop {
+  background-color: #000; }
+
+.pdfjs #viewerContainer.pdfPresentationMode:fullscreen {
+  top: 0;
+  border-top: 2px solid transparent;
+  background-color: #000;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  cursor: none;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none; }
+
+.pdfjs .pdfPresentationMode:-webkit-full-screen a:not(.internalLink) {
+  display: none; }
+
+.pdfjs .pdfPresentationMode:-moz-full-screen a:not(.internalLink) {
+  display: none; }
+
+.pdfjs .pdfPresentationMode:-ms-fullscreen a:not(.internalLink) {
+  display: none !important; }
+
+.pdfjs .pdfPresentationMode:fullscreen a:not(.internalLink) {
+  display: none; }
+
+.pdfjs .pdfPresentationMode:-webkit-full-screen .textLayer > div {
+  cursor: none; }
+
+.pdfjs .pdfPresentationMode:-moz-full-screen .textLayer > div {
+  cursor: none; }
+
+.pdfjs .pdfPresentationMode:-ms-fullscreen .textLayer > div {
+  cursor: none; }
+
+.pdfjs .pdfPresentationMode:fullscreen .textLayer > div {
+  cursor: none; }
+
+.pdfjs .pdfPresentationMode.pdfPresentationModeControls > *,
+.pdfjs .pdfPresentationMode.pdfPresentationModeControls .textLayer > div {
+  cursor: default; }
+
+.pdfjs .outerCenter {
+  pointer-events: none;
+  position: relative; }
+
+html[dir='ltr'] .pdfjs .outerCenter {
+  float: right;
+  right: 50%; }
+
+html[dir='rtl'] .pdfjs .outerCenter {
+  float: left;
+  left: 50%; }
+
+.pdfjs .innerCenter {
+  pointer-events: auto;
+  position: relative; }
+
+html[dir='ltr'] .pdfjs .innerCenter {
+  float: right;
+  right: -50%; }
+
+html[dir='rtl'] .pdfjs .innerCenter {
+  float: left;
+  left: -50%; }
+
+.pdfjs #outerContainer {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  background-color: #404040;
+  background-image: url("../images/pdf.js-viewer/texture.png"); }
+
+.pdfjs #sidebarContainer {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  width: 200px;
+  visibility: hidden;
+  -webkit-transition-duration: 200ms;
+  -webkit-transition-timing-function: ease;
+  transition-duration: 200ms;
+  transition-timing-function: ease; }
+
+html[dir='ltr'] .pdfjs #sidebarContainer {
+  -webkit-transition-property: left;
+  transition-property: left;
+  left: -200px; }
+
+html[dir='rtl'] .pdfjs #sidebarContainer {
+  -webkit-transition-property: right;
+  transition-property: right;
+  right: -200px; }
+
+.pdfjs #outerContainer.sidebarMoving > #sidebarContainer,
+.pdfjs #outerContainer.sidebarOpen > #sidebarContainer {
+  visibility: visible; }
+
+html[dir='ltr'] .pdfjs #outerContainer.sidebarOpen > #sidebarContainer {
+  left: 0; }
+
+html[dir='rtl'] .pdfjs #outerContainer.sidebarOpen > #sidebarContainer {
+  right: 0; }
+
+.pdfjs #mainContainer {
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  min-width: 320px;
+  -webkit-transition-duration: 200ms;
+  -webkit-transition-timing-function: ease;
+  transition-duration: 200ms;
+  transition-timing-function: ease; }
+
+html[dir='ltr'] .pdfjs #outerContainer.sidebarOpen > #mainContainer {
+  -webkit-transition-property: left;
+  transition-property: left;
+  left: 200px; }
+
+html[dir='rtl'] .pdfjs #outerContainer.sidebarOpen > #mainContainer {
+  -webkit-transition-property: right;
+  transition-property: right;
+  right: 200px; }
+
+.pdfjs #sidebarContent {
+  top: 32px;
+  bottom: 0;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+  position: absolute;
+  width: 200px;
+  background-color: rgba(0, 0, 0, 0.1); }
+
+html[dir='ltr'] .pdfjs #sidebarContent {
+  left: 0;
+  box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.25); }
+
+html[dir='rtl'] .pdfjs #sidebarContent {
+  right: 0;
+  box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.25); }
+
+.pdfjs #viewerContainer {
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+  position: absolute;
+  top: 32px;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  outline: none; }
+
+html[dir='ltr'] .pdfjs #viewerContainer {
+  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.05); }
+
+html[dir='rtl'] .pdfjs #viewerContainer {
+  box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.05); }
+
+.pdfjs .toolbar {
+  position: relative;
+  left: 0;
+  right: 0;
+  cursor: default; }
+
+.pdfjs #toolbarContainer {
+  width: 100%; }
+
+.pdfjs #toolbarSidebar {
+  width: 200px;
+  height: 32px;
+  background-color: #424242;
+  background-image: url("../images/pdf.js-viewer/texture.png"), linear-gradient(rgba(77, 77, 77, 0.99), rgba(64, 64, 64, 0.95)); }
+
+html[dir='ltr'] .pdfjs #toolbarSidebar {
+  box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1); }
+
+html[dir='rtl'] .pdfjs #toolbarSidebar {
+  box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1); }
+
+.pdfjs #toolbarContainer,
+.pdfjs .findbar,
+.pdfjs .secondaryToolbar {
+  position: relative;
+  height: 32px;
+  background-color: #474747;
+  background-image: url("../images/pdf.js-viewer/texture.png"), linear-gradient(rgba(82, 82, 82, 0.99), rgba(69, 69, 69, 0.95)); }
+
+html[dir='ltr'] .pdfjs #toolbarContainer,
+.pdfjs .findbar,
+.pdfjs .secondaryToolbar {
+  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.08), inset 0 1px 1px rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(0, 0, 0, 0.15), 0 1px 1px rgba(0, 0, 0, 0.1); }
+
+html[dir='rtl'] .pdfjs #toolbarContainer,
+.pdfjs .findbar,
+.pdfjs .secondaryToolbar {
+  box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.08), inset 0 1px 1px rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(0, 0, 0, 0.15), 0 1px 1px rgba(0, 0, 0, 0.1); }
+
+.pdfjs #toolbarViewer {
+  height: 32px; }
+
+.pdfjs #loadingBar {
+  position: relative;
+  width: 100%;
+  height: 4px;
+  background-color: #333;
+  border-bottom: 1px solid #333; }
+
+.pdfjs #loadingBar .progress {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 0;
+  height: 100%;
+  background-color: #ddd;
+  overflow: hidden;
+  -webkit-transition: width 200ms;
+  transition: width 200ms; }
+
+@-webkit-keyframes progressIndeterminate {
+  0% {
+    left: 0; }
+  50% {
+    left: 100%; }
+  100% {
+    left: 100%; } }
+
+@keyframes progressIndeterminate {
+  0% {
+    left: 0; }
+  50% {
+    left: 100%; }
+  100% {
+    left: 100%; } }
+
+.pdfjs #loadingBar .progress.indeterminate {
+  background-color: #999;
+  -webkit-transition: none;
+  transition: none; }
+
+.pdfjs #loadingBar .indeterminate .glimmer {
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  width: 50px;
+  background-image: linear-gradient(to right, #999 0%, #fff 50%, #999 100%);
+  background-size: 100% 100%;
+  background-repeat: no-repeat;
+  -webkit-animation: progressIndeterminate 2s linear infinite;
+  animation: progressIndeterminate 2s linear infinite; }
+
+.pdfjs .findbar,
+.pdfjs .secondaryToolbar {
+  top: 32px;
+  position: absolute;
+  z-index: 10000;
+  height: 32px;
+  min-width: 16px;
+  padding: 0 6px;
+  margin: 4px 2px;
+  color: #d9d9d9;
+  font-size: 12px;
+  line-height: 14px;
+  text-align: left;
+  cursor: default; }
+
+html[dir='ltr'] .pdfjs .findbar {
+  left: 68px; }
+
+html[dir='rtl'] .pdfjs .findbar {
+  right: 68px; }
+
+.pdfjs .findbar label {
+  -webkit-user-select: none;
+  -moz-user-select: none; }
+
+.pdfjs #findInput[data-status="pending"] {
+  background-image: url("../images/pdf.js-viewer/loading-small.png");
+  background-repeat: no-repeat;
+  background-position: right; }
+
+html[dir='rtl'] .pdfjs #findInput[data-status="pending"] {
+  background-position: left; }
+
+.pdfjs .secondaryToolbar {
+  padding: 6px;
+  height: auto;
+  z-index: 30000; }
+
+html[dir='ltr'] .pdfjs .secondaryToolbar {
+  right: 4px; }
+
+html[dir='rtl'] .pdfjs .secondaryToolbar {
+  left: 4px; }
+
+.pdfjs #secondaryToolbarButtonContainer {
+  max-width: 200px;
+  max-height: 400px;
+  overflow-y: auto;
+  -webkit-overflow-scrolling: touch;
+  margin-bottom: -4px; }
+
+.pdfjs .doorHanger,
+.pdfjs .doorHangerRight {
+  border: 1px solid rgba(0, 0, 0, 0.5);
+  border-radius: 2px;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); }
+
+.pdfjs .doorHanger:after,
+.pdfjs .doorHanger:before,
+.pdfjs .doorHangerRight:after,
+.pdfjs .doorHangerRight:before {
+  bottom: 100%;
+  border: solid transparent;
+  content: " ";
+  height: 0;
+  width: 0;
+  position: absolute;
+  pointer-events: none; }
+
+.pdfjs .doorHanger:after,
+.pdfjs .doorHangerRight:after {
+  border-bottom-color: rgba(82, 82, 82, 0.99);
+  border-width: 8px; }
+
+.pdfjs .doorHanger:before,
+.pdfjs .doorHangerRight:before {
+  border-bottom-color: rgba(0, 0, 0, 0.5);
+  border-width: 9px; }
+
+html[dir='ltr'] .pdfjs .doorHanger:after,
+html[dir='rtl'] .pdfjs .doorHangerRight:after {
+  left: 13px;
+  margin-left: -8px; }
+
+html[dir='ltr'] .pdfjs .doorHanger:before,
+html[dir='rtl'] .pdfjs .doorHangerRight:before {
+  left: 13px;
+  margin-left: -9px; }
+
+html[dir='rtl'] .pdfjs .doorHanger:after,
+html[dir='ltr'] .pdfjs .doorHangerRight:after {
+  right: 13px;
+  margin-right: -8px; }
+
+html[dir='rtl'] .pdfjs .doorHanger:before,
+html[dir='ltr'] .pdfjs .doorHangerRight:before {
+  right: 13px;
+  margin-right: -9px; }
+
+.pdfjs #findMsg {
+  font-style: italic;
+  color: #A6B7D0; }
+
+.pdfjs #findInput.notFound {
+  background-color: #f66; }
+
+html[dir='ltr'] .pdfjs #toolbarViewerLeft {
+  margin-left: -1px; }
+
+html[dir='rtl'] .pdfjs #toolbarViewerRight {
+  margin-right: -1px; }
+
+html[dir='ltr'] .pdfjs #toolbarViewerLeft,
+html[dir='rtl'] .pdfjs #toolbarViewerRight {
+  position: absolute;
+  top: 0;
+  left: 0; }
+
+html[dir='ltr'] .pdfjs #toolbarViewerRight,
+html[dir='rtl'] .pdfjs #toolbarViewerLeft {
+  position: absolute;
+  top: 0;
+  right: 0; }
+
+html[dir='ltr'] .pdfjs #toolbarViewerLeft > *,
+html[dir='ltr'] .pdfjs #toolbarViewerMiddle > *,
+html[dir='ltr'] .pdfjs #toolbarViewerRight > *,
+html[dir='ltr'] .pdfjs .findbar > * {
+  position: relative;
+  float: left; }
+
+html[dir='rtl'] .pdfjs #toolbarViewerLeft > *,
+html[dir='rtl'] .pdfjs #toolbarViewerMiddle > *,
+html[dir='rtl'] .pdfjs #toolbarViewerRight > *,
+html[dir='rtl'] .pdfjs .findbar > * {
+  position: relative;
+  float: right; }
+
+html[dir='ltr'] .pdfjs .splitToolbarButton {
+  margin: 3px 2px 4px 0;
+  display: inline-block; }
+
+html[dir='rtl'] .pdfjs .splitToolbarButton {
+  margin: 3px 0 4px 2px;
+  display: inline-block; }
+
+html[dir='ltr'] .pdfjs .splitToolbarButton > .toolbarButton {
+  border-radius: 0;
+  float: left; }
+
+html[dir='rtl'] .pdfjs .splitToolbarButton > .toolbarButton {
+  border-radius: 0;
+  float: right; }
+
+.pdfjs .toolbarButton,
+.pdfjs .secondaryToolbarButton,
+.pdfjs .overlayButton {
+  border: 0 none;
+  background: none;
+  width: 32px;
+  height: 25px; }
+
+.pdfjs .toolbarButton > span {
+  display: inline-block;
+  width: 0;
+  height: 0;
+  overflow: hidden; }
+
+.pdfjs .toolbarButton[disabled],
+.pdfjs .secondaryToolbarButton[disabled],
+.pdfjs .overlayButton[disabled] {
+  opacity: 0.5; }
+
+.pdfjs .toolbarButton.group {
+  margin-right: 0; }
+
+.pdfjs .splitToolbarButton.toggled .toolbarButton {
+  margin: 0; }
+
+.pdfjs .splitToolbarButton:hover > .toolbarButton,
+.pdfjs .splitToolbarButton:focus > .toolbarButton,
+.pdfjs .splitToolbarButton.toggled > .toolbarButton,
+.pdfjs .toolbarButton.textButton {
+  background-color: rgba(0, 0, 0, 0.12);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  border: 1px solid rgba(0, 0, 0, 0.35);
+  border-color: rgba(0, 0, 0, 0.32) rgba(0, 0, 0, 0.38) rgba(0, 0, 0, 0.42);
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.15) inset, 0 1px 0 rgba(255, 255, 255, 0.05);
+  -webkit-transition-property: background-color, border-color, box-shadow;
+  -webkit-transition-duration: 150ms;
+  -webkit-transition-timing-function: ease;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
+  transition-timing-function: ease; }
+
+.pdfjs .splitToolbarButton > .toolbarButton:hover,
+.pdfjs .splitToolbarButton > .toolbarButton:focus,
+.pdfjs .dropdownToolbarButton:hover,
+.pdfjs .overlayButton:hover,
+.pdfjs .toolbarButton.textButton:hover,
+.pdfjs .toolbarButton.textButton:focus {
+  background-color: rgba(0, 0, 0, 0.2);
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.15) inset, 0 0 1px rgba(0, 0, 0, 0.05);
+  z-index: 199; }
+
+.pdfjs .splitToolbarButton > .toolbarButton {
+  position: relative; }
+
+html[dir='ltr'] .pdfjs .splitToolbarButton > .toolbarButton:first-child,
+html[dir='rtl'] .pdfjs .splitToolbarButton > .toolbarButton:last-child {
+  position: relative;
+  margin: 0;
+  margin-right: -1px;
+  border-top-left-radius: 2px;
+  border-bottom-left-radius: 2px;
+  border-right-color: transparent; }
+
+html[dir='ltr'] .pdfjs .splitToolbarButton > .toolbarButton:last-child,
+html[dir='rtl'] .pdfjs .splitToolbarButton > .toolbarButton:first-child {
+  position: relative;
+  margin: 0;
+  margin-left: -1px;
+  border-top-right-radius: 2px;
+  border-bottom-right-radius: 2px;
+  border-left-color: transparent; }
+
+.pdfjs .splitToolbarButtonSeparator {
+  padding: 8px 0;
+  width: 1px;
+  background-color: rgba(0, 0, 0, 0.5);
+  z-index: 99;
+  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
+  display: inline-block;
+  margin: 5px 0; }
+
+html[dir='ltr'] .pdfjs .splitToolbarButtonSeparator {
+  float: left; }
+
+html[dir='rtl'] .pdfjs .splitToolbarButtonSeparator {
+  float: right; }
+
+.pdfjs .splitToolbarButton:hover > .splitToolbarButtonSeparator,
+.pdfjs .splitToolbarButton.toggled > .splitToolbarButtonSeparator {
+  padding: 12px 0;
+  margin: 1px 0;
+  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.03);
+  -webkit-transition-property: padding;
+  -webkit-transition-duration: 10ms;
+  -webkit-transition-timing-function: ease;
+  transition-property: padding;
+  transition-duration: 10ms;
+  transition-timing-function: ease; }
+
+.pdfjs .toolbarButton,
+.pdfjs .dropdownToolbarButton,
+.pdfjs .secondaryToolbarButton,
+.pdfjs .overlayButton {
+  min-width: 16px;
+  padding: 2px 6px 0;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  color: rgba(255, 255, 255, 0.8);
+  font-size: 12px;
+  line-height: 14px;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  cursor: default;
+  -webkit-transition-property: background-color, border-color, box-shadow;
+  -webkit-transition-duration: 150ms;
+  -webkit-transition-timing-function: ease;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
+  transition-timing-function: ease; }
+
+html[dir='ltr'] .pdfjs .toolbarButton,
+html[dir='ltr'] .pdfjs .overlayButton,
+html[dir='ltr'] .pdfjs .dropdownToolbarButton {
+  margin: 3px 2px 4px 0; }
+
+html[dir='rtl'] .pdfjs .toolbarButton,
+html[dir='rtl'] .pdfjs .overlayButton,
+html[dir='rtl'] .pdfjs .dropdownToolbarButton {
+  margin: 3px 0 4px 2px; }
+
+.pdfjs .toolbarButton:hover,
+.pdfjs .toolbarButton:focus,
+.pdfjs .dropdownToolbarButton,
+.pdfjs .overlayButton,
+.pdfjs .secondaryToolbarButton:hover,
+.pdfjs .secondaryToolbarButton:focus {
+  background-color: rgba(0, 0, 0, 0.12);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  border: 1px solid rgba(0, 0, 0, 0.35);
+  border-color: rgba(0, 0, 0, 0.32) rgba(0, 0, 0, 0.38) rgba(0, 0, 0, 0.42);
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.15) inset, 0 1px 0 rgba(255, 255, 255, 0.05); }
+
+.pdfjs .toolbarButton:hover:active,
+.pdfjs .overlayButton:hover:active,
+.pdfjs .dropdownToolbarButton:hover:active,
+.pdfjs .secondaryToolbarButton:hover:active {
+  background-color: rgba(0, 0, 0, 0.2);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  border-color: rgba(0, 0, 0, 0.35) rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.45);
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1) inset, 0 0 1px rgba(0, 0, 0, 0.2) inset, 0 1px 0 rgba(255, 255, 255, 0.05);
+  -webkit-transition-property: background-color, border-color, box-shadow;
+  -webkit-transition-duration: 10ms;
+  -webkit-transition-timing-function: linear;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 10ms;
+  transition-timing-function: linear; }
+
+.pdfjs .toolbarButton.toggled,
+.pdfjs .splitToolbarButton.toggled > .toolbarButton.toggled,
+.pdfjs .secondaryToolbarButton.toggled {
+  background-color: rgba(0, 0, 0, 0.3);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  border-color: rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.45) rgba(0, 0, 0, 0.5);
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1) inset, 0 0 1px rgba(0, 0, 0, 0.2) inset, 0 1px 0 rgba(255, 255, 255, 0.05);
+  -webkit-transition-property: background-color, border-color, box-shadow;
+  -webkit-transition-duration: 10ms;
+  -webkit-transition-timing-function: linear;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 10ms;
+  transition-timing-function: linear; }
+
+.pdfjs .toolbarButton.toggled:hover:active,
+.pdfjs .splitToolbarButton.toggled > .toolbarButton.toggled:hover:active,
+.pdfjs .secondaryToolbarButton.toggled:hover:active {
+  background-color: rgba(0, 0, 0, 0.4);
+  border-color: rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.5) rgba(0, 0, 0, 0.55);
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2) inset, 0 0 1px rgba(0, 0, 0, 0.3) inset, 0 1px 0 rgba(255, 255, 255, 0.05); }
+
+.pdfjs .dropdownToolbarButton {
+  width: 120px;
+  max-width: 120px;
+  padding: 0;
+  overflow: hidden;
+  background: url("../images/pdf.js-viewer/toolbarButton-menuArrows.png") no-repeat; }
+
+html[dir='ltr'] .pdfjs .dropdownToolbarButton {
+  background-position: 95%; }
+
+html[dir='rtl'] .pdfjs .dropdownToolbarButton {
+  background-position: 5%; }
+
+.pdfjs .dropdownToolbarButton > select {
+  min-width: 140px;
+  font-size: 12px;
+  color: #f2f2f2;
+  margin: 0;
+  padding: 3px 2px 2px;
+  border: none;
+  background: rgba(0, 0, 0, 0); }
+
+.pdfjs .dropdownToolbarButton > select > option {
+  background: #3d3d3d; }
+
+.pdfjs #customScaleOption {
+  display: none; }
+
+.pdfjs #pageWidthOption {
+  border-bottom: 1px rgba(255, 255, 255, 0.5) solid; }
+
+html[dir='ltr'] .pdfjs .splitToolbarButton:first-child,
+html[dir='ltr'] .pdfjs .toolbarButton:first-child,
+html[dir='rtl'] .pdfjs .splitToolbarButton:last-child,
+html[dir='rtl'] .pdfjs .toolbarButton:last-child {
+  margin-left: 4px; }
+
+html[dir='ltr'] .pdfjs .splitToolbarButton:last-child,
+html[dir='ltr'] .pdfjs .toolbarButton:last-child,
+html[dir='rtl'] .pdfjs .splitToolbarButton:first-child,
+html[dir='rtl'] .pdfjs .toolbarButton:first-child {
+  margin-right: 4px; }
+
+.pdfjs .toolbarButtonSpacer {
+  width: 30px;
+  display: inline-block;
+  height: 1px; }
+
+.pdfjs .toolbarButtonFlexibleSpacer {
+  -webkit-box-flex: 1;
+  -moz-box-flex: 1;
+  min-width: 30px; }
+
+html[dir='ltr'] .pdfjs #findPrevious {
+  margin-left: 3px; }
+
+html[dir='ltr'] .pdfjs #findNext {
+  margin-right: 3px; }
+
+html[dir='rtl'] .pdfjs #findPrevious {
+  margin-right: 3px; }
+
+html[dir='rtl'] .pdfjs #findNext {
+  margin-left: 3px; }
+
+.pdfjs .toolbarButton::before,
+.pdfjs .secondaryToolbarButton::before {
+  position: absolute;
+  display: inline-block;
+  top: 4px;
+  left: 7px; }
+
+html[dir="ltr"] .pdfjs .secondaryToolbarButton::before {
+  left: 4px; }
+
+html[dir="rtl"] .pdfjs .secondaryToolbarButton::before {
+  right: 4px; }
+
+html[dir='ltr'] .pdfjs .toolbarButton#sidebarToggle::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-sidebarToggle.png"); }
+
+html[dir='rtl'] .pdfjs .toolbarButton#sidebarToggle::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-sidebarToggle-rtl.png"); }
+
+html[dir='ltr'] .pdfjs .toolbarButton#secondaryToolbarToggle::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-secondaryToolbarToggle.png"); }
+
+html[dir='rtl'] .pdfjs .toolbarButton#secondaryToolbarToggle::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-secondaryToolbarToggle-rtl.png"); }
+
+html[dir='ltr'] .pdfjs .toolbarButton.findPrevious::before {
+  content: url("../images/pdf.js-viewer/findbarButton-previous.png"); }
+
+html[dir='rtl'] .pdfjs .toolbarButton.findPrevious::before {
+  content: url("../images/pdf.js-viewer/findbarButton-previous-rtl.png"); }
+
+html[dir='ltr'] .pdfjs .toolbarButton.findNext::before {
+  content: url("../images/pdf.js-viewer/findbarButton-next.png"); }
+
+html[dir='rtl'] .pdfjs .toolbarButton.findNext::before {
+  content: url("../images/pdf.js-viewer/findbarButton-next-rtl.png"); }
+
+html[dir='ltr'] .pdfjs .toolbarButton.pageUp::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-pageUp.png"); }
+
+html[dir='rtl'] .pdfjs .toolbarButton.pageUp::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-pageUp-rtl.png"); }
+
+html[dir='ltr'] .pdfjs .toolbarButton.pageDown::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-pageDown.png"); }
+
+html[dir='rtl'] .pdfjs .toolbarButton.pageDown::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-pageDown-rtl.png"); }
+
+.pdfjs .toolbarButton.zoomOut::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-zoomOut.png"); }
+
+.pdfjs .toolbarButton.zoomIn::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-zoomIn.png"); }
+
+.pdfjs .toolbarButton.presentationMode::before,
+.pdfjs .secondaryToolbarButton.presentationMode::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-presentationMode.png"); }
+
+.pdfjs .toolbarButton.print::before,
+.pdfjs .secondaryToolbarButton.print::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-print.png"); }
+
+.pdfjs .toolbarButton.openFile::before,
+.pdfjs .secondaryToolbarButton.openFile::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-openFile.png"); }
+
+.pdfjs .toolbarButton.download::before,
+.pdfjs .secondaryToolbarButton.download::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-download.png"); }
+
+.pdfjs .toolbarButton.bookmark,
+.pdfjs .secondaryToolbarButton.bookmark {
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  outline: none;
+  padding-top: 4px;
+  text-decoration: none; }
+
+.pdfjs .secondaryToolbarButton.bookmark {
+  padding-top: 5px; }
+
+.pdfjs .bookmark[href='#'] {
+  opacity: .5;
+  pointer-events: none; }
+
+.pdfjs .toolbarButton.bookmark::before,
+.pdfjs .secondaryToolbarButton.bookmark::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-bookmark.png"); }
+
+.pdfjs #viewThumbnail.toolbarButton::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-viewThumbnail.png"); }
+
+html[dir="ltr"] .pdfjs #viewOutline.toolbarButton::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-viewOutline.png"); }
+
+html[dir="rtl"] .pdfjs #viewOutline.toolbarButton::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-viewOutline-rtl.png"); }
+
+.pdfjs #viewAttachments.toolbarButton::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-viewAttachments.png"); }
+
+.pdfjs #viewFind.toolbarButton::before {
+  content: url("../images/pdf.js-viewer/toolbarButton-search.png"); }
+
+.pdfjs .secondaryToolbarButton {
+  position: relative;
+  margin: 0 0 4px;
+  padding: 3px 0 1px;
+  height: auto;
+  min-height: 25px;
+  width: auto;
+  min-width: 100%;
+  white-space: normal; }
+
+html[dir="ltr"] .pdfjs .secondaryToolbarButton {
+  padding-left: 24px;
+  text-align: left; }
+
+html[dir="rtl"] .pdfjs .secondaryToolbarButton {
+  padding-right: 24px;
+  text-align: right; }
+
+html[dir="ltr"] .pdfjs .secondaryToolbarButton.bookmark {
+  padding-left: 27px; }
+
+html[dir="rtl"] .pdfjs .secondaryToolbarButton.bookmark {
+  padding-right: 27px; }
+
+html[dir="ltr"] .pdfjs .secondaryToolbarButton > span {
+  padding-right: 4px; }
+
+html[dir="rtl"] .pdfjs .secondaryToolbarButton > span {
+  padding-left: 4px; }
+
+.pdfjs .secondaryToolbarButton.firstPage::before {
+  content: url("../images/pdf.js-viewer/secondaryToolbarButton-firstPage.png"); }
+
+.pdfjs .secondaryToolbarButton.lastPage::before {
+  content: url("../images/pdf.js-viewer/secondaryToolbarButton-lastPage.png"); }
+
+.pdfjs .secondaryToolbarButton.rotateCcw::before {
+  content: url("../images/pdf.js-viewer/secondaryToolbarButton-rotateCcw.png"); }
+
+.pdfjs .secondaryToolbarButton.rotateCw::before {
+  content: url("../images/pdf.js-viewer/secondaryToolbarButton-rotateCw.png"); }
+
+.pdfjs .secondaryToolbarButton.handTool::before {
+  content: url("../images/pdf.js-viewer/secondaryToolbarButton-handTool.png"); }
+
+.pdfjs .secondaryToolbarButton.documentProperties::before {
+  content: url("../images/pdf.js-viewer/secondaryToolbarButton-documentProperties.png"); }
+
+.pdfjs .verticalToolbarSeparator {
+  display: block;
+  padding: 8px 0;
+  margin: 8px 4px;
+  width: 1px;
+  background-color: rgba(0, 0, 0, 0.5);
+  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08); }
+
+html[dir='ltr'] .pdfjs .verticalToolbarSeparator {
+  margin-left: 2px; }
+
+html[dir='rtl'] .pdfjs .verticalToolbarSeparator {
+  margin-right: 2px; }
+
+.pdfjs .horizontalToolbarSeparator {
+  display: block;
+  margin: 0 0 4px;
+  height: 1px;
+  width: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08); }
+
+.pdfjs .toolbarField {
+  padding: 3px 6px;
+  margin: 4px 0;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  background-color: rgba(255, 255, 255, 0.09);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  border: 1px solid rgba(0, 0, 0, 0.35);
+  border-color: rgba(0, 0, 0, 0.32) rgba(0, 0, 0, 0.38) rgba(0, 0, 0, 0.42);
+  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.05) inset, 0 1px 0 rgba(255, 255, 255, 0.05);
+  color: #f2f2f2;
+  font-size: 12px;
+  line-height: 14px;
+  outline-style: none;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
+  transition-timing-function: ease; }
+
+.pdfjs .toolbarField[type=checkbox] {
+  display: inline-block;
+  margin: 8px 0; }
+
+.pdfjs .toolbarField.pageNumber {
+  -moz-appearance: textfield;
+  min-width: 16px;
+  text-align: right;
+  width: 40px; }
+
+.pdfjs .toolbarField.pageNumber.visiblePageIsLoading {
+  background-image: url("../images/pdf.js-viewer/loading-small.png");
+  background-repeat: no-repeat;
+  background-position: 1px; }
+
+.pdfjs .toolbarField.pageNumber::-webkit-inner-spin-button,
+.pdfjs .toolbarField.pageNumber::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  margin: 0; }
+
+.pdfjs .toolbarField:hover {
+  background-color: rgba(255, 255, 255, 0.11);
+  border-color: rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.43) rgba(0, 0, 0, 0.45); }
+
+.pdfjs .toolbarField:focus {
+  background-color: rgba(255, 255, 255, 0.15);
+  border-color: rgba(77, 184, 255, 0.8) rgba(77, 184, 255, 0.85) rgba(77, 184, 255, 0.9); }
+
+.pdfjs .toolbarLabel {
+  min-width: 16px;
+  padding: 3px 6px 3px 2px;
+  margin: 4px 2px 4px 0;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  color: #d9d9d9;
+  font-size: 12px;
+  line-height: 14px;
+  text-align: left;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  cursor: default; }
+
+.pdfjs #thumbnailView {
+  position: absolute;
+  width: 120px;
+  top: 0;
+  bottom: 0;
+  padding: 10px 40px 0;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch; }
+
+.pdfjs .thumbnail {
+  float: left;
+  margin-bottom: 5px; }
+
+.pdfjs #thumbnailView > a:last-of-type > .thumbnail {
+  margin-bottom: 10px; }
+
+.pdfjs #thumbnailView > a:last-of-type > .thumbnail:not([data-loaded]) {
+  margin-bottom: 9px; }
+
+.pdfjs .thumbnail:not([data-loaded]) {
+  border: 1px dashed rgba(255, 255, 255, 0.5);
+  margin: -1px -1px 4px; }
+
+.pdfjs .thumbnailImage {
+  border: 1px solid transparent;
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);
+  opacity: .8;
+  z-index: 99;
+  background-color: #fff;
+  background-clip: content-box; }
+
+.pdfjs .thumbnailSelectionRing {
+  border-radius: 2px;
+  padding: 7px; }
+
+.pdfjs a:focus > .thumbnail > .thumbnailSelectionRing > .thumbnailImage,
+.pdfjs .thumbnail:hover > .thumbnailSelectionRing > .thumbnailImage {
+  opacity: 0.9; }
+
+.pdfjs a:focus > .thumbnail > .thumbnailSelectionRing,
+.pdfjs .thumbnail:hover > .thumbnailSelectionRing {
+  background-color: rgba(255, 255, 255, 0.15);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.2) inset, 0 0 1px rgba(0, 0, 0, 0.2);
+  color: rgba(255, 255, 255, 0.9); }
+
+.pdfjs .thumbnail.selected > .thumbnailSelectionRing > .thumbnailImage {
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
+  opacity: 1; }
+
+.pdfjs .thumbnail.selected > .thumbnailSelectionRing {
+  background-color: rgba(255, 255, 255, 0.3);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.1) inset, 0 0 1px rgba(0, 0, 0, 0.2);
+  color: #ffffff; }
+
+.pdfjs #outlineView,
+.pdfjs #attachmentsView {
+  position: absolute;
+  width: 192px;
+  top: 0;
+  bottom: 0;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+  -webkit-user-select: none;
+  -moz-user-select: none; }
+
+.pdfjs #outlineView {
+  padding: 4px 4px 0; }
+
+.pdfjs #attachmentsView {
+  padding: 3px 4px 0; }
+
+html[dir='ltr'] .pdfjs .outlineItem > .outlineItems {
+  margin-left: 20px; }
+
+html[dir='rtl'] .pdfjs .outlineItem > .outlineItems {
+  margin-right: 20px; }
+
+.pdfjs .outlineItem > a,
+.pdfjs .attachmentsItem > button {
+  text-decoration: none;
+  display: inline-block;
+  min-width: 95%;
+  height: auto;
+  margin-bottom: 1px;
+  border-radius: 2px;
+  color: rgba(255, 255, 255, 0.8);
+  font-size: 13px;
+  line-height: 15px;
+  -moz-user-select: none;
+  white-space: normal; }
+
+.pdfjs .attachmentsItem > button {
+  border: 0 none;
+  background: none;
+  cursor: pointer;
+  width: 100%; }
+
+html[dir='ltr'] .pdfjs .outlineItem > a {
+  padding: 2px 0 5px 10px; }
+
+html[dir='ltr'] .pdfjs .attachmentsItem > button {
+  padding: 2px 0 3px 7px;
+  text-align: left; }
+
+html[dir='rtl'] .pdfjs .outlineItem > a {
+  padding: 2px 10px 5px 0; }
+
+html[dir='rtl'] .pdfjs .attachmentsItem > button {
+  padding: 2px 7px 3px 0;
+  text-align: right; }
+
+.pdfjs .outlineItem > a:hover,
+.pdfjs .attachmentsItem > button:hover {
+  background-color: rgba(255, 255, 255, 0.02);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.2) inset, 0 0 1px rgba(0, 0, 0, 0.2);
+  color: rgba(255, 255, 255, 0.9); }
+
+.pdfjs .outlineItem.selected {
+  background-color: rgba(255, 255, 255, 0.08);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0));
+  background-clip: padding-box;
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 0 1px rgba(255, 255, 255, 0.1) inset, 0 0 1px rgba(0, 0, 0, 0.2);
+  color: #ffffff; }
+
+.pdfjs .noResults {
+  font-size: 12px;
+  color: rgba(255, 255, 255, 0.8);
+  font-style: italic;
+  cursor: default; }
+
+.pdfjs ::selection {
+  background: rgba(0, 0, 255, 0.3); }
+
+.pdfjs ::-moz-selection {
+  background: rgba(0, 0, 255, 0.3); }
+
+.pdfjs #errorWrapper {
+  background: none repeat scroll 0 0 #F55;
+  color: #fff;
+  left: 0;
+  position: absolute;
+  right: 0;
+  z-index: 1000;
+  padding: 3px;
+  font-size: 0.8em; }
+
+.pdfjs .loadingInProgress #errorWrapper {
+  top: 37px; }
+
+.pdfjs #errorMessageLeft {
+  float: left; }
+
+.pdfjs #errorMessageRight {
+  float: right; }
+
+.pdfjs #errorMoreInfo {
+  background-color: #FFF;
+  color: #000;
+  padding: 3px;
+  margin: 3px;
+  width: 98%; }
+
+.pdfjs .overlayButton {
+  width: auto;
+  margin: 3px 4px 2px !important;
+  padding: 2px 6px 3px; }
+
+.pdfjs #overlayContainer {
+  display: table;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.2);
+  z-index: 40000; }
+
+.pdfjs #overlayContainer > * {
+  overflow: auto;
+  -webkit-overflow-scrolling: touch; }
+
+.pdfjs #overlayContainer > .container {
+  display: table-cell;
+  vertical-align: middle;
+  text-align: center; }
+
+.pdfjs #overlayContainer > .container > .dialog {
+  display: inline-block;
+  padding: 15px;
+  border-spacing: 4px;
+  color: #d9d9d9;
+  font-size: 12px;
+  line-height: 14px;
+  background-color: #474747;
+  background-image: url("../images/pdf.js-viewer/texture.png"), linear-gradient(rgba(82, 82, 82, 0.99), rgba(69, 69, 69, 0.95));
+  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.08), inset 0 1px 1px rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(255, 255, 255, 0.05), 0 1px 0 rgba(0, 0, 0, 0.15), 0 1px 1px rgba(0, 0, 0, 0.1);
+  border: 1px solid rgba(0, 0, 0, 0.5);
+  border-radius: 4px;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); }
+
+.pdfjs .dialog > .row {
+  display: table-row; }
+
+.pdfjs .dialog > .row > * {
+  display: table-cell; }
+
+.pdfjs .dialog .toolbarField {
+  margin: 5px 0; }
+
+.pdfjs .dialog .separator {
+  display: block;
+  margin: 4px 0;
+  height: 1px;
+  width: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08); }
+
+.pdfjs .dialog .buttonRow {
+  text-align: center;
+  vertical-align: middle; }
+
+.pdfjs #passwordOverlay > .dialog {
+  text-align: center; }
+
+.pdfjs #passwordOverlay .toolbarField {
+  width: 200px; }
+
+.pdfjs #documentPropertiesOverlay > .dialog {
+  text-align: left; }
+
+.pdfjs #documentPropertiesOverlay .row > * {
+  min-width: 100px; }
+
+html[dir='ltr'] .pdfjs #documentPropertiesOverlay .row > * {
+  text-align: left; }
+
+html[dir='rtl'] .pdfjs #documentPropertiesOverlay .row > * {
+  text-align: right; }
+
+.pdfjs #documentPropertiesOverlay .row > span {
+  width: 125px;
+  word-wrap: break-word; }
+
+.pdfjs #documentPropertiesOverlay .row > p {
+  max-width: 225px;
+  word-wrap: break-word; }
+
+.pdfjs #documentPropertiesOverlay .buttonRow {
+  margin-top: 10px; }
+
+.pdfjs .clearBoth {
+  clear: both; }
+
+.pdfjs .fileInput {
+  background: #fff;
+  color: #000;
+  margin-top: 5px;
+  visibility: hidden;
+  position: fixed;
+  right: 0;
+  top: 0; }
+
+.pdfjs #PDFBug {
+  background: none repeat scroll 0 0 #fff;
+  border: 1px solid #666;
+  position: fixed;
+  top: 32px;
+  right: 0;
+  bottom: 0;
+  font-size: 10px;
+  padding: 0;
+  width: 300px; }
+
+.pdfjs #PDFBug .controls {
+  background: #EEE;
+  border-bottom: 1px solid #666;
+  padding: 3px; }
+
+.pdfjs #PDFBug .panels {
+  bottom: 0;
+  left: 0;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+  position: absolute;
+  right: 0;
+  top: 27px; }
+
+.pdfjs #PDFBug button.active {
+  font-weight: 700; }
+
+.pdfjs .debuggerShowText {
+  background: none repeat scroll 0 0 #ff0;
+  color: blue; }
+
+.pdfjs .debuggerHideText:hover {
+  background: none repeat scroll 0 0 #ff0; }
+
+.pdfjs #PDFBug .stats {
+  font-family: courier;
+  font-size: 10px;
+  white-space: pre; }
+
+.pdfjs #PDFBug .stats .title {
+  font-weight: 700; }
+
+.pdfjs #PDFBug table {
+  font-size: 10px; }
+
+.pdfjs #viewer.textLayer-visible .textLayer > div,
+.pdfjs #viewer.textLayer-hover .textLayer > div:hover {
+  background-color: #fff;
+  color: #000; }
+
+.pdfjs #viewer.textLayer-shadow .textLayer > div {
+  background-color: rgba(255, 255, 255, 0.6);
+  color: #000; }
+
+.pdfjs .grab-to-pan-grab {
+  cursor: url("../images/pdf.js-viewer/grab.cur"), move !important;
+  cursor: -webkit-grab !important;
+  cursor: -moz-grab !important;
+  cursor: grab !important; }
+
+.pdfjs .grab-to-pan-grab :not(input):not(textarea):not(button):not(select):not(:link) {
+  cursor: inherit !important; }
+
+.pdfjs .grab-to-pan-grab:active,
+.pdfjs .grab-to-pan-grabbing {
+  cursor: url("../images/pdf.js-viewer/grabbing.cur"), move !important;
+  cursor: -webkit-grabbing !important;
+  cursor: -moz-grabbing !important;
+  cursor: grabbing !important;
+  position: fixed;
+  background: transparent;
+  display: block;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  overflow: hidden;
+  z-index: 50000; }
+
+@page {
+  margin: 0; }
+
+.pdfjs #printContainer {
+  display: none; }
+
+@media screen and (min-resolution: 2dppx) {
+  .pdfjs .toolbarButton::before {
+    -webkit-transform: scale(0.5);
+    transform: scale(0.5);
+    top: -5px; }
+  .pdfjs .secondaryToolbarButton::before {
+    -webkit-transform: scale(0.5);
+    transform: scale(0.5);
+    top: -4px; }
+  html[dir='ltr'] .pdfjs .toolbarButton::before,
+  html[dir='rtl'] .pdfjs .toolbarButton::before {
+    left: -1px; }
+  html[dir="ltr"] .pdfjs .secondaryToolbarButton::before {
+    left: -2px; }
+  html[dir="rtl"] .pdfjs .secondaryToolbarButton::before {
+    left: 186px; }
+  .pdfjs .toolbarField.pageNumber.visiblePageIsLoading,
+  .pdfjs #findInput[data-status="pending"] {
+    background-image: url("../images/pdf.js-viewer/loading-small@2x.png");
+    background-size: 16px 17px; }
+  .pdfjs .dropdownToolbarButton {
+    background: url("../images/pdf.js-viewer/toolbarButton-menuArrows@2x.png") no-repeat;
+    background-size: 7px 16px; }
+  html[dir='ltr'] .pdfjs .toolbarButton#sidebarToggle::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-sidebarToggle@2x.png"); }
+  html[dir='rtl'] .pdfjs .toolbarButton#sidebarToggle::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-sidebarToggle-rtl@2x.png"); }
+  html[dir='ltr'] .pdfjs .toolbarButton#secondaryToolbarToggle::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-secondaryToolbarToggle@2x.png"); }
+  html[dir='rtl'] .pdfjs .toolbarButton#secondaryToolbarToggle::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-secondaryToolbarToggle-rtl@2x.png"); }
+  html[dir='ltr'] .pdfjs .toolbarButton.findPrevious::before {
+    content: url("../images/pdf.js-viewer/findbarButton-previous@2x.png"); }
+  html[dir='rtl'] .pdfjs .toolbarButton.findPrevious::before {
+    content: url("../images/pdf.js-viewer/findbarButton-previous-rtl@2x.png"); }
+  html[dir='ltr'] .pdfjs .toolbarButton.findNext::before {
+    content: url("../images/pdf.js-viewer/findbarButton-next@2x.png"); }
+  html[dir='rtl'] .pdfjs .toolbarButton.findNext::before {
+    content: url("../images/pdf.js-viewer/findbarButton-next-rtl@2x.png"); }
+  html[dir='ltr'] .pdfjs .toolbarButton.pageUp::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-pageUp@2x.png"); }
+  html[dir='rtl'] .pdfjs .toolbarButton.pageUp::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-pageUp-rtl@2x.png"); }
+  html[dir='ltr'] .pdfjs .toolbarButton.pageDown::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-pageDown@2x.png"); }
+  html[dir='rtl'] .pdfjs .toolbarButton.pageDown::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-pageDown-rtl@2x.png"); }
+  .pdfjs .toolbarButton.zoomIn::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-zoomIn@2x.png"); }
+  .pdfjs .toolbarButton.zoomOut::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-zoomOut@2x.png"); }
+  .pdfjs .toolbarButton.presentationMode::before,
+  .pdfjs .secondaryToolbarButton.presentationMode::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-presentationMode@2x.png"); }
+  .pdfjs .toolbarButton.print::before,
+  .pdfjs .secondaryToolbarButton.print::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-print@2x.png"); }
+  .pdfjs .toolbarButton.openFile::before,
+  .pdfjs .secondaryToolbarButton.openFile::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-openFile@2x.png"); }
+  .pdfjs .toolbarButton.download::before,
+  .pdfjs .secondaryToolbarButton.download::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-download@2x.png"); }
+  .pdfjs .toolbarButton.bookmark::before,
+  .pdfjs .secondaryToolbarButton.bookmark::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-bookmark@2x.png"); }
+  .pdfjs #viewThumbnail.toolbarButton::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-viewThumbnail@2x.png"); }
+  html[dir="ltr"] .pdfjs #viewOutline.toolbarButton::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-viewOutline@2x.png"); }
+  html[dir="rtl"] .pdfjs #viewOutline.toolbarButton::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-viewOutline-rtl@2x.png"); }
+  .pdfjs #viewAttachments.toolbarButton::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-viewAttachments@2x.png"); }
+  .pdfjs #viewFind.toolbarButton::before {
+    content: url("../images/pdf.js-viewer/toolbarButton-search@2x.png"); }
+  .pdfjs .secondaryToolbarButton.firstPage::before {
+    content: url("../images/pdf.js-viewer/secondaryToolbarButton-firstPage@2x.png"); }
+  .pdfjs .secondaryToolbarButton.lastPage::before {
+    content: url("../images/pdf.js-viewer/secondaryToolbarButton-lastPage@2x.png"); }
+  .pdfjs .secondaryToolbarButton.rotateCcw::before {
+    content: url("../images/pdf.js-viewer/secondaryToolbarButton-rotateCcw@2x.png"); }
+  .pdfjs .secondaryToolbarButton.rotateCw::before {
+    content: url("../images/pdf.js-viewer/secondaryToolbarButton-rotateCw@2x.png"); }
+  .pdfjs .secondaryToolbarButton.handTool::before {
+    content: url("../images/pdf.js-viewer/secondaryToolbarButton-handTool@2x.png"); }
+  .pdfjs .secondaryToolbarButton.documentProperties::before {
+    content: url("../images/pdf.js-viewer/secondaryToolbarButton-documentProperties@2x.png"); } }
+
+@media print {
+  body {
+    background: transparent none; }
+  .pdfjs #sidebarContainer,
+  .pdfjs #secondaryToolbar,
+  .pdfjs .toolbar,
+  .pdfjs #loadingBox,
+  .pdfjs #errorWrapper,
+  .pdfjs .textLayer {
+    display: none; }
+  .pdfjs #viewerContainer {
+    overflow: visible; }
+  .pdfjs #mainContainer,
+  .pdfjs #viewerContainer,
+  .pdfjs .page,
+  .pdfjs .page canvas {
+    position: static;
+    padding: 0;
+    margin: 0; }
+  .pdfjs .page {
+    float: left;
+    display: none;
+    border: none;
+    box-shadow: none;
+    background-clip: content-box;
+    background-color: #fff; }
+  .pdfjs .page[data-loaded] {
+    display: block; }
+  .pdfjs .fileInput {
+    display: none; }
+  body[data-mozPrintCallback] .pdfjs #outerContainer {
+    display: none; }
+  body[data-mozPrintCallback] .pdfjs #printContainer {
+    display: block; }
+  .pdfjs #printContainer > div {
+    position: relative;
+    top: 0;
+    left: 0;
+    overflow: hidden; }
+  .pdfjs #printContainer canvas {
+    display: block; } }
+
+.pdfjs .visibleLargeView,
+.pdfjs .visibleMediumView,
+.pdfjs .visibleSmallView {
+  display: none; }
+
+@media all and (max-width: 960px) {
+  html[dir='ltr'] .pdfjs #outerContainer.sidebarMoving .outerCenter,
+  html[dir='ltr'] .pdfjs #outerContainer.sidebarOpen .outerCenter {
+    float: left;
+    left: 205px; }
+  html[dir='rtl'] .pdfjs #outerContainer.sidebarMoving .outerCenter,
+  html[dir='rtl'] .pdfjs #outerContainer.sidebarOpen .outerCenter {
+    float: right;
+    right: 205px; } }
+
+@media all and (max-width: 900px) {
+  .pdfjs .sidebarOpen .hiddenLargeView {
+    display: none; }
+  .pdfjs .sidebarOpen .visibleLargeView {
+    display: inherit; } }
+
+@media all and (max-width: 860px) {
+  .pdfjs .sidebarOpen .hiddenMediumView {
+    display: none; }
+  .pdfjs .sidebarOpen .visibleMediumView {
+    display: inherit; } }
+
+@media all and (max-width: 770px) {
+  .pdfjs #sidebarContainer {
+    top: 32px;
+    z-index: 100; }
+  .pdfjs .loadingInProgress #sidebarContainer {
+    top: 37px; }
+  .pdfjs #sidebarContent {
+    top: 32px;
+    background-color: rgba(0, 0, 0, 0.7); }
+  html[dir='ltr'] .pdfjs #outerContainer.sidebarOpen > #mainContainer {
+    left: 0; }
+  html[dir='rtl'] .pdfjs #outerContainer.sidebarOpen > #mainContainer {
+    right: 0; }
+  html[dir='ltr'] .pdfjs .outerCenter {
+    float: left;
+    left: 205px; }
+  html[dir='rtl'] .pdfjs .outerCenter {
+    float: right;
+    right: 205px; }
+  .pdfjs #outerContainer .hiddenLargeView,
+  .pdfjs #outerContainer .hiddenMediumView {
+    display: inherit; }
+  .pdfjs #outerContainer .visibleLargeView,
+  .pdfjs #outerContainer .visibleMediumView {
+    display: none; } }
+
+@media all and (max-width: 700px) {
+  .pdfjs #outerContainer .hiddenLargeView {
+    display: none; }
+  .pdfjs #outerContainer .visibleLargeView {
+    display: inherit; } }
+
+@media all and (max-width: 660px) {
+  .pdfjs #outerContainer .hiddenMediumView {
+    display: none; }
+  .pdfjs #outerContainer .visibleMediumView {
+    display: inherit; } }
+
+@media all and (max-width: 600px) {
+  .pdfjs .hiddenSmallView {
+    display: none; }
+  .pdfjs .visibleSmallView {
+    display: inherit; }
+  html[dir='ltr'] .pdfjs #outerContainer.sidebarMoving .outerCenter,
+  html[dir='ltr'] .pdfjs #outerContainer.sidebarOpen .outerCenter,
+  html[dir='ltr'] .pdfjs .outerCenter {
+    left: 156px; }
+  html[dir='rtl'] .pdfjs #outerContainer.sidebarMoving .outerCenter,
+  html[dir='rtl'] .pdfjs #outerContainer.sidebarOpen .outerCenter,
+  html[dir='rtl'] .pdfjs .outerCenter {
+    right: 156px; }
+  .pdfjs .toolbarButtonSpacer {
+    width: 0; } }
+
+@media all and (max-width: 510px) {
+  .pdfjs #scaleSelectContainer,
+  .pdfjs #pageNumberLabel {
+    display: none; } }
+
+/* should be hidden differently */
+#fileInput.fileInput {
+  display: none; }
+
+.wvSplitpane {
+  height: 100%;
+  padding: 7px 2px 2px 2px;
+  position: relative; }
+
+.wvSplitpane__cell {
+  display: inline-block;
+  float: left;
+  height: 100%;
+  width: 100%;
+  position: relative; }
+
+.wvSplitpane__cellBorder, .wvSplitpane__cellBorder--selected, .wvSplitpane__cellBorder--blue, .wvSplitpane__cellBorder--red, .wvSplitpane__cellBorder--green, .wvSplitpane__cellBorder--yellow, .wvSplitpane__cellBorder--violet {
+  display: inline-block;
+  float: left;
+  height: calc(100% - 2px);
+  width: calc(100% - 2px);
+  border: 2px dashed transparent;
+  padding: 2px;
+  margin: 1px; }
+
+.wvSplitpane__cellBorder--selected {
+  border: 2px solid rgba(51, 152, 219, 0.7); }
+
+.wvSplitpane__cellBorder--blue {
+  border-color: rgba(51, 152, 219, 0.7); }
+
+.wvSplitpane__cellBorder--red {
+  border-color: rgba(206, 0, 0, 0.7); }
+
+.wvSplitpane__cellBorder--green {
+  border-color: rgba(0, 160, 27, 0.7); }
+
+.wvSplitpane__cellBorder--yellow {
+  border-color: rgba(220, 200, 0, 0.9); }
+
+.wvSplitpane__cellBorder--violet {
+  border-color: rgba(255, 31, 255, 0.7); }
+
+wv-pane-policy {
+  display: block;
+  width: 100%;
+  height: 100%; }
+  wv-pane-policy > div[ng-transclude] {
+    display: block;
+    width: 100%;
+    height: 100%; }
+
+.wv-timeline {
+  position: relative;
+  height: 2em; }
+  .wv-timeline.reduced {
+    height: 5px; }
+    .wv-timeline.reduced .wv-timeline-loading-bar-wrapper {
+      width: 100%;
+      height: 100%; }
+
+.wv-timeline-controls-wrapper {
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: 16em;
+  height: 100%;
+  color: white; }
+
+.wv-timeline-loading-bar-wrapper {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  width: calc(100% - 16em);
+  height: calc(100% + 2px); }
+  .wv-timeline-loading-bar-wrapper svg {
+    position: absolute;
+    left: 0;
+    top: 0; }
+
+/* wv-timeline-controls directive */
+.wv-timeline-controls {
+  padding: 0.5em 0.5em 0.5em 0.5em;
+  line-height: 1em;
+  background-color: rgba(0, 0, 0, 0.66);
+  text-align: center;
+  transition: color 500ms, background-color 500ms; }
+
+.wv-timeline-controls:hover {
+  background-color: rgba(0, 0, 0, 0.9); }
+
+.wv-timeline-controls-vertical-sizing {
+  display: inline-block;
+  line-height: 1em;
+  font-size: 1em; }
+
+.wv-timeline-controls-vflip:before, .wv-timeline-controls-vflip:after {
+  transform: scaleX(-1);
+  display: inline-block; }
+
+.wv-timeline-controls-button {
+  display: inline-block;
+  height: 1em;
+  width: 1em;
+  line-height: 1em;
+  font-size: 1em;
+  margin: 0;
+  user-select: none;
+  cursor: pointer; }
+
+.wv-timeline-controls-input {
+  height: 1em;
+  width: 3em;
+  padding: 0;
+  padding-bottom: 1px;
+  box-sizing: content-box;
+  border: none;
+  border-bottom: 1px solid rgba(255, 202, 128, 0.24);
+  background-color: transparent;
+  text-align: right; }
+
+.wv-timeline-controls-play-button-wrapper {
+  float: right; }
+
+/* wv-play-button directive */
+.wv-play-button {
+  display: inline-block;
+  position: relative;
+  line-height: 1em;
+  height: 3em;
+  width: 6em;
+  padding-bottom: 1em;
+  padding-left: 0.25em;
+  padding-right: 0.25em; }
+
+.wv-play-button:hover .wv-play-button-config-position-handler {
+  visibility: visible; }
+
+.wv-play-button-config-position-handler {
+  visibility: hidden;
+  position: absolute;
+  bottom: 3em;
+  left: 1em;
+  right: 0.5em; }
+
+.wv-play-button-config {
+  position: absolute;
+  bottom: 0;
+  left: -6em;
+  width: 12em;
+  padding: 1em;
+  background-color: rgba(0, 0, 0, 0.5); }
+
+/* Style range input (see http://brennaobrien.com/blog/2014/05/style-input-type-range-in-every-browser.html) */
+.wv-play-button-config-framerate-wrapper {
+  display: inline-block;
+  margin: 0.25em 0 0.5em 0; }
+
+input[type="range"].wv-play-button-config-framerate {
+  /*removes default webkit styles*/
+  -webkit-appearance: none;
+  /*fix for FF unable to apply focus style bug */
+  border: 1px solid white;
+  /*required for proper track sizing in FF*/
+  width: 10em; }
+
+input[type="range"].wv-play-button-config-framerate::-webkit-slider-runnable-track {
+  width: 10em;
+  height: 5px;
+  background: #ddd;
+  border: none;
+  border-radius: 3px; }
+
+input[type="range"].wv-play-button-config-framerate::-webkit-slider-thumb {
+  -webkit-appearance: none;
+  border: none;
+  height: 16px;
+  width: 16px;
+  border-radius: 50%;
+  background: goldenrod;
+  margin-top: -4px; }
+
+input[type="range"].wv-play-button-config-framerate:focus {
+  outline: none; }
+
+input[type="range"].wv-play-button-config-framerate:focus::-webkit-slider-runnable-track {
+  background: #ccc; }
+
+input[type="range"].wv-play-button-config-framerate::-moz-range-track {
+  width: 10em;
+  height: 5px;
+  background: #ddd;
+  border: none;
+  border-radius: 3px; }
+
+input[type="range"].wv-play-button-config-framerate::-moz-range-thumb {
+  border: none;
+  height: 16px;
+  width: 16px;
+  border-radius: 50%;
+  background: goldenrod; }
+
+/*hide the outline behind the border*/
+input[type="range"].wv-play-button-config-framerate:-moz-focusring {
+  outline: 1px solid white;
+  outline-offset: -1px; }
+
+input[type="range"].wv-play-button-config-framerate::-ms-track {
+  width: 10em;
+  height: 5px;
+  /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */
+  background: transparent;
+  /*leave room for the larger thumb to overflow with a transparent border */
+  border-color: transparent;
+  border-width: 6px 0;
+  /*remove default tick marks*/
+  color: transparent; }
+
+input[type="range"].wv-play-button-config-framerate::-ms-fill-lower {
+  background: #777;
+  border-radius: 10px; }
+
+input[type="range"].wv-play-button-config-framerate::-ms-fill-upper {
+  background: #ddd;
+  border-radius: 10px; }
+
+input[type="range"].wv-play-button-config-framerate::-ms-thumb {
+  border: none;
+  height: 16px;
+  width: 16px;
+  border-radius: 50%;
+  background: goldenrod; }
+
+input[type="range"].wv-play-button-config-framerate:focus::-ms-fill-lower {
+  background: #888; }
+
+input[type="range"].wv-play-button-config-framerate:focus::-ms-fill-upper {
+  background: #ccc; }
+
+.wv-loadingbar-image-bar {
+  cursor: pointer; }
+
+.wv-loadingbar-not-loaded {
+  fill: rgba(255, 255, 255, 0.1); }
+
+.wv-loadingbar-not-loaded, .wv-loadingbar-LOW-quality {
+  transition: none; }
+
+.wv-loadingbar-not-loaded:hover {
+  fill: rgba(255, 255, 255, 0.2); }
+
+.wv-loadingbar-LOSSLESS-quality, .wv-loadingbar-PIXELDATA-quality {
+  fill: rgba(0, 255, 0, 0.7); }
+
+.wv-loadingbar-LOSSLESS-quality:hover,
+.wv-loadingbar-LOSSLESS-quality.wv-loadingbar-active,
+.wv-loadingbar-PIXELDATA-quality:hover,
+.wv-loadingbar-PIXELDATA-quality.wv-loadingbar-active {
+  fill: lime; }
+
+.wv-loadingbar-LOW-quality {
+  fill: rgba(255, 0, 0, 0.7); }
+
+.wv-loadingbar-LOW-quality:hover, .wv-loadingbar-LOW-quality.wv-loadingbar-active {
+  fill: red; }
+
+.wv-loadingbar-MEDIUM-quality {
+  fill: rgba(255, 95, 0, 0.7); }
+
+.wv-loadingbar-MEDIUM-quality:hover, .wv-loadingbar-MEDIUM-quality.wv-loadingbar-active {
+  fill: #ff5f00; }
+
+.disclaimer {
+  color: #E63F24;
+  background-color: #303030;
+  padding: 5px;
+  text-align: center;
+  font-weight: bold; }
+
+.tbGroup {
+  position: relative; }
+
+.tbGroup__buttons--base, .tbGroup__buttons--bottom, .tbGroup__buttons--left {
+  z-index: 5;
+  background-color: black;
+  position: absolute; }
+
+.tbGroup__buttons--bottom {
+  right: 0;
+  display: block; }
+
+.tbGroup__buttons--left {
+  right: 100%;
+  top: 0;
+  display: block; }
+
+.tbGroup__icon {
+  display: block;
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  width: 0;
+  height: 0;
+  border-style: solid;
+  border-width: 10px 0 0 10px;
+  border-color: transparent transparent transparent rgba(255, 255, 255, 0.1); }
+  .tbGroup__icon.active {
+    border-color: transparent transparent transparent #3498db; }
+
+wv-viewport {
+  display: inline-block;
+  width: 100%;
+  height: 100%; }
+  wv-viewport > div {
+    position: relative;
+    width: 100%;
+    height: 100%; }
+  wv-viewport > div > .wv-cornerstone-enabled-image {
+    width: 100%;
+    height: 100%;
+    text-align: center; }
+
+.wv-draggable-clone {
+  width: 150px;
+  height: 150px;
+  background-color: rgba(255, 255, 255, 0.25); }
+
+@media print {
+  .wvPrintExclude {
+    display: none; }
+  .wvPrintFullPage {
+    width: 100% !important;
+    height: 100% !important;
+    position: absolute !important;
+    top: 0 !important;
+    left: 0 !important;
+    display: block !important; }
+  .wvLayout__main {
+    top: 0 !important;
+    right: 0 !important;
+    left: 0 !important;
+    bottom: 0 !important; }
+  .wvPrintViewer {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center; }
+  .wvPrintViewer canvas {
+    max-width: 100% !important;
+    max-height: 100% !important;
+    margin: auto; }
+  .wv-overlay-topleft, .wv-overlay-topleft *, .wv-overlay-topright, .wv-overlay-topright *, .wv-overlay-bottomright, .wv-overlay-bottomright *, .wv-overlay-bottomleft, .wv-overlay-bottomleft * {
+    background-color: black !important;
+    -webkit-print-color-adjust: exact !important;
+    color-adjust: exact !important;
+    color: orange !important; }
+  .tooltip {
+    display: none !important; }
+  body {
+    margin: 0;
+    padding: 0;
+    position: relative;
+    width: 8.5in;
+    height: 11in; }
+    body, body * {
+      background-color: black !important;
+      -webkit-print-color-adjust: exact !important; } }
+
+.closePrintButton {
+  display: none; }
+
+body.print .wvPrintExclude {
+  display: none; }
+
+body.print .wvPrintFullPage {
+  width: 100% !important;
+  height: 100% !important;
+  position: absolute !important;
+  top: 0 !important;
+  left: 0 !important;
+  display: block !important; }
+
+body.print .wvLayout__main {
+  top: 0 !important;
+  right: 0 !important;
+  left: 0 !important;
+  bottom: 0 !important; }
+
+body.print .wvPrintViewer {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center; }
+
+body.print .wvPrintViewer canvas {
+  max-width: 100% !important;
+  max-height: 100% !important;
+  margin: auto; }
+
+body.print .wv-overlay-topleft, body.print .wv-overlay-topleft *, body.print .wv-overlay-topright, body.print .wv-overlay-topright *, body.print .wv-overlay-bottomright, body.print .wv-overlay-bottomright *, body.print .wv-overlay-bottomleft, body.print .wv-overlay-bottomleft * {
+  background-color: black !important;
+  -webkit-print-color-adjust: exact !important;
+  color-adjust: exact !important;
+  color: orange !important; }
+
+body.print .tooltip {
+  display: none !important; }
+
+body.print body {
+  margin: 0;
+  padding: 0;
+  position: relative;
+  width: 8.5in;
+  height: 11in; }
+  body.print body, body.print body * {
+    background-color: black !important;
+    -webkit-print-color-adjust: exact !important; }
+
+@media screen {
+  body.print .closePrintButton {
+    display: block;
+    position: fixed;
+    top: 0;
+    right: 0;
+    padding: 10px;
+    font-size: 24px;
+    background-color: black;
+    color: white;
+    border: none; } }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/WebApplication/app.js	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,614 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * 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/>.
+ **/
+
+
+var COLORS = [ 'blue', 'red', 'green', 'yellow', 'violet' ];
+var SERIES_INSTANCE_UID = '0020,000e';
+var STUDY_INSTANCE_UID = '0020,000d';
+var STUDY_DESCRIPTION = '0008,1030';
+var STUDY_DATE = '0008,0020';
+
+
+function getParameterFromUrl(key) {
+  var url = window.location.search.substring(1);
+  var args = url.split('&');
+  for (var i = 0; i < args.length; i++) {
+    var arg = args[i].split('=');
+    if (arg[0] == key) {
+      return arg[1];
+    }
+  }
+}
+
+
+Vue.component('viewport', {
+  props: [ 'left', 'top', 'width', 'height', 'canvasId', 'active', 'series', 'viewportIndex',
+           'quality', 'framesCount', 'currentFrame', 'showInfo' ],
+  template: '#viewport-template',
+  data: function () {
+    return {
+      stone: stone,  // To access global object "stone" from "index.html"
+      status: 'waiting'
+    }
+  },
+  watch: { 
+    series: function(newVal, oldVal) {
+      this.status = 'loading';
+
+      var studyInstanceUid = newVal.tags[STUDY_INSTANCE_UID];
+      var seriesInstanceUid = newVal.tags[SERIES_INSTANCE_UID];
+      stone.SpeedUpFetchSeriesMetadata(studyInstanceUid, seriesInstanceUid);
+      
+      if ((newVal.type == stone.ThumbnailType.IMAGE ||
+           newVal.type == stone.ThumbnailType.NO_PREVIEW) &&
+          newVal.complete) {
+        this.status = 'ready';
+
+        var that = this;
+        Vue.nextTick(function() {
+          stone.LoadSeriesInViewport(that.canvasId, seriesInstanceUid);
+        });
+      }
+      else if (newVal.type == stone.ThumbnailType.PDF ||
+               newVal.type == stone.ThumbnailType.VIDEO) {
+        // TODO
+      }
+    }
+  },
+  methods: {
+    SeriesDragAccept: function(event) {
+      event.preventDefault();
+    },
+    SeriesDragDrop: function(event) {
+      event.preventDefault();
+      this.$emit('updated-series', event.dataTransfer.getData('seriesIndex'));
+    },
+    MakeActive: function() {
+      this.$emit('selected-viewport');
+    },
+    DecrementFrame: function() {
+      stone.DecrementFrame(this.canvasId);
+    },
+    IncrementFrame: function() {
+      stone.IncrementFrame(this.canvasId);
+    }
+  }
+})
+
+
+var app = new Vue({
+  el: '#wv',
+  data: function() {
+    return {
+      stone: stone,  // To access global object "stone" from "index.html"
+      ready: false,
+      leftMode: 'grid',   // Can be 'small', 'grid' or 'full'
+      leftVisible: true,
+      showWarning: false,
+      viewportLayoutButtonsVisible: false,
+      activeViewport: 0,
+      showInfo: true,
+      showReferenceLines: true,
+      
+      viewport1Width: '100%',
+      viewport1Height: '100%',
+      viewport1Left: '0%',
+      viewport1Top: '0%',
+      viewport1Visible: true,
+      viewport1Series: {},
+      viewport1Quality: '',
+      viewport1FramesCount: 0,
+      viewport1CurrentFrame: 0,
+      
+      viewport2Width: '100%',
+      viewport2Height: '100%',
+      viewport2Left: '0%',
+      viewport2Top: '0%',
+      viewport2Visible: false,
+      viewport2Series: {},
+      viewport2Quality: '',
+      viewport2FramesCount: 0,
+      viewport2CurrentFrame: 0,
+
+      viewport3Width: '100%',
+      viewport3Height: '100%',
+      viewport3Left: '0%',
+      viewport3Top: '0%',
+      viewport3Visible: false,
+      viewport3Series: {},
+      viewport3Quality: '',
+      viewport3FramesCount: 0,
+      viewport3CurrentFrame: 0,
+
+      viewport4Width: '100%',
+      viewport4Height: '100%',
+      viewport4Left: '0%',
+      viewport4Top: '0%',
+      viewport4Visible: false,
+      viewport4Series: {},
+      viewport4Quality: '',
+      viewport4FramesCount: 0,
+      viewport4CurrentFrame: 0,
+
+      series: [],
+      studies: [],
+      seriesIndex: {}  // Maps "SeriesInstanceUID" to "index in this.series"
+    }
+  },
+  computed: {
+    getSelectedStudies() {
+      var s = '';
+      for (var i = 0; i < this.studies.length; i++) {
+        if (this.studies[i].selected) {
+          if (s.length > 0)
+            s += ', ';
+          s += (this.studies[i].tags[STUDY_DESCRIPTION] + ' [' +
+                this.studies[i].tags[STUDY_DATE] + ']');
+        }
+      }
+      if (s.length == 0) 
+        return '...';
+      else
+        return s;
+    }
+  },
+  watch: { 
+    leftVisible: function(newVal, oldVal) {
+      this.FitContent();
+    },
+    showReferenceLines: function(newVal, oldVal) {
+      stone.ShowReferenceLines(newVal ? 1 : 0);
+    }
+  },
+  methods: {
+    FitContent() {
+      // This function can be used even if WebAssembly is not initialized yet
+      if (typeof stone._AllViewportsUpdateSize !== 'undefined') {
+        this.$nextTick(function () {
+          stone.AllViewportsUpdateSize(true /* fit content */);
+        });
+      }
+    },
+    
+    GetActiveSeries() {
+      var s = [];
+
+      if ('tags' in this.viewport1Series)
+        s.push(this.viewport1Series.tags[SERIES_INSTANCE_UID]);
+
+      if ('tags' in this.viewport2Series)
+        s.push(this.viewport2Series.tags[SERIES_INSTANCE_UID]);
+
+      if ('tags' in this.viewport3Series)
+        s.push(this.viewport3Series.tags[SERIES_INSTANCE_UID]);
+
+      if ('tags' in this.viewport4Series)
+        s.push(this.viewport4Series.tags[SERIES_INSTANCE_UID]);
+
+      return s;
+    },
+
+    GetActiveCanvas() {
+      if (this.activeViewport == 1) {
+        return 'canvas1';
+      }
+      else if (this.activeViewport == 2) {
+        return 'canvas2';
+      }
+      else if (this.activeViewport == 3) {
+        return 'canvas3';
+      }
+      else if (this.activeViewport == 4) {
+        return 'canvas4';
+      }
+      else {
+        return 'canvas1';
+      }
+    },
+
+    SetResources: function(sourceStudies, sourceSeries) {     
+      var indexStudies = {};
+
+      var studies = [];
+      var posColor = 0;
+
+      for (var i = 0; i < sourceStudies.length; i++) {
+        var studyInstanceUid = sourceStudies[i][STUDY_INSTANCE_UID];
+        if (studyInstanceUid !== undefined) {
+          if (studyInstanceUid in indexStudies) {
+            console.error('Twice the same study: ' + studyInstanceUid);
+          } else {
+            indexStudies[studyInstanceUid] = studies.length;
+            
+            studies.push({
+              'studyInstanceUid' : studyInstanceUid,
+              'series' : [ ],
+              'color' : COLORS[posColor],
+              'selected' : true,
+              'tags' : sourceStudies[i]
+            });
+
+            posColor = (posColor + 1) % COLORS.length;
+          }
+        }
+      }
+
+      var series = [];
+      var seriesIndex = {};
+
+      for (var i = 0; i < sourceSeries.length; i++) {
+        var studyInstanceUid = sourceSeries[i][STUDY_INSTANCE_UID];
+        var seriesInstanceUid = sourceSeries[i][SERIES_INSTANCE_UID];
+        if (studyInstanceUid !== undefined &&
+            seriesInstanceUid !== undefined) {
+          if (studyInstanceUid in indexStudies) {
+            seriesIndex[seriesInstanceUid] = series.length;
+            var study = studies[indexStudies[studyInstanceUid]];
+            study.series.push(i);
+            series.push({
+              //'length' : 4,
+              'complete' : false,
+              'type' : stone.ThumbnailType.LOADING,
+              'color': study.color,
+              'tags': sourceSeries[i]
+            });
+          }
+        }
+      }
+      
+      this.studies = studies;
+      this.series = series;
+      this.seriesIndex = seriesIndex;
+      this.ready = true;
+    },
+    
+    SeriesDragStart: function(event, seriesIndex) {
+      event.dataTransfer.setData('seriesIndex', seriesIndex);
+    },
+    
+    SetViewportSeries: function(viewportIndex, seriesIndex) {
+      var series = this.series[seriesIndex];
+      
+      if (viewportIndex == 1) {
+        this.viewport1Series = series;
+      }
+      else if (viewportIndex == 2) {
+        this.viewport2Series = series;
+      }
+      else if (viewportIndex == 3) {
+        this.viewport3Series = series;
+      }
+      else if (viewportIndex == 4) {
+        this.viewport4Series = series;
+      }
+    },
+    
+    ClickSeries: function(seriesIndex) {
+      this.SetViewportSeries(this.activeViewport, seriesIndex);
+    },
+    
+    HideViewport: function(index) {
+      if (index == 1) {
+        this.viewport1Visible = false;
+      }
+      else if (index == 2) {
+        this.viewport2Visible = false;
+      }
+      else if (index == 3) {
+        this.viewport3Visible = false;
+      }
+      else if (index == 4) {
+        this.viewport4Visible = false;
+      }
+    },
+    
+    ShowViewport: function(index, left, top, width, height) {
+      if (index == 1) {
+        this.viewport1Visible = true;
+        this.viewport1Left = left;
+        this.viewport1Top = top;
+        this.viewport1Width = width;
+        this.viewport1Height = height;
+      }
+      else if (index == 2) {
+        this.viewport2Visible = true;
+        this.viewport2Left = left;
+        this.viewport2Top = top;
+        this.viewport2Width = width;
+        this.viewport2Height = height;
+      }
+      else if (index == 3) {
+        this.viewport3Visible = true;
+        this.viewport3Left = left;
+        this.viewport3Top = top;
+        this.viewport3Width = width;
+        this.viewport3Height = height;
+      }
+      else if (index == 4) {
+        this.viewport4Visible = true;
+        this.viewport4Left = left;
+        this.viewport4Top = top;
+        this.viewport4Width = width;
+        this.viewport4Height = height;
+      }
+    },
+    
+    SetViewportLayout: function(layout) {
+      this.viewportLayoutButtonsVisible = false;
+      if (layout == '1x1') {
+        this.ShowViewport(1, '0%', '0%', '100%', '100%');
+        this.HideViewport(2);
+        this.HideViewport(3);
+        this.HideViewport(4);
+      }
+      else if (layout == '2x2') {
+        this.ShowViewport(1, '0%', '0%', '50%', '50%');
+        this.ShowViewport(2, '50%', '0%', '50%', '50%');
+        this.ShowViewport(3, '0%', '50%', '50%', '50%');
+        this.ShowViewport(4, '50%', '50%', '50%', '50%');
+      }
+      else if (layout == '2x1') {
+        this.ShowViewport(1, '0%', '0%', '50%', '100%');
+        this.ShowViewport(2, '50%', '0%', '50%', '100%');
+        this.HideViewport(3);
+        this.HideViewport(4);
+      }
+      else if (layout == '1x2') {
+        this.ShowViewport(1, '0%', '0%', '100%', '50%');
+        this.ShowViewport(2, '0%', '50%', '100%', '50%');
+        this.HideViewport(3);
+        this.HideViewport(4);
+      }
+
+      this.FitContent();
+    },
+
+    UpdateSeriesThumbnail: function(seriesInstanceUid) {
+      if (seriesInstanceUid in this.seriesIndex) {
+        var index = this.seriesIndex[seriesInstanceUid];
+        var series = this.series[index];
+
+        var type = stone.LoadSeriesThumbnail(seriesInstanceUid);
+        series.type = type;
+
+        if (type == stone.ThumbnailType.IMAGE) {
+          series.thumbnail = stone.GetStringBuffer();
+        }
+
+        // https://fr.vuejs.org/2016/02/06/common-gotchas/#Why-isn%E2%80%99t-the-DOM-updating
+        this.$set(this.series, index, series);
+      }
+    },
+
+    UpdateIsSeriesComplete: function(seriesInstanceUid) {
+      if (seriesInstanceUid in this.seriesIndex) {
+        var index = this.seriesIndex[seriesInstanceUid];
+        var series = this.series[index];
+
+        series.complete = stone.IsSeriesComplete(seriesInstanceUid);
+
+        // https://fr.vuejs.org/2016/02/06/common-gotchas/#Why-isn%E2%80%99t-the-DOM-updating
+        this.$set(this.series, index, series);
+
+        if ('tags' in this.viewport1Series &&
+            this.viewport1Series.tags[SERIES_INSTANCE_UID] == seriesInstanceUid) {
+          this.$set(this.viewport1Series, series);
+        }
+
+        if ('tags' in this.viewport2Series &&
+            this.viewport2Series.tags[SERIES_INSTANCE_UID] == seriesInstanceUid) {
+          this.$set(this.viewport2Series, series);
+        }
+
+        if ('tags' in this.viewport3Series &&
+            this.viewport3Series.tags[SERIES_INSTANCE_UID] == seriesInstanceUid) {
+          this.$set(this.viewport3Series, series);
+        }
+
+        if ('tags' in this.viewport4Series &&
+            this.viewport4Series.tags[SERIES_INSTANCE_UID] == seriesInstanceUid) {
+          this.$set(this.viewport4Series, series);
+        }
+      }
+    },
+
+    SetWindowing(center, width) {
+      var canvas = this.GetActiveCanvas();
+      if (canvas != '') {
+        stone.SetWindowing(canvas, center, width);
+      }
+    },
+
+    SetDefaultWindowing() {
+      var canvas = this.GetActiveCanvas();
+      if (canvas != '') {
+        stone.SetDefaultWindowing(canvas);
+      }
+    },
+
+    InvertContrast() {
+      var canvas = this.GetActiveCanvas();
+      if (canvas != '') {
+        stone.InvertContrast(canvas);
+      }
+    }
+  },
+  
+  mounted: function() {
+    this.SetViewportLayout('1x1');
+  }
+});
+
+
+
+window.addEventListener('StoneInitialized', function() {
+  stone.Setup(Module);
+  stone.SetOrthancRoot('..', true);
+  console.warn('Native Stone properly intialized');
+
+  var study = getParameterFromUrl('study');
+  var series = getParameterFromUrl('series');
+
+  if (study === undefined) {
+    alert('No study was provided in the URL!');
+  } else {
+    if (series === undefined) {
+      console.warn('Loading study: ' + study);
+      stone.FetchStudy(study);
+    } else {
+      console.warn('Loading series: ' + series + ' from study ' + study);
+      stone.FetchSeries(study, series);
+      app.leftMode = 'full';
+    }
+  }
+});
+
+
+window.addEventListener('ResourcesLoaded', function() {
+  console.log('resources loaded');
+
+  var studies = [];
+  for (var i = 0; i < stone.GetStudiesCount(); i++) {
+    stone.LoadStudyTags(i);
+    studies.push(JSON.parse(stone.GetStringBuffer()));
+  }
+
+  var series = [];
+  for (var i = 0; i < stone.GetSeriesCount(); i++) {
+    stone.LoadSeriesTags(i);
+    series.push(JSON.parse(stone.GetStringBuffer()));
+  }
+
+  app.SetResources(studies, series);
+
+  for (var i = 0; i < app.series.length; i++) {
+    var seriesInstanceUid = app.series[i].tags[SERIES_INSTANCE_UID];
+    app.UpdateSeriesThumbnail(seriesInstanceUid);
+    app.UpdateIsSeriesComplete(seriesInstanceUid);
+  }
+});
+
+
+window.addEventListener('ThumbnailLoaded', function(args) {
+  //var studyInstanceUid = args.detail.studyInstanceUid;
+  var seriesInstanceUid = args.detail.seriesInstanceUid;
+  app.UpdateSeriesThumbnail(seriesInstanceUid);
+});
+
+
+window.addEventListener('MetadataLoaded', function(args) {
+  //var studyInstanceUid = args.detail.studyInstanceUid;
+  var seriesInstanceUid = args.detail.seriesInstanceUid;
+  app.UpdateIsSeriesComplete(seriesInstanceUid);
+});
+
+
+window.addEventListener('FrameUpdated', function(args) {
+  var canvasId = args.detail.canvasId;
+  var framesCount = args.detail.framesCount;
+  var currentFrame = (args.detail.currentFrame + 1);
+  var quality = args.detail.quality;
+  
+  if (canvasId == 'canvas1') {
+    app.viewport1CurrentFrame = currentFrame;
+    app.viewport1FramesCount = framesCount;
+    app.viewport1Quality = quality;
+  }
+  else if (canvasId == 'canvas2') {
+    app.viewport2CurrentFrame = currentFrame;
+    app.viewport2FramesCount = framesCount;
+    app.viewport2Quality = quality;
+  }
+  else if (canvasId == 'canvas3') {
+    app.viewport3CurrentFrame = currentFrame;
+    app.viewport3FramesCount = framesCount;
+    app.viewport3Quality = quality;
+  }
+  else if (canvasId == 'canvas4') {
+    app.viewport4CurrentFrame = currentFrame;
+    app.viewport4FramesCount = framesCount;
+    app.viewport4Quality = quality;
+  }
+});
+
+
+window.addEventListener('StoneException', function() {
+  console.error('Exception catched in Stone');
+});
+
+
+
+
+
+
+$(document).ready(function() {
+  // Enable support for tooltips in Bootstrap
+  //$('[data-toggle="tooltip"]').tooltip();
+
+  //app.showWarning = true;
+
+
+  $('#windowing-popover').popover({
+    container: 'body',
+    content: $('#windowing-content').html(),
+    template: '<div class="popover wvToolbar__windowingPresetConfigPopover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>',
+    placement: 'auto',
+    html: true,
+    sanitize: false,
+    trigger: 'focus'   // Close on click
+  });
+  
+  
+  var wasmSource = 'StoneWebViewer.js';
+  
+  // Option 1: Loading script using plain HTML
+  
+  /*
+    var script = document.createElement('script');
+    script.src = wasmSource;
+    script.type = 'text/javascript';
+    document.body.appendChild(script);
+  */
+
+  // Option 2: Loading script using AJAX (gives the opportunity to
+  // report explicit errors)
+
+  axios.get(wasmSource)
+    .then(function (response) {
+      var script = document.createElement('script');
+      script.innerHTML = response.data;
+      script.type = 'text/javascript';
+      document.body.appendChild(script);
+    })
+    .catch(function (error) {
+      alert('Cannot load the WebAssembly framework');
+    });
+});
+
+
+// "Prevent Bootstrap dropdown from closing on clicks" for the list of
+// studies: https://stackoverflow.com/questions/26639346
+$('.dropdown-menu').click(function(e) {
+  e.stopPropagation();
+});
+
+
+// Disable the selection of text using the mouse
+document.onselectstart = new Function ('return false');
Binary file StoneWebViewer/WebApplication/img/grid1x1.png has changed
Binary file StoneWebViewer/WebApplication/img/grid1x2.png has changed
Binary file StoneWebViewer/WebApplication/img/grid2x1.png has changed
Binary file StoneWebViewer/WebApplication/img/grid2x2.png has changed
Binary file StoneWebViewer/WebApplication/img/loading.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/WebApplication/index.html	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,488 @@
+<!doctype html>
+<html class="wv-html">
+  <head>
+    <title>Stone Web Viewer</title>
+    <meta charset="utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
+    <meta name="apple-mobile-web-app-capable" content="yes" />
+    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
+    <link rel="icon" href="data:;base64,iVBORw0KGgo=">
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.css">
+    <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
+    <link rel="stylesheet" href="app.css">
+
+    <!-- https://stackoverflow.com/a/16863182/881731 -->
+    <style>
+      .tooltip {
+      position: fixed;
+      }
+    </style>
+
+    <!-- Fix if Bootstrap CSS is not used -->
+    <!--style>
+        *,
+        *::before,
+        *::after {
+        box-sizing: border-box;
+        }
+        </style-->
+  </head>
+  <body class="wv-body">
+    <div id="wv">
+      <div class="wvLoadingScreen" v-show="!ready">
+        <span class="wvLoadingSpinner">
+          <div class="bounce1"></div>
+          <div class="bounce2"></div>
+          <div class="bounce3"></div>
+        </span>
+      </div>
+
+      <div class="fluid-height fluid-width" v-show="ready">
+
+        <div class="wvWarning wvPrintExclude" v-show="showWarning">
+          <div class="wvWarning-content clearfix">
+            <span class="wvWarning-text">
+              <h2 class="mb10"><i class="fa fa-exclamation-triangle wvWarning-icon mr5"></i>Warning!</h2>
+              <p class="mn mb10" style="color:#000">
+                You browser is not supported. You might expect
+                inconsistent behaviours and must not use the viewer to
+                produce a diagnostic.
+              </p>
+            </span> 
+          </div>
+          <div class="text-right mb10 mr10">
+            <button class="btn btn-primary" @click="showWarning=false">OK</button>
+          </div>
+        </div>
+
+
+        <div class="wvLayoutLeft wvLayoutLeft--closed" v-show="!leftVisible">
+          <div class="wvLayoutLeft__actions--outside" style="z-index:10">
+            <button class="wvLayoutLeft__action button__base wh__25 lh__25 text-center"
+                    @click="leftVisible = true">
+              <i class="fa fa-angle-double-right"></i>
+            </button>
+          </div>
+        </div>
+
+
+        <div class="wvLayoutLeft" v-show="leftVisible"
+             v-bind:class="{ 'wvLayoutLeft--small': leftMode == 'small' }" 
+             >
+          <div class="wvLayoutLeft__actions" style="z-index:10">
+            <button class="wvLayoutLeft__action button__base wh__25 lh__25 text-center"
+                    @click="leftVisible = false">
+              <i class="fa fa-angle-double-left"></i>
+            </button>
+          </div>
+          <div class="wvLayoutLeft__content">
+            <div class="wvLayoutLeft__contentTop">
+              <div class="float__left dropdown" style="max-width: calc(100% - 4.5rem); height:4.5rem !important" v-show="leftMode != 'small'">
+                <button type="button" class="wvButton--border" data-toggle="dropdown">
+                  {{ getSelectedStudies }}
+                  <span class="caret"></span>
+                </button>
+                <ul class="dropdown-menu checkbox-menu allow-focus">
+                  <li v-for="study in studies"
+                      v-bind:class="{ active: study.selected }" 
+                      @click="study.selected = !study.selected">
+                    <a>
+                      {{ study.tags['0008,1030'] }}
+                      <span v-if="study.selected">&nbsp;<i class="fa fa-check"></i></span>
+                    </a> 
+                  </li>
+                </ul>
+              </div>
+
+              <div class="float__right wvButton" v-if="leftMode == 'grid'" @click="leftMode = 'full'">
+                <i class="fa fa-th-list"></i>
+              </div>
+              <div class="float__right wvButton" v-if="leftMode == 'full'" @click="leftMode = 'small'">
+                <i class="fa fa-ellipsis-v"></i>
+              </div>
+              <div class="float__right wvButton" v-if="leftMode == 'small'" @click="leftMode = 'grid'">
+                <i class="fa fa-th"></i>
+              </div>
+
+              <p class="clear disclaimer mbn">For patients, teachers and researchers.</p>
+            </div>        
+            <div class="wvLayoutLeft__contentMiddle">
+
+              <div v-for="study in studies">
+                <div v-if="study.selected">
+                  <div v-bind:class="'wvStudyIsland--' + study.color">
+                    <div v-bind:class="'wvStudyIsland__header--' + study.color">
+                      <!-- Actions -->
+                      <div class="wvStudyIsland__actions"
+                           v-bind:class="{ 'wvStudyIsland__actions--oneCol': leftMode == 'small' }">
+                        <a class="wvButton">
+                          <!-- download --> 
+                          <i class="fa fa-download"></i>
+                        </a>
+                      </div>
+                      
+                      <!-- Title -->
+                      {{ study.tags['0008,1030'] }}
+                      <br/>
+                      <small>{{ study.tags['0008,0020'] }}</small>
+                    </div>
+
+                    <div class="wvStudyIsland__main">
+                      <ul class="wvSerieslist">
+                        <li class="wvSerieslist__seriesItem"
+                            v-bind:class="{ highlighted : GetActiveSeries().includes(series[seriesIndex].tags['0020,000e']), 'wvSerieslist__seriesItem--list' : leftMode != 'grid', 'wvSerieslist__seriesItem--grid' : leftMode == 'grid' }"
+                            v-on:dragstart="SeriesDragStart($event, seriesIndex)"
+                            v-on:click="ClickSeries(seriesIndex)"
+                            v-for="seriesIndex in study.series">
+                          <div class="wvSerieslist__picture" style="z-index:0"
+                               draggable="true"
+                               v-if="series[seriesIndex].type != stone.ThumbnailType.UNKNOWN"
+                               >
+                            <div v-if="series[seriesIndex].type == stone.ThumbnailType.LOADING">
+                              <img src="img/loading.gif"
+                                   style="vertical-align:baseline"
+                                   width="65px" height="65px"
+                                   />
+                            </div>
+
+                            <i v-if="series[seriesIndex].type == stone.ThumbnailType.PDF"
+                               class="wvSerieslist__placeholderIcon fa fa-file-text"></i>
+
+                            <i v-if="series[seriesIndex].type == stone.ThumbnailType.VIDEO"
+                               class="wvSerieslist__placeholderIcon fa fa-video-camera"></i>
+
+                            
+                            <div v-if="[stone.ThumbnailType.IMAGE, stone.ThumbnailType.NO_PREVIEW].includes(series[seriesIndex].type)"
+                                 class="wvSerieslist__placeholderIcon"
+                                 v-bind:title="leftMode == 'full' ? null : '[' + series[seriesIndex].tags['0008,0060'] + '] ' + series[seriesIndex].tags['0008,103e']">
+                              <i v-if="series[seriesIndex].type == stone.ThumbnailType.NO_PREVIEW"
+                                 class="fa fa-eye-slash"></i>
+
+                              <img v-if="series[seriesIndex].type == stone.ThumbnailType.IMAGE"
+                                   v-bind:src="series[seriesIndex].thumbnail"
+                                   style="vertical-align:baseline"
+                                   width="65px" height="65px"
+                                   v-bind:title="leftMode == 'full' ? null : '[' + series[seriesIndex].tags['0008,0060'] + '] ' + series[seriesIndex].tags['0008,103e']"
+                                   />
+                              
+                              <div v-bind:class="'wvSerieslist__badge--' + study.color"
+                                   v-if="'length' in series[seriesIndex]">{{ series[seriesIndex].length }}</div>
+                            </div>
+                          </div>
+
+                          <div v-if="leftMode == 'full'" class="wvSerieslist__information"
+                               draggable="true"
+                               v-on:dragstart="SeriesDragStart($event, seriesIndex)"
+                               v-on:click="ClickSeries(seriesIndex)">
+                            <p class="wvSerieslist__label">
+                              [{{ series[seriesIndex].tags['0008,0060'] }}]
+                              {{ series[seriesIndex].tags['0008,103e'] }}
+                            </p>
+                          </div>
+                        </li>
+                      </ul>
+                    </div>
+                  </div>
+                </div>
+              </div>
+
+            </div>        
+            <div class="wvLayoutLeft__contentBottom">
+            </div>        
+          </div>
+        </div>
+        <div class="wvLayout__main"
+             v-bind:class="{ 'wvLayout__main--smallleftpadding': leftVisible && leftMode == 'small', 'wvLayout__main--leftpadding': leftVisible && leftMode != 'small' }" 
+             >
+
+          <div class="wvToolbar wvToolbar--top">
+            <div class="ng-scope inline-object">
+              <div class="tbGroup">
+                <div class="tbGroup__toggl">
+                  <button class="wvButton"
+                          v-bind:class="{ 'wvButton--underline' : !viewportLayoutButtonsVisible }"
+                          @click="viewportLayoutButtonsVisible = !viewportLayoutButtonsVisible">
+                    <i class="fa fa-th"></i>
+                  </button>
+                </div>
+                
+                <div class="tbGroup__buttons--bottom" v-show="viewportLayoutButtonsVisible">
+                  <div class="inline-object">
+                    <button class="wvButton" @click="SetViewportLayout('1x1')">
+                      <img src="img/grid1x1.png" style="width:1em;height:1em" />
+                    </button>
+                  </div>
+                  <div class="inline-object">
+                    <button class="wvButton" @click="SetViewportLayout('2x1')">
+                      <img src="img/grid2x1.png" style="width:1em;height:1em" />
+                    </button>
+                  </div>
+                  <div class="inline-object">
+                    <button class="wvButton" @click="SetViewportLayout('1x2')">
+                      <img src="img/grid1x2.png" style="width:1em;height:1em" />
+                    </button>
+                  </div>
+                  <div class="inline-object">
+                    <button class="wvButton" @click="SetViewportLayout('2x2')">
+                      <img src="img/grid2x2.png" style="width:1em;height:1em" />
+                    </button>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!--div class="ng-scope inline-object">
+              <button class="wvButton--underline text-center active">
+                <i class="fa fa-hand-pointer-o"></i>
+              </button>
+            </div>
+
+            <div class="ng-scope inline-object">
+              <button class="wvButton--underline text-center">
+                <i class="fa fa-search"></i>
+              </button>
+            </div>
+
+            <div class="ng-scope inline-object">
+              <button class="wvButton--underline text-center">
+                <i class="fa fa-arrows"></i>
+              </button>
+            </div-->
+
+            <div class="ng-scope inline-object">
+              <button class="wvButton--underline text-center"
+                      v-on:click="InvertContrast()">
+                <i class="fa fa-adjust"></i>
+              </button>
+            </div>
+            
+            <div class="ng-scope inline-object">
+              <button class="wvButton--underline text-center" id="windowing-popover">
+                <i class="fa fa-sun-o"></i>
+              </button>
+            </div>
+
+            <div class="ng-scope inline-object">
+              <button class="wvButton--underline text-center"
+                      v-bind:class="{ 'active' : showInfo }"
+                      v-on:click="showInfo = !showInfo">
+                <i class="fa fa-info-circle"></i>
+              </button>
+            </div>
+
+            <div class="ng-scope inline-object">
+              <button class="wvButton--underline text-center"
+                      v-bind:class="{ 'active' : showReferenceLines }"
+                      v-on:click="showReferenceLines = !showReferenceLines">
+                <i class="fa fa-bars"></i>
+              </button>
+            </div>
+          </div>
+
+          
+          <div class="wvLayout__splitpane--toolbarAtTop">
+            <div id="viewport" class="wvSplitpane">
+              <viewport v-on:updated-series="SetViewportSeries(1, $event)"
+                        v-on:selected-viewport="activeViewport=1"
+                        v-show="viewport1Visible"
+                        canvas-id="canvas1"
+                        v-bind:series="viewport1Series"
+                        v-bind:left="viewport1Left"
+                        v-bind:top="viewport1Top"
+                        v-bind:width="viewport1Width"
+                        v-bind:height="viewport1Height"
+                        v-bind:quality="viewport1Quality"
+                        v-bind:current-frame="viewport1CurrentFrame"
+                        v-bind:frames-count="viewport1FramesCount"
+                        v-bind:show-info="showInfo"
+                        v-bind:active="activeViewport==1"></viewport>
+              <viewport v-on:updated-series="SetViewportSeries(2, $event)"
+                        v-on:selected-viewport="activeViewport=2"
+                        v-show="viewport2Visible"
+                        canvas-id="canvas2"
+                        v-bind:series="viewport2Series"
+                        v-bind:left="viewport2Left"
+                        v-bind:top="viewport2Top"
+                        v-bind:width="viewport2Width"
+                        v-bind:height="viewport2Height"
+                        v-bind:quality="viewport2Quality"
+                        v-bind:current-frame="viewport2CurrentFrame"
+                        v-bind:frames-count="viewport2FramesCount"
+                        v-bind:show-info="showInfo"
+                        v-bind:active="activeViewport==2"></viewport>
+              <viewport v-on:updated-series="SetViewportSeries(3, $event)"
+                        v-on:selected-viewport="activeViewport=3"
+                        v-show="viewport3Visible"
+                        canvas-id="canvas3"
+                        v-bind:series="viewport3Series"
+                        v-bind:left="viewport3Left"
+                        v-bind:top="viewport3Top"
+                        v-bind:width="viewport3Width"
+                        v-bind:height="viewport3Height"
+                        v-bind:quality="viewport3Quality"
+                        v-bind:current-frame="viewport3CurrentFrame"
+                        v-bind:frames-count="viewport3FramesCount"
+                        v-bind:show-info="showInfo"
+                        v-bind:active="activeViewport==3"></viewport>
+              <viewport v-on:updated-series="SetViewportSeries(4, $event)"
+                        v-on:selected-viewport="activeViewport=4"
+                        v-show="viewport4Visible"
+                        canvas-id="canvas4"
+                        v-bind:series="viewport4Series"
+                        v-bind:left="viewport4Left"
+                        v-bind:top="viewport4Top"
+                        v-bind:width="viewport4Width"
+                        v-bind:height="viewport4Height"
+                        v-bind:quality="viewport4Quality"
+                        v-bind:current-frame="viewport4CurrentFrame"
+                        v-bind:frames-count="viewport4FramesCount"
+                        v-bind:show-info="showInfo"
+                        v-bind:active="activeViewport==4"></viewport>
+            </div>
+          </div>
+
+        </div>
+      </div>
+    </div>
+
+
+
+    <script type="text/x-template" id="windowing-content">
+      <p class="wvToolbar__windowingPresetConfigNotice">
+        Click on the button to toggle the windowing tool or apply a preset to the selected viewport.
+      </p>
+
+      <ul class="wvToolbar__windowingPresetList">
+        <li class="wvToolbar__windowingPresetListItem">
+          <a href="#" onclick="app.SetDefaultWindowing()">
+            Default
+          </a>
+        </li>
+        <li class="wvToolbar__windowingPresetListItem">
+          <a href="#" onclick="app.SetWindowing(-400, 1600)">
+            CT Lung <small>(L -400, W 1,600)</small>
+          </a>
+        </li>
+        <li class="wvToolbar__windowingPresetListItem">
+          <a href="#" onclick="app.SetWindowing(300, 1500)">
+            CT Abdomen <small>(L 300, W 1,500)</small>
+          </a>
+        </li>
+        <li class="wvToolbar__windowingPresetListItem">
+          <a href="#" onclick="app.SetWindowing(40, 80)">
+            CT Bone <small>(L 40, W 80)</small>
+          </a>
+        </li>
+        <li class="wvToolbar__windowingPresetListItem">
+          <a href="#" onclick="app.SetWindowing(40, 400)">
+            CT Brain <small>(L 40, W 400)</small>
+          </a>
+        </li>
+        <li class="wvToolbar__windowingPresetListItem">
+          <a href="#" onclick="app.SetWindowing(-400, 1600)">
+            CT Chest <small>(L -400, W 1,600)</small>
+          </a>
+        </li>
+        <li class="wvToolbar__windowingPresetListItem">
+          <a href="#" onclick="app.SetWindowing(300, 600)">
+            CT Angio <small>(L 300, W 600)</small>
+          </a>
+        </li>
+      </ul>
+    </script>
+    
+
+    <script type="text/x-template" id="viewport-template">
+      <div v-bind:style="{ padding:'2px', 
+                         position:'absolute', 
+                         left: left, 
+                         top: top,
+                         width: width, 
+                         height: height }">
+        <div v-bind:class="{ 'wvSplitpane__cellBorder--selected' : active, 
+                           'wvSplitpane__cellBorder' : series.color == '', 
+                           'wvSplitpane__cellBorder--blue' : series.color == 'blue', 
+                           'wvSplitpane__cellBorder--red' : series.color == 'red',
+                           'wvSplitpane__cellBorder--green' : series.color == 'green', 
+                           'wvSplitpane__cellBorder--yellow' : series.color == 'yellow', 
+                           'wvSplitpane__cellBorder--violet' : series.color == 'violet'
+                           }" 
+             v-on:dragover="SeriesDragAccept($event)"
+             v-on:drop="SeriesDragDrop($event)"
+             style="width:100%;height:100%">
+          <div class="wvSplitpane__cell"
+               v-on:click="MakeActive()">
+            <div v-show="status == 'ready'"
+                 style="position:absolute; left:0; top:0; width:100%; height:100%">
+              <!--div style="width: 100%; height: 100%; background-color: red"></div-->
+              <canvas v-bind:id="canvasId"
+                      style="position:absolute; left:0; top:0; width:100%; height:100%"
+                      oncontextmenu="return false"></canvas>
+
+              <div v-if="'tags' in series" v-show="showInfo">
+                <div class="wv-overlay">
+                  <div class="wv-overlay-topleft">
+                    {{ series.tags['0010,0010'] }}<br/>
+                    {{ series.tags['0010,0020'] }}
+                  </div>
+                  <div class="wv-overlay-topright">
+                    {{ series.tags['0008,1030'] }}<br/>
+                    {{ series.tags['0008,0020'] }}<br/>
+                    {{ series.tags['0020,0011'] }} | {{ series.tags['0008,103e'] }}
+                  </div>
+                  <div class="wv-overlay-bottomleft"
+                       v-show="framesCount != 0">
+                    <button class="btn btn-primary" @click="DecrementFrame()">
+                      <i class="fa fa-chevron-circle-left"></i>
+                    </button>
+                    &nbsp;&nbsp;{{ currentFrame }} / {{ framesCount }}&nbsp;&nbsp;
+                    <button class="btn btn-primary" @click="IncrementFrame()">
+                      <i class="fa fa-chevron-circle-right"></i>
+                    </button>
+                  </div>
+                  <div class="wv-overlay-bottomright">
+                    <div v-show="quality == stone.DisplayedFrameQuality.NONE"
+                         style="display:block;background-color:red;width:1em;height:1em" />
+                    <div v-show="quality == stone.DisplayedFrameQuality.LOW"
+                         style="display:block;background-color:orange;width:1em;height:1em" />
+                    <div v-show="quality == stone.DisplayedFrameQuality.HIGH"
+                         style="display:block;background-color:green;width:1em;height:1em" />
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <div v-if="status == 'waiting'" class="wvPaneOverlay">
+              [ drop a series here ]
+            </div>
+                
+            <!--div v-if="status == 'video'" class="wvPaneOverlay">
+              <video class="wvVideo" autoplay="" loop="" controls="" preload="auto" type="video/mp4"
+                     src="http://viewer-pro.osimis.io/instances/e465dd27-83c96343-96848735-7035a133-1facf1a0/frames/0/raw">
+              </video>
+            </div-->
+                
+            <div v-if="status == 'loading'" class="wvPaneOverlay">
+              <span class="wvLoadingSpinner">
+                <div class="bounce1"></div>
+                <div class="bounce2"></div>
+                <div class="bounce3"></div>
+              </span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </script>
+
+
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.js"></script>
+    
+    <script src="stone.js"></script>
+    <script src="app.js"></script>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/WebAssembly/CMakeLists.txt	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,125 @@
+cmake_minimum_required(VERSION 2.8.3)
+
+project(OrthancStone)
+
+# Configuration of the Emscripten compiler for WebAssembly target
+# ---------------------------------------------------------------
+set(USE_WASM ON CACHE BOOL "")
+set(ORTHANC_FRAMEWORK_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../orthanc CACHE PATH "")
+set(STONE_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../)
+
+set(EMSCRIPTEN_SET_LLVM_WASM_BACKEND ON CACHE BOOL "")
+
+set(WASM_FLAGS "-s WASM=1 -s FETCH=1")
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+  set(WASM_FLAGS "${WASM_FLAGS} -s SAFE_HEAP=1")
+endif()
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
+
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=268435456")  # 256MB + resize
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1")
+add_definitions(
+  -DDISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1
+)
+
+# Stone of Orthanc configuration
+# ---------------------------------------------------------------
+set(ALLOW_DOWNLOADS ON)
+set(ORTHANC_FRAMEWORK_SOURCE "path")
+
+include(${STONE_ROOT}/Resources/CMake/OrthancStoneParameters.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/AutoGeneratedCode.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
+
+SET(ENABLE_DCMTK ON)
+SET(ENABLE_DCMTK_NETWORKING OFF)
+SET(ENABLE_DCMTK_TRANSCODING OFF)
+SET(ENABLE_GOOGLE_TEST OFF)
+SET(ENABLE_LOCALE ON)  # Necessary for text rendering
+SET(ENABLE_WASM ON)
+SET(ORTHANC_SANDBOXED ON)
+
+# this will set up the build system for Stone of Orthanc and will
+# populate the ORTHANC_STONE_SOURCES CMake variable
+include(${STONE_ROOT}/Resources/CMake/OrthancStoneConfiguration.cmake)
+
+
+################################################################################
+
+project(StoneWebViewer)
+
+
+# Create the wrapper to call C++ from JavaScript
+# ---------------------------------------------------------------
+
+set(LIBCLANG "libclang-4.0.so.1" CACHE PATH "Version of clang to generate the code model")
+set(STONE_WRAPPER ${CMAKE_CURRENT_BINARY_DIR}/stone.js)
+
+add_custom_command(
+  COMMAND
+  ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/ParseWebAssemblyExports.py --libclang=${LIBCLANG} ${CMAKE_SOURCE_DIR}/Test.cpp > ${STONE_WRAPPER}
+  DEPENDS
+  ${CMAKE_SOURCE_DIR}/Test.cpp
+  ${CMAKE_SOURCE_DIR}/ParseWebAssemblyExports.py
+  OUTPUT
+  ${STONE_WRAPPER}
+  )
+
+add_custom_target(StoneWrapper
+  DEPENDS
+  ${STONE_WRAPPER}
+  )  
+
+
+# Define the WASM module
+# ---------------------------------------------------------------
+
+add_executable(StoneWebViewer
+  ${ORTHANC_STONE_SOURCES}
+  ${AUTOGENERATED_SOURCES}
+  Test.cpp
+  )
+
+# Make sure to have the wrapper generated
+add_dependencies(StoneWebViewer StoneWrapper)
+
+
+# Declare installation files for the module
+# ---------------------------------------------------------------
+
+install(
+  TARGETS StoneWebViewer
+  RUNTIME DESTINATION .
+  )
+
+
+# Declare installation files for the companion files (web scaffolding)
+# please note that ${CMAKE_CURRENT_BINARY_DIR}/StoneWebViewer.js
+# (the generated JS loader for the WASM module) is handled by the `install1`
+# section above: it is considered to be the binary output of 
+# the linker.
+# ---------------------------------------------------------------
+install(
+  FILES
+  ${CMAKE_CURRENT_BINARY_DIR}/StoneWebViewer.wasm
+  ${CMAKE_SOURCE_DIR}/../WebApplication/app.css
+  ${CMAKE_SOURCE_DIR}/../WebApplication/app.js
+  ${CMAKE_SOURCE_DIR}/../WebApplication/index.html
+  ${STONE_WRAPPER}
+  DESTINATION .
+  )
+
+install(
+  FILES
+  ${CMAKE_SOURCE_DIR}/../WebApplication/img/grid1x1.png
+  ${CMAKE_SOURCE_DIR}/../WebApplication/img/grid1x2.png
+  ${CMAKE_SOURCE_DIR}/../WebApplication/img/grid2x1.png
+  ${CMAKE_SOURCE_DIR}/../WebApplication/img/grid2x2.png
+  ${CMAKE_SOURCE_DIR}/../WebApplication/img/loading.gif
+  DESTINATION img
+  )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/WebAssembly/NOTES.txt	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,2 @@
+# source ~/Downloads/emsdk/emsdk_env.sh
+# cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -G Ninja
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/WebAssembly/ParseWebAssemblyExports.py	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+
+# Stone of Orthanc
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017-2020 Osimis S.A., Belgium
+#
+# 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/>.
+
+
+
+# Ubuntu 18.04:
+# sudo apt-get install python-clang-4.0
+# ./ParseWebAssemblyExports.py --libclang=libclang-4.0.so.1 ./Test.cpp
+
+
+# Ubuntu 14.04:
+# ./ParseWebAssemblyExports.py --libclang=libclang-3.6.so.1 ./Test.cpp
+
+
+import sys
+import clang.cindex
+import pystache
+import argparse
+
+
+
+##
+## Parse the command-line arguments
+##
+
+parser = argparse.ArgumentParser(description = 'Parse WebAssembly C++ source file, and create a basic JavaScript wrapper.')
+parser.add_argument('--libclang',
+                    default = '',
+                    help = 'manually provides the path to the libclang shared library')
+parser.add_argument('source', 
+                    help = 'Input C++ file')
+
+args = parser.parse_args()
+
+
+
+if len(args.libclang) != 0:
+    clang.cindex.Config.set_library_file(args.libclang)
+
+index = clang.cindex.Index.create()
+
+# PARSE_SKIP_FUNCTION_BODIES prevents clang from failing because of
+# undefined types, which prevents compilation of functions
+tu = index.parse(args.source,
+                 [ '-DEMSCRIPTEN_KEEPALIVE=__attribute__((annotate("WebAssembly")))' ],
+                 options = clang.cindex.TranslationUnit.PARSE_SKIP_FUNCTION_BODIES)
+
+
+
+TEMPLATE = '''
+const Stone = function() {
+  {{#enumerations}}
+  this.{{name}} = {
+    {{#values}}
+    {{name}}: {{value}}{{separator}}
+    {{/values}}
+  };
+
+  {{/enumerations}}
+  {{#functions}}
+  this._{{name}} = undefined;
+  {{/functions}}
+};
+
+Stone.prototype.Setup = function(Module) {
+  {{#functions}}
+  this._{{name}} = Module.cwrap('{{name}}', {{{returnType}}}, [ {{#args}}{{{type}}}{{^last}}, {{/last}}{{/args}} ]);
+  {{/functions}}
+};
+
+{{#functions}}
+Stone.prototype.{{name}} = function({{#args}}{{name}}{{^last}}, {{/last}}{{/args}}) {
+  {{#hasReturn}}return {{/hasReturn}}this._{{name}}({{#args}}{{name}}{{^last}}, {{/last}}{{/args}});
+};
+
+{{/functions}}
+var stone = new Stone();
+'''
+
+
+
+
+# WARNING: Undefined types are mapped as "int"
+
+functions = []
+enumerations = []
+
+def ToUpperCase(source):
+    target = source[0]
+    for c in source[1:]:
+        if c.isupper():
+            target += '_'
+        target += c.upper()
+    return target
+    
+
+
+def IsExported(node):
+    for child in node.get_children():
+        if (child.kind == clang.cindex.CursorKind.ANNOTATE_ATTR and
+            child.displayname == 'WebAssembly'):
+            return True
+
+    return False
+
+
+def Explore(node):
+    if node.kind == clang.cindex.CursorKind.ENUM_DECL:
+        if IsExported(node):
+            name = node.spelling
+            values = []
+            for value in node.get_children():
+                if (value.spelling.startswith(name + '_')):
+                    s = value.spelling[len(name) + 1:]
+                    if len(values) > 0:
+                        values[-1]['separator'] = ','
+                    values.append({
+                        'name' : ToUpperCase(s),
+                        'value' : value.enum_value
+                    })
+                    
+            enumerations.append({
+                'name' : name,
+                'values' : values
+                })
+    
+    if node.kind == clang.cindex.CursorKind.FUNCTION_DECL:
+        if IsExported(node):
+            f = {
+                'name' : node.spelling,
+                'args' : [],
+            }
+
+            returnType = node.result_type.spelling
+            if returnType == 'void':
+                f['hasReturn'] = False
+                f['returnType'] = "null"
+            elif returnType == 'const char *':
+                f['hasReturn'] = True
+                f['returnType'] = "'string'"
+            elif returnType in [ 'int', 'unsigned int' ]:
+                f['hasReturn'] = True
+                f['returnType'] = "'int'"
+            else:
+                raise Exception('Unknown return type in function "%s()": %s' % (node.spelling, returnType))
+
+            for child in node.get_children():
+                if child.kind == clang.cindex.CursorKind.PARM_DECL:
+                    arg = {
+                        'name' : child.displayname,
+                    }
+                    
+                    argType = child.type.spelling
+                    if argType == 'int':
+                        arg['type'] = "'int'"
+                    elif argType == 'const char *':
+                        arg['type'] = "'string'"
+                    else:
+                        raise Exception('Unknown type for argument "%s" in function "%s()": %s' %
+                                        (child.displayname, node.spelling, argType))
+
+                    f['args'].append(arg)
+
+            if len(f['args']) != 0:
+                f['args'][-1]['last'] = True
+                    
+            functions.append(f)
+
+    for child in node.get_children():
+        Explore(child)
+
+Explore(tu.cursor)
+
+
+
+print(pystache.render(TEMPLATE, {
+    'functions' : functions,
+    'enumerations' : enumerations
+}))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/WebAssembly/Test.cpp	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,2246 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., Belgium
+ *
+ * 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/>.
+ **/
+
+
+#include <emscripten.h>
+
+
+#define DISPATCH_JAVASCRIPT_EVENT(name)                         \
+  EM_ASM(                                                       \
+    const customEvent = document.createEvent("CustomEvent");    \
+    customEvent.initCustomEvent(name, false, false, undefined); \
+    window.dispatchEvent(customEvent);                          \
+    );
+
+
+#define EXTERN_CATCH_EXCEPTIONS                         \
+  catch (Orthanc::OrthancException& e)                  \
+  {                                                     \
+    LOG(ERROR) << "OrthancException: " << e.What();     \
+    DISPATCH_JAVASCRIPT_EVENT("StoneException");        \
+  }                                                     \
+  catch (OrthancStone::StoneException& e)               \
+  {                                                     \
+    LOG(ERROR) << "StoneException: " << e.What();       \
+    DISPATCH_JAVASCRIPT_EVENT("StoneException");        \
+  }                                                     \
+  catch (std::exception& e)                             \
+  {                                                     \
+    LOG(ERROR) << "Runtime error: " << e.what();        \
+    DISPATCH_JAVASCRIPT_EVENT("StoneException");        \
+  }                                                     \
+  catch (...)                                           \
+  {                                                     \
+    LOG(ERROR) << "Native exception";                   \
+    DISPATCH_JAVASCRIPT_EVENT("StoneException");        \
+  }
+
+
+#include <Cache/MemoryObjectCache.h>
+#include <DicomFormat/DicomArray.h>
+#include <DicomParsing/Internals/DicomImageDecoder.h>
+#include <Images/Image.h>
+#include <Images/ImageProcessing.h>
+#include <Images/JpegReader.h>
+#include <Logging.h>
+
+#include "../../Framework/Loaders/DicomResourcesLoader.h"
+#include "../../Framework/Loaders/SeriesMetadataLoader.h"
+#include "../../Framework/Loaders/SeriesThumbnailsLoader.h"
+#include "../../Framework/Loaders/WebAssemblyLoadersContext.h"
+#include "../../Framework/Messages/ObserverBase.h"
+#include "../../Framework/Oracle/ParseDicomFromWadoCommand.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/FloatTextureSceneLayer.h"
+#include "../../Framework/Scene2D/PolylineSceneLayer.h"
+#include "../../Framework/StoneException.h"
+#include "../../Framework/Toolbox/DicomInstanceParameters.h"
+#include "../../Framework/Toolbox/GeometryToolbox.h"
+#include "../../Framework/Toolbox/SortedFrames.h"
+#include "../../Framework/Viewport/WebGLViewport.h"
+
+#include <boost/make_shared.hpp>
+#include <stdio.h>
+
+
+enum EMSCRIPTEN_KEEPALIVE ThumbnailType
+{
+  ThumbnailType_Image,
+    ThumbnailType_NoPreview,
+    ThumbnailType_Pdf,
+    ThumbnailType_Video,
+    ThumbnailType_Loading,
+    ThumbnailType_Unknown
+};
+
+
+enum EMSCRIPTEN_KEEPALIVE DisplayedFrameQuality
+{
+DisplayedFrameQuality_None,
+  DisplayedFrameQuality_Low,
+  DisplayedFrameQuality_High
+  };
+  
+
+
+static const int PRIORITY_HIGH = -100;
+static const int PRIORITY_LOW = 100;
+static const int PRIORITY_NORMAL = 0;
+
+static const unsigned int QUALITY_JPEG = 0;
+static const unsigned int QUALITY_FULL = 1;
+
+class ResourcesLoader : public OrthancStone::ObserverBase<ResourcesLoader>
+{
+public:
+  class IObserver : public boost::noncopyable
+  {
+  public:
+    virtual ~IObserver()
+    {
+    }
+
+    virtual void SignalResourcesLoaded() = 0;
+
+    virtual void SignalSeriesThumbnailLoaded(const std::string& studyInstanceUid,
+                                             const std::string& seriesInstanceUid) = 0;
+
+    virtual void SignalSeriesMetadataLoaded(const std::string& studyInstanceUid,
+                                            const std::string& seriesInstanceUid) = 0;
+  };
+  
+private:
+  std::unique_ptr<IObserver>                               observer_;
+  OrthancStone::DicomSource                                source_;
+  size_t                                                   pending_;
+  boost::shared_ptr<OrthancStone::LoadedDicomResources>    studies_;
+  boost::shared_ptr<OrthancStone::LoadedDicomResources>    series_;
+  boost::shared_ptr<OrthancStone::DicomResourcesLoader>    resourcesLoader_;
+  boost::shared_ptr<OrthancStone::SeriesThumbnailsLoader>  thumbnailsLoader_;
+  boost::shared_ptr<OrthancStone::SeriesMetadataLoader>    metadataLoader_;
+
+  ResourcesLoader(const OrthancStone::DicomSource& source) :
+    source_(source),
+    pending_(0),
+    studies_(new OrthancStone::LoadedDicomResources(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID)),
+    series_(new OrthancStone::LoadedDicomResources(Orthanc::DICOM_TAG_SERIES_INSTANCE_UID))
+  {
+  }
+
+  void Handle(const OrthancStone::DicomResourcesLoader::SuccessMessage& message)
+  {
+    const Orthanc::SingleValueObject<Orthanc::ResourceType>& payload =
+      dynamic_cast<const Orthanc::SingleValueObject<Orthanc::ResourceType>&>(message.GetUserPayload());
+    
+    OrthancStone::LoadedDicomResources& dicom = *message.GetResources();
+    
+    LOG(INFO) << "resources loaded: " << dicom.GetSize()
+              << ", " << Orthanc::EnumerationToString(payload.GetValue());
+
+    if (payload.GetValue() == Orthanc::ResourceType_Series)
+    {
+      for (size_t i = 0; i < dicom.GetSize(); i++)
+      {
+        std::string studyInstanceUid, seriesInstanceUid;
+        if (dicom.GetResource(i).LookupStringValue(
+              studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) &&
+            dicom.GetResource(i).LookupStringValue(
+              seriesInstanceUid, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false))
+        {
+          thumbnailsLoader_->ScheduleLoadThumbnail(source_, "", studyInstanceUid, seriesInstanceUid);
+          metadataLoader_->ScheduleLoadSeries(PRIORITY_LOW + 1, source_, studyInstanceUid, seriesInstanceUid);
+        }
+      }
+    }
+
+    if (pending_ == 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+    else
+    {
+      pending_ --;
+      if (pending_ == 0 &&
+          observer_.get() != NULL)
+      {
+        observer_->SignalResourcesLoaded();
+      }
+    }
+  }
+
+  void Handle(const OrthancStone::SeriesThumbnailsLoader::SuccessMessage& message)
+  {
+    if (observer_.get() != NULL)
+    {
+      observer_->SignalSeriesThumbnailLoaded(
+        message.GetStudyInstanceUid(), message.GetSeriesInstanceUid());
+    }
+  }
+
+  void Handle(const OrthancStone::SeriesMetadataLoader::SuccessMessage& message)
+  {
+    if (observer_.get() != NULL)
+    {
+      observer_->SignalSeriesMetadataLoaded(
+        message.GetStudyInstanceUid(), message.GetSeriesInstanceUid());
+    }
+  }
+
+  void FetchInternal(const std::string& studyInstanceUid,
+                     const std::string& seriesInstanceUid)
+  {
+    // Firstly, load the study
+    Orthanc::DicomMap filter;
+    filter.SetValue(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, studyInstanceUid, false);
+
+    std::set<Orthanc::DicomTag> tags;
+    tags.insert(Orthanc::DICOM_TAG_STUDY_DESCRIPTION);  // Necessary for Orthanc DICOMweb plugin
+
+    resourcesLoader_->ScheduleQido(
+      studies_, PRIORITY_HIGH, source_, Orthanc::ResourceType_Study, filter, tags,
+      new Orthanc::SingleValueObject<Orthanc::ResourceType>(Orthanc::ResourceType_Study));
+
+    // Secondly, load the series
+    if (!seriesInstanceUid.empty())
+    {
+      filter.SetValue(Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, seriesInstanceUid, false);
+    }
+    
+    tags.insert(Orthanc::DICOM_TAG_SERIES_NUMBER);  // Necessary for Google Cloud Platform
+    
+    resourcesLoader_->ScheduleQido(
+      series_, PRIORITY_HIGH, source_, Orthanc::ResourceType_Series, filter, tags,
+      new Orthanc::SingleValueObject<Orthanc::ResourceType>(Orthanc::ResourceType_Series));
+
+    pending_ += 2;
+  }
+
+public:
+  static boost::shared_ptr<ResourcesLoader> Create(OrthancStone::ILoadersContext::ILock& lock,
+                                                   const OrthancStone::DicomSource& source)
+  {
+    boost::shared_ptr<ResourcesLoader> loader(new ResourcesLoader(source));
+
+    loader->resourcesLoader_ = OrthancStone::DicomResourcesLoader::Create(lock);
+    loader->thumbnailsLoader_ = OrthancStone::SeriesThumbnailsLoader::Create(lock, PRIORITY_LOW);
+    loader->metadataLoader_ = OrthancStone::SeriesMetadataLoader::Create(lock);
+    
+    loader->Register<OrthancStone::DicomResourcesLoader::SuccessMessage>(
+      *loader->resourcesLoader_, &ResourcesLoader::Handle);
+
+    loader->Register<OrthancStone::SeriesThumbnailsLoader::SuccessMessage>(
+      *loader->thumbnailsLoader_, &ResourcesLoader::Handle);
+
+    loader->Register<OrthancStone::SeriesMetadataLoader::SuccessMessage>(
+      *loader->metadataLoader_, &ResourcesLoader::Handle);
+    
+    return loader;
+  }
+  
+  void FetchAllStudies()
+  {
+    FetchInternal("", "");
+  }
+  
+  void FetchStudy(const std::string& studyInstanceUid)
+  {
+    FetchInternal(studyInstanceUid, "");
+  }
+  
+  void FetchSeries(const std::string& studyInstanceUid,
+                   const std::string& seriesInstanceUid)
+  {
+    FetchInternal(studyInstanceUid, seriesInstanceUid);
+  }
+
+  size_t GetStudiesCount() const
+  {
+    return studies_->GetSize();
+  }
+
+  size_t GetSeriesCount() const
+  {
+    return series_->GetSize();
+  }
+
+  void GetStudy(Orthanc::DicomMap& target,
+                size_t i)
+  {
+    target.Assign(studies_->GetResource(i));
+  }
+
+  void GetSeries(Orthanc::DicomMap& target,
+                 size_t i)
+  {
+    target.Assign(series_->GetResource(i));
+
+    // Complement with the study-level tags
+    std::string studyInstanceUid;
+    if (target.LookupStringValue(studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) &&
+        studies_->HasResource(studyInstanceUid))
+    {
+      studies_->MergeResource(target, studyInstanceUid);
+    }
+  }
+
+  OrthancStone::SeriesThumbnailType GetSeriesThumbnail(std::string& image,
+                                                       std::string& mime,
+                                                       const std::string& seriesInstanceUid)
+  {
+    return thumbnailsLoader_->GetSeriesThumbnail(image, mime, seriesInstanceUid);
+  }
+
+  void FetchSeriesMetadata(int priority,
+                           const std::string& studyInstanceUid,
+                           const std::string& seriesInstanceUid)
+  {
+    metadataLoader_->ScheduleLoadSeries(priority, source_, studyInstanceUid, seriesInstanceUid);
+  }
+
+  bool IsSeriesComplete(const std::string& seriesInstanceUid)
+  {
+    OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
+    return accessor.IsComplete();
+  }
+
+  bool SortSeriesFrames(OrthancStone::SortedFrames& target,
+                        const std::string& seriesInstanceUid)
+  {
+    OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
+    
+    if (accessor.IsComplete())
+    {
+      target.Clear();
+
+      for (size_t i = 0; i < accessor.GetInstancesCount(); i++)
+      {
+        target.AddInstance(accessor.GetInstance(i));
+      }
+
+      target.Sort();
+      
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  void AcquireObserver(IObserver* observer)
+  {  
+    observer_.reset(observer);
+  }
+};
+
+
+
+class FramesCache : public boost::noncopyable
+{
+private:
+  class CachedImage : public Orthanc::ICacheable
+  {
+  private:
+    std::unique_ptr<Orthanc::ImageAccessor>  image_;
+    unsigned int                             quality_;
+
+  public:
+    CachedImage(Orthanc::ImageAccessor* image,
+                unsigned int quality) :
+      image_(image),
+      quality_(quality)
+    {
+      assert(image != NULL);
+    }
+
+    virtual size_t GetMemoryUsage() const
+    {    
+      assert(image_.get() != NULL);
+      return (image_->GetBytesPerPixel() * image_->GetPitch() * image_->GetHeight());
+    }
+
+    const Orthanc::ImageAccessor& GetImage() const
+    {
+      assert(image_.get() != NULL);
+      return *image_;
+    }
+
+    unsigned int GetQuality() const
+    {
+      return quality_;
+    }
+  };
+
+
+  static std::string GetKey(const std::string& sopInstanceUid,
+                            size_t frameIndex)
+  {
+    return sopInstanceUid + "|" + boost::lexical_cast<std::string>(frameIndex);
+  }
+  
+
+  Orthanc::MemoryObjectCache  cache_;
+  
+public:
+  FramesCache()
+  {
+    SetMaximumSize(100 * 1024 * 1024);  // 100 MB
+  }
+  
+  size_t GetMaximumSize()
+  {
+    return cache_.GetMaximumSize();
+  }
+    
+  void SetMaximumSize(size_t size)
+  {
+    cache_.SetMaximumSize(size);
+  }
+
+  /**
+   * Returns "true" iff the provided image has better quality than the
+   * previously cached one, or if no cache was previously available.
+   **/
+  bool Acquire(const std::string& sopInstanceUid,
+               size_t frameIndex,
+               Orthanc::ImageAccessor* image /* transfer ownership */,
+               unsigned int quality)
+  {
+    std::unique_ptr<Orthanc::ImageAccessor> protection(image);
+    
+    if (image == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+    else if (image->GetFormat() != Orthanc::PixelFormat_Float32 &&
+             image->GetFormat() != Orthanc::PixelFormat_RGB24)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+    }
+
+    const std::string& key = GetKey(sopInstanceUid, frameIndex);
+
+    bool invalidate = false;
+    
+    {
+      /**
+       * Access the previous cached entry, with side effect of tagging
+       * it as the most recently accessed frame (update of LRU recycling)
+       **/
+      Orthanc::MemoryObjectCache::Accessor accessor(cache_, key, false /* unique lock */);
+
+      if (accessor.IsValid())
+      {
+        const CachedImage& previous = dynamic_cast<const CachedImage&>(accessor.GetValue());
+        
+        // There is already a cached image for this frame
+        if (previous.GetQuality() < quality)
+        {
+          // The previously stored image has poorer quality
+          invalidate = true;
+        }
+        else
+        {
+          // No update in the quality, don't change the cache
+          return false;   
+        }
+      }
+      else
+      {
+        invalidate = false;
+      }
+    }
+
+    if (invalidate)
+    {
+      cache_.Invalidate(key);
+    }
+        
+    cache_.Acquire(key, new CachedImage(protection.release(), quality));
+    return true;
+  }
+
+  class Accessor : public boost::noncopyable
+  {
+  private:
+    Orthanc::MemoryObjectCache::Accessor accessor_;
+
+    const CachedImage& GetCachedImage() const
+    {
+      if (IsValid())
+      {
+        return dynamic_cast<CachedImage&>(accessor_.GetValue());
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+    }
+    
+  public:
+    Accessor(FramesCache& that,
+             const std::string& sopInstanceUid,
+             size_t frameIndex) :
+      accessor_(that.cache_, GetKey(sopInstanceUid, frameIndex), false /* shared lock */)
+    {
+    }
+
+    bool IsValid() const
+    {
+      return accessor_.IsValid();
+    }
+
+    const Orthanc::ImageAccessor& GetImage() const
+    {
+      return GetCachedImage().GetImage();
+    }
+
+    unsigned int GetQuality() const
+    {
+      return GetCachedImage().GetQuality();
+    }
+  };
+};
+
+
+
+class SeriesCursor : public boost::noncopyable
+{
+public:
+  enum Action
+  {
+    Action_FastPlus,
+    Action_Plus,
+    Action_None,
+    Action_Minus,
+    Action_FastMinus
+  };
+  
+private:
+  std::vector<size_t>  prefetch_;
+  int                  framesCount_;
+  int                  currentFrame_;
+  bool                 isCircular_;
+  int                  fastDelta_;
+  Action               lastAction_;
+
+  int ComputeNextFrame(int currentFrame,
+                       Action action) const
+  {
+    if (framesCount_ == 0)
+    {
+      assert(currentFrame == 0);
+      return 0;
+    }
+
+    int nextFrame = currentFrame;
+    
+    switch (action)
+    {
+      case Action_FastPlus:
+        nextFrame += fastDelta_;
+        break;
+
+      case Action_Plus:
+        nextFrame += 1;
+        break;
+
+      case Action_None:
+        break;
+
+      case Action_Minus:
+        nextFrame -= 1;
+        break;
+
+      case Action_FastMinus:
+        nextFrame -= fastDelta_;
+        break;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    if (isCircular_)
+    {
+      while (nextFrame < 0)
+      {
+        nextFrame += framesCount_;
+      }
+
+      while (nextFrame >= framesCount_)
+      {
+        nextFrame -= framesCount_;
+      }
+    }
+    else
+    {
+      if (nextFrame < 0)
+      {
+        nextFrame = 0;
+      }
+      else if (nextFrame >= framesCount_)
+      {
+        nextFrame = framesCount_ - 1;
+      }
+    }
+
+    return nextFrame;
+  }
+  
+  void UpdatePrefetch()
+  {
+    /**
+     * This method will order the frames of the series according to
+     * the number of "actions" (i.e. mouse wheels) that are necessary
+     * to reach them, starting from the current frame. It is assumed
+     * that once one action is done, it is more likely that the user
+     * will do the same action just afterwards.
+     **/
+    
+    prefetch_.clear();
+
+    if (framesCount_ == 0)
+    {
+      return;
+    }
+
+    prefetch_.reserve(framesCount_);
+    
+    // Breadth-first search using a FIFO. The queue associates a frame
+    // and the action that is the most likely in this frame
+    typedef std::list< std::pair<int, Action> >  Queue;
+
+    Queue queue;
+    std::set<int>  visited;  // Frames that have already been visited
+
+    queue.push_back(std::make_pair(currentFrame_, lastAction_));
+
+    while (!queue.empty())
+    {
+      int frame = queue.front().first;
+      Action previousAction = queue.front().second;
+      queue.pop_front();
+
+      if (visited.find(frame) == visited.end())
+      {
+        visited.insert(frame);
+        prefetch_.push_back(frame);
+
+        switch (previousAction)
+        {
+          case Action_None:
+          case Action_Plus:
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus), Action_Plus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus), Action_Minus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus), Action_FastPlus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus), Action_FastMinus));
+            break;
+          
+          case Action_Minus:
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus), Action_Minus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus), Action_Plus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus), Action_FastMinus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus), Action_FastPlus));
+            break;
+
+          case Action_FastPlus:
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus), Action_FastPlus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus), Action_FastMinus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus), Action_Plus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus), Action_Minus));
+            break;
+              
+          case Action_FastMinus:
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus), Action_FastMinus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus), Action_FastPlus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus), Action_Minus));
+            queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus), Action_Plus));
+            break;
+
+          default:
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+      }
+    }
+
+    assert(prefetch_.size() == framesCount_);
+  }
+
+  bool CheckFrameIndex(int frame) const
+  {
+    return ((framesCount_ == 0 && frame == 0) ||
+            (framesCount_ > 0 && frame >= 0 && frame < framesCount_));
+  }
+  
+public:
+  SeriesCursor(size_t framesCount) :
+    framesCount_(framesCount),
+    currentFrame_(framesCount / 2),  // Start at the middle frame    
+    isCircular_(false),
+    lastAction_(Action_None)
+  {
+    SetFastDelta(framesCount / 20);
+    UpdatePrefetch();
+  }
+
+  void SetCircular(bool isCircular)
+  {
+    isCircular_ = isCircular;
+    UpdatePrefetch();
+  }
+
+  void SetFastDelta(int delta)
+  {
+    fastDelta_ = (delta < 0 ? -delta : delta);
+
+    if (fastDelta_ <= 0)
+    {
+      fastDelta_ = 1;
+    }
+  }
+
+  void SetCurrentIndex(size_t frame)
+  {
+    if (frame >= framesCount_)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      currentFrame_ = frame;
+      lastAction_ = Action_None;
+      UpdatePrefetch();
+    }
+  }
+
+  size_t GetCurrentIndex() const
+  {
+    assert(CheckFrameIndex(currentFrame_));
+    return static_cast<size_t>(currentFrame_);
+  }
+
+  void Apply(Action action)
+  {
+    currentFrame_ = ComputeNextFrame(currentFrame_, action);
+    lastAction_ = action;
+    UpdatePrefetch();
+  }
+
+  size_t GetPrefetchSize() const
+  {
+    assert(prefetch_.size() == framesCount_);
+    return prefetch_.size();
+  }
+
+  size_t GetPrefetchFrameIndex(size_t i) const
+  {
+    if (i >= prefetch_.size())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      assert(CheckFrameIndex(prefetch_[i]));
+      return static_cast<size_t>(prefetch_[i]);
+    }
+  }
+};
+
+
+
+
+class FrameGeometry
+{
+private:
+  bool                              isValid_;
+  std::string                       frameOfReferenceUid_;
+  OrthancStone::CoordinateSystem3D  coordinates_;
+  double                            pixelSpacingX_;
+  double                            pixelSpacingY_;
+  OrthancStone::Extent2D            extent_;
+
+public:
+  FrameGeometry() :
+    isValid_(false)
+  {
+  }
+    
+  FrameGeometry(const Orthanc::DicomMap& tags) :
+    isValid_(false),
+    coordinates_(tags)
+  {
+    if (!tags.LookupStringValue(
+          frameOfReferenceUid_, Orthanc::DICOM_TAG_FRAME_OF_REFERENCE_UID, false))
+    {
+      frameOfReferenceUid_.clear();
+    }
+
+    OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, tags);
+
+    unsigned int rows, columns;
+    if (tags.HasTag(Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT) &&
+        tags.HasTag(Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT) &&
+        tags.ParseUnsignedInteger32(rows, Orthanc::DICOM_TAG_ROWS) &&
+        tags.ParseUnsignedInteger32(columns, Orthanc::DICOM_TAG_COLUMNS))
+    {
+      double ox = -pixelSpacingX_ / 2.0;
+      double oy = -pixelSpacingY_ / 2.0;
+      extent_.AddPoint(ox, oy);
+      extent_.AddPoint(ox + pixelSpacingX_ * static_cast<double>(columns),
+                       oy + pixelSpacingY_ * static_cast<double>(rows));
+
+      isValid_ = true;
+    }
+  }
+
+  bool IsValid() const
+  {
+    return isValid_;
+  }
+
+  const std::string& GetFrameOfReferenceUid() const
+  {
+    if (isValid_)
+    {
+      return frameOfReferenceUid_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+  const OrthancStone::CoordinateSystem3D& GetCoordinates() const
+  {
+    if (isValid_)
+    {
+      return coordinates_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+  double GetPixelSpacingX() const
+  {
+    if (isValid_)
+    {
+      return pixelSpacingX_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+  double GetPixelSpacingY() const
+  {
+    if (isValid_)
+    {
+      return pixelSpacingY_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+  bool Intersect(double& x1,  // Coordinates of the clipped line (out)
+                 double& y1,
+                 double& x2,
+                 double& y2,
+                 const FrameGeometry& other) const
+  {
+    if (this == &other)
+    {
+      return false;
+    }
+    
+    OrthancStone::Vector direction, origin;
+        
+    if (IsValid() &&
+        other.IsValid() &&
+        !extent_.IsEmpty() &&
+        frameOfReferenceUid_ == other.frameOfReferenceUid_ &&
+        OrthancStone::GeometryToolbox::IntersectTwoPlanes(
+          origin, direction,
+          coordinates_.GetOrigin(), coordinates_.GetNormal(),
+          other.coordinates_.GetOrigin(), other.coordinates_.GetNormal()))
+    {
+      double ax, ay, bx, by;
+      coordinates_.ProjectPoint(ax, ay, origin);
+      coordinates_.ProjectPoint(bx, by, origin + 100.0 * direction);
+      
+      return OrthancStone::GeometryToolbox::ClipLineToRectangle(
+        x1, y1, x2, y2,
+        ax, ay, bx, by,
+        extent_.GetX1(), extent_.GetY1(), extent_.GetX2(), extent_.GetY2());
+    }
+    else
+    {
+      return false;
+    }
+  }
+};
+  
+
+
+class ViewerViewport : public OrthancStone::ObserverBase<ViewerViewport>
+{
+public:
+  class IObserver : public boost::noncopyable
+  {
+  public:
+    virtual ~IObserver()
+    {
+    }
+
+    virtual void SignalFrameUpdated(const ViewerViewport& viewport,
+                                    size_t currentFrame,
+                                    size_t countFrames,
+                                    DisplayedFrameQuality quality) = 0;
+  };
+
+private:
+  static const int LAYER_TEXTURE = 0;
+  static const int LAYER_REFERENCE_LINES = 1;
+  
+  
+  class ICommand : public Orthanc::IDynamicObject
+  {
+  private:
+    boost::shared_ptr<ViewerViewport>  viewport_;
+    
+  public:
+    ICommand(boost::shared_ptr<ViewerViewport> viewport) :
+      viewport_(viewport)
+    {
+      if (viewport == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+    }
+    
+    virtual ~ICommand()
+    {
+    }
+
+    ViewerViewport& GetViewport() const
+    {
+      assert(viewport_ != NULL);
+      return *viewport_;
+    }
+    
+    virtual void Handle(const OrthancStone::DicomResourcesLoader::SuccessMessage& message) const
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+    
+    virtual void Handle(const OrthancStone::HttpCommand::SuccessMessage& message) const
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+
+    virtual void Handle(const OrthancStone::ParseDicomSuccessMessage& message) const
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+  };
+
+  class SetDefaultWindowingCommand : public ICommand
+  {
+  public:
+    SetDefaultWindowingCommand(boost::shared_ptr<ViewerViewport> viewport) :
+      ICommand(viewport)
+    {
+    }
+    
+    virtual void Handle(const OrthancStone::DicomResourcesLoader::SuccessMessage& message) const ORTHANC_OVERRIDE
+    {
+      if (message.GetResources()->GetSize() != 1)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+      }
+
+      const Orthanc::DicomMap& dicom = message.GetResources()->GetResource(0);
+      
+      {
+        OrthancStone::DicomInstanceParameters params(dicom);
+
+        if (params.HasDefaultWindowing())
+        {
+          GetViewport().defaultWindowingCenter_ = params.GetDefaultWindowingCenter();
+          GetViewport().defaultWindowingWidth_ = params.GetDefaultWindowingWidth();
+          LOG(INFO) << "Default windowing: " << params.GetDefaultWindowingCenter()
+                    << "," << params.GetDefaultWindowingWidth();
+
+          GetViewport().windowingCenter_ = params.GetDefaultWindowingCenter();
+          GetViewport().windowingWidth_ = params.GetDefaultWindowingWidth();
+        }
+        else
+        {
+          LOG(INFO) << "No default windowing";
+          GetViewport().ResetDefaultWindowing();
+        }
+      }
+
+      GetViewport().DisplayCurrentFrame();
+    }
+  };
+
+  class SetLowQualityFrame : public ICommand
+  {
+  private:
+    std::string   sopInstanceUid_;
+    unsigned int  frameIndex_;
+    float         windowCenter_;
+    float         windowWidth_;
+    bool          isMonochrome1_;
+    bool          isPrefetch_;
+    
+  public:
+    SetLowQualityFrame(boost::shared_ptr<ViewerViewport> viewport,
+                       const std::string& sopInstanceUid,
+                       unsigned int frameIndex,
+                       float windowCenter,
+                       float windowWidth,
+                       bool isMonochrome1,
+                       bool isPrefetch) :
+      ICommand(viewport),
+      sopInstanceUid_(sopInstanceUid),
+      frameIndex_(frameIndex),
+      windowCenter_(windowCenter),
+      windowWidth_(windowWidth),
+      isMonochrome1_(isMonochrome1),
+      isPrefetch_(isPrefetch)
+    {
+    }
+    
+    virtual void Handle(const OrthancStone::HttpCommand::SuccessMessage& message) const ORTHANC_OVERRIDE
+    {
+      std::unique_ptr<Orthanc::JpegReader> jpeg(new Orthanc::JpegReader);
+      jpeg->ReadFromMemory(message.GetAnswer());
+
+      bool updatedCache;
+      
+      switch (jpeg->GetFormat())
+      {
+        case Orthanc::PixelFormat_RGB24:
+          updatedCache = GetViewport().cache_->Acquire(
+            sopInstanceUid_, frameIndex_, jpeg.release(), QUALITY_JPEG);
+          break;
+
+        case Orthanc::PixelFormat_Grayscale8:
+        {
+          if (isMonochrome1_)
+          {
+            Orthanc::ImageProcessing::Invert(*jpeg);
+          }
+
+          std::unique_ptr<Orthanc::Image> converted(
+            new Orthanc::Image(Orthanc::PixelFormat_Float32, jpeg->GetWidth(),
+                               jpeg->GetHeight(), false));
+
+          Orthanc::ImageProcessing::Convert(*converted, *jpeg);
+
+          /**
+
+             Orthanc::ImageProcessing::ShiftScale() computes "(x + offset) * scaling".
+             The system to solve is thus:           
+
+             (0 + offset) * scaling = windowingCenter - windowingWidth / 2     [a]
+             (255 + offset) * scaling = windowingCenter + windowingWidth / 2   [b]
+
+             Resolution:
+
+             [b - a] => 255 * scaling = windowingWidth
+             [a] => offset = (windowingCenter - windowingWidth / 2) / scaling
+
+          **/
+
+          const float scaling = windowWidth_ / 255.0f;
+          const float offset = (OrthancStone::LinearAlgebra::IsCloseToZero(scaling) ? 0 :
+                                (windowCenter_ - windowWidth_ / 2.0f) / scaling);
+
+          Orthanc::ImageProcessing::ShiftScale(*converted, offset, scaling, false);
+          updatedCache = GetViewport().cache_->Acquire(
+            sopInstanceUid_, frameIndex_, converted.release(), QUALITY_JPEG);
+          break;
+        }
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+
+      if (updatedCache)
+      {
+        GetViewport().SignalUpdatedFrame(sopInstanceUid_, frameIndex_);
+      }
+
+      if (isPrefetch_)
+      {
+        GetViewport().ScheduleNextPrefetch();
+      }
+    }
+  };
+
+
+  class SetFullDicomFrame : public ICommand
+  {
+  private:
+    std::string   sopInstanceUid_;
+    unsigned int  frameIndex_;
+    bool          isPrefetch_;
+    
+  public:
+    SetFullDicomFrame(boost::shared_ptr<ViewerViewport> viewport,
+                      const std::string& sopInstanceUid,
+                      unsigned int frameIndex,
+                      bool isPrefetch) :
+      ICommand(viewport),
+      sopInstanceUid_(sopInstanceUid),
+      frameIndex_(frameIndex),
+      isPrefetch_(isPrefetch)
+    {
+    }
+    
+    virtual void Handle(const OrthancStone::ParseDicomSuccessMessage& message) const ORTHANC_OVERRIDE
+    {
+      Orthanc::DicomMap tags;
+      message.GetDicom().ExtractDicomSummary(tags);
+
+      std::string s;
+      if (!tags.LookupStringValue(s, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
+      {
+        // Safety check
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }      
+      
+      std::unique_ptr<Orthanc::ImageAccessor> frame(
+        Orthanc::DicomImageDecoder::Decode(message.GetDicom(), frameIndex_));
+
+      if (frame.get() == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+
+      bool updatedCache;
+
+      if (frame->GetFormat() == Orthanc::PixelFormat_RGB24)
+      {
+        updatedCache = GetViewport().cache_->Acquire(
+          sopInstanceUid_, frameIndex_, frame.release(), QUALITY_FULL);
+      }
+      else
+      {
+        double a = 1;
+        double b = 0;
+
+        double doseScaling;
+        if (tags.ParseDouble(doseScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING))
+        {
+          a = doseScaling;
+        }
+      
+        double rescaleIntercept, rescaleSlope;
+        if (tags.ParseDouble(rescaleIntercept, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) &&
+            tags.ParseDouble(rescaleSlope, Orthanc::DICOM_TAG_RESCALE_SLOPE))
+        {
+          a *= rescaleSlope;
+          b = rescaleIntercept;
+        }
+
+        std::unique_ptr<Orthanc::ImageAccessor> converted(
+          new Orthanc::Image(Orthanc::PixelFormat_Float32, frame->GetWidth(), frame->GetHeight(), false));
+        Orthanc::ImageProcessing::Convert(*converted, *frame);
+        Orthanc::ImageProcessing::ShiftScale2(*converted, b, a, false);
+
+        updatedCache = GetViewport().cache_->Acquire(
+          sopInstanceUid_, frameIndex_, converted.release(), QUALITY_FULL);
+      }
+      
+      if (updatedCache)
+      {
+        GetViewport().SignalUpdatedFrame(sopInstanceUid_, frameIndex_);
+      }
+
+      if (isPrefetch_)
+      {
+        GetViewport().ScheduleNextPrefetch();
+      }
+    }
+  };
+
+
+  class PrefetchItem
+  {
+  private:
+    size_t   frameIndex_;
+    bool     isFull_;
+
+  public:
+    PrefetchItem(size_t frameIndex,
+                 bool isFull) :
+      frameIndex_(frameIndex),
+      isFull_(isFull)
+    {
+    }
+
+    size_t GetFrameIndex() const
+    {
+      return frameIndex_;
+    }
+
+    bool IsFull() const
+    {
+      return isFull_;
+    }
+  };
+  
+
+  std::unique_ptr<IObserver>                    observer_;
+  OrthancStone::ILoadersContext&               context_;
+  boost::shared_ptr<OrthancStone::WebGLViewport>   viewport_;
+  boost::shared_ptr<OrthancStone::DicomResourcesLoader> loader_;
+  OrthancStone::DicomSource                    source_;
+  boost::shared_ptr<FramesCache>               cache_;  
+  std::unique_ptr<OrthancStone::SortedFrames>  frames_;
+  std::unique_ptr<SeriesCursor>                cursor_;
+  float                                        windowingCenter_;
+  float                                        windowingWidth_;
+  float                                        defaultWindowingCenter_;
+  float                                        defaultWindowingWidth_;
+  bool                                         inverted_;
+  bool                                         fitNextContent_;
+  bool                                         isCtrlDown_;
+  FrameGeometry                                currentFrameGeometry_;
+  std::list<PrefetchItem>                      prefetchQueue_;
+
+  void ScheduleNextPrefetch()
+  {
+    while (!prefetchQueue_.empty())
+    {
+      size_t index = prefetchQueue_.front().GetFrameIndex();
+      bool isFull = prefetchQueue_.front().IsFull();
+      prefetchQueue_.pop_front();
+      
+      const std::string sopInstanceUid = frames_->GetFrameSopInstanceUid(index);
+      unsigned int frame = frames_->GetFrameIndex(index);
+
+      {
+        FramesCache::Accessor accessor(*cache_, sopInstanceUid, frame);
+        if (!accessor.IsValid() ||
+            (isFull && accessor.GetQuality() == 0))
+        {
+          if (isFull)
+          {
+            ScheduleLoadFullDicomFrame(index, PRIORITY_NORMAL, true);
+          }
+          else
+          {
+            ScheduleLoadRenderedFrame(index, PRIORITY_NORMAL, true);
+          }
+          return;
+        }
+      }
+    }
+  }
+  
+  
+  void ResetDefaultWindowing()
+  {
+    defaultWindowingCenter_ = 128;
+    defaultWindowingWidth_ = 256;
+
+    windowingCenter_ = defaultWindowingCenter_;
+    windowingWidth_ = defaultWindowingWidth_;
+
+    inverted_ = false;
+  }
+
+  void SignalUpdatedFrame(const std::string& sopInstanceUid,
+                          unsigned int frameIndex)
+  {
+    if (cursor_.get() != NULL &&
+        frames_.get() != NULL)
+    {
+      size_t index = cursor_->GetCurrentIndex();
+
+      if (frames_->GetFrameSopInstanceUid(index) == sopInstanceUid &&
+          frames_->GetFrameIndex(index) == frameIndex)
+      {
+        DisplayCurrentFrame();
+      }
+    }
+  }
+
+  void DisplayCurrentFrame()
+  {
+    DisplayedFrameQuality quality = DisplayedFrameQuality_None;
+    
+    if (cursor_.get() != NULL &&
+        frames_.get() != NULL)
+    {
+      const size_t index = cursor_->GetCurrentIndex();
+      
+      unsigned int cachedQuality;
+      if (!DisplayFrame(cachedQuality, index))
+      {
+        // This frame is not cached yet: Load it
+        if (source_.HasDicomWebRendered())
+        {
+          ScheduleLoadRenderedFrame(index, PRIORITY_HIGH, false /* not a prefetch */);
+        }
+        else
+        {
+          ScheduleLoadFullDicomFrame(index, PRIORITY_HIGH, false /* not a prefetch */);
+        }
+      }
+      else if (cachedQuality < QUALITY_FULL)
+      {
+        // This frame is only available in low-res: Download the full DICOM
+        ScheduleLoadFullDicomFrame(index, PRIORITY_HIGH, false /* not a prefetch */);
+        quality = DisplayedFrameQuality_Low;
+      }
+      else
+      {
+        quality = DisplayedFrameQuality_High;
+      }
+
+      currentFrameGeometry_ = FrameGeometry(frames_->GetFrameTags(index));
+
+      {
+        // Prepare prefetching
+        prefetchQueue_.clear();
+        for (size_t i = 0; i < cursor_->GetPrefetchSize() && i < 16; i++)
+        {
+          size_t a = cursor_->GetPrefetchFrameIndex(i);
+          if (a != index)
+          {
+            prefetchQueue_.push_back(PrefetchItem(a, i < 2));
+          }
+        }
+
+        ScheduleNextPrefetch();
+      }      
+
+      if (observer_.get() != NULL)
+      {
+        observer_->SignalFrameUpdated(*this, cursor_->GetCurrentIndex(),
+                                      frames_->GetFramesCount(), quality);
+      }
+    }
+    else
+    {
+      currentFrameGeometry_ = FrameGeometry();
+    }
+  }
+
+  void ClearViewport()
+  {
+    {
+      std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
+      lock->GetController().GetScene().DeleteLayer(LAYER_TEXTURE);
+      //lock->GetCompositor().Refresh(lock->GetController().GetScene());
+      lock->Invalidate();
+    }
+  }
+
+  bool DisplayFrame(unsigned int& quality,
+                    size_t index)
+  {
+    if (frames_.get() == NULL)
+    {
+      return false;
+    }
+
+    const std::string sopInstanceUid = frames_->GetFrameSopInstanceUid(index);
+    unsigned int frame = frames_->GetFrameIndex(index);
+
+    FramesCache::Accessor accessor(*cache_, sopInstanceUid, frame);
+    if (accessor.IsValid())
+    {
+      quality = accessor.GetQuality();
+
+      std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer;
+
+      switch (accessor.GetImage().GetFormat())
+      {
+        case Orthanc::PixelFormat_RGB24:
+          layer.reset(new OrthancStone::ColorTextureSceneLayer(accessor.GetImage()));
+          break;
+
+        case Orthanc::PixelFormat_Float32:
+        {
+          std::unique_ptr<OrthancStone::FloatTextureSceneLayer> tmp(
+            new OrthancStone::FloatTextureSceneLayer(accessor.GetImage()));
+          tmp->SetCustomWindowing(windowingCenter_, windowingWidth_);
+          tmp->SetInverted(inverted_ ^ frames_->IsFrameMonochrome1(index));
+          layer.reset(tmp.release());
+          break;
+        }
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+      }
+
+      layer->SetLinearInterpolation(true);
+
+      double pixelSpacingX, pixelSpacingY;
+      OrthancStone::GeometryToolbox::GetPixelSpacing(
+        pixelSpacingX, pixelSpacingY, frames_->GetFrameTags(index));
+      layer->SetPixelSpacing(pixelSpacingX, pixelSpacingY);
+
+      if (layer.get() == NULL)
+      {
+        return false;
+      }
+      else
+      {
+        std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
+        lock->GetController().GetScene().SetLayer(LAYER_TEXTURE, layer.release());
+
+        if (fitNextContent_)
+        {
+          lock->GetCompositor().RefreshCanvasSize();
+          lock->GetCompositor().FitContent(lock->GetController().GetScene());
+          fitNextContent_ = false;
+        }
+        
+        //lock->GetCompositor().Refresh(lock->GetController().GetScene());
+        lock->Invalidate();
+        return true;
+      }
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  void ScheduleLoadFullDicomFrame(size_t index,
+                                  int priority,
+                                  bool isPrefetch)
+  {
+    if (frames_.get() != NULL)
+    {
+      std::string sopInstanceUid = frames_->GetFrameSopInstanceUid(index);
+      unsigned int frame = frames_->GetFrameIndex(index);
+      
+      {
+        std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock());
+        lock->Schedule(
+          GetSharedObserver(), priority, OrthancStone::ParseDicomFromWadoCommand::Create(
+            source_, frames_->GetStudyInstanceUid(), frames_->GetSeriesInstanceUid(),
+            sopInstanceUid, false /* transcoding (TODO) */,
+            Orthanc::DicomTransferSyntax_LittleEndianExplicit /* TODO */,
+            new SetFullDicomFrame(GetSharedObserver(), sopInstanceUid, frame, isPrefetch)));
+      }
+    }
+  }
+
+  void ScheduleLoadRenderedFrame(size_t index,
+                                 int priority,
+                                 bool isPrefetch)
+  {
+    if (!source_.HasDicomWebRendered())
+    {
+      ScheduleLoadFullDicomFrame(index, priority, isPrefetch);
+    }
+    else if (frames_.get() != NULL)
+    {
+      std::string sopInstanceUid = frames_->GetFrameSopInstanceUid(index);
+      unsigned int frame = frames_->GetFrameIndex(index);
+      bool isMonochrome1 = frames_->IsFrameMonochrome1(index);
+
+      const std::string uri = ("studies/" + frames_->GetStudyInstanceUid() +
+                               "/series/" + frames_->GetSeriesInstanceUid() +
+                               "/instances/" + sopInstanceUid +
+                               "/frames/" + boost::lexical_cast<std::string>(frame + 1) + "/rendered");
+
+      std::map<std::string, std::string> headers, arguments;
+      arguments["window"] = (
+        boost::lexical_cast<std::string>(defaultWindowingCenter_) + ","  +
+        boost::lexical_cast<std::string>(defaultWindowingWidth_) + ",linear");
+
+      std::unique_ptr<OrthancStone::IOracleCommand> command(
+        source_.CreateDicomWebCommand(
+          uri, arguments, headers, new SetLowQualityFrame(
+            GetSharedObserver(), sopInstanceUid, frame,
+            defaultWindowingCenter_, defaultWindowingWidth_, isMonochrome1, isPrefetch)));
+
+      {
+        std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock());
+        lock->Schedule(GetSharedObserver(), priority, command.release());
+      }
+    }
+  }
+
+  ViewerViewport(OrthancStone::ILoadersContext& context,
+                 const OrthancStone::DicomSource& source,
+                 const std::string& canvas,
+                 boost::shared_ptr<FramesCache> cache) :
+    context_(context),
+    source_(source),
+    viewport_(OrthancStone::WebGLViewport::Create(canvas)),
+    cache_(cache),
+    fitNextContent_(true),
+    isCtrlDown_(false)
+  {
+    if (!cache_)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+    
+    emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, OnWheel);
+    emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey);
+    emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnKey);
+
+    ResetDefaultWindowing();
+  }
+
+  static EM_BOOL OnKey(int eventType,
+                       const EmscriptenKeyboardEvent *event,
+                       void *userData)
+  {
+    /**
+     * WARNING: There is a problem with Firefox 71 that seems to mess
+     * the "ctrlKey" value.
+     **/
+    
+    ViewerViewport& that = *reinterpret_cast<ViewerViewport*>(userData);
+    that.isCtrlDown_ = event->ctrlKey;
+    return false;
+  }
+
+  
+  static EM_BOOL OnWheel(int eventType,
+                         const EmscriptenWheelEvent *wheelEvent,
+                         void *userData)
+  {
+    ViewerViewport& that = *reinterpret_cast<ViewerViewport*>(userData);
+
+    if (that.cursor_.get() != NULL)
+    {
+      if (wheelEvent->deltaY < 0)
+      {
+        that.ChangeFrame(that.isCtrlDown_ ? SeriesCursor::Action_FastMinus : SeriesCursor::Action_Minus);
+      }
+      else if (wheelEvent->deltaY > 0)
+      {
+        that.ChangeFrame(that.isCtrlDown_ ? SeriesCursor::Action_FastPlus : SeriesCursor::Action_Plus);
+      }
+    }
+    
+    return true;
+  }
+
+  void Handle(const OrthancStone::DicomResourcesLoader::SuccessMessage& message)
+  {
+    dynamic_cast<const ICommand&>(message.GetUserPayload()).Handle(message);
+  }
+
+  void Handle(const OrthancStone::HttpCommand::SuccessMessage& message)
+  {
+    dynamic_cast<const ICommand&>(message.GetOrigin().GetPayload()).Handle(message);
+  }
+
+  void Handle(const OrthancStone::ParseDicomSuccessMessage& message)
+  {
+    dynamic_cast<const ICommand&>(message.GetOrigin().GetPayload()).Handle(message);
+  }
+  
+public:
+  static boost::shared_ptr<ViewerViewport> Create(OrthancStone::ILoadersContext::ILock& lock,
+                                                  const OrthancStone::DicomSource& source,
+                                                  const std::string& canvas,
+                                                  boost::shared_ptr<FramesCache> cache)
+  {
+    boost::shared_ptr<ViewerViewport> viewport(
+      new ViewerViewport(lock.GetContext(), source, canvas, cache));
+
+    viewport->loader_ = OrthancStone::DicomResourcesLoader::Create(lock);
+    viewport->Register<OrthancStone::DicomResourcesLoader::SuccessMessage>(
+      *viewport->loader_, &ViewerViewport::Handle);
+
+    viewport->Register<OrthancStone::HttpCommand::SuccessMessage>(
+      lock.GetOracleObservable(), &ViewerViewport::Handle);
+
+    viewport->Register<OrthancStone::ParseDicomSuccessMessage>(
+      lock.GetOracleObservable(), &ViewerViewport::Handle);
+
+    return viewport;    
+  }
+
+  void SetFrames(OrthancStone::SortedFrames* frames)
+  {
+    if (frames == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+
+    fitNextContent_ = true;
+
+    frames_.reset(frames);
+    cursor_.reset(new SeriesCursor(frames_->GetFramesCount()));
+
+    LOG(INFO) << "Number of frames in series: " << frames_->GetFramesCount();
+
+    ResetDefaultWindowing();
+    ClearViewport();
+    prefetchQueue_.clear();
+    currentFrameGeometry_ = FrameGeometry();
+
+    if (observer_.get() != NULL)
+    {
+      observer_->SignalFrameUpdated(*this, cursor_->GetCurrentIndex(),
+                                    frames_->GetFramesCount(), DisplayedFrameQuality_None);
+    }
+    
+    if (frames_->GetFramesCount() != 0)
+    {
+      const std::string& sopInstanceUid = frames_->GetFrameSopInstanceUid(cursor_->GetCurrentIndex());
+
+      {
+        // Fetch the default windowing for the central instance
+        const std::string uri = ("studies/" + frames_->GetStudyInstanceUid() +
+                                 "/series/" + frames_->GetSeriesInstanceUid() +
+                                 "/instances/" + sopInstanceUid + "/metadata");
+        
+        loader_->ScheduleGetDicomWeb(
+          boost::make_shared<OrthancStone::LoadedDicomResources>(Orthanc::DICOM_TAG_SOP_INSTANCE_UID),
+          0, source_, uri, new SetDefaultWindowingCommand(GetSharedObserver()));
+      }
+    }
+  }
+
+  // This method is used when the layout of the HTML page changes,
+  // which does not trigger the "emscripten_set_resize_callback()"
+  void UpdateSize(bool fitContent)
+  {
+    std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
+    lock->GetCompositor().RefreshCanvasSize();
+
+    if (fitContent)
+    {
+      lock->GetCompositor().FitContent(lock->GetController().GetScene());
+    }
+
+    lock->Invalidate();
+  }
+
+  void AcquireObserver(IObserver* observer)
+  {  
+    observer_.reset(observer);
+  }
+
+  const std::string& GetCanvasId() const
+  {
+    assert(viewport_);
+    return viewport_->GetCanvasId();
+  }
+
+  void ChangeFrame(SeriesCursor::Action action)
+  {
+    if (cursor_.get() != NULL)
+    {
+      size_t previous = cursor_->GetCurrentIndex();
+      
+      cursor_->Apply(action);
+      
+      size_t current = cursor_->GetCurrentIndex();
+      if (previous != current)
+      {
+        DisplayCurrentFrame();
+      }
+    }
+  }
+
+  const FrameGeometry& GetCurrentFrameGeometry() const
+  {
+    return currentFrameGeometry_;
+  }
+
+  void UpdateReferenceLines(const std::list<const FrameGeometry*>& planes)
+  {
+    std::unique_ptr<OrthancStone::PolylineSceneLayer> layer(new OrthancStone::PolylineSceneLayer);
+    
+    if (GetCurrentFrameGeometry().IsValid())
+    {
+      for (std::list<const FrameGeometry*>::const_iterator
+             it = planes.begin(); it != planes.end(); ++it)
+      {
+        assert(*it != NULL);
+        
+        double x1, y1, x2, y2;
+        if (GetCurrentFrameGeometry().Intersect(x1, y1, x2, y2, **it))
+        {
+          OrthancStone::PolylineSceneLayer::Chain chain;
+          chain.push_back(OrthancStone::ScenePoint2D(x1, y1));
+          chain.push_back(OrthancStone::ScenePoint2D(x2, y2));
+          layer->AddChain(chain, false, 0, 255, 0);
+        }
+      }
+    }
+    
+    {
+      std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
+
+      if (layer->GetChainsCount() == 0)
+      {
+        lock->GetController().GetScene().DeleteLayer(LAYER_REFERENCE_LINES);
+      }
+      else
+      {
+        lock->GetController().GetScene().SetLayer(LAYER_REFERENCE_LINES, layer.release());
+      }
+      
+      //lock->GetCompositor().Refresh(lock->GetController().GetScene());
+      lock->Invalidate();
+    }
+  }
+
+
+  void ClearReferenceLines()
+  {
+    {
+      std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
+      lock->GetController().GetScene().DeleteLayer(LAYER_REFERENCE_LINES);
+      lock->Invalidate();
+    }
+  }
+
+
+  void SetDefaultWindowing()
+  {
+    SetWindowing(defaultWindowingCenter_, defaultWindowingWidth_);
+  }
+
+  void SetWindowing(float windowingCenter,
+                    float windowingWidth)
+  {
+    windowingCenter_ = windowingCenter;
+    windowingWidth_ = windowingWidth;
+
+    {
+      std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
+
+      if (lock->GetController().GetScene().HasLayer(LAYER_TEXTURE) &&
+          lock->GetController().GetScene().GetLayer(LAYER_TEXTURE).GetType() ==
+          OrthancStone::ISceneLayer::Type_FloatTexture)
+      {
+        dynamic_cast<OrthancStone::FloatTextureSceneLayer&>(
+          lock->GetController().GetScene().GetLayer(LAYER_TEXTURE)).
+          SetCustomWindowing(windowingCenter_, windowingWidth_);
+        lock->Invalidate();
+      }
+    }
+  }
+
+  void Invert()
+  {
+    inverted_ = !inverted_;
+    
+    {
+      std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
+
+      if (lock->GetController().GetScene().HasLayer(LAYER_TEXTURE) &&
+          lock->GetController().GetScene().GetLayer(LAYER_TEXTURE).GetType() ==
+          OrthancStone::ISceneLayer::Type_FloatTexture)
+      {
+        OrthancStone::FloatTextureSceneLayer& layer = 
+          dynamic_cast<OrthancStone::FloatTextureSceneLayer&>(
+            lock->GetController().GetScene().GetLayer(LAYER_TEXTURE));
+
+        // NB: Using "IsInverted()" instead of "inverted_" is for
+        // compatibility with MONOCHROME1 images
+        layer.SetInverted(!layer.IsInverted());
+        lock->Invalidate();
+      }
+    }
+  }
+};
+
+
+
+
+
+typedef std::map<std::string, boost::shared_ptr<ViewerViewport> >  Viewports;
+static Viewports allViewports_;
+static bool showReferenceLines_ = true;
+
+
+static void UpdateReferenceLines()
+{
+  if (showReferenceLines_)
+  {
+    std::list<const FrameGeometry*> planes;
+    
+    for (Viewports::const_iterator it = allViewports_.begin(); it != allViewports_.end(); ++it)
+    {
+      assert(it->second != NULL);
+      planes.push_back(&it->second->GetCurrentFrameGeometry());
+    }
+
+    for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it)
+    {
+      assert(it->second != NULL);
+      it->second->UpdateReferenceLines(planes);
+    }
+  }
+  else
+  {
+    for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it)
+    {
+      assert(it->second != NULL);
+      it->second->ClearReferenceLines();
+    }
+  }
+}
+
+
+class WebAssemblyObserver : public ResourcesLoader::IObserver,
+                            public ViewerViewport::IObserver
+{
+public:
+  virtual void SignalResourcesLoaded() ORTHANC_OVERRIDE
+  {
+    DISPATCH_JAVASCRIPT_EVENT("ResourcesLoaded");
+  }
+
+  virtual void SignalSeriesThumbnailLoaded(const std::string& studyInstanceUid,
+                                           const std::string& seriesInstanceUid) ORTHANC_OVERRIDE
+  {
+    EM_ASM({
+        const customEvent = document.createEvent("CustomEvent");
+        customEvent.initCustomEvent("ThumbnailLoaded", false, false,
+                                    { "studyInstanceUid" : UTF8ToString($0),
+                                        "seriesInstanceUid" : UTF8ToString($1) });
+        window.dispatchEvent(customEvent);
+      },
+      studyInstanceUid.c_str(),
+      seriesInstanceUid.c_str());
+  }
+
+  virtual void SignalSeriesMetadataLoaded(const std::string& studyInstanceUid,
+                                          const std::string& seriesInstanceUid) ORTHANC_OVERRIDE
+  {
+    EM_ASM({
+        const customEvent = document.createEvent("CustomEvent");
+        customEvent.initCustomEvent("MetadataLoaded", false, false,
+                                    { "studyInstanceUid" : UTF8ToString($0),
+                                        "seriesInstanceUid" : UTF8ToString($1) });
+        window.dispatchEvent(customEvent);
+      },
+      studyInstanceUid.c_str(),
+      seriesInstanceUid.c_str());
+  }
+
+  virtual void SignalFrameUpdated(const ViewerViewport& viewport,
+                                  size_t currentFrame,
+                                  size_t countFrames,
+                                  DisplayedFrameQuality quality) ORTHANC_OVERRIDE
+  {
+    EM_ASM({
+        const customEvent = document.createEvent("CustomEvent");
+        customEvent.initCustomEvent("FrameUpdated", false, false,
+                                    { "canvasId" : UTF8ToString($0),
+                                        "currentFrame" : $1,
+                                        "framesCount" : $2,
+                                        "quality" : $3 });
+        window.dispatchEvent(customEvent);
+      },
+      viewport.GetCanvasId().c_str(),
+      static_cast<int>(currentFrame),
+      static_cast<int>(countFrames),
+      quality);
+
+
+    UpdateReferenceLines();
+  };
+};
+
+
+
+static OrthancStone::DicomSource source_;
+static boost::shared_ptr<FramesCache> cache_;
+static boost::shared_ptr<OrthancStone::WebAssemblyLoadersContext> context_;
+static std::string stringBuffer_;
+
+
+
+static void FormatTags(std::string& target,
+                       const Orthanc::DicomMap& tags)
+{
+  Orthanc::DicomArray arr(tags);
+  Json::Value v = Json::objectValue;
+
+  for (size_t i = 0; i < arr.GetSize(); i++)
+  {
+    const Orthanc::DicomElement& element = arr.GetElement(i);
+    if (!element.GetValue().IsBinary() &&
+        !element.GetValue().IsNull())
+    {
+      v[element.GetTag().Format()] = element.GetValue().GetContent();
+    }
+  }
+
+  target = v.toStyledString();
+}
+
+
+static ResourcesLoader& GetResourcesLoader()
+{
+  static boost::shared_ptr<ResourcesLoader>  resourcesLoader_;
+
+  if (!resourcesLoader_)
+  {
+    std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_->Lock());
+    resourcesLoader_ = ResourcesLoader::Create(*lock, source_);
+    resourcesLoader_->AcquireObserver(new WebAssemblyObserver);
+  }
+
+  return *resourcesLoader_;
+}
+
+
+static boost::shared_ptr<ViewerViewport> GetViewport(const std::string& canvas)
+{
+  Viewports::iterator found = allViewports_.find(canvas);
+  if (found == allViewports_.end())
+  {
+    std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_->Lock());
+    boost::shared_ptr<ViewerViewport> viewport(ViewerViewport::Create(*lock, source_, canvas, cache_));
+    viewport->AcquireObserver(new WebAssemblyObserver);
+    allViewports_[canvas] = viewport;
+    return viewport;
+  }
+  else
+  {
+    return found->second;
+  }
+}
+
+
+extern "C"
+{
+  int main(int argc, char const *argv[]) 
+  {
+    printf("OK\n");
+    Orthanc::InitializeFramework("", true);
+    Orthanc::Logging::EnableInfoLevel(true);
+    //Orthanc::Logging::EnableTraceLevel(true);
+
+    context_.reset(new OrthancStone::WebAssemblyLoadersContext(1, 4, 1));
+    cache_.reset(new FramesCache);
+    
+    DISPATCH_JAVASCRIPT_EVENT("StoneInitialized");
+  }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void SetOrthancRoot(const char* uri,
+                      int useRendered)
+  {
+    try
+    {
+      context_->SetLocalOrthanc(uri);  // For "source_.SetDicomWebThroughOrthancSource()"
+      source_.SetDicomWebSource(std::string(uri) + "/dicom-web");
+      source_.SetDicomWebRendered(useRendered != 0);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+  
+
+  EMSCRIPTEN_KEEPALIVE
+  void SetDicomWebServer(const char* serverName,
+                         int hasRendered)
+  {
+    try
+    {
+      source_.SetDicomWebThroughOrthancSource(serverName);
+      source_.SetDicomWebRendered(hasRendered != 0);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+  
+
+  EMSCRIPTEN_KEEPALIVE
+  void FetchAllStudies()
+  {
+    try
+    {
+      GetResourcesLoader().FetchAllStudies();
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+
+  EMSCRIPTEN_KEEPALIVE
+  void FetchStudy(const char* studyInstanceUid)
+  {
+    try
+    {
+      GetResourcesLoader().FetchStudy(studyInstanceUid);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+
+  EMSCRIPTEN_KEEPALIVE
+  void FetchSeries(const char* studyInstanceUid,
+                   const char* seriesInstanceUid)
+  {
+    try
+    {
+      GetResourcesLoader().FetchSeries(studyInstanceUid, seriesInstanceUid);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+  
+  EMSCRIPTEN_KEEPALIVE
+  int GetStudiesCount()
+  {
+    try
+    {
+      return GetResourcesLoader().GetStudiesCount();
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+    return 0;  // on exception
+  }
+  
+  EMSCRIPTEN_KEEPALIVE
+  int GetSeriesCount()
+  {
+    try
+    {
+      return GetResourcesLoader().GetSeriesCount();
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+    return 0;  // on exception
+  }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  const char* GetStringBuffer()
+  {
+    return stringBuffer_.c_str();
+  }
+  
+
+  EMSCRIPTEN_KEEPALIVE
+  void LoadStudyTags(int i)
+  {
+    try
+    {
+      if (i < 0)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+      
+      Orthanc::DicomMap dicom;
+      GetResourcesLoader().GetStudy(dicom, i);
+      FormatTags(stringBuffer_, dicom);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+  
+
+  EMSCRIPTEN_KEEPALIVE
+  void LoadSeriesTags(int i)
+  {
+    try
+    {
+      if (i < 0)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+      
+      Orthanc::DicomMap dicom;
+      GetResourcesLoader().GetSeries(dicom, i);
+      FormatTags(stringBuffer_, dicom);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+  
+
+  EMSCRIPTEN_KEEPALIVE
+  int LoadSeriesThumbnail(const char* seriesInstanceUid)
+  {
+    try
+    {
+      std::string image, mime;
+      switch (GetResourcesLoader().GetSeriesThumbnail(image, mime, seriesInstanceUid))
+      {
+        case OrthancStone::SeriesThumbnailType_Image:
+          Orthanc::Toolbox::EncodeDataUriScheme(stringBuffer_, mime, image);
+          return ThumbnailType_Image;
+          
+        case OrthancStone::SeriesThumbnailType_Pdf:
+          return ThumbnailType_Pdf;
+          
+        case OrthancStone::SeriesThumbnailType_Video:
+          return ThumbnailType_Video;
+          
+        case OrthancStone::SeriesThumbnailType_NotLoaded:
+          return ThumbnailType_Loading;
+          
+        case OrthancStone::SeriesThumbnailType_Unsupported:
+          return ThumbnailType_NoPreview;
+
+        default:
+          return ThumbnailType_Unknown;
+      }
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+    return ThumbnailType_Unknown;
+  }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void SpeedUpFetchSeriesMetadata(const char* studyInstanceUid,
+                                  const char* seriesInstanceUid)
+  {
+    try
+    {
+      GetResourcesLoader().FetchSeriesMetadata(PRIORITY_HIGH, studyInstanceUid, seriesInstanceUid);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  int IsSeriesComplete(const char* seriesInstanceUid)
+  {
+    try
+    {
+      return GetResourcesLoader().IsSeriesComplete(seriesInstanceUid) ? 1 : 0;
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+    return 0;
+  }
+
+  EMSCRIPTEN_KEEPALIVE
+  int LoadSeriesInViewport(const char* canvas,
+                           const char* seriesInstanceUid)
+  {
+    try
+    {
+      std::unique_ptr<OrthancStone::SortedFrames> frames(new OrthancStone::SortedFrames);
+      
+      if (GetResourcesLoader().SortSeriesFrames(*frames, seriesInstanceUid))
+      {
+        GetViewport(canvas)->SetFrames(frames.release());
+        return 1;
+      }
+      else
+      {
+        return 0;
+      }
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+    return 0;
+  }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void AllViewportsUpdateSize(int fitContent)
+  {
+    try
+    {
+      for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it)
+      {
+        assert(it->second != NULL);
+        it->second->UpdateSize(fitContent != 0);
+      }
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void DecrementFrame(const char* canvas,
+                      int fitContent)
+  {
+    try
+    {
+      GetViewport(canvas)->ChangeFrame(SeriesCursor::Action_Minus);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void IncrementFrame(const char* canvas,
+                      int fitContent)
+  {
+    try
+    {
+      GetViewport(canvas)->ChangeFrame(SeriesCursor::Action_Plus);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }  
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void ShowReferenceLines(int show)
+  {
+    try
+    {
+      showReferenceLines_ = (show != 0);
+      UpdateReferenceLines();
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }  
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void SetDefaultWindowing(const char* canvas)
+  {
+    try
+    {
+      GetViewport(canvas)->SetDefaultWindowing();
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }  
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void SetWindowing(const char* canvas,
+                    int center,
+                    int width)
+  {
+    try
+    {
+      GetViewport(canvas)->SetWindowing(center, width);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }  
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void InvertContrast(const char* canvas)
+  {
+    try
+    {
+      GetViewport(canvas)->Invert();
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }  
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/WebAssembly/docker-build.sh	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+set -ex
+
+IMAGE=jodogne/wasm-builder:1.39.17-upstream
+
+if [ "$1" != "Debug" -a "$1" != "Release" ]; then
+    echo "Please provide build type: Debug or Release"
+    exit -1
+fi
+
+if [ -t 1 ]; then
+    # TTY is available => use interactive mode
+    DOCKER_FLAGS='-i'
+fi
+
+ROOT_DIR=`dirname $(readlink -f $0)`/../..
+
+mkdir -p ${ROOT_DIR}/wasm-binaries
+
+docker run -t ${DOCKER_FLAGS} --rm \
+    --user $(id -u):$(id -g) \
+    -v ${ROOT_DIR}:/source:ro \
+    -v ${ROOT_DIR}/wasm-binaries:/target:rw ${IMAGE} \
+    bash /source/StoneWebViewer/WebAssembly/docker-internal.sh $1
+
+ls -lR ${ROOT_DIR}/wasm-binaries/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StoneWebViewer/WebAssembly/docker-internal.sh	Thu Jun 25 16:51:10 2020 +0200
@@ -0,0 +1,30 @@
+#!/bin/bash
+set -ex
+
+source /opt/emsdk/emsdk_env.sh
+
+# Use a folder that is writeable by non-root users for the Emscripten cache
+export EM_CACHE=/tmp/emscripten-cache
+
+# Get the Orthanc framework
+cd /tmp/
+hg clone https://hg.orthanc-server.com/orthanc/
+
+# Make a copy of the read-only folder containing the source code into
+# a writeable folder, because of "DownloadPackage.cmake" that writes
+# to the "ThirdPartyDownloads" folder next to the "CMakeLists.txt"
+cd /source
+hg clone /source /tmp/source-writeable
+
+mkdir /tmp/build
+cd /tmp/build
+
+cmake /tmp/source-writeable/StoneWebViewer/WebAssembly \
+      -DCMAKE_BUILD_TYPE=$1 \
+      -DCMAKE_INSTALL_PREFIX=/target/StoneWebViewer \
+      -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake \
+      -DORTHANC_FRAMEWORK_ROOT=/tmp/orthanc \
+      -DSTATIC_BUILD=ON \
+      -G Ninja
+
+ninja -j2 install