Merge pull request #9 from daniviga/driver-api

Add Rest API driver in WebThrottle-EX
This commit is contained in:
2022-04-23 23:08:44 +02:00
committed by GitHub
47 changed files with 7835 additions and 2 deletions

View File

@@ -1 +1 @@
# django-dcc
# Django Railroad Assets Manager (django-ram)

View File

@@ -0,0 +1,30 @@
# Contributing
Firstly, thank you for wanting to contribute to WebThrottle-EX! We really appreciate any contributions, however small (even typo corrections!). If you know what you want to change,
go ahead and read the section **Making your change**. If you want to contribute, but don't know where to start, have a look at some of the open issues and join the discussion.
## Making your change
1. The first step is to fork this repo then clone it locally to your PC. If you don't know how to do this, we recommend you check out the GitHub Docs
1. This is the exciting bit! Now is the time to make your changes. If you don't know where to find a file you need, check out the **What files are where** section below.
To edit the code, you will need some form of editor. You *can* use the text editor that comes with your OS, but we recommend a proper IDE like [Visual Studio Code](https://code.visualstudio.com/).
If you are using VSCode, then the extension [Live server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) is a good one to have.
1. Once you have made your change, commit it then push it to your fork.
1. Finally, you are ready to make your pull request! We will try and review it as soon as possible and either merge it or notify you of any changes you need to make before we can merge them.
**Note:** If you add any new files, be it images, stylesheets or javascript, they must all be added to the `sw.js` folder otherwise they will not be cached, meaning any features that use them will
break if the user is offline!
## Contributions from DCC-EX members
If you are a member of DCC-EX, you can clone this repo locally to make changes. Please make your own branch for this, either using a name related to the feature you are implementing,
or better yet, in the format `user-feature`. This way it is easy to see who is using which branch and the feature they are implementing.
Once you have made these changes, you can either open a pull request to `master` or just merge it to `master` yourself, it is completly up to you.
**Note:** Never touch the `build` branch! This is the live, end-user version of WebThrottle-EX and is only pushed to when a release is made.
## What files are where?
`index.html` - this is where the main code for the webpage is. There is only one html page, it is manipulated by JavaScript.
`js/` - this is where all JavaScript files are kept, both libraries such as JQuery and our files.
`css/` - where all the stylesheets are kept.
`images/` - where all image resources are kept.
`manifest.json` - used for the PWA, provides app details.
`sw.js` - used for the PWA, tells the browser what to cache.
`changelog.md` - provides the changelog for the latest update.

View File

@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -0,0 +1,85 @@
# DCC++ EX Web Throttle for django-ram
A slightly modiffied version of [DCC++ EX Web Throttle](https://github.com/DCC-EX/WebThrottle-EX) to be used with django-rma.
Requests are sent via REST API to `/api/v1/dcc/command` instead of via a local serial interface.
## Original README
--------------------
# DCC++ EX Web Throttle
This is a prototype for a new DCC++ EX Throttle/Controller that can connect to the DCC++ EX Commmand Station directly through the USB port of a computer.
## What you need to setup the project
* Chromium-Based Browser version 89 or above
(Chrome, Opera, Edge)
## What youll need to use this application
You don't need anything to test it out and to run in emulator mode, to run trains you will need:
* An Arduino Mega or Uno Microcontroller
* An Arduino Motor Control Shield
* Chromium based browser version 80 or later
## Getting started
NOTE: If you don't have your hardware yet or just want to play with the throttle
and see commands being sent to the log window, you can skip to the operation
section.
To get started, connect your Command Station to a computer that has a USB port and have a compatible browser installed. Use a USB serial cable from your computer to the serial connector on the Arduino. Visit DCC-EX.github.io/WebThrottle-EX/ to start using the software.
Alternativly, you can download the zip file found in the releases section and extract it. Click on the "index.html" or "exwebthrotle.html" file to load the webpage. This method is only recommended if you need a specific version as it won't be auto-updated.
## Operation
To use the program, you can either click on the "serial" dropodown button and select "emulator" to run in emulator mode or after making sure your hardware is properly connected, make sure "serial" is selected and click on the "Connect DCC++ EX" button.
If the program finds a compatible device, it will popup a window showing you a selection. It may show a line at the top such as "Arduino Mega 2560 (COM3)". Your com port may vary. Click on your board to select it and then click the "connect" button.
You should then be connected to the Command Station (CS) and should see the response from the CS on the web page under the buttons. Make sure your debug console is open. If it isn't, use the slider button in the lower left to open it. You can also open the DevTools window in your browser to see more developer logging.
Once you are connected, you can enter the ``<s>`` command in the "direct command" textbox to get status information from your Command Station, just enter "s" (without the quotes) and press the SEND button. You can send any DCC++ API command in this way. You should see <iDCC++...> returned in the log window with your version, type of arduino, type of motor shield, and some other information.
Now you are ready to run trains! Place your loco on the track and click the power slider button to turn on power to your track. You should see lights on an Arduino Motor Board light and an indication that your loco has power.
Next go to the "locomotive ID" textbox and enter the address of your loco and press the ACQUIRE button. You should now have full control over your loco.
All the function buttons should be working, so you can play with the headlight, horn and bell and any other function assigned to a function button. The commands being sent to the CS and its responses will display in the log window if it is open
In the throttle control area to the left of the function buttons are vertical controls to control direction. The up arrow selects forward, the square button is stop and the down arrow is reverse.
The circular control or vertical slider (chosen by the throttle select slider) can be moved by clicking and holding down the mouse button and dragging, clicking at a spot where you want the throttle to move, or clicking the + and - buttons.
The options button lets you save labels to go on your function buttons for each of your locos. We will be updating this document soon to give you more information on this and other new features.
**Note:** The emulator doesn't fully replicate the Command station yet. This means that although the software works, not all the responses will be shown in
the debug console. We are currently working on this, so it is something that will be fixed.
## Going Further / Developing
If you want to really delve into how this works and help us improve it with your comments or your development skills, please contact us.
To load the Chrome DevTools to look at logging and be able to manually enter "write" commands for testing, click on the Menu (the 3 vertical dots in the upper right hand corner of the Chrome Window), then select "more tools" and then "Developer Tools". Or you can just hit "Ctrl-Shift-I".
## License
Copyright 2020 DCC-EX
Licensed under the GNU open source licese.
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.
[DCC-EX](https://dcc-ex.com)

Binary file not shown.

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe900;" glyph-name="up" horiz-adv-x="1156" d="M502.29 915.862l-489.931-848.331c-7.125-12.45-11.327-27.365-11.327-43.261 0-48.391 38.937-87.687 87.189-88.269l0.055-0.001h979.862c48.308 0.582 87.244 39.879 87.244 88.27 0 15.896-4.202 30.811-11.555 43.695l0.228-0.434-489.931 848.331c-15.663 26.062-43.785 43.229-75.917 43.229s-60.254-17.168-75.695-42.831l-0.222-0.398z" />
<glyph unicode="&#xe901;" glyph-name="down" horiz-adv-x="1156" d="M502.29-19.862l-489.931 848.331c-7.125 12.45-11.327 27.365-11.327 43.261 0 48.391 38.937 87.687 87.189 88.269l0.055 0.001h979.862c48.308-0.582 87.244-39.879 87.244-88.27 0-15.896-4.202-30.811-11.555-43.695l0.228 0.434-489.931-848.331c-15.663-26.062-43.785-43.229-75.917-43.229s-60.254 17.168-75.695 42.831l-0.222 0.398z" />
<glyph unicode="&#xe90b;" glyph-name="droplet" d="M864.626 486.838c-65.754 183.44-205.11 348.15-352.626 473.162-147.516-125.012-286.87-289.722-352.626-473.162-40.664-113.436-44.682-236.562 12.584-345.4 65.846-125.14 198.632-205.438 340.042-205.438s274.196 80.298 340.040 205.44c57.27 108.838 53.25 231.962 12.586 345.398zM738.764 201.044c-43.802-83.252-132.812-137.044-226.764-137.044-55.12 0-108.524 18.536-152.112 50.652 13.242-1.724 26.632-2.652 40.112-2.652 117.426 0 228.668 67.214 283.402 171.242 44.878 85.292 40.978 173.848 23.882 244.338 14.558-28.15 26.906-56.198 36.848-83.932 22.606-63.062 40.024-156.34-5.368-242.604z" />
<glyph unicode="&#xe90c;" glyph-name="paint-format" d="M1024 384v384h-192v64c0 35.2-28.8 64-64 64h-704c-35.2 0-64-28.8-64-64v-192c0-35.2 28.8-64 64-64h704c35.2 0 64 28.8 64 64v64h128v-256h-576v-128h-32c-17.674 0-32-14.326-32-32v-320c0-17.674 14.326-32 32-32h128c17.674 0 32 14.326 32 32v320c0 17.674-14.326 32-32 32h-32v64h576zM768 768h-704v64h704v-64z" />
<glyph unicode="&#xe90d;" glyph-name="image" d="M959.884 832c0.040-0.034 0.082-0.076 0.116-0.116v-767.77c-0.034-0.040-0.076-0.082-0.116-0.116h-895.77c-0.040 0.034-0.082 0.076-0.114 0.116v767.772c0.034 0.040 0.076 0.082 0.114 0.114h895.77zM960 896h-896c-35.2 0-64-28.8-64-64v-768c0-35.2 28.8-64 64-64h896c35.2 0 64 28.8 64 64v768c0 35.2-28.8 64-64 64v0zM832 672c0-53.020-42.98-96-96-96s-96 42.98-96 96 42.98 96 96 96 96-42.98 96-96zM896 128h-768v128l224 384 256-320h64l224 192z" />
<glyph unicode="&#xe90e;" glyph-name="images" horiz-adv-x="1152" d="M1088 832h-64v64c0 35.2-28.8 64-64 64h-896c-35.2 0-64-28.8-64-64v-768c0-35.2 28.8-64 64-64h64v-64c0-35.2 28.8-64 64-64h896c35.2 0 64 28.8 64 64v768c0 35.2-28.8 64-64 64zM128 768v-640h-63.886c-0.040 0.034-0.082 0.076-0.114 0.116v767.77c0.034 0.040 0.076 0.082 0.114 0.114h895.77c0.040-0.034 0.082-0.076 0.116-0.116v-63.884h-768c-35.2 0-64-28.8-64-64v0zM1088 0.116c-0.034-0.040-0.076-0.082-0.116-0.116h-895.77c-0.040 0.034-0.082 0.076-0.114 0.116v767.77c0.034 0.040 0.076 0.082 0.114 0.114h895.77c0.040-0.034 0.082-0.076 0.116-0.116v-767.768zM960 608c0-53.020-42.98-96-96-96s-96 42.98-96 96 42.98 96 96 96 96-42.98 96-96zM1024 64h-768v128l224 384 256-320h64l224 192z" />
<glyph unicode="&#xe90f;" glyph-name="camera" d="M304 352c0-114.876 93.124-208 208-208s208 93.124 208 208-93.124 208-208 208-208-93.124-208-208zM960 704h-224c-16 64-32 128-96 128h-256c-64 0-80-64-96-128h-224c-35.2 0-64-28.8-64-64v-576c0-35.2 28.8-64 64-64h896c35.2 0 64 28.8 64 64v576c0 35.2-28.8 64-64 64zM512 68c-156.85 0-284 127.148-284 284 0 156.85 127.15 284 284 284 156.852 0 284-127.15 284-284 0-156.852-127.146-284-284-284zM960 512h-128v64h128v-64z" />
<glyph unicode="&#xe914;" glyph-name="video-camera" d="M384 672c0 88.366 71.634 160 160 160s160-71.634 160-160c0-88.366-71.634-160-160-160s-160 71.634-160 160zM0 672c0 88.366 71.634 160 160 160s160-71.634 160-160c0-88.366-71.634-160-160-160s-160 71.634-160 160zM768 352v96c0 35.2-28.8 64-64 64h-640c-35.2 0-64-28.8-64-64v-320c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64v96l256-160v448l-256-160zM640 192h-512v192h512v-192z" />
<glyph unicode="&#xe91f;" glyph-name="book" d="M896 832v-832h-672c-53.026 0-96 42.98-96 96s42.974 96 96 96h608v768h-640c-70.398 0-128-57.6-128-128v-768c0-70.4 57.602-128 128-128h768v896h-64zM224.056 128v0c-0.018-0.002-0.038 0-0.056 0-17.672 0-32-14.326-32-32s14.328-32 32-32c0.018 0 0.038 0.002 0.056 0.002v-0.002h607.89v64h-607.89z" />
<glyph unicode="&#xe924;" glyph-name="file-empty" d="M917.806 730.924c-22.212 30.292-53.174 65.7-87.178 99.704s-69.412 64.964-99.704 87.178c-51.574 37.82-76.592 42.194-90.924 42.194h-496c-44.112 0-80-35.888-80-80v-864c0-44.112 35.888-80 80-80h736c44.112 0 80 35.888 80 80v624c0 14.332-4.372 39.35-42.194 90.924zM785.374 785.374c30.7-30.7 54.8-58.398 72.58-81.374h-153.954v153.946c22.984-17.78 50.678-41.878 81.374-72.572zM896 16c0-8.672-7.328-16-16-16h-736c-8.672 0-16 7.328-16 16v864c0 8.672 7.328 16 16 16 0 0 495.956 0.002 496 0v-224c0-17.672 14.326-32 32-32h224v-624z" />
<glyph unicode="&#xe925;" glyph-name="files-empty" d="M917.806 602.924c-22.21 30.292-53.174 65.7-87.178 99.704s-69.412 64.964-99.704 87.178c-51.574 37.82-76.592 42.194-90.924 42.194h-368c-44.114 0-80-35.888-80-80v-736c0-44.112 35.886-80 80-80h608c44.112 0 80 35.888 80 80v496c0 14.332-4.372 39.35-42.194 90.924zM785.374 657.374c30.7-30.7 54.8-58.398 72.58-81.374h-153.954v153.946c22.982-17.78 50.678-41.878 81.374-72.572v0zM896 16c0-8.672-7.328-16-16-16h-608c-8.672 0-16 7.328-16 16v736c0 8.672 7.328 16 16 16 0 0 367.956 0.002 368 0v-224c0-17.672 14.324-32 32-32h224v-496zM602.924 917.804c-51.574 37.822-76.592 42.196-90.924 42.196h-368c-44.112 0-80-35.888-80-80v-736c0-38.632 27.528-70.958 64-78.39v814.39c0 8.672 7.328 16 16 16h486.876c-9.646 7.92-19.028 15.26-27.952 21.804z" />
<glyph unicode="&#xe929;" glyph-name="file-play" d="M384 576l320-224-320-224v448zM917.806 730.924c-22.212 30.292-53.174 65.7-87.178 99.704s-69.412 64.964-99.704 87.178c-51.574 37.82-76.592 42.194-90.924 42.194h-496c-44.112 0-80-35.888-80-80v-864c0-44.112 35.888-80 80-80h736c44.112 0 80 35.888 80 80v624c0 14.332-4.372 39.35-42.194 90.924zM785.374 785.374c30.7-30.7 54.8-58.398 72.58-81.374h-153.954v153.946c22.984-17.78 50.678-41.878 81.374-72.572zM896 16c0-8.672-7.328-16-16-16h-736c-8.672 0-16 7.328-16 16v864c0 8.672 7.328 16 16 16 0 0 495.956 0.002 496 0v-224c0-17.672 14.326-32 32-32h224v-624z" />
<glyph unicode="&#xe92a;" glyph-name="file-video" d="M917.806 730.924c-22.208 30.292-53.174 65.7-87.178 99.704s-69.412 64.964-99.704 87.178c-51.574 37.82-76.594 42.194-90.924 42.194h-496c-44.112 0-80-35.888-80-80v-864c0-44.112 35.882-80 80-80h736c44.112 0 80 35.888 80 80v624c0 14.332-4.372 39.35-42.194 90.924v0 0zM785.374 785.374c30.7-30.7 54.8-58.398 72.58-81.374h-153.954v153.946c22.98-17.78 50.678-41.878 81.374-72.572v0 0zM896 16c0-8.672-7.328-16-16-16h-736c-8.672 0-16 7.328-16 16v864c0 8.672 7.328 16 16 16 0 0 495.956 0.002 496 0v-224c0-17.672 14.32-32 32-32h224v-624zM256 448h320v-320h-320v320zM576 320l192 128v-320l-192 128z" />
<glyph unicode="&#xe92b;" glyph-name="file-zip" d="M917.806 730.924c-22.208 30.292-53.174 65.7-87.178 99.704s-69.412 64.964-99.704 87.178c-51.574 37.82-76.592 42.194-90.924 42.194h-496c-44.112 0-80-35.888-80-80v-864c0-44.112 35.884-80 80-80h736c44.112 0 80 35.888 80 80v624c0 14.332-4.372 39.35-42.194 90.924v0 0zM785.374 785.374c30.7-30.7 54.8-58.398 72.58-81.374h-153.954v153.946c22.98-17.78 50.678-41.878 81.374-72.572v0 0zM896 16c0-8.672-7.328-16-16-16h-736c-8.672 0-16 7.328-16 16v864c0 8.672 7.328 16 16 16 0 0 495.956 0.002 496 0v-224c0-17.672 14.322-32 32-32h224v-624zM256 896h128v-64h-128v64zM384 832h128v-64h-128v64zM256 768h128v-64h-128v64zM384 704h128v-64h-128v64zM256 640h128v-64h-128v64zM384 576h128v-64h-128v64zM256 512h128v-64h-128v64zM384 448h128v-64h-128v64zM256 112c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-80v64h-128v-272zM448 192v-64h-128v64h128z" />
<glyph unicode="&#xe92e;" glyph-name="stack" d="M1024 640l-512 256-512-256 512-256 512 256zM512 811.030l342.058-171.030-342.058-171.030-342.058 171.030 342.058 171.030zM921.444 499.278l102.556-51.278-512-256-512 256 102.556 51.278 409.444-204.722zM921.444 307.278l102.556-51.278-512-256-512 256 102.556 51.278 409.444-204.722z" />
<glyph unicode="&#xe92f;" glyph-name="folder" d="M448 832l128-128h448v-704h-1024v832z" />
<glyph unicode="&#xe930;" glyph-name="folder-open" d="M832 0l192 512h-832l-192-512zM128 576l-128-576v832h288l128-128h416v-128z" />
<glyph unicode="&#xe931;" glyph-name="folder-plus" d="M576 704l-128 128h-448v-832h1024v704h-448zM704 256h-128v-128h-128v128h-128v128h128v128h128v-128h128v-128z" />
<glyph unicode="&#xe932;" glyph-name="folder-minus" d="M576 704l-128 128h-448v-832h1024v704h-448zM704 256h-384v128h384v-128z" />
<glyph unicode="&#xe933;" glyph-name="folder-download" d="M576 704l-128 128h-448v-832h1024v704h-448zM512 96l-224 224h160v256h128v-256h160l-224-224z" />
<glyph unicode="&#xe934;" glyph-name="folder-upload" d="M576 704l-128 128h-448v-832h1024v704h-448zM512 480l224-224h-160v-256h-128v256h-160l224 224z" />
<glyph unicode="&#xe935;" glyph-name="price-tag" d="M976 960h-384c-26.4 0-63.274-15.274-81.942-33.942l-476.116-476.116c-18.668-18.668-18.668-49.214 0-67.882l412.118-412.118c18.668-18.668 49.214-18.668 67.882 0l476.118 476.118c18.666 18.666 33.94 55.54 33.94 81.94v384c0 26.4-21.6 48-48 48zM736 576c-53.020 0-96 42.98-96 96s42.98 96 96 96 96-42.98 96-96-42.98-96-96-96z" />
<glyph unicode="&#xe936;" glyph-name="price-tags" horiz-adv-x="1280" d="M1232 960h-384c-26.4 0-63.274-15.274-81.942-33.942l-476.116-476.116c-18.668-18.668-18.668-49.214 0-67.882l412.118-412.118c18.668-18.668 49.214-18.668 67.882 0l476.118 476.118c18.666 18.666 33.94 55.54 33.94 81.94v384c0 26.4-21.6 48-48 48zM992 576c-53.020 0-96 42.98-96 96s42.98 96 96 96 96-42.98 96-96-42.98-96-96-96zM128 416l544 544h-80c-26.4 0-63.274-15.274-81.942-33.942l-476.116-476.116c-18.668-18.668-18.668-49.214 0-67.882l412.118-412.118c18.668-18.668 49.214-18.668 67.882 0l30.058 30.058-416 416z" />
<glyph unicode="&#xe947;" glyph-name="location" d="M512 960c-176.732 0-320-143.268-320-320 0-320 320-704 320-704s320 384 320 704c0 176.732-143.27 320-320 320zM512 448c-106.040 0-192 85.96-192 192s85.96 192 192 192 192-85.96 192-192-85.96-192-192-192z" />
<glyph unicode="&#xe94d;" glyph-name="history" horiz-adv-x="1088" d="M640 896c247.424 0 448-200.576 448-448s-200.576-448-448-448v96c94.024 0 182.418 36.614 248.902 103.098s103.098 154.878 103.098 248.902c0 94.022-36.614 182.418-103.098 248.902s-154.878 103.098-248.902 103.098c-94.022 0-182.418-36.614-248.902-103.098-51.14-51.138-84.582-115.246-97.306-184.902h186.208l-224-256-224 256h164.57c31.060 217.102 217.738 384 443.43 384zM832 512v-128h-256v320h128v-192z" />
<glyph unicode="&#xe950;" glyph-name="alarm" d="M512 832c-247.424 0-448-200.576-448-448s200.576-448 448-448 448 200.576 448 448-200.576 448-448 448zM512 24c-198.824 0-360 161.178-360 360 0 198.824 161.176 360 360 360 198.822 0 360-161.176 360-360 0-198.822-161.178-360-360-360zM934.784 672.826c16.042 28.052 25.216 60.542 25.216 95.174 0 106.040-85.96 192-192 192-61.818 0-116.802-29.222-151.92-74.596 131.884-27.236 245.206-105.198 318.704-212.578v0zM407.92 885.404c-35.116 45.374-90.102 74.596-151.92 74.596-106.040 0-192-85.96-192-192 0-34.632 9.174-67.122 25.216-95.174 73.5 107.38 186.822 185.342 318.704 212.578zM512 384v256h-64v-320h256v64z" />
<glyph unicode="&#xe951;" glyph-name="bell" d="M1025.5 160c0 288-256 224-256 448 0 18.56-1.788 34.42-5.048 47.928-16.83 113.018-92.156 203.72-189.772 231.36 0.866 3.948 1.32 8.032 1.32 12.21 0 33.278-28.8 60.502-64 60.502s-64-27.224-64-60.5c0-4.18 0.456-8.264 1.32-12.21-109.47-30.998-190.914-141.298-193.254-273.442-0.040-1.92-0.066-3.864-0.066-5.846 0-224.002-256-160.002-256-448.002 0-76.226 170.59-139.996 398.97-156.080 21.524-40.404 64.056-67.92 113.030-67.92s91.508 27.516 113.030 67.92c228.38 16.084 398.97 79.854 398.97 156.080 0 0.228-0.026 0.456-0.028 0.682l1.528-0.682zM826.246 105.904c-54.23-14.47-118.158-24.876-186.768-30.648-5.704 65.418-60.582 116.744-127.478 116.744s-121.774-51.326-127.478-116.744c-68.608 5.772-132.538 16.178-186.768 30.648-74.63 19.914-110.31 42.19-123.368 54.096 13.058 11.906 48.738 34.182 123.368 54.096 86.772 23.152 198.372 35.904 314.246 35.904s227.474-12.752 314.246-35.904c74.63-19.914 110.31-42.19 123.368-54.096-13.058-11.906-48.738-34.182-123.368-54.096z" />
<glyph unicode="&#xe952;" glyph-name="stopwatch" d="M512.002 766.788v65.212h128v64c0 35.346-28.654 64-64.002 64h-191.998c-35.346 0-64-28.654-64-64v-64h128v-65.212c-214.798-16.338-384-195.802-384-414.788 0-229.75 186.25-416 416-416s416 186.25 416 416c0 218.984-169.202 398.448-384 414.788zM706.276 125.726c-60.442-60.44-140.798-93.726-226.274-93.726s-165.834 33.286-226.274 93.726c-60.44 60.44-93.726 140.8-93.726 226.274s33.286 165.834 93.726 226.274c58.040 58.038 134.448 91.018 216.114 93.548l-21.678-314.020c-1.86-26.29 12.464-37.802 31.836-37.802s33.698 11.512 31.836 37.802l-21.676 314.022c81.666-2.532 158.076-35.512 216.116-93.55 60.44-60.44 93.726-140.8 93.726-226.274s-33.286-165.834-93.726-226.274z" />
<glyph unicode="&#xe956;" glyph-name="display" d="M0 896v-640h1024v640h-1024zM960 320h-896v512h896v-512zM672 192h-320l-32-128-64-64h512l-64 64z" />
<glyph unicode="&#xe958;" glyph-name="mobile" d="M736 960h-448c-52.8 0-96-43.2-96-96v-832c0-52.8 43.2-96 96-96h448c52.8 0 96 43.2 96 96v832c0 52.8-43.2 96-96 96zM384 912h256v-32h-256v32zM512 0c-35.346 0-64 28.654-64 64s28.654 64 64 64 64-28.654 64-64-28.654-64-64-64zM768 192h-512v640h512v-640z" />
<glyph unicode="&#xe959;" glyph-name="mobile2" d="M768 960h-576c-35.2 0-64-28.798-64-64v-896c0-35.2 28.798-64 64-64h576c35.2 0 64 28.8 64 64v896c0 35.202-28.8 64-64 64zM480-17.782c-27.492 0-49.782 22.29-49.782 49.782s22.29 49.782 49.782 49.782 49.782-22.29 49.782-49.782-22.29-49.782-49.782-49.782zM768 128h-576v704h576v-704z" />
<glyph unicode="&#xe95e;" glyph-name="box-add" d="M832 896h-640l-192-192v-672c0-17.674 14.326-32 32-32h960c17.672 0 32 14.326 32 32v672l-192 192zM512 128l-320 256h192v192h256v-192h192l-320-256zM154.51 768l64 64h586.978l64-64h-714.978z" />
<glyph unicode="&#xe95f;" glyph-name="box-remove" d="M832 896h-640l-192-192v-672c0-17.674 14.326-32 32-32h960c17.672 0 32 14.326 32 32v672l-192 192zM640 320v-192h-256v192h-192l320 256 320-256h-192zM154.51 768l64 64h586.976l64-64h-714.976z" />
<glyph unicode="&#xe960;" glyph-name="download" d="M512 384l256 256h-192v256h-128v-256h-192zM744.726 488.728l-71.74-71.742 260.080-96.986-421.066-157.018-421.066 157.018 260.080 96.986-71.742 71.742-279.272-104.728v-256l512-192 512 192v256z" />
<glyph unicode="&#xe961;" glyph-name="upload" d="M448 384h128v256h192l-256 256-256-256h192zM640 528v-98.712l293.066-109.288-421.066-157.018-421.066 157.018 293.066 109.288v98.712l-384-144v-256l512-192 512 192v256z" />
<glyph unicode="&#xe962;" glyph-name="floppy-disk" d="M896 960h-896v-1024h1024v896l-128 128zM512 832h128v-256h-128v256zM896 64h-768v768h64v-320h576v320h74.978l53.022-53.018v-714.982z" />
<glyph unicode="&#xe964;" glyph-name="database" d="M512 960c-282.77 0-512-71.634-512-160v-128c0-88.366 229.23-160 512-160s512 71.634 512 160v128c0 88.366-229.23 160-512 160zM512 416c-282.77 0-512 71.634-512 160v-192c0-88.366 229.23-160 512-160s512 71.634 512 160v192c0-88.366-229.23-160-512-160zM512 128c-282.77 0-512 71.634-512 160v-192c0-88.366 229.23-160 512-160s512 71.634 512 160v192c0-88.366-229.23-160-512-160z" />
<glyph unicode="&#xe965;" glyph-name="undo" d="M512 896c-141.384 0-269.376-57.32-362.032-149.978l-149.968 149.978v-384h384l-143.532 143.522c69.496 69.492 165.492 112.478 271.532 112.478 212.068 0 384-171.924 384-384 0-114.696-50.292-217.636-130.018-288l84.666-96c106.302 93.816 173.352 231.076 173.352 384 0 282.77-229.23 512-512 512z" />
<glyph unicode="&#xe966;" glyph-name="redo" d="M0 384c0-152.924 67.048-290.184 173.35-384l84.666 96c-79.726 70.364-130.016 173.304-130.016 288 0 212.076 171.93 384 384 384 106.042 0 202.038-42.986 271.53-112.478l-143.53-143.522h384v384l-149.97-149.978c-92.654 92.658-220.644 149.978-362.030 149.978-282.77 0-512-229.23-512-512z" />
<glyph unicode="&#xe967;" glyph-name="undo2" d="M761.862-64c113.726 206.032 132.888 520.306-313.862 509.824v-253.824l-384 384 384 384v-248.372c534.962 13.942 594.57-472.214 313.862-775.628z" />
<glyph unicode="&#xe968;" glyph-name="redo2" d="M576 711.628v248.372l384-384-384-384v253.824c-446.75 10.482-427.588-303.792-313.86-509.824-280.712 303.414-221.1 789.57 313.86 775.628z" />
<glyph unicode="&#xe971;" glyph-name="user" d="M576 253.388v52.78c70.498 39.728 128 138.772 128 237.832 0 159.058 0 288-192 288s-192-128.942-192-288c0-99.060 57.502-198.104 128-237.832v-52.78c-217.102-17.748-384-124.42-384-253.388h896c0 128.968-166.898 235.64-384 253.388z" />
<glyph unicode="&#xe972;" glyph-name="users" horiz-adv-x="1152" d="M768 189.388v52.78c70.498 39.728 128 138.772 128 237.832 0 159.058 0 288-192 288s-192-128.942-192-288c0-99.060 57.502-198.104 128-237.832v-52.78c-217.102-17.748-384-124.42-384-253.388h896c0 128.968-166.898 235.64-384 253.388zM327.196 164.672c55.31 36.15 124.080 63.636 199.788 80.414-15.054 17.784-28.708 37.622-40.492 59.020-30.414 55.234-46.492 116.058-46.492 175.894 0 86.042 0 167.31 30.6 233.762 29.706 64.504 83.128 104.496 159.222 119.488-16.914 76.48-61.94 126.75-181.822 126.75-192 0-192-128.942-192-288 0-99.060 57.502-198.104 128-237.832v-52.78c-217.102-17.748-384-124.42-384-253.388h279.006c14.518 12.91 30.596 25.172 48.19 36.672z" />
<glyph unicode="&#xe97b;" glyph-name="spinner2" d="M1024 448c-1.278 66.862-15.784 133.516-42.576 194.462-26.704 61-65.462 116.258-113.042 161.92-47.552 45.696-103.944 81.82-164.984 105.652-61.004 23.924-126.596 35.352-191.398 33.966-64.81-1.282-129.332-15.374-188.334-41.356-59.048-25.896-112.542-63.47-156.734-109.576-44.224-46.082-79.16-100.708-102.186-159.798-23.114-59.062-34.128-122.52-32.746-185.27 1.286-62.76 14.964-125.148 40.134-182.206 25.088-57.1 61.476-108.828 106.11-151.548 44.61-42.754 97.472-76.504 154.614-98.72 57.118-22.304 118.446-32.902 179.142-31.526 60.708 1.29 120.962 14.554 176.076 38.914 55.15 24.282 105.116 59.48 146.366 102.644 41.282 43.14 73.844 94.236 95.254 149.43 13.034 33.458 21.88 68.4 26.542 103.798 1.246-0.072 2.498-0.12 3.762-0.12 35.346 0 64 28.652 64 64 0 1.796-0.094 3.572-0.238 5.332h0.238zM922.306 278.052c-23.472-53.202-57.484-101.4-99.178-141.18-41.67-39.81-91-71.186-144.244-91.79-53.228-20.678-110.29-30.452-166.884-29.082-56.604 1.298-112.596 13.736-163.82 36.474-51.25 22.666-97.684 55.49-135.994 95.712-38.338 40.198-68.528 87.764-88.322 139.058-19.87 51.284-29.228 106.214-27.864 160.756 1.302 54.552 13.328 108.412 35.254 157.69 21.858 49.3 53.498 93.97 92.246 130.81 38.73 36.868 84.53 65.87 133.874 84.856 49.338 19.060 102.136 28.006 154.626 26.644 52.5-1.306 104.228-12.918 151.562-34.034 47.352-21.050 90.256-51.502 125.624-88.782 35.396-37.258 63.21-81.294 81.39-128.688 18.248-47.392 26.782-98.058 25.424-148.496h0.238c-0.144-1.76-0.238-3.536-0.238-5.332 0-33.012 24.992-60.174 57.086-63.624-6.224-34.822-16.53-68.818-30.78-100.992z" />
<glyph unicode="&#xe984;" glyph-name="spinner11" d="M1024 576h-384l143.53 143.53c-72.53 72.526-168.96 112.47-271.53 112.47s-199-39.944-271.53-112.47c-72.526-72.53-112.47-168.96-112.47-271.53s39.944-199 112.47-271.53c72.53-72.526 168.96-112.47 271.53-112.47s199 39.944 271.528 112.472c6.056 6.054 11.86 12.292 17.456 18.668l96.32-84.282c-93.846-107.166-231.664-174.858-385.304-174.858-282.77 0-512 229.23-512 512s229.23 512 512 512c141.386 0 269.368-57.326 362.016-149.984l149.984 149.984v-384z" />
<glyph unicode="&#xe985;" glyph-name="binoculars" d="M64 960h384v-64h-384zM576 960h384v-64h-384zM952 640h-56v256h-256v-256h-256v256h-256v-256h-56c-39.6 0-72-32.4-72-72v-560c0-39.6 32.4-72 72-72h304c39.6 0 72 32.4 72 72v376h128v-376c0-39.6 32.4-72 72-72h304c39.6 0 72 32.4 72 72v560c0 39.6-32.4 72-72 72zM348 0h-248c-19.8 0-36 14.4-36 32s16.2 32 36 32h248c19.8 0 36-14.4 36-32s-16.2-32-36-32zM544 448h-64c-17.6 0-32 14.4-32 32s14.4 32 32 32h64c17.6 0 32-14.4 32-32s-14.4-32-32-32zM924 0h-248c-19.8 0-36 14.4-36 32s16.2 32 36 32h248c19.8 0 36-14.4 36-32s-16.2-32-36-32z" />
<glyph unicode="&#xe986;" glyph-name="search" d="M992.262 88.604l-242.552 206.294c-25.074 22.566-51.89 32.926-73.552 31.926 57.256 67.068 91.842 154.078 91.842 249.176 0 212.078-171.922 384-384 384-212.076 0-384-171.922-384-384s171.922-384 384-384c95.098 0 182.108 34.586 249.176 91.844-1-21.662 9.36-48.478 31.926-73.552l206.294-242.552c35.322-39.246 93.022-42.554 128.22-7.356s31.892 92.898-7.354 128.22zM384 320c-141.384 0-256 114.616-256 256s114.616 256 256 256 256-114.616 256-256-114.614-256-256-256z" />
<glyph unicode="&#xe987;" glyph-name="zoom-in" d="M992.262 88.604l-242.552 206.294c-25.074 22.566-51.89 32.926-73.552 31.926 57.256 67.068 91.842 154.078 91.842 249.176 0 212.078-171.922 384-384 384-212.076 0-384-171.922-384-384s171.922-384 384-384c95.098 0 182.108 34.586 249.176 91.844-1-21.662 9.36-48.478 31.926-73.552l206.294-242.552c35.322-39.246 93.022-42.554 128.22-7.356s31.892 92.898-7.354 128.22zM384 320c-141.384 0-256 114.616-256 256s114.616 256 256 256 256-114.616 256-256-114.614-256-256-256zM448 768h-128v-128h-128v-128h128v-128h128v128h128v128h-128z" />
<glyph unicode="&#xe988;" glyph-name="zoom-out" d="M992.262 88.604l-242.552 206.294c-25.074 22.566-51.89 32.926-73.552 31.926 57.256 67.068 91.842 154.078 91.842 249.176 0 212.078-171.922 384-384 384-212.076 0-384-171.922-384-384s171.922-384 384-384c95.098 0 182.108 34.586 249.176 91.844-1-21.662 9.36-48.478 31.926-73.552l206.294-242.552c35.322-39.246 93.022-42.554 128.22-7.356s31.892 92.898-7.354 128.22zM384 320c-141.384 0-256 114.616-256 256s114.616 256 256 256 256-114.616 256-256-114.614-256-256-256zM192 640h384v-128h-384z" />
<glyph unicode="&#xe98d;" glyph-name="key" d="M704 960c-176.73 0-320-143.268-320-320 0-20.026 1.858-39.616 5.376-58.624l-389.376-389.376v-192c0-35.346 28.654-64 64-64h64v64h128v128h128v128h128l83.042 83.042c34.010-12.316 70.696-19.042 108.958-19.042 176.73 0 320 143.268 320 320s-143.27 320-320 320zM799.874 639.874c-53.020 0-96 42.98-96 96s42.98 96 96 96 96-42.98 96-96-42.98-96-96-96z" />
<glyph unicode="&#xe98e;" glyph-name="key2" d="M1002.132 645.758l-101.106 101.104c-24.792 24.794-65.37 65.368-90.162 90.164l-101.106 101.104c-24.792 24.794-68.954 29.166-98.13 9.716l-276.438-184.292c-29.176-19.452-40.218-61.028-24.536-92.39l70.486-140.974c2.154-4.306 4.646-8.896 7.39-13.66l-356.53-356.53-32-224h192v64h128v128h128v128h128v71.186c6.396-3.812 12.534-7.216 18.192-10.044l140.97-70.488c31.366-15.682 72.94-4.638 92.39 24.538l184.294 276.44c19.454 29.172 15.078 73.33-9.714 98.126zM150.628 105.374l-45.254 45.254 311.572 311.57 45.254-45.254-311.572-311.57zM917.020 536.236l-45.256-45.256c-12.446-12.444-32.808-12.444-45.254 0l-271.53 271.53c-12.446 12.444-12.446 32.81 0 45.254l45.256 45.256c12.446 12.444 32.808 12.444 45.254 0l271.53-271.53c12.446-12.444 12.446-32.81 0-45.254z" />
<glyph unicode="&#xe98f;" glyph-name="lock" d="M592 512h-16v192c0 105.87-86.13 192-192 192h-128c-105.87 0-192-86.13-192-192v-192h-16c-26.4 0-48-21.6-48-48v-480c0-26.4 21.6-48 48-48h544c26.4 0 48 21.6 48 48v480c0 26.4-21.6 48-48 48zM192 704c0 35.29 28.71 64 64 64h128c35.29 0 64-28.71 64-64v-192h-256v192z" />
<glyph unicode="&#xe990;" glyph-name="unlocked" d="M768 896c105.87 0 192-86.13 192-192v-192h-128v192c0 35.29-28.71 64-64 64h-128c-35.29 0-64-28.71-64-64v-192h16c26.4 0 48-21.6 48-48v-480c0-26.4-21.6-48-48-48h-544c-26.4 0-48 21.6-48 48v480c0 26.4 21.6 48 48 48h400v192c0 105.87 86.13 192 192 192h128z" />
<glyph unicode="&#xe991;" glyph-name="wrench" d="M1002.934 142.124l-460.552 394.76c21.448 40.298 33.618 86.282 33.618 135.116 0 159.058-128.942 288-288 288-29.094 0-57.172-4.332-83.646-12.354l166.39-166.39c24.89-24.89 24.89-65.62 0-90.51l-101.49-101.49c-24.89-24.89-65.62-24.89-90.51 0l-166.39 166.39c-8.022-26.474-12.354-54.552-12.354-83.646 0-159.058 128.942-288 288-288 48.834 0 94.818 12.17 135.116 33.62l394.76-460.552c22.908-26.724 62.016-28.226 86.904-3.338l101.492 101.492c24.888 24.888 23.386 63.994-3.338 86.902z" />
<glyph unicode="&#xe992;" glyph-name="equalizer" d="M448 832v16c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-192v-128h192v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h576v128h-576zM256 704v128h128v-128h-128zM832 528c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-576v-128h576v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h192v128h-192v16zM640 384v128h128v-128h-128zM448 208c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-192v-128h192v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h576v128h-576v16zM256 64v128h128v-128h-128z" />
<glyph unicode="&#xe993;" glyph-name="equalizer2" d="M896 512h16c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-16v192h-128v-192h-16c-26.4 0-48-21.6-48-48v-160c0-26.4 21.6-48 48-48h16v-576h128v576zM768 704h128v-128h-128v128zM592 128c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-16v576h-128v-576h-16c-26.4 0-48-21.6-48-48v-160c0-26.4 21.6-48 48-48h16v-192h128v192h16zM448 320h128v-128h-128v128zM272 512c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-16v192h-128v-192h-16c-26.4 0-48-21.6-48-48v-160c0-26.4 21.6-48 48-48h16v-576h128v576h16zM128 704h128v-128h-128v128z" />
<glyph unicode="&#xe994;" glyph-name="cog" d="M933.79 349.75c-53.726 93.054-21.416 212.304 72.152 266.488l-100.626 174.292c-28.75-16.854-62.176-26.518-97.846-26.518-107.536 0-194.708 87.746-194.708 195.99h-201.258c0.266-33.41-8.074-67.282-25.958-98.252-53.724-93.056-173.156-124.702-266.862-70.758l-100.624-174.292c28.97-16.472 54.050-40.588 71.886-71.478 53.638-92.908 21.512-211.92-71.708-266.224l100.626-174.292c28.65 16.696 61.916 26.254 97.4 26.254 107.196 0 194.144-87.192 194.7-194.958h201.254c-0.086 33.074 8.272 66.57 25.966 97.218 53.636 92.906 172.776 124.594 266.414 71.012l100.626 174.29c-28.78 16.466-53.692 40.498-71.434 71.228zM512 240.668c-114.508 0-207.336 92.824-207.336 207.334 0 114.508 92.826 207.334 207.336 207.334 114.508 0 207.332-92.826 207.332-207.334-0.002-114.51-92.824-207.334-207.332-207.334z" />
<glyph unicode="&#xe995;" glyph-name="cogs" d="M363.722 237.948l41.298 57.816-45.254 45.256-57.818-41.296c-10.722 5.994-22.204 10.774-34.266 14.192l-11.682 70.084h-64l-11.68-70.086c-12.062-3.418-23.544-8.198-34.266-14.192l-57.818 41.298-45.256-45.256 41.298-57.816c-5.994-10.72-10.774-22.206-14.192-34.266l-70.086-11.682v-64l70.086-11.682c3.418-12.060 8.198-23.544 14.192-34.266l-41.298-57.816 45.254-45.256 57.818 41.296c10.722-5.994 22.204-10.774 34.266-14.192l11.682-70.084h64l11.68 70.086c12.062 3.418 23.544 8.198 34.266 14.192l57.818-41.296 45.254 45.256-41.298 57.816c5.994 10.72 10.774 22.206 14.192 34.266l70.088 11.68v64l-70.086 11.682c-3.418 12.060-8.198 23.544-14.192 34.266zM224 96c-35.348 0-64 28.654-64 64s28.652 64 64 64 64-28.654 64-64-28.652-64-64-64zM1024 576v64l-67.382 12.25c-1.242 8.046-2.832 15.978-4.724 23.79l57.558 37.1-24.492 59.128-66.944-14.468c-4.214 6.91-8.726 13.62-13.492 20.13l39.006 56.342-45.256 45.254-56.342-39.006c-6.512 4.766-13.22 9.276-20.13 13.494l14.468 66.944-59.128 24.494-37.1-57.558c-7.812 1.892-15.744 3.482-23.79 4.724l-12.252 67.382h-64l-12.252-67.382c-8.046-1.242-15.976-2.832-23.79-4.724l-37.098 57.558-59.128-24.492 14.468-66.944c-6.91-4.216-13.62-8.728-20.13-13.494l-56.342 39.006-45.254-45.254 39.006-56.342c-4.766-6.51-9.278-13.22-13.494-20.13l-66.944 14.468-24.492-59.128 57.558-37.1c-1.892-7.812-3.482-15.742-4.724-23.79l-67.384-12.252v-64l67.382-12.25c1.242-8.046 2.832-15.978 4.724-23.79l-57.558-37.1 24.492-59.128 66.944 14.468c4.216-6.91 8.728-13.618 13.494-20.13l-39.006-56.342 45.254-45.256 56.342 39.006c6.51-4.766 13.22-9.276 20.13-13.492l-14.468-66.944 59.128-24.492 37.102 57.558c7.81-1.892 15.742-3.482 23.788-4.724l12.252-67.384h64l12.252 67.382c8.044 1.242 15.976 2.832 23.79 4.724l37.1-57.558 59.128 24.492-14.468 66.944c6.91 4.216 13.62 8.726 20.13 13.492l56.342-39.006 45.256 45.256-39.006 56.342c4.766 6.512 9.276 13.22 13.492 20.13l66.944-14.468 24.492 59.13-57.558 37.1c1.892 7.812 3.482 15.742 4.724 23.79l67.382 12.25zM672 468.8c-76.878 0-139.2 62.322-139.2 139.2s62.32 139.2 139.2 139.2 139.2-62.322 139.2-139.2c0-76.878-62.32-139.2-139.2-139.2z" />
<glyph unicode="&#xe9a6;" glyph-name="meter" d="M512 896c282.77 0 512-229.23 512-512 0-192.792-106.576-360.666-264.008-448h-495.984c-157.432 87.334-264.008 255.208-264.008 448 0 282.77 229.23 512 512 512zM801.914 94.086c77.438 77.44 120.086 180.398 120.086 289.914h-90v64h85.038c-7.014 44.998-21.39 88.146-42.564 128h-106.474v64h64.284c-9.438 11.762-19.552 23.096-30.37 33.914-46.222 46.22-101.54 80.038-161.914 99.798v-69.712h-64v85.040c-20.982 3.268-42.36 4.96-64 4.96s-43.018-1.69-64-4.96v-85.040h-64v69.712c-60.372-19.76-115.692-53.576-161.914-99.798-10.818-10.818-20.932-22.152-30.37-33.914h64.284v-64h-106.476c-21.174-39.854-35.552-83.002-42.564-128h85.040v-64h-90c0-109.516 42.648-212.474 120.086-289.914 10.71-10.71 21.924-20.728 33.56-30.086h192.354l36.572 512h54.856l36.572-512h192.354c11.636 9.358 22.852 19.378 33.56 30.086z" />
<glyph unicode="&#xe9a7;" glyph-name="meter2" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM302.836 125.848c11.106 30.632 17.164 63.688 17.164 98.152 0 124.35-78.81 230.292-189.208 270.606 10.21 84.924 48.254 163.498 109.678 224.924 72.53 72.526 168.96 112.47 271.53 112.47s199-39.944 271.53-112.47c61.428-61.426 99.468-140 109.682-224.924-110.402-40.314-189.212-146.256-189.212-270.606 0-34.468 6.060-67.52 17.166-98.15-61.706-40.242-133.77-61.85-209.166-61.85-75.394 0-147.458 21.608-209.164 61.848zM551.754 319.004c13.878-3.494 24.246-16.080 24.246-31.004v-64c0-17.6-14.4-32-32-32h-64c-17.6 0-32 14.4-32 32v64c0 14.924 10.368 27.51 24.246 31.004l23.754 448.996h32l23.754-448.996z" />
<glyph unicode="&#xe9c7;" glyph-name="download3" d="M736 512l-256-256-256 256h160v384h192v-384zM480 256h-480v-256h960v256h-480zM896 128h-128v64h128v-64z" />
<glyph unicode="&#xe9c8;" glyph-name="upload3" d="M480 256h-480v-256h960v256h-480zM896 128h-128v64h128v-64zM224 640l256 256 256-256h-160v-320h-192v320z" />
<glyph unicode="&#xe9ca;" glyph-name="earth" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512-0.002c-62.958 0-122.872 13.012-177.23 36.452l233.148 262.29c5.206 5.858 8.082 13.422 8.082 21.26v96c0 17.674-14.326 32-32 32-112.99 0-232.204 117.462-233.374 118.626-6 6.002-14.14 9.374-22.626 9.374h-128c-17.672 0-32-14.328-32-32v-192c0-12.122 6.848-23.202 17.69-28.622l110.31-55.156v-187.886c-116.052 80.956-192 215.432-192 367.664 0 68.714 15.49 133.806 43.138 192h116.862c8.488 0 16.626 3.372 22.628 9.372l128 128c6 6.002 9.372 14.14 9.372 22.628v77.412c40.562 12.074 83.518 18.588 128 18.588 70.406 0 137.004-16.26 196.282-45.2-4.144-3.502-8.176-7.164-12.046-11.036-36.266-36.264-56.236-84.478-56.236-135.764s19.97-99.5 56.236-135.764c36.434-36.432 85.218-56.264 135.634-56.26 3.166 0 6.342 0.080 9.518 0.236 13.814-51.802 38.752-186.656-8.404-372.334-0.444-1.744-0.696-3.488-0.842-5.224-81.324-83.080-194.7-134.656-320.142-134.656z" />
<glyph unicode="&#xe9cc;" glyph-name="flag" d="M0 960h128v-1024h-128v1024zM832 316.998c82.624 0 154.57 19.984 192 49.5v512c-37.43-29.518-109.376-49.502-192-49.502s-154.57 19.984-192 49.502v-512c37.43-29.516 109.376-49.5 192-49.5zM608 927.472c-46.906 19.94-115.52 32.528-192 32.528-96.396 0-180.334-19.984-224-49.502v-512c43.666 29.518 127.604 49.502 224 49.502 76.48 0 145.094-12.588 192-32.528v512z" />
<glyph unicode="&#xe9cd;" glyph-name="attachment" d="M665.832 632.952l-64.952 64.922-324.81-324.742c-53.814-53.792-53.814-141.048 0-194.844 53.804-53.792 141.060-53.792 194.874 0l389.772 389.708c89.714 89.662 89.714 235.062 0 324.726-89.666 89.704-235.112 89.704-324.782 0l-409.23-409.178c-0.29-0.304-0.612-0.576-0.876-0.846-125.102-125.096-125.102-327.856 0-452.906 125.054-125.056 327.868-125.056 452.988 0 0.274 0.274 0.516 0.568 0.82 0.876l0.032-0.034 279.332 279.292-64.986 64.92-279.33-279.262c-0.296-0.268-0.564-0.57-0.846-0.844-89.074-89.058-233.98-89.058-323.076 0-89.062 89.042-89.062 233.922 0 322.978 0.304 0.304 0.604 0.582 0.888 0.846l-0.046 0.060 409.28 409.166c53.712 53.738 141.144 53.738 194.886 0 53.712-53.734 53.712-141.148 0-194.84l-389.772-389.7c-17.936-17.922-47.054-17.922-64.972 0-17.894 17.886-17.894 47.032 0 64.92l324.806 324.782z" />
<glyph unicode="&#xe9ce;" glyph-name="eye" d="M512 768c-223.318 0-416.882-130.042-512-320 95.118-189.958 288.682-320 512-320 223.312 0 416.876 130.042 512 320-95.116 189.958-288.688 320-512 320zM764.45 598.296c60.162-38.374 111.142-89.774 149.434-150.296-38.292-60.522-89.274-111.922-149.436-150.296-75.594-48.218-162.89-73.704-252.448-73.704-89.56 0-176.858 25.486-252.452 73.704-60.158 38.372-111.138 89.772-149.432 150.296 38.292 60.524 89.274 111.924 149.434 150.296 3.918 2.5 7.876 4.922 11.86 7.3-9.96-27.328-15.41-56.822-15.41-87.596 0-141.382 114.616-256 256-256 141.382 0 256 114.618 256 256 0 30.774-5.452 60.268-15.408 87.598 3.978-2.378 7.938-4.802 11.858-7.302v0zM512 544c0-53.020-42.98-96-96-96s-96 42.98-96 96 42.98 96 96 96 96-42.982 96-96z" />
<glyph unicode="&#xe9cf;" glyph-name="eye-plus" d="M1024 832h-128v128h-128v-128h-128v-128h128v-128h128v128h128zM863.862 513.972c18.436-20.478 35.192-42.53 50.022-65.972-38.292-60.522-89.274-111.922-149.436-150.296-75.594-48.218-162.89-73.704-252.448-73.704-89.56 0-176.86 25.486-252.454 73.704-60.156 38.372-111.136 89.772-149.43 150.296 38.292 60.524 89.274 111.924 149.434 150.296 3.918 2.5 7.876 4.922 11.862 7.3-9.962-27.328-15.412-56.822-15.412-87.596 0-141.382 114.616-256 256-256 141.38 0 256 114.618 256 256 0 0.692-0.018 1.38-0.024 2.072-109.284 28.138-190.298 126.63-191.932 244.31-21.026 2.38-42.394 3.618-64.044 3.618-223.318 0-416.882-130.042-512-320 95.118-189.958 288.682-320 512-320 223.31 0 416.876 130.042 512 320-17.64 35.23-38.676 68.394-62.65 99.054-29.28-17.178-62.272-28.71-97.488-33.082zM416 640c53.020 0 96-42.982 96-96 0-53.020-42.98-96-96-96s-96 42.98-96 96 42.98 96 96 96z" />
<glyph unicode="&#xe9d0;" glyph-name="eye-minus" d="M640 832h384v-128h-384v128zM870.32 640h-294.32v124.388c-21.014 2.376-42.364 3.612-64 3.612-223.318 0-416.882-130.042-512-320 95.118-189.958 288.682-320 512-320 223.31 0 416.876 130.042 512 320-37.396 74.686-90.020 140.1-153.68 192zM416 640c53.020 0 96-42.982 96-96 0-53.020-42.98-96-96-96s-96 42.98-96 96 42.98 96 96 96zM764.448 297.704c-75.594-48.218-162.89-73.704-252.448-73.704-89.56 0-176.86 25.486-252.454 73.704-60.156 38.372-111.136 89.772-149.43 150.296 38.292 60.524 89.274 111.924 149.434 150.296 3.918 2.5 7.876 4.922 11.862 7.3-9.962-27.328-15.412-56.822-15.412-87.596 0-141.382 114.616-256 256-256 141.38 0 256 114.618 256 256 0 30.774-5.454 60.268-15.408 87.598 3.976-2.378 7.938-4.802 11.858-7.302 60.162-38.374 111.142-89.774 149.434-150.296-38.292-60.522-89.274-111.922-149.436-150.296z" />
<glyph unicode="&#xe9d7;" glyph-name="star-empty" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538zM512 206.502l-223.462-117.48 42.676 248.83-180.786 176.222 249.84 36.304 111.732 226.396 111.736-226.396 249.836-36.304-180.788-176.222 42.678-248.83-223.462 117.48z" />
<glyph unicode="&#xe9d8;" glyph-name="star-half" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538zM512 206.502l-0.942-0.496 0.942 570.768 111.736-226.396 249.836-36.304-180.788-176.222 42.678-248.83-223.462 117.48z" />
<glyph unicode="&#xe9d9;" glyph-name="star-full" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538z" />
<glyph unicode="&#xea07;" glyph-name="warning" d="M512 867.226l429.102-855.226h-858.206l429.104 855.226zM512 960c-22.070 0-44.14-14.882-60.884-44.648l-437.074-871.112c-33.486-59.532-5-108.24 63.304-108.24h869.308c68.3 0 96.792 48.708 63.3 108.24h0.002l-437.074 871.112c-16.742 29.766-38.812 44.648-60.882 44.648v0zM576 128c0-35.346-28.654-64-64-64s-64 28.654-64 64c0 35.346 28.654 64 64 64s64-28.654 64-64zM512 256c-35.346 0-64 28.654-64 64v192c0 35.346 28.654 64 64 64s64-28.654 64-64v-192c0-35.346-28.654-64-64-64z" />
<glyph unicode="&#xea08;" glyph-name="notification" d="M512 864c-111.118 0-215.584-43.272-294.156-121.844s-121.844-183.038-121.844-294.156c0-111.118 43.272-215.584 121.844-294.156s183.038-121.844 294.156-121.844c111.118 0 215.584 43.272 294.156 121.844s121.844 183.038 121.844 294.156c0 111.118-43.272 215.584-121.844 294.156s-183.038 121.844-294.156 121.844zM512 960v0c282.77 0 512-229.23 512-512s-229.23-512-512-512c-282.77 0-512 229.23-512 512s229.23 512 512 512zM448 256h128v-128h-128zM448 768h128v-384h-128z" />
<glyph unicode="&#xea09;" glyph-name="question" d="M448 256h128v-128h-128zM704 704c35.346 0 64-28.654 64-64v-192l-192-128h-128v64l192 128v64h-320v128h384zM512 864c-111.118 0-215.584-43.272-294.156-121.844s-121.844-183.038-121.844-294.156c0-111.118 43.272-215.584 121.844-294.156s183.038-121.844 294.156-121.844c111.118 0 215.584 43.272 294.156 121.844s121.844 183.038 121.844 294.156c0 111.118-43.272 215.584-121.844 294.156s-183.038 121.844-294.156 121.844zM512 960v0c282.77 0 512-229.23 512-512s-229.23-512-512-512c-282.77 0-512 229.23-512 512s229.23 512 512 512z" />
<glyph unicode="&#xea0a;" glyph-name="plus" d="M992 576h-352v352c0 17.672-14.328 32-32 32h-192c-17.672 0-32-14.328-32-32v-352h-352c-17.672 0-32-14.328-32-32v-192c0-17.672 14.328-32 32-32h352v-352c0-17.672 14.328-32 32-32h192c17.672 0 32 14.328 32 32v352h352c17.672 0 32 14.328 32 32v192c0 17.672-14.328 32-32 32z" />
<glyph unicode="&#xea0b;" glyph-name="minus" d="M0 544v-192c0-17.672 14.328-32 32-32h960c17.672 0 32 14.328 32 32v192c0 17.672-14.328 32-32 32h-960c-17.672 0-32-14.328-32-32z" />
<glyph unicode="&#xea0c;" glyph-name="info" d="M448 656c0 26.4 21.6 48 48 48h32c26.4 0 48-21.6 48-48v-32c0-26.4-21.6-48-48-48h-32c-26.4 0-48 21.6-48 48v32zM640 192h-256v64h64v192h-64v64h192v-256h64zM512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 32c-229.75 0-416 186.25-416 416s186.25 416 416 416 416-186.25 416-416-186.25-416-416-416z" />
<glyph unicode="&#xea0d;" glyph-name="cancel-circle" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 32c-229.75 0-416 186.25-416 416s186.25 416 416 416 416-186.25 416-416-186.25-416-416-416zM672 704l-160-160-160 160-96-96 160-160-160-160 96-96 160 160 160-160 96 96-160 160 160 160z" />
<glyph unicode="&#xea0e;" glyph-name="blocked" d="M874.040 810.040c-96.706 96.702-225.28 149.96-362.040 149.96s-265.334-53.258-362.040-149.96c-96.702-96.706-149.96-225.28-149.96-362.040s53.258-265.334 149.96-362.040c96.706-96.702 225.28-149.96 362.040-149.96s265.334 53.258 362.040 149.96c96.702 96.706 149.96 225.28 149.96 362.040s-53.258 265.334-149.96 362.040zM896 448c0-82.814-26.354-159.588-71.112-222.38l-535.266 535.268c62.792 44.758 139.564 71.112 222.378 71.112 211.738 0 384-172.262 384-384zM128 448c0 82.814 26.354 159.586 71.112 222.378l535.27-535.268c-62.794-44.756-139.568-71.11-222.382-71.11-211.738 0-384 172.262-384 384z" />
<glyph unicode="&#xea0f;" glyph-name="cross" d="M1014.662 137.34c-0.004 0.004-0.008 0.008-0.012 0.010l-310.644 310.65 310.644 310.65c0.004 0.004 0.008 0.006 0.012 0.010 3.344 3.346 5.762 7.254 7.312 11.416 4.246 11.376 1.824 24.682-7.324 33.83l-146.746 146.746c-9.148 9.146-22.45 11.566-33.828 7.32-4.16-1.55-8.070-3.968-11.418-7.31 0-0.004-0.004-0.006-0.008-0.010l-310.648-310.652-310.648 310.65c-0.004 0.004-0.006 0.006-0.010 0.010-3.346 3.342-7.254 5.76-11.414 7.31-11.38 4.248-24.682 1.826-33.83-7.32l-146.748-146.748c-9.148-9.148-11.568-22.452-7.322-33.828 1.552-4.16 3.97-8.072 7.312-11.416 0.004-0.002 0.006-0.006 0.010-0.010l310.65-310.648-310.65-310.652c-0.002-0.004-0.006-0.006-0.008-0.010-3.342-3.346-5.76-7.254-7.314-11.414-4.248-11.376-1.826-24.682 7.322-33.83l146.748-146.746c9.15-9.148 22.452-11.568 33.83-7.322 4.16 1.552 8.070 3.97 11.416 7.312 0.002 0.004 0.006 0.006 0.010 0.010l310.648 310.65 310.648-310.65c0.004-0.002 0.008-0.006 0.012-0.008 3.348-3.344 7.254-5.762 11.414-7.314 11.378-4.246 24.684-1.826 33.828 7.322l146.746 146.748c9.148 9.148 11.57 22.454 7.324 33.83-1.552 4.16-3.97 8.068-7.314 11.414z" />
<glyph unicode="&#xea10;" glyph-name="checkmark" d="M864 832l-480-480-224 224-160-160 384-384 640 640z" />
<glyph unicode="&#xea17;" glyph-name="stop" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 32c-229.75 0-416 186.25-416 416s186.25 416 416 416 416-186.25 416-416-186.25-416-416-416zM320 640h384v-384h-384z" />
<glyph unicode="&#xea1e;" glyph-name="stop2" d="M128 832h768v-768h-768z" />
<glyph unicode="&#xea29;" glyph-name="volume-mute" d="M416.006 0c-8.328 0-16.512 3.25-22.634 9.374l-246.626 246.626h-114.746c-17.672 0-32 14.326-32 32v320c0 17.672 14.328 32 32 32h114.746l246.626 246.628c9.154 9.154 22.916 11.89 34.874 6.936 11.958-4.952 19.754-16.622 19.754-29.564v-832c0-12.944-7.796-24.612-19.754-29.564-3.958-1.64-8.118-2.436-12.24-2.436z" />
<glyph unicode="&#xea2a;" glyph-name="volume-mute2" d="M960 340.852v-84.852h-84.852l-107.148 107.148-107.148-107.148h-84.852v84.852l107.148 107.148-107.148 107.148v84.852h84.852l107.148-107.148 107.148 107.148h84.852v-84.852l-107.148-107.148 107.148-107.148zM416.006 0c-8.328 0-16.512 3.25-22.634 9.374l-246.626 246.626h-114.746c-17.672 0-32 14.326-32 32v320c0 17.672 14.328 32 32 32h114.746l246.626 246.628c9.154 9.154 22.916 11.89 34.874 6.936 11.958-4.952 19.754-16.622 19.754-29.564v-832c0-12.944-7.796-24.612-19.754-29.564-3.958-1.64-8.118-2.436-12.24-2.436z" />
<glyph unicode="&#xea2b;" glyph-name="volume-increase" d="M1024 384h-192v-192h-128v192h-192v128h192v192h128v-192h192v-128zM416.006 0c-8.328 0-16.512 3.25-22.634 9.374l-246.626 246.626h-114.746c-17.672 0-32 14.326-32 32v320c0 17.672 14.328 32 32 32h114.746l246.626 246.628c9.154 9.154 22.916 11.89 34.874 6.936 11.958-4.952 19.754-16.622 19.754-29.564v-832c0-12.944-7.796-24.612-19.754-29.564-3.958-1.64-8.118-2.436-12.24-2.436z" />
<glyph unicode="&#xea2c;" glyph-name="volume-decrease" d="M512 512h512v-128h-512v128zM416.006 0c-8.328 0-16.512 3.25-22.634 9.374l-246.626 246.626h-114.746c-17.672 0-32 14.326-32 32v320c0 17.672 14.328 32 32 32h114.746l246.626 246.628c9.154 9.154 22.916 11.89 34.874 6.936 11.958-4.952 19.754-16.622 19.754-29.564v-832c0-12.944-7.796-24.612-19.754-29.564-3.958-1.64-8.118-2.436-12.24-2.436z" />
<glyph unicode="&#xea3a;" glyph-name="arrow-up2" d="M877.254 557.254l-320 320c-24.992 24.994-65.514 24.994-90.508 0l-320-320c-24.994-24.994-24.994-65.516 0-90.51 24.994-24.996 65.516-24.996 90.51 0l210.744 210.746v-613.49c0-35.346 28.654-64 64-64s64 28.654 64 64v613.49l210.746-210.746c12.496-12.496 28.876-18.744 45.254-18.744s32.758 6.248 45.254 18.746c24.994 24.994 24.994 65.514 0 90.508z" />
<glyph unicode="&#xea3c;" glyph-name="arrow-right2" d="M621.254 82.746l320 320c24.994 24.992 24.994 65.516 0 90.51l-320 320c-24.994 24.992-65.516 24.992-90.51 0-24.994-24.994-24.994-65.516 0-90.51l210.746-210.746h-613.49c-35.346 0-64-28.654-64-64s28.654-64 64-64h613.49l-210.746-210.746c-12.496-12.496-18.744-28.876-18.744-45.254s6.248-32.758 18.744-45.254c24.994-24.994 65.516-24.994 90.51 0z" />
<glyph unicode="&#xea3e;" glyph-name="arrow-down2" d="M877.254 338.746l-320-320c-24.992-24.994-65.514-24.994-90.508 0l-320 320c-24.994 24.994-24.994 65.516 0 90.51 24.994 24.996 65.516 24.996 90.51 0l210.744-210.746v613.49c0 35.346 28.654 64 64 64s64-28.654 64-64v-613.49l210.746 210.746c12.496 12.496 28.876 18.744 45.254 18.744s32.758-6.248 45.254-18.746c24.994-24.994 24.994-65.514 0-90.508z" />
<glyph unicode="&#xea40;" glyph-name="arrow-left2" d="M402.746 82.746l-320 320c-24.994 24.992-24.994 65.516 0 90.51l320 320c24.994 24.992 65.516 24.992 90.51 0 24.994-24.994 24.994-65.516 0-90.51l-210.746-210.746h613.49c35.346 0 64-28.654 64-64s-28.654-64-64-64h-613.49l210.746-210.746c12.496-12.496 18.744-28.876 18.744-45.254s-6.248-32.758-18.744-45.254c-24.994-24.994-65.516-24.994-90.51 0z" />
<glyph unicode="&#xea41;" glyph-name="circle-up" d="M0 448c0-282.77 229.23-512 512-512s512 229.23 512 512-229.23 512-512 512-512-229.23-512-512zM928 448c0-229.75-186.25-416-416-416s-416 186.25-416 416 186.25 416 416 416 416-186.25 416-416zM706.744 290.744l90.512 90.512-285.256 285.254-285.254-285.256 90.508-90.508 194.746 194.744z" />
<glyph unicode="&#xea42;" glyph-name="circle-right" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 32c-229.75 0-416 186.25-416 416s186.25 416 416 416 416-186.25 416-416-186.25-416-416-416zM354.744 253.256l90.512-90.512 285.254 285.256-285.256 285.254-90.508-90.508 194.744-194.746z" />
<glyph unicode="&#xea43;" glyph-name="circle-down" d="M1024 448c0 282.77-229.23 512-512 512s-512-229.23-512-512 229.23-512 512-512 512 229.23 512 512zM96 448c0 229.75 186.25 416 416 416s416-186.25 416-416-186.25-416-416-416-416 186.25-416 416zM317.256 605.256l-90.512-90.512 285.256-285.254 285.254 285.256-90.508 90.508-194.746-194.744z" />
<glyph unicode="&#xea44;" glyph-name="circle-left" d="M512-64c282.77 0 512 229.23 512 512s-229.23 512-512 512-512-229.23-512-512 229.23-512 512-512zM512 864c229.75 0 416-186.25 416-416s-186.25-416-416-416-416 186.25-416 416 186.25 416 416 416zM669.256 642.744l-90.512 90.512-285.254-285.256 285.256-285.254 90.508 90.508-194.744 194.746z" />
<glyph unicode="&#xea48;" glyph-name="sort-alpha-asc" d="M320 192v768h-128v-768h-160l224-224 224 224h-160zM928-64h-256c-11.8 0-22.644 6.496-28.214 16.9-5.566 10.404-4.958 23.030 1.59 32.85l222.832 334.25h-196.208c-17.672 0-32 14.328-32 32s14.328 32 32 32h256c11.8 0 22.644-6.496 28.214-16.9 5.566-10.404 4.958-23.030-1.59-32.85l-222.83-334.25h196.206c17.672 0 32-14.328 32-32s-14.328-32-32-32zM1020.622 558.314l-192.002 384c-5.42 10.842-16.502 17.69-28.622 17.69-12.122 0-23.202-6.848-28.624-17.69l-191.996-384c-7.904-15.806-1.496-35.030 14.31-42.932 4.594-2.296 9.476-3.386 14.288-3.386 11.736 0 23.040 6.484 28.644 17.698l55.156 110.31h216.446l55.156-110.31c7.902-15.806 27.124-22.21 42.932-14.31 15.808 7.902 22.216 27.124 14.312 42.93zM723.778 704.004l76.22 152.446 76.224-152.446h-152.444z" />
<glyph unicode="&#xea49;" glyph-name="sort-alpha-desc" d="M320 192v768h-128v-768h-160l224-224 224 224h-160zM928 512h-256c-11.8 0-22.644 6.496-28.214 16.9-5.566 10.406-4.958 23.030 1.59 32.85l222.832 334.25h-196.208c-17.672 0-32 14.328-32 32s14.328 32 32 32h256c11.8 0 22.644-6.496 28.214-16.9 5.566-10.406 4.958-23.030-1.59-32.85l-222.83-334.25h196.206c17.672 0 32-14.328 32-32s-14.328-32-32-32zM1020.622-17.69l-192.002 384c-5.42 10.842-16.502 17.69-28.622 17.69-12.122 0-23.202-6.848-28.624-17.69l-191.996-384c-7.904-15.806-1.496-35.030 14.31-42.932 4.594-2.296 9.476-3.386 14.288-3.386 11.736 0 23.040 6.484 28.644 17.698l55.158 110.31h216.446l55.156-110.31c7.902-15.806 27.124-22.21 42.932-14.31 15.806 7.902 22.214 27.124 14.31 42.93zM723.778 128l76.22 152.446 76.226-152.446h-152.446z" />
<glyph unicode="&#xea4c;" glyph-name="sort-amount-asc" d="M320 192v768h-128v-768h-160l224-224 224 224h-160zM448 384h576v-128h-576v128zM448 576h448v-128h-448v128zM448 768h320v-128h-320v128zM448 960h192v-128h-192v128z" />
<glyph unicode="&#xea4d;" glyph-name="sort-amount-desc" d="M320 192v768h-128v-768h-160l224-224 224 224h-160zM448 960h576v-128h-576v128zM448 768h448v-128h-448v128zM448 576h320v-128h-320v128zM448 384h192v-128h-192v128z" />
<glyph unicode="&#xea52;" glyph-name="checkbox-checked" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM448 165.49l-237.254 237.256 90.51 90.508 146.744-146.744 306.746 306.746 90.508-90.51-397.254-397.256z" />
<glyph unicode="&#xea53;" glyph-name="checkbox-unchecked" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM896 64h-768v768h768v-768z" />
<glyph unicode="&#xea54;" glyph-name="radio-checked" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 64c-212.078 0-384 171.922-384 384s171.922 384 384 384c212.078 0 384-171.922 384-384s-171.922-384-384-384zM320 448c0 106.039 85.961 192 192 192s192-85.961 192-192c0-106.039-85.961-192-192-192s-192 85.961-192 192z" />
<glyph unicode="&#xea55;" glyph-name="radio-checked2" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 320c-70.692 0-128 57.306-128 128 0 70.692 57.308 128 128 128 70.694 0 128-57.308 128-128 0-70.694-57.306-128-128-128z" />
<glyph unicode="&#xea56;" glyph-name="radio-unchecked" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 64c-212.078 0-384 171.922-384 384s171.922 384 384 384c212.078 0 384-171.922 384-384s-171.922-384-384-384z" />
<glyph unicode="&#xea57;" glyph-name="crop" d="M832 704l192 192-64 64-192-192h-448v192h-128v-192h-192v-128h192v-512h512v-192h128v192h192v128h-192v448zM320 640h320l-320-320v320zM384 256l320 320v-320h-320z" />
<glyph unicode="&#xea5b;" glyph-name="filter" d="M512 960c-282.77 0-512-71.634-512-160v-96l384-384v-320c0-35.346 57.306-64 128-64 70.692 0 128 28.654 128 64v320l384 384v96c0 88.366-229.23 160-512 160zM94.384 821.176c23.944 13.658 57.582 26.62 97.278 37.488 87.944 24.076 201.708 37.336 320.338 37.336 118.628 0 232.394-13.26 320.338-37.336 39.696-10.868 73.334-23.83 97.28-37.488 15.792-9.006 24.324-16.624 28.296-21.176-3.972-4.552-12.506-12.168-28.296-21.176-23.946-13.658-57.584-26.62-97.28-37.488-87.942-24.076-201.708-37.336-320.338-37.336s-232.394 13.26-320.338 37.336c-39.696 10.868-73.334 23.83-97.278 37.488-15.792 9.008-24.324 16.624-28.298 21.176 3.974 4.552 12.506 12.168 28.298 21.176z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,345 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?rby20y');
src: url('fonts/icomoon.eot?rby20y#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?rby20y') format('truetype'),
url('fonts/icomoon.woff?rby20y') format('woff'),
url('fonts/icomoon.svg?rby20y#icomoon') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
}
[class^="icon-"], [class*=" icon-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icomoon' !important;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-up:before {
content: "\e900";
}
.icon-down:before {
content: "\e901";
}
.icon-droplet:before {
content: "\e90b";
}
.icon-paint-format:before {
content: "\e90c";
}
.icon-image:before {
content: "\e90d";
}
.icon-images:before {
content: "\e90e";
}
.icon-camera:before {
content: "\e90f";
}
.icon-video-camera:before {
content: "\e914";
}
.icon-book:before {
content: "\e91f";
}
.icon-file-empty:before {
content: "\e924";
}
.icon-files-empty:before {
content: "\e925";
}
.icon-file-play:before {
content: "\e929";
}
.icon-file-video:before {
content: "\e92a";
}
.icon-file-zip:before {
content: "\e92b";
}
.icon-stack:before {
content: "\e92e";
}
.icon-folder:before {
content: "\e92f";
}
.icon-folder-open:before {
content: "\e930";
}
.icon-folder-plus:before {
content: "\e931";
}
.icon-folder-minus:before {
content: "\e932";
}
.icon-folder-download:before {
content: "\e933";
}
.icon-folder-upload:before {
content: "\e934";
}
.icon-price-tag:before {
content: "\e935";
}
.icon-price-tags:before {
content: "\e936";
}
.icon-location:before {
content: "\e947";
}
.icon-history:before {
content: "\e94d";
}
.icon-alarm:before {
content: "\e950";
}
.icon-bell:before {
content: "\e951";
}
.icon-stopwatch:before {
content: "\e952";
}
.icon-display:before {
content: "\e956";
}
.icon-mobile:before {
content: "\e958";
}
.icon-mobile2:before {
content: "\e959";
}
.icon-box-add:before {
content: "\e95e";
}
.icon-box-remove:before {
content: "\e95f";
}
.icon-download:before {
content: "\e960";
}
.icon-upload:before {
content: "\e961";
}
.icon-floppy-disk:before {
content: "\e962";
}
.icon-database:before {
content: "\e964";
}
.icon-undo:before {
content: "\e965";
}
.icon-redo:before {
content: "\e966";
}
.icon-undo2:before {
content: "\e967";
}
.icon-redo2:before {
content: "\e968";
}
.icon-user:before {
content: "\e971";
}
.icon-users:before {
content: "\e972";
}
.icon-spinner2:before {
content: "\e97b";
}
.icon-spinner11:before {
content: "\e984";
}
.icon-binoculars:before {
content: "\e985";
}
.icon-search:before {
content: "\e986";
}
.icon-zoom-in:before {
content: "\e987";
}
.icon-zoom-out:before {
content: "\e988";
}
.icon-key:before {
content: "\e98d";
}
.icon-key2:before {
content: "\e98e";
}
.icon-lock:before {
content: "\e98f";
}
.icon-unlocked:before {
content: "\e990";
}
.icon-wrench:before {
content: "\e991";
}
.icon-equalizer:before {
content: "\e992";
}
.icon-equalizer2:before {
content: "\e993";
}
.icon-cog:before {
content: "\e994";
}
.icon-cogs:before {
content: "\e995";
}
.icon-meter:before {
content: "\e9a6";
}
.icon-meter2:before {
content: "\e9a7";
}
.icon-download3:before {
content: "\e9c7";
}
.icon-upload3:before {
content: "\e9c8";
}
.icon-earth:before {
content: "\e9ca";
}
.icon-flag:before {
content: "\e9cc";
}
.icon-attachment:before {
content: "\e9cd";
}
.icon-eye:before {
content: "\e9ce";
}
.icon-eye-plus:before {
content: "\e9cf";
}
.icon-eye-minus:before {
content: "\e9d0";
}
.icon-star-empty:before {
content: "\e9d7";
}
.icon-star-half:before {
content: "\e9d8";
}
.icon-star-full:before {
content: "\e9d9";
}
.icon-warning:before {
content: "\ea07";
}
.icon-notification:before {
content: "\ea08";
}
.icon-question:before {
content: "\ea09";
}
.icon-plus:before {
content: "\ea0a";
}
.icon-minus:before {
content: "\ea0b";
}
.icon-info:before {
content: "\ea0c";
}
.icon-cancel-circle:before {
content: "\ea0d";
}
.icon-blocked:before {
content: "\ea0e";
}
.icon-cross:before {
content: "\ea0f";
}
.icon-checkmark:before {
content: "\ea10";
}
.icon-stop:before {
content: "\ea17";
}
.icon-stop2:before {
content: "\ea1e";
}
.icon-volume-mute:before {
content: "\ea29";
}
.icon-volume-mute2:before {
content: "\ea2a";
}
.icon-volume-increase:before {
content: "\ea2b";
}
.icon-volume-decrease:before {
content: "\ea2c";
}
.icon-arrow-up2:before {
content: "\ea3a";
}
.icon-arrow-right2:before {
content: "\ea3c";
}
.icon-arrow-down2:before {
content: "\ea3e";
}
.icon-arrow-left2:before {
content: "\ea40";
}
.icon-circle-up:before {
content: "\ea41";
}
.icon-circle-right:before {
content: "\ea42";
}
.icon-circle-down:before {
content: "\ea43";
}
.icon-circle-left:before {
content: "\ea44";
}
.icon-sort-alpha-asc:before {
content: "\ea48";
}
.icon-sort-alpha-desc:before {
content: "\ea49";
}
.icon-sort-amount-asc:before {
content: "\ea4c";
}
.icon-sort-amount-desc:before {
content: "\ea4d";
}
.icon-checkbox-checked:before {
content: "\ea52";
}
.icon-checkbox-unchecked:before {
content: "\ea53";
}
.icon-radio-checked:before {
content: "\ea54";
}
.icon-radio-checked2:before {
content: "\ea55";
}
.icon-radio-unchecked:before {
content: "\ea56";
}
.icon-crop:before {
content: "\ea57";
}
.icon-filter:before {
content: "\ea5b";
}

1311
ram/driver/static/wt/css/jquery-ui.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,147 @@
.rotaryswitchPlugin {
position: relative;
width: 51px;
height: 51px;
cursor: pointer;
background: url(../images/darkSmallBackground.png) no-repeat;
background-size: 100%;
background-position: center;
}
@media only screen and (-moz-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
.rotaryswitchPlugin {
background-image: url(../images/darkSmallBackground@2x.png);
background-size: 100% 100%;
background-position: center;
}
}
.rotaryswitchPlugin .switch {
width: 100%;
height: 100%;
background: url(../images/darkSmallFront.png) no-repeat;
background-size: 93%;
background-position: center;
}
@media only screen and (-moz-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
.rotaryswitchPlugin .switch {
background-image: url(../images/darkSmallFront@2x.png);
background-size: 100% 100%;
background-position: center;
}
}
.rotaryswitchPlugin .mark {
position: absolute;
left: 50%;
top: 50%;
margin: -1px 0 0 0;
width: 1px;
height: 3px;
background-color: #0e0e0e;
}
.rotaryswitchPlugin input {
position: absolute;
top: 0;
bottom: 0;
right: -60px;
width: 100%;
border: none;
z-index: 100;
text-align: left;
background: none;
font: normal 18px/18px verdana;
color: #0e0e0e;
}
.rotaryswitchPlugin.big {
width: 200px;
height: 200px;
background: url(../images/darkBigBackground.png) no-repeat;
background-size: 100%;
background-position: center;
}
@media only screen and (-moz-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
.rotaryswitchPlugin.big {
background-image: url(../images/darkBigBackground@2x.png);
background-size: 100% 100%;
background-position: center;
}
}
.rotaryswitchPlugin.big .switch { background: url(../images/darkBigFront.png) no-repeat; }
@media only screen and (-moz-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
.rotaryswitchPlugin.big .switch {
background-image: url(../images/darkBigFront@2x.png);
background-size: 100% 100%;
background-position: center;
}
}
.rotaryswitchPlugin.big .mark {
position: absolute;
left: 50%;
top: 50%;
margin: -4px 0 0 -2px;
width: 4px;
height: 10px;
background-color: #c3c3c2;
}
.rotaryswitchPlugin input {
position: absolute;
top: 0;
bottom: 0;
right: -60px;
width: 100%;
border: none;
z-index: 100;
text-align: left;
background: none;
font: normal 18px/18px verdana;
color: #0e0e0e;
}
.rotaryswitchPlugin.big.light {
margin: 36px;
background: url(../images/lightBigBackground.png) no-repeat;
background-size: 100%;
background-position: center;
display: inline-block;
}
.rotaryswitchPlugin.big.light .switch {
background: url(../images/lightBigFront.png) no-repeat;
background-size: 93%;
background-position: center;
}
.knob-value{
left: 50%;
font-size: 30px;
position: relative;
margin-left: -30px;
width: 60px;
text-align: center;
right: auto;
top: -16px;
color: #00bcd6;
text-shadow: 0 1px 1px #ffffff, 0px 0px 4px #78efff;
}
div#knobthrottle.disabled:before {
content: "";
height: 200px;
background: #ececec;
width: 200px;
display: inline-block;
position: absolute;
z-index: 100;
opacity: 0.5;
margin-left: 36px;
border-radius: 100%;
margin-top: 36px;
}
.speedController.disabled{
position: relative;
}

View File

@@ -0,0 +1,235 @@
body{
font-family: Arial, Helvetica, sans-serif;
margin: 0;
height: 100vh;
background: #efefef;
}
hr{
border-bottom: 1px solid #ffffff;
}
.column-1{
float: left;
width: 10%;
padding: 5px 15px;
}
.column-2p5 {
float: left;
width: 25%;
padding: 5px 15px;
}
.column-2 {
float: left;
width: 20%;
padding: 5px 15px;
}
.column-3 {
float: left;
width: 30%;
padding: 5px 15px;
}
.column-4 {
float: left;
width: 40%;
padding: 5px 15px;
}
.column-5 {
float: left;
width: 50%;
padding: 5px 15px;
}
.column-6{
float: left;
width: 60%;
padding: 5px 15px;
}
.column-7 {
float: left;
width: 70%;
padding: 5px 15px;
}
.column-8{
width: 80%;
float: left;
padding: 5px 15px;
}
.dcmd-clear{
padding-left: 0;
}
/* Clear floats */
.row:after {
content: "";
display: block;
clear: both;
}
.topnav{
position: fixed;
z-index: 100;
width: 100%;
}
.throttle-heading{
text-align: center;
padding-top:5px;
font-size: 14px;
display: inline-block;
}
.throttle-heading .wt-logo{
height: 48px;
width: 142px;
background: url(../images/WebThrottle.png);
display: inline-block;
background-size: 100%;
box-shadow: 0 0 3px #989898;
border-radius: 6px;
}
.throttle-heading p{
font-size: 12px;
margin: 5px;
}
.ui-widget.ui-widget-content.credits-tooltip{
border: 1px solid #ccc;
width: 200px;
padding: 10px 15px;
font-size: 14px;
border-radius: 4px;
box-shadow: 0 0 4px #aaa;
}
#rendered-form{
display: flex;
align-items: center;
height: 100%;
}
.rendered-form {
font-family: Arial, Helvetica, sans-serif;
width: 100%;
height: 100%;
position: relative;
padding-top: 68px;
}
.rendered-form .section{
width: 80%;
height: 98%;
margin: 0 10%;
background: rgba(0,0,0,0.05);
border-radius: 10px;
}
#throttle-window{
box-shadow: 0 0 5px 0px #777777;
overflow: auto;
/* height: auto; */
padding-bottom: 1px;
}
.section{
box-shadow: 0 0 5px 0px #777777;
/*overflow: auto;*/
/* height: auto; */
padding-bottom: 1px;
}
.rendered-form div {
box-sizing: border-box;
justify-content: center;
}
.rendered-form h4 {
margin: 0;
}
.nav-btn {
font-size: 30px;
cursor: pointer;
color: #666666;
padding: 10px 15px;
border: none;
background: none;
line-height: 38px;
}
.nav-btn.in{
color:#cccccc;
font-size: 40px;
position: absolute;
right: 0;
line-height: 30px;
}
.btn-expand, .btn-info{
float: right;
font-size: 20px;
cursor: pointer;
color: #666666;
padding: 10px 15px;
border: none;
background: none;
line-height: 36px;
}
.btn-info{
padding-top: 14px;
}
.nav-btn:hover {
color: #000000;
}
.nav-btn.in:hover{
color: #ffffff;
}
.menu {
background: #00A3B9;
height: 100vh;
width: 250px;
position: fixed;
top: 0;
left: 0;
z-index: 1020;
box-shadow: 0 0px 10px #666;
border-right: 1px solid #929292;
outline: none;
left: -260px;
display: none;
}
.menu .avatar img {
width: 100%;
overflow: hidden;
box-sizing: border-box;
border: 0;
}
.menu ul {
list-style: none;
padding: 0.5em 0;
margin: 0;
margin-top: 1em;
}
.menu ul li{
padding: 1em;
background-repeat: no-repeat;
background-position: left 15px center;
background-size: auto 20px;
transition: all 0.15s linear;
cursor: pointer;
color: #fff;
text-shadow: 0 0 1px #000;
}
.menu ul li:hover{
background-color:rgba(0,0,0,0.1);
}
.menu ul li:focus{
border:none;
}
.topnav .row div, .add-loco-form .row label, .add-loco-form .row input{
box-sizing: border-box;
}
.align-center{
text-align: center;
}
.pos-rel{
position: relative;
}
.spacer{
height: 5px;
}
.pl0{
padding-left: 0;
}
.pr0{
padding-right: 0;
}
.prh{
padding-left: 0;
padding-right: 0;
}

