diff --git a/.gitignore b/.gitignore index 46d5f6c..a2dea96 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,14 @@ recticel/ .venv/ __pycache__/ app/__pycache__/ -tray/ jdk-11.0.20-full/ jdk-11.0.21+9-jre/ jre-11-windows.zip + +# QZ Tray build artifacts (keep source code, ignore builds) +tray/out/ +tray/lib/javafx* +tray/js/node_modules # Ignore generated or local files backup/backup/ diff --git a/tray/.github/workflows/build.yaml b/tray/.github/workflows/build.yaml new file mode 100644 index 0000000..787177a --- /dev/null +++ b/tray/.github/workflows/build.yaml @@ -0,0 +1,60 @@ +name: build + +on: [push, pull_request] + +jobs: + ubuntu: + runs-on: [ubuntu-latest] + strategy: + matrix: + java: [11, 21] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java }} + distribution: 'liberica' + - run: sudo apt-get install nsis makeself + - run: ant makeself + - run: sudo out/qz-tray-*.run + - run: /opt/qz-tray/qz-tray --version + - run: ant nsis + + macos: + runs-on: [macos-latest] + strategy: + matrix: + java: [11, 21] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java }} + distribution: 'liberica' + - run: brew install nsis makeself + - run: ant pkgbuild + - run: echo "Setting CA trust settings to 'allow' (https://github.com/actions/runner-images/issues/4519)" + - run: security authorizationdb read com.apple.trust-settings.admin > /tmp/trust-settings-backup.xml + - run: sudo security authorizationdb write com.apple.trust-settings.admin allow + - run: sudo installer -pkg out/qz-tray-*.pkg -target / + - run: echo "Restoring CA trust settings back to default" + - run: sudo security authorizationdb write com.apple.trust-settings.admin < /tmp/trust-settings-backup.xml + - run: "'/Applications/QZ Tray.app/Contents/MacOS/QZ Tray' --version" + - run: ant makeself + - run: ant nsis + + windows: + runs-on: [windows-latest] + strategy: + matrix: + java: [11, 21] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java }} + distribution: 'liberica' + - run: choco install nsis + - run: ant nsis + - run: Start-Process -Wait ./out/qz-tray-*.exe -ArgumentList "/S" + - run: "&'C:/Program Files/QZ Tray/qz-tray.exe' --wait --version|Out-Null" diff --git a/tray/.gitignore b/tray/.gitignore new file mode 100644 index 0000000..39cea5e --- /dev/null +++ b/tray/.gitignore @@ -0,0 +1,33 @@ +# Build outputs +/out/ +*.class + +# Node modules +/js/node_modules + +# JavaFX runtime (too large, should be downloaded) +/lib/javafx* + +# IDE files +/.idea/workspace.xml +/.idea/misc.xml +/.idea/uiDesigner.xml +/.idea/compiler.xml +.idea/ +*.iml +.vscode/ + +# OS files +.DS_Store +Thumbs.db +windows-debug-launcher.nsi.in + +# Build artifacts +/fx.zip +/provision.json + +# Private keys +/ant/private/qz.ks + +# Logs +*.log diff --git a/tray/Dockerfile b/tray/Dockerfile new file mode 100644 index 0000000..15c1973 --- /dev/null +++ b/tray/Dockerfile @@ -0,0 +1,14 @@ +FROM openjdk:11 as build +RUN apt-get update +RUN apt-get install -y ant nsis makeself +COPY . /usr/src/tray +WORKDIR /usr/src/tray +RUN ant makeself + +FROM openjdk:11-jre as install +RUN apt-get update +RUN apt-get install -y libglib2.0-bin +COPY --from=build /usr/src/tray/out/*.run /tmp +RUN find /tmp -iname "*.run" -exec {} \; +WORKDIR /opt/qz-tray +ENTRYPOINT ["/opt/qz-tray/qz-tray"] \ No newline at end of file diff --git a/tray/LICENSE.txt b/tray/LICENSE.txt new file mode 100644 index 0000000..1cc0a30 --- /dev/null +++ b/tray/LICENSE.txt @@ -0,0 +1,601 @@ + ATTRIBUTION, LICENSING AND SUMMARY OF COMPONENTS + Version 1.2, February 2016 + +Project Source Code (unless otherwise specified): + Copyright (c) 2013-2016 QZ Industries, LLC + LGPL-2.1 License (attached) + https://qz.io + +All API Examples (unless otherwise specified): + Covers: JavaScript examples, Wiki API Examples, Signing API Examples + Public Domain (no restrictions) +______________________________________________________________________ + +Other licenses: + +jOOR Reflection Library (As-Is, No Modifications) + Copyright (c) 2011-2012, Lukas Eder, lukas.eder@gmail.com + Apache License, Version 2.0 (attached), with Copyright Notice + https://github.com/jOOQ/jOOR + + +jetty Web Server Library (As-Is, No Modifications) + Copyright (c) 1995-2014 Eclipse Foundation + Apache License, Version 2.0 (attached), with Copyright Notice + http://eclipse.org/jetty/ + + +Apache log4j (As-Is, No Modifications) + Copyright (C) 1999-2005 The Apache Software Foundation + Apache License, Version 2.0 (attached), with Copyright Notice + https://logging.apache.org/ + + +Apache PDFBox (As-Is, No Modifications) + Copyright (C) 2009–2015 The Apache Software Foundation + Apache License, Version 2.0 (attached), with Copyright Notice + https://pdfbox.apache.org/ + + +jSSC Library (As-Is, No Modifications) + Copyright (c) 2010-2013 Alexey Sokolov (scream3r) + LGPL-2.1 License (attached), with Copyright notice + https://code.google.com/p/java-simple-serial-connector/ + + +hid4java (As-Is, No Modifications) + Copyright (c) 2014 Gary Rowe + MIT License (attached), with Copyright notice + https://github.com/gary-rowe/hid4java + + +jsemver (As-Is, No Modifications) + Copyright 2012-2014 Zafar Khaja + MIT License (attached), with Copyright notice + https://github.com/zafarkhaja/jsemver +______________________________________________________________________ + + +LGPL 2.1 +Applies ONLY to: qz-tray, jssc + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +END OF LGPL 2.1 + +______________________________________________________________________ + + +Apache 2.0 +Applies ONLY to: joor, jetty, Apache PDFBox, Apache log4j + + + APACHE LICENSE + Version 2.0, January 2004 + + Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +END OF Apache 2.0 + +______________________________________________________________________ + + +MIT License +Applies ONLY to: hid4java, jsemver + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +END OF MIT License + +______________________________________________________________________ + +END OF ATTRIBUTION, LICENSING AND SUMMARY OF QZ-TRAY COMPONENTS \ No newline at end of file diff --git a/tray/README.md b/tray/README.md new file mode 100644 index 0000000..1509396 --- /dev/null +++ b/tray/README.md @@ -0,0 +1,23 @@ +QZ Tray +======== + + [![Build Status](https://github.com/qzind/tray/actions/workflows/build.yaml/badge.svg)](../../actions) [![Downloads](https://img.shields.io/github/downloads/qzind/tray/latest/total.svg)](../../releases) [![Issues](https://img.shields.io/github/issues/qzind/tray.svg)](../../issues) [![Commits](https://img.shields.io/github/commit-activity/m/qzind/tray.svg)](../../commits) + +Browser plugin for sending documents and raw commands to a printer or attached device + +## Getting Started + * Download here https://qz.io/download/ + * See our [Getting Started](../../wiki/getting-started) guide. + * Visit our home page https://qz.io. + +## Support + * File a bug via our [issue tracker](../../issues) + * Ask the community via our [community support page](https://qz.io/support/) + * Ask the developers via [premium support](https://qz.io/contact/) (fees may apply) + +## Changelog + * See our [most recent releases](../../releases) + +## Java Developer Resources + * [Install dependencies](../../wiki/install-dependencies) + * [Compile, Package](../../wiki/compiling) diff --git a/tray/SUPPORT.md b/tray/SUPPORT.md new file mode 100644 index 0000000..a3b79e6 --- /dev/null +++ b/tray/SUPPORT.md @@ -0,0 +1,11 @@ +Please feel free to open bug reports on GitHub. Before opening an issue, we ask that you consider whether your issue is a support question, or a potential bug with the software. + +If you have a support question, first [check the FAQ](https://qz.io/wiki/faq) and the [wiki](https://qz.io/wiki/Home). If you cannot find a solution please reach out to one of the appropriate channels: + +### Community Support + +If you need assistance using the software and do not have a paid subscription, please reference our community support channel: https://qz.io/support/ + +### Premium Support + +If you have an active support license with QZ Industries, LLC, please send support requests to support@qz.io diff --git a/tray/ant/apple/appdmg.json.in b/tray/ant/apple/appdmg.json.in new file mode 100644 index 0000000..5fd595a --- /dev/null +++ b/tray/ant/apple/appdmg.json.in @@ -0,0 +1,12 @@ +{ + "title": "${project.name}", + "background": "${basedir}/ant/apple/dmg-background.png", + "icon-size": 128, + "contents": [ + { "x": 501, "y": 154, "type": "link", "path": "/Applications" }, + { "x": 179, "y": 154, "type": "file", "path": "${build.dir}/${project.name}.app" } + ], + "code-sign": { + "signing-identity" : "${codesign.activeid}" + } +} diff --git a/tray/ant/apple/apple-bundle.plist.in b/tray/ant/apple/apple-bundle.plist.in new file mode 100644 index 0000000..5044c4c --- /dev/null +++ b/tray/ant/apple/apple-bundle.plist.in @@ -0,0 +1,28 @@ + + + + CFBundleDevelopmentRegionEnglish + CFBundleIconFile${project.filename} + CFBundleIdentifier${apple.bundleid} + CFBundlePackageTypeAPPL + CFBundleGetInfoString${project.name} ${build.version} + CFBundleSignature${project.name} + CFBundleExecutable${project.name} + CFBundleVersion${build.version} + CFBundleShortVersionString${build.version} + CFBundleName${project.name} + CFBundleInfoDictionaryVersion6.0 + CFBundleURLTypes + + + CFBundleURLName + ${project.name} + CFBundleURLSchemes + ${vendor.name} + + + LSArchitecturePriority + + ${apple.target.arch} + + \ No newline at end of file diff --git a/tray/ant/apple/apple-entitlements.plist.in b/tray/ant/apple/apple-entitlements.plist.in new file mode 100644 index 0000000..8ae1b0e --- /dev/null +++ b/tray/ant/apple/apple-entitlements.plist.in @@ -0,0 +1,30 @@ + + + + + com.apple.security.app-sandbox + <${build.sandboxed}/> + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.files.all + + com.apple.security.print + + com.apple.security.device.usb + + com.apple.security.device.bluetooth + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.debugger + + + \ No newline at end of file diff --git a/tray/ant/apple/apple-postinstall.sh.in b/tray/ant/apple/apple-postinstall.sh.in new file mode 100644 index 0000000..10bf706 --- /dev/null +++ b/tray/ant/apple/apple-postinstall.sh.in @@ -0,0 +1,23 @@ +#!/bin/bash + +# Halt on first error +set -e + +# Get working directory +DIR=$(cd "$(dirname "$0")" && pwd) +pushd "$DIR/payload/${project.name}.app/Contents/MacOS/" + +./"${project.name}" install >> "${install.log}" 2>&1 +popd + +# Use install target from pkgbuild, an undocumented feature; fallback on sane location +if [ -n "$2" ]; then + pushd "$2/Contents/MacOS/" +else + pushd "/Applications/${project.name}.app/Contents/MacOS/" +fi + +./"${project.name}" certgen >> "${install.log}" 2>&1 + +# Start qz by calling open on the .app as an ordinary user +su "$USER" -c "open ../../" || true \ No newline at end of file diff --git a/tray/ant/apple/apple-preinstall.sh.in b/tray/ant/apple/apple-preinstall.sh.in new file mode 100644 index 0000000..61091ae --- /dev/null +++ b/tray/ant/apple/apple-preinstall.sh.in @@ -0,0 +1,31 @@ +#!/bin/bash + +# Halt on first error +set -e + +# Clear the log for writing +> "${install.log}" + +# Log helper +dbg () { + echo -e "[BASH] $(date -Iseconds)\n\t$1" >> "${install.log}" 2>&1 +} + +# Get working directory +dbg "Calculating working directory..." +DIR=$(cd "$(dirname "$0")" && pwd) +dbg "Using working directory $DIR" +dbg "Switching to payload directory $DIR/payload/${project.name}.app/Contents/MacOS/" +pushd "$DIR/payload/${project.name}.app/Contents/MacOS/" >> "${install.log}" 2>&1 + +# Offer to download Java if missing +dbg "Checking for Java in payload directory..." +if ! ./"${project.name}" --version >> "${install.log}" 2>&1; then + dbg "Java was not found" + osascript -e "tell app \"Installer\" to display dialog \"Java is required. Please install Java and try again.\"" + sudo -u "$USER" open "${java.download}" + exit 1 +fi + +dbg "Java was found in payload directory, running preinstall" +./"${project.name}" preinstall >> "${install.log}" 2>&1 \ No newline at end of file diff --git a/tray/ant/apple/apple.properties b/tray/ant/apple/apple.properties new file mode 100644 index 0000000..63ee4a2 --- /dev/null +++ b/tray/ant/apple/apple.properties @@ -0,0 +1,6 @@ +# Apple build properties +apple.packager.signid=P5DMU6659X +# jdk9+ flags +# - Tray icon requires workaround https://github.com/dyorgio/macos-tray-icon-fixer/issues/9 +# - Dark theme requires workaround https://github.com/bobbylight/Darcula/issues/8 +apple.launch.jigsaw=--add-opens java.desktop/sun.lwawt.macosx=ALL-UNNAMED --add-opens java.desktop/java.awt=ALL-UNNAMED --add-exports java.desktop/com.apple.laf=ALL-UNNAMED \ No newline at end of file diff --git a/tray/ant/apple/certs/apple-codesign.cer b/tray/ant/apple/certs/apple-codesign.cer new file mode 100644 index 0000000..25c9d02 Binary files /dev/null and b/tray/ant/apple/certs/apple-codesign.cer differ diff --git a/tray/ant/apple/certs/apple-intermediate.cer b/tray/ant/apple/certs/apple-intermediate.cer new file mode 100644 index 0000000..8cbcf6f Binary files /dev/null and b/tray/ant/apple/certs/apple-intermediate.cer differ diff --git a/tray/ant/apple/certs/apple-packager.cer b/tray/ant/apple/certs/apple-packager.cer new file mode 100644 index 0000000..e9136d2 Binary files /dev/null and b/tray/ant/apple/certs/apple-packager.cer differ diff --git a/tray/ant/apple/dmg-background.png b/tray/ant/apple/dmg-background.png new file mode 100644 index 0000000..12bffe7 Binary files /dev/null and b/tray/ant/apple/dmg-background.png differ diff --git a/tray/ant/apple/dmg-background@2x.png b/tray/ant/apple/dmg-background@2x.png new file mode 100644 index 0000000..75a7684 Binary files /dev/null and b/tray/ant/apple/dmg-background@2x.png differ diff --git a/tray/ant/apple/installer.xml b/tray/ant/apple/installer.xml new file mode 100644 index 0000000..1d74f97 --- /dev/null +++ b/tray/ant/apple/installer.xml @@ -0,0 +1,376 @@ + + + + + + + + + Creating installer using pkgbuild + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @project.name@ @build.version@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creating app bundle + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copying native library files to libs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Verifying ${signed.bundle.name} Signature + Location: ${signed.bundle.dir} + + + + + + + + + + + + + ${message.description} + + + + + diff --git a/tray/ant/apple/pkg-background-blank.tiff b/tray/ant/apple/pkg-background-blank.tiff new file mode 100644 index 0000000..4121268 Binary files /dev/null and b/tray/ant/apple/pkg-background-blank.tiff differ diff --git a/tray/ant/apple/pkg-background.tiff b/tray/ant/apple/pkg-background.tiff new file mode 100644 index 0000000..e282f57 Binary files /dev/null and b/tray/ant/apple/pkg-background.tiff differ diff --git a/tray/ant/apple/product-def.plist.in b/tray/ant/apple/product-def.plist.in new file mode 100644 index 0000000..0868f44 --- /dev/null +++ b/tray/ant/apple/product-def.plist.in @@ -0,0 +1,10 @@ + + + + + arch + + ${apple.target.arch} + + + \ No newline at end of file diff --git a/tray/ant/javafx.xml b/tray/ant/javafx.xml new file mode 100644 index 0000000..bab48cc --- /dev/null +++ b/tray/ant/javafx.xml @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${host.fx.message} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${target.fx.message} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JavaFX host platform: + Version: ${host.fx.ver} (${host.os}, ${host.arch}) + Major Version: ${host.fx.majver} + URLs: "${host.fx.urlver}" + + JavaFX target platform: + Version: ${target.fx.ver} (${target.os}, ${target.arch}) + Major Version: ${target.fx.majver} + URLs: ""${target.fx.urlver}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Downloading JavaFX from ${fx.url} + Temporarily saving JavaFX to ${fx.zip} + + + + + + + diff --git a/tray/ant/lib/jsign-7.1.jar b/tray/ant/lib/jsign-7.1.jar new file mode 100644 index 0000000..38924c6 Binary files /dev/null and b/tray/ant/lib/jsign-7.1.jar differ diff --git a/tray/ant/lib/slim-icu.py b/tray/ant/lib/slim-icu.py new file mode 100644 index 0000000..1bd9e35 --- /dev/null +++ b/tray/ant/lib/slim-icu.py @@ -0,0 +1,109 @@ +# 2018 Yohanes Nugroho (@yohanes) +# +# 1. Download icu4j source code, build using ant. +# It will generate icu4j.jar and icu4j-charset.jar +# +# 2. Run slim-icu.py to generate slim version. +# +# To invoke from ant, add python to $PATH +# and add the following to build.xml: +# +# +# +# +# +# +# +# ... then call: ant distill-icu +# +# 3. Overwrite files in lib/charsets/ + +# slim ICU +import sys +import os +from pathlib import Path +import zipfile +from zipfile import ZipFile + +directory = str(Path(__file__).resolve().parent) +if len(sys.argv) > 1: + directory = sys.argv[1] + +mode = zipfile.ZIP_DEFLATED + + +def keep_file(filename): + # skip all break iterators + if filename.endswith(".brk") \ + or filename.endswith(".dict") \ + or filename.endswith("unames.icu") \ + or filename.endswith("ucadata.icu") \ + or filename.endswith(".spp"): + return False + + # keep english and arabic + if filename.startswith("en") \ + or filename.startswith("ar") \ + or not filename.endswith(".res"): + return True + + return False + + +zin = ZipFile(os.path.join(directory, 'icu4j.jar'), 'r') +zout = ZipFile(os.path.join(directory, 'icu4j-slim.jar'), 'w', mode) + +for item in zin.infolist(): + buff = zin.read(item.filename) + print(item.filename) + + if keep_file(item.filename): + print("Keep") + zout.writestr(item, buff) + else: + print("Remove") + +zout.close() +zin.close() + + +def keep_charset_file(filename): + to_remove = [ + "cns-11643-1992.cnv", + "ebcdic-xml-us.cnv", + "euc-jp-2007.cnv", + "euc-tw-2014.cnv", + "gb18030.cnv", + "ibm-1363_P11B-1998.cnv", + "ibm-1364_P110-2007.cnv", + "ibm-1371_P100-1999.cnv", + "ibm-1373_P100-2002.cnv", + "ibm-1375_P100-2008.cnv", + "ibm-1383_P110-1999.cnv", + "ibm-1386_P100-2001.cnv", + "ibm-1388_P103-2001.cnv", + "ibm-1390_P110-2003.cnv" + ] + + for i in to_remove: + if i in filename: + return False + + return True + + +zin = ZipFile(os.path.join(directory, 'icu4j-charset.jar'), 'r') +zout = ZipFile(os.path.join(directory, 'icu4j-charset-slim.jar'), 'w', mode) + +for item in zin.infolist(): + buff = zin.read(item.filename) + print(item.filename, end=' ') + + if keep_charset_file(item.filename): + print("Keep") + zout.writestr(item, buff) + else: + print("Remove") + +zout.close() +zin.close() diff --git a/tray/ant/linux/installer.xml b/tray/ant/linux/installer.xml new file mode 100644 index 0000000..08477c1 --- /dev/null +++ b/tray/ant/linux/installer.xml @@ -0,0 +1,69 @@ + + + + + + + + Creating installer using makeself + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copying native library files to libs + + + + + + + + + + + + + + + + + + + + + diff --git a/tray/ant/linux/linux-installer.sh.in b/tray/ant/linux/linux-installer.sh.in new file mode 100644 index 0000000..d5cdfcc --- /dev/null +++ b/tray/ant/linux/linux-installer.sh.in @@ -0,0 +1,68 @@ +#!/bin/bash + +# Halt on first error +set -e + +if [ "$(id -u)" != "0" ]; then + echo "This script must be run with root (sudo) privileges" 1>&2 + exit 1 +fi + +# Console colors +RED="\\x1B[1;31m";GREEN="\\x1B[1;32m";YELLOW="\\x1B[1;33m";PLAIN="\\x1B[0m" + +# Statuses +SUCCESS=" [${GREEN}success${PLAIN}]" +FAILURE=" [${RED}failure${PLAIN}]" +WARNING=" [${YELLOW}warning${PLAIN}]" + +mask=755 + +echo -e "Starting install...\n" + +# Clear the log for writing +> "${install.log}" + +run_task () { + echo -e "Running $1 task..." + if [ -n "$DEBUG" ]; then + "./${project.filename}" $@ && ret_val=$? || ret_val=$? + else + "./${project.filename}" $@ &>> "${install.log}" && ret_val=$? || ret_val=$? + fi + + if [ $ret_val -eq 0 ]; then + echo -e " $SUCCESS Task $1 was successful" + else + if [ "$1" == "spawn" ]; then + echo -e " $WARNING Task $1 skipped. You'll have to start ${project.name} manually." + return + fi + echo -e " $FAILURE Task $1 failed.\n\nRe-run with DEBUG=true for more information." + false # throw error + fi +} + +# Ensure java is installed and working before starting +"./${project.filename}" --version + +# Make a temporary jar for preliminary installation steps +run_task preinstall + +run_task install --dest "/opt/${project.filename}" + +# We should be installed now, generate the certificate +pushd "/opt/${project.filename}" &> /dev/null +run_task certgen + +# Tell the desktop to look for new mimetypes in the background +umask_bak="$(umask)" +umask 0002 # more permissive umask for mimetype registration +update-desktop-database &> /dev/null & +umask "$umask_bak" + +echo "Installation complete... Starting ${project.name}..." +# spawn itself as a regular user, inheriting environment +run_task spawn "/opt/${project.filename}/${project.filename}" + +popd &> /dev/null \ No newline at end of file diff --git a/tray/ant/linux/linux.properties b/tray/ant/linux/linux.properties new file mode 100644 index 0000000..5f06032 --- /dev/null +++ b/tray/ant/linux/linux.properties @@ -0,0 +1,2 @@ +# Expose UNIXToolkit.getGtkVersion +linux.launch.jigsaw=--add-opens java.desktop/sun.awt=ALL-UNNAMED \ No newline at end of file diff --git a/tray/ant/platform-detect.xml b/tray/ant/platform-detect.xml new file mode 100644 index 0000000..16feb06 --- /dev/null +++ b/tray/ant/platform-detect.xml @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${title} platform: + ${prefix}.os: "${prefix.os}" + ${prefix}.arch: "${prefix.arch}" + ${prefix}.libext: "${prefix.libext}" + ${os.echo}: true + ${arch.echo}: true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tray/ant/private/private.properties b/tray/ant/private/private.properties new file mode 100644 index 0000000..c36079e --- /dev/null +++ b/tray/ant/private/private.properties @@ -0,0 +1,5 @@ +signing.alias=self-signed +signing.keystore=ant/private/qz.ks +signing.keypass=jzebraonfire +signing.storepass=jzebraonfire +signing.algorithm=SHA-256 diff --git a/tray/ant/project.properties b/tray/ant/project.properties new file mode 100644 index 0000000..e4e3839 --- /dev/null +++ b/tray/ant/project.properties @@ -0,0 +1,62 @@ +vendor.name=qz +vendor.company=QZ Industries, LLC +vendor.website=https://qz.io +vendor.email=support@qz.io + +project.name=QZ Tray +project.filename=qz-tray +project.datadir=qz + +install.opts=-Djna.nosys=true +launch.opts=-Xms512m ${install.opts} +install.log=/tmp/${project.datadir}-install.log +# jdk9+ flags +# - Dark theme requires workaround https://github.com/bobbylight/Darcula/issues/8 +launch.jigsaw=--add-exports java.desktop/sun.swing=ALL-UNNAMED +launch.overrides=QZ_OPTS + +src.dir=${basedir}/src +out.dir=${basedir}/out +build.dir=${out.dir}/build +dist.dir=${out.dir}/dist + +sign.lib.dir=${out.dir}/jar-signed + +jar.compress=true +jar.index=true + +# Separate native lib resources from jars +separate.static.libs=true + +# See also qz.common.Constants.java +javac.source=11 +javac.target=11 +java.download=https://bell-sw.com/pages/downloads/#/java-11-lts + +# Java vendor to bundle into software (e.g. "*BellSoft|Adoptium|Microsoft|Amazon|IBM") +jlink.java.vendor="BellSoft" +# Java vendor to bundle into software (e.g. "11.0.17+7") +jlink.java.version="11.0.27+9" +# Java garbage collector flavor to use (e.g. "hotspot|openj9") +jlink.java.gc="hotspot" +# Java garbage collector version to use (e.g. openj9: "0.35.0", zulu: "11.62.17") +jlink.java.gc.version="gc-ver-is-empty" +# Bundle a locally built copy of Java instead +jlink.java.target=/home/ske087/quality_recticel/jdk-11.0.20-full + +# Skip bundling the java runtime +jre.skip=false + +# JavaFX version +javafx.version=19_monocle +javafx.mirror=https://download2.gluonhq.com/openjfx + +# Provisioning +# provision.file=${basedir}/provision.json +provision.dir=${dist.dir}/provision + +# Mask tray toggle (Apple only) +java.mask.tray=true + +# Workaround to delay expansion of $${foo} (e.g. shell scripts) +dollar=$ diff --git a/tray/ant/signing.xml b/tray/ant/signing.xml new file mode 100644 index 0000000..1b0b9b1 --- /dev/null +++ b/tray/ant/signing.xml @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Signing with hsm: ${hsm.keystore} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Signing without timestamp: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tsaurl was provided so the file was not timestamped. Users will not be able to validate + this file after the signer certificate's expiration date or after any future revocation date. + + + + \ No newline at end of file diff --git a/tray/ant/unix/unix-launcher.sh.in b/tray/ant/unix/unix-launcher.sh.in new file mode 100644 index 0000000..b56ea5c --- /dev/null +++ b/tray/ant/unix/unix-launcher.sh.in @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +# Shared launcher for MacOS and Linux +# Parameters -- if any -- are passed on to the app + +# Halt on first error +set -e + +# Configured by ant at build time +JAVA_MIN="${javac.target}" +LAUNCH_OPTS="${launch.opts}" +ABOUT_TITLE="${project.name}" +PROPS_FILE="${project.filename}" + +# Get working directory +DIR=$(cd "$(dirname "$0")" && pwd) +pushd "$DIR" &> /dev/null + +# Console colors +RED="\\x1B[1;31m";GREEN="\\x1B[1;32m";YELLOW="\\x1B[1;33m";PLAIN="\\x1B[0m" + +# Statuses +SUCCESS=" [${GREEN}success${PLAIN}]" +FAILURE=" [${RED}failure${PLAIN}]" +WARNING=" [${YELLOW}warning${PLAIN}]" +MESSAGE=" [${YELLOW}message${PLAIN}]" + +echo "Looking for Java..." + +# Honor JAVA_HOME +if [ -n "$JAVA_HOME" ]; then + echo -e "$WARNING JAVA_HOME was detected, using $JAVA_HOME..." + PATH="$JAVA_HOME/bin:$PATH" +fi + +# Always prefer relative runtime/jre +if [[ "$DIR" == *"/Contents/MacOS"* ]]; then + PATH="$DIR/../PlugIns/Java.runtime/Contents/Home/bin:$PATH" +else + PATH="$DIR/runtime/bin:$DIR/jre/bin:$PATH" +fi + +# Check for user overridable launch options +if [ -n "${dollar}${launch.overrides}" ]; then + echo -e "$MESSAGE Picked up additional launch options: ${dollar}${launch.overrides}" + LAUNCH_OPTS="$LAUNCH_OPTS ${dollar}${launch.overrides}" +fi + +# Fallback on some known locations +if ! command -v java > /dev/null ; then + if [[ "$OSTYPE" == "darwin"* ]]; then + # Apple: Fallback on system-wide install + DEFAULTS_READ=$(defaults read ${apple.bundleid} ${launch.overrides} 2>/dev/null) || true + if [ -n "$DEFAULTS_READ" ]; then + echo -e "$MESSAGE Picked up additional launch options: $DEFAULTS_READ" + LAUNCH_OPTS="$LAUNCH_OPTS $DEFAULTS_READ" + fi + MAC_PRIMARY="/usr/libexec/java_home" + MAC_FALLBACK="/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin" + echo "Trying $MAC_PRIMARY..." + if "$MAC_PRIMARY" -v $JAVA_MIN+ &>/dev/null; then + echo -e "$SUCCESS Using \"$MAC_PRIMARY -v $JAVA_MIN+ --exec\" to launch $ABOUT_TITLE" + java() { + "$MAC_PRIMARY" -v $JAVA_MIN+ --exec java "$@" + } + elif [ -d "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin" ]; then + echo -e "$WARNING No luck using $MAC_PRIMARY" + echo "Trying $MAC_FALLBACK..." + java() { + "$MAC_FALLBACK/java" "$@" + } + fi + else + # Linux/Unix: Fallback on known install location(s) + PATH="$PATH:/usr/java/latest/bin/" + fi +fi + +if command -v java > /dev/null ; then + echo -e "$SUCCESS Java was found: $(command -v java)" +else + echo -e "$FAILURE Please install Java $JAVA_MIN or higher to continue" + exit 1 +fi + +# Verify the bundled Java version actually works +if test -f "$DIR/runtime/bin/java" ; then + echo "Verifying the bundled Java version can run on this platform..." + if "$DIR/runtime/bin/java" -version &> /dev/null ; then + echo -e "$SUCCESS Bundled Java version is OK" + else + echo -e "$FAILURE Sorry, this version of $ABOUT_TITLE cannot be installed on this system:\n" + file "$DIR/runtime/bin/java" + exit 1 + fi +fi + +# Make sure Java version is sufficient +echo "Verifying the Java version is $JAVA_MIN+..." +curver=$(java -version 2>&1 | grep -i version | awk -F"\"" '{ print $2 }' | awk -F"." '{ print $1 "." $2 }') +minver="$JAVA_MIN" +if [ -z "$curver" ]; then + curver="0.0" +fi +desired=$(echo -e "$minver\n$curver") +actual=$(echo "$desired" |sort -t '.' -k 1,1 -k 2,2 -n) +if [ "$desired" != "$actual" ]; then + echo -e "$FAILURE Please install Java $JAVA_MIN or higher to continue" + exit 1 +else + echo -e "$SUCCESS Java $curver was detected" +fi + +jigsaw=$(echo -e "9.0\n$curver") +actual=$(echo "$jigsaw" |sort -t '.' -k 1,1 -k 2,2 -n) +if [ "$jigsaw" != "$actual" ]; then + echo -e "$SUCCESS Java < 9.0, skipping jigsaw options" +else + echo -e "$SUCCESS Java >= 9.0, adding jigsaw options" + LAUNCH_OPTS="$LAUNCH_OPTS ${launch.jigsaw}" + if [[ "$OSTYPE" == "darwin"* ]]; then + LAUNCH_OPTS="$LAUNCH_OPTS ${apple.launch.jigsaw}" + else + LAUNCH_OPTS="$LAUNCH_OPTS ${linux.launch.jigsaw}" + fi +fi + +if command -v java &>/dev/null; then + echo -e "$ABOUT_TITLE is starting..." + if [[ "$OSTYPE" == "darwin"* ]]; then + java $LAUNCH_OPTS -Xdock:name="$ABOUT_TITLE" -Xdock:icon="$DIR/../Resources/$PROPS_FILE.icns" -jar -Dapple.awt.UIElement="true" -Dapple.awt.enableTemplateImages="${java.mask.tray}" -Dapple.awt.application.appearance="system" "$DIR/../Resources/${prefix}$PROPS_FILE.jar" -NSRequiresAquaSystemAppearance False "$@" + else + java $LAUNCH_OPTS -jar "$PROPS_FILE.jar" "$@" + fi +else + echo -e "$FAILURE Java $JAVA_MIN+ was not found" +fi + +popd &>/dev/null \ No newline at end of file diff --git a/tray/ant/unix/unix-uninstall.sh.in b/tray/ant/unix/unix-uninstall.sh.in new file mode 100644 index 0000000..deab4df --- /dev/null +++ b/tray/ant/unix/unix-uninstall.sh.in @@ -0,0 +1,38 @@ +#!/bin/bash + +# Halt on first error +set -e + +if [ "$(id -u)" != "0" ]; then + echo "This script must be run with root (sudo) privileges" 1>&2 + exit 1 +fi + +# Get working directory +DIR=$(cd "$(dirname "$0")" && pwd) +pushd "$DIR" + +echo "Running uninstall tasks..." +if [[ "$OSTYPE" == "darwin"* ]]; then + # Uninstall script is in "QZ Tray.app/Contents/Resources/uninstall" + # Calculate the path to "QZ Tray.app" + APP_DIR=$(cd "$(dirname "$0")/../.." && pwd) + + if [[ "$APP_DIR" != *".app" ]]; then + echo -e "\nMalformed app directory. Uninstallation of ${project.name} failed.\n" + exit 1 + fi + # Launcher script is in "QZ Tray.app/Contents/MacOS" + "$APP_DIR/Contents/MacOS/${project.name}" uninstall +else + # Uninstall script is in root of app (e.g. "/opt/qz-tray") + APP_DIR="$DIR" + # Launcher script is adjacent to uninstall script + "$APP_DIR/${project.filename}" uninstall +fi + +echo "Deleting files..." +rm -rf "$APP_DIR" +echo -e "\nUninstall of ${project.name} complete.\n" + +popd &>/dev/null \ No newline at end of file diff --git a/tray/ant/version.xml b/tray/ant/version.xml new file mode 100644 index 0000000..a79d192 --- /dev/null +++ b/tray/ant/version.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + Version : ${build.version}${build.type} + Bundle Id : ${apple.bundleid} + + + \ No newline at end of file diff --git a/tray/ant/windows/installer.xml b/tray/ant/windows/installer.xml new file mode 100644 index 0000000..17504d1 --- /dev/null +++ b/tray/ant/windows/installer.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creating ${nsis.outfile} using ${nsisbin} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copying native library files to libs + + + + + + + + + + + + + + diff --git a/tray/ant/windows/nsis/Include/FindJava.nsh b/tray/ant/windows/nsis/Include/FindJava.nsh new file mode 100644 index 0000000..33bf986 --- /dev/null +++ b/tray/ant/windows/nsis/Include/FindJava.nsh @@ -0,0 +1,143 @@ +!include FileFunc.nsh +!include LogicLib.nsh +!include x64.nsh + +!include StrRep.nsh +!include IndexOf.nsh +!include StrTok.nsh + +; Resulting variable +Var /GLOBAL java +Var /GLOBAL javaw +Var /GLOBAL java_major + +; Constants +!define EXE "java.exe" + +!define ADOPT "SOFTWARE\Classes\AdoptOpenJDK.jarfile\shell\open\command" +!define ECLIPSE "SOFTWARE\Classes\Eclipse Adoptium.jarfile\shell\open\command" +!define ECLIPSE_OLD "SOFTWARE\Classes\Eclipse Foundation.jarfile\shell\open\command" + +!define JRE "Software\JavaSoft\Java Runtime Environment" +!define JRE32 "Software\Wow6432Node\JavaSoft\Java Runtime Environment" +!define JDK "Software\JavaSoft\JDK" +!define JDK32 "Software\Wow6432Node\JavaSoft\JDK" + +; Macros +!macro _ReadEclipseKey + ClearErrors + ReadRegStr $0 HKLM "${ECLIPSE}" "" + StrCpy $0 "$0" "" 1 ; Remove first double-quote + ${IndexOf} $1 $0 "$\"" ; Find the index of second double-quote + StrCpy $0 "$0" $1 ; Get the string section up to the index + IfFileExists "$0" Found +!macroend + +!macro _ReadEclipseOldKey + ClearErrors + ReadRegStr $0 HKLM "${ECLIPSE_OLD}" "" + StrCpy $0 "$0" "" 1 ; Remove first double-quote + ${IndexOf} $1 $0 "$\"" ; Find the index of second double-quote + StrCpy $0 "$0" $1 ; Get the string section up to the index + IfFileExists "$0" Found +!macroend + +!macro _ReadAdoptKey + ClearErrors + ReadRegStr $0 HKLM "${ADOPT}" "" + StrCpy $0 "$0" "" 1 ; Remove first double-quote + ${IndexOf} $1 $0 "$\"" ; Find the index of second double-quote + StrCpy $0 "$0" $1 ; Get the string section up to the index + IfFileExists "$0" Found +!macroend + +!macro _ReadReg key + ClearErrors + ReadRegStr $0 HKLM "${key}" "CurrentVersion" + ReadRegStr $0 HKLM "${key}\$0" "JavaHome" + IfErrors +2 0 + StrCpy $0 "$0\bin\${EXE}" + IfFileExists "$0" Found +!macroend + +!macro _ReadPayload root path + ClearErrors + StrCpy $0 "${root}\${path}\bin\${EXE}" + IfFileExists $0 Found +!macroend + +!macro _ReadWorking path + ClearErrors + StrCpy $0 "$EXEDIR\${path}\bin\${EXE}" + IfFileExists $0 Found +!macroend + +!macro _ReadEnv var + ClearErrors + ReadEnvStr $0 "${var}" + StrCpy $0 "$0\bin\${EXE}" + IfFileExists "$0" Found +!macroend + +; Create the shared function. +!macro _FindJava un + Function ${un}FindJava + ; Snag payload directory off the stack + exch $R0 + + ${If} ${RunningX64} + SetRegView 64 + ${EndIf} + + ; Check payload directories + !insertmacro _ReadPayload "$R0" "runtime" + + ; Check relative directories + !insertmacro _ReadWorking "runtime" + !insertmacro _ReadWorking "jre" + + ; Check common env vars + !insertmacro _ReadEnv "JAVA_HOME" + + ; Check registry + !insertmacro _ReadEclipseKey + !insertmacro _ReadEclipseOldKey + !insertmacro _ReadAdoptKey + !insertmacro _ReadReg "${JRE}" + !insertmacro _ReadReg "${JRE32}" + !insertmacro _ReadReg "${JDK}" + !insertmacro _ReadReg "${JDK32}" + + ; Give up. Use java.exe and hope it works + StrCpy $0 "${EXE}" + + ; Set global var + Found: + StrCpy $java $0 + ${StrRep} '$java' '$java' 'javaw.exe' '${EXE}' ; AdoptOpenJDK returns "javaw.exe" + ${StrRep} '$javaw' '$java' '${EXE}' 'javaw.exe' + + ; Discard payload directory + pop $R0 + + ; Detect java version + nsExec::ExecToStack '"$java" -version' + Pop $0 + Pop $1 + ; Isolate version number, e.g. "1.8.0" + ${StrTok} $0 "$1" "$\"" "1" "1" + ; Isolate major version + ${StrTok} $R0 "$0" "." "0" "1" + ; Handle old 1.x.x version format + ${If} "$R0" == "1" + ${StrTok} $R0 "$0" "." "1" "1" + ${EndIf} + + ; Convert to integer + IntOp $java_major $R0 + 0 + FunctionEnd +!macroend + +; Allows registering identical functions for install and uninstall +!insertmacro _FindJava "" +;!insertmacro _FindJava "un." \ No newline at end of file diff --git a/tray/ant/windows/nsis/Include/IndexOf.nsh b/tray/ant/windows/nsis/Include/IndexOf.nsh new file mode 100644 index 0000000..88f4d63 --- /dev/null +++ b/tray/ant/windows/nsis/Include/IndexOf.nsh @@ -0,0 +1,28 @@ +!define IndexOf "!insertmacro IndexOf" + +!macro IndexOf Var Str Char + Push "${Char}" + Push "${Str}" + + Exch $R0 + Exch + Exch $R1 + Push $R2 + Push $R3 + + StrCpy $R3 $R0 + StrCpy $R0 -1 + IntOp $R0 $R0 + 1 + StrCpy $R2 $R3 1 $R0 + StrCmp $R2 "" +2 + StrCmp $R2 $R1 +2 -3 + + StrCpy $R0 -1 + + Pop $R3 + Pop $R2 + Pop $R1 + Exch $R0 + + Pop "${Var}" +!macroend \ No newline at end of file diff --git a/tray/ant/windows/nsis/Include/SetTitleBar.nsh b/tray/ant/windows/nsis/Include/SetTitleBar.nsh new file mode 100644 index 0000000..c42dc34 --- /dev/null +++ b/tray/ant/windows/nsis/Include/SetTitleBar.nsh @@ -0,0 +1,5 @@ +; Allow title masquerading +!define SetTitleBar "!insertmacro SetTitleBar" +!macro SetTitlebar title + SendMessage $HWNDPARENT ${WM_SETTEXT} 0 "STR:${title}" +!macroend \ No newline at end of file diff --git a/tray/ant/windows/nsis/Include/StdUtils.nsh b/tray/ant/windows/nsis/Include/StdUtils.nsh new file mode 100644 index 0000000..537655d --- /dev/null +++ b/tray/ant/windows/nsis/Include/StdUtils.nsh @@ -0,0 +1,501 @@ +################################################################################# +# StdUtils plug-in for NSIS +# Copyright (C) 2004-2018 LoRd_MuldeR +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# http://www.gnu.org/licenses/lgpl-2.1.txt +################################################################################# + +# DEVELOPER NOTES: +# - Please see "https://github.com/lordmulder/stdutils/" for news and updates! +# - Please see "Docs\StdUtils\StdUtils.html" for detailed function descriptions! +# - Please see "Examples\StdUtils\StdUtilsTest.nsi" for usage examples! + +################################################################################# +# FUNCTION DECLARTIONS +################################################################################# + +!ifndef ___STDUTILS__NSH___ +!define ___STDUTILS__NSH___ + +!define StdUtils.Time '!insertmacro _StdU_Time' #time(), as in C standard library +!define StdUtils.GetMinutes '!insertmacro _StdU_GetMinutes' #GetSystemTimeAsFileTime(), returns the number of minutes +!define StdUtils.GetHours '!insertmacro _StdU_GetHours' #GetSystemTimeAsFileTime(), returns the number of hours +!define StdUtils.GetDays '!insertmacro _StdU_GetDays' #GetSystemTimeAsFileTime(), returns the number of days +!define StdUtils.Rand '!insertmacro _StdU_Rand' #rand(), as in C standard library +!define StdUtils.RandMax '!insertmacro _StdU_RandMax' #rand(), as in C standard library, with maximum value +!define StdUtils.RandMinMax '!insertmacro _StdU_RandMinMax' #rand(), as in C standard library, with minimum/maximum value +!define StdUtils.RandList '!insertmacro _StdU_RandList' #rand(), as in C standard library, with list support +!define StdUtils.RandBytes '!insertmacro _StdU_RandBytes' #Generates random bytes, returned as Base64-encoded string +!define StdUtils.FormatStr '!insertmacro _StdU_FormatStr' #sprintf(), as in C standard library, one '%d' placeholder +!define StdUtils.FormatStr2 '!insertmacro _StdU_FormatStr2' #sprintf(), as in C standard library, two '%d' placeholders +!define StdUtils.FormatStr3 '!insertmacro _StdU_FormatStr3' #sprintf(), as in C standard library, three '%d' placeholders +!define StdUtils.ScanStr '!insertmacro _StdU_ScanStr' #sscanf(), as in C standard library, one '%d' placeholder +!define StdUtils.ScanStr2 '!insertmacro _StdU_ScanStr2' #sscanf(), as in C standard library, two '%d' placeholders +!define StdUtils.ScanStr3 '!insertmacro _StdU_ScanStr3' #sscanf(), as in C standard library, three '%d' placeholders +!define StdUtils.TrimStr '!insertmacro _StdU_TrimStr' #Remove whitspaces from string, left and right +!define StdUtils.TrimStrLeft '!insertmacro _StdU_TrimStrLeft' #Remove whitspaces from string, left side only +!define StdUtils.TrimStrRight '!insertmacro _StdU_TrimStrRight' #Remove whitspaces from string, right side only +!define StdUtils.RevStr '!insertmacro _StdU_RevStr' #Reverse a string, e.g. "reverse me" <-> "em esrever" +!define StdUtils.ValidFileName '!insertmacro _StdU_ValidFileName' #Test whether string is a valid file name - no paths allowed +!define StdUtils.ValidPathSpec '!insertmacro _StdU_ValidPathSpec' #Test whether string is a valid full(!) path specification +!define StdUtils.ValidDomainName '!insertmacro _StdU_ValidDomain' #Test whether string is a valid host name or domain name +!define StdUtils.StrToUtf8 '!insertmacro _StdU_StrToUtf8' #Convert string from Unicode (UTF-16) or ANSI to UTF-8 bytes +!define StdUtils.StrFromUtf8 '!insertmacro _StdU_StrFromUtf8' #Convert string from UTF-8 bytes to Unicode (UTF-16) or ANSI +!define StdUtils.SHFileMove '!insertmacro _StdU_SHFileMove' #SHFileOperation(), using the FO_MOVE operation +!define StdUtils.SHFileCopy '!insertmacro _StdU_SHFileCopy' #SHFileOperation(), using the FO_COPY operation +!define StdUtils.AppendToFile '!insertmacro _StdU_AppendToFile' #Append contents of an existing file to another file +!define StdUtils.ExecShellAsUser '!insertmacro _StdU_ExecShlUser' #ShellExecute() as NON-elevated user from elevated installer +!define StdUtils.InvokeShellVerb '!insertmacro _StdU_InvkeShlVrb' #Invokes a "shell verb", e.g. for pinning items to the taskbar +!define StdUtils.ExecShellWaitEx '!insertmacro _StdU_ExecShlWaitEx' #ShellExecuteEx(), returns the handle of the new process +!define StdUtils.WaitForProcEx '!insertmacro _StdU_WaitForProcEx' #WaitForSingleObject(), e.g. to wait for a running process +!define StdUtils.GetParameter '!insertmacro _StdU_GetParameter' #Get the value of a specific command-line option +!define StdUtils.TestParameter '!insertmacro _StdU_TestParameter' #Test whether a specific command-line option has been set +!define StdUtils.ParameterCnt '!insertmacro _StdU_ParameterCnt' #Get number of command-line tokens, similar to argc in main() +!define StdUtils.ParameterStr '!insertmacro _StdU_ParameterStr' #Get the n-th command-line token, similar to argv[i] in main() +!define StdUtils.GetAllParameters '!insertmacro _StdU_GetAllParams' #Get complete command-line, but without executable name +!define StdUtils.GetRealOSVersion '!insertmacro _StdU_GetRealOSVer' #Get the *real* Windows version number, even on Windows 8.1+ +!define StdUtils.GetRealOSBuildNo '!insertmacro _StdU_GetRealOSBld' #Get the *real* Windows build number, even on Windows 8.1+ +!define StdUtils.GetRealOSName '!insertmacro _StdU_GetRealOSStr' #Get the *real* Windows version, as a "friendly" name +!define StdUtils.GetOSEdition '!insertmacro _StdU_GetOSEdition' #Get the Windows edition, i.e. "workstation" or "server" +!define StdUtils.GetOSReleaseId '!insertmacro _StdU_GetOSRelIdNo' #Get the Windows release identifier (on Windows 10) +!define StdUtils.GetOSReleaseName '!insertmacro _StdU_GetOSRelIdStr' #Get the Windows release (on Windows 10), as a "friendly" name +!define StdUtils.VerifyOSVersion '!insertmacro _StdU_VrfyRealOSVer' #Compare *real* operating system to an expected version number +!define StdUtils.VerifyOSBuildNo '!insertmacro _StdU_VrfyRealOSBld' #Compare *real* operating system to an expected build number +!define StdUtils.HashText '!insertmacro _StdU_HashText' #Compute hash from text string (CRC32, MD5, SHA1/2/3, BLAKE2) +!define StdUtils.HashFile '!insertmacro _StdU_HashFile' #Compute hash from file (CRC32, MD5, SHA1/2/3, BLAKE2) +!define StdUtils.NormalizePath '!insertmacro _StdU_NormalizePath' #Simplifies the path to produce a direct, well-formed path +!define StdUtils.GetParentPath '!insertmacro _StdU_GetParentPath' #Get parent path by removing the last component from the path +!define StdUtils.SplitPath '!insertmacro _StdU_SplitPath' #Split the components of the given path +!define StdUtils.GetDrivePart '!insertmacro _StdU_GetDrivePart' #Get drive component of path +!define StdUtils.GetDirectoryPart '!insertmacro _StdU_GetDirPart' #Get directory component of path +!define StdUtils.GetFileNamePart '!insertmacro _StdU_GetFNamePart' #Get file name component of path +!define StdUtils.GetExtensionPart '!insertmacro _StdU_GetExtnPart' #Get file extension component of path +!define StdUtils.TimerCreate '!insertmacro _StdU_TimerCreate' #Create a new event-timer that will be triggered periodically +!define StdUtils.TimerDestroy '!insertmacro _StdU_TimerDestroy' #Destroy a running timer created with TimerCreate() +!define StdUtils.ProtectStr '!insertmacro _StdU_PrtctStr' #Protect a given String using Windows' DPAPI +!define StdUtils.UnprotectStr '!insertmacro _StdU_UnprtctStr' #Unprotect a string that was protected via ProtectStr() +!define StdUtils.GetLibVersion '!insertmacro _StdU_GetLibVersion' #Get the current StdUtils library version (for debugging) +!define StdUtils.SetVerbose '!insertmacro _StdU_SetVerbose' #Enable or disable "verbose" mode (for debugging) + + +################################################################################# +# MACRO DEFINITIONS +################################################################################# + +!macro _StdU_Time out + StdUtils::Time /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetMinutes out + StdUtils::GetMinutes /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetHours out + StdUtils::GetHours /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetDays out + StdUtils::GetDays /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_Rand out + StdUtils::Rand /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_RandMax out max + push ${max} + StdUtils::RandMax /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_RandMinMax out min max + push ${min} + push ${max} + StdUtils::RandMinMax /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_RandList count max + push ${max} + push ${count} + StdUtils::RandList /NOUNLOAD +!macroend + +!macro _StdU_RandBytes out count + push ${count} + StdUtils::RandBytes /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_FormatStr out format val + push `${format}` + push ${val} + StdUtils::FormatStr /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_FormatStr2 out format val1 val2 + push `${format}` + push ${val1} + push ${val2} + StdUtils::FormatStr2 /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_FormatStr3 out format val1 val2 val3 + push `${format}` + push ${val1} + push ${val2} + push ${val3} + StdUtils::FormatStr3 /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_ScanStr out format input default + push `${format}` + push `${input}` + push ${default} + StdUtils::ScanStr /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_ScanStr2 out1 out2 format input default1 default2 + push `${format}` + push `${input}` + push ${default1} + push ${default2} + StdUtils::ScanStr2 /NOUNLOAD + pop ${out1} + pop ${out2} +!macroend + +!macro _StdU_ScanStr3 out1 out2 out3 format input default1 default2 default3 + push `${format}` + push `${input}` + push ${default1} + push ${default2} + push ${default3} + StdUtils::ScanStr3 /NOUNLOAD + pop ${out1} + pop ${out2} + pop ${out3} +!macroend + +!macro _StdU_TrimStr var + push ${var} + StdUtils::TrimStr /NOUNLOAD + pop ${var} +!macroend + +!macro _StdU_TrimStrLeft var + push ${var} + StdUtils::TrimStrLeft /NOUNLOAD + pop ${var} +!macroend + +!macro _StdU_TrimStrRight var + push ${var} + StdUtils::TrimStrRight /NOUNLOAD + pop ${var} +!macroend + +!macro _StdU_RevStr var + push ${var} + StdUtils::RevStr /NOUNLOAD + pop ${var} +!macroend + +!macro _StdU_ValidFileName out test + push `${test}` + StdUtils::ValidFileName /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_ValidPathSpec out test + push `${test}` + StdUtils::ValidPathSpec /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_ValidDomain out test + push `${test}` + StdUtils::ValidDomainName /NOUNLOAD + pop ${out} +!macroend + + +!macro _StdU_StrToUtf8 out str + push `${str}` + StdUtils::StrToUtf8 /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_StrFromUtf8 out trnc str + push ${trnc} + push `${str}` + StdUtils::StrFromUtf8 /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_SHFileMove out from to hwnd + push `${from}` + push `${to}` + push ${hwnd} + StdUtils::SHFileMove /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_SHFileCopy out from to hwnd + push `${from}` + push `${to}` + push ${hwnd} + StdUtils::SHFileCopy /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_AppendToFile out from dest offset maxlen + push `${from}` + push `${dest}` + push ${offset} + push ${maxlen} + StdUtils::AppendToFile /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_ExecShlUser out file verb args + push `${file}` + push `${verb}` + push `${args}` + StdUtils::ExecShellAsUser /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_InvkeShlVrb out path file verb_id + push "${path}" + push "${file}" + push ${verb_id} + StdUtils::InvokeShellVerb /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_ExecShlWaitEx out_res out_val file verb args + push `${file}` + push `${verb}` + push `${args}` + StdUtils::ExecShellWaitEx /NOUNLOAD + pop ${out_res} + pop ${out_val} +!macroend + +!macro _StdU_WaitForProcEx out handle + push `${handle}` + StdUtils::WaitForProcEx /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetParameter out name default + push `${name}` + push `${default}` + StdUtils::GetParameter /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_TestParameter out name + push `${name}` + StdUtils::TestParameter /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_ParameterCnt out + StdUtils::ParameterCnt /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_ParameterStr out index + push ${index} + StdUtils::ParameterStr /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetAllParams out truncate + push `${truncate}` + StdUtils::GetAllParameters /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetRealOSVer out_major out_minor out_spack + StdUtils::GetRealOsVersion /NOUNLOAD + pop ${out_major} + pop ${out_minor} + pop ${out_spack} +!macroend + +!macro _StdU_GetRealOSBld out + StdUtils::GetRealOsBuildNo /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetRealOSStr out + StdUtils::GetRealOsName /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_VrfyRealOSVer out major minor spack + push `${major}` + push `${minor}` + push `${spack}` + StdUtils::VerifyRealOsVersion /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_VrfyRealOSBld out build + push `${build}` + StdUtils::VerifyRealOsBuildNo /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetOSEdition out + StdUtils::GetOsEdition /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetOSRelIdNo out + StdUtils::GetOsReleaseId /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetOSRelIdStr out + StdUtils::GetOsReleaseName /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_HashText out type text + push `${type}` + push `${text}` + StdUtils::HashText /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_HashFile out type file + push `${type}` + push `${file}` + StdUtils::HashFile /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_NormalizePath out path + push `${path}` + StdUtils::NormalizePath /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetParentPath out path + push `${path}` + StdUtils::GetParentPath /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_SplitPath out_drive out_dir out_fname out_ext path + push `${path}` + StdUtils::SplitPath /NOUNLOAD + pop ${out_drive} + pop ${out_dir} + pop ${out_fname} + pop ${out_ext} +!macroend + +!macro _StdU_GetDrivePart out path + push `${path}` + StdUtils::GetDrivePart /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetDirPart out path + push `${path}` + StdUtils::GetDirectoryPart /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetFNamePart out path + push `${path}` + StdUtils::GetFileNamePart /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetExtnPart out path + push `${path}` + StdUtils::GetExtensionPart /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_TimerCreate out callback interval + GetFunctionAddress ${out} ${callback} + push ${out} + push ${interval} + StdUtils::TimerCreate /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_TimerDestroy out timer_id + push ${timer_id} + StdUtils::TimerDestroy /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_PrtctStr out dpsc salt text + push `${dpsc}` + push `${salt}` + push `${text}` + StdUtils::ProtectStr /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_UnprtctStr out trnc salt data + push `${trnc}` + push `${salt}` + push `${data}` + StdUtils::UnprotectStr /NOUNLOAD + pop ${out} +!macroend + +!macro _StdU_GetLibVersion out_ver out_tst + StdUtils::GetLibVersion /NOUNLOAD + pop ${out_ver} + pop ${out_tst} +!macroend + +!macro _StdU_SetVerbose enable + Push ${enable} + StdUtils::SetVerboseMode /NOUNLOAD +!macroend + + +################################################################################# +# MAGIC NUMBERS +################################################################################# + +!define StdUtils.Const.ShellVerb.PinToTaskbar 0 +!define StdUtils.Const.ShellVerb.UnpinFromTaskbar 1 +!define StdUtils.Const.ShellVerb.PinToStart 2 +!define StdUtils.Const.ShellVerb.UnpinFromStart 3 + +!endif # !___STDUTILS__NSH___ diff --git a/tray/ant/windows/nsis/Include/StrLoc.nsh b/tray/ant/windows/nsis/Include/StrLoc.nsh new file mode 100644 index 0000000..8adb1ca --- /dev/null +++ b/tray/ant/windows/nsis/Include/StrLoc.nsh @@ -0,0 +1,72 @@ +!define StrLoc "!insertmacro StrLoc" + +!macro StrLoc ResultVar String SubString StartPoint + Push "${String}" + Push "${SubString}" + Push "${StartPoint}" + Call StrLoc + Pop "${ResultVar}" +!macroend + +Function StrLoc +/*After this point: + ------------------------------------------ + $R0 = StartPoint (input) + $R1 = SubString (input) + $R2 = String (input) + $R3 = SubStringLen (temp) + $R4 = StrLen (temp) + $R5 = StartCharPos (temp) + $R6 = TempStr (temp)*/ + + ;Get input from user + Exch $R0 + Exch + Exch $R1 + Exch 2 + Exch $R2 + Push $R3 + Push $R4 + Push $R5 + Push $R6 + + ;Get "String" and "SubString" length + StrLen $R3 $R1 + StrLen $R4 $R2 + ;Start "StartCharPos" counter + StrCpy $R5 0 + + ;Loop until "SubString" is found or "String" reaches its end + ${Do} + ;Remove everything before and after the searched part ("TempStr") + StrCpy $R6 $R2 $R3 $R5 + + ;Compare "TempStr" with "SubString" + ${If} $R6 == $R1 + ${If} $R0 == `<` + IntOp $R6 $R3 + $R5 + IntOp $R0 $R4 - $R6 + ${Else} + StrCpy $R0 $R5 + ${EndIf} + ${ExitDo} + ${EndIf} + ;If not "SubString", this could be "String"'s end + ${If} $R5 >= $R4 + StrCpy $R0 `` + ${ExitDo} + ${EndIf} + ;If not, continue the loop + IntOp $R5 $R5 + 1 + ${Loop} + + ;Return output to user + Pop $R6 + Pop $R5 + Pop $R4 + Pop $R3 + Pop $R2 + Exch + Pop $R1 + Exch $R0 +FunctionEnd \ No newline at end of file diff --git a/tray/ant/windows/nsis/Include/StrRep.nsh b/tray/ant/windows/nsis/Include/StrRep.nsh new file mode 100644 index 0000000..57d1f3a --- /dev/null +++ b/tray/ant/windows/nsis/Include/StrRep.nsh @@ -0,0 +1,66 @@ +!define StrRep "!insertmacro StrRep" +!macro StrRep output string old new + Push `${string}` + Push `${old}` + Push `${new}` + ;!ifdef __UNINSTALL__ + ; Call un.StrRep + ;!else + Call StrRep + ;!endif + Pop ${output} +!macroend + +!macro Func_StrRep un + Function ${un}StrRep + Exch $R2 ;new + Exch 1 + Exch $R1 ;old + Exch 2 + Exch $R0 ;string + Push $R3 + Push $R4 + Push $R5 + Push $R6 + Push $R7 + Push $R8 + Push $R9 + + StrCpy $R3 0 + StrLen $R4 $R1 + StrLen $R6 $R0 + StrLen $R9 $R2 + loop: + StrCpy $R5 $R0 $R4 $R3 + StrCmp $R5 $R1 found + StrCmp $R3 $R6 done + IntOp $R3 $R3 + 1 ;move offset by 1 to check the next character + Goto loop + found: + StrCpy $R5 $R0 $R3 + IntOp $R8 $R3 + $R4 + StrCpy $R7 $R0 "" $R8 + StrCpy $R0 $R5$R2$R7 + StrLen $R6 $R0 + IntOp $R3 $R3 + $R9 ;move offset by length of the replacement string + Goto loop + done: + + Pop $R9 + Pop $R8 + Pop $R7 + Pop $R6 + Pop $R5 + Pop $R4 + Pop $R3 + Push $R0 + Push $R1 + Pop $R0 + Pop $R1 + Pop $R0 + Pop $R2 + Exch $R1 + FunctionEnd +!macroend +!insertmacro Func_StrRep "" +;!insertmacro Func_StrRep "un." \ No newline at end of file diff --git a/tray/ant/windows/nsis/Include/StrTok.nsh b/tray/ant/windows/nsis/Include/StrTok.nsh new file mode 100644 index 0000000..0c09ac7 --- /dev/null +++ b/tray/ant/windows/nsis/Include/StrTok.nsh @@ -0,0 +1,150 @@ +!define StrTok "!insertmacro StrTok" + +!macro StrTok ResultVar String Separators ResultPart SkipEmptyParts + Push "${String}" + Push "${Separators}" + Push "${ResultPart}" + Push "${SkipEmptyParts}" + Call StrTok + Pop "${ResultVar}" +!macroend + +Function StrTok +/*After this point: + ------------------------------------------ + $0 = SkipEmptyParts (input) + $1 = ResultPart (input) + $2 = Separators (input) + $3 = String (input) + $4 = SeparatorsLen (temp) + $5 = StrLen (temp) + $6 = StartCharPos (temp) + $7 = TempStr (temp) + $8 = CurrentLoop + $9 = CurrentSepChar + $R0 = CurrentSepCharNum + */ + + ;Get input from user + Exch $0 + Exch + Exch $1 + Exch + Exch 2 + Exch $2 + Exch 2 + Exch 3 + Exch $3 + Exch 3 + Push $4 + Push $5 + Push $6 + Push $7 + Push $8 + Push $9 + Push $R0 + + ;Parameter defaults + ${IfThen} $2 == `` ${|} StrCpy $2 `|` ${|} + ${IfThen} $1 == `` ${|} StrCpy $1 `L` ${|} + ${IfThen} $0 == `` ${|} StrCpy $0 `0` ${|} + + ;Get "String" and "Separators" length + StrLen $4 $2 + StrLen $5 $3 + ;Start "StartCharPos" and "ResultPart" counters + StrCpy $6 0 + StrCpy $8 -1 + + ;Loop until "ResultPart" is met, "Separators" is found or + ;"String" reaches its end + ResultPartLoop: ;"CurrentLoop" Loop + + ;Increase "CurrentLoop" counter + IntOp $8 $8 + 1 + + StrSearchLoop: + ${Do} ;"String" Loop + ;Remove everything before and after the searched part ("TempStr") + StrCpy $7 $3 1 $6 + + ;Verify if it's the "String" end + ${If} $6 >= $5 + ;If "CurrentLoop" is what the user wants, remove the part + ;after "TempStr" and itself and get out of here + ${If} $8 == $1 + ${OrIf} $1 == `L` + StrCpy $3 $3 $6 + ${Else} ;If not, empty "String" and get out of here + StrCpy $3 `` + ${EndIf} + StrCpy $R0 `End` + ${ExitDo} + ${EndIf} + + ;Start "CurrentSepCharNum" counter (for "Separators" Loop) + StrCpy $R0 0 + + ${Do} ;"Separators" Loop + ;Use one "Separators" character at a time + ${If} $R0 <> 0 + StrCpy $9 $2 1 $R0 + ${Else} + StrCpy $9 $2 1 + ${EndIf} + + ;Go to the next "String" char if it's "Separators" end + ${IfThen} $R0 >= $4 ${|} ${ExitDo} ${|} + + ;Or, if "TempStr" equals "CurrentSepChar", then... + ${If} $7 == $9 + StrCpy $7 $3 $6 + + ;If "String" is empty because this result part doesn't + ;contain data, verify if "SkipEmptyParts" is activated, + ;so we don't return the output to user yet + + ${If} $7 == `` + ${AndIf} $0 = 1 ;${TRUE} + IntOp $6 $6 + 1 + StrCpy $3 $3 `` $6 + StrCpy $6 0 + Goto StrSearchLoop + ${ElseIf} $8 == $1 + StrCpy $3 $3 $6 + StrCpy $R0 "End" + ${ExitDo} + ${EndIf} ;If not, go to the next result part + IntOp $6 $6 + 1 + StrCpy $3 $3 `` $6 + StrCpy $6 0 + Goto ResultPartLoop + ${EndIf} + + ;Increase "CurrentSepCharNum" counter + IntOp $R0 $R0 + 1 + ${Loop} + ${IfThen} $R0 == "End" ${|} ${ExitDo} ${|} + + ;Increase "StartCharPos" counter + IntOp $6 $6 + 1 + ${Loop} + +/*After this point: + ------------------------------------------ + $3 = ResultVar (output)*/ + + ;Return output to user + + Pop $R0 + Pop $9 + Pop $8 + Pop $7 + Pop $6 + Pop $5 + Pop $4 + Pop $0 + Pop $1 + Pop $2 + Exch $3 +FunctionEnd \ No newline at end of file diff --git a/tray/ant/windows/nsis/Plugins/Release_ANSI/AccessControl.dll b/tray/ant/windows/nsis/Plugins/Release_ANSI/AccessControl.dll new file mode 100644 index 0000000..0de0324 Binary files /dev/null and b/tray/ant/windows/nsis/Plugins/Release_ANSI/AccessControl.dll differ diff --git a/tray/ant/windows/nsis/Plugins/Release_ANSI/StdUtils.dll b/tray/ant/windows/nsis/Plugins/Release_ANSI/StdUtils.dll new file mode 100644 index 0000000..5317b6d Binary files /dev/null and b/tray/ant/windows/nsis/Plugins/Release_ANSI/StdUtils.dll differ diff --git a/tray/ant/windows/nsis/Plugins/Release_Unicode/AccessControl.dll b/tray/ant/windows/nsis/Plugins/Release_Unicode/AccessControl.dll new file mode 100644 index 0000000..cc27f63 Binary files /dev/null and b/tray/ant/windows/nsis/Plugins/Release_Unicode/AccessControl.dll differ diff --git a/tray/ant/windows/nsis/Plugins/Release_Unicode/StdUtils.dll b/tray/ant/windows/nsis/Plugins/Release_Unicode/StdUtils.dll new file mode 100644 index 0000000..6c852f4 Binary files /dev/null and b/tray/ant/windows/nsis/Plugins/Release_Unicode/StdUtils.dll differ diff --git a/tray/ant/windows/nsis/console.ico b/tray/ant/windows/nsis/console.ico new file mode 100644 index 0000000..1630b6e Binary files /dev/null and b/tray/ant/windows/nsis/console.ico differ diff --git a/tray/ant/windows/nsis/uninstall.ico b/tray/ant/windows/nsis/uninstall.ico new file mode 100644 index 0000000..dd5405c Binary files /dev/null and b/tray/ant/windows/nsis/uninstall.ico differ diff --git a/tray/ant/windows/nsis/welcome.bmp b/tray/ant/windows/nsis/welcome.bmp new file mode 100644 index 0000000..71c2da5 Binary files /dev/null and b/tray/ant/windows/nsis/welcome.bmp differ diff --git a/tray/ant/windows/windows-installer.nsi.in b/tray/ant/windows/windows-installer.nsi.in new file mode 100644 index 0000000..9b8f80d --- /dev/null +++ b/tray/ant/windows/windows-installer.nsi.in @@ -0,0 +1,132 @@ +!define MUI_BGCOLOR "SYSCLR:Window" +!define MUI_TEXTCOLOR "SYSCLR:WindowText" +!include MUI2.nsh +!include x64.nsh +!include LogicLib.nsh + +!ifdef NSIS_UNICODE + !addplugindir "${basedir}/ant/windows/nsis/Plugins/Release_Unicode" +!else + !addplugindir "${basedir}/ant/windows/nsis/Plugins/Release_ANSI" +!endif +!addincludedir "${basedir}/ant/windows/nsis/Include" +!include FindJava.nsh +!include StdUtils.nsh +!include StrLoc.nsh + +Name "${project.name}" +OutFile "${nsis.outfile}" +RequestExecutionLevel admin + +!define MUI_ICON "${basedir}/assets/branding/windows-icon.ico" + +; Branding for qz only +!if "${project.filename}" == "qz-tray" + !define MUI_WELCOMEFINISHPAGE_BITMAP "${basedir}\ant\windows\nsis\welcome.bmp" +!endif + +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_LANGUAGE "English" + +!macro QzInstaller step option value + SetDetailsPrint textonly + DetailPrint "Running ${step}..." + SetDetailsPrint listonly + DetailPrint 'Running ${step}: "$java" ${install.opts} -jar "$OUTDIR\${project.filename}.jar" "${step}" "${option}" "${value}"' + SetDetailsPrint both + ClearErrors + nsExec::ExecToLog '"$java" ${install.opts} -jar "$OUTDIR\${project.filename}.jar" "${step}" "${option}" "${value}"' + Pop $0 + ${If} "$0" != "0" + Abort "Installation failed during ${step} step. Please check log for details." + ${EndIf} +!macroend + +!macro VerifyJava jumpto + ; Test java executable + nsExec::ExecToLog '"$java" -version"' + Pop $0 + ${If} "$0" == "0" + Goto Resume + ${EndIf} + + ; Handle scenario where Java is bundled but broken + ${StrLoc} $R1 "$java" "runtime\bin" ">" ; e.g. "nss1234.tmp\payload\runtime\bin\java.exe" + ${If} $R1 != "" + SetDetailsPrint both + Abort "Sorry, this version of ${project.name} cannot be installed on this system." + ${EndIf} + + ; Offer to download Java if missing and non-silent install + ${IfNot} ${Silent} + MessageBox MB_YESNO "Java is required. Download now?" IDYES Website IDNO Resume + + ; Visit Java website + Website: + ExecShell "open" "${java.download}" + MessageBox MB_OK "Click OK after Java is installed" + + ; Check again for Java + Goto ${jumpto} + ${EndIf} + Resume: +!macroend + +Section + ; Set environmental variable for silent install to be picked up by Java + ${If} ${Silent} + System::Call 'Kernel32::SetEnvironmentVariable(t, t)i ("${vendor.name}_silent", "1").r0' + ${EndIf} + + ; Echo final destination to logs + SetOutPath $INSTDIR + + ; Copy files to a temporary location + SetOutPath "$PLUGINSDIR\payload" + DetailPrint "Extracting..." + SetDetailsPrint none ; Temporarily suppress details + File /r "${dist.dir}\*" + + ; Set the $java variable + TryAgain: + Push "$OUTDIR" + Call FindJava + !insertmacro VerifyJava "TryAgain" + + ; Run preinstall tasks + SetDetailsPrint both + !insertmacro QzInstaller "preinstall" "" "" + + ; Run install tasks + !insertmacro QzInstaller "install" "--dest" $INSTDIR + + ; Run certgen tasks + SetOutPath $INSTDIR + !insertmacro QzInstaller "certgen" "" "" + + ; Launch a non-elevated instance of ${project.name} + ${StdUtils.ExecShellAsUser} $0 "$INSTDIR\${project.filename}.exe" "open" "" +SectionEnd + +!macro Init + ${If} ${RunningX64} + SetRegView 64 + ${DisableX64FSRedirection} + ${EndIf} + ${If} $INSTDIR == "" + ${If} ${RunningX64} + StrCpy $INSTDIR "$PROGRAMFILES64\${project.name}" + ${Else} + StrCpy $INSTDIR "$PROGRAMFILES\${project.name}" + ${EndIf} + ${EndIf} +!macroend + +; Runs for installs +Function .onInit + !insertmacro Init +FunctionEnd diff --git a/tray/ant/windows/windows-launcher.nsi.in b/tray/ant/windows/windows-launcher.nsi.in new file mode 100644 index 0000000..9051491 --- /dev/null +++ b/tray/ant/windows/windows-launcher.nsi.in @@ -0,0 +1,91 @@ +!include x64.nsh +!include LogicLib.nsh + +!ifdef NSIS_UNICODE + !addplugindir "${basedir}/ant/windows/nsis/Plugins/Release_Unicode" +!else + !addplugindir "${basedir}/ant/windows/nsis/Plugins/Release_ANSI" +!endif +!addincludedir "${basedir}/ant/windows/nsis/Include" +!include StdUtils.nsh +!include StrLoc.nsh +!include FindJava.nsh + +!insertmacro GetParameters + +; Run this exe as non-admin +RequestExecutionLevel user + +; Application information +Name "${project.name}" +Caption "${project.name}" +Icon "${basedir}/assets/branding/windows-icon.ico" +OutFile "${nsis.outfile}" + +SilentInstall silent +AutoCloseWindow true +ShowInstDetails nevershow + +; Full path to jar +!define JAR "$EXEDIR/${project.filename}.jar" + +Section + ${If} ${RunningX64} + ${DisableX64FSRedirection} + ${EndIf} + SetOutPath $EXEDIR + + ; Get params to pass to jar + Var /GLOBAL params + ${GetParameters} $params + + ; Sets the $java variable + Push "$EXEDIR" + Call FindJava + + Var /GLOBAL opts + StrCpy $opts "${launch.opts}" + ; Handle jdk9+ flags + ${If} $java_major >= 9 + StrCpy $opts "${launch.opts} ${launch.jigsaw}" + ${EndIf} + + ; Check for user overridable launch options + ClearErrors + ReadEnvStr $R0 ${launch.overrides} + IfErrors +2 0 + StrCpy $opts "$opts $R0" + + Var /GLOBAL command + StrCpy $command '"$javaw" $opts -jar "${JAR}" $params' + + ; If ends in "-console.exe", use "cmd /s /k" to launch + ${StrLoc} $R1 "${nsis.outfile}" "-console.exe" "<" + ${If} $R1 == "0" + ExpandEnvStrings $R2 %COMSPEC% + StrCpy $command '"$R2" /s /k "$command"' + ${EndIf} + + ; Allow return of exit code + ${StrLoc} $R2 $params "--wait" "<" + ${If} $R2 != "" + ; Switch from /k to /c to capture exit code from -console.exe + ${StrRep} $command "$command" " /k " " /c " + ExecWait $command $R3 + ; Set error-level + SetErrorLevel $R3 + ${Else} + Exec $command + ${EndIf} + + ${If} ${RunningX64} + ${EnableX64FSRedirection} + ${EndIf} +SectionEnd + +Function .onInit + ${If} ${RunningX64} + SetRegView 64 + ${DisableX64FSRedirection} + ${EndIf} +FunctionEnd \ No newline at end of file diff --git a/tray/ant/windows/windows-uninstaller.nsi.in b/tray/ant/windows/windows-uninstaller.nsi.in new file mode 100644 index 0000000..e572ec1 --- /dev/null +++ b/tray/ant/windows/windows-uninstaller.nsi.in @@ -0,0 +1,117 @@ +!define MUI_BGCOLOR "SYSCLR:Window" +!define MUI_TEXTCOLOR "SYSCLR:WindowText" +!include MUI2.nsh +!include x64.nsh +!include LogicLib.nsh +!include FileFunc.nsh +!include WinMessages.nsh + +!ifdef NSIS_UNICODE + !addplugindir "${basedir}/ant/windows/nsis/Plugins/Release_Unicode" +!else + !addplugindir "${basedir}/ant/windows/nsis/Plugins/Release_ANSI" +!endif +!addincludedir "${basedir}/ant/windows/nsis/Include" +!include StdUtils.nsh +!include SetTitleBar.nsh +!include FindJava.nsh + +!define MUI_PRODUCT "${project.name}" +!define MUI_VERSION "${build.version}" +; Branding for qz only +!if "${project.filename}" == "qz-tray" + !define MUI_ICON "${basedir}/ant/windows/nsis/uninstall.ico" +!else + !define MUI_ICON "${basedir}/assets/branding/windows-icon.ico" +!endif + +!define MUI_PAGE_HEADER_TEXT "Uninstall ${project.name}" +!define MUI_PAGE_HEADER_SUBTEXT "Remove ${project.name} from your computer" +!define MUI_INSTFILESPAGE_FINISHHEADER_TEXT "Uninstallation Complete" +!define MUI_INSTFILESPAGE_FINISHHEADER_SUBTEXT "Uninstall was completed successfully." + +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_LANGUAGE "English" +!insertmacro GetParameters + +RequestExecutionLevel admin +CRCCheck On + +Name "Uninstall ${project.name}" +Caption "Uninstall ${project.name}" +Icon "${basedir}/ant/windows/nsis/uninstall.ico" +OutFile "${nsis.outfile}" + +Var /GLOBAL RESPAWN +Var /GLOBAL DELETE_DIR +Var /GLOBAL DELETE_EXE + +Section + ${SetTitlebar} "Uninstall" + SetDetailsPrint textonly + DetailPrint "Uninstalling" + SetDetailsPrint listonly + + ${GetParameters} $0 + ${GetOptions} "$0" "/RESPAWN=" $RESPAWN + ${GetOptions} "$0" "/DELETE_DIR=" $DELETE_DIR + ${GetOptions} "$0" "/DELETE_EXE=" $DELETE_EXE + + ${If} $RESPAWN != "" + ; We're running from $TEMP; Perform the uninstall + + ; Set environmental variable for silent uninstall to be picked up by Java + ${If} ${Silent} + System::Call 'Kernel32::SetEnvironmentVariable(t, t)i ("${vendor.name}_silent", "1").r0' + ${EndIf} + + ; Set $javaw variable + Push "$DELETE_DIR" + Call FindJava + + ; Run uninstall step using jar + SetDetailsPrint textonly + DetailPrint "Running uninstall..." + SetDetailsPrint none ; Temporarily suppress details + SetOutPath $DELETE_DIR + SetDetailsPrint listonly + DetailPrint 'Running uninstall: "$java" ${install.opts} -jar "$DELETE_DIR\${project.filename}.jar" uninstall' + ClearErrors + nsExec::ExecToLog '"$java" ${install.opts} -jar "$DELETE_DIR\${project.filename}.jar" uninstall' + Pop $0 + ${If} "$0" != "0" + Abort "Uninstall failed." + ${EndIf} + + ; Remove all files + DetailPrint "Removing remaining files..." + SetDetailsPrint none ; Temporarily suppress details + SetOutPath $TEMP + RMDir /r "$DELETE_DIR" + + ; Remove self from $TEMP after reboot + Delete /REBOOTOK $EXEPATH + + ${If} ${RunningX64} + ${EnableX64FSRedirection} + ${EndIf} + SetDetailsPrint both + ${Else} + ; We're NOT running from $TEMP, copy to temp and respawn ourself + GetTempFileName $0 + CopyFiles "$EXEPATH" "$0" + ${If} ${Silent} + Exec '"$0" /S /RESPAWN=1 /DELETE_DIR="$EXEDIR" /DELETE_EXE="$EXEPATH"' + ${Else} + Exec '"$0" /RESPAWN=1 /DELETE_DIR="$EXEDIR" /DELETE_EXE="$EXEPATH"' + ${EndIf} + Quit + ${EndIf} +SectionEnd + +Function .onInit + ${If} ${RunningX64} + SetRegView 64 + ${DisableX64FSRedirection} + ${EndIf} +FunctionEnd \ No newline at end of file diff --git a/tray/assets/branding/apple-icon.icns b/tray/assets/branding/apple-icon.icns new file mode 100644 index 0000000..a375406 Binary files /dev/null and b/tray/assets/branding/apple-icon.icns differ diff --git a/tray/assets/branding/linux-icon.svg b/tray/assets/branding/linux-icon.svg new file mode 100644 index 0000000..f672314 --- /dev/null +++ b/tray/assets/branding/linux-icon.svg @@ -0,0 +1,23 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/tray/assets/branding/windows-icon.ico b/tray/assets/branding/windows-icon.ico new file mode 100644 index 0000000..4b474ba Binary files /dev/null and b/tray/assets/branding/windows-icon.ico differ diff --git a/tray/assets/epl_multiples.txt b/tray/assets/epl_multiples.txt new file mode 100644 index 0000000..bd474b0 --- /dev/null +++ b/tray/assets/epl_multiples.txt @@ -0,0 +1,33 @@ + +N +q812;four inch width for 203dpi printer +Q1218,26;six inch height for 203dpi printer +B5,26,0,1A,3,7,152,B,"0001" +A310,26,0,3,1,1,N,"SKU 00001 MFG 0001" +A310,56,0,3,1,1,N,"QZ-Tray Java Application" +A310,86,0,3,1,1,N,"TEST PRINT SUCCESSFUL" +A310,116,0,3,1,1,N,"FROM SAMPLE.HTML" +A310,146,0,3,1,1,N,"EDIT EPL_MULTIPLES.TXT" +P1,1 + +N +q812 +Q1218,26 +B5,26,0,1A,3,7,152,B,"0002" +A310,26,0,3,1,1,N,"SKU 00002 MFG 0002" +A310,56,0,3,1,1,N,"QZ-Tray Java Application" +A310,86,0,3,1,1,N,"TEST PRINT SUCCESSFUL" +A310,116,0,3,1,1,N,"FROM SAMPLE.HTML" +A310,146,0,3,1,1,N,"EDIT EPL_MULTIPLES.TXT" +P1,1 + +N +q812 +Q1218,26 +B5,26,0,1A,3,7,152,B,"0003" +A310,26,0,3,1,1,N,"SKU 00003 MFG 0003" +A310,56,0,3,1,1,N,"QZ-Tray Java Application" +A310,86,0,3,1,1,N,"TEST PRINT SUCCESSFUL" +A310,116,0,3,1,1,N,"FROM SAMPLE.HTML" +A310,146,0,3,1,1,N,"EDIT EPL_MULTIPLES.TXT" +P1,1 diff --git a/tray/assets/epl_sample.txt b/tray/assets/epl_sample.txt new file mode 100644 index 0000000..c604175 --- /dev/null +++ b/tray/assets/epl_sample.txt @@ -0,0 +1,11 @@ + +N +q812 +Q1218,26; q and Q values edited to reflect 4x6 inch size at 203 dpi +B5,26,0,1A,3,7,152,B,"1234" +A310,26,0,3,1,1,N,"SKU 00000 MFG 0000" +A310,56,0,3,1,1,N,"QZ-Tray Java Application" +A310,86,0,3,1,1,N,"TEST PRINT SUCCESSFUL" +A310,116,0,3,1,1,N,"FROM SAMPLE.HTML" +A310,146,0,3,1,1,N,"EDIT EPL_SAMPLE.TXT" +P1,1 diff --git a/tray/assets/escpos_sample.bin b/tray/assets/escpos_sample.bin new file mode 100644 index 0000000..54f17f2 Binary files /dev/null and b/tray/assets/escpos_sample.bin differ diff --git a/tray/assets/fgl_sample.txt b/tray/assets/fgl_sample.txt new file mode 100644 index 0000000..e85379e --- /dev/null +++ b/tray/assets/fgl_sample.txt @@ -0,0 +1,25 @@ +BOCA +PROM= +GHOSTWRITER +FRIENDLY GHOST LANGUAGE +PLACE LETTERS ANYWHERE +VOID +TEST TICKET ONLY +*GHOST 123* + CODE 39 +TICKET & LABEL PRINTER +EXCELLENT PRINT QUALITY + Print any bar code +^CODE 128^ +CODE 128 +*MONKEY* +*MONKEY* + +SW1= SW2= SW3=LEFT = 1 RIGHT = 0 +Script printing is now standard. +High density printing is clear and readable +High density printing is clear and readable +Legal size printing + +

+ \ No newline at end of file diff --git a/tray/assets/img/fade-test.png b/tray/assets/img/fade-test.png new file mode 100644 index 0000000..d0b9561 Binary files /dev/null and b/tray/assets/img/fade-test.png differ diff --git a/tray/assets/img/image_sample.png b/tray/assets/img/image_sample.png new file mode 100644 index 0000000..13d145e Binary files /dev/null and b/tray/assets/img/image_sample.png differ diff --git a/tray/assets/img/image_sample_bw.png b/tray/assets/img/image_sample_bw.png new file mode 100644 index 0000000..2d0483b Binary files /dev/null and b/tray/assets/img/image_sample_bw.png differ diff --git a/tray/assets/pdf_sample.pdf b/tray/assets/pdf_sample.pdf new file mode 100644 index 0000000..ec66729 Binary files /dev/null and b/tray/assets/pdf_sample.pdf differ diff --git a/tray/assets/pgl_sample.txt b/tray/assets/pgl_sample.txt new file mode 100644 index 0000000..94f1652 --- /dev/null +++ b/tray/assets/pgl_sample.txt @@ -0,0 +1,12 @@ +~CREATE;DATAMATRIX;144 +SCALE;DOT;300;300 +ALPHA +POINT;50;100;16;9;*Printed using QZ Tray* +STOP +BARCODE +DATAMATRIX;XD16;C20;R20;ECC200;ID5;150;150 +*0100000123000017* +STOP +END +~EXECUTE;DATAMATRIX;1 +~NORMAL diff --git a/tray/assets/sbpl_sample.txt b/tray/assets/sbpl_sample.txt new file mode 100644 index 0000000..1b83e34 --- /dev/null +++ b/tray/assets/sbpl_sample.txt @@ -0,0 +1,23 @@ +AA1V01678H0812 +PS%0H0040V01510FW02H0735 +%2H200V1590P02RDB@1,057,056,SATO +%0H0040V01076FW02H0735 +%2H0353V01215P02RDB@0,026,025,BEST BEFORE +%2H0761V01215P02RDB@0,026,025,BATCH/LOT +%2H0761V01370P02RDB@0,026,025,CONTENT +%2H0761V01489P02RDB@0,026,025,SSCC +%2H0758V01590P02RDB@1,057,056,Product +%2H0777V01669P02RDB@0,057,056, +%2H0761V01183P02RDB@0,061,062,223344 +%2H0589V00109P02RDB@1,038,037,(00)038312345600000018 +%2H0741V01052BG04256>I>F0203831234560087370001 +%2H0719V00374BG04256>I>F00038312345600000018 +%2H0617V00786P02RDB@1,038,037,(02)03831234560087(37)0001 +%2H0139V01370P02RDB@0,026,025,COUNT +%2H0139V01344P02RDB@0,061,062,0001 +%2H0761V01344P02RDB@0,061,062,03831234560087 +%2H0761V01463P02RDB@1,061,062,038312345600000018 +%2H0675V00697BG04240>I>F1525052010223344 +%2H0559V00443P02RDB@1,038,037,(15)250520(10)223344 +%2H0355V01182P02RDB@0,061,062,20.05.25 +Q1Z \ No newline at end of file diff --git a/tray/assets/signing/sign-message.R b/tray/assets/signing/sign-message.R new file mode 100644 index 0000000..73b5d0c --- /dev/null +++ b/tray/assets/signing/sign-message.R @@ -0,0 +1,38 @@ +# +# Echoes the signed message and exits +# usage: R sign-message.R "test" +# + +######################################################### +# WARNING WARNING WARNING # +######################################################### +# # +# This file is intended for demonstration purposes # +# only. # +# # +# It is the SOLE responsibility of YOU, the programmer # +# to prevent against unauthorized access to any signing # +# functions. # +# # +# Organizations that do not protect against un- # +# authorized signing will be black-listed to prevent # +# software piracy. # +# # +# -QZ Industries, LLC # +# # +######################################################### + +library(openssl) + +mykey <- "private-key.pem" + +# Treat command line argument as message to be signed +message <- enc2utf8(commandArgs(trailingOnly = TRUE)) + +# Load the private key +key <- read_key(file = mykey, password = mypass) + +# Create the signature +sig <- signature_create(serialize(message, NULL), hash = sha512, key = key) # Use hash = sha1 for QZ Tray 2.0 and older + +print(sig) diff --git a/tray/assets/signing/sign-message.asp b/tray/assets/signing/sign-message.asp new file mode 100644 index 0000000..0ac1309 --- /dev/null +++ b/tray/assets/signing/sign-message.asp @@ -0,0 +1,50 @@ +<% +'######################################################### +'# WARNING WARNING WARNING # +'######################################################### +'# # +'# This file is intended for demonstration purposes # +'# only. # +'# # +'# It is the SOLE responsibility of YOU, the programmer # +'# to prevent against unauthorized access to any signing # +'# functions. # +'# # +'# Organizations that do not protect against un- # +'# authorized signing will be black-listed to prevent # +'# software piracy. # +'# # +'# -QZ Industries, LLC # +'# # +'######################################################### +Option Explicit +Dim rsa, pem, sig, data, glob, success, password +' New unlock method for Chilkat - Unregistered version, only good for 30 days +Set glob = Server.CreateObject("Chilkat_9_5_0.Global") +success = glob.UnlockBundle("Anything for 30-day trial") +If (success <> 1) Then + Response.Write "

" & Server.HTMLEncode(glob.LastErrorText) & "
" + Response.End +End If + +' ActiveX library http://www.chilkatsoft.com/ +Set pem = CreateObject("Chilkat_9_5_0.Pem") +Set rsa = CreateObject("Chilkat_9_5_0.Rsa") + +data = request("request") + +password = "" +success = pem.LoadPemFile("private-key.pem", password) +If (success <> 1) Then + Response.Write "
" & Server.HTMLEncode(pem.LastErrorText) & "
" + Response.End +End If + +rsa.ImportPrivateKey(pem.GetPrivateKey(0).getXml()) +rsa.EncodingMode = "base64" +sig = rsa.SignStringENC(data, "SHA-512") ' Use "SHA-1" for QZ Tray 2.0 and older + +Response.ContentType = "text/plain" +Response.Write Server.HTMLEncode(sig) + +%> diff --git a/tray/assets/signing/sign-message.bouncycastle.cs b/tray/assets/signing/sign-message.bouncycastle.cs new file mode 100644 index 0000000..6d98fa1 --- /dev/null +++ b/tray/assets/signing/sign-message.bouncycastle.cs @@ -0,0 +1,67 @@ +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + +using System; +using System.Text; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Security; + + +// Public method for signing the input string with the private key +// =============================================================== + +string privateKey = "private-key.pem"; // PKCS#8 PEM file + +string SignMessage(string msg) +{ + // Convert the input string to a byte array + byte[] input = Encoding.ASCII.GetBytes(msg); + + // Initialize the signer with the algorithm and the private key + ISigner sig = SignerUtilities.GetSigner("SHA512withRSA"); + sig.Init(true, getPrivateKey()); + + // Generate signature and return it as a base64 string + sig.BlockUpdate(input, 0, input.Length); + return Convert.ToBase64String(sig.GenerateSignature()); +} + +AsymmetricKeyParameter getPrivateKey() +{ + using (var reader = System.IO.File.OpenText(privateKey)) + { + var pem = new PemReader(reader).ReadObject(); + return pem as AsymmetricKeyParameter ?? (pem as AsymmetricCipherKeyPair).Private; + } +} + + +// Public method for returning the certificate +// =========================================== + +string certificate = "digital-certificate.txt"; + +string GetCertificate() +{ + using (var reader = System.IO.File.OpenText(certificate)) + { + return reader.ReadToEnd(); + } +} diff --git a/tray/assets/signing/sign-message.cfm b/tray/assets/signing/sign-message.cfm new file mode 100644 index 0000000..cc36133 --- /dev/null +++ b/tray/assets/signing/sign-message.cfm @@ -0,0 +1,73 @@ +/* + * ColdFusion signing example + * Echoes the signed message and exits + */ + +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + + + +#signature# + + +/** +* Sign the given string +* @keyPath Path to PEM formatted private key +* @message message to be signed +* @encoding I am the encoding used when returning the signature (base64 by default). +* @output false +*/ +public any function sign(required string keyPath, required string message, string algorithm = "SHA512withRSA", string encoding = "base64") { + // Note: change algorithm to "SHA1withRSA" for QZ Tray 2.0 and older + createObject("java", "java.security.Security") + .addProvider(createObject("java", "org.bouncycastle.jce.provider.BouncyCastleProvider").init()); + privateKey = createPrivateKey(fileRead(expandPath(keyPath))); + var signer = createObject("java", "java.security.Signature").getInstance(javaCast( "string", algorithm )); + signer.initSign(privateKey); + signer.update(charsetDecode(message, "utf-8")); + var signedBytes = signer.sign(); + return encoding == "binary" ? signedBytes : binaryEncode(signedBytes, encoding); +} + +/** +* Set the private key using the provided pem formatted content. +* +* @contents PEM key contents +* @output false +*/ +private any function createPrivateKey(required string contents) { + var pkcs8 = createObject("java", "java.security.spec.PKCS8EncodedKeySpec").init( + binaryDecode(stripKeyDelimiters(contents), "base64") + ); + + return createObject("java", "java.security.KeyFactory") + .getInstance(javaCast( "string", "RSA" )).generatePrivate(pkcs8); +} + +/** +* Strip X509 cert delimiters +* +* @keyText PEM formatted key data +* @output false +*/ +private string function stripKeyDelimiters(required string keyText) { + return trim(reReplace(keyText, "-----(BEGIN|END)[^\r\n]+", "", "all" )); +} + diff --git a/tray/assets/signing/sign-message.cls b/tray/assets/signing/sign-message.cls new file mode 100644 index 0000000..91e0062 --- /dev/null +++ b/tray/assets/signing/sign-message.cls @@ -0,0 +1,54 @@ +/* + * Salesforce APEX Signing Example + * Returns the signed message to a wired controller + */ + +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + +public with sharing class SignMessage { + @AuraEnabled(cacheable = true) + public static String signMessage(String toSign){ + String privateKeyBase64 = ''; + + Blob sig = Crypto.sign('RSA-SHA512', + Blob.valueOf(toSign), + EncodingUtil.base64Decode(privateKeyBase64)); + + return EncodingUtil.base64Encode(sig); + } +} + +/** JavaScript - Adjust as needed + +import signMessage from '@salesforce/apex/SignMessage.signMessage'; +@wire(signMessage) + +qz.security.setSignatureAlgorithm("SHA512"); +qz.security.setSignaturePromise(function(toSign) { + return function (resolve, reject) { + try { + resolve(signMessage({toSign : toSign})); + } catch(err) { + reject(err); + } + } +}); + +**/ diff --git a/tray/assets/signing/sign-message.core.cs b/tray/assets/signing/sign-message.core.cs new file mode 100644 index 0000000..f453e07 --- /dev/null +++ b/tray/assets/signing/sign-message.core.cs @@ -0,0 +1,53 @@ +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Web.Services; + +// To convert a .PEM PrivateKey: +// openssl pkcs12 -export -inkey private-key.pem -in digital-certificate.txt -out private-key.pfx +private static X509KeyStorageFlags STORAGE_FLAGS = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable; + +public IActionResult Index(string request) +{ + var KEY = "/path/to/private-key.pfx"; + var PASS = ""; + + try + { + byte[] data = new ASCIIEncoding().GetBytes(request); + var cert = new X509Certificate2(KEY, PASS, STORAGE_FLAGS); + RSA rsa = (RSA)cert.GetRSAPrivateKey(); + var signed = rsa.SignData(data, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1); + string base64 = Convert.ToBase64String(signed); + return Content(base64); + } + catch(Exception ex) + { + if((STORAGE_FLAGS & X509KeyStorageFlags.MachineKeySet) == X509KeyStorageFlags.MachineKeySet) + { + // IISExpress may fail with "Invalid provider type specified"; remove MachineKeySet flag, try again + STORAGE_FLAGS = STORAGE_FLAGS & ~X509KeyStorageFlags.MachineKeySet; + return Index(request); + } + throw ex; + } +} diff --git a/tray/assets/signing/sign-message.cs b/tray/assets/signing/sign-message.cs new file mode 100644 index 0000000..90abc79 --- /dev/null +++ b/tray/assets/signing/sign-message.cs @@ -0,0 +1,80 @@ +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Web.Services; + +// To convert a .PEM PrivateKey: +// openssl pkcs12 -export -inkey private-key.pem -in digital-certificate.txt -out private-key.pfx +private static X509KeyStorageFlags STORAGE_FLAGS = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable; + +/** + * Note, this example is for .NET Forms/PageMethods + * For MVC, change the following: + * + * public ActionResult SignMessage() { + * string request = Request.QueryString["request"]; + * ... + * return Content(base64, "text/plain"); + * ... + * return SignMessage(); + * + * ... and replace PageMethods calls with fetch("@Url.Content("./SignMessage/?request=")" + toSign + */ +[WebMethod] +public static string SignMessage(string request) +{ + //var WEBROOT_PATH = HttpContext.Current.Server.MapPath("/"); + //var CURRENT_PATH = HttpContext.Current.Server.MapPath("~"); + //var PARENT_PATH = System.IO.Directory.GetParent(WEBROOT).Parent.FullName; + var KEY = "/path/to/private-key.pfx"; + var PASS = ""; + + try + { + var cert = new X509Certificate2(KEY, PASS, STORAGE_FLAGS); + RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PrivateKey; // PFX defaults to the weaker "SHA1" + byte[] data = new ASCIIEncoding().GetBytes(request); + RSACryptoServiceProvider cspStrong = new RSACryptoServiceProvider(); // 2.1 and higher: Make RSACryptoServiceProvider that can handle SHA256, SHA512 + cspStrong.ImportParameters(csp.ExportParameters(true)); // Copy to stronger RSACryptoServiceProvider + byte[] hash = new SHA512CryptoServiceProvider().ComputeHash(data); // Use SHA1CryptoServiceProvider for QZ Tray 2.0 and older + string base64 = Convert.ToBase64String(cspStrong.SignHash(hash, CryptoConfig.MapNameToOID("SHA512"))); // Use "SHA1" for QZ Tray 2.0 and older + return base64; + + /* + * // Or per https://stackoverflow.com/a/50104158/3196753 + * var cert = new X509Certificate2(KEY, PASS, STORAGE_FLAGS); + * var csp = cert.GetRSAPrivateKey(); + * byte[] data = new ASCIIEncoding().GetBytes(request); + * return Convert.ToBase64String(csp.SignData(data, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1)); // Use "SHA1" for QZ Tray 2.0 and older + */ + } + catch(Exception ex) + { + if((STORAGE_FLAGS & X509KeyStorageFlags.MachineKeySet) == X509KeyStorageFlags.MachineKeySet) + { + // IISExpress may fail with "Invalid provider type specified"; remove MachineKeySet flag, try again + STORAGE_FLAGS = STORAGE_FLAGS & ~X509KeyStorageFlags.MachineKeySet; + return SignMessage(request); + } + throw ex; + } +} diff --git a/tray/assets/signing/sign-message.go b/tray/assets/signing/sign-message.go new file mode 100644 index 0000000..25bd9ee --- /dev/null +++ b/tray/assets/signing/sign-message.go @@ -0,0 +1,99 @@ +/* + * Echos the signed message + */ +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### +/* Steps: + * 1. Convert private key to golang compatible format: + * openssl rsa -in private-key.pem -out private-key-updated.pem + */ +package main + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + "io/ioutil" + "net/http" +) + +var privateKey = "C:\\path\\to\\private-key-updated.pem" +var password = "S3cur3P@ssw0rd" +var listenPort = ":8080" + +func main() { + http.HandleFunc("/", handler) + http.ListenAndServe(listenPort, nil) +} + +func handler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Add("Content-Type", "text/plain") + + rsaPrivateKey, err := decodeKey(privateKey) + + if err != nil { + displayError(w, "Error reading key", err) + return + } + + data := r.URL.Query().Get("request") + + if len(data) < 1 { + displayError(w, "Request cannot be blank", err) + return + } + + hash := sha1.Sum([]byte(data)) + rng := rand.Reader + signature, err := rsa.SignPKCS1v15(rng, rsaPrivateKey, crypto.SHA512, hash[:]) // Use crypto.SHA1 for QZ Tray 2.0 and older + if err != nil { + displayError(w, "Error from signing: %s\n", err) + return + } + fmt.Fprintf(w, base64.StdEncoding.EncodeToString(signature)) +} + +func displayError(w http.ResponseWriter, msg string, err error) { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "500 - Internal Server Error\n\n"+msg+"\n\nDetails:\n", err) +} + +func decodeKey(path string) (*rsa.PrivateKey, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + block, _ := pem.Decode(b) + if x509.IsEncryptedPEMBlock(block) { + der, err := x509.DecryptPEMBlock(block, []byte(password)) + if err != nil { + return nil, err + } + return x509.ParsePKCS1PrivateKey(der) + } + + return x509.ParsePKCS1PrivateKey(block.Bytes) +} diff --git a/tray/assets/signing/sign-message.java b/tray/assets/signing/sign-message.java new file mode 100644 index 0000000..3efef06 --- /dev/null +++ b/tray/assets/signing/sign-message.java @@ -0,0 +1,143 @@ +// package foo.bar; + +/* + * Java signing example + * Echoes the signed message and exits + */ + +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + +import java.io.*; +import java.security.*; +import java.security.spec.*; +import java.util.Base64; +import java.util.logging.*; + +/** + * Utility for creating an RSA SHA1 signature based on a supplied PEM formatted private key + */ +public class MessageSigner { + private static Logger logger = Logger.getLogger(MessageSigner.class.getName()); + private Signature sig; + + /** + * Standard Java usage example, safe to remove + */ + public static void main(String args[]) throws Exception { + if (args.length < 2) { + logger.severe("Usage:\nMessageSigner.class [path to private key] [data to sign]\n"); + System.exit(1); + } + byte[] key = MessageSigner.readFile(args[0]); + String toSign = args[1]; + MessageSigner ms = new MessageSigner(key); + String signature = ms.sign(toSign); + + logger.log(Level.INFO, "Request: {0}", toSign); + logger.log(Level.INFO, "Response: {0}", signature); + } + + /** + * Servlet usage example, safe to remove + * + protected void doProcessRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // Get request from URL + String data = request.getParameter("request"); + + String signature = new MessageSigner("private-key.pem").sign(data); + + // Send signed message back + response.setContentType("text/plain"); + PrintWriter out = response.getWriter(); + out.write(signature); + out.flush(); + out.close(); + } + + /** + * Constructs an RSA SHA1 signature object for signing + * @param keyData + * @throws Exception + */ + public MessageSigner(byte[] keyData) throws Exception { + // Warning: PKCS#8 required. If PKCS#1 (RSA) key is provided convert using: + // $ openssl pkcs8 -topk8 -inform PEM -outform PEM -in private-key.pem -out private-key-pkcs8.pem -nocrypt + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(parseKeyData(keyData)); + KeyFactory kf = KeyFactory.getInstance("RSA"); + PrivateKey key = kf.generatePrivate(keySpec); + sig = Signature.getInstance("SHA512withRSA"); // Use "SHA1withRSA" for QZ Tray 2.0 and older + sig.initSign(key); + } + + /** + * Signs the specified data with the provided private key, returning the + * RSA SHA1 signature + * @param data Message to sign + * @return Base64 encoded signature + * @throws Exception + */ + public String sign(String data) throws Exception { + sig.update(data.getBytes()); + return Base64.getEncoder().encodeToString(sig.sign()); + } + + /** + * Reads the raw byte[] data from a file resource + * @param path File path to read + * @return the raw byte data from file + * @throws IOException + */ + public static byte[] readFile(String path) throws IOException { + InputStream is = MessageSigner.class.getResourceAsStream(path); + if (is == null) { + throw new IOException(String.format("Can't open resource \"%s\"", path)); + } + DataInputStream dis = new DataInputStream(is); + byte[] data = new byte[dis.available()]; + dis.readFully(data); + dis.close(); + return data; + } + + /** + * Parses a base64 encoded private key by stripping the header and footer lines + * @param keyData PEM file contents + * @return Raw key byes + * @throws IOException + */ + private static byte[] parseKeyData(byte[] keyData) throws IOException { + StringBuilder sb = new StringBuilder(); + String[] lines = new String(keyData).split("[\r?\n]+"); + String[] skips = new String[]{"-----BEGIN", "-----END", ": "}; + for (String line : lines) { + boolean skipLine = false; + for (String skip : skips) { + if (line.contains(skip)) { + skipLine = true; + } + } + if (!skipLine) { + sb.append(line.trim()); + } + } + return Base64.getDecoder().decode(sb.toString()); + } + +} diff --git a/tray/assets/signing/sign-message.js b/tray/assets/signing/sign-message.js new file mode 100644 index 0000000..886adcd --- /dev/null +++ b/tray/assets/signing/sign-message.js @@ -0,0 +1,89 @@ +/* + * JavaScript client-side example using jsrsasign + */ + +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + +/** + * Depends: + * - jsrsasign-latest-all-min.js + * - qz-tray.js + * + * Steps: + * + * 1. Include jsrsasign 10.9.0 into your web page + * + * + * 2. Update the privateKey below with contents from private-key.pem + * + * 3. Include this script into your web page + * + * + * 4. Remove or comment out any other references to "setSignaturePromise" + * + * 5. IMPORTANT: Before deploying to production, copy "jsrsasign-all-min.js" + * to the web server. Don't trust the CDN above to be available. + */ +var privateKey = "-----BEGIN PRIVATE KEY-----\n" + + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0z9FeMynsC8+u\n" + + "dvX+LciZxnh5uRj4C9S6tNeeAlIGCfQYk0zUcNFCoCkTknNQd/YEiawDLNbxBqut\n" + + "bMDZ1aarys1a0lYmUeVLCIqvzBkPJTSQsCopQQ9V8WuT252zzNzs68dVGNdCJd5J\n" + + "NRQykpwexmnjPPv0mvj7i8XgG379TyW6P+WWV5okeUkXJ9eJS2ouDYdR2SM9BoVW\n" + + "+FgxDu6BmXhozW5EfsnajFp7HL8kQClI0QOc79yuKl3492rH6bzFsFn2lfwWy9ic\n" + + "7cP8EpCTeFp1tFaD+vxBhPZkeTQ1HKx6hQ5zeHIB5ySJJZ7af2W8r4eTGYzbdRW2\n" + + "4DDHCPhZAgMBAAECggEATvofR3gtrY8TLe+ET3wMDS8l3HU/NMlmKA9pxvjYfw7F\n" + + "8h4VBw4oOWPfzU7A07syWJUR72kckbcKMfw42G18GbnBrRQG0UIgV3/ppBQQNg9Y\n" + + "QILSR6bFXhLPnIvm/GxVa58pOEBbdec4it2Gbvie/MpJ4hn3K8atTqKk0djwxQ+b\n" + + "QNBWtVgTkyIqMpUTFDi5ECiVXaGWZ5AOVK2TzlLRNQ5Y7US8lmGxVWzt0GONjXSE\n" + + "iO/eBk8A7wI3zknMx5o1uZa/hFCPQH33uKeuqU5rmphi3zS0BY7iGY9EoKu/o+BO\n" + + "HPwLQJ3wCDA3O9APZ3gmmbHFPMFPr/mVGeAeGP/BAQKBgQDaPELRriUaanWrZpgT\n" + + "VnKKrRSqPED3anAVgmDfzTQwuR/3oD506F3AMBzloAo3y9BXmDfe8qLn6kgdZQKy\n" + + "SFNLz888at96oi+2mEKPpvssqiwE6F3OtEM6yv4DP9KJHaHmXaWv+/sjwjzpFNjs\n" + + "wGThBxFvrTWRJqBYsM1XNJJ2EQKBgQDUGbTSwHKqRCYWhQ1GPCZKE98l5UtMKvUb\n" + + "hyWWOXoyoeYbJEMfG1ynX4JeXIkl6YtBjYCqszv9PjHa1rowTZaAPJ0V70zyhTcF\n" + + "t581ii9LpiejIGrELHvJnW87QmjjStkjwGIqgKLp7Qe6CDjHI9HP1NM0uav/IQLW\n" + + "pB6wyEz1yQKBgQCuxPut+Ax2rzM05KB9PAnWzO1zt3U/rtm8IAF8uVVGf7r+EDJ0\n" + + "ZXJO6zj5G8WTEYHz5E86GI4ltBW0lKQoKouqdu27sMrv5trXG/CSImOcTVubQot9\n" + + "chc1CkOKTp5IeJajafO6j817wZ4N+0gNsbYYEBUCnm/7ojdfT5ficpOoQQKBgQDB\n" + + "PgKPmaNfGeQR1Ht5qEfCakR/RF/ML79Nq15FdmytQPBjfjBhYQ6Tt+MRkgGqtxOX\n" + + "UBMQc2iOnGHT3puYcrhScec1GufidhjhbqDxqMrag7HNYDWmMlk+IeA7/4+Mtp8L\n" + + "gbZuvvCvbLQDfIYueaYpUuBzQ08/jZYGdVU4/+WOcQKBgAGUN0kIB6EM1K/iZ0TN\n" + + "jlt8P5UEV3ZCyATWFiGZRhhE2WAh8gv1jx4J26pcUs1n8sd2a1h6ZuBSqsyIlNSp\n" + + "xtKsm3bqQFDHRrPcsBX4nanrw9DzkpH1k/I3WMSdGqkDAR3DtL7yXTJXJo2Sbrp5\n" + + "EjzSn7DcDE1tL2En/tSVXeUY\n" + + "-----END PRIVATE KEY-----"; + +qz.security.setSignatureAlgorithm("SHA512"); // Since 2.1 +qz.security.setSignaturePromise(function(toSign) { + return function(resolve, reject) { + try { + var pk = KEYUTIL.getKey(privateKey); + var sig = new KJUR.crypto.Signature({"alg": "SHA512withRSA"}); // Use "SHA1withRSA" for QZ Tray 2.0 and older + sig.init(pk); + sig.updateString(toSign); + var hex = sig.sign(); + console.log("DEBUG: \n\n" + stob64(hextorstr(hex))); + resolve(stob64(hextorstr(hex))); + } catch (err) { + console.error(err); + reject(err); + } + }; +}); diff --git a/tray/assets/signing/sign-message.jsl b/tray/assets/signing/sign-message.jsl new file mode 100644 index 0000000..754cb3c --- /dev/null +++ b/tray/assets/signing/sign-message.jsl @@ -0,0 +1,48 @@ +// +// J# Signing Example +// Echoes the signed message and exits +// + +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + +module sample + +open System +open System.Security.Cryptography +open System.Security.Cryptography.X509Certificates +open System.IO +open System.Text + +let request = "test data" + +// How to associate a private key with the X509Certificate2 class in .net +// openssl pkcs12 -export -in private-key.pem -inkey digital-certificate.txt -out private-key.pfx +let cert = new X509Certificate2("private-key.pfx") + +let sha1 = new SHA512CryptoServiceProvider() // Use "SHA1CryptoServiceProvider" for QZ Tray 2.0 and older + +let csp = cert.PrivateKey :?> RSACryptoServiceProvider +let encoder = new ASCIIEncoding() + +let data = encoder.GetBytes(request) +let binaryData = csp.SignData(data, sha1) +let output = Convert.ToBase64String(binaryData) + +Console.WriteLine output \ No newline at end of file diff --git a/tray/assets/signing/sign-message.jsp b/tray/assets/signing/sign-message.jsp new file mode 100644 index 0000000..70cc978 --- /dev/null +++ b/tray/assets/signing/sign-message.jsp @@ -0,0 +1,120 @@ +<% +/* + * JSP signing example + * Echoes the signed message and exits + */ + +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### +%> + +<%@ page import="java.io.*" %> +<%@ page import="java.util.*" %> +<%@ page import="java.lang.*" %> +<%@ page import="java.security.*" %> +<%@ page import="java.security.spec.*" %> +<%@ page import="java.util.logging.*" %> +<%@ page import="javax.xml.bind.DatatypeConverter" %> +<%@ page trimDirectiveWhitespaces="true" %> +<%@ page language="java" contentType="text/plain charset=UTF-8" pageEncoding="UTF-8"%> + +<%= getSignature(request.getParameter("request")) %> + + +<%! +/** + * Creates a signature using the provided private key and the provided (String) data inside Object o. + */ +private String getSignature(Object o) { + // Private key path if placed in CATALINA_HOME/private/ **AND** if JSP is + // placed in CATALINA_HOME/webapps/examples/. Adjust as needed. + String keyPath = "../../private/private-key.pem"; + + // Prepend servlet context path + keyPath = getServletContext().getRealPath("/") + keyPath; + String req = o == null ? "" : (String)o; + + try { + byte[] keyData = cleanseKeyData(readData(keyPath)); + // Warning: PKCS#8 required. If PKCS#1 (RSA) key is provided convert using: + // $ openssl pkcs8 -topk8 -inform PEM -outform PEM -in private-key.pem -out private-key-pkcs8.pem -nocrypt + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyData); + KeyFactory kf = KeyFactory.getInstance("RSA"); + PrivateKey key = kf.generatePrivate(keySpec); + Signature sig = Signature.getInstance("SHA512withRSA"); // Use "SHA1withRSA" for QZ Tray 2.0 and older + sig.initSign(key); + sig.update(req.getBytes()); + String output = DatatypeConverter.printBase64Binary(sig.sign()); + return output; + } catch (Throwable t) { + t.printStackTrace(); + return "Something went wrong while signing the message.\n" + + "Please check server console for sign-message.jsp"; + } +} +%> + +<%! +/** + * Reads the raw byte[] data from a file resource + * @param resourcePath + * @return the raw byte data from a resource file + * @throws IOException + */ +public byte[] readData(String resourcePath) throws IOException { + FileInputStream is = new FileInputStream(resourcePath); + + //InputStream is = getServletContext().getResourceAsStream(resourcePath); + if (is == null) { + throw new IOException(String.format("Can't open resource \"%s\"", resourcePath)); + } + DataInputStream dis = new DataInputStream(is); + byte[] data = new byte[dis.available()]; + dis.readFully(data); + dis.close(); + return data; +} +%> + +<%! +/** + * Parses an X509 PEM formatted base64 encoded private key, returns the decoded + * private key byte data + * @param keyData PEM file contents, a X509 base64 encoded private key + * @return Private key data + * @throws IOException + */ +private byte[] cleanseKeyData(byte[] keyData) throws IOException { + StringBuilder sb = new StringBuilder(); + String[] lines = new String(keyData).split("\n"); + String[] skips = new String[]{"-----BEGIN", "-----END", ": "}; + for (String line : lines) { + boolean skipLine = false; + for (String skip : skips) { + if (line.contains(skip)) { + skipLine = true; + } + } + if (!skipLine) { + sb.append(line.trim()); + } + } + return DatatypeConverter.parseBase64Binary(sb.toString()); +} +%> diff --git a/tray/assets/signing/sign-message.node.js b/tray/assets/signing/sign-message.node.js new file mode 100644 index 0000000..5ee68d8 --- /dev/null +++ b/tray/assets/signing/sign-message.node.js @@ -0,0 +1,43 @@ +/* + * Node.js signing example + * Echoes the signed message and exits + */ + +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + +var key = "private-key.pem"; +//var pass = "S3cur3P@ssw0rd"; + +app.get('/sign', function(req, res) { + var crypto = require('crypto'); + var fs = require('fs'); + var path = require('path'); + var toSign = req.query.requestToSign; + + fs.readFile(path.join(__dirname, '\\' + key), 'utf-8', function(err, privateKey) { + var sign = crypto.createSign('SHA512'); // Use "SHA1" for QZ Tray 2.0 and older + + sign.update(toSign); + var signature = sign.sign({ key: privateKey/*, passphrase: pass */ }, 'base64'); + + res.set('Content-Type', 'text/plain'); + res.send(signature); + }); +}); diff --git a/tray/assets/signing/sign-message.odoo.py b/tray/assets/signing/sign-message.odoo.py new file mode 100644 index 0000000..2321252 --- /dev/null +++ b/tray/assets/signing/sign-message.odoo.py @@ -0,0 +1,43 @@ +# +# Python Odoo example for controller.py +# Echoes the signed message and exits +# + +######################################################### +# WARNING WARNING WARNING # +######################################################### +# # +# This file is intended for demonstration purposes # +# only. # +# # +# It is the SOLE responsibility of YOU, the programmer # +# to prevent against unauthorized access to any signing # +# functions. # +# # +# Organizations that do not protect against un- # +# authorized signing will be black-listed to prevent # +# software piracy. # +# # +# -QZ Industries, LLC # +# # +######################################################### + +from odoo import http +from odoo.http import request +from OpenSSL import crypto +import base64 + + +class SignMessage(http.Controller): + + @http.route('/sign-message/', auth='public') + def index(self, **kwargs): + mypass = None + key_file = open('path/to/private-key.pem', 'r') + key = key_file.read() + key_file.close() + password = None + pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, key, password) + sign = crypto.sign(pkey, kwargs.get('request', ''), 'sha512') # Use 'sha1' for QZ Tray 2.0 and older + data_base64 = base64.b64encode(sign) + return request.make_response(data_base64, [('Content-Type', 'text/plain')]) diff --git a/tray/assets/signing/sign-message.php b/tray/assets/signing/sign-message.php new file mode 100644 index 0000000..f57620d --- /dev/null +++ b/tray/assets/signing/sign-message.php @@ -0,0 +1,55 @@ +loadKey(file_get_contents($KEY)); +$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); +$signature = $rsa->sign($req); +*/ + +if ($signature) { + header("Content-type: text/plain"); + echo base64_encode($signature); + exit(0); +} + +echo '

Error signing message

'; +http_response_code(500); +exit(1); + +?> diff --git a/tray/assets/signing/sign-message.pl b/tray/assets/signing/sign-message.pl new file mode 100644 index 0000000..977dbf0 --- /dev/null +++ b/tray/assets/signing/sign-message.pl @@ -0,0 +1,52 @@ +#!/usr/bin/perl +# +# Echoes the signed message and exits +# usage: ./sign-message.pl "test" +# +######################################################### +# WARNING WARNING WARNING # +######################################################### +# # +# This file is intended for demonstration purposes # +# only. # +# # +# It is the SOLE responsibility of YOU, the programmer # +# to prevent against unauthorized access to any signing # +# functions. # +# # +# Organizations that do not protect against un- # +# authorized signing will be black-listed to prevent # +# software piracy. # +# # +# -QZ Industries, LLC # +# # +######################################################### + +# RSA Crypto libs provided by: +# Debian: libcrypt-openssl-rsa-perl +# RedHat: perl-Crypt-OpenSSL-RSA +use Crypt::OpenSSL::RSA; +use MIME::Base64 qw(encode_base64); + +# Get first argument passed to script +my $request = $ARGV[0]; + +# Path to the private key +my $pem_file = "private-key.pem"; + +# Read private key +my $private_key = do { + local $/ = undef; + open my $fh, "<", $pem_file + or die "could not open $file: $!"; + <$fh>; +}; + +# Load private key +my $rsa = Crypt::OpenSSL::RSA->new_private_key($private_key); + +# Create signature +$rsa->use_sha512_hash(); # use_sha1_hash for QZ Tray 2.0 and older +my $sig = encode_base64($rsa->sign($request)); + +print $sig; diff --git a/tray/assets/signing/sign-message.py b/tray/assets/signing/sign-message.py new file mode 100644 index 0000000..8ad584a --- /dev/null +++ b/tray/assets/signing/sign-message.py @@ -0,0 +1,39 @@ +# +# Python Django example for views.py +# Echoes the signed message and exits +# + +######################################################### +# WARNING WARNING WARNING # +######################################################### +# # +# This file is intended for demonstration purposes # +# only. # +# # +# It is the SOLE responsibility of YOU, the programmer # +# to prevent against unauthorized access to any signing # +# functions. # +# # +# Organizations that do not protect against un- # +# authorized signing will be black-listed to prevent # +# software piracy. # +# # +# -QZ Industries, LLC # +# # +######################################################### + +import base64 +import os +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding +from django.http import HttpResponse, HttpResponseBadRequest + +def get(self, request, message): + # Load signature + key = serialization.load_pem_private_key(open("private-key.pem","rb").read(), None, backend=default_backend()) + # Create the signature + signature = key.sign(message.encode('utf-8'), padding.PKCS1v15(), hashes.SHA512()) # Use hashes.SHA1() for QZ Tray 2.0 and older + # Echo the signature + return HttpResponse(base64.b64encode(signature), content_type="text/plain") + diff --git a/tray/assets/signing/sign-message.rb b/tray/assets/signing/sign-message.rb new file mode 100644 index 0000000..c95e6b6 --- /dev/null +++ b/tray/assets/signing/sign-message.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby +# +# Echoes the signed message and exits +# usage: ./sign-message.rb "request=test" +# +######################################################### +# WARNING WARNING WARNING # +######################################################### +# # +# This file is intended for demonstration purposes # +# only. # +# # +# It is the SOLE responsibility of YOU, the programmer # +# to prevent against unauthorized access to any signing # +# functions. # +# # +# Organizations that do not protect against un- # +# authorized signing will be black-listed to prevent # +# software piracy. # +# # +# -QZ Industries, LLC # +# # +######################################################### + +# Typical rails controller +class PrintingController < ActionController::Base + def sign + digest = OpenSSL::Digest.new('sha512') # Use 'sha1' for QZ Tray 2.0 and older + pkey = OpenSSL::PKey::read(File.read(Rails.root.join('lib', 'certs', 'private-key.pem'))) + + signed = pkey.sign(digest, params[:request]) + encoded = Base64.encode64(signed) + + render text: encoded + end +end diff --git a/tray/assets/signing/sign-message.ts b/tray/assets/signing/sign-message.ts new file mode 100644 index 0000000..54df7ba --- /dev/null +++ b/tray/assets/signing/sign-message.ts @@ -0,0 +1,86 @@ + +/* + * AngularJS example using jsrsasign (client) or fetch (server) + */ + +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + +import { Component } from '@angular/core'; +import * as qz from 'qz-tray'; +import { KJUR, KEYUTIL, stob64, hextorstr } from 'jsrsasign'; + +qz.security.setCertificatePromise((resolve, reject) => { + fetch("assets/digital-certificate.txt", {cache: 'no-store', headers: {'Content-Type': 'text/plain'}}) + .then(data => resolve(data.text())); +}); + +/* + * Client-side using jsrsasign + */ +qz.security.setSignatureAlgorithm("SHA512"); // Since 2.1 +qz.security.setSignaturePromise(hash => { + return (resolve, reject) => { + fetch("assets/private-key.pem", {cache: 'no-store', headers: {'Content-Type': 'text/plain'}}) + .then(wrapped => wrapped.text()) + .then(data => { + var pk = KEYUTIL.getKey(data); + var sig = new KJUR.crypto.Signature({"alg": "SHA512withRSA"}); // Use "SHA1withRSA" for QZ Tray 2.0 and older + sig.init(pk); + sig.updateString(hash); + var hex = sig.sign(); + console.log("DEBUG: \n\n" + stob64(hextorstr(hex))); + resolve(stob64(hextorstr(hex))); + }) + .catch(err => console.error(err)); + }; +}); + + +/* + * Preferred, from a secure controller + * + qz.security.setSignaturePromise(hash => { + return (resolve, reject) => { + fetch("/path/to/controller?request=" + hash, {cache: 'no-store', headers: {'Content-Type': 'text/plain'}}) + .then(wrapped => wrapped.text()) + .then(data => resolve(data)) + .catch(err => console.error(err)); + }); + }; + }); + */ + +qz.api.setSha256Type(data => sha256(data)); +qz.api.setPromiseType(resolver => new Promise(resolver)); + +qz.websocket.connect() + .then(qz.printers.getDefault) + .then(printer => console.log("The default printer is: " + printer)) + .then(qz.websocket.disconnect) + .catch(err => console.error(err)); + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + title = 'QZ Tray AngularJS Signing'; +} diff --git a/tray/assets/signing/sign-message.vb b/tray/assets/signing/sign-message.vb new file mode 100644 index 0000000..77f7b6a --- /dev/null +++ b/tray/assets/signing/sign-message.vb @@ -0,0 +1,43 @@ +' +' Echoes the signed message and exits +' +Public Sub SignMessage(message As String) + + '********************************************************** + '* WARNING WARNING WARNING * + '********************************************************** + '* * + '* This file is intended for demonstration purposes only. * + '* only. * + '* * + '* It is the SOLE responsibility of YOU, the programmer * + '* to prevent against unauthorized access to any signing * + '* functions. * + '* * + '* Organizations that do not protect against un- * + '* authorized signing will be black-listed to prevent * + '* software piracy. * + '* * + '* -QZ Industries, LLC * + '* * + '********************************************************** + + ' Sample key. Replace with one used for CSR generation + ' How to associate a private key with the X509Certificate2 class in .net + ' openssl pkcs12 -export -inkey private-key.pem -in digital-certificate.txt -out private-key.pfx + + Dim KEY = "private-key.pfx" + + Dim cert = New X509Certificate2(KEY, X509KeyStorageFlags.MachineKeySet Or X509KeyStorageFlags.PersistKeySet Or X509KeyStorageFlags.Exportable) + Dim csp As RSACryptoServiceProvider = CType(cert.PrivateKey,RSACryptoServiceProvider) + + Dim cspStrong as RSACryptoServiceProvider() = New RSACryptoServiceProvider() ' 2.1 and higher: Make RSACryptoServiceProvider that can handle SHA256, SHA512 + cspStrong.ImportParameters(csp.ExportParameters(true)) ' Copy to stronger RSACryptoServiceProvider + + Dim data As Byte() = New ASCIIEncoding().GetBytes(message) + Dim hash As Byte() = New SHA512Managed().ComputeHash(data) ' Use SHA1Managed() for QZ Tray 2.0 and older + + Response.ContentType = "text/plain" + Response.Write(Convert.ToBase64String(cspStrong.SignHash(hash, CryptoConfig.MapNameToOID("SHA512")))) ' Use "SHA1" for QZ Tray 2.0 and older + Environment.[Exit](0) +End Sub diff --git a/tray/assets/signing/sign-message.vue.js b/tray/assets/signing/sign-message.vue.js new file mode 100644 index 0000000..74d8445 --- /dev/null +++ b/tray/assets/signing/sign-message.vue.js @@ -0,0 +1,88 @@ +/* + * JavaScript client-side example using jwa + */ + +// ######################################################### +// # WARNING WARNING WARNING # +// ######################################################### +// # # +// # This file is intended for demonstration purposes # +// # only. # +// # # +// # It is the SOLE responsibility of YOU, the programmer # +// # to prevent against unauthorized access to any signing # +// # functions. # +// # # +// # Organizations that do not protect against un- # +// # authorized signing will be black-listed to prevent # +// # software piracy. # +// # # +// # -QZ Industries, LLC # +// # # +// ######################################################### + +import Vue from "vue"; + +import qz from "qz-tray"; +import jwa from "jwa"; + +const vue = new Vue({ + el: "#app", + data: { + message: "QZ Tray Vue.js Demo" + }, + template: "
{{ message }}
" +}).$mount(); + +const rsa512 = jwa("RS512"); + +const privateKey = getPrivateKey(); +qz.security.setSignatureAlgorithm("SHA512"); // Since 2.1 +qz.security.setSignaturePromise(function (toSign) { + return function (resolve, reject) { + try { + const hexUrl = rsa512.sign(toSign, privateKey); + // Crude base64URL to base64 conversion + const hex = hexUrl.replace(/_/g, "/").replace(/-/g, "+"); + resolve(hex); + } catch (err) { + reject(err); + } + }; +}); + +const certificate = getCertificate(); +qz.security.setCertificatePromise((resolve, reject) => { + resolve(certificate); +}); + +function getPrivateKey() { + // TODO: Switch to fetch()/AJAX/etc + return ( + "-----BEGIN PRIVATE KEY-----\n" + + "..." + + "-----END PRIVATE KEY-----" + ); +} + +function getCertificate() { + // TODO: Switch to fetch()/AJAX/etc + return ( + "-----BEGIN CERTIFICATE-----\n" + + "..." + + "-----END CERTIFICATE-----" + ); +} + +qz.websocket + .connect() + .then(() => { + vue.message = "Looking for printers..."; + return qz.printers.find(); + }) + .then((printers) => { + vue.message = "Found printers: " + printers; + }) + .catch((err) => { + vue.message = err; + }); diff --git a/tray/assets/signing/sign_message.erl b/tray/assets/signing/sign_message.erl new file mode 100644 index 0000000..8a950b1 --- /dev/null +++ b/tray/assets/signing/sign_message.erl @@ -0,0 +1,50 @@ +%%%------------------------------------------------------------------- +%%% ######################################################### +%%% # WARNING WARNING WARNING # +%%% ######################################################### +%%% # # +%%% # This file is intended for demonstration purposes # +%%% # only. # +%%% # # +%%% # It is the SOLE responsibility of YOU, the programmer # +%%% # to prevent against unauthorized access to any signing # +%%% # functions. # +%%% # # +%%% # Organizations that do not protect against un- # +%%% # authorized signing will be black-listed to prevent # +%%% # software piracy. # +%%% # # +%%% # -QZ Industries, LLC # +%%% # # +%%% ######################################################### +%%%------------------------------------------------------------------- +-module(sign_message). +-export([sign/2]). + +%%% +%%% Usage: +%%% -import(sign_message, [sign/2]). +%%% [...] +%%% sign_message:sign(GetRequest, "path/to/private-key.rsa"). +%%% * Where GetRequest is the the "foo" portion of "?request=foo" +%%% * Web framework must echo the base64 encoded signature in plain text +%%% * Browser must use ajax technique to fetch base64 signature +%%% * See also qz.api.setSignaturePromise(...) +%%% +%%% Important: +%%% * Private key MUST be converted to newer RSA format using the following command: +%%% openssl rsa -in "private-key.pem" -out "private-key.rsa" +%%% +%%% Watch for: +%%% badmatch,{error,enoent} key cannot be read; check for valid path +%%% function_clause,[{public_key,sign,...}] key must be converted to newer RSA format +%%% +%%% + +sign(Message, KeyPath) -> + {ok, Data} = file:read_file(KeyPath), + [KeyEntry] = public_key:pem_decode(Data), + PrivateKey = public_key:pem_entry_decode(KeyEntry), + Signature = public_key:sign(list_to_binary(Message), sha512, PrivateKey), % Use sha1 for QZ Tray 2.0 and older + Base64 = base64:encode(Signature), + io:fwrite(Base64). diff --git a/tray/assets/zpl_sample.txt b/tray/assets/zpl_sample.txt new file mode 100644 index 0000000..8813e3e --- /dev/null +++ b/tray/assets/zpl_sample.txt @@ -0,0 +1,51 @@ +^XA^CF,0,0,0 +^FX Please avoid using PR (print rate) or MD (media darkness) commands; +They may change or be outside the range of current printer settings for subsequent print jobs and result in bad prints -lite1979^FS +^PW812^PON^CI13 +^FO0,147^GB800,4,4^FS +^FO0,401^GB800,4,4^FS +^FO0,736^GB800,4,4^FS +^FO35,12^AdN,0,0^FWN^FH^FDFrom:^FS +^FO35,31^AdN,0,0^FWN^FH^FDAlex^FS +^FO35,51^AdN,0,0^FWN^FH^FDFlagship^FS +^FO35,71^AdN,0,0^FWN^FH^FD79 Spike St.^FS +^FO35,92^AdN,0,0^FWN^FH^FD^FS +^FO35,112^AdN,0,0^FWN^FH^FDNorcross, GA 30071^FS +^FO35,132^AdN,0,0^FWN^FH^FD(123) 456-7890^FS +^FO490,31^AdN,0,0^FWN^FH^FDShip Date: 31DEC07^FS +^FO490,51^AdN,0,0^FWN^FH^FDActWgt: 10.5 LB^FS +^FO490,72^AdN,0,0^FWN^FH^FDSystem#: 1195167/WSXI0100^FS +^FO490,91^AdN,0,0^FWN^FH^FDAccount: S *********^FS +^FO43,158^A0N,25,27^FWN^FH^FDSteve Jobs^FS +^FO615,156^AdN,0,0^FWN^FH^FD(123) 456-7890^FS +^FO43,193^A0N,25,27^FWN^FH^FDApple Inc.^FS +^FO43,228^A0N,25,27^FWN^FH^FD1 Infinite Loop.^FS +^FO43,263^A0N,25,27^FWN^FH^FD^FS +^FO43,296^A0N,30,30^FWN^FH^FDCupertino, CA 95014^FS +^FO530,296^A0N,35,45^FWN^FH^FD(US)^FS +^FO725,216^AdN,0,0^FWN^FH^FDGround^FS +^FO670,238^GB105,10,10^FS +^FO670,248^GB10,112,10^FS +^FO765,248^GB10,112,10^FS +^FO670,360^GB105,10,10^FS +^FO650,173^A0N,50,55^FWN^FH^FDFedEx^FS +^FO690,256^A0N,130,130^FWN^FH^FDG^FS +^FO476,3^GB4,145,4^FS +^FO10,358^A0N,15,15^FWN^FH^FDRef:^FS +^FO406,388^A0N,15,15^FWN^FH^FDDept: Work^FS +^FO10,373^A0N,15,15^FWN^FH^FDInv:^FS +^FO10,388^A0N,15,15^FWN^FH^FDPO:^FS +^FO625,495^A0N,20,35^FWN^FH^FD1^FS +^FO640,535^A0N,20,35^FWN^FH^FDof^FS +^FO625,575^A0N,20,35^FWN^FH^FD1^FS +^FO80,771^BY4,2^BCN,290,N,N,N,N^FWN^FD>;>89612019333075610004019^FS +^FO135,1083^A0N,25,27^FWN^FH^FD(9612019) 3330756 10004019^FS +^FO783,288^A0N,15,15^FWB^FH^FDCLS090607/23/23^FS +^FO10,162^A0N,20,18^FWN^FH^FDTO^FS +^FO25,1108^A0N,50,55^FWN^FH^FDGND^FS +^FO25,1150^A0N,35,45^FWN^FH^FDPrepaid^FS +^FO300,1115^A0N,35,45^FWN^FH^FD^FS +^FO300,1149^A0N,35,45^FWN^FH^FD^FS +^FO30,428^BY2,2^B7N,10,5,12^FH^FWN^FH^FD[)>_1E01_1D02950142083_1D840_1D019_1D333075610004019_1DFDEB_1D3330756_1D365_1D _1D1/1_1D10.5LB_1DN_1D1 Infinite Loop._1DCupertino_1DCA_1DSteve Jobs_1E06_1D10ZGD004_1D11ZApple Inc._1D12Z1234567890_1D23ZN_1D22Z _1CN_1D20Z _1C0_1D29Z_1D28Z_1D26Z3632_1C_1D_1E_04^FS +^PQ1 +^XZ \ No newline at end of file diff --git a/tray/assets/zpl_sample.xml b/tray/assets/zpl_sample.xml new file mode 100644 index 0000000..ef0f51c --- /dev/null +++ b/tray/assets/zpl_sample.xml @@ -0,0 +1,228 @@ + + + + + + + + + NOTE + + NOTE + ship + 6028 + LabelPrintingOrientationType not specified - using default value of N + LabelPrintingOrientationType not specified - using default value of N + + + Ship Service V7 + + + ship + 7 + 0 + 0 + + + true + FDXG + GROUND + YOUR_PACKAGING + + TWO_DAYS + + 019 + + PAYOR_ACCOUNT + + PAYOR_ACCOUNT + 4 + ACTUAL + 0 + 5.25 + + LB + 36.0 + + + USD + 13.74 + + + USD + 7.0 + + + USD + 6.74 + + + USD + 0.35 + + + USD + 7.09 + + + USD + 0.0 + + + USD + 7.09 + + + USD + 0.0 + + + VOLUME + Base + + USD + 4.38 + + 0.0 + + + VOLUME + Matrix + + USD + 2.62 + + 0.0 + + + INSURED_VALUE + PACKAGE + Insured value + + USD + 0.0 + + + + FUEL + PACKAGE + FedEx Ground Fuel + + USD + 0.35 + + + + + false + + 1 + + GROUND + 422268010001723 + + 0 + + PAYOR_ACCOUNT + + PAYOR_ACCOUNT + ACTUAL + + LB + 36.0 + + + USD + 13.74 + + + USD + 7.0 + + + USD + 6.74 + + + USD + 0.35 + + + USD + 7.09 + + + USD + 0.0 + + + USD + 7.09 + + + USD + 0.0 + + + VOLUME + Base + + USD + 4.38 + + 0.0 + + + VOLUME + Matrix + + USD + 2.62 + + 0.0 + + + INSURED_VALUE + PACKAGE + Insured value + + USD + 0.0 + + + + FUEL + PACKAGE + FedEx Ground Fuel + + USD + 0.35 + + + + + + + COMMON_2D + Wyk+HjAxHTAyMzgwMTcdODQwHTAxOR0wMDAwMDAwMDAwMDAwMDAdRkRFQh0wMDAwMDAwHTMzNR0dMS8xHTM2LjBMQh1OHVJlY2lwaWVudCBBZGRyZXNzIExpbmUgMR1Db2xsaWVydmlsbGUdVE4dUmVjaXBpZW50IENvbnRhY3QeMDYdMTBaR0QwMDQdMTJaMTIzNDU2Nzg5MB0xNFoqKlRFU1QgTEFCRUwgLSBETyBOT1QgU0hJUCoqHTIzWk4dMjJaHE4dMjBaIBw4MB0yNlo1MzEwHB0eBA== + + + GROUND + 9612019000000000000000 + + + + OUTBOUND_LABEL + 1 + + 1 + XlhBXkNGLDAsMCwwXlBSMTJeTUQzMF5QVzgwMF5QT0leQ0kxM15MSDAsMjAKXkZPMCwxNDdeR0I4MDAsNCw0XkZTCl5GTzAsNDAxXkdCODAwLDQsNF5GUwpeRk8wLDczNl5HQjgwMCw0LDReRlMKXkZPMzUsMTJeQWROLDAsMF5GV05eRkheRkRGcm9tOl5GUwpeRk8zNSwzMV5BZE4sMCwwXkZXTl5GSF5GRF5GUwpeRk8zNSw1MV5BZE4sMCwwXkZXTl5GSF5GRFRHS15GUwpeRk8zNSw3MV5BZE4sMCwwXkZXTl5GSF5GRFNOMjAwMCBUZXN0IE1ldGVyIDheRlMKXkZPMzUsOTJeQWROLDAsMF5GV05eRkheRkQxMCBGZWRleCBQYXJrd2F5XkZTCl5GTzM1LDEzMl5BZE4sMCwwXkZXTl5GSF5GRCgxMjMpIDQ1Ni03ODkwXkZTCl5GTzQ5MCw3Ml5BZE4sMCwwXkZXTl5GSF5GRENBRDogMTE4NTAwOTA3L1dTWEkyMzUwXkZTCl5GTzQzLDE5M15BME4sMjUsMjdeRldOXkZIXkZEXkZTCl5GTzQzLDIyOF5BME4sMjUsMjdeRldOXkZIXkZEUmVjaXBpZW50IEFkZHJlc3MgTGluZSAxXkZTCl5GTzQzLDI2M15BME4sMjUsMjdeRldOXkZIXkZEKipURVNUIExBQkVMIC0gRE8gTk9UIFNISVAqKl5GUwpeRk8zNSwxMTJeQWROLDAsMF5GV05eRkheRkRBdXN0aW4sIFRYIDczMzAxXkZTCl5GTzQ5MCwzMV5BZE4sMCwwXkZXTl5GSF5GRFNoaXAgRGF0ZTogMDFERUMwOV5GUwpeRk80OTAsNTFeQWROLDAsMF5GV05eRkheRkRBY3RXZ3Q6IDM2LjAgTEJeRlMKXkZPNDkwLDkxXkFkTiwwLDBeRldOXkZIXkZEQWNjb3VudDogUyAyOTY1NTY5NjNeRlMKXkZPNDMsMTU4XkEwTiwyNSwyN15GV05eRkheRkRSZWNpcGllbnQgQ29udGFjdF5GUwpeRk82MTUsMTU2XkFkTiwwLDBeRldOXkZIXkZEKDEyMykgNDU2LTc4OTBeRlMKXkZPNDMsMjk2XkEwTiwzMCwzMF5GV05eRkheRkRDb2xsaWVydmlsbGUsIFROIDM4MDE3XkZTCl5GTzUzMCwyOTZeQTBOLDM1LDQ1XkZXTl5GSF5GRChVUyleRlMKXkZPNzI1LDIxNl5BZE4sMCwwXkZXTl5GSF5GREdyb3VuZF5GUwpeRk82NzAsMjM4XkdCMTA1LDEwLDEwXkZTCl5GTzY3MCwyNDheR0IxMCwxMTIsMTBeRlMKXkZPNzY1LDI0OF5HQjEwLDExMiwxMF5GUwpeRk82NzAsMzYwXkdCMTA1LDEwLDEwXkZTCl5GTzQ3NiwzXkdCNCwxNDUsNF5GUwpeRk82NTAsMTczXkEwTiw1MCw1NV5GV05eRkheRkRGZWRFeF5GUwpeRk82OTAsMjU2XkEwTiwxMzAsMTMwXkZXTl5GSF5GREdeRlMKXkZPODAsNzcxXkJZNCwyXkJDTiwyOTAsTixOLE4sTl5GV05eRkQ+Oz44OTYxMjAxOTAwMDAwMDAwMDAwMDAwMF5GUwpeRk8xMzUsMTA4M15BME4sMjUsMjdeRldOXkZIXkZEKDk2MTIwMTkpIDAwMDAwMDAgIDAwMDAwMDAwXkZTCl5GTzc4MywyNTheQTBOLDE1LDE1XkZXQl5GSF5GREowOTMwMDkwNzMxMjEyM15GUwpeRk8xMCwxNjJeQTBOLDIwLDE4XkZXTl5GSF5GRFRPXkZTCl5GTzI1LDExMDheQTBOLDUwLDU1XkZXTl5GSF5GREdORF5GUwpeRk8yNSwxMTUwXkEwTiwzNSw0NV5GV05eRkheRkRQcmVwYWlkXkZTCl5GTzMwMCwxMTE1XkEwTiwzNSw0NV5GV05eRkheRkReRlMKXkZPMzAwLDExNDleQTBOLDM1LDQ1XkZXTl5GSF5GRF5GUwpeRk8zMCw0MjheQlkyLDJeQjdOLDEwLDUsMTJeRkheRldOXkZIXkZEWyk+XzFFMDFfMUQwMjM4MDE3XzFEODQwXzFEMDE5XzFEMDAwMDAwMDAwMDAwMDAwXzFERkRFQl8xRDAwMDAwMDBfMUQzMzVfMURfMUQxLzFfMUQzNi4wTEJfMUROXzFEUmVjaXBpZW50IEFkZHJlc3MgTGluZSAxXzFEQ29sbGllcnZpbGxlXzFEVE5fMURSZWNpcGllbnQgQ29udGFjdF8xRTA2XzFEMTBaR0QwMDRfMUQxMloxMjM0NTY3ODkwXzFEMTRaKipURVNUIExBQkVMIC0gRE8gTk9UIFNISVAqKl8xRDIzWk5fMUQyMlpfMUNOXzFEMjBaIF8xQzgwXzFEMjZaNTMxMF8xQ18xRF8xRV8wNF5GUwpeRk8xNiwzNTheQTBOLDE1LDE1XkZXTl5GSF5GRFJlZjogXkZTCl5GTzE2LDM3M15BME4sMTUsMTVeRldOXkZIXkZESU5WOiBeRlMKXkZPMTYsMzg4XkEwTiwxNSwxNV5GV05eRkheRkRQTzogXkZTCl5GTzQwNiwzODheQTBOLDE1LDE1XkZXTl5GSF5GRERlcHQ6IF5GUwpeRk82MjUsNDk1XkEwTiwyMCwzNV5GV05eRkheRkQxXkZTCl5GTzY0MCw1MzVeQTBOLDIwLDM1XkZXTl5GSF5GRG9mXkZTCl5GTzYyNSw1NzVeQTBOLDIwLDM1XkZXTl5GSF5GRDFeRlMKXlBRMQpeWFoK + + + SERVICE_DEFAULT + + + + + + diff --git a/tray/build.xml b/tray/build.xml new file mode 100644 index 0000000..a4b4912 --- /dev/null +++ b/tray/build.xml @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + Process complete + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Building jar + + + Stripping jlink-incompatible files + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copying ${target.fx.dir} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bundling with manual cert for signing auth: ${authcert.use} + + + + + + + + Copying certificate(s) to dist/whitelist: ${whitelist.use} + + + + + + + + + + Copying demo resource files to ${demo.dir} + + + + + + + + + + + + + + + + + + + + The "include-assets" task is deprecated, please use "build-demo" instead. + + + + + + + Found provision file '${provision.file}' calling 'provision --json [...]' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Downloading and bundling the jre for ${target.os} + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tray/css/bootstrap.min.css b/tray/css/bootstrap.min.css new file mode 100644 index 0000000..bf9bbf7 --- /dev/null +++ b/tray/css/bootstrap.min.css @@ -0,0 +1,7199 @@ +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100% +} + +body { + margin: 0 +} + +article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { + display: block +} + +audio, canvas, progress, video { + display: inline-block; + vertical-align: baseline +} + +audio:not([controls]) { + display: none; + height: 0 +} + +[hidden], template { + display: none +} + +a { + background-color: transparent +} + +a:active, a:hover { + outline: 0 +} + +abbr[title] { + border-bottom: 1px dotted +} + +b, strong { + font-weight: 700 +} + +dfn { + font-style: italic +} + +h1 { + margin: .67em 0; + font-size: 2em +} + +mark { + color: #000; + background: #FF0 +} + +small { + font-size: 80% +} + +sub, sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline +} + +sup { + top: -.5em +} + +sub { + bottom: -.25em +} + +img { + border: 0 +} + +svg:not(:root) { + overflow: hidden +} + +figure { + margin: 1em 40px +} + +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box +} + +pre { + overflow: auto +} + +code, kbd, pre, samp { + font-family: monospace, monospace; + font-size: 1em +} + +button, input, optgroup, select, textarea { + margin: 0; + font: inherit; + color: inherit +} + +button { + overflow: visible +} + +button, select { + text-transform: none +} + +button, html input[type=button], input[type=reset], input[type=submit] { + -webkit-appearance: button; + cursor: pointer +} + +button[disabled], html input[disabled] { + cursor: default +} + +button::-moz-focus-inner, input::-moz-focus-inner { + padding: 0; + border: 0 +} + +input { + line-height: normal +} + +input[type=checkbox], input[type=radio] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0 +} + +input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button { + height: auto +} + +input[type=search] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield +} + +input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-decoration { + -webkit-appearance: none +} + +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid silver +} + +legend { + padding: 0; + border: 0 +} + +textarea { + overflow: auto +} + +optgroup { + font-weight: 700 +} + +table { + border-spacing: 0; + border-collapse: collapse +} + +td, th { + padding: 0 +} + +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, :after, :before { + color: #000 !important; + text-shadow: none !important; + background: 0 0 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important + } + + a, a:visited { + text-decoration: underline + } + + a[href]:after { + content: " (" attr(href) ")" + } + + abbr[title]:after { + content: " (" attr(title) ")" + } + + a[href^="javascript:"]:after, a[href^="#"]:after { + content: "" + } + + blockquote, pre { + border: 1px solid #999; + page-break-inside: avoid + } + + thead { + display: table-header-group + } + + img, tr { + page-break-inside: avoid + } + + img { + max-width: 100% !important + } + + h2, h3, p { + orphans: 3; + widows: 3 + } + + h2, h3 { + page-break-after: avoid + } + + .navbar { + display: none + } + + .btn > .caret, .dropup > .btn > .caret { + border-top-color: #000 !important + } + + .label { + border: 1px solid #000 + } + + .table { + border-collapse: collapse !important + } + + .table td, .table th { + background-color: #FFF !important + } + + .table-bordered td, .table-bordered th { + border: 1px solid #DDD !important + } +} + +@font-face { + font-family: 'Glyphicons Halflings'; + src: url(../fonts/glyphicons-halflings-regular.eot); + src: url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'), url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'), url(../fonts/glyphicons-halflings-regular.woff) format('woff'), url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'), url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg') +} + +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: 400; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale +} + +.glyphicon-asterisk:before { + content: "\2a" +} + +.glyphicon-plus:before { + content: "\2b" +} + +.glyphicon-eur:before, .glyphicon-euro:before { + content: "\20ac" +} + +.glyphicon-minus:before { + content: "\2212" +} + +.glyphicon-cloud:before { + content: "\2601" +} + +.glyphicon-envelope:before { + content: "\2709" +} + +.glyphicon-pencil:before { + content: "\270f" +} + +.glyphicon-glass:before { + content: "\e001" +} + +.glyphicon-music:before { + content: "\e002" +} + +.glyphicon-search:before { + content: "\e003" +} + +.glyphicon-heart:before { + content: "\e005" +} + +.glyphicon-star:before { + content: "\e006" +} + +.glyphicon-star-empty:before { + content: "\e007" +} + +.glyphicon-user:before { + content: "\e008" +} + +.glyphicon-film:before { + content: "\e009" +} + +.glyphicon-th-large:before { + content: "\e010" +} + +.glyphicon-th:before { + content: "\e011" +} + +.glyphicon-th-list:before { + content: "\e012" +} + +.glyphicon-ok:before { + content: "\e013" +} + +.glyphicon-remove:before { + content: "\e014" +} + +.glyphicon-zoom-in:before { + content: "\e015" +} + +.glyphicon-zoom-out:before { + content: "\e016" +} + +.glyphicon-off:before { + content: "\e017" +} + +.glyphicon-signal:before { + content: "\e018" +} + +.glyphicon-cog:before { + content: "\e019" +} + +.glyphicon-trash:before { + content: "\e020" +} + +.glyphicon-home:before { + content: "\e021" +} + +.glyphicon-file:before { + content: "\e022" +} + +.glyphicon-time:before { + content: "\e023" +} + +.glyphicon-road:before { + content: "\e024" +} + +.glyphicon-download-alt:before { + content: "\e025" +} + +.glyphicon-download:before { + content: "\e026" +} + +.glyphicon-upload:before { + content: "\e027" +} + +.glyphicon-inbox:before { + content: "\e028" +} + +.glyphicon-play-circle:before { + content: "\e029" +} + +.glyphicon-repeat:before { + content: "\e030" +} + +.glyphicon-refresh:before { + content: "\e031" +} + +.glyphicon-list-alt:before { + content: "\e032" +} + +.glyphicon-lock:before { + content: "\e033" +} + +.glyphicon-flag:before { + content: "\e034" +} + +.glyphicon-headphones:before { + content: "\e035" +} + +.glyphicon-volume-off:before { + content: "\e036" +} + +.glyphicon-volume-down:before { + content: "\e037" +} + +.glyphicon-volume-up:before { + content: "\e038" +} + +.glyphicon-qrcode:before { + content: "\e039" +} + +.glyphicon-barcode:before { + content: "\e040" +} + +.glyphicon-tag:before { + content: "\e041" +} + +.glyphicon-tags:before { + content: "\e042" +} + +.glyphicon-book:before { + content: "\e043" +} + +.glyphicon-bookmark:before { + content: "\e044" +} + +.glyphicon-print:before { + content: "\e045" +} + +.glyphicon-camera:before { + content: "\e046" +} + +.glyphicon-font:before { + content: "\e047" +} + +.glyphicon-bold:before { + content: "\e048" +} + +.glyphicon-italic:before { + content: "\e049" +} + +.glyphicon-text-height:before { + content: "\e050" +} + +.glyphicon-text-width:before { + content: "\e051" +} + +.glyphicon-align-left:before { + content: "\e052" +} + +.glyphicon-align-center:before { + content: "\e053" +} + +.glyphicon-align-right:before { + content: "\e054" +} + +.glyphicon-align-justify:before { + content: "\e055" +} + +.glyphicon-list:before { + content: "\e056" +} + +.glyphicon-indent-left:before { + content: "\e057" +} + +.glyphicon-indent-right:before { + content: "\e058" +} + +.glyphicon-facetime-video:before { + content: "\e059" +} + +.glyphicon-picture:before { + content: "\e060" +} + +.glyphicon-map-marker:before { + content: "\e062" +} + +.glyphicon-adjust:before { + content: "\e063" +} + +.glyphicon-tint:before { + content: "\e064" +} + +.glyphicon-edit:before { + content: "\e065" +} + +.glyphicon-share:before { + content: "\e066" +} + +.glyphicon-check:before { + content: "\e067" +} + +.glyphicon-move:before { + content: "\e068" +} + +.glyphicon-step-backward:before { + content: "\e069" +} + +.glyphicon-fast-backward:before { + content: "\e070" +} + +.glyphicon-backward:before { + content: "\e071" +} + +.glyphicon-play:before { + content: "\e072" +} + +.glyphicon-pause:before { + content: "\e073" +} + +.glyphicon-stop:before { + content: "\e074" +} + +.glyphicon-forward:before { + content: "\e075" +} + +.glyphicon-fast-forward:before { + content: "\e076" +} + +.glyphicon-step-forward:before { + content: "\e077" +} + +.glyphicon-eject:before { + content: "\e078" +} + +.glyphicon-chevron-left:before { + content: "\e079" +} + +.glyphicon-chevron-right:before { + content: "\e080" +} + +.glyphicon-plus-sign:before { + content: "\e081" +} + +.glyphicon-minus-sign:before { + content: "\e082" +} + +.glyphicon-remove-sign:before { + content: "\e083" +} + +.glyphicon-ok-sign:before { + content: "\e084" +} + +.glyphicon-question-sign:before { + content: "\e085" +} + +.glyphicon-info-sign:before { + content: "\e086" +} + +.glyphicon-screenshot:before { + content: "\e087" +} + +.glyphicon-remove-circle:before { + content: "\e088" +} + +.glyphicon-ok-circle:before { + content: "\e089" +} + +.glyphicon-ban-circle:before { + content: "\e090" +} + +.glyphicon-arrow-left:before { + content: "\e091" +} + +.glyphicon-arrow-right:before { + content: "\e092" +} + +.glyphicon-arrow-up:before { + content: "\e093" +} + +.glyphicon-arrow-down:before { + content: "\e094" +} + +.glyphicon-share-alt:before { + content: "\e095" +} + +.glyphicon-resize-full:before { + content: "\e096" +} + +.glyphicon-resize-small:before { + content: "\e097" +} + +.glyphicon-exclamation-sign:before { + content: "\e101" +} + +.glyphicon-gift:before { + content: "\e102" +} + +.glyphicon-leaf:before { + content: "\e103" +} + +.glyphicon-fire:before { + content: "\e104" +} + +.glyphicon-eye-open:before { + content: "\e105" +} + +.glyphicon-eye-close:before { + content: "\e106" +} + +.glyphicon-warning-sign:before { + content: "\e107" +} + +.glyphicon-plane:before { + content: "\e108" +} + +.glyphicon-calendar:before { + content: "\e109" +} + +.glyphicon-random:before { + content: "\e110" +} + +.glyphicon-comment:before { + content: "\e111" +} + +.glyphicon-magnet:before { + content: "\e112" +} + +.glyphicon-chevron-up:before { + content: "\e113" +} + +.glyphicon-chevron-down:before { + content: "\e114" +} + +.glyphicon-retweet:before { + content: "\e115" +} + +.glyphicon-shopping-cart:before { + content: "\e116" +} + +.glyphicon-folder-close:before { + content: "\e117" +} + +.glyphicon-folder-open:before { + content: "\e118" +} + +.glyphicon-resize-vertical:before { + content: "\e119" +} + +.glyphicon-resize-horizontal:before { + content: "\e120" +} + +.glyphicon-hdd:before { + content: "\e121" +} + +.glyphicon-bullhorn:before { + content: "\e122" +} + +.glyphicon-bell:before { + content: "\e123" +} + +.glyphicon-certificate:before { + content: "\e124" +} + +.glyphicon-thumbs-up:before { + content: "\e125" +} + +.glyphicon-thumbs-down:before { + content: "\e126" +} + +.glyphicon-hand-right:before { + content: "\e127" +} + +.glyphicon-hand-left:before { + content: "\e128" +} + +.glyphicon-hand-up:before { + content: "\e129" +} + +.glyphicon-hand-down:before { + content: "\e130" +} + +.glyphicon-circle-arrow-right:before { + content: "\e131" +} + +.glyphicon-circle-arrow-left:before { + content: "\e132" +} + +.glyphicon-circle-arrow-up:before { + content: "\e133" +} + +.glyphicon-circle-arrow-down:before { + content: "\e134" +} + +.glyphicon-globe:before { + content: "\e135" +} + +.glyphicon-wrench:before { + content: "\e136" +} + +.glyphicon-tasks:before { + content: "\e137" +} + +.glyphicon-filter:before { + content: "\e138" +} + +.glyphicon-briefcase:before { + content: "\e139" +} + +.glyphicon-fullscreen:before { + content: "\e140" +} + +.glyphicon-dashboard:before { + content: "\e141" +} + +.glyphicon-paperclip:before { + content: "\e142" +} + +.glyphicon-heart-empty:before { + content: "\e143" +} + +.glyphicon-link:before { + content: "\e144" +} + +.glyphicon-phone:before { + content: "\e145" +} + +.glyphicon-pushpin:before { + content: "\e146" +} + +.glyphicon-usd:before { + content: "\e148" +} + +.glyphicon-gbp:before { + content: "\e149" +} + +.glyphicon-sort:before { + content: "\e150" +} + +.glyphicon-sort-by-alphabet:before { + content: "\e151" +} + +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152" +} + +.glyphicon-sort-by-order:before { + content: "\e153" +} + +.glyphicon-sort-by-order-alt:before { + content: "\e154" +} + +.glyphicon-sort-by-attributes:before { + content: "\e155" +} + +.glyphicon-sort-by-attributes-alt:before { + content: "\e156" +} + +.glyphicon-unchecked:before { + content: "\e157" +} + +.glyphicon-expand:before { + content: "\e158" +} + +.glyphicon-collapse-down:before { + content: "\e159" +} + +.glyphicon-collapse-up:before { + content: "\e160" +} + +.glyphicon-log-in:before { + content: "\e161" +} + +.glyphicon-flash:before { + content: "\e162" +} + +.glyphicon-log-out:before { + content: "\e163" +} + +.glyphicon-new-window:before { + content: "\e164" +} + +.glyphicon-record:before { + content: "\e165" +} + +.glyphicon-save:before { + content: "\e166" +} + +.glyphicon-open:before { + content: "\e167" +} + +.glyphicon-saved:before { + content: "\e168" +} + +.glyphicon-import:before { + content: "\e169" +} + +.glyphicon-export:before { + content: "\e170" +} + +.glyphicon-send:before { + content: "\e171" +} + +.glyphicon-floppy-disk:before { + content: "\e172" +} + +.glyphicon-floppy-saved:before { + content: "\e173" +} + +.glyphicon-floppy-remove:before { + content: "\e174" +} + +.glyphicon-floppy-save:before { + content: "\e175" +} + +.glyphicon-floppy-open:before { + content: "\e176" +} + +.glyphicon-credit-card:before { + content: "\e177" +} + +.glyphicon-transfer:before { + content: "\e178" +} + +.glyphicon-cutlery:before { + content: "\e179" +} + +.glyphicon-header:before { + content: "\e180" +} + +.glyphicon-compressed:before { + content: "\e181" +} + +.glyphicon-earphone:before { + content: "\e182" +} + +.glyphicon-phone-alt:before { + content: "\e183" +} + +.glyphicon-tower:before { + content: "\e184" +} + +.glyphicon-stats:before { + content: "\e185" +} + +.glyphicon-sd-video:before { + content: "\e186" +} + +.glyphicon-hd-video:before { + content: "\e187" +} + +.glyphicon-subtitles:before { + content: "\e188" +} + +.glyphicon-sound-stereo:before { + content: "\e189" +} + +.glyphicon-sound-dolby:before { + content: "\e190" +} + +.glyphicon-sound-5-1:before { + content: "\e191" +} + +.glyphicon-sound-6-1:before { + content: "\e192" +} + +.glyphicon-sound-7-1:before { + content: "\e193" +} + +.glyphicon-copyright-mark:before { + content: "\e194" +} + +.glyphicon-registration-mark:before { + content: "\e195" +} + +.glyphicon-cloud-download:before { + content: "\e197" +} + +.glyphicon-cloud-upload:before { + content: "\e198" +} + +.glyphicon-tree-conifer:before { + content: "\e199" +} + +.glyphicon-tree-deciduous:before { + content: "\e200" +} + +.glyphicon-cd:before { + content: "\e201" +} + +.glyphicon-save-file:before { + content: "\e202" +} + +.glyphicon-open-file:before { + content: "\e203" +} + +.glyphicon-level-up:before { + content: "\e204" +} + +.glyphicon-copy:before { + content: "\e205" +} + +.glyphicon-paste:before { + content: "\e206" +} + +.glyphicon-alert:before { + content: "\e209" +} + +.glyphicon-equalizer:before { + content: "\e210" +} + +.glyphicon-king:before { + content: "\e211" +} + +.glyphicon-queen:before { + content: "\e212" +} + +.glyphicon-pawn:before { + content: "\e213" +} + +.glyphicon-bishop:before { + content: "\e214" +} + +.glyphicon-knight:before { + content: "\e215" +} + +.glyphicon-baby-formula:before { + content: "\e216" +} + +.glyphicon-tent:before { + content: "\26fa" +} + +.glyphicon-blackboard:before { + content: "\e218" +} + +.glyphicon-bed:before { + content: "\e219" +} + +.glyphicon-apple:before { + content: "\f8ff" +} + +.glyphicon-erase:before { + content: "\e221" +} + +.glyphicon-hourglass:before { + content: "\231b" +} + +.glyphicon-lamp:before { + content: "\e223" +} + +.glyphicon-duplicate:before { + content: "\e224" +} + +.glyphicon-piggy-bank:before { + content: "\e225" +} + +.glyphicon-scissors:before { + content: "\e226" +} + +.glyphicon-bitcoin:before { + content: "\e227" +} + +.glyphicon-btc:before { + content: "\e227" +} + +.glyphicon-xbt:before { + content: "\e227" +} + +.glyphicon-yen:before { + content: "\00a5" +} + +.glyphicon-jpy:before { + content: "\00a5" +} + +.glyphicon-ruble:before { + content: "\20bd" +} + +.glyphicon-rub:before { + content: "\20bd" +} + +.glyphicon-scale:before { + content: "\e230" +} + +.glyphicon-ice-lolly:before { + content: "\e231" +} + +.glyphicon-ice-lolly-tasted:before { + content: "\e232" +} + +.glyphicon-education:before { + content: "\e233" +} + +.glyphicon-option-horizontal:before { + content: "\e234" +} + +.glyphicon-option-vertical:before { + content: "\e235" +} + +.glyphicon-menu-hamburger:before { + content: "\e236" +} + +.glyphicon-modal-window:before { + content: "\e237" +} + +.glyphicon-oil:before { + content: "\e238" +} + +.glyphicon-grain:before { + content: "\e239" +} + +.glyphicon-sunglasses:before { + content: "\e240" +} + +.glyphicon-text-size:before { + content: "\e241" +} + +.glyphicon-text-color:before { + content: "\e242" +} + +.glyphicon-text-background:before { + content: "\e243" +} + +.glyphicon-object-align-top:before { + content: "\e244" +} + +.glyphicon-object-align-bottom:before { + content: "\e245" +} + +.glyphicon-object-align-horizontal:before { + content: "\e246" +} + +.glyphicon-object-align-left:before { + content: "\e247" +} + +.glyphicon-object-align-vertical:before { + content: "\e248" +} + +.glyphicon-object-align-right:before { + content: "\e249" +} + +.glyphicon-triangle-right:before { + content: "\e250" +} + +.glyphicon-triangle-left:before { + content: "\e251" +} + +.glyphicon-triangle-bottom:before { + content: "\e252" +} + +.glyphicon-triangle-top:before { + content: "\e253" +} + +.glyphicon-console:before { + content: "\e254" +} + +.glyphicon-superscript:before { + content: "\e255" +} + +.glyphicon-subscript:before { + content: "\e256" +} + +.glyphicon-menu-left:before { + content: "\e257" +} + +.glyphicon-menu-right:before { + content: "\e258" +} + +.glyphicon-menu-down:before { + content: "\e259" +} + +.glyphicon-menu-up:before { + content: "\e260" +} + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +:after, :before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +html { + font-size: 10px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0) +} + +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #FFF +} + +button, input, select, textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit +} + +a { + color: #337AB7; + text-decoration: none +} + +a:focus, a:hover { + color: #23527C; + text-decoration: underline +} + +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +figure { + margin: 0 +} + +img { + vertical-align: middle +} + +.carousel-inner > .item > a > img, .carousel-inner > .item > img, .img-responsive, .thumbnail a > img, .thumbnail > img { + display: block; + max-width: 100%; + height: auto +} + +.img-rounded { + border-radius: 6px +} + +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #FFF; + border: 1px solid #DDD; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out +} + +.img-circle { + border-radius: 50% +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #EEE +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0 +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto +} + +[role=button] { + cursor: pointer +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit +} + +.h1 .small, .h1 small, .h2 .small, .h2 small, .h3 .small, .h3 small, .h4 .small, .h4 small, .h5 .small, .h5 small, .h6 .small, .h6 small, h1 .small, h1 small, h2 .small, h2 small, h3 .small, h3 small, h4 .small, h4 small, h5 .small, h5 small, h6 .small, h6 small { + font-weight: 400; + line-height: 1; + color: #777 +} + +.h1, .h2, .h3, h1, h2, h3 { + margin-top: 20px; + margin-bottom: 10px +} + +.h1 .small, .h1 small, .h2 .small, .h2 small, .h3 .small, .h3 small, h1 .small, h1 small, h2 .small, h2 small, h3 .small, h3 small { + font-size: 65% +} + +.h4, .h5, .h6, h4, h5, h6 { + margin-top: 10px; + margin-bottom: 10px +} + +.h4 .small, .h4 small, .h5 .small, .h5 small, .h6 .small, .h6 small, h4 .small, h4 small, h5 .small, h5 small, h6 .small, h6 small { + font-size: 75% +} + +.h1, h1 { + font-size: 36px +} + +.h2, h2 { + font-size: 30px +} + +.h3, h3 { + font-size: 24px +} + +.h4, h4 { + font-size: 18px +} + +.h5, h5 { + font-size: 14px +} + +.h6, h6 { + font-size: 12px +} + +p { + margin: 0 0 10px +} + +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4 +} + +@media (min-width: 768px) { + .lead { + font-size: 21px + } +} + +.small, small { + font-size: 85% +} + +.mark, mark { + padding: .2em; + background-color: #FCF8E3 +} + +.text-left { + text-align: left +} + +.text-right { + text-align: right +} + +.text-center { + text-align: center +} + +.text-justify { + text-align: justify +} + +.text-nowrap { + white-space: nowrap +} + +.text-lowercase { + text-transform: lowercase +} + +.text-uppercase { + text-transform: uppercase +} + +.text-capitalize { + text-transform: capitalize +} + +.text-muted { + color: #777 +} + +.text-primary { + color: #337AB7 +} + +a.text-primary:focus, a.text-primary:hover { + color: #286090 +} + +.text-success { + color: #3C763D +} + +a.text-success:focus, a.text-success:hover { + color: #2B542C +} + +.text-info { + color: #31708F +} + +a.text-info:focus, a.text-info:hover { + color: #245269 +} + +.text-warning { + color: #8A6D3B +} + +a.text-warning:focus, a.text-warning:hover { + color: #66512C +} + +.text-danger { + color: #A94442 +} + +a.text-danger:focus, a.text-danger:hover { + color: #843534 +} + +.bg-primary { + color: #FFF; + background-color: #337AB7 +} + +a.bg-primary:focus, a.bg-primary:hover { + background-color: #286090 +} + +.bg-success { + background-color: #DFF0D8 +} + +a.bg-success:focus, a.bg-success:hover { + background-color: #C1E2B3 +} + +.bg-info { + background-color: #D9EDF7 +} + +a.bg-info:focus, a.bg-info:hover { + background-color: #AFD9EE +} + +.bg-warning { + background-color: #FCF8E3 +} + +a.bg-warning:focus, a.bg-warning:hover { + background-color: #F7ECB5 +} + +.bg-danger { + background-color: #F2DEDE +} + +a.bg-danger:focus, a.bg-danger:hover { + background-color: #E4B9B9 +} + +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #EEE +} + +ol, ul { + margin-top: 0; + margin-bottom: 10px +} + +ol ol, ol ul, ul ol, ul ul { + margin-bottom: 0 +} + +.list-unstyled { + padding-left: 0; + list-style: none +} + +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none +} + +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px +} + +dl { + margin-top: 0; + margin-bottom: 20px +} + +dd, dt { + line-height: 1.42857143 +} + +dt { + font-weight: 700 +} + +dd { + margin-left: 0 +} + +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap + } + + .dl-horizontal dd { + margin-left: 180px + } +} + +abbr[data-original-title], abbr[title] { + cursor: help; + border-bottom: 1px dotted #777 +} + +.initialism { + font-size: 90%; + text-transform: uppercase +} + +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #EEE +} + +blockquote ol:last-child, blockquote p:last-child, blockquote ul:last-child { + margin-bottom: 0 +} + +blockquote .small, blockquote footer, blockquote small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777 +} + +blockquote .small:before, blockquote footer:before, blockquote small:before { + content: '\2014 \00A0' +} + +.blockquote-reverse, blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #EEE; + border-left: 0 +} + +.blockquote-reverse .small:before, .blockquote-reverse footer:before, .blockquote-reverse small:before, blockquote.pull-right .small:before, blockquote.pull-right footer:before, blockquote.pull-right small:before { + content: '' +} + +.blockquote-reverse .small:after, .blockquote-reverse footer:after, .blockquote-reverse small:after, blockquote.pull-right .small:after, blockquote.pull-right footer:after, blockquote.pull-right small:after { + content: '\00A0 \2014' +} + +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143 +} + +code, kbd, pre, samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace +} + +code { + padding: 2px 4px; + font-size: 90%; + color: #C7254E; + background-color: #F9F2F4; + border-radius: 4px +} + +kbd { + padding: 2px 4px; + font-size: 90%; + color: #FFF; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25) +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700; + -webkit-box-shadow: none; + box-shadow: none +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #F5F5F5; + border: 1px solid #CCC; + border-radius: 4px +} + +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0 +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll +} + +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto +} + +@media (min-width: 768px) { + .container { + width: 750px + } +} + +@media (min-width: 992px) { + .container { + width: 970px + } +} + +@media (min-width: 1200px) { + .container { + width: 1170px + } +} + +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto +} + +.row { + margin-right: -15px; + margin-left: -15px +} + +.col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-xs-1, .col-xs-10, .col-xs-11, .col-xs-12, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px +} + +.col-xs-1, .col-xs-10, .col-xs-11, .col-xs-12, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9 { + float: left +} + +.col-xs-12 { + width: 100% +} + +.col-xs-11 { + width: 91.66666667% +} + +.col-xs-10 { + width: 83.33333333% +} + +.col-xs-9 { + width: 75% +} + +.col-xs-8 { + width: 66.66666667% +} + +.col-xs-7 { + width: 58.33333333% +} + +.col-xs-6 { + width: 50% +} + +.col-xs-5 { + width: 41.66666667% +} + +.col-xs-4 { + width: 33.33333333% +} + +.col-xs-3 { + width: 25% +} + +.col-xs-2 { + width: 16.66666667% +} + +.col-xs-1 { + width: 8.33333333% +} + +.col-xs-pull-12 { + right: 100% +} + +.col-xs-pull-11 { + right: 91.66666667% +} + +.col-xs-pull-10 { + right: 83.33333333% +} + +.col-xs-pull-9 { + right: 75% +} + +.col-xs-pull-8 { + right: 66.66666667% +} + +.col-xs-pull-7 { + right: 58.33333333% +} + +.col-xs-pull-6 { + right: 50% +} + +.col-xs-pull-5 { + right: 41.66666667% +} + +.col-xs-pull-4 { + right: 33.33333333% +} + +.col-xs-pull-3 { + right: 25% +} + +.col-xs-pull-2 { + right: 16.66666667% +} + +.col-xs-pull-1 { + right: 8.33333333% +} + +.col-xs-pull-0 { + right: auto +} + +.col-xs-push-12 { + left: 100% +} + +.col-xs-push-11 { + left: 91.66666667% +} + +.col-xs-push-10 { + left: 83.33333333% +} + +.col-xs-push-9 { + left: 75% +} + +.col-xs-push-8 { + left: 66.66666667% +} + +.col-xs-push-7 { + left: 58.33333333% +} + +.col-xs-push-6 { + left: 50% +} + +.col-xs-push-5 { + left: 41.66666667% +} + +.col-xs-push-4 { + left: 33.33333333% +} + +.col-xs-push-3 { + left: 25% +} + +.col-xs-push-2 { + left: 16.66666667% +} + +.col-xs-push-1 { + left: 8.33333333% +} + +.col-xs-push-0 { + left: auto +} + +.col-xs-offset-12 { + margin-left: 100% +} + +.col-xs-offset-11 { + margin-left: 91.66666667% +} + +.col-xs-offset-10 { + margin-left: 83.33333333% +} + +.col-xs-offset-9 { + margin-left: 75% +} + +.col-xs-offset-8 { + margin-left: 66.66666667% +} + +.col-xs-offset-7 { + margin-left: 58.33333333% +} + +.col-xs-offset-6 { + margin-left: 50% +} + +.col-xs-offset-5 { + margin-left: 41.66666667% +} + +.col-xs-offset-4 { + margin-left: 33.33333333% +} + +.col-xs-offset-3 { + margin-left: 25% +} + +.col-xs-offset-2 { + margin-left: 16.66666667% +} + +.col-xs-offset-1 { + margin-left: 8.33333333% +} + +.col-xs-offset-0 { + margin-left: 0 +} + +@media (min-width: 768px) { + .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9 { + float: left + } + + .col-sm-12 { + width: 100% + } + + .col-sm-11 { + width: 91.66666667% + } + + .col-sm-10 { + width: 83.33333333% + } + + .col-sm-9 { + width: 75% + } + + .col-sm-8 { + width: 66.66666667% + } + + .col-sm-7 { + width: 58.33333333% + } + + .col-sm-6 { + width: 50% + } + + .col-sm-5 { + width: 41.66666667% + } + + .col-sm-4 { + width: 33.33333333% + } + + .col-sm-3 { + width: 25% + } + + .col-sm-2 { + width: 16.66666667% + } + + .col-sm-1 { + width: 8.33333333% + } + + .col-sm-pull-12 { + right: 100% + } + + .col-sm-pull-11 { + right: 91.66666667% + } + + .col-sm-pull-10 { + right: 83.33333333% + } + + .col-sm-pull-9 { + right: 75% + } + + .col-sm-pull-8 { + right: 66.66666667% + } + + .col-sm-pull-7 { + right: 58.33333333% + } + + .col-sm-pull-6 { + right: 50% + } + + .col-sm-pull-5 { + right: 41.66666667% + } + + .col-sm-pull-4 { + right: 33.33333333% + } + + .col-sm-pull-3 { + right: 25% + } + + .col-sm-pull-2 { + right: 16.66666667% + } + + .col-sm-pull-1 { + right: 8.33333333% + } + + .col-sm-pull-0 { + right: auto + } + + .col-sm-push-12 { + left: 100% + } + + .col-sm-push-11 { + left: 91.66666667% + } + + .col-sm-push-10 { + left: 83.33333333% + } + + .col-sm-push-9 { + left: 75% + } + + .col-sm-push-8 { + left: 66.66666667% + } + + .col-sm-push-7 { + left: 58.33333333% + } + + .col-sm-push-6 { + left: 50% + } + + .col-sm-push-5 { + left: 41.66666667% + } + + .col-sm-push-4 { + left: 33.33333333% + } + + .col-sm-push-3 { + left: 25% + } + + .col-sm-push-2 { + left: 16.66666667% + } + + .col-sm-push-1 { + left: 8.33333333% + } + + .col-sm-push-0 { + left: auto + } + + .col-sm-offset-12 { + margin-left: 100% + } + + .col-sm-offset-11 { + margin-left: 91.66666667% + } + + .col-sm-offset-10 { + margin-left: 83.33333333% + } + + .col-sm-offset-9 { + margin-left: 75% + } + + .col-sm-offset-8 { + margin-left: 66.66666667% + } + + .col-sm-offset-7 { + margin-left: 58.33333333% + } + + .col-sm-offset-6 { + margin-left: 50% + } + + .col-sm-offset-5 { + margin-left: 41.66666667% + } + + .col-sm-offset-4 { + margin-left: 33.33333333% + } + + .col-sm-offset-3 { + margin-left: 25% + } + + .col-sm-offset-2 { + margin-left: 16.66666667% + } + + .col-sm-offset-1 { + margin-left: 8.33333333% + } + + .col-sm-offset-0 { + margin-left: 0 + } +} + +@media (min-width: 992px) { + .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9 { + float: left + } + + .col-md-12 { + width: 100% + } + + .col-md-11 { + width: 91.66666667% + } + + .col-md-10 { + width: 83.33333333% + } + + .col-md-9 { + width: 75% + } + + .col-md-8 { + width: 66.66666667% + } + + .col-md-7 { + width: 58.33333333% + } + + .col-md-6 { + width: 50% + } + + .col-md-5 { + width: 41.66666667% + } + + .col-md-4 { + width: 33.33333333% + } + + .col-md-3 { + width: 25% + } + + .col-md-2 { + width: 16.66666667% + } + + .col-md-1 { + width: 8.33333333% + } + + .col-md-pull-12 { + right: 100% + } + + .col-md-pull-11 { + right: 91.66666667% + } + + .col-md-pull-10 { + right: 83.33333333% + } + + .col-md-pull-9 { + right: 75% + } + + .col-md-pull-8 { + right: 66.66666667% + } + + .col-md-pull-7 { + right: 58.33333333% + } + + .col-md-pull-6 { + right: 50% + } + + .col-md-pull-5 { + right: 41.66666667% + } + + .col-md-pull-4 { + right: 33.33333333% + } + + .col-md-pull-3 { + right: 25% + } + + .col-md-pull-2 { + right: 16.66666667% + } + + .col-md-pull-1 { + right: 8.33333333% + } + + .col-md-pull-0 { + right: auto + } + + .col-md-push-12 { + left: 100% + } + + .col-md-push-11 { + left: 91.66666667% + } + + .col-md-push-10 { + left: 83.33333333% + } + + .col-md-push-9 { + left: 75% + } + + .col-md-push-8 { + left: 66.66666667% + } + + .col-md-push-7 { + left: 58.33333333% + } + + .col-md-push-6 { + left: 50% + } + + .col-md-push-5 { + left: 41.66666667% + } + + .col-md-push-4 { + left: 33.33333333% + } + + .col-md-push-3 { + left: 25% + } + + .col-md-push-2 { + left: 16.66666667% + } + + .col-md-push-1 { + left: 8.33333333% + } + + .col-md-push-0 { + left: auto + } + + .col-md-offset-12 { + margin-left: 100% + } + + .col-md-offset-11 { + margin-left: 91.66666667% + } + + .col-md-offset-10 { + margin-left: 83.33333333% + } + + .col-md-offset-9 { + margin-left: 75% + } + + .col-md-offset-8 { + margin-left: 66.66666667% + } + + .col-md-offset-7 { + margin-left: 58.33333333% + } + + .col-md-offset-6 { + margin-left: 50% + } + + .col-md-offset-5 { + margin-left: 41.66666667% + } + + .col-md-offset-4 { + margin-left: 33.33333333% + } + + .col-md-offset-3 { + margin-left: 25% + } + + .col-md-offset-2 { + margin-left: 16.66666667% + } + + .col-md-offset-1 { + margin-left: 8.33333333% + } + + .col-md-offset-0 { + margin-left: 0 + } +} + +@media (min-width: 1200px) { + .col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9 { + float: left + } + + .col-lg-12 { + width: 100% + } + + .col-lg-11 { + width: 91.66666667% + } + + .col-lg-10 { + width: 83.33333333% + } + + .col-lg-9 { + width: 75% + } + + .col-lg-8 { + width: 66.66666667% + } + + .col-lg-7 { + width: 58.33333333% + } + + .col-lg-6 { + width: 50% + } + + .col-lg-5 { + width: 41.66666667% + } + + .col-lg-4 { + width: 33.33333333% + } + + .col-lg-3 { + width: 25% + } + + .col-lg-2 { + width: 16.66666667% + } + + .col-lg-1 { + width: 8.33333333% + } + + .col-lg-pull-12 { + right: 100% + } + + .col-lg-pull-11 { + right: 91.66666667% + } + + .col-lg-pull-10 { + right: 83.33333333% + } + + .col-lg-pull-9 { + right: 75% + } + + .col-lg-pull-8 { + right: 66.66666667% + } + + .col-lg-pull-7 { + right: 58.33333333% + } + + .col-lg-pull-6 { + right: 50% + } + + .col-lg-pull-5 { + right: 41.66666667% + } + + .col-lg-pull-4 { + right: 33.33333333% + } + + .col-lg-pull-3 { + right: 25% + } + + .col-lg-pull-2 { + right: 16.66666667% + } + + .col-lg-pull-1 { + right: 8.33333333% + } + + .col-lg-pull-0 { + right: auto + } + + .col-lg-push-12 { + left: 100% + } + + .col-lg-push-11 { + left: 91.66666667% + } + + .col-lg-push-10 { + left: 83.33333333% + } + + .col-lg-push-9 { + left: 75% + } + + .col-lg-push-8 { + left: 66.66666667% + } + + .col-lg-push-7 { + left: 58.33333333% + } + + .col-lg-push-6 { + left: 50% + } + + .col-lg-push-5 { + left: 41.66666667% + } + + .col-lg-push-4 { + left: 33.33333333% + } + + .col-lg-push-3 { + left: 25% + } + + .col-lg-push-2 { + left: 16.66666667% + } + + .col-lg-push-1 { + left: 8.33333333% + } + + .col-lg-push-0 { + left: auto + } + + .col-lg-offset-12 { + margin-left: 100% + } + + .col-lg-offset-11 { + margin-left: 91.66666667% + } + + .col-lg-offset-10 { + margin-left: 83.33333333% + } + + .col-lg-offset-9 { + margin-left: 75% + } + + .col-lg-offset-8 { + margin-left: 66.66666667% + } + + .col-lg-offset-7 { + margin-left: 58.33333333% + } + + .col-lg-offset-6 { + margin-left: 50% + } + + .col-lg-offset-5 { + margin-left: 41.66666667% + } + + .col-lg-offset-4 { + margin-left: 33.33333333% + } + + .col-lg-offset-3 { + margin-left: 25% + } + + .col-lg-offset-2 { + margin-left: 16.66666667% + } + + .col-lg-offset-1 { + margin-left: 8.33333333% + } + + .col-lg-offset-0 { + margin-left: 0 + } +} + +table { + background-color: transparent +} + +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left +} + +th { + text-align: left +} + +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px +} + +.table > tbody > tr > td, .table > tbody > tr > th, .table > tfoot > tr > td, .table > tfoot > tr > th, .table > thead > tr > td, .table > thead > tr > th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #DDD +} + +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #DDD +} + +.table > caption + thead > tr:first-child > td, .table > caption + thead > tr:first-child > th, .table > colgroup + thead > tr:first-child > td, .table > colgroup + thead > tr:first-child > th, .table > thead:first-child > tr:first-child > td, .table > thead:first-child > tr:first-child > th { + border-top: 0 +} + +.table > tbody + tbody { + border-top: 2px solid #DDD +} + +.table .table { + background-color: #FFF +} + +.table-condensed > tbody > tr > td, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > td, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > thead > tr > th { + padding: 5px +} + +.table-bordered { + border: 1px solid #DDD +} + +.table-bordered > tbody > tr > td, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > td, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > thead > tr > th { + border: 1px solid #DDD +} + +.table-bordered > thead > tr > td, .table-bordered > thead > tr > th { + border-bottom-width: 2px +} + +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #F9F9F9 +} + +.table-hover > tbody > tr:hover { + background-color: #F5F5F5 +} + +table col[class*=col-] { + position: static; + display: table-column; + float: none +} + +table td[class*=col-], table th[class*=col-] { + position: static; + display: table-cell; + float: none +} + +.table > tbody > tr.active > td, .table > tbody > tr.active > th, .table > tbody > tr > td.active, .table > tbody > tr > th.active, .table > tfoot > tr.active > td, .table > tfoot > tr.active > th, .table > tfoot > tr > td.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > thead > tr.active > th, .table > thead > tr > td.active, .table > thead > tr > th.active { + background-color: #F5F5F5 +} + +.table-hover > tbody > tr.active:hover > td, .table-hover > tbody > tr.active:hover > th, .table-hover > tbody > tr:hover > .active, .table-hover > tbody > tr > td.active:hover, .table-hover > tbody > tr > th.active:hover { + background-color: #E8E8E8 +} + +.table > tbody > tr.success > td, .table > tbody > tr.success > th, .table > tbody > tr > td.success, .table > tbody > tr > th.success, .table > tfoot > tr.success > td, .table > tfoot > tr.success > th, .table > tfoot > tr > td.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > thead > tr.success > th, .table > thead > tr > td.success, .table > thead > tr > th.success { + background-color: #DFF0D8 +} + +.table-hover > tbody > tr.success:hover > td, .table-hover > tbody > tr.success:hover > th, .table-hover > tbody > tr:hover > .success, .table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover { + background-color: #D0E9C6 +} + +.table > tbody > tr.info > td, .table > tbody > tr.info > th, .table > tbody > tr > td.info, .table > tbody > tr > th.info, .table > tfoot > tr.info > td, .table > tfoot > tr.info > th, .table > tfoot > tr > td.info, .table > tfoot > tr > th.info, .table > thead > tr.info > td, .table > thead > tr.info > th, .table > thead > tr > td.info, .table > thead > tr > th.info { + background-color: #D9EDF7 +} + +.table-hover > tbody > tr.info:hover > td, .table-hover > tbody > tr.info:hover > th, .table-hover > tbody > tr:hover > .info, .table-hover > tbody > tr > td.info:hover, .table-hover > tbody > tr > th.info:hover { + background-color: #C4E3F3 +} + +.table > tbody > tr.warning > td, .table > tbody > tr.warning > th, .table > tbody > tr > td.warning, .table > tbody > tr > th.warning, .table > tfoot > tr.warning > td, .table > tfoot > tr.warning > th, .table > tfoot > tr > td.warning, .table > tfoot > tr > th.warning, .table > thead > tr.warning > td, .table > thead > tr.warning > th, .table > thead > tr > td.warning, .table > thead > tr > th.warning { + background-color: #FCF8E3 +} + +.table-hover > tbody > tr.warning:hover > td, .table-hover > tbody > tr.warning:hover > th, .table-hover > tbody > tr:hover > .warning, .table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover { + background-color: #FAF2CC +} + +.table > tbody > tr.danger > td, .table > tbody > tr.danger > th, .table > tbody > tr > td.danger, .table > tbody > tr > th.danger, .table > tfoot > tr.danger > td, .table > tfoot > tr.danger > th, .table > tfoot > tr > td.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > thead > tr.danger > th, .table > thead > tr > td.danger, .table > thead > tr > th.danger { + background-color: #F2DEDE +} + +.table-hover > tbody > tr.danger:hover > td, .table-hover > tbody > tr.danger:hover > th, .table-hover > tbody > tr:hover > .danger, .table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover { + background-color: #EBCCCC +} + +.table-responsive { + min-height: .01%; + overflow-x: auto +} + +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #DDD + } + + .table-responsive > .table { + margin-bottom: 0 + } + + .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > td, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > thead > tr > th { + white-space: nowrap + } + + .table-responsive > .table-bordered { + border: 0 + } + + .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > thead > tr > th:first-child { + border-left: 0 + } + + .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > thead > tr > th:last-child { + border-right: 0 + } + + .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0 + } +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0 +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #E5E5E5 +} + +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: 700 +} + +input[type=search] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +input[type=checkbox], input[type=radio] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal +} + +input[type=file] { + display: block +} + +input[type=range] { + display: block; + width: 100% +} + +select[multiple], select[size] { + height: auto +} + +input[type=file]:focus, input[type=checkbox]:focus, input[type=radio]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555 +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #FFF; + background-image: none; + border: 1px solid #CCC; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s +} + +.form-control:focus { + border-color: #66AFE9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6) +} + +.form-control::-moz-placeholder { + color: #999; + opacity: 1 +} + +.form-control:-ms-input-placeholder { + color: #999 +} + +.form-control::-webkit-input-placeholder { + color: #999 +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: #EEE; + opacity: 1 +} + +.form-control[disabled], fieldset[disabled] .form-control { + cursor: not-allowed +} + +textarea.form-control { + height: auto +} + +input[type=search] { + -webkit-appearance: none +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type=date].form-control, input[type=time].form-control, input[type=datetime-local].form-control, input[type=month].form-control { + line-height: 34px + } + + .input-group-sm input[type=date], .input-group-sm input[type=time], .input-group-sm input[type=datetime-local], .input-group-sm input[type=month], input[type=date].input-sm, input[type=time].input-sm, input[type=datetime-local].input-sm, input[type=month].input-sm { + line-height: 30px + } + + .input-group-lg input[type=date], .input-group-lg input[type=time], .input-group-lg input[type=datetime-local], .input-group-lg input[type=month], input[type=date].input-lg, input[type=time].input-lg, input[type=datetime-local].input-lg, input[type=month].input-lg { + line-height: 46px + } +} + +.form-group { + margin-bottom: 15px +} + +.checkbox, .radio { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px +} + +.checkbox label, .radio label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + cursor: pointer +} + +.checkbox input[type=checkbox], .checkbox-inline input[type=checkbox], .radio input[type=radio], .radio-inline input[type=radio] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px +} + +.checkbox + .checkbox, .radio + .radio { + margin-top: -5px +} + +.checkbox-inline, .radio-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + vertical-align: middle; + cursor: pointer +} + +.checkbox-inline + .checkbox-inline, .radio-inline + .radio-inline { + margin-top: 0; + margin-left: 10px +} + +fieldset[disabled] input[type=checkbox], fieldset[disabled] input[type=radio], input[type=checkbox].disabled, input[type=checkbox][disabled], input[type=radio].disabled, input[type=radio][disabled] { + cursor: not-allowed +} + +.checkbox-inline.disabled, .radio-inline.disabled, fieldset[disabled] .checkbox-inline, fieldset[disabled] .radio-inline { + cursor: not-allowed +} + +.checkbox.disabled label, .radio.disabled label, fieldset[disabled] .checkbox label, fieldset[disabled] .radio label { + cursor: not-allowed +} + +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0 +} + +.form-control-static.input-lg, .form-control-static.input-sm { + padding-right: 0; + padding-left: 0 +} + +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +select.input-sm { + height: 30px; + line-height: 30px +} + +select[multiple].input-sm, textarea.input-sm { + height: auto +} + +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.form-group-sm select.form-control { + height: 30px; + line-height: 30px +} + +.form-group-sm select[multiple].form-control, .form-group-sm textarea.form-control { + height: auto +} + +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5 +} + +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +select.input-lg { + height: 46px; + line-height: 46px +} + +select[multiple].input-lg, textarea.input-lg { + height: auto +} + +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +.form-group-lg select.form-control { + height: 46px; + line-height: 46px +} + +.form-group-lg select[multiple].form-control, .form-group-lg textarea.form-control { + height: auto +} + +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333 +} + +.has-feedback { + position: relative +} + +.has-feedback .form-control { + padding-right: 42.5px +} + +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none +} + +.form-group-lg .form-control + .form-control-feedback, .input-group-lg + .form-control-feedback, .input-lg + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px +} + +.form-group-sm .form-control + .form-control-feedback, .input-group-sm + .form-control-feedback, .input-sm + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px +} + +.has-success .checkbox, .has-success .checkbox-inline, .has-success .control-label, .has-success .help-block, .has-success .radio, .has-success .radio-inline, .has-success.checkbox label, .has-success.checkbox-inline label, .has-success.radio label, .has-success.radio-inline label { + color: #3C763D +} + +.has-success .form-control { + border-color: #3C763D; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075) +} + +.has-success .form-control:focus { + border-color: #2B542C; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67B168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67B168 +} + +.has-success .input-group-addon { + color: #3C763D; + background-color: #DFF0D8; + border-color: #3C763D +} + +.has-success .form-control-feedback { + color: #3C763D +} + +.has-warning .checkbox, .has-warning .checkbox-inline, .has-warning .control-label, .has-warning .help-block, .has-warning .radio, .has-warning .radio-inline, .has-warning.checkbox label, .has-warning.checkbox-inline label, .has-warning.radio label, .has-warning.radio-inline label { + color: #8A6D3B +} + +.has-warning .form-control { + border-color: #8A6D3B; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075) +} + +.has-warning .form-control:focus { + border-color: #66512C; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #C0A16B; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #C0A16B +} + +.has-warning .input-group-addon { + color: #8A6D3B; + background-color: #FCF8E3; + border-color: #8A6D3B +} + +.has-warning .form-control-feedback { + color: #8A6D3B +} + +.has-error .checkbox, .has-error .checkbox-inline, .has-error .control-label, .has-error .help-block, .has-error .radio, .has-error .radio-inline, .has-error.checkbox label, .has-error.checkbox-inline label, .has-error.radio label, .has-error.radio-inline label { + color: #A94442 +} + +.has-error .form-control { + border-color: #A94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075) +} + +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #CE8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #CE8483 +} + +.has-error .input-group-addon { + color: #A94442; + background-color: #F2DEDE; + border-color: #A94442 +} + +.has-error .form-control-feedback { + color: #A94442 +} + +.has-feedback label ~ .form-control-feedback { + top: 25px +} + +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0 +} + +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373 +} + +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle + } + + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + + .form-inline .form-control-static { + display: inline-block + } + + .form-inline .input-group { + display: inline-table; + vertical-align: middle + } + + .form-inline .input-group .form-control, .form-inline .input-group .input-group-addon, .form-inline .input-group .input-group-btn { + width: auto + } + + .form-inline .input-group > .form-control { + width: 100% + } + + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle + } + + .form-inline .checkbox, .form-inline .radio { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle + } + + .form-inline .checkbox label, .form-inline .radio label { + padding-left: 0 + } + + .form-inline .checkbox input[type=checkbox], .form-inline .radio input[type=radio] { + position: relative; + margin-left: 0 + } + + .form-inline .has-feedback .form-control-feedback { + top: 0 + } +} + +.form-horizontal .checkbox, .form-horizontal .checkbox-inline, .form-horizontal .radio, .form-horizontal .radio-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0 +} + +.form-horizontal .checkbox, .form-horizontal .radio { + min-height: 27px +} + +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px +} + +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right + } +} + +.form-horizontal .has-feedback .form-control-feedback { + right: 15px +} + +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 14.33px; + font-size: 18px + } +} + +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px + } +} + +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: 400; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px +} + +.btn.active.focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn:active:focus, .btn:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +.btn.focus, .btn:focus, .btn:hover { + color: #333; + text-decoration: none +} + +.btn.active, .btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125) +} + +.btn.disabled, .btn[disabled], fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65 +} + +a.btn.disabled, fieldset[disabled] a.btn { + pointer-events: none +} + +.btn-default { + color: #333; + background-color: #FFF; + border-color: #CCC +} + +.btn-default.focus, .btn-default:focus { + color: #333; + background-color: #E6E6E6; + border-color: #8C8C8C +} + +.btn-default:hover { + color: #333; + background-color: #E6E6E6; + border-color: #ADADAD +} + +.btn-default.active, .btn-default:active, .open > .dropdown-toggle.btn-default { + color: #333; + background-color: #E6E6E6; + border-color: #ADADAD +} + +.btn-default.active.focus, .btn-default.active:focus, .btn-default.active:hover, .btn-default:active.focus, .btn-default:active:focus, .btn-default:active:hover, .open > .dropdown-toggle.btn-default.focus, .open > .dropdown-toggle.btn-default:focus, .open > .dropdown-toggle.btn-default:hover { + color: #333; + background-color: #D4D4D4; + border-color: #8C8C8C +} + +.btn-default.active, .btn-default:active, .open > .dropdown-toggle.btn-default { + background-image: none +} + +.btn-default.disabled, .btn-default.disabled.active, .btn-default.disabled.focus, .btn-default.disabled:active, .btn-default.disabled:focus, .btn-default.disabled:hover, .btn-default[disabled], .btn-default[disabled].active, .btn-default[disabled].focus, .btn-default[disabled]:active, .btn-default[disabled]:focus, .btn-default[disabled]:hover, fieldset[disabled] .btn-default, fieldset[disabled] .btn-default.active, fieldset[disabled] .btn-default.focus, fieldset[disabled] .btn-default:active, fieldset[disabled] .btn-default:focus, fieldset[disabled] .btn-default:hover { + background-color: #FFF; + border-color: #CCC +} + +.btn-default .badge { + color: #FFF; + background-color: #333 +} + +.btn-primary { + color: #FFF; + background-color: #337AB7; + border-color: #2E6DA4 +} + +.btn-primary.focus, .btn-primary:focus { + color: #FFF; + background-color: #286090; + border-color: #122B40 +} + +.btn-primary:hover { + color: #FFF; + background-color: #286090; + border-color: #204D74 +} + +.btn-primary.active, .btn-primary:active, .open > .dropdown-toggle.btn-primary { + color: #FFF; + background-color: #286090; + border-color: #204D74 +} + +.btn-primary.active.focus, .btn-primary.active:focus, .btn-primary.active:hover, .btn-primary:active.focus, .btn-primary:active:focus, .btn-primary:active:hover, .open > .dropdown-toggle.btn-primary.focus, .open > .dropdown-toggle.btn-primary:focus, .open > .dropdown-toggle.btn-primary:hover { + color: #FFF; + background-color: #204D74; + border-color: #122B40 +} + +.btn-primary.active, .btn-primary:active, .open > .dropdown-toggle.btn-primary { + background-image: none +} + +.btn-primary.disabled, .btn-primary.disabled.active, .btn-primary.disabled.focus, .btn-primary.disabled:active, .btn-primary.disabled:focus, .btn-primary.disabled:hover, .btn-primary[disabled], .btn-primary[disabled].active, .btn-primary[disabled].focus, .btn-primary[disabled]:active, .btn-primary[disabled]:focus, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary, fieldset[disabled] .btn-primary.active, fieldset[disabled] .btn-primary.focus, fieldset[disabled] .btn-primary:active, fieldset[disabled] .btn-primary:focus, fieldset[disabled] .btn-primary:hover { + background-color: #337AB7; + border-color: #2E6DA4 +} + +.btn-primary .badge { + color: #337AB7; + background-color: #FFF +} + +.btn-success { + color: #FFF; + background-color: #5CB85C; + border-color: #4CAE4C +} + +.btn-success.focus, .btn-success:focus { + color: #FFF; + background-color: #449D44; + border-color: #255625 +} + +.btn-success:hover { + color: #FFF; + background-color: #449D44; + border-color: #398439 +} + +.btn-success.active, .btn-success:active, .open > .dropdown-toggle.btn-success { + color: #FFF; + background-color: #449D44; + border-color: #398439 +} + +.btn-success.active.focus, .btn-success.active:focus, .btn-success.active:hover, .btn-success:active.focus, .btn-success:active:focus, .btn-success:active:hover, .open > .dropdown-toggle.btn-success.focus, .open > .dropdown-toggle.btn-success:focus, .open > .dropdown-toggle.btn-success:hover { + color: #FFF; + background-color: #398439; + border-color: #255625 +} + +.btn-success.active, .btn-success:active, .open > .dropdown-toggle.btn-success { + background-image: none +} + +.btn-success.disabled, .btn-success.disabled.active, .btn-success.disabled.focus, .btn-success.disabled:active, .btn-success.disabled:focus, .btn-success.disabled:hover, .btn-success[disabled], .btn-success[disabled].active, .btn-success[disabled].focus, .btn-success[disabled]:active, .btn-success[disabled]:focus, .btn-success[disabled]:hover, fieldset[disabled] .btn-success, fieldset[disabled] .btn-success.active, fieldset[disabled] .btn-success.focus, fieldset[disabled] .btn-success:active, fieldset[disabled] .btn-success:focus, fieldset[disabled] .btn-success:hover { + background-color: #5CB85C; + border-color: #4CAE4C +} + +.btn-success .badge { + color: #5CB85C; + background-color: #FFF +} + +.btn-info { + color: #FFF; + background-color: #5BC0DE; + border-color: #46B8DA +} + +.btn-info.focus, .btn-info:focus { + color: #FFF; + background-color: #31B0D5; + border-color: #1B6D85 +} + +.btn-info:hover { + color: #FFF; + background-color: #31B0D5; + border-color: #269ABC +} + +.btn-info.active, .btn-info:active, .open > .dropdown-toggle.btn-info { + color: #FFF; + background-color: #31B0D5; + border-color: #269ABC +} + +.btn-info.active.focus, .btn-info.active:focus, .btn-info.active:hover, .btn-info:active.focus, .btn-info:active:focus, .btn-info:active:hover, .open > .dropdown-toggle.btn-info.focus, .open > .dropdown-toggle.btn-info:focus, .open > .dropdown-toggle.btn-info:hover { + color: #FFF; + background-color: #269ABC; + border-color: #1B6D85 +} + +.btn-info.active, .btn-info:active, .open > .dropdown-toggle.btn-info { + background-image: none +} + +.btn-info.disabled, .btn-info.disabled.active, .btn-info.disabled.focus, .btn-info.disabled:active, .btn-info.disabled:focus, .btn-info.disabled:hover, .btn-info[disabled], .btn-info[disabled].active, .btn-info[disabled].focus, .btn-info[disabled]:active, .btn-info[disabled]:focus, .btn-info[disabled]:hover, fieldset[disabled] .btn-info, fieldset[disabled] .btn-info.active, fieldset[disabled] .btn-info.focus, fieldset[disabled] .btn-info:active, fieldset[disabled] .btn-info:focus, fieldset[disabled] .btn-info:hover { + background-color: #5BC0DE; + border-color: #46B8DA +} + +.btn-info .badge { + color: #5BC0DE; + background-color: #FFF +} + +.btn-warning { + color: #FFF; + background-color: #F0AD4E; + border-color: #EEA236 +} + +.btn-warning.focus, .btn-warning:focus { + color: #FFF; + background-color: #EC971F; + border-color: #985F0D +} + +.btn-warning:hover { + color: #FFF; + background-color: #EC971F; + border-color: #D58512 +} + +.btn-warning.active, .btn-warning:active, .open > .dropdown-toggle.btn-warning { + color: #FFF; + background-color: #EC971F; + border-color: #D58512 +} + +.btn-warning.active.focus, .btn-warning.active:focus, .btn-warning.active:hover, .btn-warning:active.focus, .btn-warning:active:focus, .btn-warning:active:hover, .open > .dropdown-toggle.btn-warning.focus, .open > .dropdown-toggle.btn-warning:focus, .open > .dropdown-toggle.btn-warning:hover { + color: #FFF; + background-color: #D58512; + border-color: #985F0D +} + +.btn-warning.active, .btn-warning:active, .open > .dropdown-toggle.btn-warning { + background-image: none +} + +.btn-warning.disabled, .btn-warning.disabled.active, .btn-warning.disabled.focus, .btn-warning.disabled:active, .btn-warning.disabled:focus, .btn-warning.disabled:hover, .btn-warning[disabled], .btn-warning[disabled].active, .btn-warning[disabled].focus, .btn-warning[disabled]:active, .btn-warning[disabled]:focus, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning, fieldset[disabled] .btn-warning.active, fieldset[disabled] .btn-warning.focus, fieldset[disabled] .btn-warning:active, fieldset[disabled] .btn-warning:focus, fieldset[disabled] .btn-warning:hover { + background-color: #F0AD4E; + border-color: #EEA236 +} + +.btn-warning .badge { + color: #F0AD4E; + background-color: #FFF +} + +.btn-danger { + color: #FFF; + background-color: #D9534F; + border-color: #D43F3A +} + +.btn-danger.focus, .btn-danger:focus { + color: #FFF; + background-color: #C9302C; + border-color: #761C19 +} + +.btn-danger:hover { + color: #FFF; + background-color: #C9302C; + border-color: #AC2925 +} + +.btn-danger.active, .btn-danger:active, .open > .dropdown-toggle.btn-danger { + color: #FFF; + background-color: #C9302C; + border-color: #AC2925 +} + +.btn-danger.active.focus, .btn-danger.active:focus, .btn-danger.active:hover, .btn-danger:active.focus, .btn-danger:active:focus, .btn-danger:active:hover, .open > .dropdown-toggle.btn-danger.focus, .open > .dropdown-toggle.btn-danger:focus, .open > .dropdown-toggle.btn-danger:hover { + color: #FFF; + background-color: #AC2925; + border-color: #761C19 +} + +.btn-danger.active, .btn-danger:active, .open > .dropdown-toggle.btn-danger { + background-image: none +} + +.btn-danger.disabled, .btn-danger.disabled.active, .btn-danger.disabled.focus, .btn-danger.disabled:active, .btn-danger.disabled:focus, .btn-danger.disabled:hover, .btn-danger[disabled], .btn-danger[disabled].active, .btn-danger[disabled].focus, .btn-danger[disabled]:active, .btn-danger[disabled]:focus, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger, fieldset[disabled] .btn-danger.active, fieldset[disabled] .btn-danger.focus, fieldset[disabled] .btn-danger:active, fieldset[disabled] .btn-danger:focus, fieldset[disabled] .btn-danger:hover { + background-color: #D9534F; + border-color: #D43F3A +} + +.btn-danger .badge { + color: #D9534F; + background-color: #FFF +} + +.btn-link { + font-weight: 400; + color: #337AB7; + border-radius: 0 +} + +.btn-link, .btn-link.active, .btn-link:active, .btn-link[disabled], fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none +} + +.btn-link, .btn-link:active, .btn-link:focus, .btn-link:hover { + border-color: transparent +} + +.btn-link:focus, .btn-link:hover { + color: #23527C; + text-decoration: underline; + background-color: transparent +} + +.btn-link[disabled]:focus, .btn-link[disabled]:hover, fieldset[disabled] .btn-link:focus, fieldset[disabled] .btn-link:hover { + color: #777; + text-decoration: none +} + +.btn-group-lg > .btn, .btn-lg { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +.btn-group-sm > .btn, .btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.btn-group-xs > .btn, .btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.btn-block { + display: block; + width: 100% +} + +.btn-block + .btn-block { + margin-top: 5px +} + +input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].btn-block { + width: 100% +} + +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear +} + +.fade.in { + opacity: 1 +} + +.collapse { + display: none +} + +.collapse.in { + display: block +} + +tr.collapse.in { + display: table-row +} + +tbody.collapse.in { + display: table-row-group +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility +} + +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent +} + +.dropdown, .dropup { + position: relative +} + +.dropdown-toggle:focus { + outline: 0 +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #FFF; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #CCC; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175) +} + +.dropdown-menu.pull-right { + right: 0; + left: auto +} + +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #E5E5E5 +} + +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; + white-space: nowrap +} + +.dropdown-menu > li > a:focus, .dropdown-menu > li > a:hover { + color: #262626; + text-decoration: none; + background-color: #F5F5F5 +} + +.dropdown-menu > .active > a, .dropdown-menu > .active > a:focus, .dropdown-menu > .active > a:hover { + color: #FFF; + text-decoration: none; + background-color: #337AB7; + outline: 0 +} + +.dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:focus, .dropdown-menu > .disabled > a:hover { + color: #777 +} + +.dropdown-menu > .disabled > a:focus, .dropdown-menu > .disabled > a:hover { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false) +} + +.open > .dropdown-menu { + display: block +} + +.open > a { + outline: 0 +} + +.dropdown-menu-right { + right: 0; + left: auto +} + +.dropdown-menu-left { + right: auto; + left: 0 +} + +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap +} + +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990 +} + +.pull-right > .dropdown-menu { + right: 0; + left: auto +} + +.dropup .caret, .navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9 +} + +.dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px +} + +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto + } + + .navbar-right .dropdown-menu-left { + right: auto; + left: 0 + } +} + +.btn-group, .btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle +} + +.btn-group-vertical > .btn, .btn-group > .btn { + position: relative; + float: left +} + +.btn-group-vertical > .btn.active, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn:focus, .btn-group-vertical > .btn:hover, .btn-group > .btn.active, .btn-group > .btn:active, .btn-group > .btn:focus, .btn-group > .btn:hover { + z-index: 2 +} + +.btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { + margin-left: -1px +} + +.btn-toolbar { + margin-left: -5px +} + +.btn-toolbar .btn, .btn-toolbar .btn-group, .btn-toolbar .input-group { + float: left +} + +.btn-toolbar > .btn, .btn-toolbar > .btn-group, .btn-toolbar > .input-group { + margin-left: 5px +} + +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0 +} + +.btn-group > .btn:first-child { + margin-left: 0 +} + +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group > .btn-group { + float: left +} + +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0 +} + +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { + outline: 0 +} + +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px +} + +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px +} + +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125) +} + +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none +} + +.btn .caret { + margin-left: 0 +} + +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0 +} + +.dropup .btn-lg .caret { + border-width: 0 5px 5px +} + +.btn-group-vertical > .btn, .btn-group-vertical > .btn-group, .btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100% +} + +.btn-group-vertical > .btn-group > .btn { + float: none +} + +.btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0 +} + +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0 +} + +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px +} + +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0 +} + +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate +} + +.btn-group-justified > .btn, .btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1% +} + +.btn-group-justified > .btn-group .btn { + width: 100% +} + +.btn-group-justified > .btn-group .dropdown-menu { + left: auto +} + +[data-toggle=buttons] > .btn input[type=checkbox], [data-toggle=buttons] > .btn input[type=radio], [data-toggle=buttons] > .btn-group > .btn input[type=checkbox], [data-toggle=buttons] > .btn-group > .btn input[type=radio] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none +} + +.input-group { + position: relative; + display: table; + border-collapse: separate +} + +.input-group[class*=col-] { + float: none; + padding-right: 0; + padding-left: 0 +} + +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0 +} + +.input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +select.input-group-lg > .form-control, select.input-group-lg > .input-group-addon, select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px +} + +select[multiple].input-group-lg > .form-control, select[multiple].input-group-lg > .input-group-addon, select[multiple].input-group-lg > .input-group-btn > .btn, textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn { + height: auto +} + +.input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +select.input-group-sm > .form-control, select.input-group-sm > .input-group-addon, select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px +} + +select[multiple].input-group-sm > .form-control, select[multiple].input-group-sm > .input-group-addon, select[multiple].input-group-sm > .input-group-btn > .btn, textarea.input-group-sm > .form-control, textarea.input-group-sm > .input-group-addon, textarea.input-group-sm > .input-group-btn > .btn { + height: auto +} + +.input-group .form-control, .input-group-addon, .input-group-btn { + display: table-cell +} + +.input-group .form-control:not(:first-child):not(:last-child), .input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child) { + border-radius: 0 +} + +.input-group-addon, .input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: #555; + text-align: center; + background-color: #EEE; + border: 1px solid #CCC; + border-radius: 4px +} + +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px +} + +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px +} + +.input-group-addon input[type=checkbox], .input-group-addon input[type=radio] { + margin-top: 0 +} + +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn-group:not(:last-child) > .btn, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.input-group-addon:first-child { + border-right: 0 +} + +.input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:first-child > .btn-group:not(:first-child) > .btn, .input-group-btn:first-child > .btn:not(:first-child), .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group > .btn, .input-group-btn:last-child > .dropdown-toggle { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.input-group-addon:last-child { + border-left: 0 +} + +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap +} + +.input-group-btn > .btn { + position: relative +} + +.input-group-btn > .btn + .btn { + margin-left: -1px +} + +.input-group-btn > .btn:active, .input-group-btn > .btn:focus, .input-group-btn > .btn:hover { + z-index: 2 +} + +.input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group { + margin-right: -1px +} + +.input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px +} + +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none +} + +.nav > li { + position: relative; + display: block +} + +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px +} + +.nav > li > a:focus, .nav > li > a:hover { + text-decoration: none; + background-color: #EEE +} + +.nav > li.disabled > a { + color: #777 +} + +.nav > li.disabled > a:focus, .nav > li.disabled > a:hover { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent +} + +.nav .open > a, .nav .open > a:focus, .nav .open > a:hover { + background-color: #EEE; + border-color: #337AB7 +} + +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #E5E5E5 +} + +.nav > li > a > img { + max-width: none +} + +.nav-tabs { + border-bottom: 1px solid #DDD +} + +.nav-tabs > li { + float: left; + margin-bottom: -1px +} + +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0 +} + +.nav-tabs > li > a:hover { + border-color: #EEE #EEE #DDD +} + +.nav-tabs > li.active > a, .nav-tabs > li.active > a:focus, .nav-tabs > li.active > a:hover { + color: #555; + cursor: default; + background-color: #FFF; + border: 1px solid #DDD; + border-bottom-color: transparent +} + +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0 +} + +.nav-tabs.nav-justified > li { + float: none +} + +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center +} + +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto +} + +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1% + } + + .nav-tabs.nav-justified > li > a { + margin-bottom: 0 + } +} + +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px +} + +.nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:focus, .nav-tabs.nav-justified > .active > a:hover { + border: 1px solid #DDD +} + +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #DDD; + border-radius: 4px 4px 0 0 + } + + .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:focus, .nav-tabs.nav-justified > .active > a:hover { + border-bottom-color: #FFF + } +} + +.nav-pills > li { + float: left +} + +.nav-pills > li > a { + border-radius: 4px +} + +.nav-pills > li + li { + margin-left: 2px +} + +.nav-pills > li.active > a, .nav-pills > li.active > a:focus, .nav-pills > li.active > a:hover { + color: #FFF; + background-color: #337AB7 +} + +.nav-stacked > li { + float: none +} + +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0 +} + +.nav-justified { + width: 100% +} + +.nav-justified > li { + float: none +} + +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center +} + +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto +} + +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1% + } + + .nav-justified > li > a { + margin-bottom: 0 + } +} + +.nav-tabs-justified { + border-bottom: 0 +} + +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px +} + +.nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:focus, .nav-tabs-justified > .active > a:hover { + border: 1px solid #DDD +} + +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #DDD; + border-radius: 4px 4px 0 0 + } + + .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:focus, .nav-tabs-justified > .active > a:hover { + border-bottom-color: #FFF + } +} + +.tab-content > .tab-pane { + display: none +} + +.tab-content > .active { + display: block +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent +} + +@media (min-width: 768px) { + .navbar { + border-radius: 4px + } +} + +@media (min-width: 768px) { + .navbar-header { + float: left + } +} + +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1) +} + +.navbar-collapse.in { + overflow-y: auto +} + +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none + } + + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important + } + + .navbar-collapse.in { + overflow-y: visible + } + + .navbar-fixed-bottom .navbar-collapse, .navbar-fixed-top .navbar-collapse, .navbar-static-top .navbar-collapse { + padding-right: 0; + padding-left: 0 + } +} + +.navbar-fixed-bottom .navbar-collapse, .navbar-fixed-top .navbar-collapse { + max-height: 340px +} + +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-bottom .navbar-collapse, .navbar-fixed-top .navbar-collapse { + max-height: 200px + } +} + +.container-fluid > .navbar-collapse, .container-fluid > .navbar-header, .container > .navbar-collapse, .container > .navbar-header { + margin-right: -15px; + margin-left: -15px +} + +@media (min-width: 768px) { + .container-fluid > .navbar-collapse, .container-fluid > .navbar-header, .container > .navbar-collapse, .container > .navbar-header { + margin-right: 0; + margin-left: 0 + } +} + +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px +} + +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0 + } +} + +.navbar-fixed-bottom, .navbar-fixed-top { + position: fixed; + right: 0; + left: 0; + z-index: 1030 +} + +@media (min-width: 768px) { + .navbar-fixed-bottom, .navbar-fixed-top { + border-radius: 0 + } +} + +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px +} + +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0 +} + +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px +} + +.navbar-brand:focus, .navbar-brand:hover { + text-decoration: none +} + +.navbar-brand > img { + display: block +} + +@media (min-width: 768px) { + .navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand { + margin-left: -15px + } +} + +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px +} + +.navbar-toggle:focus { + outline: 0 +} + +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px +} + +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px +} + +@media (min-width: 768px) { + .navbar-toggle { + display: none + } +} + +.navbar-nav { + margin: 7.5px -15px +} + +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px +} + +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none + } + + .navbar-nav .open .dropdown-menu .dropdown-header, .navbar-nav .open .dropdown-menu > li > a { + padding: 5px 15px 5px 25px + } + + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px + } + + .navbar-nav .open .dropdown-menu > li > a:focus, .navbar-nav .open .dropdown-menu > li > a:hover { + background-image: none + } +} + +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0 + } + + .navbar-nav > li { + float: left + } + + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px + } +} + +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1) +} + +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle + } + + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + + .navbar-form .form-control-static { + display: inline-block + } + + .navbar-form .input-group { + display: inline-table; + vertical-align: middle + } + + .navbar-form .input-group .form-control, .navbar-form .input-group .input-group-addon, .navbar-form .input-group .input-group-btn { + width: auto + } + + .navbar-form .input-group > .form-control { + width: 100% + } + + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle + } + + .navbar-form .checkbox, .navbar-form .radio { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle + } + + .navbar-form .checkbox label, .navbar-form .radio label { + padding-left: 0 + } + + .navbar-form .checkbox input[type=checkbox], .navbar-form .radio input[type=radio] { + position: relative; + margin-left: 0 + } + + .navbar-form .has-feedback .form-control-feedback { + top: 0 + } +} + +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px + } + + .navbar-form .form-group:last-child { + margin-bottom: 0 + } +} + +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none + } +} + +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px +} + +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px +} + +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px +} + +.navbar-text { + margin-top: 15px; + margin-bottom: 15px +} + +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px + } +} + +@media (min-width: 768px) { + .navbar-left { + float: left !important + } + + .navbar-right { + float: right !important; + margin-right: -15px + } + + .navbar-right ~ .navbar-right { + margin-right: 0 + } +} + +.navbar-default { + background-color: #F8F8F8; + border-color: #E7E7E7 +} + +.navbar-default .navbar-brand { + color: #777 +} + +.navbar-default .navbar-brand:focus, .navbar-default .navbar-brand:hover { + color: #5E5E5E; + background-color: transparent +} + +.navbar-default .navbar-text { + color: #777 +} + +.navbar-default .navbar-nav > li > a { + color: #777 +} + +.navbar-default .navbar-nav > li > a:focus, .navbar-default .navbar-nav > li > a:hover { + color: #333; + background-color: transparent +} + +.navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:focus, .navbar-default .navbar-nav > .active > a:hover { + color: #555; + background-color: #E7E7E7 +} + +.navbar-default .navbar-nav > .disabled > a, .navbar-default .navbar-nav > .disabled > a:focus, .navbar-default .navbar-nav > .disabled > a:hover { + color: #CCC; + background-color: transparent +} + +.navbar-default .navbar-toggle { + border-color: #DDD +} + +.navbar-default .navbar-toggle:focus, .navbar-default .navbar-toggle:hover { + background-color: #DDD +} + +.navbar-default .navbar-toggle .icon-bar { + background-color: #888 +} + +.navbar-default .navbar-collapse, .navbar-default .navbar-form { + border-color: #E7E7E7 +} + +.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:focus, .navbar-default .navbar-nav > .open > a:hover { + color: #555; + background-color: #E7E7E7 +} + +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777 + } + + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus, .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover { + color: #333; + background-color: transparent + } + + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover { + color: #555; + background-color: #E7E7E7 + } + + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover { + color: #CCC; + background-color: transparent + } +} + +.navbar-default .navbar-link { + color: #777 +} + +.navbar-default .navbar-link:hover { + color: #333 +} + +.navbar-default .btn-link { + color: #777 +} + +.navbar-default .btn-link:focus, .navbar-default .btn-link:hover { + color: #333 +} + +.navbar-default .btn-link[disabled]:focus, .navbar-default .btn-link[disabled]:hover, fieldset[disabled] .navbar-default .btn-link:focus, fieldset[disabled] .navbar-default .btn-link:hover { + color: #CCC +} + +.navbar-inverse { + background-color: #222; + border-color: #080808 +} + +.navbar-inverse .navbar-brand { + color: #9D9D9D +} + +.navbar-inverse .navbar-brand:focus, .navbar-inverse .navbar-brand:hover { + color: #FFF; + background-color: transparent +} + +.navbar-inverse .navbar-text { + color: #9D9D9D +} + +.navbar-inverse .navbar-nav > li > a { + color: #9D9D9D +} + +.navbar-inverse .navbar-nav > li > a:focus, .navbar-inverse .navbar-nav > li > a:hover { + color: #FFF; + background-color: transparent +} + +.navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:focus, .navbar-inverse .navbar-nav > .active > a:hover { + color: #FFF; + background-color: #080808 +} + +.navbar-inverse .navbar-nav > .disabled > a, .navbar-inverse .navbar-nav > .disabled > a:focus, .navbar-inverse .navbar-nav > .disabled > a:hover { + color: #444; + background-color: transparent +} + +.navbar-inverse .navbar-toggle { + border-color: #333 +} + +.navbar-inverse .navbar-toggle:focus, .navbar-inverse .navbar-toggle:hover { + background-color: #333 +} + +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #FFF +} + +.navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { + border-color: #101010 +} + +.navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .open > a:focus, .navbar-inverse .navbar-nav > .open > a:hover { + color: #FFF; + background-color: #080808 +} + +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808 + } + + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808 + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9D9D9D + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover { + color: #FFF; + background-color: transparent + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover { + color: #FFF; + background-color: #080808 + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover { + color: #444; + background-color: transparent + } +} + +.navbar-inverse .navbar-link { + color: #9D9D9D +} + +.navbar-inverse .navbar-link:hover { + color: #FFF +} + +.navbar-inverse .btn-link { + color: #9D9D9D +} + +.navbar-inverse .btn-link:focus, .navbar-inverse .btn-link:hover { + color: #FFF +} + +.navbar-inverse .btn-link[disabled]:focus, .navbar-inverse .btn-link[disabled]:hover, fieldset[disabled] .navbar-inverse .btn-link:focus, fieldset[disabled] .navbar-inverse .btn-link:hover { + color: #444 +} + +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #F5F5F5; + border-radius: 4px +} + +.breadcrumb > li { + display: inline-block +} + +.breadcrumb > li + li:before { + padding: 0 5px; + color: #CCC; + content: "/\00a0" +} + +.breadcrumb > .active { + color: #777 +} + +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px +} + +.pagination > li { + display: inline +} + +.pagination > li > a, .pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337AB7; + text-decoration: none; + background-color: #FFF; + border: 1px solid #DDD +} + +.pagination > li:first-child > a, .pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px +} + +.pagination > li:last-child > a, .pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px +} + +.pagination > li > a:focus, .pagination > li > a:hover, .pagination > li > span:focus, .pagination > li > span:hover { + z-index: 3; + color: #23527C; + background-color: #EEE; + border-color: #DDD +} + +.pagination > .active > a, .pagination > .active > a:focus, .pagination > .active > a:hover, .pagination > .active > span, .pagination > .active > span:focus, .pagination > .active > span:hover { + z-index: 2; + color: #FFF; + cursor: default; + background-color: #337AB7; + border-color: #337AB7 +} + +.pagination > .disabled > a, .pagination > .disabled > a:focus, .pagination > .disabled > a:hover, .pagination > .disabled > span, .pagination > .disabled > span:focus, .pagination > .disabled > span:hover { + color: #777; + cursor: not-allowed; + background-color: #FFF; + border-color: #DDD +} + +.pagination-lg > li > a, .pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333 +} + +.pagination-lg > li:first-child > a, .pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px +} + +.pagination-lg > li:last-child > a, .pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px +} + +.pagination-sm > li > a, .pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5 +} + +.pagination-sm > li:first-child > a, .pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px +} + +.pagination-sm > li:last-child > a, .pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px +} + +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none +} + +.pager li { + display: inline +} + +.pager li > a, .pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #FFF; + border: 1px solid #DDD; + border-radius: 15px +} + +.pager li > a:focus, .pager li > a:hover { + text-decoration: none; + background-color: #EEE +} + +.pager .next > a, .pager .next > span { + float: right +} + +.pager .previous > a, .pager .previous > span { + float: left +} + +.pager .disabled > a, .pager .disabled > a:focus, .pager .disabled > a:hover, .pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #FFF +} + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: 700; + line-height: 1; + color: #FFF; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em +} + +a.label:focus, a.label:hover { + color: #FFF; + text-decoration: none; + cursor: pointer +} + +.label:empty { + display: none +} + +.btn .label { + position: relative; + top: -1px +} + +.label-default { + background-color: #777 +} + +.label-default[href]:focus, .label-default[href]:hover { + background-color: #5E5E5E +} + +.label-primary { + background-color: #337AB7 +} + +.label-primary[href]:focus, .label-primary[href]:hover { + background-color: #286090 +} + +.label-success { + background-color: #5CB85C +} + +.label-success[href]:focus, .label-success[href]:hover { + background-color: #449D44 +} + +.label-info { + background-color: #5BC0DE +} + +.label-info[href]:focus, .label-info[href]:hover { + background-color: #31B0D5 +} + +.label-warning { + background-color: #F0AD4E +} + +.label-warning[href]:focus, .label-warning[href]:hover { + background-color: #EC971F +} + +.label-danger { + background-color: #D9534F +} + +.label-danger[href]:focus, .label-danger[href]:hover { + background-color: #C9302C +} + +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: 700; + line-height: 1; + color: #FFF; + text-align: center; + white-space: nowrap; + vertical-align: middle; + background-color: #777; + border-radius: 10px +} + +.badge:empty { + display: none +} + +.btn .badge { + position: relative; + top: -1px +} + +.btn-group-xs > .btn .badge, .btn-xs .badge { + top: 0; + padding: 1px 5px +} + +a.badge:focus, a.badge:hover { + color: #FFF; + text-decoration: none; + cursor: pointer +} + +.list-group-item.active > .badge, .nav-pills > .active > a > .badge { + color: #337AB7; + background-color: #FFF +} + +.list-group-item > .badge { + float: right +} + +.list-group-item > .badge + .badge { + margin-right: 5px +} + +.nav-pills > li > a > .badge { + margin-left: 3px +} + +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #EEE +} + +.jumbotron .h1, .jumbotron h1 { + color: inherit +} + +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200 +} + +.jumbotron > hr { + border-top-color: #D5D5D5 +} + +.container .jumbotron, .container-fluid .jumbotron { + border-radius: 6px +} + +.jumbotron .container { + max-width: 100% +} + +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px + } + + .container .jumbotron, .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px + } + + .jumbotron .h1, .jumbotron h1 { + font-size: 63px + } +} + +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #FFF; + border: 1px solid #DDD; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out +} + +.thumbnail a > img, .thumbnail > img { + margin-right: auto; + margin-left: auto +} + +a.thumbnail.active, a.thumbnail:focus, a.thumbnail:hover { + border-color: #337AB7 +} + +.thumbnail .caption { + padding: 9px; + color: #333 +} + +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px +} + +.alert h4 { + margin-top: 0; + color: inherit +} + +.alert .alert-link { + font-weight: 700 +} + +.alert > p, .alert > ul { + margin-bottom: 0 +} + +.alert > p + p { + margin-top: 5px +} + +.alert-dismissable, .alert-dismissible { + padding-right: 35px +} + +.alert-dismissable .close, .alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit +} + +.alert-success { + color: #3C763D; + background-color: #DFF0D8; + border-color: #D6E9C6 +} + +.alert-success hr { + border-top-color: #C9E2B3 +} + +.alert-success .alert-link { + color: #2B542C +} + +.alert-info { + color: #31708F; + background-color: #D9EDF7; + border-color: #BCE8F1 +} + +.alert-info hr { + border-top-color: #A6E1EC +} + +.alert-info .alert-link { + color: #245269 +} + +.alert-warning { + color: #8A6D3B; + background-color: #FCF8E3; + border-color: #FAEBCC +} + +.alert-warning hr { + border-top-color: #F7E1B5 +} + +.alert-warning .alert-link { + color: #66512C +} + +.alert-danger { + color: #A94442; + background-color: #F2DEDE; + border-color: #EBCCD1 +} + +.alert-danger hr { + border-top-color: #E4B9C0 +} + +.alert-danger .alert-link { + color: #843534 +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0 + } + to { + background-position: 0 0 + } +} + +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0 + } + to { + background-position: 0 0 + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 40px 0 + } + to { + background-position: 0 0 + } +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #F5F5F5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1) +} + +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #FFF; + text-align: center; + background-color: #337AB7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease +} + +.progress-bar-striped, .progress-striped .progress-bar { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px +} + +.progress-bar.active, .progress.active .progress-bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite +} + +.progress-bar-success { + background-color: #5CB85C +} + +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) +} + +.progress-bar-info { + background-color: #5BC0DE +} + +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) +} + +.progress-bar-warning { + background-color: #F0AD4E +} + +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) +} + +.progress-bar-danger { + background-color: #D9534F +} + +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) +} + +.media { + margin-top: 15px +} + +.media:first-child { + margin-top: 0 +} + +.media, .media-body { + overflow: hidden; + zoom: 1 +} + +.media-body { + width: 10000px +} + +.media-object { + display: block +} + +.media-object.img-thumbnail { + max-width: none +} + +.media-right, .media > .pull-right { + padding-left: 10px +} + +.media-left, .media > .pull-left { + padding-right: 10px +} + +.media-body, .media-left, .media-right { + display: table-cell; + vertical-align: top +} + +.media-middle { + vertical-align: middle +} + +.media-bottom { + vertical-align: bottom +} + +.media-heading { + margin-top: 0; + margin-bottom: 5px +} + +.media-list { + padding-left: 0; + list-style: none +} + +.list-group { + padding-left: 0; + margin-bottom: 20px +} + +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #FFF; + border: 1px solid #DDD +} + +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px +} + +a.list-group-item, button.list-group-item { + color: #555 +} + +a.list-group-item .list-group-item-heading, button.list-group-item .list-group-item-heading { + color: #333 +} + +a.list-group-item:focus, a.list-group-item:hover, button.list-group-item:focus, button.list-group-item:hover { + color: #555; + text-decoration: none; + background-color: #F5F5F5 +} + +button.list-group-item { + width: 100%; + text-align: left +} + +.list-group-item.disabled, .list-group-item.disabled:focus, .list-group-item.disabled:hover { + color: #777; + cursor: not-allowed; + background-color: #EEE +} + +.list-group-item.disabled .list-group-item-heading, .list-group-item.disabled:focus .list-group-item-heading, .list-group-item.disabled:hover .list-group-item-heading { + color: inherit +} + +.list-group-item.disabled .list-group-item-text, .list-group-item.disabled:focus .list-group-item-text, .list-group-item.disabled:hover .list-group-item-text { + color: #777 +} + +.list-group-item.active, .list-group-item.active:focus, .list-group-item.active:hover { + z-index: 2; + color: #FFF; + background-color: #337AB7; + border-color: #337AB7 +} + +.list-group-item.active .list-group-item-heading, .list-group-item.active .list-group-item-heading > .small, .list-group-item.active .list-group-item-heading > small, .list-group-item.active:focus .list-group-item-heading, .list-group-item.active:focus .list-group-item-heading > .small, .list-group-item.active:focus .list-group-item-heading > small, .list-group-item.active:hover .list-group-item-heading, .list-group-item.active:hover .list-group-item-heading > .small, .list-group-item.active:hover .list-group-item-heading > small { + color: inherit +} + +.list-group-item.active .list-group-item-text, .list-group-item.active:focus .list-group-item-text, .list-group-item.active:hover .list-group-item-text { + color: #C7DDEF +} + +.list-group-item-success { + color: #3C763D; + background-color: #DFF0D8 +} + +a.list-group-item-success, button.list-group-item-success { + color: #3C763D +} + +a.list-group-item-success .list-group-item-heading, button.list-group-item-success .list-group-item-heading { + color: inherit +} + +a.list-group-item-success:focus, a.list-group-item-success:hover, button.list-group-item-success:focus, button.list-group-item-success:hover { + color: #3C763D; + background-color: #D0E9C6 +} + +a.list-group-item-success.active, a.list-group-item-success.active:focus, a.list-group-item-success.active:hover, button.list-group-item-success.active, button.list-group-item-success.active:focus, button.list-group-item-success.active:hover { + color: #FFF; + background-color: #3C763D; + border-color: #3C763D +} + +.list-group-item-info { + color: #31708F; + background-color: #D9EDF7 +} + +a.list-group-item-info, button.list-group-item-info { + color: #31708F +} + +a.list-group-item-info .list-group-item-heading, button.list-group-item-info .list-group-item-heading { + color: inherit +} + +a.list-group-item-info:focus, a.list-group-item-info:hover, button.list-group-item-info:focus, button.list-group-item-info:hover { + color: #31708F; + background-color: #C4E3F3 +} + +a.list-group-item-info.active, a.list-group-item-info.active:focus, a.list-group-item-info.active:hover, button.list-group-item-info.active, button.list-group-item-info.active:focus, button.list-group-item-info.active:hover { + color: #FFF; + background-color: #31708F; + border-color: #31708F +} + +.list-group-item-warning { + color: #8A6D3B; + background-color: #FCF8E3 +} + +a.list-group-item-warning, button.list-group-item-warning { + color: #8A6D3B +} + +a.list-group-item-warning .list-group-item-heading, button.list-group-item-warning .list-group-item-heading { + color: inherit +} + +a.list-group-item-warning:focus, a.list-group-item-warning:hover, button.list-group-item-warning:focus, button.list-group-item-warning:hover { + color: #8A6D3B; + background-color: #FAF2CC +} + +a.list-group-item-warning.active, a.list-group-item-warning.active:focus, a.list-group-item-warning.active:hover, button.list-group-item-warning.active, button.list-group-item-warning.active:focus, button.list-group-item-warning.active:hover { + color: #FFF; + background-color: #8A6D3B; + border-color: #8A6D3B +} + +.list-group-item-danger { + color: #A94442; + background-color: #F2DEDE +} + +a.list-group-item-danger, button.list-group-item-danger { + color: #A94442 +} + +a.list-group-item-danger .list-group-item-heading, button.list-group-item-danger .list-group-item-heading { + color: inherit +} + +a.list-group-item-danger:focus, a.list-group-item-danger:hover, button.list-group-item-danger:focus, button.list-group-item-danger:hover { + color: #A94442; + background-color: #EBCCCC +} + +a.list-group-item-danger.active, a.list-group-item-danger.active:focus, a.list-group-item-danger.active:hover, button.list-group-item-danger.active, button.list-group-item-danger.active:focus, button.list-group-item-danger.active:hover { + color: #FFF; + background-color: #A94442; + border-color: #A94442 +} + +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px +} + +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3 +} + +.panel { + margin-bottom: 20px; + background-color: #FFF; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05) +} + +.panel-body { + padding: 15px +} + +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px +} + +.panel-heading > .dropdown .dropdown-toggle { + color: inherit +} + +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit +} + +.panel-title > .small, .panel-title > .small > a, .panel-title > a, .panel-title > small, .panel-title > small > a { + color: inherit +} + +.panel-footer { + padding: 10px 15px; + background-color: #F5F5F5; + border-top: 1px solid #DDD; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px +} + +.panel > .list-group, .panel > .panel-collapse > .list-group { + margin-bottom: 0 +} + +.panel > .list-group .list-group-item, .panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0 +} + +.panel > .list-group:first-child .list-group-item:first-child, .panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px +} + +.panel > .list-group:last-child .list-group-item:last-child, .panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px +} + +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0 +} + +.list-group + .panel-footer { + border-top-width: 0 +} + +.panel > .panel-collapse > .table, .panel > .table, .panel > .table-responsive > .table { + margin-bottom: 0 +} + +.panel > .panel-collapse > .table caption, .panel > .table caption, .panel > .table-responsive > .table caption { + padding-right: 15px; + padding-left: 15px +} + +.panel > .table-responsive:first-child > .table:first-child, .panel > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px +} + +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child, .panel > .table:first-child > thead:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px +} + +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, .panel > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table:first-child > thead:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px +} + +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, .panel > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table:first-child > thead:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px +} + +.panel > .table-responsive:last-child > .table:last-child, .panel > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px +} + +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child, .panel > .table:last-child > tbody:last-child > tr:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px +} + +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child, .panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px +} + +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child, .panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px +} + +.panel > .panel-body + .table, .panel > .panel-body + .table-responsive, .panel > .table + .panel-body, .panel > .table-responsive + .panel-body { + border-top: 1px solid #DDD +} + +.panel > .table > tbody:first-child > tr:first-child td, .panel > .table > tbody:first-child > tr:first-child th { + border-top: 0 +} + +.panel > .table-bordered, .panel > .table-responsive > .table-bordered { + border: 0 +} + +.panel > .table-bordered > tbody > tr > td:first-child, .panel > .table-bordered > tbody > tr > th:first-child, .panel > .table-bordered > tfoot > tr > td:first-child, .panel > .table-bordered > tfoot > tr > th:first-child, .panel > .table-bordered > thead > tr > td:first-child, .panel > .table-bordered > thead > tr > th:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, .panel > .table-responsive > .table-bordered > thead > tr > td:first-child, .panel > .table-responsive > .table-bordered > thead > tr > th:first-child { + border-left: 0 +} + +.panel > .table-bordered > tbody > tr > td:last-child, .panel > .table-bordered > tbody > tr > th:last-child, .panel > .table-bordered > tfoot > tr > td:last-child, .panel > .table-bordered > tfoot > tr > th:last-child, .panel > .table-bordered > thead > tr > td:last-child, .panel > .table-bordered > thead > tr > th:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, .panel > .table-responsive > .table-bordered > thead > tr > td:last-child, .panel > .table-responsive > .table-bordered > thead > tr > th:last-child { + border-right: 0 +} + +.panel > .table-bordered > tbody > tr:first-child > td, .panel > .table-bordered > tbody > tr:first-child > th, .panel > .table-bordered > thead > tr:first-child > td, .panel > .table-bordered > thead > tr:first-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > th, .panel > .table-responsive > .table-bordered > thead > tr:first-child > td, .panel > .table-responsive > .table-bordered > thead > tr:first-child > th { + border-bottom: 0 +} + +.panel > .table-bordered > tbody > tr:last-child > td, .panel > .table-bordered > tbody > tr:last-child > th, .panel > .table-bordered > tfoot > tr:last-child > td, .panel > .table-bordered > tfoot > tr:last-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0 +} + +.panel > .table-responsive { + margin-bottom: 0; + border: 0 +} + +.panel-group { + margin-bottom: 20px +} + +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px +} + +.panel-group .panel + .panel { + margin-top: 5px +} + +.panel-group .panel-heading { + border-bottom: 0 +} + +.panel-group .panel-heading + .panel-collapse > .list-group, .panel-group .panel-heading + .panel-collapse > .panel-body { + border-top: 1px solid #DDD +} + +.panel-group .panel-footer { + border-top: 0 +} + +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #DDD +} + +.panel-default { + border-color: #DDD +} + +.panel-default > .panel-heading { + color: #333; + background-color: #F5F5F5; + border-color: #DDD +} + +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #DDD +} + +.panel-default > .panel-heading .badge { + color: #F5F5F5; + background-color: #333 +} + +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #DDD +} + +.panel-primary { + border-color: #337AB7 +} + +.panel-primary > .panel-heading { + color: #FFF; + background-color: #337AB7; + border-color: #337AB7 +} + +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337AB7 +} + +.panel-primary > .panel-heading .badge { + color: #337AB7; + background-color: #FFF +} + +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337AB7 +} + +.panel-success { + border-color: #D6E9C6 +} + +.panel-success > .panel-heading { + color: #3C763D; + background-color: #DFF0D8; + border-color: #D6E9C6 +} + +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #D6E9C6 +} + +.panel-success > .panel-heading .badge { + color: #DFF0D8; + background-color: #3C763D +} + +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #D6E9C6 +} + +.panel-info { + border-color: #BCE8F1 +} + +.panel-info > .panel-heading { + color: #31708F; + background-color: #D9EDF7; + border-color: #BCE8F1 +} + +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #BCE8F1 +} + +.panel-info > .panel-heading .badge { + color: #D9EDF7; + background-color: #31708F +} + +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #BCE8F1 +} + +.panel-warning { + border-color: #FAEBCC +} + +.panel-warning > .panel-heading { + color: #8A6D3B; + background-color: #FCF8E3; + border-color: #FAEBCC +} + +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #FAEBCC +} + +.panel-warning > .panel-heading .badge { + color: #FCF8E3; + background-color: #8A6D3B +} + +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #FAEBCC +} + +.panel-danger { + border-color: #EBCCD1 +} + +.panel-danger > .panel-heading { + color: #A94442; + background-color: #F2DEDE; + border-color: #EBCCD1 +} + +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #EBCCD1 +} + +.panel-danger > .panel-heading .badge { + color: #F2DEDE; + background-color: #A94442 +} + +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #EBCCD1 +} + +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden +} + +.embed-responsive .embed-responsive-item, .embed-responsive embed, .embed-responsive iframe, .embed-responsive object, .embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0 +} + +.embed-responsive-16by9 { + padding-bottom: 56.25% +} + +.embed-responsive-4by3 { + padding-bottom: 75% +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #F5F5F5; + border: 1px solid #E3E3E3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05) +} + +.well blockquote { + border-color: #DDD; + border-color: rgba(0, 0, 0, .15) +} + +.well-lg { + padding: 24px; + border-radius: 6px +} + +.well-sm { + padding: 9px; + border-radius: 3px +} + +.close { + float: right; + font-size: 21px; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #FFF; + filter: alpha(opacity=20); + opacity: .2 +} + +.close:focus, .close:hover { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5 +} + +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: 0 0; + border: 0 +} + +.modal-open { + overflow: hidden +} + +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0 +} + +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%) +} + +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0) +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto +} + +.modal-dialog { + position: relative; + width: auto; + margin: 10px +} + +.modal-content { + position: relative; + background-color: #FFF; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5) +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000 +} + +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0 +} + +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5 +} + +.modal-header { + min-height: 16.43px; + padding: 15px; + border-bottom: 1px solid #E5E5E5 +} + +.modal-header .close { + margin-top: -2px +} + +.modal-title { + margin: 0; + line-height: 1.42857143 +} + +.modal-body { + position: relative; + padding: 15px +} + +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #E5E5E5 +} + +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px +} + +.modal-footer .btn-group .btn + .btn { + margin-left: -1px +} + +.modal-footer .btn-block + .btn-block { + margin-left: 0 +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll +} + +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto + } + + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5) + } + + .modal-sm { + width: 300px + } +} + +@media (min-width: 992px) { + .modal-lg { + width: 900px + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + filter: alpha(opacity=0); + opacity: 0; + line-break: auto +} + +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9 +} + +.tooltip.top { + padding: 5px 0; + margin-top: -3px +} + +.tooltip.right { + padding: 0 5px; + margin-left: 3px +} + +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px +} + +.tooltip.left { + padding: 0 5px; + margin-left: -3px +} + +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #FFF; + text-align: center; + background-color: #000; + border-radius: 4px +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000 +} + +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000 +} + +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000 +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000 +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000 +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000 +} + +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000 +} + +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000 +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + background-color: #FFF; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #CCC; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + line-break: auto +} + +.popover.top { + margin-top: -10px +} + +.popover.right { + margin-left: 10px +} + +.popover.bottom { + margin-top: 10px +} + +.popover.left { + margin-left: -10px +} + +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #F7F7F7; + border-bottom: 1px solid #EBEBEB; + border-radius: 5px 5px 0 0 +} + +.popover-content { + padding: 9px 14px +} + +.popover > .arrow, .popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid +} + +.popover > .arrow { + border-width: 11px +} + +.popover > .arrow:after { + content: ""; + border-width: 10px +} + +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0 +} + +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #FFF; + border-bottom-width: 0 +} + +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0 +} + +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #FFF; + border-left-width: 0 +} + +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25) +} + +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #FFF +} + +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25) +} + +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #FFF +} + +.carousel { + position: relative +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden +} + +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left +} + +.carousel-inner > .item > a > img, .carousel-inner > .item > img { + line-height: 1 +} + +@media all and (transform-3d),(-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px + } + + .carousel-inner > .item.active.right, .carousel-inner > .item.next { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) + } + + .carousel-inner > .item.active.left, .carousel-inner > .item.prev { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) + } + + .carousel-inner > .item.active, .carousel-inner > .item.next.left, .carousel-inner > .item.prev.right { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) + } +} + +.carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { + display: block +} + +.carousel-inner > .active { + left: 0 +} + +.carousel-inner > .next, .carousel-inner > .prev { + position: absolute; + top: 0; + width: 100% +} + +.carousel-inner > .next { + left: 100% +} + +.carousel-inner > .prev { + left: -100% +} + +.carousel-inner > .next.left, .carousel-inner > .prev.right { + left: 0 +} + +.carousel-inner > .active.left { + left: -100% +} + +.carousel-inner > .active.right { + left: 100% +} + +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #FFF; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + filter: alpha(opacity=50); + opacity: .5 +} + +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x +} + +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x +} + +.carousel-control:focus, .carousel-control:hover { + color: #FFF; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9 +} + +.carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next, .carousel-control .icon-prev { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + margin-top: -10px +} + +.carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { + left: 50%; + margin-left: -10px +} + +.carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { + right: 50%; + margin-right: -10px +} + +.carousel-control .icon-next, .carousel-control .icon-prev { + width: 20px; + height: 20px; + font-family: serif; + line-height: 1 +} + +.carousel-control .icon-prev:before { + content: '\2039' +} + +.carousel-control .icon-next:before { + content: '\203a' +} + +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none +} + +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #FFF; + border-radius: 10px +} + +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #FFF +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #FFF; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6) +} + +.carousel-caption .btn { + text-shadow: none +} + +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next, .carousel-control .icon-prev { + width: 30px; + height: 30px; + margin-top: -15px; + font-size: 30px + } + + .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { + margin-left: -15px + } + + .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { + margin-right: -15px + } + + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px + } + + .carousel-indicators { + bottom: 20px + } +} + +.btn-group-vertical > .btn-group:after, .btn-group-vertical > .btn-group:before, .btn-toolbar:after, .btn-toolbar:before, .clearfix:after, .clearfix:before, .container-fluid:after, .container-fluid:before, .container:after, .container:before, .dl-horizontal dd:after, .dl-horizontal dd:before, .form-horizontal .form-group:after, .form-horizontal .form-group:before, .modal-footer:after, .modal-footer:before, .nav:after, .nav:before, .navbar-collapse:after, .navbar-collapse:before, .navbar-header:after, .navbar-header:before, .navbar:after, .navbar:before, .pager:after, .pager:before, .panel-body:after, .panel-body:before, .row:after, .row:before { + display: table; + content: " " +} + +.btn-group-vertical > .btn-group:after, .btn-toolbar:after, .clearfix:after, .container-fluid:after, .container:after, .dl-horizontal dd:after, .form-horizontal .form-group:after, .modal-footer:after, .nav:after, .navbar-collapse:after, .navbar-header:after, .navbar:after, .pager:after, .panel-body:after, .row:after { + clear: both +} + +.center-block { + display: block; + margin-right: auto; + margin-left: auto +} + +.pull-right { + float: right !important +} + +.pull-left { + float: left !important +} + +.hide { + display: none !important +} + +.show { + display: block !important +} + +.invisible { + visibility: hidden +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0 +} + +.hidden { + display: none !important +} + +.affix { + position: fixed +} + +@-ms-viewport { + width: device-width +} + +.visible-lg, .visible-md, .visible-sm, .visible-xs { + display: none !important +} + +.visible-lg-block, .visible-lg-inline, .visible-lg-inline-block, .visible-md-block, .visible-md-inline, .visible-md-inline-block, .visible-sm-block, .visible-sm-inline, .visible-sm-inline-block, .visible-xs-block, .visible-xs-inline, .visible-xs-inline-block { + display: none !important +} + +@media (max-width: 767px) { + .visible-xs { + display: block !important + } + + table.visible-xs { + display: table !important + } + + tr.visible-xs { + display: table-row !important + } + + td.visible-xs, th.visible-xs { + display: table-cell !important + } +} + +@media (max-width: 767px) { + .visible-xs-block { + display: block !important + } +} + +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important + } +} + +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important + } + + table.visible-sm { + display: table !important + } + + tr.visible-sm { + display: table-row !important + } + + td.visible-sm, th.visible-sm { + display: table-cell !important + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important + } + + table.visible-md { + display: table !important + } + + tr.visible-md { + display: table-row !important + } + + td.visible-md, th.visible-md { + display: table-cell !important + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important + } +} + +@media (min-width: 1200px) { + .visible-lg { + display: block !important + } + + table.visible-lg { + display: table !important + } + + tr.visible-lg { + display: table-row !important + } + + td.visible-lg, th.visible-lg { + display: table-cell !important + } +} + +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important + } +} + +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important + } +} + +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important + } +} + +@media (max-width: 767px) { + .hidden-xs { + display: none !important + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important + } +} + +@media (min-width: 1200px) { + .hidden-lg { + display: none !important + } +} + +.visible-print { + display: none !important +} + +@media print { + .visible-print { + display: block !important + } + + table.visible-print { + display: table !important + } + + tr.visible-print { + display: table-row !important + } + + td.visible-print, th.visible-print { + display: table-cell !important + } +} + +.visible-print-block { + display: none !important +} + +@media print { + .visible-print-block { + display: block !important + } +} + +.visible-print-inline { + display: none !important +} + +@media print { + .visible-print-inline { + display: inline !important + } +} + +.visible-print-inline-block { + display: none !important +} + +@media print { + .visible-print-inline-block { + display: inline-block !important + } +} + +@media print { + .hidden-print { + display: none !important + } +} diff --git a/tray/css/font-awesome.min.css b/tray/css/font-awesome.min.css new file mode 100644 index 0000000..0e90314 --- /dev/null +++ b/tray/css/font-awesome.min.css @@ -0,0 +1,2101 @@ +/*! + * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.2.0'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal +} + +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale +} + +.fa-lg { + font-size: 1.33333333em; + line-height: .75em; + vertical-align: -15% +} + +.fa-2x { + font-size: 2em +} + +.fa-3x { + font-size: 3em +} + +.fa-4x { + font-size: 4em +} + +.fa-5x { + font-size: 5em +} + +.fa-fw { + width: 1.28571429em; + text-align: center +} + +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none +} + +.fa-ul > li { + position: relative +} + +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: .14285714em; + text-align: center +} + +.fa-li.fa-lg { + left: -1.85714286em +} + +.fa-border { + padding: .2em .25em .15em; + border: solid .08em #EEE; + border-radius: .1em +} + +.pull-right { + float: right +} + +.pull-left { + float: left +} + +.fa.pull-left { + margin-right: .3em +} + +.fa.pull-right { + margin-left: .3em +} + +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear +} + +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg) + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg) + } +} + +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg) + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg) + } +} + +.fa-rotate-90 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg) +} + +.fa-rotate-180 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg) +} + +.fa-rotate-270 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg) +} + +.fa-flip-horizontal { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1) +} + +.fa-flip-vertical { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1) +} + +:root .fa-rotate-90, :root .fa-rotate-180, :root .fa-rotate-270, :root .fa-flip-horizontal, :root .fa-flip-vertical { + filter: none +} + +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle +} + +.fa-stack-1x, .fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center +} + +.fa-stack-1x { + line-height: inherit +} + +.fa-stack-2x { + font-size: 2em +} + +.fa-inverse { + color: #FFF +} + +.fa-glass:before { + content: "\f000" +} + +.fa-music:before { + content: "\f001" +} + +.fa-search:before { + content: "\f002" +} + +.fa-envelope-o:before { + content: "\f003" +} + +.fa-heart:before { + content: "\f004" +} + +.fa-star:before { + content: "\f005" +} + +.fa-star-o:before { + content: "\f006" +} + +.fa-user:before { + content: "\f007" +} + +.fa-film:before { + content: "\f008" +} + +.fa-th-large:before { + content: "\f009" +} + +.fa-th:before { + content: "\f00a" +} + +.fa-th-list:before { + content: "\f00b" +} + +.fa-check:before { + content: "\f00c" +} + +.fa-remove:before, .fa-close:before, .fa-times:before { + content: "\f00d" +} + +.fa-search-plus:before { + content: "\f00e" +} + +.fa-search-minus:before { + content: "\f010" +} + +.fa-power-off:before { + content: "\f011" +} + +.fa-signal:before { + content: "\f012" +} + +.fa-gear:before, .fa-cog:before { + content: "\f013" +} + +.fa-trash-o:before { + content: "\f014" +} + +.fa-home:before { + content: "\f015" +} + +.fa-file-o:before { + content: "\f016" +} + +.fa-clock-o:before { + content: "\f017" +} + +.fa-road:before { + content: "\f018" +} + +.fa-download:before { + content: "\f019" +} + +.fa-arrow-circle-o-down:before { + content: "\f01a" +} + +.fa-arrow-circle-o-up:before { + content: "\f01b" +} + +.fa-inbox:before { + content: "\f01c" +} + +.fa-play-circle-o:before { + content: "\f01d" +} + +.fa-rotate-right:before, .fa-repeat:before { + content: "\f01e" +} + +.fa-refresh:before { + content: "\f021" +} + +.fa-list-alt:before { + content: "\f022" +} + +.fa-lock:before { + content: "\f023" +} + +.fa-flag:before { + content: "\f024" +} + +.fa-headphones:before { + content: "\f025" +} + +.fa-volume-off:before { + content: "\f026" +} + +.fa-volume-down:before { + content: "\f027" +} + +.fa-volume-up:before { + content: "\f028" +} + +.fa-qrcode:before { + content: "\f029" +} + +.fa-barcode:before { + content: "\f02a" +} + +.fa-tag:before { + content: "\f02b" +} + +.fa-tags:before { + content: "\f02c" +} + +.fa-book:before { + content: "\f02d" +} + +.fa-bookmark:before { + content: "\f02e" +} + +.fa-print:before { + content: "\f02f" +} + +.fa-camera:before { + content: "\f030" +} + +.fa-font:before { + content: "\f031" +} + +.fa-bold:before { + content: "\f032" +} + +.fa-italic:before { + content: "\f033" +} + +.fa-text-height:before { + content: "\f034" +} + +.fa-text-width:before { + content: "\f035" +} + +.fa-align-left:before { + content: "\f036" +} + +.fa-align-center:before { + content: "\f037" +} + +.fa-align-right:before { + content: "\f038" +} + +.fa-align-justify:before { + content: "\f039" +} + +.fa-list:before { + content: "\f03a" +} + +.fa-dedent:before, .fa-outdent:before { + content: "\f03b" +} + +.fa-indent:before { + content: "\f03c" +} + +.fa-video-camera:before { + content: "\f03d" +} + +.fa-photo:before, .fa-image:before, .fa-picture-o:before { + content: "\f03e" +} + +.fa-pencil:before { + content: "\f040" +} + +.fa-map-marker:before { + content: "\f041" +} + +.fa-adjust:before { + content: "\f042" +} + +.fa-tint:before { + content: "\f043" +} + +.fa-edit:before, .fa-pencil-square-o:before { + content: "\f044" +} + +.fa-share-square-o:before { + content: "\f045" +} + +.fa-check-square-o:before { + content: "\f046" +} + +.fa-arrows:before { + content: "\f047" +} + +.fa-step-backward:before { + content: "\f048" +} + +.fa-fast-backward:before { + content: "\f049" +} + +.fa-backward:before { + content: "\f04a" +} + +.fa-play:before { + content: "\f04b" +} + +.fa-pause:before { + content: "\f04c" +} + +.fa-stop:before { + content: "\f04d" +} + +.fa-forward:before { + content: "\f04e" +} + +.fa-fast-forward:before { + content: "\f050" +} + +.fa-step-forward:before { + content: "\f051" +} + +.fa-eject:before { + content: "\f052" +} + +.fa-chevron-left:before { + content: "\f053" +} + +.fa-chevron-right:before { + content: "\f054" +} + +.fa-plus-circle:before { + content: "\f055" +} + +.fa-minus-circle:before { + content: "\f056" +} + +.fa-times-circle:before { + content: "\f057" +} + +.fa-check-circle:before { + content: "\f058" +} + +.fa-question-circle:before { + content: "\f059" +} + +.fa-info-circle:before { + content: "\f05a" +} + +.fa-crosshairs:before { + content: "\f05b" +} + +.fa-times-circle-o:before { + content: "\f05c" +} + +.fa-check-circle-o:before { + content: "\f05d" +} + +.fa-ban:before { + content: "\f05e" +} + +.fa-arrow-left:before { + content: "\f060" +} + +.fa-arrow-right:before { + content: "\f061" +} + +.fa-arrow-up:before { + content: "\f062" +} + +.fa-arrow-down:before { + content: "\f063" +} + +.fa-mail-forward:before, .fa-share:before { + content: "\f064" +} + +.fa-expand:before { + content: "\f065" +} + +.fa-compress:before { + content: "\f066" +} + +.fa-plus:before { + content: "\f067" +} + +.fa-minus:before { + content: "\f068" +} + +.fa-asterisk:before { + content: "\f069" +} + +.fa-exclamation-circle:before { + content: "\f06a" +} + +.fa-gift:before { + content: "\f06b" +} + +.fa-leaf:before { + content: "\f06c" +} + +.fa-fire:before { + content: "\f06d" +} + +.fa-eye:before { + content: "\f06e" +} + +.fa-eye-slash:before { + content: "\f070" +} + +.fa-warning:before, .fa-exclamation-triangle:before { + content: "\f071" +} + +.fa-plane:before { + content: "\f072" +} + +.fa-calendar:before { + content: "\f073" +} + +.fa-random:before { + content: "\f074" +} + +.fa-comment:before { + content: "\f075" +} + +.fa-magnet:before { + content: "\f076" +} + +.fa-chevron-up:before { + content: "\f077" +} + +.fa-chevron-down:before { + content: "\f078" +} + +.fa-retweet:before { + content: "\f079" +} + +.fa-shopping-cart:before { + content: "\f07a" +} + +.fa-folder:before { + content: "\f07b" +} + +.fa-folder-open:before { + content: "\f07c" +} + +.fa-arrows-v:before { + content: "\f07d" +} + +.fa-arrows-h:before { + content: "\f07e" +} + +.fa-bar-chart-o:before, .fa-bar-chart:before { + content: "\f080" +} + +.fa-twitter-square:before { + content: "\f081" +} + +.fa-facebook-square:before { + content: "\f082" +} + +.fa-camera-retro:before { + content: "\f083" +} + +.fa-key:before { + content: "\f084" +} + +.fa-gears:before, .fa-cogs:before { + content: "\f085" +} + +.fa-comments:before { + content: "\f086" +} + +.fa-thumbs-o-up:before { + content: "\f087" +} + +.fa-thumbs-o-down:before { + content: "\f088" +} + +.fa-star-half:before { + content: "\f089" +} + +.fa-heart-o:before { + content: "\f08a" +} + +.fa-sign-out:before { + content: "\f08b" +} + +.fa-linkedin-square:before { + content: "\f08c" +} + +.fa-thumb-tack:before { + content: "\f08d" +} + +.fa-external-link:before { + content: "\f08e" +} + +.fa-sign-in:before { + content: "\f090" +} + +.fa-trophy:before { + content: "\f091" +} + +.fa-github-square:before { + content: "\f092" +} + +.fa-upload:before { + content: "\f093" +} + +.fa-lemon-o:before { + content: "\f094" +} + +.fa-phone:before { + content: "\f095" +} + +.fa-square-o:before { + content: "\f096" +} + +.fa-bookmark-o:before { + content: "\f097" +} + +.fa-phone-square:before { + content: "\f098" +} + +.fa-twitter:before { + content: "\f099" +} + +.fa-facebook:before { + content: "\f09a" +} + +.fa-github:before { + content: "\f09b" +} + +.fa-unlock:before { + content: "\f09c" +} + +.fa-credit-card:before { + content: "\f09d" +} + +.fa-rss:before { + content: "\f09e" +} + +.fa-hdd-o:before { + content: "\f0a0" +} + +.fa-bullhorn:before { + content: "\f0a1" +} + +.fa-bell:before { + content: "\f0f3" +} + +.fa-certificate:before { + content: "\f0a3" +} + +.fa-hand-o-right:before { + content: "\f0a4" +} + +.fa-hand-o-left:before { + content: "\f0a5" +} + +.fa-hand-o-up:before { + content: "\f0a6" +} + +.fa-hand-o-down:before { + content: "\f0a7" +} + +.fa-arrow-circle-left:before { + content: "\f0a8" +} + +.fa-arrow-circle-right:before { + content: "\f0a9" +} + +.fa-arrow-circle-up:before { + content: "\f0aa" +} + +.fa-arrow-circle-down:before { + content: "\f0ab" +} + +.fa-globe:before { + content: "\f0ac" +} + +.fa-wrench:before { + content: "\f0ad" +} + +.fa-tasks:before { + content: "\f0ae" +} + +.fa-filter:before { + content: "\f0b0" +} + +.fa-briefcase:before { + content: "\f0b1" +} + +.fa-arrows-alt:before { + content: "\f0b2" +} + +.fa-group:before, .fa-users:before { + content: "\f0c0" +} + +.fa-chain:before, .fa-link:before { + content: "\f0c1" +} + +.fa-cloud:before { + content: "\f0c2" +} + +.fa-flask:before { + content: "\f0c3" +} + +.fa-cut:before, .fa-scissors:before { + content: "\f0c4" +} + +.fa-copy:before, .fa-files-o:before { + content: "\f0c5" +} + +.fa-paperclip:before { + content: "\f0c6" +} + +.fa-save:before, .fa-floppy-o:before { + content: "\f0c7" +} + +.fa-square:before { + content: "\f0c8" +} + +.fa-navicon:before, .fa-reorder:before, .fa-bars:before { + content: "\f0c9" +} + +.fa-list-ul:before { + content: "\f0ca" +} + +.fa-list-ol:before { + content: "\f0cb" +} + +.fa-strikethrough:before { + content: "\f0cc" +} + +.fa-underline:before { + content: "\f0cd" +} + +.fa-table:before { + content: "\f0ce" +} + +.fa-magic:before { + content: "\f0d0" +} + +.fa-truck:before { + content: "\f0d1" +} + +.fa-pinterest:before { + content: "\f0d2" +} + +.fa-pinterest-square:before { + content: "\f0d3" +} + +.fa-google-plus-square:before { + content: "\f0d4" +} + +.fa-google-plus:before { + content: "\f0d5" +} + +.fa-money:before { + content: "\f0d6" +} + +.fa-caret-down:before { + content: "\f0d7" +} + +.fa-caret-up:before { + content: "\f0d8" +} + +.fa-caret-left:before { + content: "\f0d9" +} + +.fa-caret-right:before { + content: "\f0da" +} + +.fa-columns:before { + content: "\f0db" +} + +.fa-unsorted:before, .fa-sort:before { + content: "\f0dc" +} + +.fa-sort-down:before, .fa-sort-desc:before { + content: "\f0dd" +} + +.fa-sort-up:before, .fa-sort-asc:before { + content: "\f0de" +} + +.fa-envelope:before { + content: "\f0e0" +} + +.fa-linkedin:before { + content: "\f0e1" +} + +.fa-rotate-left:before, .fa-undo:before { + content: "\f0e2" +} + +.fa-legal:before, .fa-gavel:before { + content: "\f0e3" +} + +.fa-dashboard:before, .fa-tachometer:before { + content: "\f0e4" +} + +.fa-comment-o:before { + content: "\f0e5" +} + +.fa-comments-o:before { + content: "\f0e6" +} + +.fa-flash:before, .fa-bolt:before { + content: "\f0e7" +} + +.fa-sitemap:before { + content: "\f0e8" +} + +.fa-umbrella:before { + content: "\f0e9" +} + +.fa-paste:before, .fa-clipboard:before { + content: "\f0ea" +} + +.fa-lightbulb-o:before { + content: "\f0eb" +} + +.fa-exchange:before { + content: "\f0ec" +} + +.fa-cloud-download:before { + content: "\f0ed" +} + +.fa-cloud-upload:before { + content: "\f0ee" +} + +.fa-user-md:before { + content: "\f0f0" +} + +.fa-stethoscope:before { + content: "\f0f1" +} + +.fa-suitcase:before { + content: "\f0f2" +} + +.fa-bell-o:before { + content: "\f0a2" +} + +.fa-coffee:before { + content: "\f0f4" +} + +.fa-cutlery:before { + content: "\f0f5" +} + +.fa-file-text-o:before { + content: "\f0f6" +} + +.fa-building-o:before { + content: "\f0f7" +} + +.fa-hospital-o:before { + content: "\f0f8" +} + +.fa-ambulance:before { + content: "\f0f9" +} + +.fa-medkit:before { + content: "\f0fa" +} + +.fa-fighter-jet:before { + content: "\f0fb" +} + +.fa-beer:before { + content: "\f0fc" +} + +.fa-h-square:before { + content: "\f0fd" +} + +.fa-plus-square:before { + content: "\f0fe" +} + +.fa-angle-double-left:before { + content: "\f100" +} + +.fa-angle-double-right:before { + content: "\f101" +} + +.fa-angle-double-up:before { + content: "\f102" +} + +.fa-angle-double-down:before { + content: "\f103" +} + +.fa-angle-left:before { + content: "\f104" +} + +.fa-angle-right:before { + content: "\f105" +} + +.fa-angle-up:before { + content: "\f106" +} + +.fa-angle-down:before { + content: "\f107" +} + +.fa-desktop:before { + content: "\f108" +} + +.fa-laptop:before { + content: "\f109" +} + +.fa-tablet:before { + content: "\f10a" +} + +.fa-mobile-phone:before, .fa-mobile:before { + content: "\f10b" +} + +.fa-circle-o:before { + content: "\f10c" +} + +.fa-quote-left:before { + content: "\f10d" +} + +.fa-quote-right:before { + content: "\f10e" +} + +.fa-spinner:before { + content: "\f110" +} + +.fa-circle:before { + content: "\f111" +} + +.fa-mail-reply:before, .fa-reply:before { + content: "\f112" +} + +.fa-github-alt:before { + content: "\f113" +} + +.fa-folder-o:before { + content: "\f114" +} + +.fa-folder-open-o:before { + content: "\f115" +} + +.fa-smile-o:before { + content: "\f118" +} + +.fa-frown-o:before { + content: "\f119" +} + +.fa-meh-o:before { + content: "\f11a" +} + +.fa-gamepad:before { + content: "\f11b" +} + +.fa-keyboard-o:before { + content: "\f11c" +} + +.fa-flag-o:before { + content: "\f11d" +} + +.fa-flag-checkered:before { + content: "\f11e" +} + +.fa-terminal:before { + content: "\f120" +} + +.fa-code:before { + content: "\f121" +} + +.fa-mail-reply-all:before, .fa-reply-all:before { + content: "\f122" +} + +.fa-star-half-empty:before, .fa-star-half-full:before, .fa-star-half-o:before { + content: "\f123" +} + +.fa-location-arrow:before { + content: "\f124" +} + +.fa-crop:before { + content: "\f125" +} + +.fa-code-fork:before { + content: "\f126" +} + +.fa-unlink:before, .fa-chain-broken:before { + content: "\f127" +} + +.fa-question:before { + content: "\f128" +} + +.fa-info:before { + content: "\f129" +} + +.fa-exclamation:before { + content: "\f12a" +} + +.fa-superscript:before { + content: "\f12b" +} + +.fa-subscript:before { + content: "\f12c" +} + +.fa-eraser:before { + content: "\f12d" +} + +.fa-puzzle-piece:before { + content: "\f12e" +} + +.fa-microphone:before { + content: "\f130" +} + +.fa-microphone-slash:before { + content: "\f131" +} + +.fa-shield:before { + content: "\f132" +} + +.fa-calendar-o:before { + content: "\f133" +} + +.fa-fire-extinguisher:before { + content: "\f134" +} + +.fa-rocket:before { + content: "\f135" +} + +.fa-maxcdn:before { + content: "\f136" +} + +.fa-chevron-circle-left:before { + content: "\f137" +} + +.fa-chevron-circle-right:before { + content: "\f138" +} + +.fa-chevron-circle-up:before { + content: "\f139" +} + +.fa-chevron-circle-down:before { + content: "\f13a" +} + +.fa-html5:before { + content: "\f13b" +} + +.fa-css3:before { + content: "\f13c" +} + +.fa-anchor:before { + content: "\f13d" +} + +.fa-unlock-alt:before { + content: "\f13e" +} + +.fa-bullseye:before { + content: "\f140" +} + +.fa-ellipsis-h:before { + content: "\f141" +} + +.fa-ellipsis-v:before { + content: "\f142" +} + +.fa-rss-square:before { + content: "\f143" +} + +.fa-play-circle:before { + content: "\f144" +} + +.fa-ticket:before { + content: "\f145" +} + +.fa-minus-square:before { + content: "\f146" +} + +.fa-minus-square-o:before { + content: "\f147" +} + +.fa-level-up:before { + content: "\f148" +} + +.fa-level-down:before { + content: "\f149" +} + +.fa-check-square:before { + content: "\f14a" +} + +.fa-pencil-square:before { + content: "\f14b" +} + +.fa-external-link-square:before { + content: "\f14c" +} + +.fa-share-square:before { + content: "\f14d" +} + +.fa-compass:before { + content: "\f14e" +} + +.fa-toggle-down:before, .fa-caret-square-o-down:before { + content: "\f150" +} + +.fa-toggle-up:before, .fa-caret-square-o-up:before { + content: "\f151" +} + +.fa-toggle-right:before, .fa-caret-square-o-right:before { + content: "\f152" +} + +.fa-euro:before, .fa-eur:before { + content: "\f153" +} + +.fa-gbp:before { + content: "\f154" +} + +.fa-dollar:before, .fa-usd:before { + content: "\f155" +} + +.fa-rupee:before, .fa-inr:before { + content: "\f156" +} + +.fa-cny:before, .fa-rmb:before, .fa-yen:before, .fa-jpy:before { + content: "\f157" +} + +.fa-ruble:before, .fa-rouble:before, .fa-rub:before { + content: "\f158" +} + +.fa-won:before, .fa-krw:before { + content: "\f159" +} + +.fa-bitcoin:before, .fa-btc:before { + content: "\f15a" +} + +.fa-file:before { + content: "\f15b" +} + +.fa-file-text:before { + content: "\f15c" +} + +.fa-sort-alpha-asc:before { + content: "\f15d" +} + +.fa-sort-alpha-desc:before { + content: "\f15e" +} + +.fa-sort-amount-asc:before { + content: "\f160" +} + +.fa-sort-amount-desc:before { + content: "\f161" +} + +.fa-sort-numeric-asc:before { + content: "\f162" +} + +.fa-sort-numeric-desc:before { + content: "\f163" +} + +.fa-thumbs-up:before { + content: "\f164" +} + +.fa-thumbs-down:before { + content: "\f165" +} + +.fa-youtube-square:before { + content: "\f166" +} + +.fa-youtube:before { + content: "\f167" +} + +.fa-xing:before { + content: "\f168" +} + +.fa-xing-square:before { + content: "\f169" +} + +.fa-youtube-play:before { + content: "\f16a" +} + +.fa-dropbox:before { + content: "\f16b" +} + +.fa-stack-overflow:before { + content: "\f16c" +} + +.fa-instagram:before { + content: "\f16d" +} + +.fa-flickr:before { + content: "\f16e" +} + +.fa-adn:before { + content: "\f170" +} + +.fa-bitbucket:before { + content: "\f171" +} + +.fa-bitbucket-square:before { + content: "\f172" +} + +.fa-tumblr:before { + content: "\f173" +} + +.fa-tumblr-square:before { + content: "\f174" +} + +.fa-long-arrow-down:before { + content: "\f175" +} + +.fa-long-arrow-up:before { + content: "\f176" +} + +.fa-long-arrow-left:before { + content: "\f177" +} + +.fa-long-arrow-right:before { + content: "\f178" +} + +.fa-apple:before { + content: "\f179" +} + +.fa-windows:before { + content: "\f17a" +} + +.fa-android:before { + content: "\f17b" +} + +.fa-linux:before { + content: "\f17c" +} + +.fa-dribbble:before { + content: "\f17d" +} + +.fa-skype:before { + content: "\f17e" +} + +.fa-foursquare:before { + content: "\f180" +} + +.fa-trello:before { + content: "\f181" +} + +.fa-female:before { + content: "\f182" +} + +.fa-male:before { + content: "\f183" +} + +.fa-gittip:before { + content: "\f184" +} + +.fa-sun-o:before { + content: "\f185" +} + +.fa-moon-o:before { + content: "\f186" +} + +.fa-archive:before { + content: "\f187" +} + +.fa-bug:before { + content: "\f188" +} + +.fa-vk:before { + content: "\f189" +} + +.fa-weibo:before { + content: "\f18a" +} + +.fa-renren:before { + content: "\f18b" +} + +.fa-pagelines:before { + content: "\f18c" +} + +.fa-stack-exchange:before { + content: "\f18d" +} + +.fa-arrow-circle-o-right:before { + content: "\f18e" +} + +.fa-arrow-circle-o-left:before { + content: "\f190" +} + +.fa-toggle-left:before, .fa-caret-square-o-left:before { + content: "\f191" +} + +.fa-dot-circle-o:before { + content: "\f192" +} + +.fa-wheelchair:before { + content: "\f193" +} + +.fa-vimeo-square:before { + content: "\f194" +} + +.fa-turkish-lira:before, .fa-try:before { + content: "\f195" +} + +.fa-plus-square-o:before { + content: "\f196" +} + +.fa-space-shuttle:before { + content: "\f197" +} + +.fa-slack:before { + content: "\f198" +} + +.fa-envelope-square:before { + content: "\f199" +} + +.fa-wordpress:before { + content: "\f19a" +} + +.fa-openid:before { + content: "\f19b" +} + +.fa-institution:before, .fa-bank:before, .fa-university:before { + content: "\f19c" +} + +.fa-mortar-board:before, .fa-graduation-cap:before { + content: "\f19d" +} + +.fa-yahoo:before { + content: "\f19e" +} + +.fa-google:before { + content: "\f1a0" +} + +.fa-reddit:before { + content: "\f1a1" +} + +.fa-reddit-square:before { + content: "\f1a2" +} + +.fa-stumbleupon-circle:before { + content: "\f1a3" +} + +.fa-stumbleupon:before { + content: "\f1a4" +} + +.fa-delicious:before { + content: "\f1a5" +} + +.fa-digg:before { + content: "\f1a6" +} + +.fa-pied-piper:before { + content: "\f1a7" +} + +.fa-pied-piper-alt:before { + content: "\f1a8" +} + +.fa-drupal:before { + content: "\f1a9" +} + +.fa-joomla:before { + content: "\f1aa" +} + +.fa-language:before { + content: "\f1ab" +} + +.fa-fax:before { + content: "\f1ac" +} + +.fa-building:before { + content: "\f1ad" +} + +.fa-child:before { + content: "\f1ae" +} + +.fa-paw:before { + content: "\f1b0" +} + +.fa-spoon:before { + content: "\f1b1" +} + +.fa-cube:before { + content: "\f1b2" +} + +.fa-cubes:before { + content: "\f1b3" +} + +.fa-behance:before { + content: "\f1b4" +} + +.fa-behance-square:before { + content: "\f1b5" +} + +.fa-steam:before { + content: "\f1b6" +} + +.fa-steam-square:before { + content: "\f1b7" +} + +.fa-recycle:before { + content: "\f1b8" +} + +.fa-automobile:before, .fa-car:before { + content: "\f1b9" +} + +.fa-cab:before, .fa-taxi:before { + content: "\f1ba" +} + +.fa-tree:before { + content: "\f1bb" +} + +.fa-spotify:before { + content: "\f1bc" +} + +.fa-deviantart:before { + content: "\f1bd" +} + +.fa-soundcloud:before { + content: "\f1be" +} + +.fa-database:before { + content: "\f1c0" +} + +.fa-file-pdf-o:before { + content: "\f1c1" +} + +.fa-file-word-o:before { + content: "\f1c2" +} + +.fa-file-excel-o:before { + content: "\f1c3" +} + +.fa-file-powerpoint-o:before { + content: "\f1c4" +} + +.fa-file-photo-o:before, .fa-file-picture-o:before, .fa-file-image-o:before { + content: "\f1c5" +} + +.fa-file-zip-o:before, .fa-file-archive-o:before { + content: "\f1c6" +} + +.fa-file-sound-o:before, .fa-file-audio-o:before { + content: "\f1c7" +} + +.fa-file-movie-o:before, .fa-file-video-o:before { + content: "\f1c8" +} + +.fa-file-code-o:before { + content: "\f1c9" +} + +.fa-vine:before { + content: "\f1ca" +} + +.fa-codepen:before { + content: "\f1cb" +} + +.fa-jsfiddle:before { + content: "\f1cc" +} + +.fa-life-bouy:before, .fa-life-buoy:before, .fa-life-saver:before, .fa-support:before, .fa-life-ring:before { + content: "\f1cd" +} + +.fa-circle-o-notch:before { + content: "\f1ce" +} + +.fa-ra:before, .fa-rebel:before { + content: "\f1d0" +} + +.fa-ge:before, .fa-empire:before { + content: "\f1d1" +} + +.fa-git-square:before { + content: "\f1d2" +} + +.fa-git:before { + content: "\f1d3" +} + +.fa-hacker-news:before { + content: "\f1d4" +} + +.fa-tencent-weibo:before { + content: "\f1d5" +} + +.fa-qq:before { + content: "\f1d6" +} + +.fa-wechat:before, .fa-weixin:before { + content: "\f1d7" +} + +.fa-send:before, .fa-paper-plane:before { + content: "\f1d8" +} + +.fa-send-o:before, .fa-paper-plane-o:before { + content: "\f1d9" +} + +.fa-history:before { + content: "\f1da" +} + +.fa-circle-thin:before { + content: "\f1db" +} + +.fa-header:before { + content: "\f1dc" +} + +.fa-paragraph:before { + content: "\f1dd" +} + +.fa-sliders:before { + content: "\f1de" +} + +.fa-share-alt:before { + content: "\f1e0" +} + +.fa-share-alt-square:before { + content: "\f1e1" +} + +.fa-bomb:before { + content: "\f1e2" +} + +.fa-soccer-ball-o:before, .fa-futbol-o:before { + content: "\f1e3" +} + +.fa-tty:before { + content: "\f1e4" +} + +.fa-binoculars:before { + content: "\f1e5" +} + +.fa-plug:before { + content: "\f1e6" +} + +.fa-slideshare:before { + content: "\f1e7" +} + +.fa-twitch:before { + content: "\f1e8" +} + +.fa-yelp:before { + content: "\f1e9" +} + +.fa-newspaper-o:before { + content: "\f1ea" +} + +.fa-wifi:before { + content: "\f1eb" +} + +.fa-calculator:before { + content: "\f1ec" +} + +.fa-paypal:before { + content: "\f1ed" +} + +.fa-google-wallet:before { + content: "\f1ee" +} + +.fa-cc-visa:before { + content: "\f1f0" +} + +.fa-cc-mastercard:before { + content: "\f1f1" +} + +.fa-cc-discover:before { + content: "\f1f2" +} + +.fa-cc-amex:before { + content: "\f1f3" +} + +.fa-cc-paypal:before { + content: "\f1f4" +} + +.fa-cc-stripe:before { + content: "\f1f5" +} + +.fa-bell-slash:before { + content: "\f1f6" +} + +.fa-bell-slash-o:before { + content: "\f1f7" +} + +.fa-trash:before { + content: "\f1f8" +} + +.fa-copyright:before { + content: "\f1f9" +} + +.fa-at:before { + content: "\f1fa" +} + +.fa-eyedropper:before { + content: "\f1fb" +} + +.fa-paint-brush:before { + content: "\f1fc" +} + +.fa-birthday-cake:before { + content: "\f1fd" +} + +.fa-area-chart:before { + content: "\f1fe" +} + +.fa-pie-chart:before { + content: "\f200" +} + +.fa-line-chart:before { + content: "\f201" +} + +.fa-lastfm:before { + content: "\f202" +} + +.fa-lastfm-square:before { + content: "\f203" +} + +.fa-toggle-off:before { + content: "\f204" +} + +.fa-toggle-on:before { + content: "\f205" +} + +.fa-bicycle:before { + content: "\f206" +} + +.fa-bus:before { + content: "\f207" +} + +.fa-ioxhost:before { + content: "\f208" +} + +.fa-angellist:before { + content: "\f209" +} + +.fa-cc:before { + content: "\f20a" +} + +.fa-shekel:before, .fa-sheqel:before, .fa-ils:before { + content: "\f20b" +} + +.fa-meanpath:before { + content: "\f20c" +} diff --git a/tray/css/gh-fork-ribbon.min.css b/tray/css/gh-fork-ribbon.min.css new file mode 100644 index 0000000..b67dbd3 --- /dev/null +++ b/tray/css/gh-fork-ribbon.min.css @@ -0,0 +1,7 @@ +/*! + * "Fork me on GitHub" CSS ribbon v0.2.3 | MIT License + * https://github.com/simonwhitaker/github-fork-ribbon-css +*/.github-fork-ribbon{width:12.1em;height:12.1em;position:absolute;overflow:hidden;top:0;right:0;z-index:9999;pointer-events:none;font-size:13px;text-decoration:none;text-indent:-999999px}.github-fork-ribbon.fixed{position:fixed}.github-fork-ribbon:active,.github-fork-ribbon:hover{background-color:rgba(0,0,0,0)}.github-fork-ribbon:after,.github-fork-ribbon:before{position:absolute;display:block;width:15.38em;height:1.54em;top:3.23em;right:-3.23em;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.github-fork-ribbon:before{content:"";padding:.38em 0;background-color:#a00;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.15)));background-image:-webkit-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:-moz-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:-ms-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:-o-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:linear-gradient(to bottom,rgba(0,0,0,0),rgba(0,0,0,.15));-webkit-box-shadow:0 .15em .23em 0 rgba(0,0,0,.5);-moz-box-shadow:0 .15em .23em 0 rgba(0,0,0,.5);box-shadow:0 .15em .23em 0 rgba(0,0,0,.5);pointer-events:auto}.github-fork-ribbon:after{content:attr(data-ribbon);color:#fff;font:700 1em "Helvetica Neue",Helvetica,Arial,sans-serif;line-height:1.54em;text-decoration:none;text-shadow:0 -.08em rgba(0,0,0,.5);text-align:center;text-indent:0;padding:.15em 0;margin:.15em 0;border-width:.08em 0;border-style:dotted;border-color:#fff;border-color:rgba(255,255,255,.7)}.github-fork-ribbon.left-bottom,.github-fork-ribbon.left-top{right:auto;left:0}.github-fork-ribbon.left-bottom,.github-fork-ribbon.right-bottom{top:auto;bottom:0}.github-fork-ribbon.left-bottom:after,.github-fork-ribbon.left-bottom:before,.github-fork-ribbon.left-top:after,.github-fork-ribbon.left-top:before{right:auto;left:-3.23em}.github-fork-ribbon.left-bottom:after,.github-fork-ribbon.left-bottom:before,.github-fork-ribbon.right-bottom:after,.github-fork-ribbon.right-bottom:before{top:auto;bottom:3.23em}.github-fork-ribbon.left-top:after,.github-fork-ribbon.left-top:before,.github-fork-ribbon.right-bottom:after,.github-fork-ribbon.right-bottom:before{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)} +/*# sourceMappingURL=gh-fork-ribbon.min.css.map */ +/* QZ Colors */ +.github-fork-ribbon:before { background-color: #3D994A; } \ No newline at end of file diff --git a/tray/css/style.css b/tray/css/style.css new file mode 100644 index 0000000..0fc8237 --- /dev/null +++ b/tray/css/style.css @@ -0,0 +1,427 @@ +.btn-default { + color: #333; + background-color: #FFF; + border-color: #CCC; +} + +.btn-primary { + color: #FFFFFF; + background-color: #44AB53; + border-color: #3D994A; +} + +.btn-success { + color: #FFF; + background-color: #33658A; + border-color: #2C5777; +} + +.btn-warning { + color: #FFF; + background-color: #F6AE2D; + border-color: #F5A515; +} + +.btn-danger { + color: #FFF; + background-color: #F26419; + border-color: #E5570D; +} + +.btn-info { + color: #FFF; + background-color: #3680A8; + border-color: #307195; +} + +.btn-default:hover { + background-color: #E6E6E6; + border-color: #B3B3B3; +} + +.btn-primary:hover { + background-color: #358741; + border-color: #2E7438; +} + +.btn-success:hover { + background-color: #254A65; + border-color: #1E3C52; +} + +.btn-warning:hover { + background-color: #E6970A; + border-color: #CE8709; +} + +.btn-danger:hover { + background-color: #CC4E0C; + border-color: #B4450A; +} + +.btn-info:hover { + background-color: #2A6382; + border-color: #24546E; +} + +.btn-default:focus { + background-color: #F2F2F2; + border-color: #BFBFBF; +} + +.btn-primary:focus { + background-color: #3D994A; + border-color: #358741; +} + +.btn-success:focus { + background-color: #2C5777; + border-color: #254A65; +} + +.btn-warning:focus { + background-color: #F5A515; + border-color: #E6970A; +} + +.btn-danger:focus { + background-color: #E5570D; + border-color: #CC4E0C; +} + +.btn-info:focus { + background-color: #307195; + border-color: #2A6382; +} + +.btn-default:active { + background-color: #E6E6E6; + border-color: #B3B3B3; +} + +.btn-primary:active { + background-color: #358741; + border-color: #2E7438; +} + +.btn-success:active { + background-color: #254A65; + border-color: #1E3C52; +} + +.btn-warning:active { + background-color: #E6970A; + border-color: #CE8709; +} + +.btn-danger:active { + background-color: #CC4E0C; + border-color: #B4450A; +} + +.btn-info:active { + background-color: #2A6382; + border-color: #24546E; +} + +.btn-default.disabled { + background-color: #FFFFFF; + border-color: #CFCFCF; +} + +.btn-primary.disabled { + background-color: #45AF55; + border-color: #3E9C4C; +} + +.btn-success.disabled { + background-color: #34688E; + border-color: #2D5A7B; +} + +.btn-warning.disabled { + background-color: #F6B032; + border-color: #F5A619; +} + +.btn-danger.disabled { + background-color: #F2671E; + border-color: #E9590D; +} + +.btn-info.disabled { + background-color: #3783AC; + border-color: #317499; +} + +a { + color: #3680A8; +} + +a:hover, +a:focus { + color: #24546E; +} + +.text-primary { + color: #44AB53; +} + +.text-danger { + color: #F26419; +} + +.form-control .has-error { + border-color: #EED3D7; +} + +.form-control .has-success { + border-color: #D6E9C6; +} + +.navbar-default { + background-color: #44AB53; +} + +.navbar-default .navbar-nav > li > a, +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-header .navbar-brand { + color: #FFF; +} + +@media (max-width: 768px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #FFF; + } +} + +.navbar-default .navbar-nav > li > a:hover { + background-color: #3D994A; +} + +.navbar-default .navbar-toggle > .icon-bar { + background-color: #FFF; +} + +.navbar-default .navbar-toggle:focus > .icon-bar { + background-color: #555; + color: #24546E; +} + +.navbar-default .navbar-toggle:hover > .icon-bar { + background-color: #FFF; +} + +.navbar-default .navbar-toggle:hover { + background-color: #3D994A; +} + +.panel.panel-primary { + border-color: #3D994A; +} + +.panel.panel-primary .panel-heading { + border-color: #3D994A; + background-color: #3D994A; +} + +.alert.alert-warning a { + color: #68522C; +} + +.alert.alert-danger a { + color: #B4450A; +} + +html { + position: relative; + min-height: 100%; +} + +body { + padding-bottom: 80px; +} + +table { + font-size: inherit; +} + +.versiontable > tbody > tr > th { + text-align: right; +} + +.versiontable > thead > tr > th { + vertical-align: top; +} + +.homesection + .homesection { + border-top: solid 1px #EEE; +} + +.homesection { + text-align: center; + margin: 40px 0; + padding: 40px 0; +} + +img.jumbo { + margin-top: -40px; + margin-left: -60px; +} + +h1.jumbo { + margin-top: 0; +} + +footer { + text-align: center; + height: 80px; + position: absolute; + bottom: 0; + width: 100%; +} + +@media (min-width: 768px) { + h1.jumbo, + h3.jumbo { + text-align: right; + } +} + +@media (max-width: 767px) { + h1.jumbo, + h3.jumbo { + text-align: center; + } +} + +@media (min-width: 1200px) { + h1.jumbo { + margin-top: 40px; + } +} + +@media (min-width: 768px) { + h3.jumbo { + margin-top: -6px; + } +} + +.vertical-align { + display: flex; + align-items: center; +} + +.hide_ { + display: none; +} + +.dropdown-submenu { + position: relative; +} + +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: 10px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} + +.drag-target { + /* color: white; */ + background: repeating-linear-gradient( + 45deg, + #dbf0de, + #dbf0de 10px, + #a5d9ad 10px, + #a5d9ad 20px + ); +} + +input[type="file"] { + display: none; +} + +button > .fa-caret-down { + height: 100%; + display: inline; + margin-left: -.75em; + margin-right: -1em; +} + +@media (min-width: 768px) { + .dropdown-submenu:hover > .dropdown-menu { + display: block; + } + + .dropdown-submenu > .dropdown-menu { + margin-left: -1px; + } +} + +.dropdown-submenu > a:after { + display: block; + content: " "; + float: right; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 5px 0 5px 5px; + border-left-color: #CCCCCC; + margin-top: 5px; + margin-right: -10px; +} + +.dropdown-submenu:hover > a:after { + border-left-color: #FFFFFF; +} + +.dropdown-submenu.pull-left { + float: none; +} + +.dropdown-submenu.pull-left > .dropdown-menu { + left: -100%; + margin-left: 10px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.tip { + border-bottom: 1px dashed; +} + +summary { + cursor: pointer; +} + +code { + color: #31708F; + background-color: #D9EDF7; +} + +input[type='radio'] { + margin-left: .5em; +} + +#printersLog { + max-height: 20em; + overflow: auto; +} + +#printersLog .INFO { + color: #303030; +} + +#printersLog .WARN { + color: #9F7435; +} + +#printersLog .FATAL { + color: #9F3535; +} diff --git a/tray/fonts/FontAwesome.otf b/tray/fonts/FontAwesome.otf new file mode 100644 index 0000000..681bdd4 Binary files /dev/null and b/tray/fonts/FontAwesome.otf differ diff --git a/tray/fonts/fontawesome-webfont.eot b/tray/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..a30335d Binary files /dev/null and b/tray/fonts/fontawesome-webfont.eot differ diff --git a/tray/fonts/fontawesome-webfont.svg b/tray/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..6fd19ab --- /dev/null +++ b/tray/fonts/fontawesome-webfont.svg @@ -0,0 +1,640 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tray/fonts/fontawesome-webfont.ttf b/tray/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..d7994e1 Binary files /dev/null and b/tray/fonts/fontawesome-webfont.ttf differ diff --git a/tray/fonts/fontawesome-webfont.woff b/tray/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..6fd4ede Binary files /dev/null and b/tray/fonts/fontawesome-webfont.woff differ diff --git a/tray/fonts/fontawesome-webfont.woff2 b/tray/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..5560193 Binary files /dev/null and b/tray/fonts/fontawesome-webfont.woff2 differ diff --git a/tray/js/.npmignore b/tray/js/.npmignore new file mode 100644 index 0000000..64ff58d --- /dev/null +++ b/tray/js/.npmignore @@ -0,0 +1,4 @@ +/additional/ +/dependencies/ +sample/ +.DS_Store diff --git a/tray/js/README.md b/tray/js/README.md new file mode 100644 index 0000000..8d50d75 --- /dev/null +++ b/tray/js/README.md @@ -0,0 +1,23 @@ +```js +const qz = require("qz-tray"); + +qz.websocket.connect().then(() => { + return qz.printers.find(); +}).then((printers) => { + console.log(printers); + let config = qz.configs.create('PDF'); + return qz.print(config, [{ + type: 'pixel', + format: 'html', + flavor: 'plain', + data: '

Hello JavaScript!

' + }]); +}).then(() => { + return qz.websocket.disconnect(); +}).then(() => { + // process.exit(0); +}).catch((err) => { + console.error(err); + // process.exit(1); +}); +``` diff --git a/tray/js/package.json b/tray/js/package.json new file mode 100644 index 0000000..0850c4b --- /dev/null +++ b/tray/js/package.json @@ -0,0 +1,35 @@ +{ + "name": "qz-tray", + "version": "2.2.6-SNAPSHOT", + "description": "Connects a web client to the QZ Tray software. Enables printing and device communication from javascript. ", + "main": "qz-tray.js", + "browser": { + "path": false + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/qzind/tray.git" + }, + "keywords": [ + "Printing", + "USB", + "Serial", + "RS232", + "USB", + "Scales", + "Zebra", + "Epson", + "Star", + "Citizen", + "BOCA" + ], + "author": "QZ Industries, LLC", + "license": "LGPL-2.1", + "bugs": { + "url": "https://github.com/qzind/tray/issues" + }, + "homepage": "https://github.com/qzind/tray#readme" +} diff --git a/tray/js/qz-tray.js b/tray/js/qz-tray.js new file mode 100644 index 0000000..3c85483 --- /dev/null +++ b/tray/js/qz-tray.js @@ -0,0 +1,2862 @@ +'use strict'; + +/** + * @version 2.2.6-SNAPSHOT + * @overview QZ Tray Connector + * @license LGPL-2.1-only + *

+ * Connects a web client to the QZ Tray software. + * Enables printing and device communication from javascript. + */ +var qz = (function() { + +///// POLYFILLS ///// + + if (!Array.isArray) { + Array.isArray = function(arg) { + return Object.prototype.toString.call(arg) === '[object Array]'; + }; + } + + if (!Number.isInteger) { + Number.isInteger = function(value) { + return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; + }; + } + +///// PRIVATE METHODS ///// + + var _qz = { + VERSION: "2.2.6-SNAPSHOT", //must match @version above + DEBUG: false, + + log: { + /** Debugging messages */ + trace: function() { if (_qz.DEBUG) { console.log.apply(console, arguments); } }, + /** General messages */ + info: function() { console.info.apply(console, arguments); }, + /** General warnings */ + warn: function() { console.warn.apply(console, arguments); }, + /** Debugging errors */ + allay: function() { if (_qz.DEBUG) { console.warn.apply(console, arguments); } }, + /** General errors */ + error: function() { console.error.apply(console, arguments); } + }, + + + //stream types + streams: { + serial: 'SERIAL', usb: 'USB', hid: 'HID', printer: 'PRINTER', file: 'FILE', socket: 'SOCKET' + }, + + + websocket: { + /** The actual websocket object managing the connection. */ + connection: null, + /** Track if a connection attempt is being cancelled. */ + shutdown: false, + + /** Default parameters used on new connections. Override values using options parameter on {@link qz.websocket.connect}. */ + connectConfig: { + host: ["localhost", "localhost.qz.io"], //hosts QZ Tray can be running on + hostIndex: 0, //internal var - index on host array + usingSecure: true, //boolean use of secure protocol + protocol: { + secure: "wss://", //secure websocket + insecure: "ws://" //insecure websocket + }, + port: { + secure: [8181, 8282, 8383, 8484], //list of secure ports QZ Tray could be listening on + insecure: [8182, 8283, 8384, 8485], //list of insecure ports QZ Tray could be listening on + portIndex: 0 //internal var - index on active port array + }, + keepAlive: 60, //time between pings to keep connection alive, in seconds + retries: 0, //number of times to reconnect before failing + delay: 0 //seconds before firing a connection + }, + + setup: { + /** Loop through possible ports to open connection, sets web socket calls that will settle the promise. */ + findConnection: function(config, resolve, reject) { + if (_qz.websocket.shutdown) { + reject(new Error("Connection attempt cancelled by user")); + return; + } + + //force flag if missing ports + if (!config.port.secure.length) { + if (!config.port.insecure.length) { + reject(new Error("No ports have been specified to connect over")); + return; + } else if (config.usingSecure) { + _qz.log.error("No secure ports specified - forcing insecure connection"); + config.usingSecure = false; + } + } else if (!config.port.insecure.length && !config.usingSecure) { + _qz.log.trace("No insecure ports specified - forcing secure connection"); + config.usingSecure = true; + } + + var deeper = function() { + if (_qz.websocket.shutdown) { + //connection attempt was cancelled, bail out + reject(new Error("Connection attempt cancelled by user")); + return; + } + + config.port.portIndex++; + + if ((config.usingSecure && config.port.portIndex >= config.port.secure.length) + || (!config.usingSecure && config.port.portIndex >= config.port.insecure.length)) { + if (config.hostIndex >= config.host.length - 1) { + //give up, all hope is lost + reject(new Error("Unable to establish connection with QZ")); + return; + } else { + config.hostIndex++; + config.port.portIndex = 0; + } + } + + // recursive call until connection established or all ports are exhausted + _qz.websocket.setup.findConnection(config, resolve, reject); + }; + + var address; + if (config.usingSecure) { + address = config.protocol.secure + config.host[config.hostIndex] + ":" + config.port.secure[config.port.portIndex]; + } else { + address = config.protocol.insecure + config.host[config.hostIndex] + ":" + config.port.insecure[config.port.portIndex]; + } + + try { + _qz.log.trace("Attempting connection", address); + _qz.websocket.connection = new _qz.tools.ws(address); + } + catch(err) { + _qz.log.error(err); + deeper(); + return; + } + + if (_qz.websocket.connection != null) { + _qz.websocket.connection.established = false; + + //called on successful connection to qz, begins setup of websocket calls and resolves connect promise after certificate is sent + _qz.websocket.connection.onopen = function(evt) { + if (!_qz.websocket.connection.established) { + _qz.log.trace(evt); + _qz.log.info("Established connection with QZ Tray on " + address); + + _qz.websocket.setup.openConnection({ resolve: resolve, reject: reject }); + + if (config.keepAlive > 0) { + var interval = setInterval(function() { + if (!_qz.tools.isActive() || _qz.websocket.connection.interval !== interval) { + clearInterval(interval); + return; + } + + _qz.websocket.connection.send("ping"); + }, config.keepAlive * 1000); + + _qz.websocket.connection.interval = interval; + } + } + }; + + //called during websocket close during setup + _qz.websocket.connection.onclose = function() { + // Safari compatibility fix to raise error event + if (_qz.websocket.connection && typeof navigator !== 'undefined' && navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) { + _qz.websocket.connection.onerror(); + } + }; + + //called for errors during setup (such as invalid ports), reject connect promise only if all ports have been tried + _qz.websocket.connection.onerror = function(evt) { + _qz.log.trace(evt); + + _qz.websocket.connection = null; + + deeper(); + }; + } else { + reject(new Error("Unable to create a websocket connection")); + } + }, + + /** Finish setting calls on successful connection, sets web socket calls that won't settle the promise. */ + openConnection: function(openPromise) { + _qz.websocket.connection.established = true; + + //called when an open connection is closed + _qz.websocket.connection.onclose = function(evt) { + _qz.log.trace(evt); + + _qz.websocket.connection = null; + _qz.websocket.callClose(evt); + _qz.log.info("Closed connection with QZ Tray"); + + for(var uid in _qz.websocket.pendingCalls) { + if (_qz.websocket.pendingCalls.hasOwnProperty(uid)) { + _qz.websocket.pendingCalls[uid].reject(new Error("Connection closed before response received")); + } + } + + //if this is set, then an explicit close call was made + if (this.promise != undefined) { + this.promise.resolve(); + } + }; + + //called for any errors with an open connection + _qz.websocket.connection.onerror = function(evt) { + _qz.websocket.callError(evt); + }; + + //send JSON objects to qz + _qz.websocket.connection.sendData = function(obj) { + _qz.log.trace("Preparing object for websocket", obj); + + if (obj.timestamp == undefined) { + obj.timestamp = Date.now(); + if (typeof obj.timestamp !== 'number') { + obj.timestamp = new Date().getTime(); + } + } + if (obj.promise != undefined) { + obj.uid = _qz.websocket.setup.newUID(); + _qz.websocket.pendingCalls[obj.uid] = obj.promise; + } + + // track requesting monitor + obj.position = { + x: typeof screen !== 'undefined' ? ((screen.availWidth || screen.width) / 2) + (screen.left || screen.availLeft || 0) : 0, + y: typeof screen !== 'undefined' ? ((screen.availHeight || screen.height) / 2) + (screen.top || screen.availTop || 0) : 0 + }; + + try { + if (obj.call != undefined && obj.signature == undefined && _qz.security.needsSigned(obj.call)) { + var signObj = { + call: obj.call, + params: obj.params, + timestamp: obj.timestamp + }; + + //make a hashing promise if not already one + var hashing = _qz.tools.hash(_qz.tools.stringify(signObj)); + if (!hashing.then) { + hashing = _qz.tools.promise(function(resolve) { + resolve(hashing); + }); + } + + hashing.then(function(hashed) { + return _qz.security.callSign(hashed); + }).then(function(signature) { + _qz.log.trace("Signature for call", signature); + obj.signature = signature || ""; + obj.signAlgorithm = _qz.security.signAlgorithm; + + _qz.signContent = undefined; + _qz.websocket.connection.send(_qz.tools.stringify(obj)); + }).catch(function(err) { + _qz.log.error("Signing failed", err); + + if (obj.promise != undefined) { + obj.promise.reject(new Error("Failed to sign request")); + delete _qz.websocket.pendingCalls[obj.uid]; + } + }); + } else { + _qz.log.trace("Signature for call", obj.signature); + + //called for pre-signed content and (unsigned) setup calls + _qz.websocket.connection.send(_qz.tools.stringify(obj)); + } + } + catch(err) { + _qz.log.error(err); + + if (obj.promise != undefined) { + obj.promise.reject(err); + delete _qz.websocket.pendingCalls[obj.uid]; + } + } + }; + + //receive message from qz + _qz.websocket.connection.onmessage = function(evt) { + var returned = JSON.parse(evt.data); + + if (returned.uid == null) { + if (returned.type == null) { + //incorrect response format, likely connected to incompatible qz version + _qz.websocket.connection.close(4003, "Connected to incompatible QZ Tray version"); + + } else { + //streams (callbacks only, no promises) + switch(returned.type) { + case _qz.streams.serial: + if (!returned.event) { + returned.event = JSON.stringify({ portName: returned.key, output: returned.data }); + } + + _qz.serial.callSerial(JSON.parse(returned.event)); + break; + case _qz.streams.socket: + _qz.socket.callSocket(JSON.parse(returned.event)); + break; + case _qz.streams.usb: + if (!returned.event) { + returned.event = JSON.stringify({ vendorId: returned.key[0], productId: returned.key[1], output: returned.data }); + } + + _qz.usb.callUsb(JSON.parse(returned.event)); + break; + case _qz.streams.hid: + _qz.hid.callHid(JSON.parse(returned.event)); + break; + case _qz.streams.printer: + _qz.printers.callPrinter(JSON.parse(returned.event)); + break; + case _qz.streams.file: + _qz.file.callFile(JSON.parse(returned.event)); + break; + default: + _qz.log.allay("Cannot determine stream type for callback", returned); + break; + } + } + + return; + } + + _qz.log.trace("Received response from websocket", returned); + + var promise = _qz.websocket.pendingCalls[returned.uid]; + if (promise == undefined) { + _qz.log.allay('No promise found for returned response'); + } else { + if (returned.error != undefined) { + promise.reject(new Error(returned.error)); + } else { + promise.resolve(returned.result); + } + } + + delete _qz.websocket.pendingCalls[returned.uid]; + }; + + + //send up the certificate before making any calls + //also gives the user a chance to deny the connection + function sendCert(cert) { + if (cert === undefined) { cert = null; } + + //websocket setup, query what version is connected + qz.api.getVersion().then(function(version) { + _qz.websocket.connection.version = version; + _qz.websocket.connection.semver = version.toLowerCase().replace(/-rc\./g, "-rc").split(/[\\+\\.-]/g); + for(var i = 0; i < _qz.websocket.connection.semver.length; i++) { + try { + if (i == 3 && _qz.websocket.connection.semver[i].toLowerCase().indexOf("rc") == 0) { + // Handle "rc1" pre-release by negating build info + _qz.websocket.connection.semver[i] = -(_qz.websocket.connection.semver[i].replace(/\D/g, "")); + continue; + } + _qz.websocket.connection.semver[i] = parseInt(_qz.websocket.connection.semver[i]); + } + catch(ignore) {} + + if (_qz.websocket.connection.semver.length < 4) { + _qz.websocket.connection.semver[3] = 0; + } + } + + //algorithm can be declared before a connection, check for incompatibilities now that we have one + _qz.compatible.algorithm(true); + }).then(function() { + _qz.websocket.connection.sendData({ certificate: cert, promise: openPromise }); + }); + } + + _qz.security.callCert().then(sendCert).catch(function(error) { + _qz.log.warn("Failed to get certificate:", error); + + if (_qz.security.rejectOnCertFailure) { + openPromise.reject(error); + } else { + sendCert(null); + } + }); + }, + + /** Generate unique ID used to map a response to a call. */ + newUID: function() { + var len = 6; + return (new Array(len + 1).join("0") + (Math.random() * Math.pow(36, len) << 0).toString(36)).slice(-len) + } + }, + + dataPromise: function(callName, params, signature, signingTimestamp) { + return _qz.tools.promise(function(resolve, reject) { + var msg = { + call: callName, + promise: { resolve: resolve, reject: reject }, + params: params, + signature: signature, + timestamp: signingTimestamp + }; + + _qz.websocket.connection.sendData(msg); + }); + }, + + /** Library of promises awaiting a response, uid -> promise */ + pendingCalls: {}, + + /** List of functions to call on error from the websocket. */ + errorCallbacks: [], + /** Calls all functions registered to listen for errors. */ + callError: function(evt) { + if (Array.isArray(_qz.websocket.errorCallbacks)) { + for(var i = 0; i < _qz.websocket.errorCallbacks.length; i++) { + _qz.websocket.errorCallbacks[i](evt); + } + } else { + _qz.websocket.errorCallbacks(evt); + } + }, + + /** List of function to call on closing from the websocket. */ + closedCallbacks: [], + /** Calls all functions registered to listen for closing. */ + callClose: function(evt) { + if (Array.isArray(_qz.websocket.closedCallbacks)) { + for(var i = 0; i < _qz.websocket.closedCallbacks.length; i++) { + _qz.websocket.closedCallbacks[i](evt); + } + } else { + _qz.websocket.closedCallbacks(evt); + } + } + }, + + + printing: { + /** Default options used for new printer configs. Can be overridden using {@link qz.configs.setDefaults}. */ + defaultConfig: { + //value purposes are explained in the qz.configs.setDefaults docs + + bounds: null, + colorType: 'color', + copies: 1, + density: 0, + duplex: false, + fallbackDensity: null, + interpolation: 'bicubic', + jobName: null, + legacy: false, + margins: 0, + orientation: null, + paperThickness: null, + printerTray: null, + rasterize: false, + rotation: 0, + scaleContent: true, + size: null, + units: 'in', + + forceRaw: false, + encoding: null, + spool: null + } + }, + + + serial: { + /** List of functions called when receiving data from serial connection. */ + serialCallbacks: [], + /** Calls all functions registered to listen for serial events. */ + callSerial: function(streamEvent) { + if (Array.isArray(_qz.serial.serialCallbacks)) { + for(var i = 0; i < _qz.serial.serialCallbacks.length; i++) { + _qz.serial.serialCallbacks[i](streamEvent); + } + } else { + _qz.serial.serialCallbacks(streamEvent); + } + } + }, + + + socket: { + /** List of functions called when receiving data from network socket connection. */ + socketCallbacks: [], + /** Calls all functions registered to listen for network socket events. */ + callSocket: function(socketEvent) { + if (Array.isArray(_qz.socket.socketCallbacks)) { + for(var i = 0; i < _qz.socket.socketCallbacks.length; i++) { + _qz.socket.socketCallbacks[i](socketEvent); + } + } else { + _qz.socket.socketCallbacks(socketEvent); + } + } + }, + + + usb: { + /** List of functions called when receiving data from usb connection. */ + usbCallbacks: [], + /** Calls all functions registered to listen for usb events. */ + callUsb: function(streamEvent) { + if (Array.isArray(_qz.usb.usbCallbacks)) { + for(var i = 0; i < _qz.usb.usbCallbacks.length; i++) { + _qz.usb.usbCallbacks[i](streamEvent); + } + } else { + _qz.usb.usbCallbacks(streamEvent); + } + } + }, + + + hid: { + /** List of functions called when receiving data from hid connection. */ + hidCallbacks: [], + /** Calls all functions registered to listen for hid events. */ + callHid: function(streamEvent) { + if (Array.isArray(_qz.hid.hidCallbacks)) { + for(var i = 0; i < _qz.hid.hidCallbacks.length; i++) { + _qz.hid.hidCallbacks[i](streamEvent); + } + } else { + _qz.hid.hidCallbacks(streamEvent); + } + } + }, + + + printers: { + /** List of functions called when receiving data from printer connection. */ + printerCallbacks: [], + /** Calls all functions registered to listen for printer events. */ + callPrinter: function(streamEvent) { + if (Array.isArray(_qz.printers.printerCallbacks)) { + for(var i = 0; i < _qz.printers.printerCallbacks.length; i++) { + _qz.printers.printerCallbacks[i](streamEvent); + } + } else { + _qz.printers.printerCallbacks(streamEvent); + } + } + }, + + + file: { + /** List of functions called when receiving info regarding file changes. */ + fileCallbacks: [], + /** Calls all functions registered to listen for file events. */ + callFile: function(streamEvent) { + if (Array.isArray(_qz.file.fileCallbacks)) { + for(var i = 0; i < _qz.file.fileCallbacks.length; i++) { + _qz.file.fileCallbacks[i](streamEvent); + } + } else { + _qz.file.fileCallbacks(streamEvent); + } + } + }, + + + security: { + /** Function used to resolve promise when acquiring site's public certificate. */ + certHandler: function(resolve, reject) { reject(); }, + /** Called to create new promise (using {@link _qz.security.certHandler}) for certificate retrieval. */ + callCert: function() { + if (typeof _qz.security.certHandler.then === 'function') { + //already a promise + return _qz.security.certHandler; + } else if (_qz.security.certHandler.constructor.name === "AsyncFunction") { + //already callable as a promise + return _qz.security.certHandler(); + } else { + //turn into a promise + return _qz.tools.promise(_qz.security.certHandler); + } + }, + + /** Function used to create promise resolver when requiring signed calls. */ + signatureFactory: function() { return function(resolve) { resolve(); } }, + /** Called to create new promise (using {@link _qz.security.signatureFactory}) for signed calls. */ + callSign: function(toSign) { + if (_qz.security.signatureFactory.constructor.name === "AsyncFunction") { + //use directly + return _qz.security.signatureFactory(toSign); + } else { + //use in a promise + return _qz.tools.promise(_qz.security.signatureFactory(toSign)); + } + }, + + /** Signing algorithm used on signatures */ + signAlgorithm: "SHA1", + + rejectOnCertFailure: false, + + needsSigned: function(callName) { + const undialoged = [ + "printers.getStatus", + "printers.stopListening", + "usb.isClaimed", + "usb.closeStream", + "usb.releaseDevice", + "hid.stopListening", + "hid.isClaimed", + "hid.closeStream", + "hid.releaseDevice", + "file.stopListening", + "getVersion" + ]; + + return callName != null && undialoged.indexOf(callName) === -1; + } + }, + + + tools: { + /** Create a new promise */ + promise: function(resolver) { + //prefer global object for historical purposes + if (typeof RSVP !== 'undefined') { + return new RSVP.Promise(resolver); + } else if (typeof Promise !== 'undefined') { + return new Promise(resolver); + } else { + _qz.log.error("Promise/A+ support is required. See qz.api.setPromiseType(...)"); + } + }, + + /** Stub for rejecting with an Error from withing a Promise */ + reject: function(error) { + return _qz.tools.promise(function(resolve, reject) { + reject(error); + }); + }, + + stringify: function(object) { + //old versions of prototype affect stringify + var pjson = Array.prototype.toJSON; + delete Array.prototype.toJSON; + + function skipKeys(key, value) { + if (key === "promise") { + return undefined; + } + + return value; + } + + var result = JSON.stringify(object, skipKeys); + + if (pjson) { + Array.prototype.toJSON = pjson; + } + + return result; + }, + + hash: function(data) { + //prefer global object for historical purposes + if (typeof Sha256 !== 'undefined') { + return Sha256.hash(data); + } else { + return _qz.SHA.hash(data); + } + }, + + ws: typeof WebSocket !== 'undefined' ? WebSocket : null, + + absolute: function(loc) { + if (typeof window !== 'undefined' && typeof document.createElement === 'function') { + var a = document.createElement("a"); + a.href = loc; + return a.href; + } else if (typeof exports === 'object') { + //node.js + require('path').resolve(loc); + } + return loc; + }, + + relative: function(data) { + for(var i = 0; i < data.length; i++) { + if (data[i].constructor === Object) { + var absolute = false; + + if (data[i].data && data[i].data.search && data[i].data.search(/data:image\/\w+;base64,/) === 0) { + //upgrade from old base64 behavior + data[i].flavor = "base64"; + data[i].data = data[i].data.replace(/^data:image\/\w+;base64,/, ""); + } else if (data[i].flavor) { + //if flavor is known, we can directly check for absolute flavor types + if (["FILE", "XML"].indexOf(data[i].flavor.toUpperCase()) > -1) { + absolute = true; + } + } else if (data[i].format && ["HTML", "IMAGE", "PDF", "FILE", "XML"].indexOf(data[i].format.toUpperCase()) > -1) { + //if flavor is not known, all valid pixel formats default to file flavor + //previous v2.0 data also used format as what is now flavor, so we check for those values here too + absolute = true; + } else if (data[i].type && ((["PIXEL", "IMAGE", "PDF"].indexOf(data[i].type.toUpperCase()) > -1 && !data[i].format) + || (["HTML", "PDF"].indexOf(data[i].type.toUpperCase()) > -1 && (!data[i].format || data[i].format.toUpperCase() === "FILE")))) { + //if all we know is pixel type, then it is image's file flavor + //previous v2.0 data also used type as what is now format, so we check for those value here too + absolute = true; + } + + if (absolute) { + //change relative links to absolute + data[i].data = _qz.tools.absolute(data[i].data); + } + if (data[i].options && typeof data[i].options.overlay === 'string') { + data[i].options.overlay = _qz.tools.absolute(data[i].options.overlay); + } + } + } + }, + + /** Performs deep copy to target from remaining params */ + extend: function(target) { + //special case when reassigning properties as objects in a deep copy + if (typeof target !== 'object') { + target = {}; + } + + for(var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + if (!source) { continue; } + + for(var key in source) { + if (source.hasOwnProperty(key)) { + if (target === source[key]) { continue; } + + if (source[key] && source[key].constructor && source[key].constructor === Object) { + var clone; + if (Array.isArray(source[key])) { + clone = target[key] || []; + } else { + clone = target[key] || {}; + } + + target[key] = _qz.tools.extend(clone, source[key]); + } else if (source[key] !== undefined) { + target[key] = source[key]; + } + } + } + } + + return target; + }, + + versionCompare: function(major, minor, patch, build) { + if (_qz.tools.assertActive()) { + var semver = _qz.websocket.connection.semver; + if(Array.isArray(semver)) { + if (major != undefined && semver.length > 0 && semver[0] != major) { + return semver[0] - major; + } + if (minor != undefined && semver.length > 1 && semver[1] != minor) { + return semver[1] - minor; + } + if (patch != undefined && semver.length > 2 && semver[2] != patch) { + return semver[2] - patch; + } + if (build != undefined && semver.length > 3 && semver[3] != build) { + return Number.isInteger(semver[3]) && Number.isInteger(build) ? semver[3] - build : semver[3].toString().localeCompare(build.toString()); + } + } + return 0; + } + }, + + isVersion: function(major, minor, patch, build) { + return _qz.tools.versionCompare(major, minor, patch, build) == 0; + }, + + isActive: function() { + return !_qz.websocket.shutdown && _qz.websocket.connection != null + && (_qz.websocket.connection.readyState === _qz.tools.ws.OPEN + || _qz.websocket.connection.readyState === _qz.tools.ws.CONNECTING); + }, + + assertActive: function() { + if (_qz.tools.isActive()) { + return true; + } + // Promise won't reject on throw; yet better than 'undefined' + throw new Error("A connection to QZ has not been established yet"); + }, + + uint8ArrayToHex: function(uint8) { + return Array.from(uint8) + .map(function(i) { return i.toString(16).padStart(2, '0'); }) + .join(''); + }, + + uint8ArrayToBase64: function(uint8) { + /** + * Adapted from Egor Nepomnyaschih's code under MIT Licence (C) 2020 + * see https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727 + */ + var map = [ + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", + "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", + "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/" + ]; + + var result = '', i, l = uint8.length; + for (i = 2; i < l; i += 3) { + result += map[uint8[i - 2] >> 2]; + result += map[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)]; + result += map[((uint8[i - 1] & 0x0F) << 2) | (uint8[i] >> 6)]; + result += map[uint8[i] & 0x3F]; + } + if (i === l + 1) { // 1 octet yet to write + result += map[uint8[i - 2] >> 2]; + result += map[(uint8[i - 2] & 0x03) << 4]; + result += "=="; + } + if (i === l) { // 2 octets yet to write + result += map[uint8[i - 2] >> 2]; + result += map[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)]; + result += map[(uint8[i - 1] & 0x0F) << 2]; + result += "="; + } + return result; + }, + }, + + compatible: { + /** Converts message format to a previous version's */ + data: function(printData) { + // special handling for Uint8Array + for(var i = 0; i < printData.length; i++) { + if (printData[i].constructor === Object && printData[i].data instanceof Uint8Array) { + if (printData[i].flavor) { + var flavor = printData[i].flavor.toString().toUpperCase(); + switch(flavor) { + case 'BASE64': + printData[i].data = _qz.tools.uint8ArrayToBase64(printData[i].data); + break; + case 'HEX': + printData[i].data = _qz.tools.uint8ArrayToHex(printData[i].data); + break; + default: + throw new Error("Uint8Array conversion to '" + flavor + "' is not supported."); + } + } + } + } + + if(_qz.tools.versionCompare(2, 2, 4) < 0) { + for(var i = 0; i < printData.length; i++) { + if (printData[i].constructor === Object) { + // dotDensity: "double-legacy|single-legacy" since 2.2.4. Fallback to "double|single" + if (printData[i].options && typeof printData[i].options.dotDensity === 'string') { + printData[i].options.dotDensity = printData[i].options.dotDensity.toLowerCase().replace("-legacy", ""); + } + } + } + } + + if (_qz.tools.isVersion(2, 0)) { + /* + 2.0.x conversion + ----- + type=pixel -> use format as 2.0 type (unless 'command' format, which forces 2.0 'raw' type) + type=raw -> 2.0 type has to be 'raw' + if format is 'image' -> force 2.0 'image' format, ignore everything else (unsupported in 2.0) + + flavor translates straight to 2.0 format (unless forced to 'raw'/'image') + */ + _qz.log.trace("Converting print data to v2.0 for " + _qz.websocket.connection.version); + for(var i = 0; i < printData.length; i++) { + if (printData[i].constructor === Object) { + if (printData[i].type && printData[i].type.toUpperCase() === "RAW" && printData[i].format && printData[i].format.toUpperCase() === "IMAGE") { + if (printData[i].flavor && printData[i].flavor.toUpperCase() === "BASE64") { + //special case for raw base64 images + printData[i].data = "data:image/compat;base64," + printData[i].data; + } + printData[i].flavor = "IMAGE"; //forces 'image' format when shifting for conversion + } + if ((printData[i].type && printData[i].type.toUpperCase() === "RAW") || (printData[i].format && printData[i].format.toUpperCase() === "COMMAND")) { + printData[i].format = "RAW"; //forces 'raw' type when shifting for conversion + } + + printData[i].type = printData[i].format; + printData[i].format = printData[i].flavor; + delete printData[i].flavor; + } + } + } + }, + + /* Converts config defaults to match previous version */ + config: function(config, dirty) { + if (_qz.tools.isVersion(2, 0)) { + if (!dirty.rasterize) { + config.rasterize = true; + } + } + if(_qz.tools.versionCompare(2, 2) < 0) { + if(config.forceRaw !== 'undefined') { + config.altPrinting = config.forceRaw; + delete config.forceRaw; + } + } + if(_qz.tools.versionCompare(2, 1, 2, 11) < 0) { + if(config.spool) { + if(config.spool.size) { + config.perSpool = config.spool.size; + delete config.spool.size; + } + if(config.spool.end) { + config.endOfDoc = config.spool.end; + delete config.spool.end; + } + delete config.spool; + } + } + return config; + }, + + /** Compat wrapper with previous version **/ + networking: function(hostname, port, signature, signingTimestamp, mappingCallback) { + // Use 2.0 + if (_qz.tools.isVersion(2, 0)) { + return _qz.tools.promise(function(resolve, reject) { + _qz.websocket.dataPromise('websocket.getNetworkInfo', { + hostname: hostname, + port: port + }, signature, signingTimestamp).then(function(data) { + if (typeof mappingCallback !== 'undefined') { + resolve(mappingCallback(data)); + } else { + resolve(data); + } + }, reject); + }); + } + // Wrap 2.1 + return _qz.tools.promise(function(resolve, reject) { + _qz.websocket.dataPromise('networking.device', { + hostname: hostname, + port: port + }, signature, signingTimestamp).then(function(data) { + resolve({ ipAddress: data.ip, macAddress: data.mac }); + }, reject); + }); + }, + + /** Check if QZ version supports chosen algorithm */ + algorithm: function(quiet) { + //if not connected yet we will assume compatibility exists for the time being + //check semver to guard race condition for pending connections + if (_qz.tools.isActive() && _qz.websocket.connection.semver) { + if (_qz.tools.isVersion(2, 0)) { + if (!quiet) { + _qz.log.warn("Connected to an older version of QZ, alternate signature algorithms are not supported"); + } + return false; + } + } + + return true; + } + }, + + /** + * Adapted from Chris Veness's code under MIT Licence (C) 2002 + * see http://www.movable-type.co.uk/scripts/sha256.html + */ + SHA: { + //@formatter:off - keep this block compact + hash: function(msg) { + // add trailing '1' bit (+ 0's padding) to string [§5.1.1] + msg = _qz.SHA._utf8Encode(msg) + String.fromCharCode(0x80); + + // constants [§4.2.2] + var K = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ]; + // initial hash value [§5.3.1] + var H = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ]; + + // convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1] + var l = msg.length / 4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length + var N = Math.ceil(l / 16); // number of 16-integer-blocks required to hold 'l' ints + var M = new Array(N); + + for(var i = 0; i < N; i++) { + M[i] = new Array(16); + for(var j = 0; j < 16; j++) { // encode 4 chars per integer, big-endian encoding + M[i][j] = (msg.charCodeAt(i * 64 + j * 4) << 24) | (msg.charCodeAt(i * 64 + j * 4 + 1) << 16) | + (msg.charCodeAt(i * 64 + j * 4 + 2) << 8) | (msg.charCodeAt(i * 64 + j * 4 + 3)); + } // note running off the end of msg is ok 'cos bitwise ops on NaN return 0 + } + // add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1] + // note: most significant word would be (len-1)*8 >>> 32, but since JS converts + // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators + M[N-1][14] = ((msg.length - 1) * 8) / Math.pow(2, 32); + M[N-1][14] = Math.floor(M[N-1][14]); + M[N-1][15] = ((msg.length - 1) * 8) & 0xffffffff; + + // HASH COMPUTATION [§6.1.2] + var W = new Array(64); var a, b, c, d, e, f, g, h; + for(var i = 0; i < N; i++) { + // 1 - prepare message schedule 'W' + for(var t = 0; t < 16; t++) { W[t] = M[i][t]; } + for(var t = 16; t < 64; t++) { W[t] = (_qz.SHA._dev1(W[t-2]) + W[t-7] + _qz.SHA._dev0(W[t-15]) + W[t-16]) & 0xffffffff; } + // 2 - initialise working variables a, b, c, d, e, f, g, h with previous hash value + a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7]; + // 3 - main loop (note 'addition modulo 2^32') + for(var t = 0; t < 64; t++) { + var T1 = h + _qz.SHA._sig1(e) + _qz.SHA._ch(e, f, g) + K[t] + W[t]; + var T2 = _qz.SHA._sig0(a) + _qz.SHA._maj(a, b, c); + h = g; g = f; f = e; e = (d + T1) & 0xffffffff; + d = c; c = b; b = a; a = (T1 + T2) & 0xffffffff; + } + // 4 - compute the new intermediate hash value (note 'addition modulo 2^32') + H[0] = (H[0]+a) & 0xffffffff; H[1] = (H[1]+b) & 0xffffffff; H[2] = (H[2]+c) & 0xffffffff; H[3] = (H[3]+d) & 0xffffffff; + H[4] = (H[4]+e) & 0xffffffff; H[5] = (H[5]+f) & 0xffffffff; H[6] = (H[6]+g) & 0xffffffff; H[7] = (H[7]+h) & 0xffffffff; + } + + return _qz.SHA._hexStr(H[0]) + _qz.SHA._hexStr(H[1]) + _qz.SHA._hexStr(H[2]) + _qz.SHA._hexStr(H[3]) + + _qz.SHA._hexStr(H[4]) + _qz.SHA._hexStr(H[5]) + _qz.SHA._hexStr(H[6]) + _qz.SHA._hexStr(H[7]); + }, + + // Rotates right (circular right shift) value x by n positions + _rotr: function(n, x) { return (x >>> n) | (x << (32 - n)); }, + // logical functions + _sig0: function(x) { return _qz.SHA._rotr(2, x) ^ _qz.SHA._rotr(13, x) ^ _qz.SHA._rotr(22, x); }, + _sig1: function(x) { return _qz.SHA._rotr(6, x) ^ _qz.SHA._rotr(11, x) ^ _qz.SHA._rotr(25, x); }, + _dev0: function(x) { return _qz.SHA._rotr(7, x) ^ _qz.SHA._rotr(18, x) ^ (x >>> 3); }, + _dev1: function(x) { return _qz.SHA._rotr(17, x) ^ _qz.SHA._rotr(19, x) ^ (x >>> 10); }, + _ch: function(x, y, z) { return (x & y) ^ (~x & z); }, + _maj: function(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); }, + // note can't use toString(16) as it is implementation-dependant, and in IE returns signed numbers when used on full words + _hexStr: function(n) { var s = "", v; for(var i = 7; i >= 0; i--) { v = (n >>> (i * 4)) & 0xf; s += v.toString(16); } return s; }, + // implementation of deprecated unescape() based on https://cwestblog.com/2011/05/23/escape-unescape-deprecated/ (and comments) + _unescape: function(str) { + return str.replace(/%(u[\da-f]{4}|[\da-f]{2})/gi, function(seq) { + if (seq.length - 1) { + return String.fromCharCode(parseInt(seq.substring(seq.length - 3 ? 2 : 1), 16)) + } else { + var code = seq.charCodeAt(0); + return code < 256 ? "%" + (0 + code.toString(16)).slice(-2).toUpperCase() : "%u" + ("000" + code.toString(16)).slice(-4).toUpperCase() + } + }); + }, + _utf8Encode: function(str) { + return _qz.SHA._unescape(encodeURIComponent(str)); + } + //@formatter:on + }, + }; + + +///// CONFIG CLASS //// + + /** Object to handle configured printer options. */ + function Config(printer, opts) { + + this.config = _qz.tools.extend({}, _qz.printing.defaultConfig); //create a copy of the default options + this._dirtyOpts = {}; //track which config options have changed from the defaults + + /** + * Set the printer assigned to this config. + * @param {string|Object} newPrinter Name of printer. Use object type to specify printing to file or host. + * @param {string} [newPrinter.name] Name of printer to send printing. + * @param {string} [newPrinter.file] DEPRECATED: Name of file to send printing. + * @param {string} [newPrinter.host] IP address or host name to send printing. + * @param {string} [newPrinter.port] Port used by <printer.host>. + */ + this.setPrinter = function(newPrinter) { + if (typeof newPrinter === 'string') { + newPrinter = { name: newPrinter }; + } + this.printer = newPrinter; + }; + + /** + * @returns {Object} The printer currently assigned to this config. + */ + this.getPrinter = function() { + return this.printer; + }; + + /** + * Alter any of the printer options currently applied to this config. + * @param newOpts {Object} The options to change. See qz.configs.setDefaults docs for available values. + * + * @see qz.configs.setDefaults + */ + this.reconfigure = function(newOpts) { + for(var key in newOpts) { + if (newOpts[key] !== undefined) { + this._dirtyOpts[key] = true; + } + } + + _qz.tools.extend(this.config, newOpts); + }; + + /** + * @returns {Object} The currently applied options on this config. + */ + this.getOptions = function() { + return _qz.compatible.config(this.config, this._dirtyOpts); + }; + + // init calls for new config object + this.setPrinter(printer); + this.reconfigure(opts); + } + + /** + * Shortcut method for calling qz.print with a particular config. + * @param {Array} data Array of data being sent to the printer. See qz.print docs for available values. + * @param {boolean} [signature] Pre-signed signature of JSON string containing call, params, and timestamp. + * @param {number} [signingTimestamp] Required with signature. Timestamp used with pre-signed content. + * + * @example + * qz.print(myConfig, ...); // OR + * myConfig.print(...); + * + * @see qz.print + */ + Config.prototype.print = function(data, signature, signingTimestamp) { + qz.print(this, data, signature, signingTimestamp); + }; + + +///// PUBLIC METHODS ///// + + /** @namespace qz */ + var qz = { + + /** + * Calls related specifically to the web socket connection. + * @namespace qz.websocket + */ + websocket: { + /** + * Check connection status. Active connection is necessary for other calls to run. + * + * @returns {boolean} If there is an active connection with QZ Tray. + * + * @see connect + * + * @memberof qz.websocket + */ + isActive: function() { + return _qz.tools.isActive(); + }, + + /** + * Call to setup connection with QZ Tray on user's system. + * + * @param {Object} [options] Configuration options for the web socket connection. + * @param {string|Array} [options.host=['localhost', 'localhost.qz.io']] Host running the QZ Tray software. + * @param {Object} [options.port] Config options for ports to cycle. + * @param {Array} [options.port.secure=[8181, 8282, 8383, 8484]] Array of secure (WSS) ports to try + * @param {Array} [options.port.insecure=[8182, 8283, 8384, 8485]] Array of insecure (WS) ports to try + * @param {boolean} [options.usingSecure=true] If the web socket should try to use secure ports for connecting. + * @param {number} [options.keepAlive=60] Seconds between keep-alive pings to keep connection open. Set to 0 to disable. + * @param {number} [options.retries=0] Number of times to reconnect before failing. + * @param {number} [options.delay=0] Seconds before firing a connection. Ignored if options.retries is 0. + * + * @returns {Promise} + * + * @memberof qz.websocket + */ + connect: function(options) { + return _qz.tools.promise(function(resolve, reject) { + if (_qz.websocket.connection) { + const state = _qz.websocket.connection.readyState; + + if (state === _qz.tools.ws.OPEN) { + reject(new Error("An open connection with QZ Tray already exists")); + return; + } else if (state === _qz.tools.ws.CONNECTING) { + reject(new Error("The current connection attempt has not returned yet")); + return; + } else if (state === _qz.tools.ws.CLOSING) { + reject(new Error("Waiting for previous disconnect request to complete")); + return; + } + } + + if (!_qz.tools.ws) { + reject(new Error("WebSocket not supported by this browser")); + return; + } else if (!_qz.tools.ws.CLOSED || _qz.tools.ws.CLOSED == 2) { + reject(new Error("Unsupported WebSocket version detected: HyBi-00/Hixie-76")); + return; + } + + //ensure some form of options exists for value checks + if (options == undefined) { options = {}; } + + //disable secure ports if page is not secure + if (typeof location === 'undefined' || location.protocol !== 'https:') { + //respect forcing secure ports if it is defined, otherwise disable + if (typeof options.usingSecure === 'undefined') { + _qz.log.trace("Disabling secure ports due to insecure page"); + options.usingSecure = false; + } + } + + //ensure any hosts are passed to internals as an array + if (typeof options.host !== 'undefined' && !Array.isArray(options.host)) { + options.host = [options.host]; + } + + _qz.websocket.shutdown = false; //reset state for new connection attempt + var attempt = function(count) { + var tried = false; + var nextAttempt = function() { + if (!tried) { + tried = true; + + if (options && count < options.retries) { + attempt(count + 1); + } else { + _qz.websocket.connection = null; + reject.apply(null, arguments); + } + } + }; + + var delayed = function() { + var config = _qz.tools.extend({}, _qz.websocket.connectConfig, options); + _qz.websocket.setup.findConnection(config, resolve, nextAttempt) + }; + if (count == 0) { + delayed(); // only retries will be called with a delay + } else { + setTimeout(delayed, options.delay * 1000); + } + }; + + attempt(0); + }); + }, + + /** + * Stop any active connection with QZ Tray. + * + * @returns {Promise} + * + * @memberof qz.websocket + */ + disconnect: function() { + return _qz.tools.promise(function(resolve, reject) { + if (_qz.websocket.connection != null) { + if (_qz.tools.isActive()) { + // handles closing both 'connecting' and 'connected' states + _qz.websocket.shutdown = true; + _qz.websocket.connection.promise = { resolve: resolve, reject: reject }; + _qz.websocket.connection.close(); + } else { + reject(new Error("Current connection is still closing")); + } + } else { + reject(new Error("No open connection with QZ Tray")); + } + }); + }, + + /** + * List of functions called for any connections errors outside of an API call.

+ * Also called if {@link websocket#connect} fails to connect. + * + * @param {Function|Array} calls Single or array of Function({Event} event) calls. + * + * @memberof qz.websocket + */ + setErrorCallbacks: function(calls) { + _qz.websocket.errorCallbacks = calls; + }, + + /** + * List of functions called for any connection closing event outside of an API call.

+ * Also called when {@link websocket#disconnect} is called. + * + * @param {Function|Array} calls Single or array of Function({Event} event) calls. + * + * @memberof qz.websocket + */ + setClosedCallbacks: function(calls) { + _qz.websocket.closedCallbacks = calls; + }, + + /** + * @deprecated Since 2.1.0. Please use qz.networking.device() instead + * + * @param {string} [hostname] Hostname to try to connect to when determining network interfaces, defaults to "google.com" + * @param {number} [port] Port to use with custom hostname, defaults to 443 + * @param {string} [signature] Pre-signed signature of hashed JSON string containing call='websocket.getNetworkInfo', params object, and timestamp. + * @param {number} [signingTimestamp] Required with signature. Timestamp used with pre-signed content. + * + * @returns {Promise|Error>} Connected system's network information. + * + * @memberof qz.websocket + */ + getNetworkInfo: _qz.compatible.networking, + + /** + * @returns {Object<{socket: String, host: String, port: Number}>} Details of active websocket connection + * + * @memberof qz.websocket + */ + getConnectionInfo: function() { + if (_qz.tools.assertActive()) { + var url = _qz.websocket.connection.url.split(/[:\/]+/g); + return { socket: url[0], host: url[1], port: +url[2] }; + } + } + }, + + + /** + * Calls related to getting printer information from the connection. + * @namespace qz.printers + */ + printers: { + /** + * @param {string} [signature] Pre-signed signature of hashed JSON string containing call='printers.getDefault, params, and timestamp. + * @param {number} [signingTimestamp] Required with signature. Timestamp used with pre-signed content. + * + * @returns {Promise} Name of the connected system's default printer. + * + * @memberof qz.printers + */ + getDefault: function(signature, signingTimestamp) { + return _qz.websocket.dataPromise('printers.getDefault', null, signature, signingTimestamp); + }, + + /** + * @param {string} [query] Search for a specific printer. All printers are returned if not provided. + * @param {string} [signature] Pre-signed signature of hashed JSON string containing call='printers.find', params, and timestamp. + * @param {number} [signingTimestamp] Required with signature. Timestamp used with pre-signed content. + * + * @returns {Promise|string|Error>} The matched printer name if query is provided. + * Otherwise an array of printer names found on the connected system. + * + * @memberof qz.printers + */ + find: function(query, signature, signingTimestamp) { + return _qz.websocket.dataPromise('printers.find', { query: query }, signature, signingTimestamp); + }, + + /** + * Provides a list, with additional information, for each printer available to QZ. + * + * @returns {Promise|Object|Error>} + * + * @memberof qz.printers + */ + details: function() { + return _qz.websocket.dataPromise('printers.detail'); + }, + + /** + * Start listening for printer status events, such as paper_jam events. + * Reported under the ACTION type in the streamEvent on callbacks. + * + * @returns {Promise} + * @since 2.1.0 + * + * @see qz.printers.setPrinterCallbacks + * + * @param {null|string|Array} printers Printer or list of printers to listen to, null listens to all. + * @param {Object|null} [options] Printer listener options + * @param {null|boolean} [options.jobData=false] Flag indicating if raw spool file content should be return as well as status information (Windows only) + * @param {null|number} [options.maxJobData=-1] Maximum number of bytes to returns for raw spooled file content (Windows only) + * @param {null|string} [options.flavor="plain"] Flavor of data format returned. Valid flavors are [base64 | hex | plain*] (Windows only) + * + * @memberof qz.printers + */ + startListening: function(printers, options) { + if (!Array.isArray(printers)) { + printers = [printers]; + } + var params = { + printerNames: printers + }; + if (options && options.jobData == true) params.jobData = true; + if (options && options.maxJobData) params.maxJobData = options.maxJobData; + if (options && options.flavor) params.flavor = options.flavor; + return _qz.websocket.dataPromise('printers.startListening', params); + }, + + /** + * Clear the queue of a specified printer or printers. Does not delete retained jobs. + * + * @param {string|Object} [options] Name of printer to clear + * @param {string} [options.printerName] Name of printer to clear + * @param {number} [options.jobId] Cancel a job of a specific JobId instead of canceling all. Must include a printerName. + * + * @returns {Promise} + * @since 2.2.4 + * + * @memberof qz.printers + */ + clearQueue: function(options) { + if (typeof options !== 'object') { + options = { + printerName: options + }; + } + return _qz.websocket.dataPromise('printers.clearQueue', options); + }, + + /** + * Stop listening for printer status actions. + * + * @returns {Promise} + * @since 2.1.0 + * + * @see qz.printers.setPrinterCallbacks + * + * @memberof qz.printers + */ + stopListening: function() { + return _qz.websocket.dataPromise('printers.stopListening'); + }, + + /** + * Retrieve current printer status from any active listeners. + * + * @returns {Promise} + * @since 2.1.0 + * + * @see qz.printers.startListening + * + * @memberof qz.printers + */ + getStatus: function() { + return _qz.websocket.dataPromise('printers.getStatus'); + }, + + /** + * List of functions called for any printer status change. + * Event data will contain {string} printerName and {string} status for all types. + * For RECEIVE types, {Array} output (in hexadecimal format). + * For ERROR types, {string} exception. + * For ACTION types, {string} actionType. + * + * @param {Function|Array} calls Single or array of Function({Object} eventData) calls. + * @since 2.1.0 + * + * @memberof qz.printers + */ + setPrinterCallbacks: function(calls) { + _qz.printers.printerCallbacks = calls; + } + }, + + /** + * Calls related to setting up new printer configurations. + * @namespace qz.configs + */ + configs: { + /** + * Default options used by new configs if not overridden. + * Setting a value to NULL will use the printer's default options. + * Updating these will not update the options on any created config. + * + * @param {Object} options Default options used by printer configs if not overridden. + * + * @param {Object} [options.bounds=null] Bounding box rectangle. + * @param {number} [options.bounds.x=0] Distance from left for bounding box starting corner + * @param {number} [options.bounds.y=0] Distance from top for bounding box starting corner + * @param {number} [options.bounds.width=0] Width of bounding box + * @param {number} [options.bounds.height=0] Height of bounding box + * @param {string} [options.colorType='color'] Valid values [color | grayscale | blackwhite | default] + * @param {number} [options.copies=1] Number of copies to be printed. + * @param {number|Array|Object|Array|string} [options.density=0] Pixel density (DPI, DPMM, or DPCM depending on [options.units]). + * If provided as an array, uses the first supported density found (or the first entry if none found). + * If provided as a string, valid values are [best | draft], corresponding to highest or lowest reported density respectively. + * @param {number} [options.density.cross=0] Asymmetric pixel density for the cross feed direction. + * @param {number} [options.density.feed=0] Asymmetric pixel density for the feed direction. + * @param {boolean|string} [options.duplex=false] Double sided printing, Can specify duplex style by passing a string value: [one-sided | duplex | long-edge | tumble | short-edge] + * @param {number} [options.fallbackDensity=null] Value used when default density value cannot be read, or in cases where reported as "Normal" by the driver, (in DPI, DPMM, or DPCM depending on [options.units]). + * @param {string} [options.interpolation='bicubic'] Valid values [bicubic | bilinear | nearest-neighbor]. Controls how images are handled when resized. + * @param {string} [options.jobName=null] Name to display in print queue. + * @param {boolean} [options.legacy=false] If legacy style printing should be used. + * @param {Object|number} [options.margins=0] If just a number is provided, it is used as the margin for all sides. + * @param {number} [options.margins.top=0] + * @param {number} [options.margins.right=0] + * @param {number} [options.margins.bottom=0] + * @param {number} [options.margins.left=0] + * @param {string} [options.orientation=null] Valid values [portrait | landscape | reverse-landscape | null]. + * If set to null, orientation will be determined automatically. + * @param {number} [options.paperThickness=null] + * @param {string|number} [options.printerTray=null] Printer tray to pull from. The number N assumes string equivalent of 'Tray N'. Uses printer default if NULL. + * @param {boolean} [options.rasterize=false] Whether documents should be rasterized before printing. + * Specifying [options.density] for PDF print formats will set this to true. + * @param {number} [options.rotation=0] Image rotation in degrees. + * @param {boolean} [options.scaleContent=true] Scales print content to page size, keeping ratio. + * @param {Object} [options.size=null] Paper size. + * @param {number} [options.size.width=null] Page width. + * @param {number} [options.size.height=null] Page height. + * @param {string} [options.units='in'] Page units, applies to paper size, margins, and density. Valid value [in | cm | mm] + * + * @param {boolean} [options.forceRaw=false] Print the specified raw data using direct method, skipping the driver. Not yet supported on Windows. + * @param {string|Object} [options.encoding=null] Character set for commands. Can be provided as an object for converting encoding types for RAW types. + * @param {string} [options.encoding.from] If this encoding type is provided, RAW type commands will be parsed from this for the purpose of being converted to the encoding.to value. + * @param {string} [options.encoding.to] Encoding RAW type commands will be converted into. If encoding.from is not provided, this will be treated as if a string was passed for encoding. + * @param {string} [options.endOfDoc=null] DEPRECATED Raw only: Character(s) denoting end of a page to control spooling. + * @param {number} [options.perSpool=1] DEPRECATED: Raw only: Number of pages per spool. + * @param {boolean} [options.retainTemp=false] Retain any temporary files used. Ignored unless forceRaw true. + * @param {Object} [options.spool=null] Advanced spooling options. + * @param {number} [options.spool.size=null] Number of pages per spool. Default is no limit. If spool.end is provided, defaults to 1 + * @param {string} [options.spool.end=null] Raw only: Character(s) denoting end of a page to control spooling. + * + * @memberof qz.configs + */ + setDefaults: function(options) { + _qz.tools.extend(_qz.printing.defaultConfig, options); + }, + + /** + * Creates new printer config to be used in printing. + * + * @param {string|object} printer Name of printer. Use object type to specify printing to file or host. + * @param {string} [printer.name] Name of printer to send printing. + * @param {string} [printer.file] Name of file to send printing. + * @param {string} [printer.host] IP address or host name to send printing. + * @param {string} [printer.port] Port used by <printer.host>. + * @param {Object} [options] Override any of the default options for this config only. + * + * @returns {Config} The new config. + * + * @see configs.setDefaults + * + * @memberof qz.configs + */ + create: function(printer, options) { + return new Config(printer, options); + } + }, + + + /** + * Send data to selected config for printing. + * The promise for this method will resolve when the document has been sent to the printer. Actual printing may not be complete. + *

+ * Optionally, print requests can be pre-signed: + * Signed content consists of a JSON object string containing no spacing, + * following the format of the "call" and "params" keys in the API call, with the addition of a "timestamp" key in milliseconds + * ex. '{"call":"","params":{...},"timestamp":1450000000}' + * + * @param {Object|Array>} configs Previously created config object or objects. + * @param {Array|Array>} data Array of data being sent to the printer.
+ * String values are interpreted as {type: 'raw', format: 'command', flavor: 'plain', data: <string>}. + * @param {string} data.data + * @param {string} data.type Printing type. Valid types are [pixel | raw*]. *Default + * @param {string} data.format Format of data type used. *Default per type

+ * For [pixel] types, valid formats are [html | image* | pdf].

+ * For [raw] types, valid formats are [command* | html | image | pdf]. + * @param {string} data.flavor Flavor of data format used. *Default per format

+ * For [command] formats, valid flavors are [base64 | file | hex | plain* | xml].

+ * For [html] formats, valid flavors are [file* | plain].

+ * For [image] formats, valid flavors are [base64 | file*].

+ * For [pdf] formats, valid flavors are [base64 | file*]. + * @param {Object} [data.options] + * @param {string} [data.options.language] Required with [raw] type + [image] format. Printer language. + * @param {number} [data.options.x] Optional with [raw] type + [image] format. The X position of the image. + * @param {number} [data.options.y] Optional with [raw] type + [image] format. The Y position of the image. + * @param {string|number} [data.options.dotDensity] Optional with [raw] type + [image] format. + * @param {number} [data.precision=128] Optional with [raw] type [image] format. Bit precision of the ribbons. + * @param {boolean|string|Array>} [data.options.overlay=false] Optional with [raw] type [image] format. + * Boolean sets entire layer, string sets mask image, Array sets array of rectangles in format [x1,y1,x2,y2]. + * @param {string} [data.options.xmlTag] Required with [xml] flavor. Tag name containing base64 formatted data. + * @param {number} [data.options.pageWidth] Optional with [html | pdf] formats. Width of the rendering. + * Defaults to paper width. + * @param {number} [data.options.pageHeight] Optional with [html | pdf] formats. Height of the rendering. + * Defaults to paper height for [pdf], or auto sized for [html]. + * @param {string} [data.options.pageRanges] Optional with [pdf] formats. Comma-separated list of page ranges to include. + * @param {boolean} [data.options.ignoreTransparency=false] Optional with [pdf] formats. Instructs transparent PDF elements to be ignored. + * Transparent PDF elements are known to degrade performance and quality when printing. + * @param {boolean} [data.options.altFontRendering=false] Optional with [pdf] formats. Instructs PDF to be rendered using PDFBOX 1.8 techniques. + * Drastically improves low-DPI PDF print quality on Windows. + * @param {...*} [arguments] Additionally three more parameters can be specified:

+ * {boolean} [resumeOnError=false] Whether the chain should continue printing if it hits an error on one the the prints.

+ * {string|Array} [signature] Pre-signed signature(s) of the JSON string for containing call, params, and timestamp.

+ * {number|Array} [signingTimestamps] Required to match with signature. Timestamps for each of the passed pre-signed content. + * + * @returns {Promise} + * + * @see qz.configs.create + * + * @memberof qz + */ + print: function(configs, data) { + var resumeOnError = false, + signatures = [], + signaturesTimestamps = []; + + //find optional parameters + if (arguments.length >= 3) { + if (typeof arguments[2] === 'boolean') { + resumeOnError = arguments[2]; + + if (arguments.length >= 5) { + signatures = arguments[3]; + signaturesTimestamps = arguments[4]; + } + } else if (arguments.length >= 4) { + signatures = arguments[2]; + signaturesTimestamps = arguments[3]; + } + + //ensure values are arrays for consistency + if (signatures && !Array.isArray(signatures)) { signatures = [signatures]; } + if (signaturesTimestamps && !Array.isArray(signaturesTimestamps)) { signaturesTimestamps = [signaturesTimestamps]; } + } + + if (!Array.isArray(configs)) { configs = [configs]; } //single config -> array of configs + if (!Array.isArray(data[0])) { data = [data]; } //single data array -> array of data arrays + + //clean up data formatting + for(var d = 0; d < data.length; d++) { + _qz.tools.relative(data[d]); + _qz.compatible.data(data[d]); + } + + var sendToPrint = function(mapping) { + var params = { + printer: mapping.config.getPrinter(), + options: mapping.config.getOptions(), + data: mapping.data + }; + + return _qz.websocket.dataPromise('print', params, mapping.signature, mapping.timestamp); + }; + + //chain instead of Promise.all, so resumeOnError can collect each error + var chain = []; + for(var i = 0; i < configs.length || i < data.length; i++) { + (function(i_) { + var map = { + config: configs[Math.min(i_, configs.length - 1)], + data: data[Math.min(i_, data.length - 1)], + signature: signatures[i_], + timestamp: signaturesTimestamps[i_] + }; + + chain.push(function() { return sendToPrint(map) }); + })(i); + } + + //setup to catch errors if needed + var fallThrough = null; + if (resumeOnError) { + var fallen = []; + fallThrough = function(err) { fallen.push(err); }; + + //final promise to reject any errors as a group + chain.push(function() { + return _qz.tools.promise(function(resolve, reject) { + fallen.length ? reject(fallen) : resolve(); + }); + }); + } + + var last = null; + chain.reduce(function(sequence, link) { + last = sequence.catch(fallThrough).then(link); //catch is ignored if fallThrough is null + return last; + }, _qz.tools.promise(function(r) { r(); })); //an immediately resolved promise to start off the chain + + //return last promise so users can chain off final action or catch when stopping on error + return last; + }, + + + /** + * Calls related to interaction with serial ports. + * @namespace qz.serial + */ + serial: { + /** + * @returns {Promise|Error>} Communication (RS232, COM, TTY) ports available on connected system. + * + * @memberof qz.serial + */ + findPorts: function() { + return _qz.websocket.dataPromise('serial.findPorts'); + }, + + /** + * List of functions called for any response from open serial ports. + * Event data will contain {string} portName for all types. + * For RECEIVE types, {string} output. + * For ERROR types, {string} exception. + * + * @param {Function|Array} calls Single or array of Function({object} streamEvent) calls. + * + * @memberof qz.serial + */ + setSerialCallbacks: function(calls) { + _qz.serial.serialCallbacks = calls; + }, + + /** + * Opens a serial port for sending and receiving data + * + * @param {string} port Name of serial port to open. + * @param {Object} [options] Serial port configurations. + * @param {number} [options.baudRate=9600] Serial port speed. Set to 0 for auto negotiation. + * @param {number} [options.dataBits=8] Serial port data bits. Set to 0 for auto negotiation. + * @param {number} [options.stopBits=1] Serial port stop bits. Set to 0 for auto negotiation. + * @param {string} [options.parity='NONE'] Serial port parity. Set to AUTO for auto negotiation. Valid values [NONE | EVEN | ODD | MARK | SPACE | AUTO] + * @param {string} [options.flowControl='NONE'] Serial port flow control. Set to AUTO for auto negotiation. Valid values [NONE | XONXOFF | XONXOFF_OUT | XONXOFF_IN | RTSCTS | RTSCTS_OUT | RTSCTS_IN | AUTO] + * @param {string} [options.encoding='UTF-8'] Character set for communications. + * @param {string} [options.start=0x0002] DEPRECATED: Legacy character denoting start of serial response. Use options.rx.start instead. + * @param {string} [options.end=0x000D] DEPRECATED: Legacy character denoting end of serial response. Use options.rx.end instead. + * @param {number} [options.width] DEPRECATED: Legacy use for fixed-width response serial communication. Use options.rx.width instead. + * @param {Object} [options.rx] Serial communications response definitions. If an object is passed but no options are defined, all response data will be sent back as it is received unprocessed. + * @param {string|Array} [options.rx.start] Character(s) denoting start of response bytes. Used in conjunction with `end`, `width`, or `lengthbit` property. + * @param {string} [options.rx.end] Character denoting end of response bytes. Used in conjunction with `start` property. + * @param {number} [options.rx.width] Fixed width size of response bytes (not including header if `start` is set). Used alone or in conjunction with `start` property. + * @param {boolean} [options.rx.untilNewline] Returns data between newline characters (`\n` or `\r`) Truncates empty responses. Overrides `start`, `end`, `width`. + * @param {number|Object} [options.rx.lengthBytes] If a number is passed it is treated as the length index. Other values are left as their defaults. + * @param {number} [options.rx.lengthBytes.index=0] Position of the response byte (not including response `start` bytes) used to denote the length of the remaining response data. + * @param {number} [options.rx.lengthBytes.length=1] Length of response length bytes after response header. + * @param {string} [options.rx.lengthBytes.endian='BIG'] Byte endian for multi-byte length values. Valid values [BIG | LITTLE] + * @param {number|Object} [options.rx.crcBytes] If a number is passed it is treated as the crc length. Other values are left as their defaults. + * @param {number} [options.rx.crcBytes.index=0] Position after the response data (not including length or data bytes) used to denote the crc. + * @param {number} [options.rx.crcBytes.length=1] Length of response crc bytes after the response data length. + * @param {boolean} [options.rx.includeHeader=false] Whether any of the header bytes (`start` bytes and any length bytes) should be included in the processed response. + * @param {string} [options.rx.encoding] Override the encoding used for response data. Uses the same value as options.encoding otherwise. + * + * @returns {Promise} + * + * @memberof qz.serial + */ + openPort: function(port, options) { + var params = { + port: port, + options: options + }; + return _qz.websocket.dataPromise('serial.openPort', params); + }, + + /** + * Send commands over a serial port. + * Any responses from the device will be sent to serial callback functions. + * + * @param {string} port An open serial port to send data. + * @param {string|Array|Object} data Data to be sent to the serial device. + * @param {string} [data.type='PLAIN'] Valid values [FILE | PLAIN | HEX | BASE64] + * @param {string|Array} data.data Data to be sent to the serial device. + * @param {Object} options Serial port configuration updates. See qz.serial.openPort `options` docs for available values. + * For best performance, it is recommended to only set these values on the port open call. + * + * @returns {Promise} + * + * @see qz.serial.setSerialCallbacks + * + * @memberof qz.serial + */ + sendData: function(port, data, options) { + if (_qz.tools.versionCompare(2, 1, 0, 12) >= 0) { + if (typeof data !== 'object') { + data = { + data: data, + type: "PLAIN" + } + } + + if (data.type && data.type.toUpperCase() == "FILE") { + data.data = _qz.tools.absolute(data.data); + } + } + + var params = { + port: port, + data: data, + options: options + }; + return _qz.websocket.dataPromise('serial.sendData', params); + }, + + /** + * @param {string} port Name of port to close. + * + * @returns {Promise} + * + * @memberof qz.serial + */ + closePort: function(port) { + return _qz.websocket.dataPromise('serial.closePort', { port: port }); + } + }, + + /** + * Calls related to interaction with communication sockets. + * @namespace qz.socket + */ + socket: { + /** + * Opens a network port for sending and receiving data. + * + * @param {string} host The connection hostname. + * @param {number} port The connection port number. + * @param {Object} [options] Network socket configuration. + * @param {string} [options.encoding='UTF-8'] Character set for communications. + * + * @memberof qz.socket + */ + open: function(host, port, options) { + var params = { + host: host, + port: port, + options: options + }; + return _qz.websocket.dataPromise("socket.open", params); + }, + + /** + * @param {string} host The connection hostname. + * @param {number} port The connection port number. + * + * @memberof qz.socket + */ + close: function(host, port) { + var params = { + host: host, + port: port + }; + return _qz.websocket.dataPromise("socket.close", params); + }, + + /** + * Send data over an open socket. + * + * @param {string} host The connection hostname. + * @param {number} port The connection port number. + * @param {string|Object} data Data to be sent over the port. + * @param {string} [data.type='PLAIN'] Valid values [PLAIN] + * @param {string} data.data Data to be sent over the port. + * + * @memberof qz.socket + */ + sendData: function(host, port, data) { + if (typeof data !== 'object') { + data = { + data: data, + type: "PLAIN" + }; + } + + var params = { + host: host, + port: port, + data: data + }; + return _qz.websocket.dataPromise("socket.sendData", params); + }, + + /** + * List of functions called for any response from open network sockets. + * Event data will contain {string} host and {number} port for all types. + * For RECEIVE types, {string} response. + * For ERROR types, {string} exception. + * + * @param {Function|Array} calls Single or array of Function({Object} eventData) calls. + * + * @memberof qz.socket + */ + setSocketCallbacks: function(calls) { + _qz.socket.socketCallbacks = calls; + } + }, + + /** + * Calls related to interaction with USB devices. + * @namespace qz.usb + */ + usb: { + /** + * List of available USB devices. Includes (hexadecimal) vendor ID, (hexadecimal) product ID, and hub status. + * If supported, also returns manufacturer and product descriptions. + * + * @param includeHubs Whether to include USB hubs. + * @returns {Promise|Error>} Array of JSON objects containing information on connected USB devices. + * + * @memberof qz.usb + */ + listDevices: function(includeHubs) { + return _qz.websocket.dataPromise('usb.listDevices', { includeHubs: includeHubs }); + }, + + /** + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of USB device's vendor ID. + * @param deviceInfo.productId Hex string of USB device's product ID. + * @returns {Promise|Error>} List of available (hexadecimal) interfaces on a USB device. + * + * @memberof qz.usb + */ + listInterfaces: function(deviceInfo) { + if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility + + return _qz.websocket.dataPromise('usb.listInterfaces', deviceInfo); + }, + + /** + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of USB device's vendor ID. + * @param deviceInfo.productId Hex string of USB device's product ID. + * @param deviceInfo.iface Hex string of interface on the USB device to search. + * @returns {Promise|Error>} List of available (hexadecimal) endpoints on a USB device's interface. + * + * @memberof qz.usb + */ + listEndpoints: function(deviceInfo) { + //backwards compatibility + if (typeof deviceInfo !== 'object') { + deviceInfo = { + vendorId: arguments[0], + productId: arguments[1], + interface: arguments[2] + }; + } + + return _qz.websocket.dataPromise('usb.listEndpoints', deviceInfo); + }, + + /** + * List of functions called for any response from open usb devices. + * Event data will contain {string} vendorId and {string} productId for all types. + * For RECEIVE types, {Array} output (in hexadecimal format). + * For ERROR types, {string} exception. + * + * @param {Function|Array} calls Single or array of Function({Object} eventData) calls. + * + * @memberof qz.usb + */ + setUsbCallbacks: function(calls) { + _qz.usb.usbCallbacks = calls; + }, + + /** + * Claim a USB device's interface to enable sending/reading data across an endpoint. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of USB device's vendor ID. + * @param deviceInfo.productId Hex string of USB device's product ID. + * @param deviceInfo.interface Hex string of interface on the USB device to claim. + * @returns {Promise} + * + * @memberof qz.usb + */ + claimDevice: function(deviceInfo) { + //backwards compatibility + if (typeof deviceInfo !== 'object') { + deviceInfo = { + vendorId: arguments[0], + productId: arguments[1], + interface: arguments[2] + }; + } + + return _qz.websocket.dataPromise('usb.claimDevice', deviceInfo); + }, + + /** + * Check the current claim state of a USB device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of USB device's vendor ID. + * @param deviceInfo.productId Hex string of USB device's product ID. + * @returns {Promise} + * + * @since 2.0.2 + * @memberOf qz.usb + */ + isClaimed: function(deviceInfo) { + if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility + + return _qz.websocket.dataPromise('usb.isClaimed', deviceInfo); + }, + + /** + * Send data to a claimed USB device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of USB device's vendor ID. + * @param deviceInfo.productId Hex string of USB device's product ID. + * @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device. + * @param deviceInfo.data Bytes to send over specified endpoint. + * @param {string} [deviceInfo.type='PLAIN'] Valid values [FILE | PLAIN | HEX | BASE64] + * @returns {Promise} + * + * @memberof qz.usb + */ + sendData: function(deviceInfo) { + //backwards compatibility + if (typeof deviceInfo !== 'object') { + deviceInfo = { + vendorId: arguments[0], + productId: arguments[1], + endpoint: arguments[2], + data: arguments[3] + }; + } + + if (_qz.tools.versionCompare(2, 1, 0, 12) >= 0) { + if (typeof deviceInfo.data !== 'object') { + deviceInfo.data = { + data: deviceInfo.data, + type: "PLAIN" + } + } + + if (deviceInfo.data.type && deviceInfo.data.type.toUpperCase() == "FILE") { + deviceInfo.data.data = _qz.tools.absolute(deviceInfo.data.data); + } + } + + return _qz.websocket.dataPromise('usb.sendData', deviceInfo); + }, + + /** + * Read data from a claimed USB device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of USB device's vendor ID. + * @param deviceInfo.productId Hex string of USB device's product ID. + * @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device. + * @param deviceInfo.responseSize Size of the byte array to receive a response in. + * @returns {Promise|Error>} List of (hexadecimal) bytes received from the USB device. + * + * @memberof qz.usb + */ + readData: function(deviceInfo) { + //backwards compatibility + if (typeof deviceInfo !== 'object') { + deviceInfo = { + vendorId: arguments[0], + productId: arguments[1], + endpoint: arguments[2], + responseSize: arguments[3] + }; + } + + return _qz.websocket.dataPromise('usb.readData', deviceInfo); + }, + + /** + * Provides a continuous stream of read data from a claimed USB device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of USB device's vendor ID. + * @param deviceInfo.productId Hex string of USB device's product ID. + * @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device. + * @param deviceInfo.responseSize Size of the byte array to receive a response in. + * @param deviceInfo.interval=100 Frequency to send read data back, in milliseconds. + * @returns {Promise} + * + * @see qz.usb.setUsbCallbacks + * + * @memberof qz.usb + */ + openStream: function(deviceInfo) { + //backwards compatibility + if (typeof deviceInfo !== 'object') { + deviceInfo = { + vendorId: arguments[0], + productId: arguments[1], + endpoint: arguments[2], + responseSize: arguments[3], + interval: arguments[4] + }; + } + + return _qz.websocket.dataPromise('usb.openStream', deviceInfo); + }, + + /** + * Stops the stream of read data from a claimed USB device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of USB device's vendor ID. + * @param deviceInfo.productId Hex string of USB device's product ID. + * @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device. + * @returns {Promise} + * + * @memberof qz.usb + */ + closeStream: function(deviceInfo) { + //backwards compatibility + if (typeof deviceInfo !== 'object') { + deviceInfo = { + vendorId: arguments[0], + productId: arguments[1], + endpoint: arguments[2] + }; + } + + return _qz.websocket.dataPromise('usb.closeStream', deviceInfo); + }, + + /** + * Release a claimed USB device to free resources after sending/reading data. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of USB device's vendor ID. + * @param deviceInfo.productId Hex string of USB device's product ID. + * @returns {Promise} + * + * @memberof qz.usb + */ + releaseDevice: function(deviceInfo) { + if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility + + return _qz.websocket.dataPromise('usb.releaseDevice', deviceInfo); + } + }, + + + /** + * Calls related to interaction with HID USB devices
+ * Many of these calls can be accomplished from the qz.usb namespace, + * but HID allows for simpler interaction + * @namespace qz.hid + * @since 2.0.1 + */ + hid: { + /** + * List of available HID devices. Includes (hexadecimal) vendor ID and (hexadecimal) product ID. + * If available, also returns manufacturer and product descriptions. + * + * @returns {Promise|Error>} Array of JSON objects containing information on connected HID devices. + * @since 2.0.1 + * + * @memberof qz.hid + */ + listDevices: function() { + return _qz.websocket.dataPromise('hid.listDevices'); + }, + + /** + * Start listening for HID device actions, such as attach / detach events. + * Reported under the ACTION type in the streamEvent on callbacks. + * + * @returns {Promise} + * @since 2.0.1 + * + * @see qz.hid.setHidCallbacks + * + * @memberof qz.hid + */ + startListening: function() { + return _qz.websocket.dataPromise('hid.startListening'); + }, + + /** + * Stop listening for HID device actions. + * + * @returns {Promise} + * @since 2.0.1 + * + * @see qz.hid.setHidCallbacks + * + * @memberof qz.hid + */ + stopListening: function() { + return _qz.websocket.dataPromise('hid.stopListening'); + }, + + /** + * List of functions called for any response from open usb devices. + * Event data will contain {string} vendorId and {string} productId for all types. + * For RECEIVE types, {Array} output (in hexadecimal format). + * For ERROR types, {string} exception. + * For ACTION types, {string} actionType. + * + * @param {Function|Array} calls Single or array of Function({Object} eventData) calls. + * @since 2.0.1 + * + * @memberof qz.hid + */ + setHidCallbacks: function(calls) { + _qz.hid.hidCallbacks = calls; + }, + + /** + * Claim a HID device to enable sending/reading data across. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of HID device's vendor ID. + * @param deviceInfo.productId Hex string of HID device's product ID. + * @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present. + * @param deviceInfo.serial Serial ID of HID device. + * @returns {Promise} + * @since 2.0.1 + * + * @memberof qz.hid + */ + claimDevice: function(deviceInfo) { + if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility + + return _qz.websocket.dataPromise('hid.claimDevice', deviceInfo); + }, + + /** + * Check the current claim state of a HID device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of HID device's vendor ID. + * @param deviceInfo.productId Hex string of HID device's product ID. + * @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present. + * @param deviceInfo.serial Serial ID of HID device. + * @returns {Promise} + * + * @since 2.0.2 + * @memberOf qz.hid + */ + isClaimed: function(deviceInfo) { + if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility + + return _qz.websocket.dataPromise('hid.isClaimed', deviceInfo); + }, + + /** + * Send data to a claimed HID device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of HID device's vendor ID. + * @param deviceInfo.productId Hex string of HID device's product ID. + * @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present. + * @param deviceInfo.serial Serial ID of HID device. + * @param deviceInfo.data Bytes to send over specified endpoint. + * @param deviceInfo.endpoint=0x00 First byte of the data packet signifying the HID report ID. + * Must be 0x00 for devices only supporting a single report. + * @param deviceInfo.reportId=0x00 Alias for deviceInfo.endpoint. Not used if endpoint is provided. + * @param {string} [deviceInfo.type='PLAIN'] Valid values [FILE | PLAIN | HEX | BASE64] + * @returns {Promise} + * @since 2.0.1 + * + * @memberof qz.hid + */ + sendData: function(deviceInfo) { + //backwards compatibility + if (typeof deviceInfo !== 'object') { + deviceInfo = { + vendorId: arguments[0], + productId: arguments[1], + data: arguments[2], + endpoint: arguments[3] + }; + } + + if (_qz.tools.versionCompare(2, 1, 0, 12) >= 0) { + if (typeof deviceInfo.data !== 'object') { + deviceInfo.data = { + data: deviceInfo.data, + type: "PLAIN" + } + } + + if (deviceInfo.data.type && deviceInfo.data.type.toUpperCase() == "FILE") { + deviceInfo.data.data = _qz.tools.absolute(deviceInfo.data.data); + } + } else { + if (typeof deviceInfo.data === 'object') { + if (deviceInfo.data.type.toUpperCase() !== "PLAIN" + || typeof deviceInfo.data.data !== "string") { + return _qz.tools.reject(new Error("Data format is not supported with connected QZ Tray version " + _qz.websocket.connection.version)); + } + + deviceInfo.data = deviceInfo.data.data; + } + } + + return _qz.websocket.dataPromise('hid.sendData', deviceInfo); + }, + + /** + * Read data from a claimed HID device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of HID device's vendor ID. + * @param deviceInfo.productId Hex string of HID device's product ID. + * @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present. + * @param deviceInfo.serial Serial ID of HID device. + * @param deviceInfo.responseSize Size of the byte array to receive a response in. + * @returns {Promise|Error>} List of (hexadecimal) bytes received from the HID device. + * @since 2.0.1 + * + * @memberof qz.hid + */ + readData: function(deviceInfo) { + //backwards compatibility + if (typeof deviceInfo !== 'object') { + deviceInfo = { + vendorId: arguments[0], + productId: arguments[1], + responseSize: arguments[2] + }; + } + + return _qz.websocket.dataPromise('hid.readData', deviceInfo); + }, + + /** + * Send a feature report to a claimed HID device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of HID device's vendor ID. + * @param deviceInfo.productId Hex string of HID device's product ID. + * @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present. + * @param deviceInfo.serial Serial ID of HID device. + * @param deviceInfo.data Bytes to send over specified endpoint. + * @param deviceInfo.endpoint=0x00 First byte of the data packet signifying the HID report ID. + * Must be 0x00 for devices only supporting a single report. + * @param deviceInfo.reportId=0x00 Alias for deviceInfo.endpoint. Not used if endpoint is provided. + * @param {string} [deviceInfo.type='PLAIN'] Valid values [FILE | PLAIN | HEX | BASE64] + * @returns {Promise} + * + * @memberof qz.hid + */ + sendFeatureReport: function(deviceInfo) { + return _qz.websocket.dataPromise('hid.sendFeatureReport', deviceInfo); + }, + + /** + * Get a feature report from a claimed HID device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of HID device's vendor ID. + * @param deviceInfo.productId Hex string of HID device's product ID. + * @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present. + * @param deviceInfo.serial Serial ID of HID device. + * @param deviceInfo.responseSize Size of the byte array to receive a response in. + * @returns {Promise|Error>} List of (hexadecimal) bytes received from the HID device. + * + * @memberof qz.hid + */ + getFeatureReport: function(deviceInfo) { + return _qz.websocket.dataPromise('hid.getFeatureReport', deviceInfo); + }, + + /** + * Provides a continuous stream of read data from a claimed HID device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of HID device's vendor ID. + * @param deviceInfo.productId Hex string of HID device's product ID. + * @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present. + * @param deviceInfo.serial Serial ID of HID device. + * @param deviceInfo.responseSize Size of the byte array to receive a response in. + * @param deviceInfo.interval=100 Frequency to send read data back, in milliseconds. + * @returns {Promise} + * @since 2.0.1 + * + * @see qz.hid.setHidCallbacks + * + * @memberof qz.hid + */ + openStream: function(deviceInfo) { + //backwards compatibility + if (typeof deviceInfo !== 'object') { + deviceInfo = { + vendorId: arguments[0], + productId: arguments[1], + responseSize: arguments[2], + interval: arguments[3] + }; + } + + return _qz.websocket.dataPromise('hid.openStream', deviceInfo); + }, + + /** + * Stops the stream of read data from a claimed HID device. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of HID device's vendor ID. + * @param deviceInfo.productId Hex string of HID device's product ID. + * @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present. + * @param deviceInfo.serial Serial ID of HID device. + * @returns {Promise} + * @since 2.0.1 + * + * @memberof qz.hid + */ + closeStream: function(deviceInfo) { + if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility + + return _qz.websocket.dataPromise('hid.closeStream', deviceInfo); + }, + + /** + * Release a claimed HID device to free resources after sending/reading data. + * + * @param {object} deviceInfo Config details of the HID device. + * @param deviceInfo.vendorId Hex string of HID device's vendor ID. + * @param deviceInfo.productId Hex string of HID device's product ID. + * @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present. + * @param deviceInfo.serial Serial ID of HID device. + * @returns {Promise} + * @since 2.0.1 + * + * @memberof qz.hid + */ + releaseDevice: function(deviceInfo) { + if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility + + return _qz.websocket.dataPromise('hid.releaseDevice', deviceInfo); + } + }, + + + /** + * Calls related to interactions with the filesystem + * @namespace qz.file + * @since 2.1 + */ + file: { + /** + * List of files available at the given directory.
+ * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file. + * + * @param {string} path Relative or absolute directory path. Must reside in qz data directory or a white-listed location. + * @param {Object} [params] Object containing file access parameters + * @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections + * @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user + * @returns {Promise|Error>} Array of files at the given path + * + * @memberof qz.file + */ + list: function(path, params) { + var param = _qz.tools.extend({ path: path }, params); + return _qz.websocket.dataPromise('file.list', param); + }, + + /** + * Reads contents of file at the given path.
+ * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file. + * + * @param {string} path Relative or absolute file path. Must reside in qz data directory or a white-listed location. + * @param {Object} [params] Object containing file access parameters + * @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections + * @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user + * @param {string} [params.flavor='plain'] Flavor of data format used, valid flavors are [base64 | hex | plain]. + * @returns {Promise} String containing the file contents + * + * @memberof qz.file + */ + read: function(path, params) { + var param = _qz.tools.extend({ path: path }, params); + return _qz.websocket.dataPromise('file.read', param); + }, + + /** + * Writes data to the file at the given path.
+ * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file. + * + * @param {string} path Relative or absolute file path. Must reside in qz data directory or a white-listed location. + * @param {Object} params Object containing file access parameters + * @param {string} params.data File data to be written + * @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections + * @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user + * @param {boolean} [params.append=false] Appends to the end of the file if set, otherwise overwrites existing contents + * @param {string} [params.flavor='plain'] Flavor of data format used, valid flavors are [base64 | file | hex | plain]. + * @returns {Promise} + * + * @memberof qz.file + */ + write: function(path, params) { + var param = _qz.tools.extend({ path: path }, params); + return _qz.websocket.dataPromise('file.write', param); + }, + + /** + * Deletes a file at given path.
+ * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file. + * + * @param {string} path Relative or absolute file path. Must reside in qz data directory or a white-listed location. + * @param {Object} [params] Object containing file access parameters + * @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections + * @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user + * @returns {Promise} + * + * @memberof qz.file + */ + remove: function(path, params) { + var param = _qz.tools.extend({ path: path }, params); + return _qz.websocket.dataPromise('file.remove', param); + }, + + /** + * Provides a continuous stream of events (and optionally data) from a local file. + * + * @param {string} path Relative or absolute directory path. Must reside in qz data directory or a white-listed location. + * @param {Object} [params] Object containing file access parameters + * @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections + * @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user + * @param {Object} [params.listener] If defined, file data will be returned on events + * @param {number} [params.listener.bytes=-1] Number of bytes to return or -1 for all + * @param {number} [params.listener.lines=-1] Number of lines to return or -1 for all + * @param {boolean} [params.listener.reverse] Controls whether data should be returned from the bottom of the file. Default value is true for line mode and false for byte mode. + * @param {string|Array} [params.include] File patterns to match. Blank values will be ignored. + * @param {string|Array} [params.exclude] File patterns to exclude. Blank values will be ignored. Takes priority over params.include. + * @param {boolean} [params.ignoreCase=true] Whether params.include or params.exclude are case-sensitive. + * @returns {Promise} + * @since 2.1.0 + * + * @see qz.file.setFileCallbacks + * + * @memberof qz.file + */ + startListening: function(path, params) { + if (params && typeof params.include !== 'undefined' && !Array.isArray(params.include)) { + params.include = [params.include]; + } + if (params && typeof params.exclude !== 'undefined' && !Array.isArray(params.exclude)) { + params.exclude = [params.exclude]; + } + var param = _qz.tools.extend({ path: path }, params); + return _qz.websocket.dataPromise('file.startListening', param); + }, + + /** + * Closes listeners with the provided settings. Omitting the path parameter will result in all listeners closing. + * + * @param {string} [path] Previously opened directory path of listener to close, or omit to close all. + * @param {Object} [params] Object containing file access parameters + * @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections + * @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user + * @returns {Promise} + * + * @memberof qz.file + */ + stopListening: function(path, params) { + var param = _qz.tools.extend({ path: path }, params); + return _qz.websocket.dataPromise('file.stopListening', param); + }, + + /** + * List of functions called for any response from a file listener. + * For ERROR types event data will contain, {string} message. + * For ACTION types event data will contain, {string} file {string} eventType {string} [data]. + * + * @param {Function|Array} calls Single or array of Function({Object} eventData) calls. + * @since 2.1.0 + * + * @memberof qz.file + */ + setFileCallbacks: function(calls) { + _qz.file.fileCallbacks = calls; + } + }, + + /** + * Calls related to networking information + * @namespace qz.networking + * @since 2.1.0 + */ + networking: { + /** + * @param {string} [hostname] Hostname to try to connect to when determining network interfaces, defaults to "google.com" + * @param {number} [port] Port to use with custom hostname, defaults to 443 + * @returns {Promise} Connected system's network information. + * + * @memberof qz.networking + * @since 2.1.0 + */ + device: function(hostname, port) { + // Wrap 2.0 + if (_qz.tools.isVersion(2, 0)) { + return _qz.compatible.networking(hostname, port, null, null, function(data) { + return { ip: data.ipAddress, mac: data.macAddress }; + }); + } + // Use 2.1 + return _qz.websocket.dataPromise('networking.device', { + hostname: hostname, + port: port + }); + }, + + /** + * Get computer hostname + * + * @param {string} [hostname] DEPRECATED Hostname to try to connect to when determining network interfaces, defaults to "google.com" + * @param {number} [port] DEPRECATED Port to use with custom hostname, defaults to 443 + * @returns {Promise} Connected system's hostname. + * + * @memberof qz.networking + * @since 2.2.2 + */ + hostname: function(hostname, port) { + // Wrap < 2.2.2 + if (_qz.tools.versionCompare(2, 2, 2) < 0) { + return _qz.tools.promise(function(resolve, reject) { + _qz.websocket.dataPromise('networking.device', { hostname: hostname, port: port }).then(function(device) { + console.log(device); + resolve(device.hostname); + }); + }); + } else { + return _qz.websocket.dataPromise('networking.hostname'); + } + }, + + /** + * @param {string} [hostname] Hostname to try to connect to when determining network interfaces, defaults to "google.com" + * @param {number} [port] Port to use with custom hostname, defaults to 443 + * @returns {Promise|Error>} Connected system's network information. + * + * @memberof qz.networking + * @since 2.1.0 + */ + devices: function(hostname, port) { + // Wrap 2.0 + if (_qz.tools.isVersion(2, 0)) { + return _qz.compatible.networking(hostname, port, null, null, function(data) { + return [{ ip: data.ipAddress, mac: data.macAddress }]; + }); + } + // Use 2.1 + return _qz.websocket.dataPromise('networking.devices', { + hostname: hostname, + port: port + }); + } + }, + + + /** + * Calls related to signing connection requests. + * @namespace qz.security + */ + security: { + /** + * Set promise resolver for calls to acquire the site's certificate. + * + * @param {Function|AsyncFunction|Promise} promiseHandler Either a function that will be used as a promise resolver (of format Function({function} resolve, {function}reject)), + * an async function, or a promise. Any of which should return the public certificate via their respective resolve call. + * @param {Object} [options] Configuration options for the certificate resolver + * @param {boolean} [options.rejectOnFailure=[false]] Overrides default behavior to call resolve with a blank certificate on failure. + * @memberof qz.security + */ + setCertificatePromise: function(promiseHandler, options) { + _qz.security.certHandler = promiseHandler; + _qz.security.rejectOnCertFailure = !!(options && options.rejectOnFailure); + }, + + /** + * Set promise factory for calls to sign API calls. + * + * @param {Function|AsyncFunction} promiseFactory Either a function that accepts a string parameter of the data to be signed + * and returns a function to be used as a promise resolver (of format Function({function} resolve, {function}reject)), + * or an async function that can take a string parameter of the data to be signed. Either of which should return the signed contents of + * the passed string parameter via their respective resolve call. + * + * @example + * qz.security.setSignaturePromise(function(dataToSign) { + * return function(resolve, reject) { + * $.ajax("/signing-url?data=" + dataToSign).then(resolve, reject); + * } + * }) + * + * @memberof qz.security + */ + setSignaturePromise: function(promiseFactory) { + _qz.security.signatureFactory = promiseFactory; + }, + + /** + * Set which signing algorithm QZ will check signatures against. + * + * @param {string} algorithm The algorithm used in signing. Valid values: [SHA1 | SHA256 | SHA512] + * @since 2.1.0 + * + * @memberof qz.security + */ + setSignatureAlgorithm: function(algorithm) { + //warn for incompatibilities if known + if (!_qz.compatible.algorithm()) { + return; + } + + if (["SHA1", "SHA256", "SHA512"].indexOf(algorithm.toUpperCase()) < 0) { + _qz.log.error("Signing algorithm '" + algorithm + "' is not supported."); + } else { + _qz.security.signAlgorithm = algorithm; + } + }, + + /** + * Get the signing algorithm QZ will be checking signatures against. + * + * @returns {string} The algorithm used in signing. + * @since 2.1.0 + * + * @memberof qz.security + */ + getSignatureAlgorithm: function() { + return _qz.security.signAlgorithm; + } + }, + + /** + * Calls related to compatibility adjustments + * @namespace qz.api + */ + api: { + /** + * Show or hide QZ api debugging statements in the browser console. + * + * @param {boolean} show Whether the debugging logs for QZ should be shown. Hidden by default. + * @returns {boolean} Value of debugging flag + * @memberof qz.api + */ + showDebug: function(show) { + return (_qz.DEBUG = show); + }, + + /** + * Get version of connected QZ Tray application. + * + * @returns {Promise} Version number of QZ Tray. + * + * @memberof qz.api + */ + getVersion: function() { + return _qz.websocket.dataPromise('getVersion'); + }, + + /** + * Checks for the specified version of connected QZ Tray application. + * + * @param {string|number} [major] Major version to check + * @param {string|number} [minor] Minor version to check + * @param {string|number} [patch] Patch version to check + * + * @memberof qz.api + */ + isVersion: _qz.tools.isVersion, + + /** + * Checks if the connected QZ Tray application is greater than the specified version. + * + * @param {string|number} major Major version to check + * @param {string|number} [minor] Minor version to check + * @param {string|number} [patch] Patch version to check + * @param {string|number} [build] Build version to check + * @returns {boolean} True if connected version is greater than the version specified. + * + * @memberof qz.api + * @since 2.1.0-4 + */ + isVersionGreater: function(major, minor, patch, build) { + return _qz.tools.versionCompare(major, minor, patch, build) > 0; + }, + + /** + * Checks if the connected QZ Tray application is less than the specified version. + * + * @param {string|number} major Major version to check + * @param {string|number} [minor] Minor version to check + * @param {string|number} [patch] Patch version to check + * @param {string|number} [build] Build version to check + * @returns {boolean} True if connected version is less than the version specified. + * + * @memberof qz.api + * @since 2.1.0-4 + */ + isVersionLess: function(major, minor, patch, build) { + return _qz.tools.versionCompare(major, minor, patch, build) < 0; + }, + + /** + * Change the promise library used by QZ API. + * Should be called before any initialization to avoid possible errors. + * + * @param {Function} promiser Function({function} resolver) called to create new promises. + * + * @memberof qz.api + */ + setPromiseType: function(promiser) { + _qz.tools.promise = promiser; + }, + + /** + * Change the SHA-256 hashing function used by QZ API. + * Should be called before any initialization to avoid possible errors. + * + * @param {Function} hasher Function({function} message) called to create hash of passed string. + * + * @memberof qz.api + */ + setSha256Type: function(hasher) { + _qz.tools.hash = hasher; + }, + + /** + * Change the WebSocket handler. + * Should be called before any initialization to avoid possible errors. + * + * @param {Function} ws Function({function} WebSocket) called to override the internal WebSocket handler. + * + * @memberof qz.api + */ + setWebSocketType: function(ws) { + _qz.tools.ws = ws; + } + }, + + /** + * Version of this JavaScript library + * + * @constant {string} + * + * @memberof qz + */ + version: _qz.VERSION + }; + + return qz; +})(); + + +(function() { + if (typeof define === 'function' && define.amd) { + define(qz); + } else if (typeof exports === 'object') { + module.exports = qz; + } else { + window.qz = qz; + } +})(); diff --git a/tray/js/sample/array-from-pollyfill.js b/tray/js/sample/array-from-pollyfill.js new file mode 100644 index 0000000..3cf8d54 --- /dev/null +++ b/tray/js/sample/array-from-pollyfill.js @@ -0,0 +1,76 @@ +if (!Array.from) { + Array.from = (function () { + var toStr = Object.prototype.toString; + var isCallable = function (fn) { + return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; + }; + var toInteger = function (value) { + var number = Number(value); + if (isNaN(number)) { return 0; } + if (number === 0 || !isFinite(number)) { return number; } + return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); + }; + var maxSafeInteger = Math.pow(2, 53) - 1; + var toLength = function (value) { + var len = toInteger(value); + return Math.min(Math.max(len, 0), maxSafeInteger); + }; + + // The length property of the from method is 1. + return function from(arrayLike/*, mapFn, thisArg */) { + // 1. Let C be the this value. + var C = this; + + // 2. Let items be ToObject(arrayLike). + var items = Object(arrayLike); + + // 3. ReturnIfAbrupt(items). + if (arrayLike == null) { + throw new TypeError("Array.from requires an array-like object - not null or undefined"); + } + + // 4. If mapfn is undefined, then let mapping be false. + var mapFn = arguments.length > 1 ? arguments[1] : void undefined; + var T; + if (typeof mapFn !== 'undefined') { + // 5. else + // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. + if (!isCallable(mapFn)) { + throw new TypeError('Array.from: when provided, the second argument must be a function'); + } + + // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (arguments.length > 2) { + T = arguments[2]; + } + } + + // 10. Let lenValue be Get(items, "length"). + // 11. Let len be ToLength(lenValue). + var len = toLength(items.length); + + // 13. If IsConstructor(C) is true, then + // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len. + // 14. a. Else, Let A be ArrayCreate(len). + var A = isCallable(C) ? Object(new C(len)) : new Array(len); + + // 16. Let k be 0. + var k = 0; + // 17. Repeat, while k < len… (also steps a - h) + var kValue; + while (k < len) { + kValue = items[k]; + if (mapFn) { + A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); + } else { + A[k] = kValue; + } + k += 1; + } + // 18. Let putStatus be Put(A, "length", len, true). + A.length = len; + // 20. Return A. + return A; + }; + }()); +} \ No newline at end of file diff --git a/tray/js/sample/bootstrap.min.js b/tray/js/sample/bootstrap.min.js new file mode 100644 index 0000000..9f47837 --- /dev/null +++ b/tray/js/sample/bootstrap.min.js @@ -0,0 +1,678 @@ +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under the MIT license + */ +if ("undefined" == typeof jQuery) { + throw new Error("Bootstrap's JavaScript requires jQuery"); +} ++function(a) { + "use strict"; + var b = a.fn.jquery.split(" ")[0].split("."); + if (b[0] < 2 && b[1] < 9 || 1 == b[0] && 9 == b[1] && b[2] < 1) { + throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher") + } +}(jQuery), +function(a) { + "use strict"; + function b() { + var a = document.createElement("bootstrap"), b = { + WebkitTransition: "webkitTransitionEnd", + MozTransition: "transitionend", + OTransition: "oTransitionEnd otransitionend", + transition: "transitionend" + }; + for(var c in b) { + if (void 0 !== a.style[c]) { + return { end: b[c] }; + } + } + return !1 + } + + a.fn.emulateTransitionEnd = function(b) { + var c = !1, d = this; + a(this).one("bsTransitionEnd", function() {c = !0}); + var e = function() {c || a(d).trigger(a.support.transition.end)}; + return setTimeout(e, b), this + }, a(function() { + a.support.transition = b(), a.support.transition && (a.event.special.bsTransitionEnd = { + bindType: a.support.transition.end, + delegateType: a.support.transition.end, + handle: function(b) {return a(b.target).is(this) ? b.handleObj.handler.apply(this, arguments) : void 0} + }) + }) +}(jQuery), +function(a) { + "use strict"; + function b(b) { + return this.each(function() { + var c = a(this), e = c.data("bs.alert"); + e || c.data("bs.alert", e = new d(this)), "string" == typeof b && e[b].call(c) + }) + } + + var c = '[data-dismiss="alert"]', d = function(b) {a(b).on("click", c, this.close)}; + d.VERSION = "3.3.5", d.TRANSITION_DURATION = 150, d.prototype.close = function(b) { + function c() {g.detach().trigger("closed.bs.alert").remove()} + + var e = a(this), f = e.attr("data-target"); + f || (f = e.attr("href"), f = f && f.replace(/.*(?=#[^\s]*$)/, "")); + var g = a(f); + b && b.preventDefault(), g.length || (g = e.closest(".alert")), g.trigger(b = a.Event("close.bs.alert")), b.isDefaultPrevented() || (g.removeClass("in"), a.support.transition && g.hasClass("fade") ? g.one("bsTransitionEnd", c).emulateTransitionEnd(d.TRANSITION_DURATION) : c()) + }; + var e = a.fn.alert; + a.fn.alert = b, a.fn.alert.Constructor = d, a.fn.alert.noConflict = function() {return a.fn.alert = e, this}, a(document).on("click.bs.alert.data-api", c, d.prototype.close) +}(jQuery), +function(a) { + "use strict"; + function b(b) { + return this.each(function() { + var d = a(this), e = d.data("bs.button"), f = "object" == typeof b && b; + e || d.data("bs.button", e = new c(this, f)), "toggle" == b ? e.toggle() : b && e.setState(b) + }) + } + + var c = function(b, d) {this.$element = a(b), this.options = a.extend({}, c.DEFAULTS, d), this.isLoading = !1}; + c.VERSION = "3.3.5", c.DEFAULTS = { loadingText: "loading..." }, c.prototype.setState = function(b) { + var c = "disabled", d = this.$element, e = d.is("input") ? "val" : "html", f = d.data(); + b += "Text", null == f.resetText && d.data("resetText", d[e]()), setTimeout(a.proxy(function() {d[e](null == f[b] ? this.options[b] : f[b]), "loadingText" == b ? (this.isLoading = !0, d.addClass(c).attr(c, c)) : this.isLoading && (this.isLoading = !1, d.removeClass(c).removeAttr(c))}, this), 0) + }, c.prototype.toggle = function() { + var a = !0, b = this.$element.closest('[data-toggle="buttons"]'); + if (b.length) { + var c = this.$element.find("input"); + "radio" == c.prop("type") ? (c.prop("checked") && (a = !1), b.find(".active").removeClass("active"), this.$element.addClass("active")) : "checkbox" == c.prop("type") && (c.prop("checked") !== this.$element.hasClass("active") && (a = !1), this.$element.toggleClass("active")), c.prop("checked", this.$element.hasClass("active")), a && c.trigger("change") + } else { + this.$element.attr("aria-pressed", !this.$element.hasClass("active")), this.$element.toggleClass("active") + } + }; + var d = a.fn.button; + a.fn.button = b, a.fn.button.Constructor = c, a.fn.button.noConflict = function() {return a.fn.button = d, this}, a(document).on("click.bs.button.data-api", '[data-toggle^="button"]', function(c) { + var d = a(c.target); + d.hasClass("btn") || (d = d.closest(".btn")), b.call(d, "toggle"), a(c.target).is('input[type="radio"]') || a(c.target).is('input[type="checkbox"]') || c.preventDefault() + }).on("focus.bs.button.data-api blur.bs.button.data-api", '[data-toggle^="button"]', function(b) {a(b.target).closest(".btn").toggleClass("focus", /^focus(in)?$/.test(b.type))}) +}(jQuery), +function(a) { + "use strict"; + function b(b) { + return this.each(function() { + var d = a(this), e = d.data("bs.carousel"), f = a.extend({}, c.DEFAULTS, d.data(), "object" == typeof b && b), g = "string" == typeof b ? b : f.slide; + e || d.data("bs.carousel", e = new c(this, f)), "number" == typeof b ? e.to(b) : g ? e[g]() : f.interval && e.pause().cycle() + }) + } + + var c = function(b, c) {this.$element = a(b), this.$indicators = this.$element.find(".carousel-indicators"), this.options = c, this.paused = null, this.sliding = null, this.interval = null, this.$active = null, this.$items = null, this.options.keyboard && this.$element.on("keydown.bs.carousel", a.proxy(this.keydown, this)), "hover" == this.options.pause && !("ontouchstart"in document.documentElement) && this.$element.on("mouseenter.bs.carousel", a.proxy(this.pause, this)).on("mouseleave.bs.carousel", a.proxy(this.cycle, this))}; + c.VERSION = "3.3.5", c.TRANSITION_DURATION = 600, c.DEFAULTS = { + interval: 5e3, + pause: "hover", + wrap: !0, + keyboard: !0 + }, c.prototype.keydown = function(a) { + if (!/input|textarea/i.test(a.target.tagName)) { + switch(a.which) { + case 37: + this.prev(); + break; + case 39: + this.next(); + break; + default: + return + } + a.preventDefault() + } + }, c.prototype.cycle = function(b) {return b || (this.paused = !1), this.interval && clearInterval(this.interval), this.options.interval && !this.paused && (this.interval = setInterval(a.proxy(this.next, this), this.options.interval)), this}, c.prototype.getItemIndex = function(a) {return this.$items = a.parent().children(".item"), this.$items.index(a || this.$active)}, c.prototype.getItemForDirection = function(a, b) { + var c = this.getItemIndex(b), d = "prev" == a && 0 === c || "next" == a && c == this.$items.length - 1; + if (d && !this.options.wrap) { + return b; + } + var e = "prev" == a ? -1 : 1, f = (c + e) % this.$items.length; + return this.$items.eq(f) + }, c.prototype.to = function(a) { + var b = this, c = this.getItemIndex(this.$active = this.$element.find(".item.active")); + return a > this.$items.length - 1 || 0 > a ? void 0 : this.sliding ? this.$element.one("slid.bs.carousel", function() {b.to(a)}) : c == a ? this.pause().cycle() : this.slide(a > c ? "next" : "prev", this.$items.eq(a)) + }, c.prototype.pause = function(b) {return b || (this.paused = !0), this.$element.find(".next, .prev").length && a.support.transition && (this.$element.trigger(a.support.transition.end), this.cycle(!0)), this.interval = clearInterval(this.interval), this}, c.prototype.next = function() {return this.sliding ? void 0 : this.slide("next")}, c.prototype.prev = function() {return this.sliding ? void 0 : this.slide("prev")}, c.prototype.slide = function(b, d) { + var e = this.$element.find(".item.active"), f = d || this.getItemForDirection(b, e), g = this.interval, h = "next" == b ? "left" : "right", i = this; + if (f.hasClass("active")) { + return this.sliding = !1; + } + var j = f[0], k = a.Event("slide.bs.carousel", { relatedTarget: j, direction: h }); + if (this.$element.trigger(k), !k.isDefaultPrevented()) { + if (this.sliding = !0, g && this.pause(), this.$indicators.length) { + this.$indicators.find(".active").removeClass("active"); + var l = a(this.$indicators.children()[this.getItemIndex(f)]); + l && l.addClass("active") + } + var m = a.Event("slid.bs.carousel", { relatedTarget: j, direction: h }); + return a.support.transition && this.$element.hasClass("slide") ? (f.addClass(b), f[0].offsetWidth, e.addClass(h), f.addClass(h), e.one("bsTransitionEnd", function() {f.removeClass([b, h].join(" ")).addClass("active"), e.removeClass(["active", h].join(" ")), i.sliding = !1, setTimeout(function() {i.$element.trigger(m)}, 0)}).emulateTransitionEnd(c.TRANSITION_DURATION)) : (e.removeClass("active"), f.addClass("active"), this.sliding = !1, this.$element.trigger(m)), g && this.cycle(), this + } + }; + var d = a.fn.carousel; + a.fn.carousel = b, a.fn.carousel.Constructor = c, a.fn.carousel.noConflict = function() {return a.fn.carousel = d, this}; + var e = function(c) { + var d, e = a(this), f = a(e.attr("data-target") || (d = e.attr("href")) && d.replace(/.*(?=#[^\s]+$)/, "")); + if (f.hasClass("carousel")) { + var g = a.extend({}, f.data(), e.data()), h = e.attr("data-slide-to"); + h && (g.interval = !1), b.call(f, g), h && f.data("bs.carousel").to(h), c.preventDefault() + } + }; + a(document).on("click.bs.carousel.data-api", "[data-slide]", e).on("click.bs.carousel.data-api", "[data-slide-to]", e), a(window).on("load", function() { + a('[data-ride="carousel"]').each(function() { + var c = a(this); + b.call(c, c.data()) + }) + }) +}(jQuery), +function(a) { + "use strict"; + function b(b) { + var c, d = b.attr("data-target") || (c = b.attr("href")) && c.replace(/.*(?=#[^\s]+$)/, ""); + return a(d) + } + + function c(b) { + return this.each(function() { + var c = a(this), e = c.data("bs.collapse"), f = a.extend({}, d.DEFAULTS, c.data(), "object" == typeof b && b); + !e && f.toggle && /show|hide/.test(b) && (f.toggle = !1), e || c.data("bs.collapse", e = new d(this, f)), "string" == typeof b && e[b]() + }) + } + + var d = function(b, c) {this.$element = a(b), this.options = a.extend({}, d.DEFAULTS, c), this.$trigger = a('[data-toggle="collapse"][href="#' + b.id + '"],[data-toggle="collapse"][data-target="#' + b.id + '"]'), this.transitioning = null, this.options.parent ? this.$parent = this.getParent() : this.addAriaAndCollapsedClass(this.$element, this.$trigger), this.options.toggle && this.toggle()}; + d.VERSION = "3.3.5", d.TRANSITION_DURATION = 350, d.DEFAULTS = { toggle: !0 }, d.prototype.dimension = function() { + var a = this.$element.hasClass("width"); + return a ? "width" : "height" + }, d.prototype.show = function() { + if (!this.transitioning && !this.$element.hasClass("in")) { + var b, e = this.$parent && this.$parent.children(".panel").children(".in, .collapsing"); + if (!(e && e.length && (b = e.data("bs.collapse"), b && b.transitioning))) { + var f = a.Event("show.bs.collapse"); + if (this.$element.trigger(f), !f.isDefaultPrevented()) { + e && e.length && (c.call(e, "hide"), b || e.data("bs.collapse", null)); + var g = this.dimension(); + this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded", !0), this.$trigger.removeClass("collapsed").attr("aria-expanded", !0), this.transitioning = 1; + var h = function() {this.$element.removeClass("collapsing").addClass("collapse in")[g](""), this.transitioning = 0, this.$element.trigger("shown.bs.collapse")}; + if (!a.support.transition) { + return h.call(this); + } + var i = a.camelCase(["scroll", g].join("-")); + this.$element.one("bsTransitionEnd", a.proxy(h, this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i]) + } + } + } + }, d.prototype.hide = function() { + if (!this.transitioning && this.$element.hasClass("in")) { + var b = a.Event("hide.bs.collapse"); + if (this.$element.trigger(b), !b.isDefaultPrevented()) { + var c = this.dimension(); + this.$element[c](this.$element[c]())[0].offsetHeight, this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded", !1), this.$trigger.addClass("collapsed").attr("aria-expanded", !1), this.transitioning = 1; + var e = function() {this.transitioning = 0, this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")}; + return a.support.transition ? void this.$element[c](0).one("bsTransitionEnd", a.proxy(e, this)).emulateTransitionEnd(d.TRANSITION_DURATION) : e.call(this) + } + } + }, d.prototype.toggle = function() {this[this.$element.hasClass("in") ? "hide" : "show"]()}, d.prototype.getParent = function() { + return a(this.options.parent).find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]').each(a.proxy(function(c, d) { + var e = a(d); + this.addAriaAndCollapsedClass(b(e), e) + }, this)).end() + }, d.prototype.addAriaAndCollapsedClass = function(a, b) { + var c = a.hasClass("in"); + a.attr("aria-expanded", c), b.toggleClass("collapsed", !c).attr("aria-expanded", c) + }; + var e = a.fn.collapse; + a.fn.collapse = c, a.fn.collapse.Constructor = d, a.fn.collapse.noConflict = function() {return a.fn.collapse = e, this}, a(document).on("click.bs.collapse.data-api", '[data-toggle="collapse"]', function(d) { + var e = a(this); + e.attr("data-target") || d.preventDefault(); + var f = b(e), g = f.data("bs.collapse"), h = g ? "toggle" : e.data(); + c.call(f, h) + }) +}(jQuery), +function(a) { + "use strict"; + function b(b) { + var c = b.attr("data-target"); + c || (c = b.attr("href"), c = c && /#[A-Za-z]/.test(c) && c.replace(/.*(?=#[^\s]*$)/, "")); + var d = c && a(c); + return d && d.length ? d : b.parent() + } + + function c(c) { + c && 3 === c.which || (a(e).remove(), a(f).each(function() { + var d = a(this), e = b(d), f = { relatedTarget: this }; + e.hasClass("open") && (c && "click" == c.type && /input|textarea/i.test(c.target.tagName) && a.contains(e[0], c.target) || (e.trigger(c = a.Event("hide.bs.dropdown", f)), c.isDefaultPrevented() || (d.attr("aria-expanded", "false"), e.removeClass("open").trigger("hidden.bs.dropdown", f)))) + })) + } + + function d(b) { + return this.each(function() { + var c = a(this), d = c.data("bs.dropdown"); + d || c.data("bs.dropdown", d = new g(this)), "string" == typeof b && d[b].call(c) + }) + } + + var e = ".dropdown-backdrop", f = '[data-toggle="dropdown"]', g = function(b) {a(b).on("click.bs.dropdown", this.toggle)}; + g.VERSION = "3.3.5", g.prototype.toggle = function(d) { + var e = a(this); + if (!e.is(".disabled, :disabled")) { + var f = b(e), g = f.hasClass("open"); + if (c(), !g) { + "ontouchstart"in document.documentElement && !f.closest(".navbar-nav").length && a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click", c); + var h = { relatedTarget: this }; + if (f.trigger(d = a.Event("show.bs.dropdown", h)), d.isDefaultPrevented()) { + return; + } + e.trigger("focus").attr("aria-expanded", "true"), f.toggleClass("open").trigger("shown.bs.dropdown", h) + } + return !1 + } + }, g.prototype.keydown = function(c) { + if (/(38|40|27|32)/.test(c.which) && !/input|textarea/i.test(c.target.tagName)) { + var d = a(this); + if (c.preventDefault(), c.stopPropagation(), !d.is(".disabled, :disabled")) { + var e = b(d), g = e.hasClass("open"); + if (!g && 27 != c.which || g && 27 == c.which) { + return 27 == c.which && e.find(f).trigger("focus"), d.trigger("click"); + } + var h = " li:not(.disabled):visible a", i = e.find(".dropdown-menu" + h); + if (i.length) { + var j = i.index(c.target); + 38 == c.which && j > 0 && j--, 40 == c.which && j < i.length - 1 && j++, ~j || (j = 0), i.eq(j).trigger("focus") + } + } + } + }; + var h = a.fn.dropdown; + a.fn.dropdown = d, a.fn.dropdown.Constructor = g, a.fn.dropdown.noConflict = function() {return a.fn.dropdown = h, this}, a(document).on("click.bs.dropdown.data-api", c).on("click.bs.dropdown.data-api", ".dropdown form", function(a) {a.stopPropagation()}).on("click.bs.dropdown.data-api", f, g.prototype.toggle).on("keydown.bs.dropdown.data-api", f, g.prototype.keydown).on("keydown.bs.dropdown.data-api", ".dropdown-menu", g.prototype.keydown) +}(jQuery), +function(a) { + "use strict"; + function b(b, d) { + return this.each(function() { + var e = a(this), f = e.data("bs.modal"), g = a.extend({}, c.DEFAULTS, e.data(), "object" == typeof b && b); + f || e.data("bs.modal", f = new c(this, g)), "string" == typeof b ? f[b](d) : g.show && f.show(d) + }) + } + + var c = function(b, c) {this.options = c, this.$body = a(document.body), this.$element = a(b), this.$dialog = this.$element.find(".modal-dialog"), this.$backdrop = null, this.isShown = null, this.originalBodyPad = null, this.scrollbarWidth = 0, this.ignoreBackdropClick = !1, this.options.remote && this.$element.find(".modal-content").load(this.options.remote, a.proxy(function() {this.$element.trigger("loaded.bs.modal")}, this))}; + c.VERSION = "3.3.5", c.TRANSITION_DURATION = 300, c.BACKDROP_TRANSITION_DURATION = 150, c.DEFAULTS = { + backdrop: !0, + keyboard: !0, + show: !0 + }, c.prototype.toggle = function(a) {return this.isShown ? this.hide() : this.show(a)}, c.prototype.show = function(b) { + var d = this, e = a.Event("show.bs.modal", { relatedTarget: b }); + this.$element.trigger(e), this.isShown || e.isDefaultPrevented() || (this.isShown = !0, this.checkScrollbar(), this.setScrollbar(), this.$body.addClass("modal-open"), this.escape(), this.resize(), this.$element.on("click.dismiss.bs.modal", '[data-dismiss="modal"]', a.proxy(this.hide, this)), this.$dialog.on("mousedown.dismiss.bs.modal", function() {d.$element.one("mouseup.dismiss.bs.modal", function(b) {a(b.target).is(d.$element) && (d.ignoreBackdropClick = !0)})}), this.backdrop(function() { + var e = a.support.transition && d.$element.hasClass("fade"); + d.$element.parent().length || d.$element.appendTo(d.$body), d.$element.show().scrollTop(0), d.adjustDialog(), e && d.$element[0].offsetWidth, d.$element.addClass("in"), d.enforceFocus(); + var f = a.Event("shown.bs.modal", { relatedTarget: b }); + e ? d.$dialog.one("bsTransitionEnd", function() {d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION) : d.$element.trigger("focus").trigger(f) + })) + }, c.prototype.hide = function(b) {b && b.preventDefault(), b = a.Event("hide.bs.modal"), this.$element.trigger(b), this.isShown && !b.isDefaultPrevented() && (this.isShown = !1, this.escape(), this.resize(), a(document).off("focusin.bs.modal"), this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"), this.$dialog.off("mousedown.dismiss.bs.modal"), a.support.transition && this.$element.hasClass("fade") ? this.$element.one("bsTransitionEnd", a.proxy(this.hideModal, this)).emulateTransitionEnd(c.TRANSITION_DURATION) : this.hideModal())}, c.prototype.enforceFocus = function() {a(document).off("focusin.bs.modal").on("focusin.bs.modal", a.proxy(function(a) {this.$element[0] === a.target || this.$element.has(a.target).length || this.$element.trigger("focus")}, this))}, c.prototype.escape = function() {this.isShown && this.options.keyboard ? this.$element.on("keydown.dismiss.bs.modal", a.proxy(function(a) {27 == a.which && this.hide()}, this)) : this.isShown || this.$element.off("keydown.dismiss.bs.modal")}, c.prototype.resize = function() {this.isShown ? a(window).on("resize.bs.modal", a.proxy(this.handleUpdate, this)) : a(window).off("resize.bs.modal")}, c.prototype.hideModal = function() { + var a = this; + this.$element.hide(), this.backdrop(function() {a.$body.removeClass("modal-open"), a.resetAdjustments(), a.resetScrollbar(), a.$element.trigger("hidden.bs.modal")}) + }, c.prototype.removeBackdrop = function() {this.$backdrop && this.$backdrop.remove(), this.$backdrop = null}, c.prototype.backdrop = function(b) { + var d = this, e = this.$element.hasClass("fade") ? "fade" : ""; + if (this.isShown && this.options.backdrop) { + var f = a.support.transition && e; + if (this.$backdrop = a(document.createElement("div")).addClass("modal-backdrop " + e).appendTo(this.$body), this.$element.on("click.dismiss.bs.modal", a.proxy(function(a) {return this.ignoreBackdropClick ? void(this.ignoreBackdropClick = !1) : void(a.target === a.currentTarget && ("static" == this.options.backdrop ? this.$element[0].focus() : this.hide()))}, this)), f && this.$backdrop[0].offsetWidth, this.$backdrop.addClass("in"), !b) { + return; + } + f ? this.$backdrop.one("bsTransitionEnd", b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION) : b() + } else if (!this.isShown && this.$backdrop) { + this.$backdrop.removeClass("in"); + var g = function() {d.removeBackdrop(), b && b()}; + a.support.transition && this.$element.hasClass("fade") ? this.$backdrop.one("bsTransitionEnd", g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION) : g() + } else { + b && b() + } + }, c.prototype.handleUpdate = function() {this.adjustDialog()}, c.prototype.adjustDialog = function() { + var a = this.$element[0].scrollHeight > document.documentElement.clientHeight; + this.$element.css({ + paddingLeft: !this.bodyIsOverflowing && a ? this.scrollbarWidth : "", + paddingRight: this.bodyIsOverflowing && !a ? this.scrollbarWidth : "" + }) + }, c.prototype.resetAdjustments = function() { + this.$element.css({ + paddingLeft: "", + paddingRight: "" + }) + }, c.prototype.checkScrollbar = function() { + var a = window.innerWidth; + if (!a) { + var b = document.documentElement.getBoundingClientRect(); + a = b.right - Math.abs(b.left) + } + this.bodyIsOverflowing = document.body.clientWidth < a, this.scrollbarWidth = this.measureScrollbar() + }, c.prototype.setScrollbar = function() { + var a = parseInt(this.$body.css("padding-right") || 0, 10); + this.originalBodyPad = document.body.style.paddingRight || "", this.bodyIsOverflowing && this.$body.css("padding-right", a + this.scrollbarWidth) + }, c.prototype.resetScrollbar = function() {this.$body.css("padding-right", this.originalBodyPad)}, c.prototype.measureScrollbar = function() { + var a = document.createElement("div"); + a.className = "modal-scrollbar-measure", this.$body.append(a); + var b = a.offsetWidth - a.clientWidth; + return this.$body[0].removeChild(a), b + }; + var d = a.fn.modal; + a.fn.modal = b, a.fn.modal.Constructor = c, a.fn.modal.noConflict = function() {return a.fn.modal = d, this}, a(document).on("click.bs.modal.data-api", '[data-toggle="modal"]', function(c) { + var d = a(this), e = d.attr("href"), f = a(d.attr("data-target") || e && e.replace(/.*(?=#[^\s]+$)/, "")), g = f.data("bs.modal") ? "toggle" : a.extend({ remote: !/#/.test(e) && e }, f.data(), d.data()); + d.is("a") && c.preventDefault(), f.one("show.bs.modal", function(a) {a.isDefaultPrevented() || f.one("hidden.bs.modal", function() {d.is(":visible") && d.trigger("focus")})}), b.call(f, g, this) + }) +}(jQuery), +function(a) { + "use strict"; + function b(b) { + return this.each(function() { + var d = a(this), e = d.data("bs.tooltip"), f = "object" == typeof b && b; + (e || !/destroy|hide/.test(b)) && (e || d.data("bs.tooltip", e = new c(this, f)), "string" == typeof b && e[b]()) + }) + } + + var c = function(a, b) {this.type = null, this.options = null, this.enabled = null, this.timeout = null, this.hoverState = null, this.$element = null, this.inState = null, this.init("tooltip", a, b)}; + c.VERSION = "3.3.5", c.TRANSITION_DURATION = 150, c.DEFAULTS = { + animation: !0, + placement: "top", + selector: !1, + template: '

', + trigger: "hover focus", + title: "", + delay: 0, + html: !1, + container: !1, + viewport: { selector: "body", padding: 0 } + }, c.prototype.init = function(b, c, d) { + if (this.enabled = !0, this.type = b, this.$element = a(c), this.options = this.getOptions(d), this.$viewport = this.options.viewport && a(a.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : this.options.viewport.selector || this.options.viewport), this.inState = { + click: !1, + hover: !1, + focus: !1 + }, this.$element[0]instanceof document.constructor && !this.options.selector) { + throw new Error("`selector` option must be specified when initializing " + this.type + " on the window.document object!"); + } + for(var e = this.options.trigger.split(" "), f = e.length; f--;) { + var g = e[f]; + if ("click" == g) { + this.$element.on("click." + this.type, this.options.selector, a.proxy(this.toggle, this)); + } else if ("manual" != g) { + var h = "hover" == g ? "mouseenter" : "focusin", i = "hover" == g ? "mouseleave" : "focusout"; + this.$element.on(h + "." + this.type, this.options.selector, a.proxy(this.enter, this)), this.$element.on(i + "." + this.type, this.options.selector, a.proxy(this.leave, this)) + } + } + this.options.selector ? this._options = a.extend({}, this.options, { trigger: "manual", selector: "" }) : this.fixTitle() + }, c.prototype.getDefaults = function() {return c.DEFAULTS}, c.prototype.getOptions = function(b) { + return b = a.extend({}, this.getDefaults(), this.$element.data(), b), b.delay && "number" == typeof b.delay && (b.delay = { + show: b.delay, + hide: b.delay + }), b + }, c.prototype.getDelegateOptions = function() { + var b = {}, c = this.getDefaults(); + return this._options && a.each(this._options, function(a, d) {c[a] != d && (b[a] = d)}), b + }, c.prototype.enter = function(b) { + var c = b instanceof this.constructor ? b : a(b.currentTarget).data("bs." + this.type); + return c || (c = new this.constructor(b.currentTarget, this.getDelegateOptions()), a(b.currentTarget).data("bs." + this.type, c)), b instanceof a.Event && (c.inState["focusin" == b.type ? "focus" : "hover"] = !0), c.tip().hasClass("in") || "in" == c.hoverState ? void(c.hoverState = "in") : (clearTimeout(c.timeout), c.hoverState = "in", c.options.delay && c.options.delay.show ? void(c.timeout = setTimeout(function() {"in" == c.hoverState && c.show()}, c.options.delay.show)) : c.show()) + }, c.prototype.isInStateTrue = function() { + for(var a in this.inState) { + if (this.inState[a]) { + return !0; + } + } + return !1 + }, c.prototype.leave = function(b) { + var c = b instanceof this.constructor ? b : a(b.currentTarget).data("bs." + this.type); + return c || (c = new this.constructor(b.currentTarget, this.getDelegateOptions()), a(b.currentTarget).data("bs." + this.type, c)), b instanceof a.Event && (c.inState["focusout" == b.type ? "focus" : "hover"] = !1), c.isInStateTrue() ? void 0 : (clearTimeout(c.timeout), c.hoverState = "out", c.options.delay && c.options.delay.hide ? void(c.timeout = setTimeout(function() {"out" == c.hoverState && c.hide()}, c.options.delay.hide)) : c.hide()) + }, c.prototype.show = function() { + var b = a.Event("show.bs." + this.type); + if (this.hasContent() && this.enabled) { + this.$element.trigger(b); + var d = a.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]); + if (b.isDefaultPrevented() || !d) { + return; + } + var e = this, f = this.tip(), g = this.getUID(this.type); + this.setContent(), f.attr("id", g), this.$element.attr("aria-describedby", g), this.options.animation && f.addClass("fade"); + var h = "function" == typeof this.options.placement ? this.options.placement.call(this, f[0], this.$element[0]) : this.options.placement, i = /\s?auto?\s?/i, j = i.test(h); + j && (h = h.replace(i, "") || "top"), f.detach().css({ + top: 0, + left: 0, + display: "block" + }).addClass(h).data("bs." + this.type, this), this.options.container ? f.appendTo(this.options.container) : f.insertAfter(this.$element), this.$element.trigger("inserted.bs." + this.type); + var k = this.getPosition(), l = f[0].offsetWidth, m = f[0].offsetHeight; + if (j) { + var n = h, o = this.getPosition(this.$viewport); + h = "bottom" == h && k.bottom + m > o.bottom ? "top" : "top" == h && k.top - m < o.top ? "bottom" : "right" == h && k.right + l > o.width ? "left" : "left" == h && k.left - l < o.left ? "right" : h, f.removeClass(n).addClass(h) + } + var p = this.getCalculatedOffset(h, k, l, m); + this.applyPlacement(p, h); + var q = function() { + var a = e.hoverState; + e.$element.trigger("shown.bs." + e.type), e.hoverState = null, "out" == a && e.leave(e) + }; + a.support.transition && this.$tip.hasClass("fade") ? f.one("bsTransitionEnd", q).emulateTransitionEnd(c.TRANSITION_DURATION) : q() + } + }, c.prototype.applyPlacement = function(b, c) { + var d = this.tip(), e = d[0].offsetWidth, f = d[0].offsetHeight, g = parseInt(d.css("margin-top"), 10), h = parseInt(d.css("margin-left"), 10); + isNaN(g) && (g = 0), isNaN(h) && (h = 0), b.top += g, b.left += h, a.offset.setOffset(d[0], a.extend({ + using: function(a) { + d.css({ + top: Math.round(a.top), + left: Math.round(a.left) + }) + } + }, b), 0), d.addClass("in"); + var i = d[0].offsetWidth, j = d[0].offsetHeight; + "top" == c && j != f && (b.top = b.top + f - j); + var k = this.getViewportAdjustedDelta(c, b, i, j); + k.left ? b.left += k.left : b.top += k.top; + var l = /top|bottom/.test(c), m = l ? 2 * k.left - e + i : 2 * k.top - f + j, n = l ? "offsetWidth" : "offsetHeight"; + d.offset(b), this.replaceArrow(m, d[0][n], l) + }, c.prototype.replaceArrow = function(a, b, c) {this.arrow().css(c ? "left" : "top", 50 * (1 - a / b) + "%").css(c ? "top" : "left", "")}, c.prototype.setContent = function() { + var a = this.tip(), b = this.getTitle(); + a.find(".tooltip-inner")[this.options.html ? "html" : "text"](b), a.removeClass("fade in top bottom left right") + }, c.prototype.hide = function(b) { + function d() {"in" != e.hoverState && f.detach(), e.$element.removeAttr("aria-describedby").trigger("hidden.bs." + e.type), b && b()} + + var e = this, f = a(this.$tip), g = a.Event("hide.bs." + this.type); + return this.$element.trigger(g), g.isDefaultPrevented() ? void 0 : (f.removeClass("in"), a.support.transition && f.hasClass("fade") ? f.one("bsTransitionEnd", d).emulateTransitionEnd(c.TRANSITION_DURATION) : d(), this.hoverState = null, this) + }, c.prototype.fixTitle = function() { + var a = this.$element; + (a.attr("title") || "string" != typeof a.attr("data-original-title")) && a.attr("data-original-title", a.attr("title") || "").attr("title", "") + }, c.prototype.hasContent = function() {return this.getTitle()}, c.prototype.getPosition = function(b) { + b = b || this.$element; + var c = b[0], d = "BODY" == c.tagName, e = c.getBoundingClientRect(); + null == e.width && (e = a.extend({}, e, { width: e.right - e.left, height: e.bottom - e.top })); + var f = d ? { + top: 0, + left: 0 + } : b.offset(), g = { scroll: d ? document.documentElement.scrollTop || document.body.scrollTop : b.scrollTop() }, h = d ? { + width: a(window).width(), + height: a(window).height() + } : null; + return a.extend({}, e, g, h, f) + }, c.prototype.getCalculatedOffset = function(a, b, c, d) { + return "bottom" == a ? { + top: b.top + b.height, + left: b.left + b.width / 2 - c / 2 + } : "top" == a ? { top: b.top - d, left: b.left + b.width / 2 - c / 2 } : "left" == a ? { + top: b.top + b.height / 2 - d / 2, + left: b.left - c + } : { top: b.top + b.height / 2 - d / 2, left: b.left + b.width } + }, c.prototype.getViewportAdjustedDelta = function(a, b, c, d) { + var e = { top: 0, left: 0 }; + if (!this.$viewport) { + return e; + } + var f = this.options.viewport && this.options.viewport.padding || 0, g = this.getPosition(this.$viewport); + if (/right|left/.test(a)) { + var h = b.top - f - g.scroll, i = b.top + f - g.scroll + d; + h < g.top ? e.top = g.top - h : i > g.top + g.height && (e.top = g.top + g.height - i) + } else { + var j = b.left - f, k = b.left + f + c; + j < g.left ? e.left = g.left - j : k > g.right && (e.left = g.left + g.width - k) + } + return e + }, c.prototype.getTitle = function() { + var a, b = this.$element, c = this.options; + return a = b.attr("data-original-title") || ("function" == typeof c.title ? c.title.call(b[0]) : c.title) + }, c.prototype.getUID = function(a) { + do { + a += ~~(1e6 * Math.random()); + } while(document.getElementById(a)); + return a + }, c.prototype.tip = function() { + if (!this.$tip && (this.$tip = a(this.options.template), 1 != this.$tip.length)) { + throw new Error(this.type + " `template` option must consist of exactly 1 top-level element!"); + } + return this.$tip + }, c.prototype.arrow = function() {return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")}, c.prototype.enable = function() {this.enabled = !0}, c.prototype.disable = function() {this.enabled = !1}, c.prototype.toggleEnabled = function() {this.enabled = !this.enabled}, c.prototype.toggle = function(b) { + var c = this; + b && (c = a(b.currentTarget).data("bs." + this.type), c || (c = new this.constructor(b.currentTarget, this.getDelegateOptions()), a(b.currentTarget).data("bs." + this.type, c))), b ? (c.inState.click = !c.inState.click, c.isInStateTrue() ? c.enter(c) : c.leave(c)) : c.tip().hasClass("in") ? c.leave(c) : c.enter(c) + }, c.prototype.destroy = function() { + var a = this; + clearTimeout(this.timeout), this.hide(function() {a.$element.off("." + a.type).removeData("bs." + a.type), a.$tip && a.$tip.detach(), a.$tip = null, a.$arrow = null, a.$viewport = null}) + }; + var d = a.fn.tooltip; + a.fn.tooltip = b, a.fn.tooltip.Constructor = c, a.fn.tooltip.noConflict = function() {return a.fn.tooltip = d, this} +}(jQuery), +function(a) { + "use strict"; + function b(b) { + return this.each(function() { + var d = a(this), e = d.data("bs.popover"), f = "object" == typeof b && b; + (e || !/destroy|hide/.test(b)) && (e || d.data("bs.popover", e = new c(this, f)), "string" == typeof b && e[b]()) + }) + } + + var c = function(a, b) {this.init("popover", a, b)}; + if (!a.fn.tooltip) { + throw new Error("Popover requires tooltip.js"); + } + c.VERSION = "3.3.5", c.DEFAULTS = a.extend({}, a.fn.tooltip.Constructor.DEFAULTS, { + placement: "right", + trigger: "click", + content: "", + template: '' + }), c.prototype = a.extend({}, a.fn.tooltip.Constructor.prototype), c.prototype.constructor = c, c.prototype.getDefaults = function() {return c.DEFAULTS}, c.prototype.setContent = function() { + var a = this.tip(), b = this.getTitle(), c = this.getContent(); + a.find(".popover-title")[this.options.html ? "html" : "text"](b), a.find(".popover-content").children().detach().end()[this.options.html ? "string" == typeof c ? "html" : "append" : "text"](c), a.removeClass("fade top bottom left right in"), a.find(".popover-title").html() || a.find(".popover-title").hide() + }, c.prototype.hasContent = function() {return this.getTitle() || this.getContent()}, c.prototype.getContent = function() { + var a = this.$element, b = this.options; + return a.attr("data-content") || ("function" == typeof b.content ? b.content.call(a[0]) : b.content) + }, c.prototype.arrow = function() {return this.$arrow = this.$arrow || this.tip().find(".arrow")}; + var d = a.fn.popover; + a.fn.popover = b, a.fn.popover.Constructor = c, a.fn.popover.noConflict = function() {return a.fn.popover = d, this} +}(jQuery), +function(a) { + "use strict"; + function b(c, d) {this.$body = a(document.body), this.$scrollElement = a(a(c).is(document.body) ? window : c), this.options = a.extend({}, b.DEFAULTS, d), this.selector = (this.options.target || "") + " .nav li > a", this.offsets = [], this.targets = [], this.activeTarget = null, this.scrollHeight = 0, this.$scrollElement.on("scroll.bs.scrollspy", a.proxy(this.process, this)), this.refresh(), this.process()} + + function c(c) { + return this.each(function() { + var d = a(this), e = d.data("bs.scrollspy"), f = "object" == typeof c && c; + e || d.data("bs.scrollspy", e = new b(this, f)), "string" == typeof c && e[c]() + }) + } + + b.VERSION = "3.3.5", b.DEFAULTS = { offset: 10 }, b.prototype.getScrollHeight = function() {return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)}, b.prototype.refresh = function() { + var b = this, c = "offset", d = 0; + this.offsets = [], this.targets = [], this.scrollHeight = this.getScrollHeight(), a.isWindow(this.$scrollElement[0]) || (c = "position", d = this.$scrollElement.scrollTop()), this.$body.find(this.selector).map(function() { + var b = a(this), e = b.data("target") || b.attr("href"), f = /^#./.test(e) && a(e); + return f && f.length && f.is(":visible") && [[f[c]().top + d, e]] || null + }).sort(function(a, b) {return a[0] - b[0]}).each(function() {b.offsets.push(this[0]), b.targets.push(this[1])}) + }, b.prototype.process = function() { + var a, b = this.$scrollElement.scrollTop() + this.options.offset, c = this.getScrollHeight(), d = this.options.offset + c - this.$scrollElement.height(), e = this.offsets, f = this.targets, g = this.activeTarget; + if (this.scrollHeight != c && this.refresh(), b >= d) { + return g != (a = f[f.length - 1]) && this.activate(a); + } + if (g && b < e[0]) { + return this.activeTarget = null, this.clear(); + } + for(a = e.length; a--;) { + g != f[a] && b >= e[a] && (void 0 === e[a + 1] || b < e[a + 1]) && this.activate(f[a]) + } + }, b.prototype.activate = function(b) { + this.activeTarget = b, this.clear(); + var c = this.selector + '[data-target="' + b + '"],' + this.selector + '[href="' + b + '"]', d = a(c).parents("li").addClass("active"); + d.parent(".dropdown-menu").length && (d = d.closest("li.dropdown").addClass("active")), + d.trigger("activate.bs.scrollspy") + }, b.prototype.clear = function() {a(this.selector).parentsUntil(this.options.target, ".active").removeClass("active")}; + var d = a.fn.scrollspy; + a.fn.scrollspy = c, a.fn.scrollspy.Constructor = b, a.fn.scrollspy.noConflict = function() {return a.fn.scrollspy = d, this}, a(window).on("load.bs.scrollspy.data-api", function() { + a('[data-spy="scroll"]').each(function() { + var b = a(this); + c.call(b, b.data()) + }) + }) +}(jQuery), +function(a) { + "use strict"; + function b(b) { + return this.each(function() { + var d = a(this), e = d.data("bs.tab"); + e || d.data("bs.tab", e = new c(this)), "string" == typeof b && e[b]() + }) + } + + var c = function(b) {this.element = a(b)}; + c.VERSION = "3.3.5", c.TRANSITION_DURATION = 150, c.prototype.show = function() { + var b = this.element, c = b.closest("ul:not(.dropdown-menu)"), d = b.data("target"); + if (d || (d = b.attr("href"), d = d && d.replace(/.*(?=#[^\s]*$)/, "")), !b.parent("li").hasClass("active")) { + var e = c.find(".active:last a"), f = a.Event("hide.bs.tab", { relatedTarget: b[0] }), g = a.Event("show.bs.tab", { relatedTarget: e[0] }); + if (e.trigger(f), b.trigger(g), !g.isDefaultPrevented() && !f.isDefaultPrevented()) { + var h = a(d); + this.activate(b.closest("li"), c), this.activate(h, h.parent(), function() { + e.trigger({ + type: "hidden.bs.tab", + relatedTarget: b[0] + }), b.trigger({ type: "shown.bs.tab", relatedTarget: e[0] }) + }) + } + } + }, c.prototype.activate = function(b, d, e) { + function f() {g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded", !1), b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded", !0), h ? (b[0].offsetWidth, b.addClass("in")) : b.removeClass("fade"), b.parent(".dropdown-menu").length && b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded", !0), e && e()} + + var g = d.find("> .active"), h = e && a.support.transition && (g.length && g.hasClass("fade") || !!d.find("> .fade").length); + g.length && h ? g.one("bsTransitionEnd", f).emulateTransitionEnd(c.TRANSITION_DURATION) : f(), g.removeClass("in") + }; + var d = a.fn.tab; + a.fn.tab = b, a.fn.tab.Constructor = c, a.fn.tab.noConflict = function() {return a.fn.tab = d, this}; + var e = function(c) {c.preventDefault(), b.call(a(this), "show")}; + a(document).on("click.bs.tab.data-api", '[data-toggle="tab"]', e).on("click.bs.tab.data-api", '[data-toggle="pill"]', e) +}(jQuery), +function(a) { + "use strict"; + function b(b) { + return this.each(function() { + var d = a(this), e = d.data("bs.affix"), f = "object" == typeof b && b; + e || d.data("bs.affix", e = new c(this, f)), "string" == typeof b && e[b]() + }) + } + + var c = function(b, d) {this.options = a.extend({}, c.DEFAULTS, d), this.$target = a(this.options.target).on("scroll.bs.affix.data-api", a.proxy(this.checkPosition, this)).on("click.bs.affix.data-api", a.proxy(this.checkPositionWithEventLoop, this)), this.$element = a(b), this.affixed = null, this.unpin = null, this.pinnedOffset = null, this.checkPosition()}; + c.VERSION = "3.3.5", c.RESET = "affix affix-top affix-bottom", c.DEFAULTS = { + offset: 0, + target: window + }, c.prototype.getState = function(a, b, c, d) { + var e = this.$target.scrollTop(), f = this.$element.offset(), g = this.$target.height(); + if (null != c && "top" == this.affixed) { + return c > e ? "top" : !1; + } + if ("bottom" == this.affixed) { + return null != c ? e + this.unpin <= f.top ? !1 : "bottom" : a - d >= e + g ? !1 : "bottom"; + } + var h = null == this.affixed, i = h ? e : f.top, j = h ? g : b; + return null != c && c >= e ? "top" : null != d && i + j >= a - d ? "bottom" : !1 + }, c.prototype.getPinnedOffset = function() { + if (this.pinnedOffset) { + return this.pinnedOffset; + } + this.$element.removeClass(c.RESET).addClass("affix"); + var a = this.$target.scrollTop(), b = this.$element.offset(); + return this.pinnedOffset = b.top - a + }, c.prototype.checkPositionWithEventLoop = function() {setTimeout(a.proxy(this.checkPosition, this), 1)}, c.prototype.checkPosition = function() { + if (this.$element.is(":visible")) { + var b = this.$element.height(), d = this.options.offset, e = d.top, f = d.bottom, g = Math.max(a(document).height(), a(document.body).height()); + "object" != typeof d && (f = e = d), "function" == typeof e && (e = d.top(this.$element)), "function" == typeof f && (f = d.bottom(this.$element)); + var h = this.getState(g, b, e, f); + if (this.affixed != h) { + null != this.unpin && this.$element.css("top", ""); + var i = "affix" + (h ? "-" + h : ""), j = a.Event(i + ".bs.affix"); + if (this.$element.trigger(j), j.isDefaultPrevented()) { + return; + } + this.affixed = h, this.unpin = "bottom" == h ? this.getPinnedOffset() : null, this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix", "affixed") + ".bs.affix") + } + "bottom" == h && this.$element.offset({ top: g - b - f }) + } + }; + var d = a.fn.affix; + a.fn.affix = b, a.fn.affix.Constructor = c, a.fn.affix.noConflict = function() {return a.fn.affix = d, this}, a(window).on("load", function() { + a('[data-spy="affix"]').each(function() { + var c = a(this), d = c.data(); + d.offset = d.offset || {}, null != d.offsetBottom && (d.offset.bottom = d.offsetBottom), null != d.offsetTop && (d.offset.top = d.offsetTop), b.call(c, d) + }) + }) +}(jQuery); diff --git a/tray/js/sample/jquery-1.11.3.min.js b/tray/js/sample/jquery-1.11.3.min.js new file mode 100644 index 0000000..d273a7f --- /dev/null +++ b/tray/js/sample/jquery-1.11.3.min.js @@ -0,0 +1,2832 @@ +/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ +!function(a, b) { + "object" == typeof module && "object" == typeof module.exports ? module.exports = a.document ? b(a, !0) : function(a) { + if (!a.document) { + throw new Error("jQuery requires a window with a document"); + } + return b(a) + } : b(a) +}("undefined" != typeof window ? window : this, function(a, b) { + var c = [], d = c.slice, e = c.concat, f = c.push, g = c.indexOf, h = {}, i = h.toString, j = h.hasOwnProperty, k = {}, l = "1.11.3", m = function(a, b) {return new m.fn.init(a, b)}, n = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, o = /^-ms-/, p = /-([\da-z])/gi, q = function(a, b) {return b.toUpperCase()}; + m.fn = m.prototype = { + jquery: l, + constructor: m, + selector: "", + length: 0, + toArray: function() {return d.call(this)}, + get: function(a) {return null != a ? 0 > a ? this[a + this.length] : this[a] : d.call(this)}, + pushStack: function(a) { + var b = m.merge(this.constructor(), a); + return b.prevObject = this, b.context = this.context, b + }, + each: function(a, b) {return m.each(this, a, b)}, + map: function(a) {return this.pushStack(m.map(this, function(b, c) {return a.call(b, c, b)}))}, + slice: function() {return this.pushStack(d.apply(this, arguments))}, + first: function() {return this.eq(0)}, + last: function() {return this.eq(-1)}, + eq: function(a) { + var b = this.length, c = +a + (0 > a ? b : 0); + return this.pushStack(c >= 0 && b > c ? [this[c]] : []) + }, + end: function() {return this.prevObject || this.constructor(null)}, + push: f, + sort: c.sort, + splice: c.splice + }, m.extend = m.fn.extend = function() { + var a, b, c, d, e, f, g = arguments[0] || {}, h = 1, i = arguments.length, j = !1; + for("boolean" == typeof g && (j = g, g = arguments[h] || {}, h++), "object" == typeof g || m.isFunction(g) || (g = {}), h === i && (g = this, h--); i > h; h++) { + if (null != (e = arguments[h])) { + for(d in e) { + a = g[d], c = e[d], g !== c && (j && c && (m.isPlainObject(c) || (b = m.isArray(c))) ? (b ? (b = !1, f = a && m.isArray(a) ? a : []) : f = a && m.isPlainObject(a) ? a : {}, g[d] = m.extend(j, f, c)) : void 0 !== c && (g[d] = c)); + } + } + } + return g + }, m.extend({ + expando: "jQuery" + (l + Math.random()).replace(/\D/g, ""), + isReady: !0, + error: function(a) {throw new Error(a)}, + noop: function() {}, + isFunction: function(a) {return "function" === m.type(a)}, + isArray: Array.isArray || function(a) {return "array" === m.type(a)}, + isWindow: function(a) {return null != a && a == a.window}, + isNumeric: function(a) {return !m.isArray(a) && a - parseFloat(a) + 1 >= 0}, + isEmptyObject: function(a) { + var b; + for(b in a) { + return !1; + } + return !0 + }, + isPlainObject: function(a) { + var b; + if (!a || "object" !== m.type(a) || a.nodeType || m.isWindow(a)) { + return !1; + } + try { + if (a.constructor && !j.call(a, "constructor") && !j.call(a.constructor.prototype, "isPrototypeOf")) { + return !1 + } + } + catch(c) {return !1} + if (k.ownLast) { + for(b in a) { + return j.call(a, b); + } + } + for(b in a) { + ; + } + return void 0 === b || j.call(a, b) + }, + type: function(a) {return null == a ? a + "" : "object" == typeof a || "function" == typeof a ? h[i.call(a)] || "object" : typeof a}, + globalEval: function(b) {b && m.trim(b) && (a.execScript || function(b) {a.eval.call(a, b)})(b)}, + camelCase: function(a) {return a.replace(o, "ms-").replace(p, q)}, + nodeName: function(a, b) {return a.nodeName && a.nodeName.toLowerCase() === b.toLowerCase()}, + each: function(a, b, c) { + var d, e = 0, f = a.length, g = r(a); + if (c) { + if (g) { + for(; f > e; e++) { + if (d = b.apply(a[e], c), d === !1) { + break + } + } + } else { + for(e in a) { + if (d = b.apply(a[e], c), d === !1) { + break + } + } + } + } else if (g) { + for(; f > e; e++) { + if (d = b.call(a[e], e, a[e]), d === !1) { + break + } + } + } else { + for(e in a) { + if (d = b.call(a[e], e, a[e]), d === !1) { + break; + } + } + } + return a + }, + trim: function(a) {return null == a ? "" : (a + "").replace(n, "")}, + makeArray: function(a, b) { + var c = b || []; + return null != a && (r(Object(a)) ? m.merge(c, "string" == typeof a ? [a] : a) : f.call(c, a)), c + }, + inArray: function(a, b, c) { + var d; + if (b) { + if (g) { + return g.call(b, a, c); + } + for(d = b.length, c = c ? 0 > c ? Math.max(0, d + c) : c : 0; d > c; c++) { + if (c in b && b[c] === a) { + return c + } + } + } + return -1 + }, + merge: function(a, b) { + var c = +b.length, d = 0, e = a.length; + while(c > d) { + a[e++] = b[d++]; + } + if (c !== c) { + while(void 0 !== b[d]) { + a[e++] = b[d++]; + } + } + return a.length = e, a + }, + grep: function(a, b, c) { + for(var d, e = [], f = 0, g = a.length, h = !c; g > f; f++) { + d = !b(a[f], f), d !== h && e.push(a[f]); + } + return e + }, + map: function(a, b, c) { + var d, f = 0, g = a.length, h = r(a), i = []; + if (h) { + for(; g > f; f++) { + d = b(a[f], f, c), null != d && i.push(d); + } + } else { + for(f in a) { + d = b(a[f], f, c), null != d && i.push(d); + } + } + return e.apply([], i) + }, + guid: 1, + proxy: function(a, b) { + var c, e, f; + return "string" == typeof b && (f = a[b], b = a, a = f), m.isFunction(a) ? (c = d.call(arguments, 2), e = function() {return a.apply(b || this, c.concat(d.call(arguments)))}, e.guid = a.guid = a.guid || m.guid++, e) : void 0 + }, + now: function() {return +new Date}, + support: k + }), m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(a, b) {h["[object " + b + "]"] = b.toLowerCase()}); + function r(a) { + var b = "length"in a && a.length, c = m.type(a); + return "function" === c || m.isWindow(a) ? !1 : 1 === a.nodeType && b ? !0 : "array" === c || 0 === b || "number" == typeof b && b > 0 && b - 1 in a + } + + var s = function(a) { + var b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u = "sizzle" + 1 * new Date, v = a.document, w = 0, x = 0, y = ha(), z = ha(), A = ha(), B = function(a, b) {return a === b && (l = !0), 0}, C = 1 << 31, D = {}.hasOwnProperty, E = [], F = E.pop, G = E.push, H = E.push, I = E.slice, J = function(a, b) { + for(var c = 0, d = a.length; d > c; c++) { + if (a[c] === b) { + return c; + } + } + return -1 + }, K = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", L = "[\\x20\\t\\r\\n\\f]", M = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", N = M.replace("w", "w#"), O = "\\[" + L + "*(" + M + ")(?:" + L + "*([*^$|!~]?=)" + L + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + N + "))|)" + L + "*\\]", P = ":(" + M + ")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|" + O + ")*)|.*)\\)|)", Q = new RegExp(L + "+", "g"), R = new RegExp("^" + L + "+|((?:^|[^\\\\])(?:\\\\.)*)" + L + "+$", "g"), S = new RegExp("^" + L + "*," + L + "*"), T = new RegExp("^" + L + "*([>+~]|" + L + ")" + L + "*"), U = new RegExp("=" + L + "*([^\\]'\"]*?)" + L + "*\\]", "g"), V = new RegExp(P), W = new RegExp("^" + N + "$"), X = { + ID: new RegExp("^#(" + M + ")"), + CLASS: new RegExp("^\\.(" + M + ")"), + TAG: new RegExp("^(" + M.replace("w", "w*") + ")"), + ATTR: new RegExp("^" + O), + PSEUDO: new RegExp("^" + P), + CHILD: new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + L + "*(even|odd|(([+-]|)(\\d*)n|)" + L + "*(?:([+-]|)" + L + "*(\\d+)|))" + L + "*\\)|)", "i"), + bool: new RegExp("^(?:" + K + ")$", "i"), + needsContext: new RegExp("^" + L + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + L + "*((?:-\\d)?\\d*)" + L + "*\\)|)(?=[^-]|$)", "i") + }, Y = /^(?:input|select|textarea|button)$/i, Z = /^h\d$/i, $ = /^[^{]+\{\s*\[native \w/, _ = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, aa = /[+~]/, ba = /'|\\/g, ca = new RegExp("\\\\([\\da-f]{1,6}" + L + "?|(" + L + ")|.)", "ig"), da = function(a, b, c) { + var d = "0x" + b - 65536; + return d !== d || c ? b : 0 > d ? String.fromCharCode(d + 65536) : String.fromCharCode(d >> 10 | 55296, 1023 & d | 56320) + }, ea = function() {m()}; + try {H.apply(E = I.call(v.childNodes), v.childNodes), E[v.childNodes.length].nodeType} + catch(fa) { + H = { + apply: E.length ? function(a, b) {G.apply(a, I.call(b))} : function(a, b) { + var c = a.length, d = 0; + while(a[c++] = b[d++]) { + ; + } + a.length = c - 1 + } + } + } + function ga(a, b, d, e) { + var f, h, j, k, l, o, r, s, w, x; + if ((b ? b.ownerDocument || b : v) !== n && m(b), b = b || n, d = d || [], k = b.nodeType, "string" != typeof a || !a || 1 !== k && 9 !== k && 11 !== k) { + return d; + } + if (!e && p) { + if (11 !== k && (f = _.exec(a))) { + if (j = f[1]) { + if (9 === k) { + if (h = b.getElementById(j), !h || !h.parentNode) { + return d; + } + if (h.id === j) { + return d.push(h), d + } + } else if (b.ownerDocument && (h = b.ownerDocument.getElementById(j)) && t(b, h) && h.id === j) { + return d.push(h), d + } + } else { + if (f[2]) { + return H.apply(d, b.getElementsByTagName(a)), d; + } + if ((j = f[3]) && c.getElementsByClassName) { + return H.apply(d, b.getElementsByClassName(j)), d + } + } + } + if (c.qsa && (!q || !q.test(a))) { + if (s = r = u, w = b, x = 1 !== k && a, 1 === k && "object" !== b.nodeName.toLowerCase()) { + o = g(a), (r = b.getAttribute("id")) ? s = r.replace(ba, "\\$&") : b.setAttribute("id", s), s = "[id='" + s + "'] ", l = o.length; + while(l--) { + o[l] = s + ra(o[l]); + } + w = aa.test(a) && pa(b.parentNode) || b, x = o.join(",") + } + if (x) { + try {return H.apply(d, w.querySelectorAll(x)), d} + catch(y) {} + finally {r || b.removeAttribute("id")} + } + } + } + return i(a.replace(R, "$1"), b, d, e) + } + + function ha() { + var a = []; + + function b(c, e) {return a.push(c + " ") > d.cacheLength && delete b[a.shift()], b[c + " "] = e} + + return b + } + + function ia(a) {return a[u] = !0, a} + + function ja(a) { + var b = n.createElement("div"); + try {return !!a(b)} + catch(c) {return !1} + finally {b.parentNode && b.parentNode.removeChild(b), b = null} + } + + function ka(a, b) { + var c = a.split("|"), e = a.length; + while(e--) { + d.attrHandle[c[e]] = b + } + } + + function la(a, b) { + var c = b && a, d = c && 1 === a.nodeType && 1 === b.nodeType && (~b.sourceIndex || C) - (~a.sourceIndex || C); + if (d) { + return d; + } + if (c) { + while(c = c.nextSibling) { + if (c === b) { + return -1; + } + } + } + return a ? 1 : -1 + } + + function ma(a) { + return function(b) { + var c = b.nodeName.toLowerCase(); + return "input" === c && b.type === a + } + } + + function na(a) { + return function(b) { + var c = b.nodeName.toLowerCase(); + return ("input" === c || "button" === c) && b.type === a + } + } + + function oa(a) { + return ia(function(b) { + return b = +b, ia(function(c, d) { + var e, f = a([], c.length, b), g = f.length; + while(g--) { + c[e = f[g]] && (c[e] = !(d[e] = c[e])) + } + }) + }) + } + + function pa(a) {return a && "undefined" != typeof a.getElementsByTagName && a} + + c = ga.support = {}, f = ga.isXML = function(a) { + var b = a && (a.ownerDocument || a).documentElement; + return b ? "HTML" !== b.nodeName : !1 + }, m = ga.setDocument = function(a) { + var b, e, g = a ? a.ownerDocument || a : v; + return g !== n && 9 === g.nodeType && g.documentElement ? (n = g, o = g.documentElement, e = g.defaultView, e && e !== e.top && (e.addEventListener ? e.addEventListener("unload", ea, !1) : e.attachEvent && e.attachEvent("onunload", ea)), p = !f(g), c.attributes = ja(function(a) {return a.className = "i", !a.getAttribute("className")}), c.getElementsByTagName = ja(function(a) {return a.appendChild(g.createComment("")), !a.getElementsByTagName("*").length}), c.getElementsByClassName = $.test(g.getElementsByClassName), c.getById = ja(function(a) {return o.appendChild(a).id = u, !g.getElementsByName || !g.getElementsByName(u).length}), c.getById ? (d.find.ID = function(a, b) { + if ("undefined" != typeof b.getElementById && p) { + var c = b.getElementById(a); + return c && c.parentNode ? [c] : [] + } + }, d.filter.ID = function(a) { + var b = a.replace(ca, da); + return function(a) {return a.getAttribute("id") === b} + }) : (delete d.find.ID, d.filter.ID = function(a) { + var b = a.replace(ca, da); + return function(a) { + var c = "undefined" != typeof a.getAttributeNode && a.getAttributeNode("id"); + return c && c.value === b + } + }), d.find.TAG = c.getElementsByTagName ? function(a, b) {return "undefined" != typeof b.getElementsByTagName ? b.getElementsByTagName(a) : c.qsa ? b.querySelectorAll(a) : void 0} : function(a, b) { + var c, d = [], e = 0, f = b.getElementsByTagName(a); + if ("*" === a) { + while(c = f[e++]) { + 1 === c.nodeType && d.push(c); + } + return d + } + return f + }, d.find.CLASS = c.getElementsByClassName && function(a, b) {return p ? b.getElementsByClassName(a) : void 0}, r = [], q = [], (c.qsa = $.test(g.querySelectorAll)) && (ja(function(a) {o.appendChild(a).innerHTML = "", a.querySelectorAll("[msallowcapture^='']").length && q.push("[*^$]=" + L + "*(?:''|\"\")"), a.querySelectorAll("[selected]").length || q.push("\\[" + L + "*(?:value|" + K + ")"), a.querySelectorAll("[id~=" + u + "-]").length || q.push("~="), a.querySelectorAll(":checked").length || q.push(":checked"), a.querySelectorAll("a#" + u + "+*").length || q.push(".#.+[+~]")}), ja(function(a) { + var b = g.createElement("input"); + b.setAttribute("type", "hidden"), a.appendChild(b).setAttribute("name", "D"), a.querySelectorAll("[name=d]").length && q.push("name" + L + "*[*^$|!~]?="), a.querySelectorAll(":enabled").length || q.push(":enabled", ":disabled"), a.querySelectorAll("*,:x"), q.push(",.*:") + })), (c.matchesSelector = $.test(s = o.matches || o.webkitMatchesSelector || o.mozMatchesSelector || o.oMatchesSelector || o.msMatchesSelector)) && ja(function(a) {c.disconnectedMatch = s.call(a, "div"), s.call(a, "[s!='']:x"), r.push("!=", P)}), q = q.length && new RegExp(q.join("|")), r = r.length && new RegExp(r.join("|")), b = $.test(o.compareDocumentPosition), t = b || $.test(o.contains) ? function(a, b) { + var c = 9 === a.nodeType ? a.documentElement : a, d = b && b.parentNode; + return a === d || !(!d || 1 !== d.nodeType || !(c.contains ? c.contains(d) : a.compareDocumentPosition && 16 & a.compareDocumentPosition(d))) + } : function(a, b) { + if (b) { + while(b = b.parentNode) { + if (b === a) { + return !0; + } + } + } + return !1 + }, B = b ? function(a, b) { + if (a === b) { + return l = !0, 0; + } + var d = !a.compareDocumentPosition - !b.compareDocumentPosition; + return d ? d : (d = (a.ownerDocument || a) === (b.ownerDocument || b) ? a.compareDocumentPosition(b) : 1, 1 & d || !c.sortDetached && b.compareDocumentPosition(a) === d ? a === g || a.ownerDocument === v && t(v, a) ? -1 : b === g || b.ownerDocument === v && t(v, b) ? 1 : k ? J(k, a) - J(k, b) : 0 : 4 & d ? -1 : 1) + } : function(a, b) { + if (a === b) { + return l = !0, 0; + } + var c, d = 0, e = a.parentNode, f = b.parentNode, h = [a], i = [b]; + if (!e || !f) { + return a === g ? -1 : b === g ? 1 : e ? -1 : f ? 1 : k ? J(k, a) - J(k, b) : 0; + } + if (e === f) { + return la(a, b); + } + c = a; + while(c = c.parentNode) { + h.unshift(c); + } + c = b; + while(c = c.parentNode) { + i.unshift(c); + } + while(h[d] === i[d]) { + d++; + } + return d ? la(h[d], i[d]) : h[d] === v ? -1 : i[d] === v ? 1 : 0 + }, g) : n + }, ga.matches = function(a, b) {return ga(a, null, null, b)}, ga.matchesSelector = function(a, b) { + if ((a.ownerDocument || a) !== n && m(a), b = b.replace(U, "='$1']"), !(!c.matchesSelector || !p || r && r.test(b) || q && q.test(b))) { + try { + var d = s.call(a, b); + if (d || c.disconnectedMatch || a.document && 11 !== a.document.nodeType) { + return d + } + } + catch(e) {} + } + return ga(b, n, null, [a]).length > 0 + }, ga.contains = function(a, b) {return (a.ownerDocument || a) !== n && m(a), t(a, b)}, ga.attr = function(a, b) { + (a.ownerDocument || a) !== n && m(a); + var e = d.attrHandle[b.toLowerCase()], f = e && D.call(d.attrHandle, b.toLowerCase()) ? e(a, b, !p) : void 0; + return void 0 !== f ? f : c.attributes || !p ? a.getAttribute(b) : (f = a.getAttributeNode(b)) && f.specified ? f.value : null + }, ga.error = function(a) {throw new Error("Syntax error, unrecognized expression: " + a)}, ga.uniqueSort = function(a) { + var b, d = [], e = 0, f = 0; + if (l = !c.detectDuplicates, k = !c.sortStable && a.slice(0), a.sort(B), l) { + while(b = a[f++]) { + b === a[f] && (e = d.push(f)); + } + while(e--) { + a.splice(d[e], 1) + } + } + return k = null, a + }, e = ga.getText = function(a) { + var b, c = "", d = 0, f = a.nodeType; + if (f) { + if (1 === f || 9 === f || 11 === f) { + if ("string" == typeof a.textContent) { + return a.textContent; + } + for(a = a.firstChild; a; a = a.nextSibling) { + c += e(a) + } + } else if (3 === f || 4 === f) { + return a.nodeValue + } + } else { + while(b = a[d++]) { + c += e(b); + } + } + return c + }, d = ga.selectors = { + cacheLength: 50, + createPseudo: ia, + match: X, + attrHandle: {}, + find: {}, + relative: { + ">": { dir: "parentNode", first: !0 }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: !0 }, + "~": { dir: "previousSibling" } + }, + preFilter: { + ATTR: function(a) {return a[1] = a[1].replace(ca, da), a[3] = (a[3] || a[4] || a[5] || "").replace(ca, da), "~=" === a[2] && (a[3] = " " + a[3] + " "), a.slice(0, 4)}, + CHILD: function(a) {return a[1] = a[1].toLowerCase(), "nth" === a[1].slice(0, 3) ? (a[3] || ga.error(a[0]), a[4] = +(a[4] ? a[5] + (a[6] || 1) : 2 * ("even" === a[3] || "odd" === a[3])), a[5] = +(a[7] + a[8] || "odd" === a[3])) : a[3] && ga.error(a[0]), a}, + PSEUDO: function(a) { + var b, c = !a[6] && a[2]; + return X.CHILD.test(a[0]) ? null : (a[3] ? a[2] = a[4] || a[5] || "" : c && V.test(c) && (b = g(c, !0)) && (b = c.indexOf(")", c.length - b) - c.length) && (a[0] = a[0].slice(0, b), a[2] = c.slice(0, b)), a.slice(0, 3)) + } + }, + filter: { + TAG: function(a) { + var b = a.replace(ca, da).toLowerCase(); + return "*" === a ? function() {return !0} : function(a) {return a.nodeName && a.nodeName.toLowerCase() === b} + }, CLASS: function(a) { + var b = y[a + " "]; + return b || (b = new RegExp("(^|" + L + ")" + a + "(" + L + "|$)")) && y(a, function(a) {return b.test("string" == typeof a.className && a.className || "undefined" != typeof a.getAttribute && a.getAttribute("class") || "")}) + }, ATTR: function(a, b, c) { + return function(d) { + var e = ga.attr(d, a); + return null == e ? "!=" === b : b ? (e += "", "=" === b ? e === c : "!=" === b ? e !== c : "^=" === b ? c && 0 === e.indexOf(c) : "*=" === b ? c && e.indexOf(c) > -1 : "$=" === b ? c && e.slice(-c.length) === c : "~=" === b ? (" " + e.replace(Q, " ") + " ").indexOf(c) > -1 : "|=" === b ? e === c || e.slice(0, c.length + 1) === c + "-" : !1) : !0 + } + }, CHILD: function(a, b, c, d, e) { + var f = "nth" !== a.slice(0, 3), g = "last" !== a.slice(-4), h = "of-type" === b; + return 1 === d && 0 === e ? function(a) {return !!a.parentNode} : function(b, c, i) { + var j, k, l, m, n, o, p = f !== g ? "nextSibling" : "previousSibling", q = b.parentNode, r = h && b.nodeName.toLowerCase(), s = !i && !h; + if (q) { + if (f) { + while(p) { + l = b; + while(l = l[p]) { + if (h ? l.nodeName.toLowerCase() === r : 1 === l.nodeType) { + return !1; + } + } + o = p = "only" === a && !o && "nextSibling" + } + return !0 + } + if (o = [g ? q.firstChild : q.lastChild], g && s) { + k = q[u] || (q[u] = {}), j = k[a] || [], n = j[0] === w && j[1], m = j[0] === w && j[2], l = n && q.childNodes[n]; + while(l = ++n && l && l[p] || (m = n = 0) || o.pop()) { + if (1 === l.nodeType && ++m && l === b) { + k[a] = [w, n, m]; + break + } + } + } else if (s && (j = (b[u] || (b[u] = {}))[a]) && j[0] === w) { + m = j[1]; + } else { + while(l = ++n && l && l[p] || (m = n = 0) || o.pop()) { + if ((h ? l.nodeName.toLowerCase() === r : 1 === l.nodeType) && ++m && (s && ((l[u] || (l[u] = {}))[a] = [w, m]), l === b)) { + break; + } + } + } + return m -= e, m === d || m % d === 0 && m / d >= 0 + } + } + }, PSEUDO: function(a, b) { + var c, e = d.pseudos[a] || d.setFilters[a.toLowerCase()] || ga.error("unsupported pseudo: " + a); + return e[u] ? e(b) : e.length > 1 ? (c = [a, a, "", b], d.setFilters.hasOwnProperty(a.toLowerCase()) ? ia(function(a, c) { + var d, f = e(a, b), g = f.length; + while(g--) { + d = J(a, f[g]), a[d] = !(c[d] = f[g]) + } + }) : function(a) {return e(a, 0, c)}) : e + } + }, + pseudos: { + not: ia(function(a) { + var b = [], c = [], d = h(a.replace(R, "$1")); + return d[u] ? ia(function(a, b, c, e) { + var f, g = d(a, null, e, []), h = a.length; + while(h--) { + (f = g[h]) && (a[h] = !(b[h] = f)) + } + }) : function(a, e, f) {return b[0] = a, d(b, null, f, c), b[0] = null, !c.pop()} + }), + has: ia(function(a) {return function(b) {return ga(a, b).length > 0}}), + contains: ia(function(a) {return a = a.replace(ca, da), function(b) {return (b.textContent || b.innerText || e(b)).indexOf(a) > -1}}), + lang: ia(function(a) { + return W.test(a || "") || ga.error("unsupported lang: " + a), a = a.replace(ca, da).toLowerCase(), function(b) { + var c; + do { + if (c = p ? b.lang : b.getAttribute("xml:lang") || b.getAttribute("lang")) { + return c = c.toLowerCase(), c === a || 0 === c.indexOf(a + "-"); + } + } while((b = b.parentNode) && 1 === b.nodeType); + return !1 + } + }), + target: function(b) { + var c = a.location && a.location.hash; + return c && c.slice(1) === b.id + }, + root: function(a) {return a === o}, + focus: function(a) {return a === n.activeElement && (!n.hasFocus || n.hasFocus()) && !!(a.type || a.href || ~a.tabIndex)}, + enabled: function(a) {return a.disabled === !1}, + disabled: function(a) {return a.disabled === !0}, + checked: function(a) { + var b = a.nodeName.toLowerCase(); + return "input" === b && !!a.checked || "option" === b && !!a.selected + }, + selected: function(a) {return a.parentNode && a.parentNode.selectedIndex, a.selected === !0}, + empty: function(a) { + for(a = a.firstChild; a; a = a.nextSibling) { + if (a.nodeType < 6) { + return !1; + } + } + return !0 + }, + parent: function(a) {return !d.pseudos.empty(a)}, + header: function(a) {return Z.test(a.nodeName)}, + input: function(a) {return Y.test(a.nodeName)}, + button: function(a) { + var b = a.nodeName.toLowerCase(); + return "input" === b && "button" === a.type || "button" === b + }, + text: function(a) { + var b; + return "input" === a.nodeName.toLowerCase() && "text" === a.type && (null == (b = a.getAttribute("type")) || "text" === b.toLowerCase()) + }, + first: oa(function() {return [0]}), + last: oa(function(a, b) {return [b - 1]}), + eq: oa(function(a, b, c) {return [0 > c ? c + b : c]}), + even: oa(function(a, b) { + for(var c = 0; b > c; c += 2) { + a.push(c); + } + return a + }), + odd: oa(function(a, b) { + for(var c = 1; b > c; c += 2) { + a.push(c); + } + return a + }), + lt: oa(function(a, b, c) { + for(var d = 0 > c ? c + b : c; --d >= 0;) { + a.push(d); + } + return a + }), + gt: oa(function(a, b, c) { + for(var d = 0 > c ? c + b : c; ++d < b;) { + a.push(d); + } + return a + }) + } + }, d.pseudos.nth = d.pseudos.eq; + for(b in{ radio: !0, checkbox: !0, file: !0, password: !0, image: !0 }) { + d.pseudos[b] = ma(b); + } + for(b in{ submit: !0, reset: !0 }) { + d.pseudos[b] = na(b); + } + function qa() {} + + qa.prototype = d.filters = d.pseudos, d.setFilters = new qa, g = ga.tokenize = function(a, b) { + var c, e, f, g, h, i, j, k = z[a + " "]; + if (k) { + return b ? 0 : k.slice(0); + } + h = a, i = [], j = d.preFilter; + while(h) { + (!c || (e = S.exec(h))) && (e && (h = h.slice(e[0].length) || h), i.push(f = [])), c = !1, (e = T.exec(h)) && (c = e.shift(), f.push({ + value: c, + type: e[0].replace(R, " ") + }), h = h.slice(c.length)); + for(g in d.filter) { + !(e = X[g].exec(h)) || j[g] && !(e = j[g](e)) || (c = e.shift(), f.push({ + value: c, + type: g, + matches: e + }), h = h.slice(c.length)); + } + if (!c) { + break + } + } + return b ? h.length : h ? ga.error(a) : z(a, i).slice(0) + }; + function ra(a) { + for(var b = 0, c = a.length, d = ""; c > b; b++) { + d += a[b].value; + } + return d + } + + function sa(a, b, c) { + var d = b.dir, e = c && "parentNode" === d, f = x++; + return b.first ? function(b, c, f) { + while(b = b[d]) { + if (1 === b.nodeType || e) { + return a(b, c, f) + } + } + } : function(b, c, g) { + var h, i, j = [w, f]; + if (g) { + while(b = b[d]) { + if ((1 === b.nodeType || e) && a(b, c, g)) { + return !0 + } + } + } else { + while(b = b[d]) { + if (1 === b.nodeType || e) { + if (i = b[u] || (b[u] = {}), (h = i[d]) && h[0] === w && h[1] === f) { + return j[2] = h[2]; + } + if (i[d] = j, j[2] = a(b, c, g)) { + return !0 + } + } + } + } + } + } + + function ta(a) { + return a.length > 1 ? function(b, c, d) { + var e = a.length; + while(e--) { + if (!a[e](b, c, d)) { + return !1; + } + } + return !0 + } : a[0] + } + + function ua(a, b, c) { + for(var d = 0, e = b.length; e > d; d++) { + ga(a, b[d], c); + } + return c + } + + function va(a, b, c, d, e) { + for(var f, g = [], h = 0, i = a.length, j = null != b; i > h; h++) { + (f = a[h]) && (!c || c(f, d, e)) && (g.push(f), j && b.push(h)); + } + return g + } + + function wa(a, b, c, d, e, f) { + return d && !d[u] && (d = wa(d)), e && !e[u] && (e = wa(e, f)), ia(function(f, g, h, i) { + var j, k, l, m = [], n = [], o = g.length, p = f || ua(b || "*", h.nodeType ? [h] : h, []), q = !a || !f && b ? p : va(p, m, a, h, i), r = c ? e || (f ? a : o || d) ? [] : g : q; + if (c && c(q, r, h, i), d) { + j = va(r, n), d(j, [], h, i), k = j.length; + while(k--) { + (l = j[k]) && (r[n[k]] = !(q[n[k]] = l)) + } + } + if (f) { + if (e || a) { + if (e) { + j = [], k = r.length; + while(k--) { + (l = r[k]) && j.push(q[k] = l); + } + e(null, r = [], j, i) + } + k = r.length; + while(k--) { + (l = r[k]) && (j = e ? J(f, l) : m[k]) > -1 && (f[j] = !(g[j] = l)) + } + } + } else { + r = va(r === g ? r.splice(o, r.length) : r), e ? e(null, g, r, i) : H.apply(g, r) + } + }) + } + + function xa(a) { + for(var b, c, e, f = a.length, g = d.relative[a[0].type], h = g || d.relative[" "], i = g ? 1 : 0, k = sa(function(a) {return a === b}, h, !0), l = sa(function(a) {return J(b, a) > -1}, h, !0), m = [function(a, c, d) { + var e = !g && (d || c !== j) || ((b = c).nodeType ? k(a, c, d) : l(a, c, d)); + return b = null, e + }]; f > i; i++) { + if (c = d.relative[a[i].type]) { + m = [sa(ta(m), c)]; + } else { + if (c = d.filter[a[i].type].apply(null, a[i].matches), c[u]) { + for(e = ++i; f > e; e++) { + if (d.relative[a[e].type]) { + break; + } + } + return wa(i > 1 && ta(m), i > 1 && ra(a.slice(0, i - 1).concat({ value: " " === a[i - 2].type ? "*" : "" })).replace(R, "$1"), c, e > i && xa(a.slice(i, e)), f > e && xa(a = a.slice(e)), f > e && ra(a)) + } + m.push(c) + } + } + return ta(m) + } + + function ya(a, b) { + var c = b.length > 0, e = a.length > 0, f = function(f, g, h, i, k) { + var l, m, o, p = 0, q = "0", r = f && [], s = [], t = j, u = f || e && d.find.TAG("*", k), v = w += null == t ? 1 : Math.random() || .1, x = u.length; + for(k && (j = g !== n && g); q !== x && null != (l = u[q]); q++) { + if (e && l) { + m = 0; + while(o = a[m++]) { + if (o(l, g, h)) { + i.push(l); + break + } + } + k && (w = v) + } + c && ((l = !o && l) && p--, f && r.push(l)) + } + if (p += q, c && q !== p) { + m = 0; + while(o = b[m++]) { + o(r, s, g, h); + } + if (f) { + if (p > 0) { + while(q--) { + r[q] || s[q] || (s[q] = F.call(i)); + } + } + s = va(s) + } + H.apply(i, s), k && !f && s.length > 0 && p + b.length > 1 && ga.uniqueSort(i) + } + return k && (w = v, j = t), r + }; + return c ? ia(f) : f + } + + return h = ga.compile = function(a, b) { + var c, d = [], e = [], f = A[a + " "]; + if (!f) { + b || (b = g(a)), c = b.length; + while(c--) { + f = xa(b[c]), f[u] ? d.push(f) : e.push(f); + } + f = A(a, ya(e, d)), f.selector = a + } + return f + }, i = ga.select = function(a, b, e, f) { + var i, j, k, l, m, n = "function" == typeof a && a, o = !f && g(a = n.selector || a); + if (e = e || [], 1 === o.length) { + if (j = o[0] = o[0].slice(0), j.length > 2 && "ID" === (k = j[0]).type && c.getById && 9 === b.nodeType && p && d.relative[j[1].type]) { + if (b = (d.find.ID(k.matches[0].replace(ca, da), b) || [])[0], !b) { + return e; + } + n && (b = b.parentNode), a = a.slice(j.shift().value.length) + } + i = X.needsContext.test(a) ? 0 : j.length; + while(i--) { + if (k = j[i], d.relative[l = k.type]) { + break; + } + if ((m = d.find[l]) && (f = m(k.matches[0].replace(ca, da), aa.test(j[0].type) && pa(b.parentNode) || b))) { + if (j.splice(i, 1), a = f.length && ra(j), !a) { + return H.apply(e, f), e; + } + break + } + } + } + return (n || h(a, o))(f, b, !p, e, aa.test(a) && pa(b.parentNode) || b), e + }, c.sortStable = u.split("").sort(B).join("") === u, c.detectDuplicates = !!l, m(), c.sortDetached = ja(function(a) {return 1 & a.compareDocumentPosition(n.createElement("div"))}), ja(function(a) {return a.innerHTML = "", "#" === a.firstChild.getAttribute("href")}) || ka("type|href|height|width", function(a, b, c) {return c ? void 0 : a.getAttribute(b, "type" === b.toLowerCase() ? 1 : 2)}), c.attributes && ja(function(a) {return a.innerHTML = "", a.firstChild.setAttribute("value", ""), "" === a.firstChild.getAttribute("value")}) || ka("value", function(a, b, c) {return c || "input" !== a.nodeName.toLowerCase() ? void 0 : a.defaultValue}), ja(function(a) {return null == a.getAttribute("disabled")}) || ka(K, function(a, b, c) { + var d; + return c ? void 0 : a[b] === !0 ? b.toLowerCase() : (d = a.getAttributeNode(b)) && d.specified ? d.value : null + }), ga + }(a); + m.find = s, m.expr = s.selectors, m.expr[":"] = m.expr.pseudos, m.unique = s.uniqueSort, m.text = s.getText, m.isXMLDoc = s.isXML, m.contains = s.contains; + var t = m.expr.match.needsContext, u = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, v = /^.[^:#\[\.,]*$/; + + function w(a, b, c) { + if (m.isFunction(b)) { + return m.grep(a, function(a, d) {return !!b.call(a, d, a) !== c}); + } + if (b.nodeType) { + return m.grep(a, function(a) {return a === b !== c}); + } + if ("string" == typeof b) { + if (v.test(b)) { + return m.filter(b, a, c); + } + b = m.filter(b, a) + } + return m.grep(a, function(a) {return m.inArray(a, b) >= 0 !== c}) + } + + m.filter = function(a, b, c) { + var d = b[0]; + return c && (a = ":not(" + a + ")"), 1 === b.length && 1 === d.nodeType ? m.find.matchesSelector(d, a) ? [d] : [] : m.find.matches(a, m.grep(b, function(a) {return 1 === a.nodeType})) + }, m.fn.extend({ + find: function(a) { + var b, c = [], d = this, e = d.length; + if ("string" != typeof a) { + return this.pushStack(m(a).filter(function() { + for(b = 0; e > b; b++) { + if (m.contains(d[b], this)) { + return !0 + } + } + })); + } + for(b = 0; e > b; b++) { + m.find(a, d[b], c); + } + return c = this.pushStack(e > 1 ? m.unique(c) : c), c.selector = this.selector ? this.selector + " " + a : a, c + }, + filter: function(a) {return this.pushStack(w(this, a || [], !1))}, + not: function(a) {return this.pushStack(w(this, a || [], !0))}, + is: function(a) {return !!w(this, "string" == typeof a && t.test(a) ? m(a) : a || [], !1).length} + }); + var x, y = a.document, z = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, A = m.fn.init = function(a, b) { + var c, d; + if (!a) { + return this; + } + if ("string" == typeof a) { + if (c = "<" === a.charAt(0) && ">" === a.charAt(a.length - 1) && a.length >= 3 ? [null, a, null] : z.exec(a), !c || !c[1] && b) { + return !b || b.jquery ? (b || x).find(a) : this.constructor(b).find(a); + } + if (c[1]) { + if (b = b instanceof m ? b[0] : b, m.merge(this, m.parseHTML(c[1], b && b.nodeType ? b.ownerDocument || b : y, !0)), u.test(c[1]) && m.isPlainObject(b)) { + for(c in b) { + m.isFunction(this[c]) ? this[c](b[c]) : this.attr(c, b[c]); + } + } + return this + } + if (d = y.getElementById(c[2]), d && d.parentNode) { + if (d.id !== c[2]) { + return x.find(a); + } + this.length = 1, this[0] = d + } + return this.context = y, this.selector = a, this + } + return a.nodeType ? (this.context = this[0] = a, this.length = 1, this) : m.isFunction(a) ? "undefined" != typeof x.ready ? x.ready(a) : a(m) : (void 0 !== a.selector && (this.selector = a.selector, this.context = a.context), m.makeArray(a, this)) + }; + A.prototype = m.fn, x = m(y); + var B = /^(?:parents|prev(?:Until|All))/, C = { children: !0, contents: !0, next: !0, prev: !0 }; + m.extend({ + dir: function(a, b, c) { + var d = [], e = a[b]; + while(e && 9 !== e.nodeType && (void 0 === c || 1 !== e.nodeType || !m(e).is(c))) { + 1 === e.nodeType && d.push(e), e = e[b]; + } + return d + }, sibling: function(a, b) { + for(var c = []; a; a = a.nextSibling) { + 1 === a.nodeType && a !== b && c.push(a); + } + return c + } + }), m.fn.extend({ + has: function(a) { + var b, c = m(a, this), d = c.length; + return this.filter(function() { + for(b = 0; d > b; b++) { + if (m.contains(this, c[b])) { + return !0 + } + } + }) + }, + closest: function(a, b) { + for(var c, d = 0, e = this.length, f = [], g = t.test(a) || "string" != typeof a ? m(a, b || this.context) : 0; e > d; d++) { + for(c = this[d]; c && c !== b; c = c.parentNode) { + if (c.nodeType < 11 && (g ? g.index(c) > -1 : 1 === c.nodeType && m.find.matchesSelector(c, a))) { + f.push(c); + break + } + } + } + return this.pushStack(f.length > 1 ? m.unique(f) : f) + }, + index: function(a) {return a ? "string" == typeof a ? m.inArray(this[0], m(a)) : m.inArray(a.jquery ? a[0] : a, this) : this[0] && this[0].parentNode ? this.first().prevAll().length : -1}, + add: function(a, b) {return this.pushStack(m.unique(m.merge(this.get(), m(a, b))))}, + addBack: function(a) {return this.add(null == a ? this.prevObject : this.prevObject.filter(a))} + }); + function D(a, b) { + do { + a = a[b]; + } while(a && 1 !== a.nodeType); + return a + } + + m.each({ + parent: function(a) { + var b = a.parentNode; + return b && 11 !== b.nodeType ? b : null + }, + parents: function(a) {return m.dir(a, "parentNode")}, + parentsUntil: function(a, b, c) {return m.dir(a, "parentNode", c)}, + next: function(a) {return D(a, "nextSibling")}, + prev: function(a) {return D(a, "previousSibling")}, + nextAll: function(a) {return m.dir(a, "nextSibling")}, + prevAll: function(a) {return m.dir(a, "previousSibling")}, + nextUntil: function(a, b, c) {return m.dir(a, "nextSibling", c)}, + prevUntil: function(a, b, c) {return m.dir(a, "previousSibling", c)}, + siblings: function(a) {return m.sibling((a.parentNode || {}).firstChild, a)}, + children: function(a) {return m.sibling(a.firstChild)}, + contents: function(a) {return m.nodeName(a, "iframe") ? a.contentDocument || a.contentWindow.document : m.merge([], a.childNodes)} + }, function(a, b) { + m.fn[a] = function(c, d) { + var e = m.map(this, b, c); + return "Until" !== a.slice(-5) && (d = c), d && "string" == typeof d && (e = m.filter(d, e)), this.length > 1 && (C[a] || (e = m.unique(e)), B.test(a) && (e = e.reverse())), this.pushStack(e) + } + }); + var E = /\S+/g, F = {}; + + function G(a) { + var b = F[a] = {}; + return m.each(a.match(E) || [], function(a, c) {b[c] = !0}), b + } + + m.Callbacks = function(a) { + a = "string" == typeof a ? F[a] || G(a) : m.extend({}, a); + var b, c, d, e, f, g, h = [], i = !a.once && [], j = function(l) { + for(c = a.memory && l, d = !0, f = g || 0, g = 0, e = h.length, b = !0; h && e > f; f++) { + if (h[f].apply(l[0], l[1]) === !1 && a.stopOnFalse) { + c = !1; + break + } + } + b = !1, h && (i ? i.length && j(i.shift()) : c ? h = [] : k.disable()) + }, k = { + add: function() { + if (h) { + var d = h.length; + !function f(b) { + m.each(b, function(b, c) { + var d = m.type(c); + "function" === d ? a.unique && k.has(c) || h.push(c) : c && c.length && "string" !== d && f(c) + }) + }(arguments), b ? e = h.length : c && (g = d, j(c)) + } + return this + }, + remove: function() { + return h && m.each(arguments, function(a, c) { + var d; + while((d = m.inArray(c, h, d)) > -1) { + h.splice(d, 1), b && (e >= d && e--, f >= d && f--) + } + }), this + }, + has: function(a) {return a ? m.inArray(a, h) > -1 : !(!h || !h.length)}, + empty: function() {return h = [], e = 0, this}, + disable: function() {return h = i = c = void 0, this}, + disabled: function() {return !h}, + lock: function() {return i = void 0, c || k.disable(), this}, + locked: function() {return !i}, + fireWith: function(a, c) {return !h || d && !i || (c = c || [], c = [a, c.slice ? c.slice() : c], b ? i.push(c) : j(c)), this}, + fire: function() {return k.fireWith(this, arguments), this}, + fired: function() {return !!d} + }; + return k + }, m.extend({ + Deferred: function(a) { + var b = [["resolve", "done", m.Callbacks("once memory"), "resolved"], ["reject", "fail", m.Callbacks("once memory"), "rejected"], ["notify", "progress", m.Callbacks("memory")]], c = "pending", d = { + state: function() {return c}, + always: function() {return e.done(arguments).fail(arguments), this}, + then: function() { + var a = arguments; + return m.Deferred(function(c) { + m.each(b, function(b, f) { + var g = m.isFunction(a[b]) && a[b]; + e[f[1]](function() { + var a = g && g.apply(this, arguments); + a && m.isFunction(a.promise) ? a.promise().done(c.resolve).fail(c.reject).progress(c.notify) : c[f[0] + "With"](this === d ? c.promise() : this, g ? [a] : arguments) + }) + }), a = null + }).promise() + }, + promise: function(a) {return null != a ? m.extend(a, d) : d} + }, e = {}; + return d.pipe = d.then, m.each(b, function(a, f) { + var g = f[2], h = f[3]; + d[f[1]] = g.add, h && g.add(function() {c = h}, b[1 ^ a][2].disable, b[2][2].lock), e[f[0]] = function() {return e[f[0] + "With"](this === e ? d : this, arguments), this}, e[f[0] + "With"] = g.fireWith + }), d.promise(e), a && a.call(e, e), e + }, + when: function(a) { + var b = 0, c = d.call(arguments), e = c.length, f = 1 !== e || a && m.isFunction(a.promise) ? e : 0, g = 1 === f ? a : m.Deferred(), h = function(a, b, c) {return function(e) {b[a] = this, c[a] = arguments.length > 1 ? d.call(arguments) : e, c === i ? g.notifyWith(b, c) : --f || g.resolveWith(b, c)}}, i, j, k; + if (e > 1) { + for(i = new Array(e), j = new Array(e), k = new Array(e); e > b; b++) { + c[b] && m.isFunction(c[b].promise) ? c[b].promise().done(h(b, k, c)).fail(g.reject).progress(h(b, j, i)) : --f; + } + } + return f || g.resolveWith(k, c), g.promise() + } + }); + var H; + m.fn.ready = function(a) {return m.ready.promise().done(a), this}, m.extend({ + isReady: !1, + readyWait: 1, + holdReady: function(a) {a ? m.readyWait++ : m.ready(!0)}, + ready: function(a) { + if (a === !0 ? !--m.readyWait : !m.isReady) { + if (!y.body) { + return setTimeout(m.ready); + } + m.isReady = !0, a !== !0 && --m.readyWait > 0 || (H.resolveWith(y, [m]), m.fn.triggerHandler && (m(y).triggerHandler("ready"), m(y).off("ready"))) + } + } + }); + function I() {y.addEventListener ? (y.removeEventListener("DOMContentLoaded", J, !1), a.removeEventListener("load", J, !1)) : (y.detachEvent("onreadystatechange", J), a.detachEvent("onload", J))} + + function J() {(y.addEventListener || "load" === event.type || "complete" === y.readyState) && (I(), m.ready())} + + m.ready.promise = function(b) { + if (!H) { + if (H = m.Deferred(), "complete" === y.readyState) { + setTimeout(m.ready); + } else if (y.addEventListener) { + y.addEventListener("DOMContentLoaded", J, !1), a.addEventListener("load", J, !1); + } else { + y.attachEvent("onreadystatechange", J), a.attachEvent("onload", J); + var c = !1; + try {c = null == a.frameElement && y.documentElement} + catch(d) {} + c && c.doScroll && !function e() { + if (!m.isReady) { + try {c.doScroll("left")} + catch(a) {return setTimeout(e, 50)} + I(), m.ready() + } + }() + } + } + return H.promise(b) + }; + var K = "undefined", L; + for(L in m(k)) { + break; + } + k.ownLast = "0" !== L, k.inlineBlockNeedsLayout = !1, m(function() { + var a, b, c, d; + c = y.getElementsByTagName("body")[0], c && c.style && (b = y.createElement("div"), d = y.createElement("div"), d.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px", c.appendChild(d).appendChild(b), typeof b.style.zoom !== K && (b.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1", k.inlineBlockNeedsLayout = a = 3 === b.offsetWidth, a && (c.style.zoom = 1)), c.removeChild(d)) + }), function() { + var a = y.createElement("div"); + if (null == k.deleteExpando) { + k.deleteExpando = !0; + try {delete a.test} + catch(b) {k.deleteExpando = !1} + } + a = null + }(), m.acceptData = function(a) { + var b = m.noData[(a.nodeName + " ").toLowerCase()], c = +a.nodeType || 1; + return 1 !== c && 9 !== c ? !1 : !b || b !== !0 && a.getAttribute("classid") === b + }; + var M = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, N = /([A-Z])/g; + + function O(a, b, c) { + if (void 0 === c && 1 === a.nodeType) { + var d = "data-" + b.replace(N, "-$1").toLowerCase(); + if (c = a.getAttribute(d), "string" == typeof c) { + try {c = "true" === c ? !0 : "false" === c ? !1 : "null" === c ? null : +c + "" === c ? +c : M.test(c) ? m.parseJSON(c) : c} + catch(e) {} + m.data(a, b, c) + } else { + c = void 0 + } + } + return c + } + + function P(a) { + var b; + for(b in a) { + if (("data" !== b || !m.isEmptyObject(a[b])) && "toJSON" !== b) { + return !1; + } + } + + return !0 + } + + function Q(a, b, d, e) { + if (m.acceptData(a)) { + var f, g, h = m.expando, i = a.nodeType, j = i ? m.cache : a, k = i ? a[h] : a[h] && h; + if (k && j[k] && (e || j[k].data) || void 0 !== d || "string" != typeof b) { + return k || (k = i ? a[h] = c.pop() || m.guid++ : h), j[k] || (j[k] = i ? {} : { toJSON: m.noop }), ("object" == typeof b || "function" == typeof b) && (e ? j[k] = m.extend(j[k], b) : j[k].data = m.extend(j[k].data, b)), g = j[k], e || (g.data || (g.data = {}), g = g.data), void 0 !== d && (g[m.camelCase(b)] = d), "string" == typeof b ? (f = g[b], null == f && (f = g[m.camelCase(b)])) : f = g, f + } + } + } + + function R(a, b, c) { + if (m.acceptData(a)) { + var d, e, f = a.nodeType, g = f ? m.cache : a, h = f ? a[m.expando] : m.expando; + if (g[h]) { + if (b && (d = c ? g[h] : g[h].data)) { + m.isArray(b) ? b = b.concat(m.map(b, m.camelCase)) : b in d ? b = [b] : (b = m.camelCase(b), b = b in d ? [b] : b.split(" ")), e = b.length; + while(e--) { + delete d[b[e]]; + } + if (c ? !P(d) : !m.isEmptyObject(d)) { + return + } + } + (c || (delete g[h].data, P(g[h]))) && (f ? m.cleanData([a], !0) : k.deleteExpando || g != g.window ? delete g[h] : g[h] = null) + } + } + } + + m.extend({ + cache: {}, + noData: { "applet ": !0, "embed ": !0, "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" }, + hasData: function(a) {return a = a.nodeType ? m.cache[a[m.expando]] : a[m.expando], !!a && !P(a)}, + data: function(a, b, c) {return Q(a, b, c)}, + removeData: function(a, b) {return R(a, b)}, + _data: function(a, b, c) {return Q(a, b, c, !0)}, + _removeData: function(a, b) {return R(a, b, !0)} + }), m.fn.extend({ + data: function(a, b) { + var c, d, e, f = this[0], g = f && f.attributes; + if (void 0 === a) { + if (this.length && (e = m.data(f), 1 === f.nodeType && !m._data(f, "parsedAttrs"))) { + c = g.length; + while(c--) { + g[c] && (d = g[c].name, 0 === d.indexOf("data-") && (d = m.camelCase(d.slice(5)), O(f, d, e[d]))); + } + m._data(f, "parsedAttrs", !0) + } + return e + } + return "object" == typeof a ? this.each(function() {m.data(this, a)}) : arguments.length > 1 ? this.each(function() {m.data(this, a, b)}) : f ? O(f, a, m.data(f, a)) : void 0 + }, removeData: function(a) {return this.each(function() {m.removeData(this, a)})} + }), m.extend({ + queue: function(a, b, c) { + var d; + return a ? (b = (b || "fx") + "queue", d = m._data(a, b), c && (!d || m.isArray(c) ? d = m._data(a, b, m.makeArray(c)) : d.push(c)), d || []) : void 0 + }, dequeue: function(a, b) { + b = b || "fx"; + var c = m.queue(a, b), d = c.length, e = c.shift(), f = m._queueHooks(a, b), g = function() {m.dequeue(a, b)}; + "inprogress" === e && (e = c.shift(), d--), e && ("fx" === b && c.unshift("inprogress"), delete f.stop, e.call(a, g, f)), !d && f && f.empty.fire() + }, _queueHooks: function(a, b) { + var c = b + "queueHooks"; + return m._data(a, c) || m._data(a, c, { empty: m.Callbacks("once memory").add(function() {m._removeData(a, b + "queue"), m._removeData(a, c)}) }) + } + }), m.fn.extend({ + queue: function(a, b) { + var c = 2; + return "string" != typeof a && (b = a, a = "fx", c--), arguments.length < c ? m.queue(this[0], a) : void 0 === b ? this : this.each(function() { + var c = m.queue(this, a, b); + m._queueHooks(this, a), "fx" === a && "inprogress" !== c[0] && m.dequeue(this, a) + }) + }, + dequeue: function(a) {return this.each(function() {m.dequeue(this, a)})}, + clearQueue: function(a) {return this.queue(a || "fx", [])}, + promise: function(a, b) { + var c, d = 1, e = m.Deferred(), f = this, g = this.length, h = function() {--d || e.resolveWith(f, [f])}; + "string" != typeof a && (b = a, a = void 0), a = a || "fx"; + while(g--) { + c = m._data(f[g], a + "queueHooks"), c && c.empty && (d++, c.empty.add(h)); + } + return h(), e.promise(b) + } + }); + var S = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, T = ["Top", "Right", "Bottom", "Left"], U = function(a, b) {return a = b || a, "none" === m.css(a, "display") || !m.contains(a.ownerDocument, a)}, V = m.access = function(a, b, c, d, e, f, g) { + var h = 0, i = a.length, j = null == c; + if ("object" === m.type(c)) { + e = !0; + for(h in c) { + m.access(a, b, h, c[h], !0, f, g) + } + } else if (void 0 !== d && (e = !0, m.isFunction(d) || (g = !0), j && (g ? (b.call(a, d), b = null) : (j = b, b = function(a, b, c) {return j.call(m(a), c)})), b)) { + for(; i > h; h++) { + b(a[h], c, g ? d : d.call(a[h], h, b(a[h], c))); + } + } + return e ? a : j ? b.call(a) : i ? b(a[0], c) : f + }, W = /^(?:checkbox|radio)$/i; + !function() { + var a = y.createElement("input"), b = y.createElement("div"), c = y.createDocumentFragment(); + if (b.innerHTML = "
a", k.leadingWhitespace = 3 === b.firstChild.nodeType, k.tbody = !b.getElementsByTagName("tbody").length, k.htmlSerialize = !!b.getElementsByTagName("link").length, k.html5Clone = "<:nav>" !== y.createElement("nav").cloneNode(!0).outerHTML, a.type = "checkbox", a.checked = !0, c.appendChild(a), k.appendChecked = a.checked, b.innerHTML = "", k.noCloneChecked = !!b.cloneNode(!0).lastChild.defaultValue, c.appendChild(b), b.innerHTML = "", k.checkClone = b.cloneNode(!0).cloneNode(!0).lastChild.checked, k.noCloneEvent = !0, b.attachEvent && (b.attachEvent("onclick", function() {k.noCloneEvent = !1}), b.cloneNode(!0).click()), null == k.deleteExpando) { + k.deleteExpando = !0; + try {delete b.test} + catch(d) {k.deleteExpando = !1} + } + }(), function() { + var b, c, d = y.createElement("div"); + for(b in{ + submit: !0, + change: !0, + focusin: !0 + }) { + c = "on" + b, (k[b + "Bubbles"] = c in a) || (d.setAttribute(c, "t"), k[b + "Bubbles"] = d.attributes[c].expando === !1); + } + d = null + }(); + var X = /^(?:input|select|textarea)$/i, Y = /^key/, Z = /^(?:mouse|pointer|contextmenu)|click/, $ = /^(?:focusinfocus|focusoutblur)$/, _ = /^([^.]*)(?:\.(.+)|)$/; + + function aa() {return !0} + + function ba() {return !1} + + function ca() { + try {return y.activeElement} + catch(a) {} + } + + m.event = { + global: {}, + add: function(a, b, c, d, e) { + var f, g, h, i, j, k, l, n, o, p, q, r = m._data(a); + if (r) { + c.handler && (i = c, c = i.handler, e = i.selector), c.guid || (c.guid = m.guid++), (g = r.events) || (g = r.events = {}), (k = r.handle) || (k = r.handle = function(a) {return typeof m === K || a && m.event.triggered === a.type ? void 0 : m.event.dispatch.apply(k.elem, arguments)}, k.elem = a), b = (b || "").match(E) || [""], h = b.length; + while(h--) { + f = _.exec(b[h]) || [], o = q = f[1], p = (f[2] || "").split(".").sort(), o && (j = m.event.special[o] || {}, o = (e ? j.delegateType : j.bindType) || o, j = m.event.special[o] || {}, l = m.extend({ + type: o, + origType: q, + data: d, + handler: c, + guid: c.guid, + selector: e, + needsContext: e && m.expr.match.needsContext.test(e), + namespace: p.join(".") + }, i), (n = g[o]) || (n = g[o] = [], n.delegateCount = 0, j.setup && j.setup.call(a, d, p, k) !== !1 || (a.addEventListener ? a.addEventListener(o, k, !1) : a.attachEvent && a.attachEvent("on" + o, k))), j.add && (j.add.call(a, l), l.handler.guid || (l.handler.guid = c.guid)), e ? n.splice(n.delegateCount++, 0, l) : n.push(l), m.event.global[o] = !0); + } + a = null + } + }, + remove: function(a, b, c, d, e) { + var f, g, h, i, j, k, l, n, o, p, q, r = m.hasData(a) && m._data(a); + if (r && (k = r.events)) { + b = (b || "").match(E) || [""], j = b.length; + while(j--) { + if (h = _.exec(b[j]) || [], o = q = h[1], p = (h[2] || "").split(".").sort(), o) { + l = m.event.special[o] || {}, o = (d ? l.delegateType : l.bindType) || o, n = k[o] || [], h = h[2] && new RegExp("(^|\\.)" + p.join("\\.(?:.*\\.|)") + "(\\.|$)"), i = f = n.length; + while(f--) { + g = n[f], !e && q !== g.origType || c && c.guid !== g.guid || h && !h.test(g.namespace) || d && d !== g.selector && ("**" !== d || !g.selector) || (n.splice(f, 1), g.selector && n.delegateCount--, l.remove && l.remove.call(a, g)); + } + i && !n.length && (l.teardown && l.teardown.call(a, p, r.handle) !== !1 || m.removeEvent(a, o, r.handle), delete k[o]) + } else { + for(o in k) { + m.event.remove(a, o + b[j], c, d, !0); + } + } + } + m.isEmptyObject(k) && (delete r.handle, m._removeData(a, "events")) + } + }, + trigger: function(b, c, d, e) { + var f, g, h, i, k, l, n, o = [d || y], p = j.call(b, "type") ? b.type : b, q = j.call(b, "namespace") ? b.namespace.split(".") : []; + if (h = l = d = d || y, 3 !== d.nodeType && 8 !== d.nodeType && !$.test(p + m.event.triggered) && (p.indexOf(".") >= 0 && (q = p.split("."), p = q.shift(), q.sort()), g = p.indexOf(":") < 0 && "on" + p, b = b[m.expando] ? b : new m.Event(p, "object" == typeof b && b), b.isTrigger = e ? 2 : 3, b.namespace = q.join("."), b.namespace_re = b.namespace ? new RegExp("(^|\\.)" + q.join("\\.(?:.*\\.|)") + "(\\.|$)") : null, b.result = void 0, b.target || (b.target = d), c = null == c ? [b] : m.makeArray(c, [b]), k = m.event.special[p] || {}, e || !k.trigger || k.trigger.apply(d, c) !== !1)) { + if (!e && !k.noBubble && !m.isWindow(d)) { + for(i = k.delegateType || p, $.test(i + p) || (h = h.parentNode); h; h = h.parentNode) { + o.push(h), l = h; + } + l === (d.ownerDocument || y) && o.push(l.defaultView || l.parentWindow || a) + } + n = 0; + while((h = o[n++]) && !b.isPropagationStopped()) { + b.type = n > 1 ? i : k.bindType || p, f = (m._data(h, "events") || {})[b.type] && m._data(h, "handle"), f && f.apply(h, c), f = g && h[g], f && f.apply && m.acceptData(h) && (b.result = f.apply(h, c), b.result === !1 && b.preventDefault()); + } + if (b.type = p, !e && !b.isDefaultPrevented() && (!k._default || k._default.apply(o.pop(), c) === !1) && m.acceptData(d) && g && d[p] && !m.isWindow(d)) { + l = d[g], l && (d[g] = null), m.event.triggered = p; + try {d[p]()} + catch(r) {} + m.event.triggered = void 0, l && (d[g] = l) + } + return b.result + } + }, + dispatch: function(a) { + a = m.event.fix(a); + var b, c, e, f, g, h = [], i = d.call(arguments), j = (m._data(this, "events") || {})[a.type] || [], k = m.event.special[a.type] || {}; + if (i[0] = a, a.delegateTarget = this, !k.preDispatch || k.preDispatch.call(this, a) !== !1) { + h = m.event.handlers.call(this, a, j), b = 0; + while((f = h[b++]) && !a.isPropagationStopped()) { + a.currentTarget = f.elem, g = 0; + while((e = f.handlers[g++]) && !a.isImmediatePropagationStopped()) { + (!a.namespace_re || a.namespace_re.test(e.namespace)) && (a.handleObj = e, a.data = e.data, c = ((m.event.special[e.origType] || {}).handle || e.handler).apply(f.elem, i), void 0 !== c && (a.result = c) === !1 && (a.preventDefault(), a.stopPropagation())) + } + } + return k.postDispatch && k.postDispatch.call(this, a), a.result + } + }, + handlers: function(a, b) { + var c, d, e, f, g = [], h = b.delegateCount, i = a.target; + if (h && i.nodeType && (!a.button || "click" !== a.type)) { + for(; i != this; i = i.parentNode || this) { + if (1 === i.nodeType && (i.disabled !== !0 || "click" !== a.type)) { + for(e = [], f = 0; h > f; f++) { + d = b[f], c = d.selector + " ", void 0 === e[c] && (e[c] = d.needsContext ? m(c, this).index(i) >= 0 : m.find(c, this, null, [i]).length), e[c] && e.push(d); + } + e.length && g.push({ elem: i, handlers: e }) + } + } + } + return h < b.length && g.push({ elem: this, handlers: b.slice(h) }), g + }, + fix: function(a) { + if (a[m.expando]) { + return a; + } + var b, c, d, e = a.type, f = a, g = this.fixHooks[e]; + g || (this.fixHooks[e] = g = Z.test(e) ? this.mouseHooks : Y.test(e) ? this.keyHooks : {}), d = g.props ? this.props.concat(g.props) : this.props, a = new m.Event(f), b = d.length; + while(b--) { + c = d[b], a[c] = f[c]; + } + return a.target || (a.target = f.srcElement || y), 3 === a.target.nodeType && (a.target = a.target.parentNode), a.metaKey = !!a.metaKey, g.filter ? g.filter(a, f) : a + }, + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + fixHooks: {}, + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function(a, b) {return null == a.which && (a.which = null != b.charCode ? b.charCode : b.keyCode), a} + }, + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function(a, b) { + var c, d, e, f = b.button, g = b.fromElement; + return null == a.pageX && null != b.clientX && (d = a.target.ownerDocument || y, e = d.documentElement, c = d.body, a.pageX = b.clientX + (e && e.scrollLeft || c && c.scrollLeft || 0) - (e && e.clientLeft || c && c.clientLeft || 0), a.pageY = b.clientY + (e && e.scrollTop || c && c.scrollTop || 0) - (e && e.clientTop || c && c.clientTop || 0)), !a.relatedTarget && g && (a.relatedTarget = g === a.target ? b.toElement : g), a.which || void 0 === f || (a.which = 1 & f ? 1 : 2 & f ? 3 : 4 & f ? 2 : 0), a + } + }, + special: { + load: { noBubble: !0 }, + focus: { + trigger: function() { + if (this !== ca() && this.focus) { + try {return this.focus(), !1} + catch(a) {} + } + }, delegateType: "focusin" + }, + blur: { trigger: function() {return this === ca() && this.blur ? (this.blur(), !1) : void 0}, delegateType: "focusout" }, + click: { + trigger: function() {return m.nodeName(this, "input") && "checkbox" === this.type && this.click ? (this.click(), !1) : void 0}, + _default: function(a) {return m.nodeName(a.target, "a")} + }, + beforeunload: { postDispatch: function(a) {void 0 !== a.result && a.originalEvent && (a.originalEvent.returnValue = a.result)} } + }, + simulate: function(a, b, c, d) { + var e = m.extend(new m.Event, c, { type: a, isSimulated: !0, originalEvent: {} }); + d ? m.event.trigger(e, null, b) : m.event.dispatch.call(b, e), e.isDefaultPrevented() && c.preventDefault() + } + }, m.removeEvent = y.removeEventListener ? function(a, b, c) {a.removeEventListener && a.removeEventListener(b, c, !1)} : function(a, b, c) { + var d = "on" + b; + a.detachEvent && (typeof a[d] === K && (a[d] = null), a.detachEvent(d, c)) + }, m.Event = function(a, b) {return this instanceof m.Event ? (a && a.type ? (this.originalEvent = a, this.type = a.type, this.isDefaultPrevented = a.defaultPrevented || void 0 === a.defaultPrevented && a.returnValue === !1 ? aa : ba) : this.type = a, b && m.extend(this, b), this.timeStamp = a && a.timeStamp || m.now(), void(this[m.expando] = !0)) : new m.Event(a, b)}, m.Event.prototype = { + isDefaultPrevented: ba, + isPropagationStopped: ba, + isImmediatePropagationStopped: ba, + preventDefault: function() { + var a = this.originalEvent; + this.isDefaultPrevented = aa, a && (a.preventDefault ? a.preventDefault() : a.returnValue = !1) + }, + stopPropagation: function() { + var a = this.originalEvent; + this.isPropagationStopped = aa, a && (a.stopPropagation && a.stopPropagation(), a.cancelBubble = !0) + }, + stopImmediatePropagation: function() { + var a = this.originalEvent; + this.isImmediatePropagationStopped = aa, a && a.stopImmediatePropagation && a.stopImmediatePropagation(), this.stopPropagation() + } + }, m.each({ + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" + }, function(a, b) { + m.event.special[a] = { + delegateType: b, bindType: b, handle: function(a) { + var c, d = this, e = a.relatedTarget, f = a.handleObj; + return (!e || e !== d && !m.contains(d, e)) && (a.type = f.origType, c = f.handler.apply(this, arguments), a.type = b), c + } + } + }), k.submitBubbles || (m.event.special.submit = { + setup: function() { + return m.nodeName(this, "form") ? !1 : void m.event.add(this, "click._submit keypress._submit", function(a) { + var b = a.target, c = m.nodeName(b, "input") || m.nodeName(b, "button") ? b.form : void 0; + c && !m._data(c, "submitBubbles") && (m.event.add(c, "submit._submit", function(a) {a._submit_bubble = !0}), m._data(c, "submitBubbles", !0)) + }) + }, + postDispatch: function(a) {a._submit_bubble && (delete a._submit_bubble, this.parentNode && !a.isTrigger && m.event.simulate("submit", this.parentNode, a, !0))}, + teardown: function() {return m.nodeName(this, "form") ? !1 : void m.event.remove(this, "._submit")} + }), k.changeBubbles || (m.event.special.change = { + setup: function() { + return X.test(this.nodeName) ? (("checkbox" === this.type || "radio" === this.type) && (m.event.add(this, "propertychange._change", function(a) {"checked" === a.originalEvent.propertyName && (this._just_changed = !0)}), m.event.add(this, "click._change", function(a) {this._just_changed && !a.isTrigger && (this._just_changed = !1), m.event.simulate("change", this, a, !0)})), !1) : void m.event.add(this, "beforeactivate._change", function(a) { + var b = a.target; + X.test(b.nodeName) && !m._data(b, "changeBubbles") && (m.event.add(b, "change._change", function(a) {!this.parentNode || a.isSimulated || a.isTrigger || m.event.simulate("change", this.parentNode, a, !0)}), m._data(b, "changeBubbles", !0)) + }) + }, handle: function(a) { + var b = a.target; + return this !== b || a.isSimulated || a.isTrigger || "radio" !== b.type && "checkbox" !== b.type ? a.handleObj.handler.apply(this, arguments) : void 0 + }, teardown: function() {return m.event.remove(this, "._change"), !X.test(this.nodeName)} + }), k.focusinBubbles || m.each({ + focus: "focusin", + blur: "focusout" + }, function(a, b) { + var c = function(a) {m.event.simulate(b, a.target, m.event.fix(a), !0)}; + m.event.special[b] = { + setup: function() { + var d = this.ownerDocument || this, e = m._data(d, b); + e || d.addEventListener(a, c, !0), m._data(d, b, (e || 0) + 1) + }, teardown: function() { + var d = this.ownerDocument || this, e = m._data(d, b) - 1; + e ? m._data(d, b, e) : (d.removeEventListener(a, c, !0), m._removeData(d, b)) + } + } + }), m.fn.extend({ + on: function(a, b, c, d, e) { + var f, g; + if ("object" == typeof a) { + "string" != typeof b && (c = c || b, b = void 0); + for(f in a) { + this.on(f, b, c, a[f], e); + } + return this + } + if (null == c && null == d ? (d = b, c = b = void 0) : null == d && ("string" == typeof b ? (d = c, c = void 0) : (d = c, c = b, b = void 0)), d === !1) { + d = ba; + } else if (!d) { + return this; + } + return 1 === e && (g = d, d = function(a) {return m().off(a), g.apply(this, arguments)}, d.guid = g.guid || (g.guid = m.guid++)), this.each(function() {m.event.add(this, a, d, c, b)}) + }, one: function(a, b, c, d) {return this.on(a, b, c, d, 1)}, off: function(a, b, c) { + var d, e; + if (a && a.preventDefault && a.handleObj) { + return d = a.handleObj, m(a.delegateTarget).off(d.namespace ? d.origType + "." + d.namespace : d.origType, d.selector, d.handler), this; + } + if ("object" == typeof a) { + for(e in a) { + this.off(e, b, a[e]); + } + return this + } + return (b === !1 || "function" == typeof b) && (c = b, b = void 0), c === !1 && (c = ba), this.each(function() {m.event.remove(this, a, c, b)}) + }, trigger: function(a, b) {return this.each(function() {m.event.trigger(a, b, this)})}, triggerHandler: function(a, b) { + var c = this[0]; + return c ? m.event.trigger(a, b, c, !0) : void 0 + } + }); + function da(a) { + var b = ea.split("|"), c = a.createDocumentFragment(); + if (c.createElement) { + while(b.length) { + c.createElement(b.pop()); + } + } + return c + } + + var ea = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", fa = / jQuery\d+="(?:null|\d+)"/g, ga = new RegExp("<(?:" + ea + ")[\\s/>]", "i"), ha = /^\s+/, ia = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, ja = /<([\w:]+)/, ka = /\s*$/g, ra = { + option: [1, ""], + legend: [1, "
", "
"], + area: [1, "", ""], + param: [1, "", ""], + thead: [1, "", "
"], + tr: [2, "", "
"], + col: [2, "", "
"], + td: [3, "", "
"], + _default: k.htmlSerialize ? [0, "", ""] : [1, "X
", "
"] + }, sa = da(y), ta = sa.appendChild(y.createElement("div")); + ra.optgroup = ra.option, ra.tbody = ra.tfoot = ra.colgroup = ra.caption = ra.thead, ra.th = ra.td; + function ua(a, b) { + var c, d, e = 0, f = typeof a.getElementsByTagName !== K ? a.getElementsByTagName(b || "*") : typeof a.querySelectorAll !== K ? a.querySelectorAll(b || "*") : void 0; + if (!f) { + for(f = [], c = a.childNodes || a; null != (d = c[e]); e++) { + !b || m.nodeName(d, b) ? f.push(d) : m.merge(f, ua(d, b)); + } + } + return void 0 === b || b && m.nodeName(a, b) ? m.merge([a], f) : f + } + + function va(a) {W.test(a.type) && (a.defaultChecked = a.checked)} + + function wa(a, b) {return m.nodeName(a, "table") && m.nodeName(11 !== b.nodeType ? b : b.firstChild, "tr") ? a.getElementsByTagName("tbody")[0] || a.appendChild(a.ownerDocument.createElement("tbody")) : a} + + function xa(a) {return a.type = (null !== m.find.attr(a, "type")) + "/" + a.type, a} + + function ya(a) { + var b = pa.exec(a.type); + return b ? a.type = b[1] : a.removeAttribute("type"), a + } + + function za(a, b) { + for(var c, d = 0; null != (c = a[d]); d++) { + m._data(c, "globalEval", !b || m._data(b[d], "globalEval")) + } + } + + function Aa(a, b) { + if (1 === b.nodeType && m.hasData(a)) { + var c, d, e, f = m._data(a), g = m._data(b, f), h = f.events; + if (h) { + delete g.handle, g.events = {}; + for(c in h) { + for(d = 0, e = h[c].length; e > d; d++) { + m.event.add(b, c, h[c][d]) + } + } + } + g.data && (g.data = m.extend({}, g.data)) + } + } + + function Ba(a, b) { + var c, d, e; + if (1 === b.nodeType) { + if (c = b.nodeName.toLowerCase(), !k.noCloneEvent && b[m.expando]) { + e = m._data(b); + for(d in e.events) { + m.removeEvent(b, d, e.handle); + } + b.removeAttribute(m.expando) + } + "script" === c && b.text !== a.text ? (xa(b).text = a.text, ya(b)) : "object" === c ? (b.parentNode && (b.outerHTML = a.outerHTML), k.html5Clone && a.innerHTML && !m.trim(b.innerHTML) && (b.innerHTML = a.innerHTML)) : "input" === c && W.test(a.type) ? (b.defaultChecked = b.checked = a.checked, b.value !== a.value && (b.value = a.value)) : "option" === c ? b.defaultSelected = b.selected = a.defaultSelected : ("input" === c || "textarea" === c) && (b.defaultValue = a.defaultValue) + } + } + + m.extend({ + clone: function(a, b, c) { + var d, e, f, g, h, i = m.contains(a.ownerDocument, a); + if (k.html5Clone || m.isXMLDoc(a) || !ga.test("<" + a.nodeName + ">") ? f = a.cloneNode(!0) : (ta.innerHTML = a.outerHTML, ta.removeChild(f = ta.firstChild)), !(k.noCloneEvent && k.noCloneChecked || 1 !== a.nodeType && 11 !== a.nodeType || m.isXMLDoc(a))) { + for(d = ua(f), h = ua(a), g = 0; null != (e = h[g]); ++g) { + d[g] && Ba(e, d[g]); + } + } + if (b) { + if (c) { + for(h = h || ua(a), d = d || ua(f), g = 0; null != (e = h[g]); g++) { + Aa(e, d[g]); + } + } else { + Aa(a, f); + } + } + return d = ua(f, "script"), d.length > 0 && za(d, !i && ua(a, "script")), d = h = e = null, f + }, + buildFragment: function(a, b, c, d) { + for(var e, f, g, h, i, j, l, n = a.length, o = da(b), p = [], q = 0; n > q; q++) { + if (f = a[q], f || 0 === f) { + if ("object" === m.type(f)) { + m.merge(p, f.nodeType ? [f] : f); + } else if (la.test(f)) { + h = h || o.appendChild(b.createElement("div")), i = (ja.exec(f) || ["", ""])[1].toLowerCase(), l = ra[i] || ra._default, h.innerHTML = l[1] + f.replace(ia, "<$1>") + l[2], e = l[0]; + while(e--) { + h = h.lastChild; + } + if (!k.leadingWhitespace && ha.test(f) && p.push(b.createTextNode(ha.exec(f)[0])), !k.tbody) { + f = "table" !== i || ka.test(f) ? "" !== l[1] || ka.test(f) ? 0 : h : h.firstChild, e = f && f.childNodes.length; + while(e--) { + m.nodeName(j = f.childNodes[e], "tbody") && !j.childNodes.length && f.removeChild(j) + } + } + m.merge(p, h.childNodes), h.textContent = ""; + while(h.firstChild) { + h.removeChild(h.firstChild); + } + h = o.lastChild + } else { + p.push(b.createTextNode(f)); + } + } + } + h && o.removeChild(h), k.appendChecked || m.grep(ua(p, "input"), va), q = 0; + while(f = p[q++]) { + if ((!d || -1 === m.inArray(f, d)) && (g = m.contains(f.ownerDocument, f), h = ua(o.appendChild(f), "script"), g && za(h), c)) { + e = 0; + while(f = h[e++]) { + oa.test(f.type || "") && c.push(f) + } + } + } + return h = null, o + }, + cleanData: function(a, b) { + for(var d, e, f, g, h = 0, i = m.expando, j = m.cache, l = k.deleteExpando, n = m.event.special; null != (d = a[h]); h++) { + if ((b || m.acceptData(d)) && (f = d[i], g = f && j[f])) { + if (g.events) { + for(e in g.events) { + n[e] ? m.event.remove(d, e) : m.removeEvent(d, e, g.handle); + } + } + j[f] && (delete j[f], l ? delete d[i] : typeof d.removeAttribute !== K ? d.removeAttribute(i) : d[i] = null, c.push(f)) + } + } + } + }), m.fn.extend({ + text: function(a) {return V(this, function(a) {return void 0 === a ? m.text(this) : this.empty().append((this[0] && this[0].ownerDocument || y).createTextNode(a))}, null, a, arguments.length)}, + append: function() { + return this.domManip(arguments, function(a) { + if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) { + var b = wa(this, a); + b.appendChild(a) + } + }) + }, + prepend: function() { + return this.domManip(arguments, function(a) { + if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) { + var b = wa(this, a); + b.insertBefore(a, b.firstChild) + } + }) + }, + before: function() {return this.domManip(arguments, function(a) {this.parentNode && this.parentNode.insertBefore(a, this)})}, + after: function() {return this.domManip(arguments, function(a) {this.parentNode && this.parentNode.insertBefore(a, this.nextSibling)})}, + remove: function(a, b) { + for(var c, d = a ? m.filter(a, this) : this, e = 0; null != (c = d[e]); e++) { + b || 1 !== c.nodeType || m.cleanData(ua(c)), c.parentNode && (b && m.contains(c.ownerDocument, c) && za(ua(c, "script")), c.parentNode.removeChild(c)); + } + return this + }, + empty: function() { + for(var a, b = 0; null != (a = this[b]); b++) { + 1 === a.nodeType && m.cleanData(ua(a, !1)); + while(a.firstChild) { + a.removeChild(a.firstChild); + } + a.options && m.nodeName(a, "select") && (a.options.length = 0) + } + return this + }, + clone: function(a, b) {return a = null == a ? !1 : a, b = null == b ? a : b, this.map(function() {return m.clone(this, a, b)})}, + html: function(a) { + return V(this, function(a) { + var b = this[0] || {}, c = 0, d = this.length; + if (void 0 === a) { + return 1 === b.nodeType ? b.innerHTML.replace(fa, "") : void 0; + } + if (!("string" != typeof a || ma.test(a) || !k.htmlSerialize && ga.test(a) || !k.leadingWhitespace && ha.test(a) || ra[(ja.exec(a) || ["", ""])[1].toLowerCase()])) { + a = a.replace(ia, "<$1>"); + try { + for(; d > c; c++) { + b = this[c] || {}, 1 === b.nodeType && (m.cleanData(ua(b, !1)), b.innerHTML = a); + } + b = 0 + } + catch(e) {} + } + b && this.empty().append(a) + }, null, a, arguments.length) + }, + replaceWith: function() { + var a = arguments[0]; + return this.domManip(arguments, function(b) {a = this.parentNode, m.cleanData(ua(this)), a && a.replaceChild(b, this)}), a && (a.length || a.nodeType) ? this : this.remove() + }, + detach: function(a) {return this.remove(a, !0)}, + domManip: function(a, b) { + a = e.apply([], a); + var c, d, f, g, h, i, j = 0, l = this.length, n = this, o = l - 1, p = a[0], q = m.isFunction(p); + if (q || l > 1 && "string" == typeof p && !k.checkClone && na.test(p)) { + return this.each(function(c) { + var d = n.eq(c); + q && (a[0] = p.call(this, c, d.html())), d.domManip(a, b) + }); + } + if (l && (i = m.buildFragment(a, this[0].ownerDocument, !1, this), c = i.firstChild, 1 === i.childNodes.length && (i = c), c)) { + for(g = m.map(ua(i, "script"), xa), f = g.length; l > j; j++) { + d = i, j !== o && (d = m.clone(d, !0, !0), f && m.merge(g, ua(d, "script"))), b.call(this[j], d, j); + } + if (f) { + for(h = g[g.length - 1].ownerDocument, m.map(g, ya), j = 0; f > j; j++) { + d = g[j], oa.test(d.type || "") && !m._data(d, "globalEval") && m.contains(h, d) && (d.src ? m._evalUrl && m._evalUrl(d.src) : m.globalEval((d.text || d.textContent || d.innerHTML || "").replace(qa, ""))); + } + } + i = c = null + } + return this + } + }), m.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" + }, function(a, b) { + m.fn[a] = function(a) { + for(var c, d = 0, e = [], g = m(a), h = g.length - 1; h >= d; d++) { + c = d === h ? this : this.clone(!0), m(g[d])[b](c), f.apply(e, c.get()); + } + return this.pushStack(e) + } + }); + var Ca, Da = {}; + + function Ea(b, c) { + var d, e = m(c.createElement(b)).appendTo(c.body), f = a.getDefaultComputedStyle && (d = a.getDefaultComputedStyle(e[0])) ? d.display : m.css(e[0], "display"); + return e.detach(), f + } + + function Fa(a) { + var b = y, c = Da[a]; + return c || (c = Ea(a, b), "none" !== c && c || (Ca = (Ca || m("