View File

@@ -0,0 +1,5 @@
.add-button {
position: absolute;
top: 1px;
left: 1px;
}

View File

@@ -0,0 +1,2 @@
/*! roundSlider v1.6.1 | (c) 2015-2020, Soundar | MIT license | http://roundsliderui.com/licence.html */
.rs-ie,.rs-edge,.rs-handle{-ms-touch-action:none;touch-action:none}.rs-control{position:relative;outline:0 none}.rs-container{position:relative}.rs-control *,.rs-control *:before,.rs-control *:after{-webkit-box-sizing:border-box;box-sizing:border-box}.rs-animation .rs-transition{transition:all 0.5s linear 0s}.rs-bar{-webkit-transform-origin:100% 50%;-ms-transform-origin:100% 50%;transform-origin:100% 50%}.rs-control .rs-split .rs-path,.rs-control .rs-overlay1,.rs-control .rs-overlay2{-webkit-transform-origin:50% 100%;-ms-transform-origin:50% 100%;transform-origin:50% 100%}.rs-control .rs-overlay{-webkit-transform-origin:100% 100%;-ms-transform-origin:100% 100%;transform-origin:100% 100%}.rs-rounded .rs-seperator,.rs-split .rs-path{-webkit-background-clip:padding-box;background-clip:padding-box}.rs-disabled{opacity:.35}.rs-inner-container{height:100%;width:100%;position:absolute;top:0;overflow:hidden}.rs-control .rs-quarter div.rs-block{height:200%;width:200%}.rs-control .rs-half.rs-top div.rs-block,.rs-control .rs-half.rs-bottom div.rs-block{height:200%;width:100%}.rs-control .rs-half.rs-left div.rs-block,.rs-control .rs-half.rs-right div.rs-block{height:100%;width:200%}.rs-control .rs-bottom .rs-block{top:auto;bottom:0}.rs-control .rs-right .rs-block{right:0}.rs-block.rs-outer{border-radius:1000px}.rs-block{height:100%;width:100%;display:block;position:absolute;top:0;overflow:hidden;z-index:3}.rs-block .rs-inner{border-radius:1000px;display:block;height:100%;width:100%;position:relative}.rs-overlay{width:50%}.rs-overlay1,.rs-overlay2{width:100%}.rs-overlay,.rs-overlay1,.rs-overlay2{position:absolute;background-color:#fff;z-index:3;top:0;height:50%}.rs-bar{display:block;position:absolute;bottom:0;height:0;z-index:10}.rs-bar.rs-rounded{z-index:5}.rs-bar .rs-seperator{height:0;display:block;float:left}.rs-bar:not(.rs-rounded) .rs-seperator{border-left:none;border-right:none}.rs-bar.rs-start .rs-seperator{border-top:none}.rs-bar.rs-end .rs-seperator{border-bottom:none}.rs-bar.rs-start.rs-rounded .rs-seperator{border-radius:0 0 1000px 1000px}.rs-bar.rs-end.rs-rounded .rs-seperator{border-radius:1000px 1000px 0 0}.rs-full .rs-bar,.rs-half .rs-bar{width:50%}.rs-half.rs-left .rs-bar,.rs-half.rs-right .rs-bar,.rs-quarter .rs-bar{width:100%}.rs-full .rs-bar,.rs-half.rs-left .rs-bar,.rs-half.rs-right .rs-bar{top:50%}.rs-bottom .rs-bar{top:0}.rs-half.rs-right .rs-bar,.rs-quarter.rs-right .rs-bar{right:100%}.rs-handle.rs-move{cursor:move}.rs-readonly .rs-handle.rs-move{cursor:default}.rs-classic-mode .rs-path{display:block;height:100%;width:100%}.rs-split .rs-path{border-radius:1000px 1000px 0 0;overflow:hidden;height:50%;position:absolute;top:0;z-index:2}.rs-control .rs-svg-container{display:block;position:absolute;top:0}.rs-control .rs-bottom .rs-svg-container{top:auto;bottom:0}.rs-control .rs-right .rs-svg-container{right:0}.rs-tooltip{position:absolute;cursor:default;border:1px solid transparent;z-index:10}.rs-full .rs-tooltip{top:50%;left:50%}.rs-bottom .rs-tooltip{top:0}.rs-top .rs-tooltip{bottom:0}.rs-right .rs-tooltip{left:0}.rs-left .rs-tooltip{right:0}.rs-half.rs-top .rs-tooltip,.rs-half.rs-bottom .rs-tooltip{left:50%}.rs-half.rs-left .rs-tooltip,.rs-half.rs-right .rs-tooltip{top:50%}.rs-tooltip .rs-input{outline:0 none;border:none;background:transparent}.rs-tooltip-text{font-family:verdana;font-size:13px;border-radius:7px;text-align:center;color:inherit}.rs-tooltip.rs-edit{padding:5px 8px}.rs-tooltip.rs-hover,.rs-tooltip.rs-edit:hover{border:1px solid #AAA;cursor:pointer}.rs-readonly .rs-tooltip.rs-edit:hover{border-color:transparent;cursor:default}.rs-tooltip.rs-center{margin:0px!important}.rs-half.rs-top .rs-tooltip.rs-center,.rs-half.rs-bottom .rs-tooltip.rs-center{transform:translate(-50%,0)}.rs-half.rs-left .rs-tooltip.rs-center,.rs-half.rs-right .rs-tooltip.rs-center{transform:translate(0,-50%)}.rs-full .rs-tooltip.rs-center{transform:translate(-50%,-50%)}.rs-tooltip.rs-reset{margin:0px!important;top:0px!important;left:0px!important}.rs-handle{border-radius:1000px;outline:0 none;float:left}.rs-handle.rs-handle-square{border-radius:0}.rs-handle-dot{border:1px solid #AAA;padding:6px}.rs-handle-dot:after{display:block;content:"";border:1px solid #AAA;height:100%;width:100%;border-radius:1000px}.rs-seperator{border:1px solid #AAA}.rs-border{border:1px solid #AAA}.rs-path-color{background-color:#FFF}.rs-range-color{background-color:#54BBE0}.rs-bg-color{background-color:#FFF}.rs-handle{background-color:#838383}.rs-handle-dot{background-color:#FFF}.rs-handle-dot:after{background-color:#838383}.rs-path-inherited .rs-path{opacity:.2}.rs-svg-mode .rs-path{stroke:#FFF}.rs-svg-mode .rs-range{stroke:#54BBE0}.rs-svg-mode .rs-border{stroke:#AAA}

View File

@@ -0,0 +1,145 @@
* {
box-sizing: border-box;
}
/* Style the header */
.settings-heading {
padding: 10px 20px;
text-align: left;
font-size: 30px;
border-bottom: 1px solid #bbbbbb;
}
.settings-heading .hdng{
padding-top: 10px;
}
/* Container for flexboxes */
.settings-content {
display: -webkit-flex;
display: flex;
height: calc(100% - 70px);
}
/* Style the navigation menu */
.side-panel {
width: 200px;
border-right: 1px solid #bbbbbb;
padding: 20px 10px;
}
.settings-section{
margin-top: 15px;
}
/* Style the list inside the menu */
.side-panel ul {
list-style-type: none;
margin: 5px 0;
padding: 0;
}
.side-panel ul li {
padding: 10px;
border-radius: 5px;
margin: 5px 0;
cursor: pointer;
font-size: 14px;
}
.settings-subheading{
font-size: 18px;
margin-bottom: 5px;
}
.settings-group{
border-radius: 5px;
border: 1px solid #e0e0e0;
padding: 10px 20px;
background: #fff;
margin-bottom: 5px;
}
.setting-entry{
margin: 15px 0;
display: -webkit-flex;
display: flex;
}
.setting-label{
padding: 5px;
font-size: 14px;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
.setting-content{
-webkit-flex: 3;
-ms-flex: 3;
flex: 3;
}
.side-panel ul li:hover, .side-panel ul li.active {
background: rgba(4,4,4, 0.05);
}
.settings-group.placeholder {
min-height: 200px;
text-align: center;
padding: 20%;
}
.settings-group.placeholder p {
border: 1px dashed;
padding: 4%;
color: #808080;
border-radius: 10px;
}
.row.settings-group.fnhead {
background: #f3f3f3;
font-weight: 600;
}
.maps-content{
overflow-y: auto;
font-size: 14px;
max-height: calc(100% - 90px);
}
a.edit-cur-loco, a.option-btn{
float: right;
text-decoration: none;
padding: 5px 15px;
text-align: center;
border-radius: 5px;
display: inline-flex;
background: #f3f3f3;
border: 1px solid #e8e8e8;
box-shadow: 0 0 3px #e8e8e8;
margin-left: 5px;
}
a.edit-cur-loco{
margin-top: 6px;
}
.settings-subheading.row .map-name-heading{
padding-top: 8px;
}
/* Style the content */
.settings-panel {
-webkit-flex: 3;
-ms-flex: 3;
flex: 3;
padding: 24px;
padding-top: 10px;
overflow-y: auto;
}
.back-button{
text-align: left;
padding: 10px;
}
.caplitalize{
text-transform: capitalize;
}
.waste-bin{
font-size: 13px;
}
/* Responsive layout - makes the menu and the content (inside the section) sit on top of each other instead of next to each other */
@media (max-width: 600px) {
.settings-content {
-webkit-flex-direction: column;
flex-direction: column;
}
}
.scrollbar {
overflow: auto;
width: 100%;
}

View File

@@ -0,0 +1,226 @@
body{
background: #111111;
}
.rendered-form{
background: #1b1b1b;
background-image: url(../../images/carbon_fibre.png);
background-repeat: repeat;
color: #fff;
background-size: 16px;
}
.rs-control .rs-bg-color, #throttle-window{
background: #1b1b1b;
background-image: url(../../images/carbon_fibre.png);
background-repeat: repeat;
background-size: 16px;
}
.rs-control .rs-range-color{
background-color: #00A3B9;
}
#throttle-window{
border: 1px solid #303030;
box-shadow: 0px 0px 10px 0px #0c0c0c;
}
label.formbuilder-text-label,
label.formbuilder-select-label,
label.formbuilder-textarea-label,
label.formbuilder-file-label{
color: #ffffff;
}
input.form-control, select.form-control, textarea.form-control {
border: 1px solid #3b3b3b;
border-radius: 4px;
background: #282828;
color: #ffffff;
box-shadow: inset 0px 0px 2px #000000;
}
.formbuilder-button .btn, .server-button .btn, .fn-button .btn {
box-shadow: 0px 0px 2px #000000;
}
.fn-button.field-button-fn .btn.press{
border: 1px solid #3e8e41;
box-shadow: 0px 0px 2px #000000;
}
.fn-button.field-button-fn button[aria-pressed="true"].btn.press{
border: 1px solid #3e8e41;
box-shadow: none;
}
.select-control {
border: 1px solid #3b3b3b;
border-radius: 4px;
background: #282828;
color: #ffffff;
box-shadow: inset 0px 0px 2px #000000;
}
.btn-grey{
background: #282828;
border: 1px solid #3f3f3f;
box-shadow: 0px 0px 2px #000000;
color: #afafaf;
}
.btn-hide{
background: #282828;
border: 1px solid #3f3f3f;
box-shadow: 0px 0px 2px #000000;
color: #afafaf;
}
.btn-speed {
background: #282828;
border: 4px solid #3f3f3f;
box-shadow: 0px 0px 2px #000000;
color: #afafaf;
}
.btn-speed:hover {
background: #3f3f3f;
color: #FFFFFF;
}
.rotaryswitchPlugin.big .mark{
background: #111111;
box-shadow: inset 0px 0px 2px #000000;
}
.slider{
border: 1px solid #3b3b3b;
background: #121212;
box-shadow: inset 0px 0px 2px #000000;
-webkit-transition: .4s;
transition: .4s;
border-radius: 34px;
}
.slider:before {
left: 3px;
bottom: 2px;
background: #484848;
border: 1px solid #3f3f3f;
box-shadow: 0px 0px 2px #000000;
}
input:checked + .slider:before{
box-shadow: 0px 0px 2px #b40000;
border: 1px solid #ff3a3a;
}
input + .slider,
input:checked + .slider{
box-shadow: inset 0px 0px 2px #000000;
}
input:checked + .slider.debug-slider{
box-shadow: inset 0px 0px 2px #000000;
}
input:checked + .slider.debug-slider:before{
box-shadow: 0px 0px 2px #00A3B9;
border: 1px solid #01b3ca;
}
.slider.debug-slider:before {
bottom: 1px;
}
.knob-value{
text-shadow: 0 0px 1px #000000, 0px 0px 4px #31d9f3;
}
.log-msg{
color: cyan;
border: 1px solid #3b3b3b;
background: #121212;
box-shadow: inset 0px 0px 2px #000000;
}
.dir-toggle {
background-color: #121212;
-moz-box-shadow: inset 0 0 5px #000000;
-webkit-box-shadow: inset 0 0 5px #000000;
box-shadow: inset 0 0 5px #000000;
border: 4px solid #141414;
}
.dir-toggle:before{
-moz-box-shadow: 0 0 5px #000000;
-webkit-box-shadow: 0 0 5px #000000;
box-shadow: 0 0 5px #000000;
}
.em-btn button.em-stop{
padding: 12px;
box-shadow: 0 0 5px 2px #000000;
border: 1px solid #ececec;
background: #ddd;
}
.rotaryswitchPlugin.big.light {
margin: 36px;
background: url(../../images/darkmkBigBackground.png) no-repeat;
background-size: 100%;
background-position: center;
display: inline-block;
}
.rotaryswitchPlugin.big.light .switch {
background: url(../../images/darkmkBigFront.png) no-repeat;
background-size: 93%;
background-position: center;
}
.ui-slider .ui-slider-range {
background: #282828;
box-shadow: inset 0 0 5px 0px #000;
}
.ui-widget.ui-widget-content {
border: 4px solid #3f3f3f;
box-shadow: 0px 0px 2px #000000;
}
.ui-slider-vertical .ui-slider-handle {
border: 1px solid #3f3f3f;
background-color: #282828;
background: linear-gradient(to bottom,#3b3b3b, #282828);
left: -6px;
}
hr {
border-color: #3f3f3f;
}
.menu {
box-shadow: 0 0px 10px #000000;
border-right: 1px solid #2f2f2f;
}
.nav-btn, .btn-expand, .btn-info, .btn-hide{
color: #afafaf;
}
.nav-btn:hover, .btn-expand:hover, .btn-info:hover{
color: #fff;
}
.add-loco-form .add-loco-head,
.add-loco-form .row label {
color: #333;
}
.settings-group{
border: 1px solid #3b3b3b;
background: #121212;
box-shadow: inset 0px 0px 2px #000000;
}
.settings-heading {
border-bottom: 1px solid #000000;
box-shadow: 0 1px 0px #464646;
}
.side-panel {
border-right: 1px solid #000000;
box-shadow: 1px 0 #464646;
}
.row.settings-group.fnhead{
background: #232323;
box-shadow: 0 1px 1px black;
border: 1px solid #333;
}
a.edit-cur-loco, a.option-btn{
background: #282828;
border: 1px solid #3f3f3f;
box-shadow: 0px 0px 2px #000000;
color: #ffffff;
}
.loco-details p.ac-loco-name{
text-shadow: 0 0 1px #000000;
color: #00A3B9
}
.loco-details .sub-text p{
background: #282828;
}
.loco-list-ctrl input{
border: 1px solid #3b3b3b;
background: #282828;
color: #ffffff;
box-shadow: inset 0px 0px 2px #000000;
}

View File

@@ -0,0 +1,208 @@
.rendered-form{
background-image: url("../../images/pattern.png");
background-size: 50%;
background-repeat: repeat;
}
.btn-speed {
border: 1px solid #dcdcdc;
background: linear-gradient(to bottom,#e2e2e2, #cfd0da);
color: rgb(59, 59, 59);
border: 1px solid #9e9e9e;
text-shadow: 0px 1px 0px #fafafa;
box-shadow: 0px 1px 1px 1px #b3b3b3;
}
.btn-speed:hover{
background-color: #c7c7c7;
background: linear-gradient(to bottom, #e2e2e2, #cfd0da);
color: rgb(59, 59, 59);
}
.btn-speed:active{
box-shadow: 0 1px 2px 1px #b3b3b3 inset;
background: linear-gradient(to bottom,#cfd0da, #e2e2e2);
border: 1px solid #9e9e9e;
}
.dir-toggle:before{
width: 40px;
height: 70px;
}
.dir-btn span.arrow-up,
.dir-btn span.arrow-down{
color: #4d4d4d;
}
.dir-btn span.stop{
background: #4d4d4d;
}
.dir-btn.selected span.stop{
background: #da0000;
box-shadow: 0px 0px 4px #da0000;
}
.dir-btn.selected span.arrow-up,
.dir-btn.selected span.arrow-down{
color: #00bcd6;
text-shadow: 0px 0px 4px #29d6ec;
}
.dir-toggle.forward:before, .dir-toggle.stop:before, .dir-toggle.backward:before{
box-sizing: border-box;
border: 1px solid #f9f9f9;
background-color: #efefef;
background: linear-gradient(to bottom,#efefef, #e2e2e2);
}
.em-btn button.em-stop{
padding: 12px;
background: linear-gradient(to bottom,#e2e2e2, #cfd0da);
border: 1px solid #9e9e9e;
text-shadow: 0px 1px 0px #fafafa;
box-shadow: 0px 1px 1px 1px #b3b3b3;
}
.fn-button.field-button-fn .btn, .save-fn, .btn-grey,
.fn-button.field-button-fn .btn.press{
color: rgb(59, 59, 59);
border: 1px solid #9e9e9e;
border-bottom: 4px solid #9e9e9e;
text-shadow: 0px 1px 0px #fafafa;
background-color: #efefef;
background: -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#e2e2e2));
background: -moz-linear-gradient(top, #efefef, #e2e2e2);
box-shadow: -1px 2px 4px 2px #c7c7c7; /* 0 0 0px 4px #d8d8d8, 0 0 0px 1px #ddd;*/
background: linear-gradient(to bottom,#efefef, #e2e2e2);
height: 30px;
box-sizing: border-box;
cursor: pointer;
}
.fn-button.field-button-fn button[aria-pressed="true"].btn,
.fn-button.field-button-fn button[aria-pressed="true"].btn.press,
.save-fn:active {
background: -webkit-gradient(linear, left top, left bottom, from(#cfd0da), to(#e2e2e2));
background: -moz-linear-gradient(top, #bfbfbf, #dcdcdc);
box-shadow: 1px 1px 0 #fff, inset 0 1px 1px rgba(0, 0, 0, 0.3);
border: 1px solid #9e9e9e;
}
.fn-button.field-button-fn .btn.press {
color: #005a69;
}
.formbuilder-button .btn, .server-button .btn, .fn-button .btn, .dropbtn{
border: 1px solid #007a8a;
border-bottom: 4px solid #007a8a;
background: -webkit-gradient(linear, left top, left bottom, from(#00b2ca), to(#00A3B9));
background: -moz-linear-gradient(top, #00b2ca, #00A3B9);
background: linear-gradient(to bottom,#00b2ca, #00A3B9);
box-shadow: -1px 2px 4px 2px #c7c7c7; /* 0 0 0px 4px #d8d8d8, 0 0 0px 1px #ddd;*/
vertical-align: middle;
box-sizing: border-box;
text-shadow: 0px 0px 1px #444444;
}
.formbuilder-button .btn:active ,
.server-button .btn:active, .dropbtn:active{
background: -webkit-gradient(linear, left top, left bottom, from(#0299ad), to(#00A3B9));
background: -moz-linear-gradient(top, #0299ad, #00A3B9);
background: linear-gradient(to bottom,#0299ad, #00A3B9);
box-shadow: 1px 1px 0 #fff, inset 0 1px 1px rgba(0, 0, 0, 0.3);
border-bottom: 1px solid #007a8a;
height: 28px;
}
.dropbtn{
padding: 5px 8px;
}
.dropbtn:active{
padding: 6px 8px;
}
.server-button .btn, .fn-button .btnm,
input.form-control, select.form-control, textarea.form-control {
padding: 7px 8px;
vertical-align: middle;
}
input.form-control:active, select.form-control:active, textarea.form-control:active,
input.form-control:focus, select.form-control:focus, textarea.form-control:focus{
background: #ffffff;
}
.formbuilder-button .btn.acq-loco-btn,
.server-button .btn,
.formbuilder-button .add-loco-btn,
.formbuilder-button .btn.acq-loco-btn:active,
.server-button .btn:active,
.formbuilder-button .add-loco-btn:active{
height: 34px;
}
.select-control{
height: 30px;
border: 1px solid #9e9e9e;
box-shadow: 1px 1px 0 #fff, inset 0 1px 1px rgba(0, 0, 0, 0.3);
background: #fafafa;
}
.select-control.select-xl{
height: 36px;
margin-top: 1px;
}
.btn-grey{
padding: 7px;
padding-top: 5px;
height: 30px;
margin-top: 1px;
}
/* Acquire loco heading Styles*/
.loco-board{
display: inline-block;
border-radius: 20px;
background: linear-gradient(to bottom,#e2e2e2, #cfd0da);
color: rgb(59, 59, 59);
border: 1px solid #9e9e9e;
border-bottom: 4px solid #9e9e9e;
text-shadow: 0px 1px 0px #fafafa;
box-shadow: inset 0px 1px 1px #f5f5f5;
padding: 7px 11px;
min-width: 270px;
}
.nut-effect{
border-radius: 16px;
padding: 5px;
background: #dededd;
height: 5px;
width: 5px;
display: inline-block;
vertical-align: middle;
box-shadow: 0 0 2px #737373;
}
.nut-effect:after{
content: "";
display: inline-block;
background: #b9b9b9;
height: 15px;
width: 1px;
position: relative;
top: -5px;
left: 0;
}
.nut-effect.l{
margin-left: 10px;
margin-right: 16px;
}
.nut-effect.r{
margin-left: 16px;
margin-right: 10px;
}
.nut-effect.l:after{
transform: rotate(135deg);
}
.nut-effect.r:after{
transform: rotate(15deg);
}
.rs-control .rs-bg-color {
background-color: #ddd;
background-image: url(../../images/pattern.png);
background-size: 900%;
opacity: 0.9;
}
.rs-overlay.rs-transition.rs-bg-color{
background: #dcdcdc;
opacity: 1;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 MiB

View File

@@ -0,0 +1,496 @@
<!DOCTYPE html>
<html HTML>
<!--
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Authors: Mani Kumar
Fred Decker
Matt
This file is part of the DCC++ EX Project for model railroading and more.
For more information, visit use at dcc-ex.com
-->
<head>
<link rel="manifest" href="manifest.json">
<script>
var version = "1.3.1";
</script>
<link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon">
<link href="images/favicon.ico" rel="icon" type="image/x-icon">
<link href="css/jquery-ui.css" rel="stylesheet" type="text/css">
<link href="css/layout.css" rel="stylesheet" type="text/css">
<link href="css/roundslider.min.css" rel="stylesheet" type="text/css"/>
<link href="css/throttle.css" rel="stylesheet" type="text/css">
<link href="css/jquery.rotaryswitch.css" rel="stylesheet" type="text/css">
<link href="css/icons.css" rel="stylesheet" type="text/css">
<link href="css/settings.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="js/jquery-ui.min.js"></script>
<script type="text/javascript" src="js/roundslider.min.js"></script>
<script type="text/javascript" src="js/jquery.rotaryswitch.js"></script>
<script type="text/javascript" src="js/fnMaster.js"></script>
<script type="text/javascript" src="js/commandController.js"></script>
<script type="text/javascript" src="js/storageController.js"></script>
<script type="text/javascript" src="js/addloco.js"></script>
<script type="text/javascript" src="js/restapi.js"></script>
<script type="text/javascript" src="js/exwebthrottle.js"></script>
<script type="text/javascript" src="js/pwa.js"></script>
<!--
NOTE: You can replace the above links with these if you like if you will always run when
connected to the internet. Using the links above, they must be installed on your machine.
This program will search for them online and import them if you use this method instead.
<link href="https://cdn.jsdelivr.net/npm/round-slider@1.6.1/dist/roundslider.min.css" rel="stylesheet" />
<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.js"></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/round-slider@1.6.1/dist/roundslider.min.js"></script>
<script type="text/javascript" src="js/exwebthrottle.js"></script>
-->
<title>DCC++ EX Web Throttle</title>
<meta name="description" content="Chromium browser based web throttle for a DCC++ EX Command Station to control model trains">
<html lang="en">
</head>
<body>
<nav class="menu" id="side-menu" tabindex="0">
<header class="avatar">
<button class="nav-btn in" id="nav-close">&times;</button>
<img src="images/cover.jpg" />
</header>
<ul>
<li tabindex="0" class="throttle" id="throttle-nav"><span>Throttle</span></li>
<li tabindex="0" class="locomotives" id="loco-nav"><span>Locomotives</span></li>
<li tabindex="0" class="function-maps" id="fn-map-nav"><span>Function Maps</span></li>
<li tabindex="0" class="settings" id="settings-nav"><span>Settings</span></li>
</ul>
</nav>
<div class="topnav">
<div class="row">
<div class="column-2">
<button class="nav-btn" id="nav-open"></button>
</div>
<div class="column-6 align-center">
<div class="throttle-heading">
<a href="http://www.dcc-ex.com" target="_blank" rel="noopener noreferrer"><div class="wt-logo"></div></a>
<!--img src="images/WebThrottle.png" /> -->
<!-- <p>Version 1.2.0</p> -->
</div>
</div>
<div class="column-2">
<button class="btn-expand" id="fs-toggle" state="ws" title="Fullscreen">&#10530;</button>
<button class="btn-info" id="info-tooltip" state="ws" title="Information"><span class="icon-info"></span></button>
<button class="btn-info" id="help-button" state="ws" title="Help" onclick=" window.open('https://dcc-ex.com/throttles/ex-webthrottle.html', '_blank')"><span class="icon-question"></span></button>
</div>
</div>
</div>
<div id="rendered-form">
<div class="rendered-form">
<section id="throttle-window" class="section">
<div Class="details-panel" id="details-panel">
<div class="row">
<div class="column-5">
<div class="loco-list-container row">
<div class="column-5 loco-list-ctrl">
<label for="ex-locoid" class="formbuilder-text-label">
Locomotive ID <span class="formbuilder-required">*</span>
</label>
<input id="ex-locoid" type="text" loco-cv="0" name="Locomotives"/>
</div>
<div class="column-2 formbuilder-button acquire-wrap">
<button id="button-getloco" class="acq-loco-btn btn" data-acquired="false">
<span class="icon-circle-right"></span>
</button>
</div>
<div class="column-2 formbuilder-button acquire-wrap">
</div>
</div>
</div>
<div class="server-button column-5">
<select id="select-method" class="select-control select-xl" name="selectMethod" title="Change the connection method">
<option value="rest">RestAPI</option>
</select>
<button type="button" class="btn-default btn" title="Connect to the Command Station" aria-state="connected" name="button-connect" access="false" id="button-connect">
<span class="con-ind"></span>Connect DCC++ EX
</button>
<!-- <button class="btn-grey" id="fs-toggle" state="ws" title="Fullscreen">&#10530;</button> -->
</div>
</div>
<hr>
</div>
<div class="row pos-rel">
<button type="button" class="btn-default btn btn-hide" title="Hide the top bar" name="button-hide" access="false" id="button-hide">
<span class="icon-circle-up"></span>
</button>
</div>
<div class="row flex-center">
<div class="column-5 mobile-100 throttle-container">
<div class="row mobile-100 width100 flexx">
<div class="column-7 flexx">
<div class="flexx btns">
<button type="button" title="Decrease speed" class="btn-default btn btn btn-speed btn-left" name="button-left" access="false" id="button-left">
<span class="left" style="line-height: 12px;">&nbsp;-&nbsp;</span>
</button>
</div>
<div class="Throttlewrap">
<div id="vertical-throttle" class="vertical-throttle speedController">
<div id="v-throttle">
</div>
<div id="speed-indicator" class="progress-numb">
0
</div>
</div>
<div id="knobthrottle" class="speedController">
<input type="text" class="rotarySwitch" value="0">
<div id="knob-value" class="knob-value">0</div>
</div>
<div id="circular-throttle" class="circular-throttle speedController">
</div>
</div>
<div class="flexx btns">
<button type="button" title="Increase speed" class="btn-default btn btn-speed btn-right" name="button-right" access="false" id="button-right">
<span class="right">&nbsp;+&nbsp;</span>
</button>
</div>
</div>
<div class=" column-2">
<div class="em-btn">
<button class="em-stop" id="emergency-stop" title="Emergency Stop">
<span class="icon-stop"></span>
</button>
</div>
<div class="dir-toggle">
<button class="dir-btn forward selected" id="dir-f" aria-label="forward" ><span class="arrow-up icon-up"></span></button>
<button class="dir-btn stop" id="dir-S" aria-label="stop"> <span class="stop"></span></button>
<button class="dir-btn backward" id="dir-b" aria-label="backward"> <span class="arrow-down icon-down"></button>
</div>
</div>
</div>
</div>
<div class="functionKeys column-5 mobile-100">
<div class="row mobile-100">
<div class="power-slider column-4 formbuilder-button " id="power-switch-div" title="Enable track power">
<label class="switch">
<input type="checkbox" id="power-switch">
<span class="slider round"></span>
</label>
<span class="pow-status">
Power <span id="power-status">Off</span>
</span>
</div>
<div class="note-msg column-6">
<select id="select-map" class="btn select-map select-control" name="selectMap" title="Load a Saved Map">
</select>
</div>
</div>
<div class="row" id="fn-wrapper">
</div>
</div>
</div>
<div class="row">
<div class="console-slider column-4" title="Enable the debug console">
<label class="debug switch">
<input type="checkbox" id="console-toggle">
<span class="slider round debug-slider"></span>
</label>
<span class="debug-status">
Debug Console
</span>
</div>
</div>
<div id="debug-console" hidden="true">
<hr>
<div class="row mobile-margin-top20">
<div class="formbuilder-text column-8">
<input placeholder="Direct Command (without &lt; &gt;)" class="form-control" name="cmd-direct" access="false" id="cmd-direct">
</div>
<div class="formbuilder-button column-1">
<button type="button" class="btn-default btn" name="sendCmd" access="false" data-acquired="false" id="button-sendCmd">
Send
</button>
</div>
<div class="formbuilder-button dcmd-clear column-1">
<button type="button" class="btn-default btn" name="clearLog" access="false" data-acquired="false" id="button-clearLog">
Clear Log
</button>
</div>
</div>
<div class="log-msg" id="log-box">
</div>
</div>
</section>
<section id="loco-window" class="section" hidden="true">
<div class="row settings-heading">
<div class="column-7 pl0 hdng">
Locomotives
</div>
<div class="column-3 pr0">
<div class="option-btns formbuilder-button row">
<div class="column-2">&nbsp;</div>
<div class="column-4">
<button id="add-loco" class="add-loco-btn btn">
<span class="icon-plus"></span> Add New
</button>
<div id="loco-form-content" class="add-loco-form">
<form id="loco-form" class="form-inline">
<div class="fn-modal-header row">
<h4 class="fn-heading add-loco-head">Add Locomotive</h4>
<span class="close" id="close-addloco-model">×</span>
</div>
<!-- <div class="add-loco-head">s
Add Locomotive
<>
<hr>
</div> -->
<div class="row">
<label class="column-4"for="name">Name:*</label>
<input class="column-6" type="text" id="name" placeholder="Enter name" name="name" required>
</div>
<div class="row">
<label class="column-4"for="cv">CV:*</label>
<input class="column-6" type="number" id="cv" placeholder="Enter CV" name="cv" required>
</div>
<div class="row">
<label class="column-4"for="type">Engine Type:</label>
<select class="column-6 add-select" id="type" placeholder="Select Type" name="type">
<option value="Diesel"> Diesel </option>
<option value="Steam"> Steam </option>
<option value="Electric"> Electric </option>
<option value="Other"> Other </option>
</select>
</div>
<div class="row">
<label class="column-4"for="brand">Brand:</label>
<input class="column-6" type="text" id="brand" placeholder="Brand Name" name="brand">
</div>
<div class="row">
<label class="column-4"for="decoder">Decoder:</label>
<input class="column-6" type="text" id="decoder" placeholder="Decoder Name" name="decoder">
</div>
<div class="row">
<label class="column-4"for="map">Function map:*</label>
<input id="function-maps" class="column-6" type="text" id="map" placeholder="Select Map" name="map" required>
</div>
<div class="spacer">
</div>
<div class="row">
<div class="column-2"></div>
<button id="loco-submit" loco-mode="add" class="column-6" type="submit">Submit</button>
<div class="column-2"></div>
</div>
</form>
</div>
</div>
<div class="column-2">
<button class="add-loco-btn btn" id="export-locolist" title="Export Locomotives"><span class="icon-upload3"></span></button>
</div>
<div class="column-2">
<button class="add-loco-btn btn" id="import-locolist" title="Import Locomotives"><span class="icon-download3"></span></button>
</div>
</div>
</div>
</div>
<div class="row settings-content">
<div class="settings-panel" id="locomotives-panel">
<div class="settings-group">
Select a Locomotive
</div>
</div>
</div>
</section>
<section id="fn-map-window" class="section" hidden="true">
<div class="row settings-heading">
<div class="column-7 pl0 hdng">
Function Mappings
</div>
<div class="column-3 pr0">
<div class="option-btns formbuilder-button row">
<div class="column-2">&nbsp;</div>
<div class="column-4">
<button id="add-map" class="add-loco-btn btn">
<span class="icon-plus"></span> New Map
</button>
</div>
<div class="column-2">
<button class="add-loco-btn btn" id="export-all-maps" title="Export Map" ><span class="icon-upload3"></span></button>
</div>
<div class="column-2">
<button class="add-loco-btn btn" id="import-all-maps" title="Import Map"><span class="icon-download3"></span></button>
</div>
</div>
</div>
</div>
<div class="row settings-content">
<div class="side-panel">
<ul id="function-mappings">
</ul>
</div>
<div class="settings-panel" id="mapping-panel">
<div class="settings-group placeholder">
<p>Select a Map</p>
</div>
</div>
</div>
</section>
<section id="settings-window" class="section" hidden="true">
<div class="row">
<div class="settings-heading hdng">
Settings
</div>
</div>
<div class="row settings-content">
<div class="side-panel">
<ul>
<li id="settings-general">General</li>
<li id="settings-storage">Storage</li>
<li id="settings-app">App</li>
</ul>
</div>
<div class="settings-panel scrollbar" id="settings-panel">
<div class="settings-section" id="general-section">
<div class="settings-subheading">
General
</div>
<div class="settings-group">
<div class="row setting-entry">
<label class="setting-label">
Theme
</label>
<div class="setting-content">
<select id="theme-selector" class="btn theme-selector select-control" name="themeselector" title="Change Theme">
<option value="simple"> Simple </option>
<option value="metallic"> Metallic </option>
<option value="dark"> Dark </option>
</select>
</div>
</div>
<div class="row setting-entry">
<label class="setting-label">
Speed Controller
</label>
<div class="setting-content">
<select id="throttle-selector" class="btn throttle-selector select-control" name="throttleselector" title="Change Throttle Controller">
<option value="vertical"> Vertical </option>
<option value="knob"> Knob </option>
<option value="circular"> Circular </option>
</select>
</div>
</div>
</div>
</div>
<div class="settings-section" id="storage-section">
<div class="settings-subheading">
Storage
</div>
<div class="settings-group">
<!--<div class="row setting-entry">
<label class="setting-label">
Some placeholder
</label>
<div class="setting-content">
<input />
</div>
</div>
<div class="row setting-entry">
<label class="setting-label">
Some placeholder
</label>
<div class="setting-content">
<input />
</div>
</div>-->
<!--<div class="hr"></div>-->
<div class="row setting-entry">
<label class="setting-label">
<button id="backup-app-settings">&#10514;&nbsp; Export App data</button>
</label>
</div>
<div class="row setting-entry">
<label class="setting-label">
<button id="restore-app-settings">&#10515;&nbsp; Import App data</button>
</label>
</div>
<div class="row setting-entry">
<label class="setting-label">
<button id="wipe-app-settings">&#10539;&nbsp; Wipe App data</button>
</label>
</div>
</div>
<div class="settings-section" id="app-section">
<div class="settings-subheading">
App
</div>
<div class="settings-group">
<div class="row setting-entry">
<label class="setting-label">
Install as an app
</label>
<label class="setting-label">
<button class="add-button">Install</button>
<label class="installed-label">App Installed</label>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
<div id="fnModal" class="fn-modal">
<!-- Modal content -->
<div class="fn-modal-header row">
<h4 class="fn-heading" > New Map </h4>
<span class="close" id="close-model">&times;</span>
</div>
<div class="fn-modal-content">
Content
</div>
<div class="fn-modal-footer row">
<button type="button" mode="new" class="btn-default btn save-fn" name="save-fn" id="save-fn-map">
Save Map
</button>
</div>
</div>
<input id="map-upload" type="file" hidden/>
<input id="maps-upload" type="file" hidden/>
<input id="cabs-upload" type="file" hidden/>
<input id="app-upload" type="file" hidden/>
</body>
</html>
<script>
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js')
.then(function() {
console.log('Service Worker Registered');
});
}
</script>

View File

@@ -0,0 +1,156 @@
$(document).ready(function(){
locoList = getLocoList();
savedMaps = getPreparedMaps();
$("#loco-form").on("submit", function (e) {
e.preventDefault();
data = $(this).serializeArray();
mode = $("#loco-submit").attr("loco-mode");
if(mode != 'edit'){
saveLocomotive(data);
}else{
id = $("#loco-submit").attr("loco-id")
saveEditedLocomotive(data, id);
}
locoList = getLocoList();
loadLocomotives();
$("#loco-form")[0].reset();
$("#loco-form-content").css("display", "none");
});
$("#add-loco").on("click", function () {
savedMaps = getPreparedMaps();
$("#loco-form")[0].reset();
$("#loco-form-content").css("display", "inline-block");
$(".add-loco-form .add-loco-head").html("Add Locomotive");
$("#loco-submit").attr("loco-mode", "add");
});
$("#close-addloco-model").on("click", function () {
if ($("#loco-form-content").is(":visible")) {
$("#loco-form-content").css("display", "none");
}
});
$("#ex-locoid").autocomplete({
delay: 0,
minLength: 0,
source: function (request, response) {
var matcher = new RegExp(
$.ui.autocomplete.escapeRegex(request.term),
"i"
);
response(
$.grep(locoList, function (item) {
if (item != undefined) {
console.log(item);
return (
matcher.test(item.name) ||
matcher.test(item.cv) ||
matcher.test(item.type) ||
matcher.test(item.manufacturer)
);
} else {
return true;
}
})
);
},
select: function (event, ui) {
$(this).val(ui.item.cv + " | " + ui.item.name);
$(this).attr("loco-cv", ui.item.cv);
return false;
},
})
.focus(function () {
console.log("Focused");
$(this).autocomplete("search", "");
})
.data("ui-autocomplete")._renderItem = function (ul, item) {
$(ul).addClass("res-list");
return $("<li></li>")
.data("item.autocomplete", item)
.append(
"<div><p class='ac-loco-name'>" +
item.name +
"</p><small> <span class='pill'>CV:" +
item.cv +
"</span>|<span class='pill'>" +
item.type +
"</span>|<span class='pill wrap'>" +
item.brand +
"</span></small></div>"
)
.appendTo(ul);
};
$("#function-maps").autocomplete({
delay: 0,
minLength: 0,
source: function (request, response) {
var matcher = new RegExp(
$.ui.autocomplete.escapeRegex(request.term),
"i"
);
response(
$.grep(savedMaps, function (item) {
return matcher.test(item.mname);
})
);
},
select: function (event, ui) {
$(this).val(ui.item.mname);
return false;
},
})
.focus(function () {
console.log("Focused");
$(this).autocomplete("search", "");
})
.data("ui-autocomplete")._renderItem = function (ul, item) {
$(ul).addClass("res-list-sm");
return $("<li></li>")
.data("item.autocomplete", item)
.append("<div><p class='item'>" + item.mname + "</p></div>")
.appendTo(ul);
};
$("#export-locolist").on("click", function (e) {
downloadCabData();
});
$("#import-locolist").on("click", function (e) {
e.preventDefault();
$("#cabs-upload").trigger("click");
locoList = getLocoList();
});
});
function getPreparedMaps() {
const defaultMap = {
mname: "Default",
fnData: {},
}
return [defaultMap, ...getMapData()];
}
/** Reference Map Structure
maps = [
{
mname: "mkmap",
fnData: {
f0: [0, 1, "Head Light", 1],
},
},
];
locos = [
{
name: "Mikado",
cv: 3,
type: "Diesel",
manufacturer: "Kato",
},
];
*/

View File

@@ -0,0 +1,268 @@
/* This is part of the DCC++ EX Project for model railroading and more.
For licence information, please see index.html
For more information, see us at dcc-ex.com.
commandController.js
Open a serial port and create a stream to read and write data
While there is data, we read the results in loop function
*/
$(document).ready(function(){
console.log("Command Controller loaded");
uiDisable(true)
restClass = new RestAPI({logger: displayLog});
});
// - Request a port and open an asynchronous connection,
// which prevents the UI from blocking when waiting for
// input, and allows serial to be received by the web page
// whenever it arrives.
async function connectServer() {
// Gets values of the connection method selector
selectMethod = document.getElementById('select-method')
mode = selectMethod.value;
// Disables selector so it can't be changed whilst connected
selectMethod.disabled = true;
console.log("Set Mode: "+mode)
// Checks which method was selected
if (mode == "serial") {
try{
// - Request a port and open an asynchronous connection,
// which prevents the UI from blocking when waiting for
// input, and allows serial to be received by the web page
// whenever it arrives.
port = await navigator.serial.requestPort(); // prompt user to select device connected to a com port
// - Wait for the port to open.
await port.open({ baudRate: 115200 }); // open the port at the proper supported baud rate
// create a text encoder output stream and pipe the stream to port.writeable
const encoder = new TextEncoderStream();
outputDone = encoder.readable.pipeTo(port.writable);
outputStream = encoder.writable;
// To put the system into a known state and stop it from echoing back the characters that we send it,
// we need to send a CTRL-C and turn off the echo
writeToStream('\x03', 'echo(false);');
// Create an input stream and a reader to read the data. port.readable gets the readable stream
// DCC++ commands are text, so we will pipe it through a text decoder.
let decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable
// .pipeThrough(new TransformStream(new LineBreakTransformer())); // added this line to pump through transformer
//.pipeThrough(new TransformStream(new JSONTransformer()));
// get a reader and start the non-blocking asynchronous read loop to read data from the stream.
reader = inputStream.getReader();
readLoop();
uiDisable(false)
displayLog("[CONNECTION] Serial connected")
return true;
} catch (err) {
console.log("User didn't select a port to connect to")
return false;
}
} else{
restMode = true;
// Displays dummy hardware message
displayLog("\n[CONNECTION] Connected to "+window.location.host);
restClass.write("<s>");
uiDisable(false)
return true;
}
}
// While there is still data in the serial buffer us an asynchronous read loop
// to get the data and place it in the "value" variable. When "done" is true
// all the data has been read or the port is closed
async function readLoop() {
while (true) {
const { value, done } = await reader.read();
// if (value && value.button) { // alternate check and calling a function
// buttonPushed(value);
if (value) {
displayLog('[RECEIVE] '+value);
console.log('[RECEIVE] '+value);
}
if (done) {
console.log('[readLoop] DONE'+done.toString());
reader.releaseLock();
break;
}
}
}
function writeToStream(...lines) {
let stream = restClass
if (port) {
stream = outputStream.getWriter();
}
lines.forEach((line) => {
if (line == "\x03" || line == "echo(false);") {
} else {
displayLog('[SEND]'+line.toString());
}
const packet = `<${line}>\n`;
stream.write(packet)
console.log(packet)
});
}
// Transformer for the Web Serial API. Data comes in as a stream so we
// need a container to buffer what is coming from the serial port and
// parse the data into separate lines by looking for the breaks
class LineBreakTransformer {
constructor() {
// A container for holding stream data until it sees a new line.
this.container = '';
}
transform(chunk, controller) {
// Handle incoming chunk
this.container += chunk; // add new data to the container
const lines = this.container.split('\r\n'); // look for line breaks and if it finds any
this.container = lines.pop(); // split them into an array
lines.forEach(line => controller.enqueue(line)); // iterate parsed lines and send them
}
flush(controller) {
// When the stream is closed, flush any remaining data
controller.enqueue(this.container);
}
}
// Optional transformer for use with the web serial API
// to parse a JSON file into its component commands
class JSONTransformer {
transform(chunk, controller) {
// Attempt to parse JSON content
try {
controller.enqueue(JSON.parse(chunk));
} catch (e) {
//displayLog(chunk.toString());
//displayLog(chunk);
console.log('No JSON, dumping the raw chunk', chunk);
controller.enqueue(chunk);
}
}
}
async function disconnectServer() {
if ($("#power-switch").is(':checked')) {
$("#log-box").append('<br>'+'turn off power'+'<br>');
writeToStream('0');
$("#power-switch").prop('checked', false)
$("#power-status").html('Off');
}
uiDisable(true)
if (port) {
// Close the input stream (reader).
if (reader) {
await reader.cancel(); // .cancel is asynchronous so must use await to wave for it to finish
await inputDone.catch(() => {});
reader = null;
inputDone = null;
console.log('close reader');
}
// Close the output stream.
if (outputStream) {
await outputStream.getWriter().close();
await outputDone; // have to wait for the azync calls to finish and outputDone to close
outputStream = null;
outputDone = null;
console.log('close outputStream');
}
// Close the serial port.
await port.close();
port = null;
console.log('close port');
displayLog("[CONNECTION] Serial disconnected");
} else {
// Disables emulator
restMode = undefined;
displayLog("[CONNECTION] "+window.location.host+" disconnected");
}
// Allows a new method to be chosen
selectMethod.disabled = false;
}
// Connect or disconnect from the command station
async function toggleServer(btn) {
// If already connected, disconnect
if (port || restMode) {
await disconnectServer();
btn.attr('aria-state','Disconnected');
btn.html('<span class="con-ind"></span>Connect DCC++ EX'); //<span id="con-ind"></span>Connect DCC++ EX
return;
}
// Otherwise, call the connect() routine when the user clicks the connect button
success = await connectServer();
// Checks if the port was opened successfully
if (success) {
btn.attr('aria-state','Connected');
btn.html('<span class="con-ind connected"></span>Disconnect DCC++ EX');
} else {
selectMethod.disabled = false;
}
}
// Display log of events
function displayLog(data){
data = data.replaceAll("<"," ");
data = data.replaceAll(">"," ");
$("#log-box").append("<br>"+data.toString()+"<br>");
$("#log-box").scrollTop($("#log-box").prop("scrollHeight"));
}
// Function to generate commands for functions F0 to F4
function sendCommandForF0ToF4(fn, opr){
setFunCurrentVal(fn,opr);
cabval = (128+getFunCurrentVal("f1")*1 + getFunCurrentVal("f2")*2 + getFunCurrentVal("f3")*4 + getFunCurrentVal("f4")*8 + getFunCurrentVal("f0")*16);
writeToStream("f "+getCV()+" "+cabval);
console.log("Command: "+ "f "+getCV()+" "+cabval);
}
// Function to generate commands for functions F5 to F8
function sendCommandForF5ToF8(fn, opr){
setFunCurrentVal(fn,opr);
cabval = (176+getFunCurrentVal("f5")*1 + getFunCurrentVal("f6")*2 + getFunCurrentVal("f7")*4 + getFunCurrentVal("f8")*8);
writeToStream("f "+getCV()+" "+cabval);
console.log("Command: "+ "f "+getCV()+" "+cabval);
}
// Function to generate commands for functions F9 to F12
function sendCommandForF9ToF12(fn, opr){
setFunCurrentVal(fn,opr);
cabval = (160+getFunCurrentVal("f9")*1 + getFunCurrentVal("f10")*2 + getFunCurrentVal("f11")*4 + getFunCurrentVal("f12")*8);
writeToStream("f "+getCV()+" "+cabval);
console.log("Command: "+ "f "+getCV()+" "+cabval);
}
// Function to generate commands for functions F13 to F20
function sendCommandForF13ToF20(fn, opr){
setFunCurrentVal(fn,opr);
cabval = (getFunCurrentVal("f13")*1 + getFunCurrentVal("f14")*2 + getFunCurrentVal("f15")*4 + getFunCurrentVal("f16")*8 + getFunCurrentVal("f17")*16 + getFunCurrentVal("f18")*32 + getFunCurrentVal("f19")*64 + getFunCurrentVal("f20")*128);
writeToStream("f "+getCV()+" 222 "+cabval);
console.log("Command: "+ "f "+getCV()+" 222 "+cabval);
}
// Function to generate commands for functions F21 to F28
function sendCommandForF21ToF28(fn, opr){
setFunCurrentVal(fn,opr);
cabval = (getFunCurrentVal("f21")*1 + getFunCurrentVal("f22")*2 + getFunCurrentVal("f23")*4 + getFunCurrentVal("f24")*8 + getFunCurrentVal("f25")*16 + getFunCurrentVal("f26")*32 + getFunCurrentVal("f27")*64 + getFunCurrentVal("f28")*128);
writeToStream("f "+getCV()+" 223 "+cabval);
console.log("Command: "+ "f "+getCV()+" 223 "+cabval);
}

View File

@@ -0,0 +1,187 @@
/* This is part of the DCC++ EX Project for model railroading and more.
For licence information, please see index.html.
For more information, see us at dcc-ex.com.
emulator.js
Allows the software to operate without a Command Station attached. This
file manages the correct response that a Command Station would provide.
*/
/**
* Utility function can be moved when we can import files
* @description Removes control characters "<", ">" and "\n"
* @param {string} packet
* @return {string}
*/
function removeControlCharacters(packet) {
return [...packet].filter(char => !["<", ">", "\n"].includes(char)).join("")
}
/**
* Utility function can be moved when we can import files
* @description Finds the first non-blank character after control characters are removed
* @param {string} packet
* @return {string}
*/
function extractPacketKey(packet) {
const cleanedPacket = [...removeControlCharacters(packet)]
return cleanedPacket.find(char => char !== " ");
}
class Emulator {
constructor({logger}) {
this.turnoutEmulator = new TurnoutEmulator()
this.logger = logger
}
/**
* @param {string} packet
*/
write(packet) {
if (packet === "<credits>") {
console.log("Credits")
return credits()
}
let packetKey = extractPacketKey(packet);
switch (packetKey) {
// Cab control
case ("t"):
this.logger('[RECEIVE] '+ this.#cabControlCommand(packet))
break;
// Track power off
case ('0') :
this.logger('[RECEIVE] '+ this.#powerOffCommand(packet));
break;
// Track power on
case ('1'):
this.logger('[RECEIVE] '+ this.#powerOnCommand(packet));
break;
// New cab functions
case ('F'):
this.logger('[RECEIVE] '+ this.#cabFunctionCommand(packet));
break;
// Legacy cab functions
case ('f'):
this.logger('[RECEIVE] '+ this.#cabFunctionCommand(packet, true));
break;
// Turnouts
case ('T'): //Not fully finished
this.logger('[RECEIVE] '+ this.#turnoutCommand(packet, this.turnoutEmulator))
break
default:
break;
}
}
/**
* @param {string} packet
* @return {string}
*/
#cabControlCommand(packet) {
const splitPacket = packet.split(" ");
return 'T 1 ' + splitPacket[3] + ' ' + splitPacket[4].substring(0, splitPacket[4].length - 2);
}
/**
* @param {string} packet
* @return {string}
*/
#powerOffCommand(packet) {
return 'p0';
}
/**
* @param {string} packet
* @return {string}
*/
#powerOnCommand(packet) {
return 'p1';
}
/**
* @param {string} packet
* @param {boolean} legacy
* @return number
*/
#cabFunctionCommand(packet, legacy = false) {
return NaN;
}
/**
* @param {string} packet
* @param {TurnoutEmulator} turnoutEmulator
* @return {string}
*/
#turnoutCommand(packet, turnoutEmulator = new TurnoutEmulator()) {
const splitPacket = removeControlCharacters(packet).split(" ");
if (splitPacket.length === 4) {
// Adds a Turnout
return turnoutEmulator.addTurnout(new Turnout(packet))
} else if (splitPacket.length === 2) {
// Removes a Turnout
return turnoutEmulator.removeTurnout(splitPacket[1])
} else if (splitPacket.length === 1) {
// Reads Turnouts
return turnoutEmulator.turnouts
}
}
}
class TurnoutEmulator {
/**
* @type {[]}
*/
#turnouts = []
/**
* @return {string|string[]}
*/
get turnouts() {
if (!this.#turnouts.length) {
return 'X'
}
return this.#turnouts.map(turnout => `H ${turnout.id} ${turnout.address} ${turnout.subaddress} ${turnout.thrown}`)
}
/**
* @param {*} turnout
* @return {string}
*/
addTurnout(turnout) {
this.#turnouts = [...this.#turnouts, turnout]
console.log(this.#turnouts);
return '<O>';
}
/**
* @param {string} turnoutId
* @return {string}
*/
removeTurnout(turnoutId) {
this.#turnouts = this.#turnouts.filter(turnout => turnout.id !== turnoutId);
console.log(this.#turnouts);
return 'O';
}
}
class Turnout {
constructor(packet) {
let [_key, id, address, subaddress] = removeControlCharacters(packet).split(" ");
this.id = id
this.address = address
this.subaddress = subaddress
this.thrown = 0
}
}

View File

@@ -0,0 +1,971 @@
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or$("#log-box").append("<br>"+data+"<br>");
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Authors: Fred Decker
Mani Kumar
Matt
This is part of the DCC++ EX Project for model railroading and more.
For more information, see us at dcc-ex.com.
*/
window.cv=0;
window.speed=0;
window.direction=1;
window.server="";
window.port=4444;
window.speedStep = 1;
window.functions = {
"f0": 0,
"f1": 0,
"f2": 0,
"f3": 0,
"f4": 0,
"f5": 0,
"f6": 0,
"f7": 0,
"f8": 0,
"f9": 0,
"f10": 0,
"f11": 0,
"f12": 0,
"f13": 0,
"f14": 0,
"f15": 0,
"f16": 0,
"f17": 0,
"f18": 0,
"f19": 0,
"f20": 0,
"f21": 0,
"f22": 0,
"f23": 0,
"f24": 0,
"f25": 0,
"f26": 0,
"f27": 0,
"f28": 0
};
window.isStopped = true;
let port;
let restMode;
let reader;
let inputDone;
let outputDone;
let inputStream;
let outputStream;
let pressed = false;
// Enables and disables ui elements
function uiDisable (status) {
/*document.getElementById('ex-locoid').disabled = status
document.getElementById('power-switch').disabled = status
document.getElementById('button-sendCmd').disabled = status
document.getElementById('dir-f').disabled = status
document.getElementById('dir-S').disabled = status*/
//document.getElementById('dir-b').disabled = status
$("#ex-locoid").prop('disabled', status)
$("#power-switch").prop('disabled', status)
$("#button-sendCmd").prop('disabled', status)
$("#dir-f").prop('disabled', status)
$("#dir-S").prop('disabled', status)
$("#dir-b").prop('disabled', status)
if (status){
//$("#throttle").roundSlider("disable");
//toggleThrottleState(false)
$("#button-getloco").trigger("click");
} else {
//$("#throttle").roundSlider("enable");
//toggleThrottleState(true)
//$("#button-getloco").trigger("click");
}
}
// Returns given function current value (0-disable/1-enable)
function getFunCurrentVal(fun){
return window.functions[fun];
}
// Set given function current value with given value (0/1)
function setFunCurrentVal(fun, val){
window.functions[fun]=val;
}
// Set given CV value
function setCV(val){
window.cv = val;
console.log("Set Cab Address: "+val);
}
// Get stored CV value
function getCV(){
return window.cv
}
// Set Speed value
function setSpeed(sp){
window.speed=sp;
}
// Get Speed value
function getSpeed(){
return parseInt(window.speed);
}
// Set Direction
function setDirection(dir){
window.direction=dir;
}
// Get Direction value
function getDirection(dir){
return window.direction;
}
// Handling Disabling functionality for Knob Type throttle (it will not Out of the box)
function toggleKnobState(ele, state) {
if(!state){
ele.addClass("disabled");
}else{
ele.removeClass("disabled");
}
}
function loadMapData(map){
data = [];
if (map == "Default") {
data = { mname: "Default", fnData: fnMasterData };
} else {
data = getStoredMapData(map);
}
$("#mapping-panel").empty();
$("#mapping-panel").append(
'<div class="settings-subheading row">' +
'<div class="column-7 pl0"><div class="map-name-heading">' +
map +
"</div></div>" +
'<div class="column-3 pr0">' +
'<input type="hidden" id="cur-map-val" cur-map="'+map+'"/>' +
'<a href="#" class="option-btn waste-bin" id="delete-map"> &#128465;</a>' +
'<a href="#" class="option-btn" id="import-map"> &#8595;</a>' +
'<a href="#" class="option-btn" id="export-cur-map"> &#8593;</a>' +
'<a href="#" class="option-btn" id="edit-cur-map">&#9998;</a>' +
"</div>" +
"</div>"
);
$("#mapping-panel").append(
'<div class="row settings-group fnhead">' +
'<div class="column-2">Function</div>'+
'<div class="column-4">Name</div>'+
'<div class="column-2">Type</div>' +
'<div class="column-2">Visible</div>' +
'</div>'
);
container = $('<div class="maps-content"></div>').appendTo("#mapping-panel");
$.each(data.fnData, function (key, value) {
container.append(
'<div class="row settings-group" id="'+key+'">' +
'<div class="column-2 caplitalize">'+key+'</div>'+
'<div class="column-4">'+value[2]+'</div>'+
'<div class="column-2">'+ (value[1] == 1 ? '<span class="pill red">Momentary</span>' : '<span class="pill green">Latching</span>') +'</div>' +
'<div class="column-2">'+ (value[3] == 1 ? '<span class="pill green">Visible</span>' : '<span class="pill">Hidden</span>') +'</div>' +
'</div>'
);
});
}
function loadLocomotives(){
locos = getLocoList();
$("#locomotives-panel").empty();
$.each(locos, function (key, value) {
$("#locomotives-panel").append(
'<div class="row settings-group" id="'+key+'">'+
'<div class="column-1 sno"><p>' + (key + 1) + "</p></div>" +
'<div class="column-7 loco-details">' +
'<div class="row">' +
'<div class="column-7"><p class="ac-loco-name column-10">' +
value.name +
"</p></div>" +
'<div class="column-2 cv-num"><p><small>CV </small>' +
value.cv +
"</p></div>" +
"</div>" +
'<div class="row sub-text">' +
'<div class="column-3"><p>' + value.type + '</p></div>' +
'<div class="column-3">' +
(value.decoder == "" ? '<p class="nd">Undefined</p>' : "<p>" + value.decoder + "</p>") +
"</div>" +
'<div class="column-3">' +
(value.brand == "" ? '<p class="nd">Undefined</p>' : "<p>" + value.brand + "</p>") +
"</div></div></div>" +
'<div class="column-2 asst-map"><div class="row">' +
'<div class="column-7"><p class="muted">Map</p><p>' +
value.map +
"</p></div>" +
'<div class="column-3 prh"><a href="#" loco-id="'+key+'" data-loco="'+
value.name+'" class="edit-cur-loco"> &#9998; </a></div></div>' +
"</div></br>"
);
});
}
//Initialization routine for Throttle screen
function setThrottleScreenUI() {
loadmaps();
loadButtons({ mname: "default", fnData: fnMasterData });
controller = getPreference("scontroller");
$("#throttle-selector").val(controller).trigger("change");
setspeedControllerType(controller);
// Show and hide debug console based on preference set in earlier session
if (getPreference("dbugConsole") == null) {
setPreference("dbugConsole", true);
}
if(getPreference("dbugConsole")) {
$("#debug-console").show()
$("#console-toggle").prop("checked", true);
}else{
$("#debug-console").hide();
$("#console-toggle").prop("checked", false);
}
$(".dir-toggle").addClass("forward");
// Set theme
if (getPreference("theme") == null) {
setPreference("theme", "simple");
}else{
theme = getPreference("theme");
$("#theme-selector").val(theme).trigger("change");
if (theme != "simple") {
$("link[title*='theme']").remove();
$("head").append(
'<link rel="stylesheet" type="text/css" title="theme" href="css/themes/'+theme+'.css">'
);
}
}
}
// Change the Speed controller type
function setspeedControllerType(pref){
// Set saved throttle or use circular throttle as default
$(".speedController").hide();
switch (pref) {
case "vertical":
console.log("Vertical Speed Controller");
$("#vertical-throttle").show();
break;
case "knob":
console.log("Knob Speed Controller");
$("#knobthrottle").show();
break;
case "circular":
console.log("Circular Speed Controller");
$("#circular-throttle").show();
break;
case "default":
case null:
case undefined:
console.log("Fallback Speed Controller");
$("#vertical-throttle").show();
setPreference("scontroller", "vertical");
$("#throttle-selector").val("vertical").trigger("change");
}
}
// Enabling/disabling Speed Controllers
function toggleThrottleState(state){
if(state){
$("#circular-throttle").roundSlider("enable");
$("#v-throttle").slider("enable");
toggleKnobState($("#knobthrottle"), true);
}else{
$("#circular-throttle").roundSlider("disable");
$("#v-throttle").slider("disable");
toggleKnobState($("#knobthrottle"), false);
}
}
/**********************************************************
* This function will
1. Send Speed command
2. Set speed value of all the available sliders/controllers
3. Set respctive speed number
************************************************************/
function setSpeedofControllers(){
spd = getSpeed();
if(!isStopped){
writeToStream("t 01 " + getCV() + " " + spd + " " + getDirection());
}
// Circular
$("#circular-throttle").roundSlider("setValue", spd);
// Vertical
$("#v-throttle").val(spd).change();
$("#v-throttle").slider("option", "value", spd);
$("#speed-indicator").html(spd);
// Knob
$("#knob-value").html(spd);
knob.val(spd).change();
}
// This function will generate commands for each type of function
function generateFnCommand(clickedBtn){
func = clickedBtn.attr('name'); // Gives function name (F1, F2, .... F28)
eventType = clickedBtn.data("type"); // Gives type of button (Press/Hold or Toggle)
btnPressed = clickedBtn.attr("aria-pressed");
//console.log("Function Name=>"+func+" , Button Type=>"+eventType+" , Button Pressed=>"+btnStatus);
switch(func){
case "f0":
case "f1":
case "f2":
case "f3":
case "f4":
{
if(btnPressed=="true"){
sendCommandForF0ToF4(func,1);
}else{
sendCommandForF0ToF4(func,0);
}
break;
}
case "f5":
case "f6":
case "f7":
case "f8":
{
if(btnPressed=="true"){
sendCommandForF5ToF8(func,1);
}else{
sendCommandForF5ToF8(func,0);
}
break;
}
case "f9":
case "f10":
case "f11":
case "f12":
{
if(btnPressed=="true"){
sendCommandForF9ToF12(func,1);
}else{
sendCommandForF9ToF12(func,0);
}
break;
}
case "f13":
case "f14":
case "f15":
case "f16":
case "f17":
case "f18":
case "f19":
case "f20":
{
if(btnPressed=="true"){
sendCommandForF13ToF20(func,1);
}else{
sendCommandForF13ToF20(func,0);
}
break;
}
case "f21":
case "f22":
case "f23":
case "f24":
case "f25":
case "f26":
case "f27":
case "f28":
{
if(btnPressed=="true"){
sendCommandForF21ToF28(func,1);
}else{
sendCommandForF21ToF28(func,0);
}
break;
}
default:
{
alert("Invalid Function");
}
}
}
$(document).ready(function(){
var mode = 0;
// Left Menu
$("#nav-open").on("click", function () {
$("#side-menu").show().animate({ left: 0 });
});
$("#nav-close").on("click", function () {
$("#side-menu").animate({ left: -260 }, function(){
$("#side-menu").hide();
});
});
$("#info-tooltip").tooltip({
content:
"<p>DCC++ EX Web Throttle<br>(WebThrottle-EX)</p><hr><p>Version: "+version+"</p><p><b>Credits</b><br> Fred Decker <br> Mani Kumar <br> Matt H</p>",
show: {
effect: "slideDown",
delay: 100,
},
classes: {
"ui-tooltip": "credits-tooltip",
},
position: {
my: "left top",
at: "left bottom",
},
});
// Load function map, buttons throttle etc
setThrottleScreenUI();
$("#throttle-selector").on("change", function (e) {
selectedval = $(this).val();
console.log(selectedval);
setPreference("scontroller", selectedval);
setspeedControllerType(selectedval);
});
$("#theme-selector").on("change", function (e) {
selectedval = $(this).val();
console.log(selectedval);
setPreference("theme", selectedval);
$("link[title*='theme']").remove();
if (selectedval != "simple") {
$("head").append(
'<link rel="stylesheet" type="text/css" title="theme" href="css/themes/' +
selectedval +
'.css">'
);
}
});
// Connect command station
$("#button-connect").on("click", function () {
toggleServer($(this));
});
// Disconnect command station
$("#button-disconnect").on("click", function () {
disconnectServer();
});
// Aquire loco of given CV
$("#button-getloco").on("click", function () {
acButton = $(this);
isAcquired = $(this).data("acquired");
// Parse int only returns number if the string is starting with Number
locoid_input = parseInt($("#ex-locoid").val());
if (locoid_input != 0) {
if (isAcquired == false && getCV() == 0) {
setCV(locoid_input);
$("#loco-info").html("Acquired Locomotive: " + locoid_input);
acButton.data("acquired", true);
acButton.html('<span class="icon-cross"></span>');
toggleThrottleState(true);
} else {
currentCV = getCV();
$("#ex-locoid").val(0);
setCV(0);
$("#loco-info").html("Released Locomotive: " + currentCV);
acButton.data("acquired", false);
acButton.html('<span class="icon-circle-right"></span>');
toggleThrottleState(false);
}
}
});
// Switch ON/OFF power of the Command station
$("#power-switch").on("click", function () {
pb = $(this).is(":checked");
if (pb == true) {
writeToStream("1");
$("#power-status").html("On");
} else {
writeToStream("0");
$("#power-status").html("Off");
}
});
////////////////////////////////////
$("#v-throttle").slider({
orientation: "vertical",
min: 0,
max: 126,
disabled: true,
range: "max",
slide: function (event, ui) {
$("#speed-indicator").html(ui.value);
setSpeed(ui.value);
setSpeedofControllers();
},
});
/////////////////////////////////////////*/
knob = $(".rotarySwitch").rotaryswitch({
minimum: 0,
maximum: 126,
step: 2,
beginDeg: 210,
lengthDeg: 295,
minimumOverMaximum: true,
showMarks: true,
themeClass: "big light",
});
toggleKnobState($("#knobthrottle"), false);
knob.on("change", function () {
oldValue = getSpeed();
kval = knob.val();
$("#knob-value").html(kval);
setSpeed(kval);
// Below condition is to avoid infinite loop
// that triggers change() event indifinitely
if (oldValue != kval) {
setSpeedofControllers();
} else {
writeToStream(
"t 01 " + getCV() + " " + getSpeed() + " " + getDirection()
);
}
//console.log( "t 01 " + getCV() + " " + getSpeed() + " " + getDirection());
});
/////////////////////////////////////////////
// Speed (round) Slider allows user to change the speed of the locomotive
Tht = $("#circular-throttle").roundSlider({
width: 20,
radius: 116,
value: speed,
circleShape: "pie",
handleShape: "dot",
startAngle: 315,
lineCap: "round",
sliderType: "min-range",
showTooltip: true,
editableTooltip: false,
handleSize: "+18",
max: "126",
disabled: true,
update: function (slider) {
setSpeed(slider.value);
setSpeedofControllers();
//console.log("t 01 "+getCV()+" "+getSpeed()+" "+getDirection());
},
valueChange: function (slider) {
//setSpeed(slider.value);
//writeToStream("t 01 "+getCV()+" "+getSpeed()+" "+getDirection());
// console.log("This event is similar to 'update' event, in addition it will trigger even the value was changed through programmatically also.");
},
});
// Allows user to change the direction of the loco and STOP.
$(".dir-btn").on("click", function () {
if (getCV() != 0){
current = $(this);
dir = current.attr("aria-label");
$(".dir-btn").removeClass("selected");
current.addClass("selected", 200);
console.log(dir);
$(".dir-toggle").removeClass("forward backward stop");
$(".dir-toggle").addClass(dir);
// Do direction stuff here
switch (dir) {
case "forward": {
isStopped = false;
setDirection(1);
setSpeedofControllers();
writeToStream("t 01 " + getCV() + " " + getSpeed() + " 1");
break;
}
case "backward": {
isStopped = false;
setDirection(0);
setSpeedofControllers();
writeToStream("t 01 " + getCV() + " " + getSpeed() + " 0");
break;
}
case "stop": {
isStopped = true;
dir = getDirection();
setSpeed(0);
setSpeedofControllers();
writeToStream("t 01 " + getCV() + " 0 " + dir);
break;
}
}
}else{
console.log("No loco acquired");
}
});
$("#emergency-stop").on("click", function () {
if (getCV() != 0){
isStopped = true;
dir = getDirection();
setSpeed(0);
setSpeedofControllers();
writeToStream("t 01 " + getCV() + " -1 " + dir);
}
else{
console.log("No loco acquired");
}
});
// Hide/Show the Loco, Connect server fields (on top)
$("#button-hide").on("click", function () {
if ($(".details-panel").is(":visible")) {
$(".details-panel").hide();
$(this).css("top", 0);
$(this).html('<span class="icon-circle-down"></span>');
} else {
$(".details-panel").show();
$(this).html('<span class="icon-circle-up"></span>');
$(this).css("top", "-9px");
}
});
// PLUS button. Increases speed on Hold / Tap
var tId = 0;
$("#button-right")
.on("mousedown", function () {
event.stopImmediatePropagation();
tId = setInterval(function () {
var sp = getSpeed();
if (sp <= 125 && getDirection() != -1 && getCV() != 0) {
setSpeed(sp + speedStep);
setSpeedofControllers();
writeToStream(
"t 01 " + getCV() + " " + getSpeed() + " " + getDirection()
);
sp = 0;
}
}, 100);
})
.on("mouseup mouseleave", function () {
clearInterval(tId);
})
.on("click", function () {
event.stopImmediatePropagation();
var sp = getSpeed();
if (sp <= 125 && getDirection() != -1 && getCV() != 0) {
setSpeed(sp + speedStep);
setSpeedofControllers();
writeToStream(
"t 01 " + getCV() + " " + getSpeed() + " " + getDirection()
);
sp = 0;
}
});
// MINUS button. Decreases speed on Hold / Tap
var tId = 0;
$("#button-left")
.on("mousedown", function () {
event.stopImmediatePropagation();
tId = setInterval(function () {
var sp = getSpeed(sp);
if (sp >= 1 && getDirection() != -1 && getCV() != 0) {
setSpeed(sp - speedStep);
setSpeedofControllers();
writeToStream(
"t 01 " + getCV() + " " + getSpeed() + " " + getDirection()
);
sp = 0;
}
}, 100);
})
.on("mouseup mouseleave", function () {
clearInterval(tId);
})
.on("click", function () {
event.stopImmediatePropagation();
var sp = getSpeed(sp);
if (sp >= 1 && getDirection() != -1 && getCV() != 0) {
setSpeed(sp - speedStep);
setSpeedofControllers();
writeToStream(
"t 01 " + getCV() + " " + getSpeed() + " " + getDirection()
);
sp = 0;
}
});
// Functions buttons
// Send Instructions to generate command depends the type of Button (press/toggle)
var timer = 0;
$(document)
.on("mousedown", ".fn-btn", function () {
console.log($(this).val);
clickedBtn = $(this);
btnType = clickedBtn.data("type");
if (btnType == "press") {
timer = setInterval(function () {
// MOMENTARY HOLD ON
clickedBtn.attr("aria-pressed", "true");
generateFnCommand(clickedBtn);
console.log("PRESSED HOLD ==> " + clickedBtn.attr("name"));
}, 100);
}
})
.on("mouseup mouserelease", ".fn-btn", function () {
clearInterval(timer);
clickedBtn = $(this);
btnType = clickedBtn.data("type");
btnState = clickedBtn.attr("aria-pressed");
if (btnType == "press") {
// MOMENTARY HOLD OFF
clickedBtn.attr("aria-pressed", "false");
generateFnCommand(clickedBtn);
console.log("RELEASED HOLD ==> " + clickedBtn.attr("name"));
} else {
if (btnState == "false") {
// TOGGLE ON
clickedBtn.attr("aria-pressed", "true");
generateFnCommand(clickedBtn);
console.log("TOGGLE ON ==> " + clickedBtn.attr("name"));
} else {
// TOGGLE OFF
clickedBtn.attr("aria-pressed", "false");
generateFnCommand(clickedBtn);
console.log("TOGGLE OFF ==> " + clickedBtn.attr("name"));
}
}
});
// Hide/Show the Debug console
$("#console-toggle").on("click", function () {
pb = $(this).is(":checked");
if (pb == true) {
$("#debug-console").show();
setPreference("dbugConsole", true);
} else {
$("#debug-console").hide();
setPreference("dbugConsole", false);
}
});
// Send command written in console
$("#button-sendCmd").on("click", function () {
cmd = $("#cmd-direct").val();
writeToStream(cmd);
document.getElementById("cmd-direct").value = "";
});
// Clear the console log window
$("#button-clearLog").on("click", function () {
$("#log-box").html("");
});
// Function to toggle fullScreen viceversa
$("#fs-toggle").on("click", function () {
st = $(this).attr("state");
var elem = document.documentElement;
if (st == "ws") {
$(this).attr("state", "fs");
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.mozRequestFullScreen) {
/* Firefox */
elem.mozRequestFullScreen();
} else if (elem.webkitRequestFullscreen) {
/* Chrome, Safari and Opera */
elem.webkitRequestFullscreen();
} else if (elem.msRequestFullscreen) {
/* IE/Edge */
elem.msRequestFullscreen();
}
} else {
$(this).attr("state", "ws");
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
/* Firefox */
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
/* Chrome, Safari and Opera */
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
/* IE/Edge */
document.msExitFullscreen();
}
}
});
//Handles navigation clicks
$("#throttle-nav").on("click", function () {
hideWindows();
$("#throttle-window").show();
$("#nav-close").trigger("click");
});
$("#loco-nav").on("click", function () {
hideWindows();
$("#loco-window").show();
$("#nav-close").trigger("click");
loadLocomotives();
});
$("#fn-map-nav").on("click", function () {
hideWindows();
$("#fn-map-window").show();
$("#nav-close").trigger("click");
setFunctionMaps();
//loadMapData("Default");
});
$("#settings-nav").on("click", function () {
hideWindows();
$("#settings-window").show();
$("#nav-close").trigger("click");
});
eventListeners();
/*
$("#settings-general").on('click', function(){
hideSettings();
$("#general-section").show();
});
$("#settings-storage").on('click', function(){
hideSettings();
$("#storage-section").show();
});*/
$("#settings-general").on("click", function () {
/*var target = $('#general-section');
if (target.length) {
$('#settings-panel').animate({
scrollTop: target.offset().top
}, 1000);
}*/
$("#general-section")[0].scrollIntoView(true);
});
$("#settings-storage").on("click", function () {
/*var target = $('#storage-section');
if (target.length) {
$('#settings-panel').animate({
scrollTop: target.offset().top
}, 1000);
}*/
$("#storage-section")[0].scrollIntoView(true);
});
$('#settings-app').on('click', function() {
$("#app-section")[0].scrollIntoView(true);
})
$(document).on("click", ".map-name", function () {
loadMapData($(this).attr("map-val"));
$("li.map-name").removeClass("active");
$(this).addClass("active");
});
// This allows user to delete currently selected Map
$(document).on("click", "#delete-map", function () {
selectedval = $("#cur-map-val").attr("cur-map");
if (selectedval != "Default") {
deleteFuncData(selectedval);
loadmaps();
setFunctionMaps();
loadMapData("Default");
$("#select-map").val("default").trigger("change");
$("li.map-name").removeClass("active");
$("li.map-name[map-val= 'Default']").addClass("active");
}
});
$(document).on("click", ".edit-cur-loco", function () {
cabdata = getStoredLocoData($(this).attr("data-loco"));
$("#loco-form")[0].reset();
$("#loco-form-content").css("display", "inline-block");
$(".add-loco-form .add-loco-head").html("Edit Locomotive");
$("#loco-submit").attr("loco-mode", "edit");
$("#loco-submit").attr("loco-id", $(this).attr("loco-id"));
$.each(cabdata, function (key, value) {
$("#loco-form").children().find("#"+key).val(value);
if(key=="map"){
$("#function-maps").autocomplete("search", value);
var menu = $("#function-maps").autocomplete("widget");
$(menu[0].children[0]).click();
}
});
});
});
function setFunctionMaps() {
const defaultMap = {
mname: "Default",
fnData: {},
}
const maps = [defaultMap, ...getMapData()];
$("#function-mappings").empty();
maps.forEach(map => {
const name = map.mname
$("#function-mappings").append(`<li class='map-name' map-val=${name}>${name}</li>`);
})
}
function hideWindows(){
$("#throttle-window").hide();
$("#loco-window").hide();
$("#fn-map-window").hide();
$("#settings-window").hide();
}
function hideSettings(){
$("#general-section").hide();
$("#storage-section").hide();
}
function credits() {
authors = ["Fred Decker","Mani Kumar","Matt"]
displayLog("Credits:")
console.log("Credits:")
for (i=0; i<authors.length; i++) {
displayLog(authors[i])
console.log(authors[i])
}
}
function eventListeners(){
var cmdDirect = document.getElementById("cmd-direct");
var exLocoID = document.getElementById("ex-locoid");
cmdDirect.addEventListener("keyup", function(event) {
if (event.key === "Enter") {
event.preventDefault();
// Trigger the button element with a click
$('#button-sendCmd').click();
}
});
exLocoID.addEventListener("keyup", function(event) {
if (event.key === "Enter") {
event.preventDefault();
// Trigger the button element with a click
$('#button-getloco').click();
}
})
}

View File

@@ -0,0 +1,34 @@
// This is default function definition
// New functions are based on this
// If no Map is loaded this will load ad default
var fnMasterData = {
"f0": [0, 0, "Head Light", 1], // Index => 0=state, 1=type (0-Toggle, 1-press),
"f1": [0, 1, "Bell" , 1], // 2=Label, 3= Available
"f2": [0, 1, "Horn", 1],
"f3": [0, 0, "F3" , 1],
"f4": [0, 0, "F4", 1],
"f5": [0, 0, "F5", 1],
"f6": [0, 0, "F6", 1],
"f7": [0, 0, "F7", 1],
"f8": [0, 0, "F8", 1],
"f9": [0, 0, "F9", 1],
"f10": [0, 0, "F10", 1],
"f11": [0, 0, "F11", 1],
"f12": [0, 0, "F12", 1],
"f13": [0, 0, "F13", 1],
"f14": [0, 0, "F14", 1],
"f15": [0, 0, "F15", 1],
"f16": [0, 0, "F16", 1],
"f17": [0, 0, "F17", 1],
"f18": [0, 0, "F18", 1],
"f19": [0, 0, "F19", 1],
"f20": [0, 0, "F20", 1],
"f21": [0, 0, "F21", 1],
"f22": [0, 0, "F22", 1],
"f23": [0, 0, "F23", 1],
"f24": [0, 0, "F24", 1],
"f25": [0, 0, "F25", 1],
"f26": [0, 0, "F26", 1],
"f27": [0, 0, "F27", 1],
"f28": [0, 0, "F28", 1],
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,420 @@
/*
Version 1.0.1
Copyright 2014 Red White Silver GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function($, window, document, undefined) {
var pluginName = 'rotaryswitch',
defaults = {
minimum: 0, // Minimal value
maximum: 12, // Maximum value
step: 1, // Step size
snapInMotion: true, // Snap to steps in motion
beginDeg: 0, // Start point in deg
lengthDeg: 360, // Length in deg
minimumOverMaximum: true, // Which value will used, if the the start and the end point at the same deg.
showInput: false, // Show input element
showMarks: false, // Show deg marks
themeClass: 'defaultTheme' // Theme class
};
function Plugin(element, options) {
this.element = $(element);
this.domElements = {};
this.htmlStructure = {
wrap: '<div class="rotaryswitchPlugin"></div>',
switchButton: '<div class="switch"></div>',
overlay: '<div class="overlay"></div>',
marks: '<div class="marks"></div>',
mark: '<div class="mark"></div>'
};
this.mousePosition = {x: -1, y: -1};
this.switchDeg = 0;
this.valueInPercent = 0;
this.value = 0;
this.steps = 0;
this.totalDeg = 0;
this.degPerStep = 0;
this.lastTriggeredValue = -1;
this.options = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
this.initialize();
}
Plugin.prototype = {
/**
* Initialze this plugin
* See inline comments for more details
*
* @param none
* @return none
*/
initialize: function() {
// Save the needed jquery DOM elements
this.domElements = {
// Wrap the input element and save the parent as main element and add theme class:
main: this.element.wrap(this.htmlStructure.wrap).parent().addClass(this.options.themeClass),
switchButton: $(this.htmlStructure.switchButton),
overlay: $(this.htmlStructure.overlay)
};
// Append addional dom elements:
this.domElements.main.append([this.domElements.switchButton, this.domElements.overlay]);
// Calculate the length (Maximum - minimum)
this.steps = Math.abs(this.options.maximum - this.options.minimum);
// Calculate the total deg length
this.totalDeg = this.options.lengthDeg;
// Calculate deg per step
this.degPerStep = this.totalDeg / this.steps;
// Listen to some necessary events
this.domElements.main.on('mousedown', $.proxy(this.onMouseDown, this));
this.domElements.main.on('touchstart', $.proxy(this.onTouchStart, this));
this.element.on('change', $.proxy(this.onChangeElementValue, this));
this.readValueFromInput(); // Get the value from the input element
this.rotateSwitch(); // Rotate the switch
// Show marks if wished
if (this.options.showMarks === true) {
this.renderMarks();
}
// Show the input element if wished
if (this.options.showInput === false) {
this.element.hide();
}
},
/**
* Adds marks for each step
*
* @param none
* @return none
*/
renderMarks: function() {
var i=0,
len = this.steps / this.options.step,
deg = this.options.beginDeg,
degPerStep = this.degPerStep * this.options.step,
marks = $(this.htmlStructure.marks);
for (; i < len; i += 1) {
deg += degPerStep;
var mark = $(this.htmlStructure.mark).css({'transform': 'rotate('+deg+'deg) translate(0, -'+ (this.domElements.main.width()/2 + (this.domElements.main.width()*0.1)) +'px)'});
marks.append(mark);
}
this.domElements.main.append(marks);
},
/**
* On mouse down event handler
* Save the mouse position (x, y) to the object this.mousePosition
* call the method this.startHandling
*
* @param event jquery mouse event
* @return none
*/
onMouseDown: function(event) {
event.preventDefault();
this.mousePosition.x = event.pageX;
this.mousePosition.y = event.pageY;
this.startHandling();
},
/**
* On mouse up event handler
* call the method this.stopHandling
*
* @param event jquery mouse event
* @return none
*/
onMouseUp: function(event) {
this.stopHandling();
},
/**
* On mouse move event handler
* Save the mouse position (x, y) to the object this.mousePosition
* call the method this.calculateSwitchDeg
* call the method this.calculateValueByDeg
* call the method this.rotateSwitch
* call the method this.setValueToInput
*
* @param event jquery mouse event
* @return none
*/
onMousemove: function(event) {
event.preventDefault();
this.mousePosition.x = event.pageX;
this.mousePosition.y = event.pageY;
this.calculateSwitchDeg();
this.calculateValueByDeg();
this.rotateSwitch();
this.setValueToInput();
},
/**
* On touch start event handler
* Identical width this.onMouseDown
*
* @param event jquery touch event
* @return none
* @see this.onMouseDown
*/
onTouchStart: function(event) {
event.preventDefault();
this.mousePosition.x = event.originalEvent.targetTouches[0].pageX;
this.mousePosition.y = event.originalEvent.targetTouches[0].pageY;
this.startHandling();
},
/**
* On touch end event handler
* Identical width this.onMouseUp
*
* @param event jquery touch event
* @return none
* @see this.onMouseUp
*/
onTouchEnd: function(event) {
this.stopHandling();
},
/**
* On touch move event handler
* Identical width this.onMousemove
*
* @param event jquery touch event
* @return none
* @see this.onMousemove
*/
onTouchMove: function(event) {
event.preventDefault();
this.mousePosition.x = event.originalEvent.targetTouches[0].pageX;
this.mousePosition.y = event.originalEvent.targetTouches[0].pageY;
this.calculateSwitchDeg();
this.calculateValueByDeg();
this.rotateSwitch();
this.setValueToInput();
},
/**
* On change input element event handler
* If event not triggered by plugin:
* Call the method this.readValueFromInput
* Call the method this.rotateSwitch
*
* @param event jquery event
* @return none
*/
onChangeElementValue: function(event) {
if (!event.plugin || event.plugin !== this) {
this.readValueFromInput();
this.rotateSwitch();
}
},
/**
* Triggered by mouse or touch begin event
* Calls some methods
* Listen to some events
* Add class 'active' to the main DOM element (this.domElements.main)
*
* @param none
* @return none
*/
startHandling: function() {
this.calculateSwitchDeg();
this.calculateValueByDeg();
this.rotateSwitch();
this.setValueToInput();
$(document).on('mouseup', $.proxy(this.onMouseUp, this));
$(document).on('mousemove', $.proxy(this.onMousemove, this));
$(document).on('touchend', $.proxy(this.onTouchEnd, this));
$(document).on('touchmove', $.proxy(this.onTouchMove, this));
this.domElements.main.addClass('active');
},
/**
* Triggered by mouseup or touchend event
* Stop listen to some events
* Call method this.rotateSwitch
* Remove class 'active' from the main DOM element (this.domElements.main)
*
* @param none
* @return none
*/
stopHandling: function() {
$(document).off('mouseup', $.proxy(this.onMouseUp, this))
.off('mousemove', $.proxy(this.onMousemove, this))
.off('touchend', $.proxy(this.onTouchEnd, this))
.off('touchmove', $.proxy(this.onTouchMove, this));
this.rotateSwitch(true);
this.domElements.main.removeClass('active');
},
/**
* Calculate the switch deg by the element position and the mouse position
* Stores the switch deg in this.switchDeg
* @param none
* @return none
*/
calculateSwitchDeg: function() {
var offset = this.domElements.main.offset(),
radians = Math.atan2(this.mousePosition.x - (offset.left + (this.domElements.main.width()/2)), this.mousePosition.y - (offset.top + (this.domElements.main.height()/2)));
if (this.mousePosition.x !== -1) {
this.switchDeg = (radians * (180 / Math.PI) * -1) + 180;
}
},
/**
* Calculate the value by deg
* Stores the in percent in this.valueInPercent
* Stores the value in this.value
* @param none
* @return none
*/
calculateValueByDeg: function() {
var range = this.options.maximum - this.options.minimum;
if (this.switchDeg - this.options.beginDeg > 0) {
this.valueInPercent = (this.switchDeg - this.options.beginDeg) / this.totalDeg;
} else {
this.valueInPercent = (this.switchDeg - this.options.beginDeg + 360) / this.totalDeg;
}
if (this.valueInPercent > 1) {
if (this.valueInPercent > (((360 / this.totalDeg)-1) / 2)+1 ) {
this.valueInPercent = 0;
} else {
this.valueInPercent = 1;
}
}
this.value = ~~ (((((range * this.valueInPercent) < 0) ? -0.5 : 0.5) + ((range * this.valueInPercent) / this.options.step))) * this.options.step;
this.value += this.options.minimum;
if (this.options.lengthDeg === 360 && (this.value === this.options.minimum || this.value === this.options.maximum)) {
if (this.options.minimumOverMaximum === true) {
this.value = this.options.minimum;
} else {
this.value = this.options.maximum;
}
}
},
/**
* Rotate the switch with css transform
* snap to the the next rounded value if the parameter snap is true
* @param snap boolean
* @return none
*/
rotateSwitch: function(snap) {
var deg = 0,
exactDeg = (this.valueInPercent * this.totalDeg),
roundedDeg = ((this.value / this.steps) * this.totalDeg) - (this.options.minimum * this.degPerStep),
difference = Math.abs(Math.abs(exactDeg) - Math.abs(roundedDeg)),
rotateString = '';
if (snap === true || (this.options.snapInMotion === true && difference < this.degPerStep / 6)) {
if (roundedDeg + this.options.beginDeg < 360) {
deg = (roundedDeg + this.options.beginDeg);
} else {
deg = roundedDeg + this.options.beginDeg - 360;
}
} else {
if (exactDeg + this.options.beginDeg < 360) {
deg = (exactDeg + this.options.beginDeg);
} else {
deg = exactDeg + this.options.beginDeg - 360;
}
}
rotateString = ['rotate(', deg, 'deg)'].join('');
this.domElements.switchButton.css({
'transform': rotateString,
'-webkit-transform': rotateString,
'-moz-transform': rotateString,
'-o-transform': rotateString,
'-ms-transform': rotateString
});
},
/**
* Read the valur from the input element
* If no value available, this.options.minimum is used
* @param snap boolean
* @return none
*/
readValueFromInput: function() {
var elementValue = parseInt(this.element.val(), 10);
if (isNaN(elementValue) === true) {
this.value = this.options.minimum;
} else {
this.value = Math.max(this.options.minimum, elementValue);
this.value = Math.min(this.options.maximum, this.value);
}
this.value -= this.options.minimum;
this.valueInPercent = this.value / (this.options.maximum - this.options.minimum);
},
/**
* Set the value to the input element
* and trigger the change event on the input element
* @param none
* @return none
*/
setValueToInput: function() {
if (this.value !== this.lastTriggeredValue) {
this.lastTriggeredValue = this.value;
this.element.val(this.value).trigger({type: 'change', plugin: this});
}
}
};
$.fn[pluginName] = function(options) {
return this.each(function() {
if (!$.data(this, 'plugin_' + pluginName)){
$.data(this, 'plugin_' + pluginName, new Plugin(this, options));
}
});
};
}(jQuery, window, document));

View File

@@ -0,0 +1,34 @@
$(document).ready(function(){
let deferredPrompt;
const addBtn = document.querySelector('.add-button');
const removeBtn = document.querySelector('.installed-label');
addBtn.style.display = 'none';
removeBtn.style.display = 'block';
window.addEventListener('beforeinstallprompt', (e) => {
removeBtn.style.display = 'none';
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
// Update UI to notify the user they can add to home screen
addBtn.style.display = 'block';
addBtn.addEventListener('click', (e) => {
// hide our user interface that shows our A2HS button
//addBtn.style.display = 'none';
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the prompt, app installed');
addBtn.style.display = 'none';
removeBtn.style.display = 'block';
} else {
console.log('User dismissed the prompt');
}
deferredPrompt = null;
});
});
});
});

View File

@@ -0,0 +1,33 @@
class RestAPI {
constructor({logger}) {
this.csrftoken = this.getCookie('csrftoken');
this.logger = logger
}
getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
write(packet) {
$.ajax({
type: "PUT",
url: "/api/v1/dcc/command",
data: packet,
success: function (data) { displayLog('[RECEIVE] '+data.response); },
contentType: "text/plain",
headers: {'X-CSRFToken': this.csrftoken}
});
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,586 @@
/* This is part of the DCC++ EX Project for model railroading and more.
For licence information, please see index.html.
For more information, see us at dcc-ex.com.
storageController.js
Manages the setting storage capabilities
*/
$(document).ready(function(){
// This is displays message about Local storage Support of the Local browser
if (typeof Storage !== "undefined") {
console.log("Your browser supports Local Storage");
} else {
console.log("Sorry! Your browser does not supporting Local Storage");
}
// Opens NEW MAP window with all fields empty
$("#add-map").on("click", function () {
$("#save-fn-map").attr("mode", "new");
$(".fn-heading").html("New Mapping");
showBtnConfig({ mname: "", fnData: fnMasterData });
});
// This will Load buttons on selecting a map from the select box
$("#select-map").change(function () {
selectedval = $(this).val();
if (selectedval != "default") {
data = getStoredMapData(selectedval);
loadButtons(data);
} else {
loadButtons({ mname: "default", fnData: fnMasterData });
}
});
// Opens MAP window with all fields filled from the selected map that allows editing Map
$(document).on("click", "#edit-cur-map", function () {
$("#save-fn-map").attr("mode", "edit");
$(".fn-heading").html("Edit Mapping");
selectedval = $("#cur-map-val").attr("cur-map");
if (selectedval != "Default") {
data = getStoredMapData(selectedval);
showBtnConfig(data);
} else {
alert("Cannot edit Default mapping!");
}
//showBtnConfig();
});
// Closes MAP window on clicking X icon
$("#close-model").on("click", function () {
$("#fnModal").hide();
});
//This will check for Save mode (NEW MAP / EDIT MAP) and delegate the functionality
$("#save-fn-map").on("click", function () {
mode = $(this).attr("mode");
// alert(mode); // debug line
if (mode == "new") {
addNewMap();
} else {
editMap();
}
});
//Allows user to download the selected map in .JSON format
$(document).on("click", "#export-cur-map", function () {
map = $("#cur-map-val").attr("cur-map");
if (map != "default") {
downloadMapData(map);
} else {
alert("Please select Custom map");
}
});
// This remove whole exthrottle app data but with confirmation
$("#wipe-map").on("click", function () {
var r = confirm("Are you sure on deletion?");
if (r == true) {
window.localStorage.removeItem("mapData");
console.log("!!!!!!MAPS WIPED!!!!!!");
loadmaps();
}
});
// This allows user to download whole exthrottle app data
$("#export-all-maps").on("click", function () {
exportMapData();
});
// This allows user to upload previously downloaded Map (JSON format must adhere)
$(document).on("click", "#import-map", function (e) {
e.preventDefault();
$("#map-upload").trigger("click");
});
// This part with above which is responsible for actual file upload for MAP
$("#map-upload").on("change", function (e) {
var file = e.target.files[0];
var field = $(this);
var freader = new FileReader();
freader.onload = function (evt) {
data = JSON.parse(evt.target.result);
setMapData(data);
field.val("");
};
freader.readAsText(file);
});
// This is responsible for Cab upload
$("#cabs-upload").on("change", function (e) {
var file = e.target.files[0];
var field = $(this);
var freader = new FileReader();
freader.onload = function (evt) {
data = JSON.parse(evt.target.result);
importLocoData(data);
field.val("");
};
freader.readAsText(file);
});
// This allows user to upload previously downloaded MAP DATA (JSON format must adhere)
$(document).on("click", "#import-all-maps", function (e) {
e.preventDefault();
//$("#appdata-upload").attr("mode", "mapData");
$("#maps-upload").trigger("click");
});
// This part of above which is responsible for actual file upload for MAP DATA
$("#maps-upload").on("change", function (e) {
var file = e.target.files[0];
var field = $(this);
var freader = new FileReader();
freader.onload = function (evt) {
data = JSON.parse(evt.target.result);
importMapdata(data);
field.val("");
};
freader.readAsText(file);
});
// Set height of throttle container according to functions panel
$(".throttle-container").height($(".functionKeys").first().height());
//Temparory function Shows APP DATA in console
$("#loco-info").on("click", function () {
console.log(getMapData());
});
//Functions for the storage page in settings
$("#backup-app-settings").on("click", function () {
exportAppData();
});
// This allows user to upload previously downloaded APP DATA (JSON format must adhere)
$("#restore-app-settings").on("click", function (e) {
e.preventDefault();
$("#app-upload").trigger("click");
});
// This part of above which is responsible for actual file upload for APP DATA
$("#app-upload").on("change", function (e) {
var file = e.target.files[0];
var field = $(this);
var freader = new FileReader();
freader.onload = function (evt) {
data = JSON.parse(evt.target.result);
importAppdata(data);
field.val("");
};
freader.readAsText(file);
});
$("#wipe-app-settings").on("click", function () {
var r = confirm("Are you sure on deletion?");
if (r == true) {
window.localStorage.removeItem("mapData");
window.localStorage.removeItem("cabList");
window.localStorage.removeItem("userpref");
console.log("!!!!!DATA IS WIPED!!!!!!");
loadmaps();
}
});
});
// Load all maps to select box
function loadmaps(){
$("#select-map").empty();
$("#select-map").append($("<option />").val("default").text("Default"));
getMapData().forEach(map => {
$("#select-map").append($("<option />").val(map.mname).text(map.mname));
})
}
// Load button layout of selected Map
function loadButtons(data){
$("#fn-wrapper").empty();
$.each(data.fnData, function(key, value){
isPressed = value[0] != 0 ? true : false;
btnType = value[1] != 0 ? "press" : "toggle";
if(value[3]==1){
$("#fn-wrapper").append(
"<div class='fn-button form-group field-button-fn'> <button class='btn-default btn fn-btn "+btnType+"' data-type='"+
btnType+"' aria-pressed='"+isPressed+"' name='"+key+"' id='"+key+"'>"+
value[2]+"</button>"
+"</div>");
}
});
}
// Show the Custom Map fields inside Custom map window while adding and editing a Map.
function showBtnConfig(data){
$("#fnModal").show();
$('#fnModal').css({"top":"7%", "left": "18%"});
$('#fnModal').draggable();
$("#fnModal .fn-modal-content").empty();
$("#fnModal .fn-modal-content").append('<div class="row header-row"><div class="column-2 header-col func-title">Map Name</div> <div class="column-5 header-col"><input type="text" class="fn-input" id="map-name" value="'+data.mname+'"/></div> <div class="column-3 header-col"></div></div>');
$("#fnModal .fn-modal-content").append('<div class="row header-row"><div class="column-1 header-col">Function</div> <div class="column-4 header-col">Label</div> <div class="column-3 header-col">Button Type</div><div class="column-2 header-col">Visibility</div></div>');
$.each(data.fnData, function(key, value){
isPressed = value[0] != 0 ? true : false;
btnType = value[1] != 0 ? "press" : "toggle";
btnpress = value[1] == 1 ? "checked" : "";
btnToggle = value[1] == 0 ? "checked" : "";
fvisible = value[3] == 1 ? "checked" : "";
$("#fnModal .fn-modal-content").append('<div class="row edit-row" id="'+key+'">'+
'<div class="column-1 func-title">'+key +'</div>'+
'<div class="column-4"> <input class="fn-input" name="'+key+'" id="'+key+'" value="'+value[2]+'"/>'+
'<span class="focus-border"><i></i></span>'+
'</div>'+
'<div class="fn-radio column-3" name="'+key+'Type" id="'+key+'Type">'+
'<input type="radio" id="'+key+'press" name="btn'+key+'Type" value="press" '+btnpress+'/>'+
'<label for="'+key+'press">Momentary</label> &nbsp;'+
'<input type="radio" id="'+key+'toggle" name="btn'+key+'Type" value="toggle" '+btnToggle+'/>'+
'<label for="'+key+'toggle">Latching</label>'+
'</div>'+
'<div class="fn-chkbox column-2" name="'+key+'Visible" id="'+key+'Type">'+
'<input type="checkbox" id="'+key+'Visible" name="'+key+'Visible" '+fvisible+'/>'+
'<label for="'+key+'Visible">Show</label> &nbsp;'+
'</div>'+
'</div>');
});
}
// Saves New Map data to Local storage
function addNewMap(){
customFnData = {};
$(".edit-row").each(function (val) {
key = $(this).find(".func-title").text();
btnType = $(this).children().find("input[type='radio']:checked").val() == "press" ? 1 : 0;
fnvisible = $(this).children().find("input[type='checkbox']").prop("checked") ? 1 : 0;
arr = [0, btnType, $(this).children().find(".fn-input").val(), fnvisible];
customFnData[key] = arr;
});
mapName = $.trim($("#map-name").val());
if (!ifExists(mapName)) {
if (mapName) {
// Send data to store in Local storage
setMapData({ mname: mapName, fnData: customFnData });
$("#fnModal").hide();
setFunctionMaps();
alert("Map Saved Sucessfully");
} else {
alert("Name is missing!!");
}
} else {
alert("Map with the Name already exists!! Please change the Map name..");
}
}
// Saves Edited Map data to local storage
function editMap(){
customFnData = {};
$(".edit-row").each(function(val){
key = $(this).find(".func-title").text();
btnType = $(this).children().find("input[type='radio']:checked").val() == "press" ? 1 : 0;
fnvisible = $(this).children().find("input[type='checkbox']").prop('checked') ? 1 : 0;
arr = [ 0, btnType, $(this).children().find(".fn-input").val(), fnvisible ];
customFnData[key] = arr;
});
mapName = $.trim($("#map-name").val());
if (mapName) {
setMapData({ mname: mapName, fnData: customFnData });
$("#fnModal").hide();
setFunctionMaps();
loadMapData(mapName);
alert("Map Saved Sucessfully");
} else {
alert("Name is missing!!");
}
}
//*** Saves given data into Local storage**/
// Create new data object if one does not exist
// Verify if the given Map data already exists and replace it
// Or Create new map data and inserts it into local storage object
// Finally Saves the Data into Local storage */
function setMapData(mapdata) {
if (typeof Storage === "undefined") {
return;
}
const smapdata = getMapData()
if (ifExists(mapdata.mname)) {
smapdata.find(function (item, i) {
if (item.mname == mapdata.mname) {
item.fnData = mapdata.fnData;
}
});
} else {
smapdata.push(mapdata);
}
window.localStorage.setItem('mapData', JSON.stringify(smapdata));
loadmaps();
setFunctionMaps();
loadMapData(mapdata.mname);
$("#select-map").val(mapdata.mname).trigger("change");
}
//Returns the Map data of given Map name
function getStoredMapData(name){
const data = getMapData();
if(data !=null){
return data.find(function(item, i){
if(item.mname == name){
return item.fnData;
}
});
}else{
return null;
}
}
//Download the Map data of given Map name
function downloadMapData(mapName){
data = JSON.stringify(getStoredMapData(mapName));
const a = document.createElement("a");
const file = new Blob([data], {type: 'application/json'});
a.href = URL.createObjectURL(file);
a.download = mapName+".json";
a.click();
}
//Delete the Map data of given Map name
function deleteFuncData(name){
var r = confirm("Are you sure on deletion?");
if (r == true) {
if (typeof(Storage) !== "undefined") {
curmapdata = [];
const data = getMapData()
if(!data){
alert("No Data stored");
}else{
data.find(function(item, i){
if(item.mname != name){
curmapdata.push(item);
}
});
window.localStorage.setItem('mapData', JSON.stringify(curmapdata));
}
}
}
}
/**
* Returns the Map data of ExWebThrottle
* @return {[]}
*/
function getMapData(){
if (typeof Storage === "undefined") {
return [];
}
const localMapData = JSON.parse(window.localStorage.getItem("mapData"));
return localMapData || []
}
// Returns boolen if the given Map exists in local storage
function ifExists(name) {
const data = getMapData()
const existingItem = data.find((item) => item.mname === name);
return !!existingItem;
}
//Download all Maps of EXthrottle
function exportMapData() {
const data = getMapData()
const a = document.createElement("a");
const file = new Blob([data], {type: 'application/json'});
a.href = URL.createObjectURL(file);
a.download = "ListofMaps.json";
a.click();
}
function importMapdata(data){
if(data){
window.localStorage.setItem('mapData', JSON.stringify(data));
loadmaps();
$("#select-map").val('default').trigger("change");
setFunctionMaps();
}
}
//Import the Locomotives List data
function importLocoData(data) {
if (data) {
window.localStorage.setItem("cabList", JSON.stringify(data));
loadLocomotives();
locoList = getLocoList();
}
}
/*************************************************/
/********** Locomotives Data functions ***********/
/*************************************************/
function saveLocomotive(data){
locodata = $(data).arrayToJSON();
if (typeof Storage !== "undefined") {
curCabList = [];
cabData = JSON.parse(window.localStorage.getItem("cabList"));
if (!cabData) {
curCabList.push(locodata);
window.localStorage.setItem("cabList", JSON.stringify(curCabList));
return true;
} else {
cabData.push(locodata);
window.localStorage.setItem("cabList", JSON.stringify(cabData));
return true;
}
}
return false;
}
function saveEditedLocomotive(data, id){
locodata = $(data).arrayToJSON();
if (typeof Storage !== "undefined") {
cabData = JSON.parse(window.localStorage.getItem("cabList"));
cabData.find(function (item, i) {
if (i == id) {
cabData[i] = locodata;
}
});
window.localStorage.setItem("cabList", JSON.stringify(cabData));
}
}
function ifLocoExists(name) {
data = JSON.parse(window.localStorage.getItem("cabList"));
found = false;
if (data != null) {
data.find(function (item, i) {
if (item.name == name) {
found = true;
}
});
return found;
}
return found;
}
// Returns the AppData of ExWebThrottle
function getLocoList(){
if (typeof Storage !== "undefined") {
return JSON.parse(window.localStorage.getItem("cabList"));
}else{
return [];
}
}
//Download the Locomotives List data
function downloadCabData(){
data = JSON.stringify(getLocoList());
const a = document.createElement("a");
const file = new Blob([data], {type: 'application/json'});
a.href = URL.createObjectURL(file);
a.download = "CabList.json";
a.click();
}
// Returns the LocoData of ExWebThrottle
function getStoredLocoData(name) {
console.log(name);
data = JSON.parse(window.localStorage.getItem("cabList"));
if (data != null) {
return data.find(function (item, i) {
if (item.name == name) {
return item;
}
});
} else {
return null;
}
}
/********************************************/
/************** Preferences ***************/
/********************************************/
// Get a given user preference
function getPreference(pref){
if (window.localStorage.getItem("userpref") != null) {
curpref = JSON.parse(window.localStorage.getItem("userpref"));
return curpref[pref];
} else {
return null;
}
}
// Set a given user preference
function setPreference(pref, val){
if (window.localStorage.getItem("userpref") != null){
curpref = JSON.parse(window.localStorage.getItem("userpref"));
}else{
curpref = {};
}
curpref[pref] = val;
setUserPreferences(curpref);
}
// Store user preferences in local storage
function setUserPreferences(pref){
if (typeof(Storage) !== "undefined") {
window.localStorage.setItem("userpref", JSON.stringify(pref));
}
}
function getUserPreferences() {
if (typeof Storage !== "undefined") {
return JSON.parse(window.localStorage.getItem("userpref"));
}else{
return [];
}
}
function importPrefData(data) {
if (data) {
window.localStorage.setItem("userpref", JSON.stringify(data));
}
}
function exportAppData(){
const jsonObj = [
{maps: getMapData()},
{locos: getLocoList()},
{preferences: getUserPreferences()}
]
const data = JSON.stringify(jsonObj);
const a = document.createElement("a");
const file = new Blob([data], { type: "application/json" });
a.href = URL.createObjectURL(file);
a.download = "AppData.json";
a.click();
}
function importAppdata(data){
importMapdata(data[0]["maps"]);
importLocoData(data[1]["locos"]);
importPrefData(data[2]["preferences"]);
setThrottleScreenUI();
}
(function ($) {
$.fn.arrayToJSON = function () {
var o = {};
$.each($(this), function () {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || "");
} else {
o[this.name] = this.value || "";
}
});
return o;
};
}
)(jQuery);

View File

@@ -0,0 +1,20 @@
{
"short_name": "WebThrottle-EX",
"name": "DCC++ EX Web Throttle",
"icons": [
{
"src": "images/full-logo.png",
"type": "image/png",
"sizes": "176x176"
}
],
"start_url": "/WebThrottle-EX/",
"background_color": "#fff",
"display": "standalone",
"scope": "/WebThrottle-EX/",
"related_applications": [{
"platform": "webapp",
"url": "https://DCC-EX.github.io/WebThrottle-EX/manifest.json"
}],
"theme_color": "#fff"
}

View File

@@ -0,0 +1,64 @@
var cacheName = 'WebThrottle-EX';
var filesToCache = [
'index.html',
'images/favicon.ico',
'images/carbon_fibre.png',
'images/cover.jpg',
'images/darkmkBigBackground.png',
'images/darkmkBigFront.png',
'images/full-logo.png',
'images/lightBigBackground.png',
'images/lightBigBackground@2x.png',
'images/lightBigFront.png',
'images/lightBigFront@2x.png',
'images/pattern.png',
'images/WebThrottle.png',
'css/icons.css',
'css/jquery-ui.css',
'css/jquery.rotaryswitch.css',
'css/layout.css',
'css/pwa.css',
'css/roundslider.min.css',
'css/settings.css',
'css/throttle.css',
'css/fonts/icomoon.eot',
'css/fonts/icomoon.svg',
'css/fonts/icomoon.ttf',
'css/fonts/icomoon.woff',
'css/themes/dark.css',
'css/themes/metallic.css',
'js/addloco.js',
'js/commandController.js',
'js/emulator.js',
'js/exwebthrottle.js',
'js/fnMaster.js',
'js/jquery-3.2.1.min.js',
'js/jquery-ui.min.js',
'js/jquery.rotaryswitch.js',
'js/pwa.js',
'js/roundslider.min.js',
'js/storageController.js'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request, {ignoreSearch:true}).then(response => {
return response || fetch(event.request);
})
);
});

View File

@@ -52,7 +52,7 @@ class Firewall(BasePermission):
# accept IP configured is settings or localhost
if ip in network or ip in IPv4Network("127.0.0.0/8"):
return request.method in SAFE_METHODS
return True
class Test(APIView):