├── .compilerc
├── .dockerignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── Dockerfile
├── LICENSE
├── appveyor.yml
├── build.sh
├── documentation.md
├── mix-manifest.json
├── package.json
├── readme.md
├── resources
├── assets
│ ├── js
│ │ ├── app.js
│ │ └── components
│ │ │ ├── footer.vue
│ │ │ └── track.vue
│ └── stylus
│ │ └── app.styl
└── images
│ ├── icon
│ ├── icon.gvdesign
│ ├── icon.icns
│ ├── icon.ico
│ └── icon.png
│ ├── logo.svg
│ ├── preview
│ ├── os.gvdesign
│ ├── os.png
│ ├── preview.gvdesign
│ └── preview.png
│ └── triangle.svg
├── src
├── build
│ ├── app.css
│ ├── app.js
│ └── mix-manifest.json
├── css
│ └── font-awesome.css
├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── icons
│ ├── mac
│ │ └── icon.icns
│ ├── png
│ │ ├── 128x128.png
│ │ ├── 16x16.png
│ │ ├── 24x24.png
│ │ ├── 256x256.png
│ │ ├── 32x32.png
│ │ ├── 48x48.png
│ │ ├── 512x512.png
│ │ ├── 64x64.png
│ │ └── 96x96.png
│ └── win
│ │ └── icon.ico
├── images
│ ├── default
│ │ ├── tray-running
│ │ │ ├── iconTemplate.png
│ │ │ └── iconTemplate@2x.png
│ │ └── tray
│ │ │ ├── iconTemplate.png
│ │ │ └── iconTemplate@2x.png
│ └── mac
│ │ ├── tray-running
│ │ ├── iconTemplate.png
│ │ └── iconTemplate@2x.png
│ │ └── tray
│ │ ├── iconTemplate.png
│ │ └── iconTemplate@2x.png
├── index.html
├── index.js
├── log.html
├── render.js
├── settings.html
├── test.html
└── writable-file-config.js
├── webpack.mix.js
└── yarn.lock
/.compilerc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "development": {
4 | "application/javascript": {
5 | "presets": [
6 | [
7 | "env",
8 | {
9 | "targets": {
10 | "electron": "2.0.8"
11 | }
12 | }
13 | ],
14 | "react"
15 | ],
16 | "plugins": [
17 | "transform-async-to-generator"
18 | ],
19 | "sourceMaps": "none"
20 | }
21 | },
22 | "production": {
23 | "application/javascript": {
24 | "presets": [
25 | [
26 | "env",
27 | {
28 | "targets": {
29 | "electron": "2.0.8"
30 | }
31 | }
32 | ],
33 | "react"
34 | ],
35 | "plugins": [
36 | "transform-async-to-generator"
37 | ],
38 | "sourceMaps": "none"
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /out
3 | /resources
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint-config-airbnb",
3 | "rules": {
4 | "import/extensions": 0,
5 | "import/no-extraneous-dependencies": 0,
6 | "import/no-unresolved": [2, { "ignore": ["electron"] }],
7 | "linebreak-style": 0
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | out
3 | .idea
4 | /Icon*
5 | yarn-error.log
6 | .tresorit
7 | **/.DS_Store
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | dist: trusty
3 | #if: tag IS present
4 |
5 | branches:
6 | only:
7 | - production
8 |
9 | language: c
10 |
11 | matrix:
12 | include:
13 | - os: osx
14 | - os: linux
15 | env: CC=clang CXX=clang++ npm_config_clang=1
16 | compiler: clang
17 |
18 | node_js:
19 | - 8.4
20 |
21 | cache:
22 | directories:
23 | - node_modules
24 |
25 | addons:
26 | apt:
27 | sources:
28 | - ubuntu-toolchain-r-test
29 | packages:
30 | - docker
31 |
32 | install:
33 | - nvm install 8.4
34 | - npm install -g yarn
35 | - yarn install
36 |
37 | before_script:
38 | - export DISPLAY=:99.0
39 | - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
40 | - yarn run production
41 |
42 | script:
43 | - if [[ $TRAVIS_OS_NAME == 'osx' ]]; then yarn run publish; else yarn run publish-docker; fi
44 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 |
3 | RUN rm /bin/sh && ln -s /bin/bash /bin/sh && \
4 | sed -i 's/^mesg n$/tty -s \&\& mesg n/g' /root/.profile
5 |
6 | WORKDIR /code
7 |
8 | RUN apt update && apt install -y \
9 | g++-4.8 \
10 | icnsutils \
11 | graphicsmagick \
12 | libgnome-keyring-dev \
13 | xz-utils \
14 | xorriso \
15 | xvfb \
16 | devscripts \
17 | fakeroot \
18 | debhelper \
19 | automake \
20 | autotools-dev \
21 | pkg-config \
22 | git \
23 | ca-certificates \
24 | rpm \
25 | zip \
26 | libpng-dev \
27 | snapcraft
28 |
29 | RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash && \
30 | export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \
31 | nvm install 8.4 && \
32 | npm install -g yarn
33 |
34 | ENTRYPOINT ["/bin/bash", "--login", "-i", "-c"]
35 | CMD ["bash"]
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | os:
2 | - Visual Studio 2017
3 | cache:
4 | - node_modules -> yarn.lock
5 | branches:
6 | only:
7 | - production
8 | environment:
9 | NODE_ENV: 'production'
10 | GITHUB_TOKEN:
11 | secure: G/hTb/AdUY4ynt5XGj10fROfQZC2xgogiQAiqXT63cEMGAGcZ8+6rOdQJzf4/PHf
12 | matrix:
13 | - nodejs_version: 10.4
14 | install:
15 | - ps: Install-Product node $env:nodejs_version
16 | - set CI=true
17 | - npm install -g npm@latest
18 | - npm install -g yarn@1.7.0
19 | - set PATH=%APPDATA%\npm;%PATH%
20 | - set PATH=C:\Program Files (x86)\Windows Kits\10\bin\x64;%PATH%
21 | - yarn install --production=false
22 | # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
23 | matrix:
24 | fast_finish: true
25 | build: off
26 | version: '{build}'
27 | shallow_clone: true
28 | clone_depth: 1
29 | test_script:
30 | - yarn run production-windows
31 | - yarn run publish
32 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
4 | export DISPLAY=:99.0
5 | export DEBUG=electron-installer-snap:snapcraft
6 | sleep 5
7 |
8 | yarn install --force
9 | yarn run production
10 | yarn run publish
11 |
--------------------------------------------------------------------------------
/documentation.md:
--------------------------------------------------------------------------------
1 | # gtt taskbar documentation
2 |
3 | Coming soon!
4 |
5 | ## contents
6 |
7 | * [support further development 🍺](#support-further-development)
8 | * [license](#license)
9 |
10 | ## support further development
11 |
12 | gtt is an open source project, developed and maintained completely in my free time.
13 |
14 | If you enjoy using gtt you can support the project by [contributing](#) to the code base,
15 | sharing it to your colleagues and co-workers or monetarily by [donating via PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TXL5G4BDN8JP6).
16 | Every type of support is helpful and thank you very much if you consider supporting the project
17 | or already have done so. 💜
18 |
19 | ## license
20 |
21 | GPL v2
22 |
--------------------------------------------------------------------------------
/mix-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "/P:/src/build/app.js": "/P:/src/build/app.js",
3 | "/src/build/app.css": "/src/build/app.css"
4 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gtt-taskbar",
3 | "productName": "gtt-taskbar",
4 | "genericName": "GitLab menubar/taskbar application",
5 | "description": "A crossplatform menubar/taskbar application for GitLabs time tracking feature.",
6 | "homepage": "https://github.com/kriskbx/gitlab-time-tracker-taskbar",
7 | "keywords": [
8 | "GitLab",
9 | "Timetracking"
10 | ],
11 | "author": "Kris Siepert",
12 | "private": true,
13 | "license": "GPL-2.0",
14 | "version": "0.3.10",
15 | "main": "src/index.js",
16 | "scripts": {
17 | "start": "electron-forge start",
18 | "package": "electron-forge package",
19 | "make": "electron-forge make",
20 | "publish": "electron-forge publish",
21 | "publish-docker": "docker run --rm -it -e \"GITHUB_TOKEN=$GITHUB_TOKEN\" -v $(pwd):/code kriskbx/gtt-taskbar-builder ./build.sh",
22 | "lint": "eslint src",
23 | "dev": "NODE_ENV=development webpack --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
24 | "watch": "NODE_ENV=development webpack --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
25 | "hot": "NODE_ENV=development webpack-dev-server --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
26 | "production": "NODE_ENV=production webpack --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
27 | "production-windows": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
28 | },
29 | "config": {
30 | "forge": {
31 | "publish_targets": {
32 | "win32": [
33 | "github"
34 | ],
35 | "darwin": [
36 | "github"
37 | ],
38 | "linux": [
39 | "github"
40 | ]
41 | },
42 | "make_targets": {
43 | "win32": [
44 | "squirrel"
45 | ],
46 | "darwin": [
47 | "zip"
48 | ],
49 | "linux": [
50 | "deb",
51 | "rpm"
52 | ]
53 | },
54 | "electronPackagerConfig": {
55 | "ignore": [
56 | ".idea",
57 | "resources",
58 | "out",
59 | ".tresorit",
60 | "appveyor.yml",
61 | "build.sh",
62 | "Dockerfile",
63 | "documentation.md",
64 | "Icon",
65 | "LICENSE",
66 | "mix-manifest.json",
67 | "readme.md",
68 | "webpack.mix.js",
69 | "yarn.lock",
70 | "yarn-error.log"
71 | ],
72 | "packageManager": "yarn",
73 | "icon": "./resources/images/icon/icon.png"
74 | },
75 | "electronWinstallerConfig": {
76 | "setupIcon": "./resources/images/icon/icon.ico"
77 | },
78 | "electronInstallerDebian": {
79 | "depends": [
80 | "libappindicator1"
81 | ],
82 | "categories": [
83 | "Utility"
84 | ],
85 | "icon": "./resources/images/icon/icon.png"
86 | },
87 | "electronInstallerRedhat": {
88 | "requires": [
89 | "lsb",
90 | "libappindicator"
91 | ],
92 | "compressionLevel": 9,
93 | "categories": [
94 | "Utility"
95 | ]
96 | },
97 | "electronInstallerSnap": {
98 | "grade": "stable",
99 | "confinement": "strict",
100 | "icon": "./resources/images/icon/icon.png"
101 | },
102 | "snapStore": {
103 | "release": "stable"
104 | },
105 | "github_repository": {
106 | "owner": "kriskbx",
107 | "name": "gitlab-time-tracker-taskbar",
108 | "draft": false,
109 | "prerelease": true
110 | },
111 | "windowsStoreConfig": {
112 | "packageName": "gtt-taskbar",
113 | "packageDisplayName": "gitlab time tracker taskbar",
114 | "packageVersion": "<%= version %>.0",
115 | "publisher": "CN=F0C20719-E0F4-4F1A-99EB-2C25503A0622",
116 | "devCert": "",
117 | "deploy": false
118 | }
119 | }
120 | },
121 | "dependencies": {
122 | "babel-preset-es2016": "^6.24.1",
123 | "chokidar": "^2.0.0",
124 | "electron": "2.0.8",
125 | "electron-compile": "^6.4.2",
126 | "electron-log": "^3.0.0-beta4",
127 | "electron-squirrel-startup": "^1.0.0",
128 | "gitlab-time-tracker": "^1.7.37",
129 | "moment": "^2.20.1",
130 | "raven": "^2.6.2",
131 | "write-yaml": "^1.0.0"
132 | },
133 | "devDependencies": {
134 | "babel-plugin-transform-async-to-generator": "^6.24.1",
135 | "babel-preset-env": "^1.6.1",
136 | "babel-preset-react": "^6.24.1",
137 | "cross-env": "^5.2.0",
138 | "electron-forge": "^5.0.0",
139 | "electron-prebuilt-compile": "1.7.11",
140 | "eslint": "^3",
141 | "eslint-config-airbnb": "^15",
142 | "eslint-plugin-import": "^2",
143 | "eslint-plugin-jsx-a11y": "^5",
144 | "eslint-plugin-react": "^7",
145 | "laravel-mix": "^2.0.0",
146 | "milligram-stylus": "^1.3.0",
147 | "roboto": "^0.8.2",
148 | "stylus": "^0.54.5",
149 | "stylus-loader": "^3.0.1",
150 | "url-parse": "^1.4.3",
151 | "vue": "^2.5.13",
152 | "vue-datetime-2": "^0.6.1",
153 | "vue-js-toggle-button": "^1.2.2",
154 | "vue-resource": "^1.3.5",
155 | "vue-select": "^2.4.0"
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | ## introduction
12 |
13 | gtt taskbar is an open source crossplatform (Linux, Mac OS, Windows) menubar/taskbar application for GitLab's time tracking feature based on the **[gtt command line interface](https://github.com/kriskbx/gitlab-time-tracker)**.
14 |
15 | 
16 |
17 | ## documentation
18 |
19 | How to install and use gtt taskbar? You can find the documentation [here](https://github.com/kriskbx/gitlab-time-tracker-taskbar/blob/master/documentation.md).
20 |
21 | ## support further development
22 |
23 | Please support the development of this free software by [donating or sharing](https://github.com/kriskbx/gitlab-time-tracker-taskbar/blob/master/documentation.md#support-further-development)!
24 |
25 | ## license
26 |
27 | gtt taskbar is open-source software licensed under the [GPL V2 license](https://github.com/kriskbx/gitlab-time-tracker-taskbar/blob/master/LICENSE).
28 |
--------------------------------------------------------------------------------
/resources/assets/js/app.js:
--------------------------------------------------------------------------------
1 | const Config = require('gitlab-time-tracker/src/include/config');
2 | const Frame = require('gitlab-time-tracker/src/models/baseFrame');
3 |
4 | window.Vue = require('vue');
5 | window.Vue.use(require('vue-resource'));
6 | const moment = require('moment');
7 | const URL = require('url-parse');
8 | const _ = require('underscore');
9 | import ToggleButton from 'vue-js-toggle-button';
10 | import {Datetime} from 'vue-datetime-2';
11 |
12 | window.Vue.use(ToggleButton);
13 |
14 | const shell = window.state.shell;
15 | const ipc = window.state.ipc;
16 |
17 | const app = new Vue({
18 | el: '#app',
19 | data: window.state.data,
20 | components: {
21 | 'content-track': require('./components/track.vue'),
22 | 'panel-footer': require('./components/footer.vue'),
23 | 'datetime': Datetime
24 | },
25 |
26 | watch: {
27 | 'resourceType': function () {
28 | this.saveState();
29 | },
30 | 'resource': {
31 | deep: true,
32 | handler: function () {
33 | this.saveState();
34 | }
35 | },
36 | 'mergeRequests': {
37 | deep: true,
38 | handler: function () {
39 | this.saveState();
40 | }
41 | },
42 | 'issues': {
43 | deep: true,
44 | handler: function () {
45 | this.saveState();
46 | }
47 | },
48 | 'projects': {
49 | deep: true,
50 | handler: function () {
51 | this.saveState();
52 | }
53 | },
54 | 'project': {
55 | deep: true,
56 | handler: function () {
57 | this.loadResource();
58 | this.saveState();
59 | }
60 | },
61 | 'config': {
62 | deep: true,
63 | handler: function () {
64 | if (this.loadingConfig) return;
65 | this.writeConfig(this.config)
66 | }
67 | }
68 | },
69 |
70 | computed: {
71 | gitlab() {
72 | let url = new URL(this.config.get('url'), true);
73 | return url.protocol + (url.slashes ? '//' : '') + url.host;
74 | },
75 | days() {
76 | let tmp = this.config;
77 | return this.log && this.log.frames ? Object.keys(this.log.frames).sort().reverse() : [];
78 | },
79 | frames() {
80 | let frames = {};
81 | this.days.forEach(day => frames[day] = this.log.frames[day].sort((a, b) => a.start >= b.start ? 1 : -1));
82 | return frames;
83 | }
84 | },
85 |
86 | mounted() {
87 | this.setConfig(ipc.sync('gtt-config', 'get'));
88 | this.version = ipc.sync('gtt-version', 'get');
89 | this.platform = ipc.sync('gtt-platform', 'get');
90 | this.editing = false;
91 |
92 | // set ipc listeners
93 | ipc.on('gtt-last-sync', (event, lastSync) => this.lastSync = lastSync);
94 | ipc.on('gtt-config', (event, config) => this.setConfig(config));
95 | ipc.on('gtt-log', (event, data) => {
96 | this.loadingLog = false;
97 | let i;
98 | for (i in data.frames) {
99 | if (!data.frames.hasOwnProperty(i)) continue;
100 | data.frames[i] = _.map(data.frames[i], frame => Frame.copy(frame));
101 | }
102 | this.log = data;
103 | });
104 | ipc.on('gtt-status', (event, status) => {
105 | this.running = status ? status.map(frame => Frame.copy(frame)) : false;
106 | this.loadingStatus = false;
107 | this.starting = false;
108 | this.stopping = false;
109 | this.cancelling = false;
110 | });
111 | ipc.on('gtt-projects', (event, projects) => {
112 | this.projects = projects;
113 | this.loadingProjects = false;
114 | });
115 | ipc.on('gtt-issues', (event, data) => {
116 | if (data.project) {
117 | this.issues[data.project] = data.issues;
118 | this.$emit('loaded-issues');
119 | }
120 | this.loadingResource = false;
121 | });
122 | ipc.on('gtt-merge-requests', (event, data) => {
123 | if (data.project) {
124 | this.mergeRequests[data.project] = data.mergeRequests;
125 | this.$emit('loaded-mergeRequests');
126 | }
127 | this.loadingResource = false;
128 | });
129 |
130 | ipc.on('gtt-stop', () => this.sync());
131 |
132 | this.ready = true;
133 |
134 | if (this.$refs.log) this.loadLog();
135 | if (!this.$refs.main) return;
136 |
137 | // set intervals
138 | setInterval(this.loadProjects, 10 * 60 * 1000);
139 | setInterval(this.loadIssues, 10 * 60 * 1000);
140 | setInterval(this.loadMergeRequests, 10 * 60 * 1000);
141 |
142 | // kick it off once
143 | this.loadStatus();
144 | this.loadProjects();
145 | this.loadIssues();
146 | this.loadMergeRequests();
147 | this.sync();
148 | },
149 |
150 | methods: {
151 | synced(modified) {
152 | if (!this.lastSync) return false;
153 | return moment(modified).diff(this.lastSync) < 0;
154 | },
155 | human(input) {
156 | if (!this.config) return;
157 | return this.config.toHumanReadable(input);
158 | },
159 | setConfig(config) {
160 | this.loadingConfig = true;
161 | this.config = Object.assign(new Config, config);
162 | setTimeout(() => {
163 | this.loadingConfig = false;
164 | }, 100);
165 | },
166 | moment(input) {
167 | return moment(input);
168 | },
169 | open(url) {
170 | shell.open(url);
171 | },
172 | start() {
173 | this.starting = true;
174 | ipc.send('gtt-start', {
175 | project: this.project ? this.project.label : false,
176 | type: this.resourceType ? 'issue' : 'merge_request',
177 | id: this.resource ? this.resource.id : false
178 | });
179 | },
180 | stop() {
181 | this.stopping = true;
182 | ipc.send('gtt-stop');
183 | },
184 | cancel() {
185 | this.cancelling = true;
186 | ipc.send('gtt-cancel');
187 | },
188 | sync() {
189 | ipc.send('gtt-sync');
190 | },
191 | contextMenu() {
192 | ipc.send('context-menu');
193 | },
194 | listWindow() {
195 | ipc.send('list-window');
196 | },
197 | settings() {
198 | ipc.send('settings-window');
199 | },
200 | loadLog() {
201 | this.loadingLog = true;
202 | ipc.send('gtt-log');
203 | ipc.send('gtt-sync');
204 | },
205 | loadResource() {
206 | if (this.resourceType) {
207 | this.loadIssues();
208 | } else {
209 | this.loadMergeRequests();
210 | }
211 | },
212 | loadMergeRequests() {
213 | this.loadingResource = true;
214 | ipc.send('gtt-merge-requests', this.project ? this.project.label : false);
215 | },
216 | loadIssues() {
217 | this.loadingResource = true;
218 | ipc.send('gtt-issues', this.project ? this.project.label : false);
219 | },
220 | loadStatus() {
221 | this.loadingStatus = true;
222 | ipc.send('gtt-status');
223 | },
224 | loadProjects() {
225 | this.loadingProjects = true;
226 | ipc.send('gtt-projects');
227 | },
228 | saveState() {
229 | let state = Object.assign({}, window.state.data);
230 | delete state.config;
231 | delete state.editing;
232 | delete state.entry;
233 | delete state.currentEntry;
234 | ipc.send('cache-set', {key: 'state', data: state});
235 | },
236 | writeConfig(config) {
237 | ipc.send('gtt-config-write', config);
238 | },
239 | timeFormat() {
240 | return this.config ? this.config.get('dateFormat').replace(this.dayFormat(), "") : "HH:mm";
241 | },
242 | dayFormat() {
243 | return this.config ? this.config.get('dateFormat').replace(/([k|h|H|m|s|S][:]?)/gm, "") : 'YYYY-MM-DD';
244 | },
245 | getTitleById(id, project) {
246 | if (!this.issues[project]) return false;
247 |
248 | let filtered = this.issues[project].filter(issue => issue.iid == id);
249 | if (!filtered[0]) return false;
250 |
251 | return filtered[0].title;
252 | },
253 | edit(frame) {
254 | this.editing = true;
255 | this.currentEntry = frame;
256 | this.entry = Frame.copy(frame);
257 | },
258 | save() {
259 | for (let key in this.entry) {
260 | if (!this.entry.hasOwnProperty(key)) continue;
261 | this.currentEntry[key] = this.entry[key];
262 | }
263 | this.currentEntry.modified = moment().toISOString();
264 | ipc.send('gtt-edit', {frame: this.currentEntry});
265 | this.editing = false;
266 | },
267 | dump(data) {
268 | console.log(data);
269 | }
270 | }
271 | });
272 |
--------------------------------------------------------------------------------
/resources/assets/js/components/footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/resources/assets/js/components/track.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
22 |
24 |
25 |
26 |
32 |
33 |
38 |
39 |
40 |
41 |
47 | Start
49 |
50 |
51 |
57 | Stop
58 |
59 |
65 | Cancel
66 |
67 |
68 |
69 |
70 |
71 |
270 |
--------------------------------------------------------------------------------
/resources/assets/stylus/app.styl:
--------------------------------------------------------------------------------
1 | $color-red = #F8561E
2 | $color-white = #ffffff
3 | $color-gray = #cecece
4 | $color-gray = #909090
5 | $color-light-gray = #e6e6e6
6 | $color-middle-gray = #cbcbcb
7 | $color-mac-gray = #E7E7E7
8 | $color-bluish = #bfcbd9
9 | $color-blue = #1c79c3
10 | $color-black = #000
11 | $border-radius = .7rem
12 |
13 | color-primary = $color-red
14 | @import "./../../../node_modules/milligram-stylus/src/milligram.styl"
15 |
16 | [v-cloak]
17 | display: none
18 |
19 | html
20 | height: 100%
21 | padding: 0
22 |
23 | body
24 | overflow: visible
25 | padding: 0 10px
26 | border-bottom-left-radius: $border-radius
27 | border-bottom-right-radius: $border-radius
28 | background: transparent
29 |
30 | body, html
31 | border: 0
32 | margin: 0
33 |
34 | #app
35 | height: 25.4rem
36 | overflow: visible
37 | position: relative
38 |
39 | div.triangle
40 | display: none
41 | &.bottom
42 | transform: rotate(180deg)
43 | filter: drop-shadow(0 -3px 2px rgba(0, 0, 0, 0.5))
44 | &.mac, &.linux
45 | & > .triangle.top
46 | display: block
47 | &.win
48 | position: absolute
49 | bottom: 0
50 | left: 10px
51 | right: 10px
52 | .v-select .dropdown-menu
53 | top: auto
54 | bottom: 100%
55 | & > .triangle.bottom
56 | display: block
57 |
58 | .wrapper
59 | display: flex
60 | flex-direction: column
61 | height: calc(100% - 3rem)
62 | overflow-y: visible
63 | overflow-x: hidden
64 | box-shadow: 0 0 .2rem rgba(0, 0, 0, 0.5)
65 | border-radius: $border-radius
66 |
67 | .settings
68 | line-height: 1.2
69 |
70 | .triangle
71 | text-align: center
72 | height: 1.5rem
73 | filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.5))
74 | &.bottom
75 | transform: rotate(180deg)
76 | display: none
77 | svg
78 | height: 1.5rem
79 | width: auto
80 | display: inline-block
81 | .triangle
82 | fill: $color-light-gray
83 |
84 | .vertical-center
85 | display: flex
86 | align-items: center
87 |
88 | .panel
89 | background: $color-light-gray
90 | position: relative
91 | padding: .8rem 1rem
92 |
93 | .content
94 | background: $color-white
95 | flex: 1
96 |
97 | .track
98 | padding: 1rem
99 | position: absolute
100 | width: 100%
101 |
102 | .step
103 | margin-bottom: 1rem
104 |
105 | .toggle
106 | margin-right: 1.4rem
107 | margin-top: .65rem
108 | float: right
109 |
110 | .refresh
111 | color: $color-middle-gray
112 | font-size: 1.4rem
113 | margin-top: .7rem
114 | float: right
115 | display: inline-block
116 |
117 | &.disabled
118 | opacity: 0.7
119 | pointer-events: none
120 | cursor: none
121 |
122 | & > .fa
123 | cursor: pointer
124 |
125 | .v-select
126 | &.project
127 | margin-right: 2.5rem
128 | &.resource
129 | margin-right: 10rem
130 | font-size: 1.4rem
131 | &:not(.open) > .dropdown-toggle
132 | overflow: hidden
133 | .dropdown-toggle
134 | position: relative
135 | & > input[type=search]
136 | width: 100% !important
137 | font-size: 1.2rem
138 | position: absolute
139 | top: 0
140 | left: 0
141 | right: 0
142 | bottom: 0
143 | &:before
144 | content: ''
145 | display: inline-block
146 | width: 1px
147 | height: 28px
148 |
149 | .selected-tag
150 | background-color: transparent
151 | border: none
152 | margin-right: 0
153 | max-width: calc(100% - 3rem)
154 | white-space: nowrap
155 | overflow: hidden
156 | text-overflow: ellipsis
157 | font-size: 1.2rem
158 | padding: .2rem 0 0 .5rem
159 | left: 0
160 |
161 | li
162 | font-size: 1.2rem
163 | max-width: 100%
164 | overflow: hidden
165 | line-height: 1.4 !important
166 | margin: 0 !important
167 |
168 | &:last-child
169 | margin-bottom: 0;
170 |
171 | &.highlight > a
172 | background-color: $color-bluish !important
173 | color: $color-black !important
174 | & > a
175 | white-space: normal
176 | color: $color-dark-gray
177 | margin: 0
178 | padding: .8rem .5rem
179 |
180 | .footer
181 | border-bottom-left-radius: $border-radius
182 | border-bottom-right-radius: $border-radius
183 | border-top: .1rem solid $color-gray
184 | display: flex
185 | align-items: center
186 |
187 | .spinner
188 | height: 1.301rem
189 | font-size: 1.3rem
190 | margin-right: 1rem
191 |
192 | .label
193 | font-size: 1.2rem
194 | line-height: 1.2
195 | .time
196 | margin-legt: .5rem
197 | font-style: italic
198 | font-size: 85%
199 | span
200 | display: inline-block
201 | margin-right: .1rem
202 |
203 | .track-buttons
204 | display: flex
205 | justify-content: space-between
206 | margin-bottom: 0 !important
207 | margin-top: 1.5rem
208 | & > .button
209 | flex: 0 0 auto
210 | width: 31%
211 | height: 3rem
212 | line-height: 1
213 | padding: 0
214 | margin: 0
215 | border: 0
216 | &:hover
217 | background: $color-gray !important
218 | &.start
219 | background: rgb(117, 199, 145)
220 | &.stop
221 | background: rgb(191, 203, 217)
222 | &:first-child
223 | margin-left: 0
224 | &:last-child
225 | margin-right: 0
226 | &.working, &.disabled
227 | pointer-events: none
228 | opacity: .3
229 | background: #afafaf
230 | border: 1px solid transparent
231 |
232 | &.working
233 | & > i:before
234 | content: "\f021"
235 |
236 | .window
237 | background: $color-mac-gray
238 | color: $color-black
239 | padding: 2rem 0
240 | font-size: 1.4rem
241 | &.white
242 | background: $color-white
243 |
244 | .fieldset
245 | display: flex
246 | flex-direction: row
247 | flex-wrap: wrap
248 | .input
249 | margin-bottom: 1.2rem
250 | flex: 0 0 70%
251 | max-width: 70%
252 | input[type='email'], input[type='number'], input[type='password'], input[type='search'], input[type='tel'], input[type='text'], input[type='url'], textarea, select
253 | background: #fff
254 | padding: .5rem
255 | height: 2.5rem
256 | border-radius: 0
257 | box-shadow: inset 0 0.1rem 0.1rem rgba(0, 0, 0, 0.2)
258 | border: .1rem solid rgba(0, 0, 0, 0.15)
259 | .label
260 | padding-right: 1rem
261 | text-align: right
262 | flex: 0 0 30%
263 | max-width: 30%
264 | label
265 | font-size: 1.4rem
266 |
267 | .licenses
268 | font-size: 1.4rem
269 | list-style: none
270 | line-height: 1.2
271 | & > li
272 | margin-bottom: 2rem
273 |
274 | .header
275 | border-top-left-radius: $border-radius
276 | border-top-right-radius: $border-radius
277 | border-bottom: .1rem solid $color-gray
278 |
279 | .header-button
280 | color: $color-gray !important
281 | cursor: pointer
282 |
283 | .log
284 |
285 | .sync
286 | font-size: 1.6rem
287 | .red
288 | color: $color-red
289 | .green
290 | color: green
291 |
292 | & > .day
293 | margin-bottom: 2rem
294 |
295 | & > .headline
296 | color: $color-gray
297 | padding: 1rem
298 | font-size: 2rem
299 | & > small
300 | font-weight: normal
301 | display: inline-block
302 | margin-top: .9rem
303 | margin-left: 1.2rem
304 | font-size: 1.3rem
305 | & > .list
306 | & > .entry
307 | letter-spacing: .05rem
308 | display: flex
309 | flex-direction: row
310 | width: 100%
311 | border-top: 1px solid rgba(0, 0, 0, 0.1)
312 | padding: 1.4rem 1rem
313 | & > .cell
314 | flex: 1
315 |
316 | &.timeframe
317 | max-width: 13rem
318 | color: $color-gray
319 | font-size: 1.2rem
320 | display: inline-block
321 | margin-top: .3rem
322 | &.sync
323 | max-width: 10rem
324 | &.time
325 | font-weight: 400
326 | max-width: 10rem
327 | &.name
328 | flex: auto
329 | font-weight: 400
330 |
331 | .actions
332 | font-size: 1.2rem
333 | display: none
334 | float: right
335 | margin: -.9rem 0
336 | padding-right: 1rem
337 | padding-top: .2rem
338 | & > a
339 | display: block
340 |
341 | &:hover
342 | background: lighten($color-light-gray, 40%)
343 |
344 | .actions
345 | display: inline-block
346 |
347 | button.clear
348 | display: none
349 |
350 | .modal-bg
351 | position: fixed
352 | left: 0
353 | right: 0
354 | top: 0
355 | bottom: 0
356 | z-index: 10
357 |
358 | .modal
359 | width: 45rem
360 | height: auto
361 | top: -.5rem
362 | position: fixed
363 | z-index: 20
364 | left: 50%
365 | margin-left: -22.5rem
366 | background: $color-white
367 | border-radius: .5rem
368 | box-shadow: 0 .1rem .3rem rgba(0, 0, 0, 0.3)
369 | padding: 2.5rem 3rem 2rem 3rem
370 |
371 | .track-buttons
372 | margin-top: 3rem
373 |
374 | .form
375 | .times
376 | .vdatetime-slot__not-available, .vdatetime-slot__available
377 | display: none
378 | position: relative
379 | z-index: 20
380 | font-weight: 400
381 | margin-bottom: 1rem
382 | display: flex
383 | text-align: center
384 | align-items: center
385 | & > *
386 | flex: 1
387 | .datetime
388 | display: inline-block
389 | min-width: 12rem
390 | input
391 | margin: 0
392 | .content .track
393 | padding: 0
394 | position: static
395 |
--------------------------------------------------------------------------------
/resources/images/icon/icon.gvdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/resources/images/icon/icon.gvdesign
--------------------------------------------------------------------------------
/resources/images/icon/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/resources/images/icon/icon.icns
--------------------------------------------------------------------------------
/resources/images/icon/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/resources/images/icon/icon.ico
--------------------------------------------------------------------------------
/resources/images/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/resources/images/icon/icon.png
--------------------------------------------------------------------------------
/resources/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
9 |
10 | logo-square
11 | Created with Sketch.
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | logo-square
53 | Created with Sketch.
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/resources/images/preview/os.gvdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/resources/images/preview/os.gvdesign
--------------------------------------------------------------------------------
/resources/images/preview/os.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/resources/images/preview/os.png
--------------------------------------------------------------------------------
/resources/images/preview/preview.gvdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/resources/images/preview/preview.gvdesign
--------------------------------------------------------------------------------
/resources/images/preview/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/resources/images/preview/preview.png
--------------------------------------------------------------------------------
/resources/images/triangle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/build/app.css:
--------------------------------------------------------------------------------
1 | *,
2 | *:after,
3 | *:before {
4 | -webkit-box-sizing: inherit;
5 | box-sizing: inherit;
6 | }
7 | html {
8 | -webkit-box-sizing: border-box;
9 | box-sizing: border-box;
10 | font-size: 62.5%;
11 | }
12 | body {
13 | color: #606c76;
14 | font-family: 'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
15 | font-size: 1.6em;
16 | font-weight: 300;
17 | letter-spacing: 0.01em;
18 | line-height: 1.6;
19 | }
20 | blockquote {
21 | border-left: 0.3rem solid #d1d1d1;
22 | margin-left: 0;
23 | margin-right: 0;
24 | padding: 1rem 1.5rem;
25 | }
26 | blockquote *:last-child {
27 | margin-bottom: 0;
28 | }
29 | .button,
30 | button,
31 | input[type='button'],
32 | input[type='reset'],
33 | input[type='submit'] {
34 | background-color: #f8561e;
35 | border: 0.1rem solid #f8561e;
36 | border-radius: 0.4rem;
37 | color: #fff;
38 | cursor: pointer;
39 | display: inline-block;
40 | font-size: 1.1rem;
41 | font-weight: 700;
42 | height: 3.8rem;
43 | letter-spacing: 0.1rem;
44 | line-height: 3.8rem;
45 | padding: 0 3rem;
46 | text-align: center;
47 | text-decoration: none;
48 | text-transform: uppercase;
49 | white-space: nowrap;
50 | }
51 | .button:focus,
52 | button:focus,
53 | input[type='button']:focus,
54 | input[type='reset']:focus,
55 | input[type='submit']:focus,
56 | .button:hover,
57 | button:hover,
58 | input[type='button']:hover,
59 | input[type='reset']:hover,
60 | input[type='submit']:hover {
61 | background-color: #606c76;
62 | border-color: #606c76;
63 | color: #fff;
64 | outline: 0;
65 | }
66 | .button[disabled],
67 | button[disabled],
68 | input[type='button'][disabled],
69 | input[type='reset'][disabled],
70 | input[type='submit'][disabled] {
71 | cursor: default;
72 | opacity: 0.5;
73 | }
74 | .button[disabled]:focus,
75 | button[disabled]:focus,
76 | input[type='button'][disabled]:focus,
77 | input[type='reset'][disabled]:focus,
78 | input[type='submit'][disabled]:focus,
79 | .button[disabled]:hover,
80 | button[disabled]:hover,
81 | input[type='button'][disabled]:hover,
82 | input[type='reset'][disabled]:hover,
83 | input[type='submit'][disabled]:hover {
84 | background-color: #f8561e;
85 | border-color: #f8561e;
86 | }
87 | .button.button-outline,
88 | button.button-outline,
89 | input[type='button'].button-outline,
90 | input[type='reset'].button-outline,
91 | input[type='submit'].button-outline {
92 | background-color: transparent;
93 | color: #f8561e;
94 | }
95 | .button.button-outline:focus,
96 | button.button-outline:focus,
97 | input[type='button'].button-outline:focus,
98 | input[type='reset'].button-outline:focus,
99 | input[type='submit'].button-outline:focus,
100 | .button.button-outline:hover,
101 | button.button-outline:hover,
102 | input[type='button'].button-outline:hover,
103 | input[type='reset'].button-outline:hover,
104 | input[type='submit'].button-outline:hover {
105 | background-color: transparent;
106 | border-color: #606c76;
107 | color: #606c76;
108 | }
109 | .button.button-outline[disabled]:focus,
110 | button.button-outline[disabled]:focus,
111 | input[type='button'].button-outline[disabled]:focus,
112 | input[type='reset'].button-outline[disabled]:focus,
113 | input[type='submit'].button-outline[disabled]:focus,
114 | .button.button-outline[disabled]:hover,
115 | button.button-outline[disabled]:hover,
116 | input[type='button'].button-outline[disabled]:hover,
117 | input[type='reset'].button-outline[disabled]:hover,
118 | input[type='submit'].button-outline[disabled]:hover {
119 | border-color: inherit;
120 | color: #f8561e;
121 | }
122 | .button.button-clear,
123 | button.button-clear,
124 | input[type='button'].button-clear,
125 | input[type='reset'].button-clear,
126 | input[type='submit'].button-clear {
127 | background-color: transparent;
128 | border-color: transparent;
129 | color: #f8561e;
130 | }
131 | .button.button-clear:focus,
132 | button.button-clear:focus,
133 | input[type='button'].button-clear:focus,
134 | input[type='reset'].button-clear:focus,
135 | input[type='submit'].button-clear:focus,
136 | .button.button-clear:hover,
137 | button.button-clear:hover,
138 | input[type='button'].button-clear:hover,
139 | input[type='reset'].button-clear:hover,
140 | input[type='submit'].button-clear:hover {
141 | background-color: transparent;
142 | border-color: transparent;
143 | color: #606c76;
144 | }
145 | .button.button-clear[disabled]:focus,
146 | button.button-clear[disabled]:focus,
147 | input[type='button'].button-clear[disabled]:focus,
148 | input[type='reset'].button-clear[disabled]:focus,
149 | input[type='submit'].button-clear[disabled]:focus,
150 | .button.button-clear[disabled]:hover,
151 | button.button-clear[disabled]:hover,
152 | input[type='button'].button-clear[disabled]:hover,
153 | input[type='reset'].button-clear[disabled]:hover,
154 | input[type='submit'].button-clear[disabled]:hover {
155 | color: #f8561e;
156 | }
157 | code {
158 | background: #f4f5f6;
159 | border-radius: 0.4rem;
160 | font-size: 86%;
161 | margin: 0 0.2rem;
162 | padding: 0.2rem 0.5rem;
163 | white-space: nowrap;
164 | }
165 | pre {
166 | background: #f4f5f6;
167 | border-left: 0.3rem solid #f8561e;
168 | overflow-y: hidden;
169 | }
170 | pre > code {
171 | border-radius: 0;
172 | display: block;
173 | padding: 1rem 1.5rem;
174 | white-space: pre;
175 | }
176 | hr {
177 | border: 0;
178 | border-top: 0.1rem solid #f4f5f6;
179 | margin: 3rem 0;
180 | }
181 | input[type='email'],
182 | input[type='number'],
183 | input[type='password'],
184 | input[type='search'],
185 | input[type='tel'],
186 | input[type='text'],
187 | input[type='url'],
188 | textarea,
189 | select {
190 | -webkit-appearance: none;
191 | -moz-appearance: none;
192 | appearance: none;
193 | background-color: transparent;
194 | border: 0.1rem solid #d1d1d1;
195 | border-radius: 0.4rem;
196 | -webkit-box-shadow: none;
197 | box-shadow: none;
198 | -webkit-box-sizing: inherit;
199 | box-sizing: inherit;
200 | height: 3.8rem;
201 | padding: 0.6rem 1rem;
202 | width: 100%;
203 | }
204 | input[type='email']:focus,
205 | input[type='number']:focus,
206 | input[type='password']:focus,
207 | input[type='search']:focus,
208 | input[type='tel']:focus,
209 | input[type='text']:focus,
210 | input[type='url']:focus,
211 | textarea:focus,
212 | select:focus {
213 | border-color: #f8561e;
214 | outline: 0;
215 | }
216 | select {
217 | background: url("data:image/svg+xml;utf8, ") center right no-repeat;
218 | padding-right: 3rem;
219 | }
220 | select:focus {
221 | background-image: url("data:image/svg+xml;utf8, ");
222 | }
223 | textarea {
224 | min-height: 6.5rem;
225 | }
226 | label,
227 | legend {
228 | display: block;
229 | font-size: 1.6rem;
230 | font-weight: 700;
231 | margin-bottom: 0.5rem;
232 | }
233 | fieldset {
234 | border-width: 0;
235 | padding: 0;
236 | }
237 | input[type='checkbox'],
238 | input[type='radio'] {
239 | display: inline;
240 | }
241 | .label-inline {
242 | display: inline-block;
243 | font-weight: normal;
244 | margin-left: 0.5rem;
245 | }
246 | .container {
247 | margin: 0 auto;
248 | max-width: 112rem;
249 | padding: 0 2rem;
250 | position: relative;
251 | width: 100%;
252 | }
253 | .row {
254 | display: -webkit-box;
255 | display: -ms-flexbox;
256 | display: flex;
257 | -webkit-box-orient: vertical;
258 | -webkit-box-direction: normal;
259 | -ms-flex-direction: column;
260 | flex-direction: column;
261 | padding: 0;
262 | width: 100%;
263 | }
264 | .row.row-no-padding {
265 | padding: 0;
266 | }
267 | .row.row-no-padding> .column {
268 | padding: 0;
269 | }
270 | .row.row-wrap {
271 | -ms-flex-wrap: wrap;
272 | flex-wrap: wrap;
273 | }
274 | .row.row-top {
275 | -webkit-box-align: start;
276 | -ms-flex-align: start;
277 | align-items: flex-start;
278 | }
279 | .row.row-bottom {
280 | -webkit-box-align: end;
281 | -ms-flex-align: end;
282 | align-items: flex-end;
283 | }
284 | .row.row-center {
285 | -webkit-box-align: center;
286 | -ms-flex-align: center;
287 | align-items: center;
288 | }
289 | .row.row-stretch {
290 | -webkit-box-align: stretch;
291 | -ms-flex-align: stretch;
292 | align-items: stretch;
293 | }
294 | .row.row-baseline {
295 | -webkit-box-align: baseline;
296 | -ms-flex-align: baseline;
297 | align-items: baseline;
298 | }
299 | .row .column {
300 | display: block;
301 | -webkit-box-flex: 1;
302 | -ms-flex: 1 1 auto;
303 | flex: 1 1 auto;
304 | margin-left: 0;
305 | max-width: 100%;
306 | width: 100%;
307 | }
308 | .row .column.column-offset-10 {
309 | margin-left: 10%;
310 | }
311 | .row .column.column-offset-20 {
312 | margin-left: 20%;
313 | }
314 | .row .column.column-offset-25 {
315 | margin-left: 25%;
316 | }
317 | .row .column.column-offset-33,
318 | .row .column.column-offset-34 {
319 | margin-left: 33.3333%;
320 | }
321 | .row .column.column-offset-50 {
322 | margin-left: 50%;
323 | }
324 | .row .column.column-offset-66,
325 | .row .column.column-offset-67 {
326 | margin-left: 66.6666%;
327 | }
328 | .row .column.column-offset-75 {
329 | margin-left: 75%;
330 | }
331 | .row .column.column-offset-80 {
332 | margin-left: 80%;
333 | }
334 | .row .column.column-offset-90 {
335 | margin-left: 90%;
336 | }
337 | .row .column.column-10 {
338 | -webkit-box-flex: 0;
339 | -ms-flex: 0 0 10%;
340 | flex: 0 0 10%;
341 | max-width: 10%;
342 | }
343 | .row .column.column-20 {
344 | -webkit-box-flex: 0;
345 | -ms-flex: 0 0 20%;
346 | flex: 0 0 20%;
347 | max-width: 20%;
348 | }
349 | .row .column.column-25 {
350 | -webkit-box-flex: 0;
351 | -ms-flex: 0 0 25%;
352 | flex: 0 0 25%;
353 | max-width: 25%;
354 | }
355 | .row .column.column-33,
356 | .row .column.column-34 {
357 | -webkit-box-flex: 0;
358 | -ms-flex: 0 0 33.3333%;
359 | flex: 0 0 33.3333%;
360 | max-width: 33.3333%;
361 | }
362 | .row .column.column-40 {
363 | -webkit-box-flex: 0;
364 | -ms-flex: 0 0 40%;
365 | flex: 0 0 40%;
366 | max-width: 40%;
367 | }
368 | .row .column.column-50 {
369 | -webkit-box-flex: 0;
370 | -ms-flex: 0 0 50%;
371 | flex: 0 0 50%;
372 | max-width: 50%;
373 | }
374 | .row .column.column-60 {
375 | -webkit-box-flex: 0;
376 | -ms-flex: 0 0 60%;
377 | flex: 0 0 60%;
378 | max-width: 60%;
379 | }
380 | .row .column.column-66,
381 | .row .column.column-67 {
382 | -webkit-box-flex: 0;
383 | -ms-flex: 0 0 66.6666%;
384 | flex: 0 0 66.6666%;
385 | max-width: 66.6666%;
386 | }
387 | .row .column.column-75 {
388 | -webkit-box-flex: 0;
389 | -ms-flex: 0 0 75%;
390 | flex: 0 0 75%;
391 | max-width: 75%;
392 | }
393 | .row .column.column-80 {
394 | -webkit-box-flex: 0;
395 | -ms-flex: 0 0 80%;
396 | flex: 0 0 80%;
397 | max-width: 80%;
398 | }
399 | .row .column.column-90 {
400 | -webkit-box-flex: 0;
401 | -ms-flex: 0 0 90%;
402 | flex: 0 0 90%;
403 | max-width: 90%;
404 | }
405 | .row .column .column-top {
406 | -ms-flex-item-align: start;
407 | align-self: flex-start;
408 | }
409 | .row .column .column-bottom {
410 | -ms-flex-item-align: end;
411 | align-self: flex-end;
412 | }
413 | .row .column .column-center {
414 | -ms-flex-item-align: center;
415 | align-self: center;
416 | }
417 | @media (min-width: 40rem) {
418 | .row {
419 | -webkit-box-orient: horizontal;
420 | -webkit-box-direction: normal;
421 | -ms-flex-direction: row;
422 | flex-direction: row;
423 | margin-left: -1rem;
424 | width: calc(100% + 2rem);
425 | }
426 | .row .column {
427 | margin-bottom: inherit;
428 | padding: 0 1rem;
429 | }
430 | }
431 | a {
432 | color: #f8561e;
433 | text-decoration: none;
434 | }
435 | a:focus,
436 | a:hover {
437 | color: #606c76;
438 | }
439 | dl,
440 | ol,
441 | ul {
442 | list-style: none;
443 | margin-top: 0;
444 | padding-left: 0;
445 | }
446 | dl dl,
447 | ol dl,
448 | ul dl,
449 | dl ol,
450 | ol ol,
451 | ul ol,
452 | dl ul,
453 | ol ul,
454 | ul ul {
455 | font-size: 90%;
456 | margin: 1.5rem 0 1.5rem 3rem;
457 | }
458 | ol {
459 | list-style: decimal inside;
460 | }
461 | ul {
462 | list-style: circle inside;
463 | }
464 | .button,
465 | button,
466 | dd,
467 | dt,
468 | li {
469 | margin-bottom: 1rem;
470 | }
471 | fieldset,
472 | input,
473 | select,
474 | textarea {
475 | margin-bottom: 1.5rem;
476 | }
477 | blockquote,
478 | dl,
479 | figure,
480 | form,
481 | ol,
482 | p,
483 | pre,
484 | table,
485 | ul {
486 | margin-bottom: 2.5rem;
487 | }
488 | table {
489 | border-spacing: 0;
490 | width: 100%;
491 | }
492 | td,
493 | th {
494 | border-bottom: 0.1rem solid #e1e1e1;
495 | padding: 1.2rem 1.5rem;
496 | text-align: left;
497 | }
498 | td:first-child,
499 | th:first-child {
500 | padding-left: 0;
501 | }
502 | td:last-child,
503 | th:last-child {
504 | padding-right: 0;
505 | }
506 | b,
507 | strong {
508 | font-weight: bold;
509 | }
510 | p {
511 | margin-top: 0;
512 | }
513 | h1,
514 | h2,
515 | h3,
516 | h4,
517 | h5,
518 | h6 {
519 | font-weight: 300;
520 | letter-spacing: -0.1rem;
521 | margin-bottom: 2rem;
522 | margin-top: 0;
523 | }
524 | h1 {
525 | font-size: 4.6rem;
526 | line-height: 1.2;
527 | }
528 | h2 {
529 | font-size: 3.6rem;
530 | line-height: 1.25;
531 | }
532 | h3 {
533 | font-size: 2.8rem;
534 | line-height: 1.3;
535 | }
536 | h4 {
537 | font-size: 2.2rem;
538 | letter-spacing: -0.08rem;
539 | line-height: 1.35;
540 | }
541 | h5 {
542 | font-size: 1.8rem;
543 | letter-spacing: -0.05rem;
544 | line-height: 1.5;
545 | }
546 | h6 {
547 | font-size: 1.6rem;
548 | letter-spacing: 0;
549 | line-height: 1.4;
550 | }
551 | img {
552 | max-width: 100%;
553 | }
554 | .clearfix:after {
555 | clear: both;
556 | content: ' ';
557 | display: table;
558 | }
559 | .float-left {
560 | float: left;
561 | }
562 | .float-right {
563 | float: right;
564 | }
565 | [v-cloak] {
566 | display: none;
567 | }
568 | html {
569 | height: 100%;
570 | padding: 0;
571 | }
572 | body {
573 | overflow: visible;
574 | padding: 0 10px;
575 | border-bottom-left-radius: 0.7rem;
576 | border-bottom-right-radius: 0.7rem;
577 | background: transparent;
578 | }
579 | body,
580 | html {
581 | border: 0;
582 | margin: 0;
583 | }
584 | #app {
585 | height: 25.4rem;
586 | overflow: visible;
587 | position: relative;
588 | }
589 | #app div.triangle {
590 | display: none;
591 | }
592 | #app div.triangle.bottom {
593 | -webkit-transform: rotate(180deg);
594 | transform: rotate(180deg);
595 | -webkit-filter: drop-shadow(0 -3px 2px rgba(0,0,0,0.5));
596 | filter: drop-shadow(0 -3px 2px rgba(0,0,0,0.5));
597 | }
598 | #app.mac > .triangle.top,
599 | #app.linux > .triangle.top {
600 | display: block;
601 | }
602 | #app.win {
603 | position: absolute;
604 | bottom: 0;
605 | left: 10px;
606 | right: 10px;
607 | }
608 | #app.win .v-select .dropdown-menu {
609 | top: auto;
610 | bottom: 100%;
611 | }
612 | #app.win > .triangle.bottom {
613 | display: block;
614 | }
615 | .wrapper {
616 | display: -webkit-box;
617 | display: -ms-flexbox;
618 | display: flex;
619 | -webkit-box-orient: vertical;
620 | -webkit-box-direction: normal;
621 | -ms-flex-direction: column;
622 | flex-direction: column;
623 | height: calc(100% - 3rem);
624 | overflow-y: visible;
625 | overflow-x: hidden;
626 | -webkit-box-shadow: 0 0 0.2rem rgba(0,0,0,0.5);
627 | box-shadow: 0 0 0.2rem rgba(0,0,0,0.5);
628 | border-radius: 0.7rem;
629 | }
630 | .settings {
631 | line-height: 1.2;
632 | }
633 | .triangle {
634 | text-align: center;
635 | height: 1.5rem;
636 | -webkit-filter: drop-shadow(0 0 2px rgba(0,0,0,0.5));
637 | filter: drop-shadow(0 0 2px rgba(0,0,0,0.5));
638 | }
639 | .triangle.bottom {
640 | -webkit-transform: rotate(180deg);
641 | transform: rotate(180deg);
642 | display: none;
643 | }
644 | .triangle svg {
645 | height: 1.5rem;
646 | width: auto;
647 | display: inline-block;
648 | }
649 | .triangle .triangle {
650 | fill: #e6e6e6;
651 | }
652 | .vertical-center {
653 | display: -webkit-box;
654 | display: -ms-flexbox;
655 | display: flex;
656 | -webkit-box-align: center;
657 | -ms-flex-align: center;
658 | align-items: center;
659 | }
660 | .panel {
661 | background: #e6e6e6;
662 | position: relative;
663 | padding: 0.8rem 1rem;
664 | }
665 | .content {
666 | background: #fff;
667 | -webkit-box-flex: 1;
668 | -ms-flex: 1;
669 | flex: 1;
670 | }
671 | .content .track {
672 | padding: 1rem;
673 | position: absolute;
674 | width: 100%;
675 | }
676 | .content .track .step {
677 | margin-bottom: 1rem;
678 | }
679 | .content .track .toggle {
680 | margin-right: 1.4rem;
681 | margin-top: 0.65rem;
682 | float: right;
683 | }
684 | .content .track .refresh {
685 | color: #cbcbcb;
686 | font-size: 1.4rem;
687 | margin-top: 0.7rem;
688 | float: right;
689 | display: inline-block;
690 | }
691 | .content .track .refresh.disabled {
692 | opacity: 0.7;
693 | pointer-events: none;
694 | cursor: none;
695 | }
696 | .content .track .refresh > .fa {
697 | cursor: pointer;
698 | }
699 | .content .track .v-select {
700 | font-size: 1.4rem;
701 | }
702 | .content .track .v-select.project {
703 | margin-right: 2.5rem;
704 | }
705 | .content .track .v-select.resource {
706 | margin-right: 10rem;
707 | }
708 | .content .track .v-select:not(.open) > .dropdown-toggle {
709 | overflow: hidden;
710 | }
711 | .content .track .v-select .dropdown-toggle {
712 | position: relative;
713 | }
714 | .content .track .v-select .dropdown-toggle > input[type=search] {
715 | width: 100% !important;
716 | font-size: 1.2rem;
717 | position: absolute;
718 | top: 0;
719 | left: 0;
720 | right: 0;
721 | bottom: 0;
722 | }
723 | .content .track .v-select .dropdown-toggle:before {
724 | content: '';
725 | display: inline-block;
726 | width: 1px;
727 | height: 28px;
728 | }
729 | .content .track .v-select .selected-tag {
730 | background-color: transparent;
731 | border: none;
732 | margin-right: 0;
733 | max-width: calc(100% - 3rem);
734 | white-space: nowrap;
735 | overflow: hidden;
736 | text-overflow: ellipsis;
737 | font-size: 1.2rem;
738 | padding: 0.2rem 0 0 0.5rem;
739 | left: 0;
740 | }
741 | .content .track .v-select li {
742 | font-size: 1.2rem;
743 | max-width: 100%;
744 | overflow: hidden;
745 | line-height: 1.4 !important;
746 | margin: 0 !important;
747 | }
748 | .content .track .v-select li:last-child {
749 | margin-bottom: 0;
750 | }
751 | .content .track .v-select li.highlight > a {
752 | background-color: #bfcbd9 !important;
753 | color: #000 !important;
754 | }
755 | .content .track .v-select li > a {
756 | white-space: normal;
757 | color: $color-dark-gray;
758 | margin: 0;
759 | padding: 0.8rem 0.5rem;
760 | }
761 | .footer {
762 | border-bottom-left-radius: 0.7rem;
763 | border-bottom-right-radius: 0.7rem;
764 | border-top: 0.1rem solid #909090;
765 | display: -webkit-box;
766 | display: -ms-flexbox;
767 | display: flex;
768 | -webkit-box-align: center;
769 | -ms-flex-align: center;
770 | align-items: center;
771 | }
772 | .footer .spinner {
773 | height: 1.301rem;
774 | font-size: 1.3rem;
775 | margin-right: 1rem;
776 | }
777 | .footer .label {
778 | font-size: 1.2rem;
779 | line-height: 1.2;
780 | }
781 | .footer .label .time {
782 | margin-legt: 0.5rem;
783 | font-style: italic;
784 | font-size: 85%;
785 | }
786 | .footer .label span {
787 | display: inline-block;
788 | margin-right: 0.1rem;
789 | }
790 | .track-buttons {
791 | display: -webkit-box;
792 | display: -ms-flexbox;
793 | display: flex;
794 | -webkit-box-pack: justify;
795 | -ms-flex-pack: justify;
796 | justify-content: space-between;
797 | margin-bottom: 0 !important;
798 | margin-top: 1.5rem;
799 | }
800 | .track-buttons > .button {
801 | -webkit-box-flex: 0;
802 | -ms-flex: 0 0 auto;
803 | flex: 0 0 auto;
804 | width: 31%;
805 | height: 3rem;
806 | line-height: 1;
807 | padding: 0;
808 | margin: 0;
809 | border: 0;
810 | }
811 | .track-buttons > .button:hover {
812 | background: #909090 !important;
813 | }
814 | .track-buttons > .button.start {
815 | background: #75c791;
816 | }
817 | .track-buttons > .button.stop {
818 | background: #bfcbd9;
819 | }
820 | .track-buttons > .button:first-child {
821 | margin-left: 0;
822 | }
823 | .track-buttons > .button:last-child {
824 | margin-right: 0;
825 | }
826 | .track-buttons > .button.working,
827 | .track-buttons > .button.disabled {
828 | pointer-events: none;
829 | opacity: 0.3;
830 | background: #afafaf;
831 | border: 1px solid transparent;
832 | }
833 | .track-buttons > .button.working > i:before {
834 | content: "\F021";
835 | }
836 | .window {
837 | background: #e7e7e7;
838 | color: #000;
839 | padding: 2rem 0;
840 | font-size: 1.4rem;
841 | }
842 | .window.white {
843 | background: #fff;
844 | }
845 | .window .fieldset {
846 | display: -webkit-box;
847 | display: -ms-flexbox;
848 | display: flex;
849 | -webkit-box-orient: horizontal;
850 | -webkit-box-direction: normal;
851 | -ms-flex-direction: row;
852 | flex-direction: row;
853 | -ms-flex-wrap: wrap;
854 | flex-wrap: wrap;
855 | }
856 | .window .fieldset .input {
857 | margin-bottom: 1.2rem;
858 | -webkit-box-flex: 0;
859 | -ms-flex: 0 0 70%;
860 | flex: 0 0 70%;
861 | max-width: 70%;
862 | }
863 | .window .fieldset .input input[type='email'],
864 | .window .fieldset .input input[type='number'],
865 | .window .fieldset .input input[type='password'],
866 | .window .fieldset .input input[type='search'],
867 | .window .fieldset .input input[type='tel'],
868 | .window .fieldset .input input[type='text'],
869 | .window .fieldset .input input[type='url'],
870 | .window .fieldset .input textarea,
871 | .window .fieldset .input select {
872 | background: #fff;
873 | padding: 0.5rem;
874 | height: 2.5rem;
875 | border-radius: 0;
876 | -webkit-box-shadow: inset 0 0.1rem 0.1rem rgba(0,0,0,0.2);
877 | box-shadow: inset 0 0.1rem 0.1rem rgba(0,0,0,0.2);
878 | border: 0.1rem solid rgba(0,0,0,0.15);
879 | }
880 | .window .fieldset .label {
881 | padding-right: 1rem;
882 | text-align: right;
883 | -webkit-box-flex: 0;
884 | -ms-flex: 0 0 30%;
885 | flex: 0 0 30%;
886 | max-width: 30%;
887 | }
888 | .window .fieldset .label label {
889 | font-size: 1.4rem;
890 | }
891 | .licenses {
892 | font-size: 1.4rem;
893 | list-style: none;
894 | line-height: 1.2;
895 | }
896 | .licenses > li {
897 | margin-bottom: 2rem;
898 | }
899 | .header {
900 | border-top-left-radius: 0.7rem;
901 | border-top-right-radius: 0.7rem;
902 | border-bottom: 0.1rem solid #909090;
903 | }
904 | .header .header-button {
905 | color: #909090 !important;
906 | cursor: pointer;
907 | }
908 | .log .sync {
909 | font-size: 1.6rem;
910 | }
911 | .log .sync .red {
912 | color: #f8561e;
913 | }
914 | .log .sync .green {
915 | color: #008000;
916 | }
917 | .log > .day {
918 | margin-bottom: 2rem;
919 | }
920 | .log > .day > .headline {
921 | color: #909090;
922 | padding: 1rem;
923 | font-size: 2rem;
924 | }
925 | .log > .day > .headline > small {
926 | font-weight: normal;
927 | display: inline-block;
928 | margin-top: 0.9rem;
929 | margin-left: 1.2rem;
930 | font-size: 1.3rem;
931 | }
932 | .log > .day > .list > .entry {
933 | letter-spacing: 0.05rem;
934 | display: -webkit-box;
935 | display: -ms-flexbox;
936 | display: flex;
937 | -webkit-box-orient: horizontal;
938 | -webkit-box-direction: normal;
939 | -ms-flex-direction: row;
940 | flex-direction: row;
941 | width: 100%;
942 | border-top: 1px solid rgba(0,0,0,0.1);
943 | padding: 1.4rem 1rem;
944 | }
945 | .log > .day > .list > .entry > .cell {
946 | -webkit-box-flex: 1;
947 | -ms-flex: 1;
948 | flex: 1;
949 | }
950 | .log > .day > .list > .entry > .cell.timeframe {
951 | max-width: 13rem;
952 | color: #909090;
953 | font-size: 1.2rem;
954 | display: inline-block;
955 | margin-top: 0.3rem;
956 | }
957 | .log > .day > .list > .entry > .cell.sync {
958 | max-width: 10rem;
959 | }
960 | .log > .day > .list > .entry > .cell.time {
961 | font-weight: 400;
962 | max-width: 10rem;
963 | }
964 | .log > .day > .list > .entry > .cell.name {
965 | -webkit-box-flex: 1;
966 | -ms-flex: auto;
967 | flex: auto;
968 | font-weight: 400;
969 | }
970 | .log > .day > .list > .entry .actions {
971 | font-size: 1.2rem;
972 | display: none;
973 | float: right;
974 | margin: -0.9rem 0;
975 | padding-right: 1rem;
976 | padding-top: 0.2rem;
977 | }
978 | .log > .day > .list > .entry .actions > a {
979 | display: block;
980 | }
981 | .log > .day > .list > .entry:hover {
982 | background: #f0f0f0;
983 | }
984 | .log > .day > .list > .entry:hover .actions {
985 | display: inline-block;
986 | }
987 | button.clear {
988 | display: none;
989 | }
990 | .modal-bg {
991 | position: fixed;
992 | left: 0;
993 | right: 0;
994 | top: 0;
995 | bottom: 0;
996 | z-index: 10;
997 | }
998 | .modal {
999 | width: 45rem;
1000 | height: auto;
1001 | top: -0.5rem;
1002 | position: fixed;
1003 | z-index: 20;
1004 | left: 50%;
1005 | margin-left: -22.5rem;
1006 | background: #fff;
1007 | border-radius: 0.5rem;
1008 | -webkit-box-shadow: 0 0.1rem 0.3rem rgba(0,0,0,0.3);
1009 | box-shadow: 0 0.1rem 0.3rem rgba(0,0,0,0.3);
1010 | padding: 2.5rem 3rem 2rem 3rem;
1011 | }
1012 | .modal .track-buttons {
1013 | margin-top: 3rem;
1014 | }
1015 | .modal .form .times {
1016 | position: relative;
1017 | z-index: 20;
1018 | font-weight: 400;
1019 | margin-bottom: 1rem;
1020 | display: -webkit-box;
1021 | display: -ms-flexbox;
1022 | display: flex;
1023 | text-align: center;
1024 | -webkit-box-align: center;
1025 | -ms-flex-align: center;
1026 | align-items: center;
1027 | }
1028 | .modal .form .times .vdatetime-slot__not-available,
1029 | .modal .form .times .vdatetime-slot__available {
1030 | display: none;
1031 | }
1032 | .modal .form .times > * {
1033 | -webkit-box-flex: 1;
1034 | -ms-flex: 1;
1035 | flex: 1;
1036 | }
1037 | .modal .datetime {
1038 | display: inline-block;
1039 | min-width: 12rem;
1040 | }
1041 | .modal .datetime input {
1042 | margin: 0;
1043 | }
1044 | .modal .content .track {
1045 | padding: 0;
1046 | position: static;
1047 | }
1048 |
--------------------------------------------------------------------------------
/src/build/mix-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "/app.js": "/app.js",
3 | "/app.css": "/app.css"
4 | }
--------------------------------------------------------------------------------
/src/css/font-awesome.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */
5 | /* FONT PATH
6 | * -------------------------- */
7 | @font-face {
8 | font-family: 'FontAwesome';
9 | src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
10 | src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
11 | font-weight: normal;
12 | font-style: normal;
13 | }
14 | .fa {
15 | display: inline-block;
16 | font: normal normal normal 14px/1 FontAwesome;
17 | font-size: inherit;
18 | text-rendering: auto;
19 | -webkit-font-smoothing: antialiased;
20 | -moz-osx-font-smoothing: grayscale;
21 | }
22 | /* makes the font 33% larger relative to the icon container */
23 | .fa-lg {
24 | font-size: 1.33333333em;
25 | line-height: 0.75em;
26 | vertical-align: -15%;
27 | }
28 | .fa-2x {
29 | font-size: 2em;
30 | }
31 | .fa-3x {
32 | font-size: 3em;
33 | }
34 | .fa-4x {
35 | font-size: 4em;
36 | }
37 | .fa-5x {
38 | font-size: 5em;
39 | }
40 | .fa-fw {
41 | width: 1.28571429em;
42 | text-align: center;
43 | }
44 | .fa-ul {
45 | padding-left: 0;
46 | margin-left: 2.14285714em;
47 | list-style-type: none;
48 | }
49 | .fa-ul > li {
50 | position: relative;
51 | }
52 | .fa-li {
53 | position: absolute;
54 | left: -2.14285714em;
55 | width: 2.14285714em;
56 | top: 0.14285714em;
57 | text-align: center;
58 | }
59 | .fa-li.fa-lg {
60 | left: -1.85714286em;
61 | }
62 | .fa-border {
63 | padding: .2em .25em .15em;
64 | border: solid 0.08em #eeeeee;
65 | border-radius: .1em;
66 | }
67 | .fa-pull-left {
68 | float: left;
69 | }
70 | .fa-pull-right {
71 | float: right;
72 | }
73 | .fa.fa-pull-left {
74 | margin-right: .3em;
75 | }
76 | .fa.fa-pull-right {
77 | margin-left: .3em;
78 | }
79 | /* Deprecated as of 4.4.0 */
80 | .pull-right {
81 | float: right;
82 | }
83 | .pull-left {
84 | float: left;
85 | }
86 | .fa.pull-left {
87 | margin-right: .3em;
88 | }
89 | .fa.pull-right {
90 | margin-left: .3em;
91 | }
92 | .fa-spin {
93 | -webkit-animation: fa-spin 2s infinite linear;
94 | animation: fa-spin 2s infinite linear;
95 | }
96 | .fa-pulse {
97 | -webkit-animation: fa-spin 1s infinite steps(8);
98 | animation: fa-spin 1s infinite steps(8);
99 | }
100 | @-webkit-keyframes fa-spin {
101 | 0% {
102 | -webkit-transform: rotate(0deg);
103 | transform: rotate(0deg);
104 | }
105 | 100% {
106 | -webkit-transform: rotate(359deg);
107 | transform: rotate(359deg);
108 | }
109 | }
110 | @keyframes fa-spin {
111 | 0% {
112 | -webkit-transform: rotate(0deg);
113 | transform: rotate(0deg);
114 | }
115 | 100% {
116 | -webkit-transform: rotate(359deg);
117 | transform: rotate(359deg);
118 | }
119 | }
120 | .fa-rotate-90 {
121 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
122 | -webkit-transform: rotate(90deg);
123 | -ms-transform: rotate(90deg);
124 | transform: rotate(90deg);
125 | }
126 | .fa-rotate-180 {
127 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
128 | -webkit-transform: rotate(180deg);
129 | -ms-transform: rotate(180deg);
130 | transform: rotate(180deg);
131 | }
132 | .fa-rotate-270 {
133 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
134 | -webkit-transform: rotate(270deg);
135 | -ms-transform: rotate(270deg);
136 | transform: rotate(270deg);
137 | }
138 | .fa-flip-horizontal {
139 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
140 | -webkit-transform: scale(-1, 1);
141 | -ms-transform: scale(-1, 1);
142 | transform: scale(-1, 1);
143 | }
144 | .fa-flip-vertical {
145 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
146 | -webkit-transform: scale(1, -1);
147 | -ms-transform: scale(1, -1);
148 | transform: scale(1, -1);
149 | }
150 | :root .fa-rotate-90,
151 | :root .fa-rotate-180,
152 | :root .fa-rotate-270,
153 | :root .fa-flip-horizontal,
154 | :root .fa-flip-vertical {
155 | filter: none;
156 | }
157 | .fa-stack {
158 | position: relative;
159 | display: inline-block;
160 | width: 2em;
161 | height: 2em;
162 | line-height: 2em;
163 | vertical-align: middle;
164 | }
165 | .fa-stack-1x,
166 | .fa-stack-2x {
167 | position: absolute;
168 | left: 0;
169 | width: 100%;
170 | text-align: center;
171 | }
172 | .fa-stack-1x {
173 | line-height: inherit;
174 | }
175 | .fa-stack-2x {
176 | font-size: 2em;
177 | }
178 | .fa-inverse {
179 | color: #ffffff;
180 | }
181 | /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
182 | readers do not read off random characters that represent icons */
183 | .fa-glass:before {
184 | content: "\f000";
185 | }
186 | .fa-music:before {
187 | content: "\f001";
188 | }
189 | .fa-search:before {
190 | content: "\f002";
191 | }
192 | .fa-envelope-o:before {
193 | content: "\f003";
194 | }
195 | .fa-heart:before {
196 | content: "\f004";
197 | }
198 | .fa-star:before {
199 | content: "\f005";
200 | }
201 | .fa-star-o:before {
202 | content: "\f006";
203 | }
204 | .fa-user:before {
205 | content: "\f007";
206 | }
207 | .fa-film:before {
208 | content: "\f008";
209 | }
210 | .fa-th-large:before {
211 | content: "\f009";
212 | }
213 | .fa-th:before {
214 | content: "\f00a";
215 | }
216 | .fa-th-list:before {
217 | content: "\f00b";
218 | }
219 | .fa-check:before {
220 | content: "\f00c";
221 | }
222 | .fa-remove:before,
223 | .fa-close:before,
224 | .fa-times:before {
225 | content: "\f00d";
226 | }
227 | .fa-search-plus:before {
228 | content: "\f00e";
229 | }
230 | .fa-search-minus:before {
231 | content: "\f010";
232 | }
233 | .fa-power-off:before {
234 | content: "\f011";
235 | }
236 | .fa-signal:before {
237 | content: "\f012";
238 | }
239 | .fa-gear:before,
240 | .fa-cog:before {
241 | content: "\f013";
242 | }
243 | .fa-trash-o:before {
244 | content: "\f014";
245 | }
246 | .fa-home:before {
247 | content: "\f015";
248 | }
249 | .fa-file-o:before {
250 | content: "\f016";
251 | }
252 | .fa-clock-o:before {
253 | content: "\f017";
254 | }
255 | .fa-road:before {
256 | content: "\f018";
257 | }
258 | .fa-download:before {
259 | content: "\f019";
260 | }
261 | .fa-arrow-circle-o-down:before {
262 | content: "\f01a";
263 | }
264 | .fa-arrow-circle-o-up:before {
265 | content: "\f01b";
266 | }
267 | .fa-inbox:before {
268 | content: "\f01c";
269 | }
270 | .fa-play-circle-o:before {
271 | content: "\f01d";
272 | }
273 | .fa-rotate-right:before,
274 | .fa-repeat:before {
275 | content: "\f01e";
276 | }
277 | .fa-refresh:before {
278 | content: "\f021";
279 | }
280 | .fa-list-alt:before {
281 | content: "\f022";
282 | }
283 | .fa-lock:before {
284 | content: "\f023";
285 | }
286 | .fa-flag:before {
287 | content: "\f024";
288 | }
289 | .fa-headphones:before {
290 | content: "\f025";
291 | }
292 | .fa-volume-off:before {
293 | content: "\f026";
294 | }
295 | .fa-volume-down:before {
296 | content: "\f027";
297 | }
298 | .fa-volume-up:before {
299 | content: "\f028";
300 | }
301 | .fa-qrcode:before {
302 | content: "\f029";
303 | }
304 | .fa-barcode:before {
305 | content: "\f02a";
306 | }
307 | .fa-tag:before {
308 | content: "\f02b";
309 | }
310 | .fa-tags:before {
311 | content: "\f02c";
312 | }
313 | .fa-book:before {
314 | content: "\f02d";
315 | }
316 | .fa-bookmark:before {
317 | content: "\f02e";
318 | }
319 | .fa-print:before {
320 | content: "\f02f";
321 | }
322 | .fa-camera:before {
323 | content: "\f030";
324 | }
325 | .fa-font:before {
326 | content: "\f031";
327 | }
328 | .fa-bold:before {
329 | content: "\f032";
330 | }
331 | .fa-italic:before {
332 | content: "\f033";
333 | }
334 | .fa-text-height:before {
335 | content: "\f034";
336 | }
337 | .fa-text-width:before {
338 | content: "\f035";
339 | }
340 | .fa-align-left:before {
341 | content: "\f036";
342 | }
343 | .fa-align-center:before {
344 | content: "\f037";
345 | }
346 | .fa-align-right:before {
347 | content: "\f038";
348 | }
349 | .fa-align-justify:before {
350 | content: "\f039";
351 | }
352 | .fa-list:before {
353 | content: "\f03a";
354 | }
355 | .fa-dedent:before,
356 | .fa-outdent:before {
357 | content: "\f03b";
358 | }
359 | .fa-indent:before {
360 | content: "\f03c";
361 | }
362 | .fa-video-camera:before {
363 | content: "\f03d";
364 | }
365 | .fa-photo:before,
366 | .fa-image:before,
367 | .fa-picture-o:before {
368 | content: "\f03e";
369 | }
370 | .fa-pencil:before {
371 | content: "\f040";
372 | }
373 | .fa-map-marker:before {
374 | content: "\f041";
375 | }
376 | .fa-adjust:before {
377 | content: "\f042";
378 | }
379 | .fa-tint:before {
380 | content: "\f043";
381 | }
382 | .fa-edit:before,
383 | .fa-pencil-square-o:before {
384 | content: "\f044";
385 | }
386 | .fa-share-square-o:before {
387 | content: "\f045";
388 | }
389 | .fa-check-square-o:before {
390 | content: "\f046";
391 | }
392 | .fa-arrows:before {
393 | content: "\f047";
394 | }
395 | .fa-step-backward:before {
396 | content: "\f048";
397 | }
398 | .fa-fast-backward:before {
399 | content: "\f049";
400 | }
401 | .fa-backward:before {
402 | content: "\f04a";
403 | }
404 | .fa-play:before {
405 | content: "\f04b";
406 | }
407 | .fa-pause:before {
408 | content: "\f04c";
409 | }
410 | .fa-stop:before {
411 | content: "\f04d";
412 | }
413 | .fa-forward:before {
414 | content: "\f04e";
415 | }
416 | .fa-fast-forward:before {
417 | content: "\f050";
418 | }
419 | .fa-step-forward:before {
420 | content: "\f051";
421 | }
422 | .fa-eject:before {
423 | content: "\f052";
424 | }
425 | .fa-chevron-left:before {
426 | content: "\f053";
427 | }
428 | .fa-chevron-right:before {
429 | content: "\f054";
430 | }
431 | .fa-plus-circle:before {
432 | content: "\f055";
433 | }
434 | .fa-minus-circle:before {
435 | content: "\f056";
436 | }
437 | .fa-times-circle:before {
438 | content: "\f057";
439 | }
440 | .fa-check-circle:before {
441 | content: "\f058";
442 | }
443 | .fa-question-circle:before {
444 | content: "\f059";
445 | }
446 | .fa-info-circle:before {
447 | content: "\f05a";
448 | }
449 | .fa-crosshairs:before {
450 | content: "\f05b";
451 | }
452 | .fa-times-circle-o:before {
453 | content: "\f05c";
454 | }
455 | .fa-check-circle-o:before {
456 | content: "\f05d";
457 | }
458 | .fa-ban:before {
459 | content: "\f05e";
460 | }
461 | .fa-arrow-left:before {
462 | content: "\f060";
463 | }
464 | .fa-arrow-right:before {
465 | content: "\f061";
466 | }
467 | .fa-arrow-up:before {
468 | content: "\f062";
469 | }
470 | .fa-arrow-down:before {
471 | content: "\f063";
472 | }
473 | .fa-mail-forward:before,
474 | .fa-share:before {
475 | content: "\f064";
476 | }
477 | .fa-expand:before {
478 | content: "\f065";
479 | }
480 | .fa-compress:before {
481 | content: "\f066";
482 | }
483 | .fa-plus:before {
484 | content: "\f067";
485 | }
486 | .fa-minus:before {
487 | content: "\f068";
488 | }
489 | .fa-asterisk:before {
490 | content: "\f069";
491 | }
492 | .fa-exclamation-circle:before {
493 | content: "\f06a";
494 | }
495 | .fa-gift:before {
496 | content: "\f06b";
497 | }
498 | .fa-leaf:before {
499 | content: "\f06c";
500 | }
501 | .fa-fire:before {
502 | content: "\f06d";
503 | }
504 | .fa-eye:before {
505 | content: "\f06e";
506 | }
507 | .fa-eye-slash:before {
508 | content: "\f070";
509 | }
510 | .fa-warning:before,
511 | .fa-exclamation-triangle:before {
512 | content: "\f071";
513 | }
514 | .fa-plane:before {
515 | content: "\f072";
516 | }
517 | .fa-calendar:before {
518 | content: "\f073";
519 | }
520 | .fa-random:before {
521 | content: "\f074";
522 | }
523 | .fa-comment:before {
524 | content: "\f075";
525 | }
526 | .fa-magnet:before {
527 | content: "\f076";
528 | }
529 | .fa-chevron-up:before {
530 | content: "\f077";
531 | }
532 | .fa-chevron-down:before {
533 | content: "\f078";
534 | }
535 | .fa-retweet:before {
536 | content: "\f079";
537 | }
538 | .fa-shopping-cart:before {
539 | content: "\f07a";
540 | }
541 | .fa-folder:before {
542 | content: "\f07b";
543 | }
544 | .fa-folder-open:before {
545 | content: "\f07c";
546 | }
547 | .fa-arrows-v:before {
548 | content: "\f07d";
549 | }
550 | .fa-arrows-h:before {
551 | content: "\f07e";
552 | }
553 | .fa-bar-chart-o:before,
554 | .fa-bar-chart:before {
555 | content: "\f080";
556 | }
557 | .fa-twitter-square:before {
558 | content: "\f081";
559 | }
560 | .fa-facebook-square:before {
561 | content: "\f082";
562 | }
563 | .fa-camera-retro:before {
564 | content: "\f083";
565 | }
566 | .fa-key:before {
567 | content: "\f084";
568 | }
569 | .fa-gears:before,
570 | .fa-cogs:before {
571 | content: "\f085";
572 | }
573 | .fa-comments:before {
574 | content: "\f086";
575 | }
576 | .fa-thumbs-o-up:before {
577 | content: "\f087";
578 | }
579 | .fa-thumbs-o-down:before {
580 | content: "\f088";
581 | }
582 | .fa-star-half:before {
583 | content: "\f089";
584 | }
585 | .fa-heart-o:before {
586 | content: "\f08a";
587 | }
588 | .fa-sign-out:before {
589 | content: "\f08b";
590 | }
591 | .fa-linkedin-square:before {
592 | content: "\f08c";
593 | }
594 | .fa-thumb-tack:before {
595 | content: "\f08d";
596 | }
597 | .fa-external-link:before {
598 | content: "\f08e";
599 | }
600 | .fa-sign-in:before {
601 | content: "\f090";
602 | }
603 | .fa-trophy:before {
604 | content: "\f091";
605 | }
606 | .fa-github-square:before {
607 | content: "\f092";
608 | }
609 | .fa-upload:before {
610 | content: "\f093";
611 | }
612 | .fa-lemon-o:before {
613 | content: "\f094";
614 | }
615 | .fa-phone:before {
616 | content: "\f095";
617 | }
618 | .fa-square-o:before {
619 | content: "\f096";
620 | }
621 | .fa-bookmark-o:before {
622 | content: "\f097";
623 | }
624 | .fa-phone-square:before {
625 | content: "\f098";
626 | }
627 | .fa-twitter:before {
628 | content: "\f099";
629 | }
630 | .fa-facebook-f:before,
631 | .fa-facebook:before {
632 | content: "\f09a";
633 | }
634 | .fa-github:before {
635 | content: "\f09b";
636 | }
637 | .fa-unlock:before {
638 | content: "\f09c";
639 | }
640 | .fa-credit-card:before {
641 | content: "\f09d";
642 | }
643 | .fa-feed:before,
644 | .fa-rss:before {
645 | content: "\f09e";
646 | }
647 | .fa-hdd-o:before {
648 | content: "\f0a0";
649 | }
650 | .fa-bullhorn:before {
651 | content: "\f0a1";
652 | }
653 | .fa-bell:before {
654 | content: "\f0f3";
655 | }
656 | .fa-certificate:before {
657 | content: "\f0a3";
658 | }
659 | .fa-hand-o-right:before {
660 | content: "\f0a4";
661 | }
662 | .fa-hand-o-left:before {
663 | content: "\f0a5";
664 | }
665 | .fa-hand-o-up:before {
666 | content: "\f0a6";
667 | }
668 | .fa-hand-o-down:before {
669 | content: "\f0a7";
670 | }
671 | .fa-arrow-circle-left:before {
672 | content: "\f0a8";
673 | }
674 | .fa-arrow-circle-right:before {
675 | content: "\f0a9";
676 | }
677 | .fa-arrow-circle-up:before {
678 | content: "\f0aa";
679 | }
680 | .fa-arrow-circle-down:before {
681 | content: "\f0ab";
682 | }
683 | .fa-globe:before {
684 | content: "\f0ac";
685 | }
686 | .fa-wrench:before {
687 | content: "\f0ad";
688 | }
689 | .fa-tasks:before {
690 | content: "\f0ae";
691 | }
692 | .fa-filter:before {
693 | content: "\f0b0";
694 | }
695 | .fa-briefcase:before {
696 | content: "\f0b1";
697 | }
698 | .fa-arrows-alt:before {
699 | content: "\f0b2";
700 | }
701 | .fa-group:before,
702 | .fa-users:before {
703 | content: "\f0c0";
704 | }
705 | .fa-chain:before,
706 | .fa-link:before {
707 | content: "\f0c1";
708 | }
709 | .fa-cloud:before {
710 | content: "\f0c2";
711 | }
712 | .fa-flask:before {
713 | content: "\f0c3";
714 | }
715 | .fa-cut:before,
716 | .fa-scissors:before {
717 | content: "\f0c4";
718 | }
719 | .fa-copy:before,
720 | .fa-files-o:before {
721 | content: "\f0c5";
722 | }
723 | .fa-paperclip:before {
724 | content: "\f0c6";
725 | }
726 | .fa-save:before,
727 | .fa-floppy-o:before {
728 | content: "\f0c7";
729 | }
730 | .fa-square:before {
731 | content: "\f0c8";
732 | }
733 | .fa-navicon:before,
734 | .fa-reorder:before,
735 | .fa-bars:before {
736 | content: "\f0c9";
737 | }
738 | .fa-list-ul:before {
739 | content: "\f0ca";
740 | }
741 | .fa-list-ol:before {
742 | content: "\f0cb";
743 | }
744 | .fa-strikethrough:before {
745 | content: "\f0cc";
746 | }
747 | .fa-underline:before {
748 | content: "\f0cd";
749 | }
750 | .fa-table:before {
751 | content: "\f0ce";
752 | }
753 | .fa-magic:before {
754 | content: "\f0d0";
755 | }
756 | .fa-truck:before {
757 | content: "\f0d1";
758 | }
759 | .fa-pinterest:before {
760 | content: "\f0d2";
761 | }
762 | .fa-pinterest-square:before {
763 | content: "\f0d3";
764 | }
765 | .fa-google-plus-square:before {
766 | content: "\f0d4";
767 | }
768 | .fa-google-plus:before {
769 | content: "\f0d5";
770 | }
771 | .fa-money:before {
772 | content: "\f0d6";
773 | }
774 | .fa-caret-down:before {
775 | content: "\f0d7";
776 | }
777 | .fa-caret-up:before {
778 | content: "\f0d8";
779 | }
780 | .fa-caret-left:before {
781 | content: "\f0d9";
782 | }
783 | .fa-caret-right:before {
784 | content: "\f0da";
785 | }
786 | .fa-columns:before {
787 | content: "\f0db";
788 | }
789 | .fa-unsorted:before,
790 | .fa-sort:before {
791 | content: "\f0dc";
792 | }
793 | .fa-sort-down:before,
794 | .fa-sort-desc:before {
795 | content: "\f0dd";
796 | }
797 | .fa-sort-up:before,
798 | .fa-sort-asc:before {
799 | content: "\f0de";
800 | }
801 | .fa-envelope:before {
802 | content: "\f0e0";
803 | }
804 | .fa-linkedin:before {
805 | content: "\f0e1";
806 | }
807 | .fa-rotate-left:before,
808 | .fa-undo:before {
809 | content: "\f0e2";
810 | }
811 | .fa-legal:before,
812 | .fa-gavel:before {
813 | content: "\f0e3";
814 | }
815 | .fa-dashboard:before,
816 | .fa-tachometer:before {
817 | content: "\f0e4";
818 | }
819 | .fa-comment-o:before {
820 | content: "\f0e5";
821 | }
822 | .fa-comments-o:before {
823 | content: "\f0e6";
824 | }
825 | .fa-flash:before,
826 | .fa-bolt:before {
827 | content: "\f0e7";
828 | }
829 | .fa-sitemap:before {
830 | content: "\f0e8";
831 | }
832 | .fa-umbrella:before {
833 | content: "\f0e9";
834 | }
835 | .fa-paste:before,
836 | .fa-clipboard:before {
837 | content: "\f0ea";
838 | }
839 | .fa-lightbulb-o:before {
840 | content: "\f0eb";
841 | }
842 | .fa-exchange:before {
843 | content: "\f0ec";
844 | }
845 | .fa-cloud-download:before {
846 | content: "\f0ed";
847 | }
848 | .fa-cloud-upload:before {
849 | content: "\f0ee";
850 | }
851 | .fa-user-md:before {
852 | content: "\f0f0";
853 | }
854 | .fa-stethoscope:before {
855 | content: "\f0f1";
856 | }
857 | .fa-suitcase:before {
858 | content: "\f0f2";
859 | }
860 | .fa-bell-o:before {
861 | content: "\f0a2";
862 | }
863 | .fa-coffee:before {
864 | content: "\f0f4";
865 | }
866 | .fa-cutlery:before {
867 | content: "\f0f5";
868 | }
869 | .fa-file-text-o:before {
870 | content: "\f0f6";
871 | }
872 | .fa-building-o:before {
873 | content: "\f0f7";
874 | }
875 | .fa-hospital-o:before {
876 | content: "\f0f8";
877 | }
878 | .fa-ambulance:before {
879 | content: "\f0f9";
880 | }
881 | .fa-medkit:before {
882 | content: "\f0fa";
883 | }
884 | .fa-fighter-jet:before {
885 | content: "\f0fb";
886 | }
887 | .fa-beer:before {
888 | content: "\f0fc";
889 | }
890 | .fa-h-square:before {
891 | content: "\f0fd";
892 | }
893 | .fa-plus-square:before {
894 | content: "\f0fe";
895 | }
896 | .fa-angle-double-left:before {
897 | content: "\f100";
898 | }
899 | .fa-angle-double-right:before {
900 | content: "\f101";
901 | }
902 | .fa-angle-double-up:before {
903 | content: "\f102";
904 | }
905 | .fa-angle-double-down:before {
906 | content: "\f103";
907 | }
908 | .fa-angle-left:before {
909 | content: "\f104";
910 | }
911 | .fa-angle-right:before {
912 | content: "\f105";
913 | }
914 | .fa-angle-up:before {
915 | content: "\f106";
916 | }
917 | .fa-angle-down:before {
918 | content: "\f107";
919 | }
920 | .fa-desktop:before {
921 | content: "\f108";
922 | }
923 | .fa-laptop:before {
924 | content: "\f109";
925 | }
926 | .fa-tablet:before {
927 | content: "\f10a";
928 | }
929 | .fa-mobile-phone:before,
930 | .fa-mobile:before {
931 | content: "\f10b";
932 | }
933 | .fa-circle-o:before {
934 | content: "\f10c";
935 | }
936 | .fa-quote-left:before {
937 | content: "\f10d";
938 | }
939 | .fa-quote-right:before {
940 | content: "\f10e";
941 | }
942 | .fa-spinner:before {
943 | content: "\f110";
944 | }
945 | .fa-circle:before {
946 | content: "\f111";
947 | }
948 | .fa-mail-reply:before,
949 | .fa-reply:before {
950 | content: "\f112";
951 | }
952 | .fa-github-alt:before {
953 | content: "\f113";
954 | }
955 | .fa-folder-o:before {
956 | content: "\f114";
957 | }
958 | .fa-folder-open-o:before {
959 | content: "\f115";
960 | }
961 | .fa-smile-o:before {
962 | content: "\f118";
963 | }
964 | .fa-frown-o:before {
965 | content: "\f119";
966 | }
967 | .fa-meh-o:before {
968 | content: "\f11a";
969 | }
970 | .fa-gamepad:before {
971 | content: "\f11b";
972 | }
973 | .fa-keyboard-o:before {
974 | content: "\f11c";
975 | }
976 | .fa-flag-o:before {
977 | content: "\f11d";
978 | }
979 | .fa-flag-checkered:before {
980 | content: "\f11e";
981 | }
982 | .fa-terminal:before {
983 | content: "\f120";
984 | }
985 | .fa-code:before {
986 | content: "\f121";
987 | }
988 | .fa-mail-reply-all:before,
989 | .fa-reply-all:before {
990 | content: "\f122";
991 | }
992 | .fa-star-half-empty:before,
993 | .fa-star-half-full:before,
994 | .fa-star-half-o:before {
995 | content: "\f123";
996 | }
997 | .fa-location-arrow:before {
998 | content: "\f124";
999 | }
1000 | .fa-crop:before {
1001 | content: "\f125";
1002 | }
1003 | .fa-code-fork:before {
1004 | content: "\f126";
1005 | }
1006 | .fa-unlink:before,
1007 | .fa-chain-broken:before {
1008 | content: "\f127";
1009 | }
1010 | .fa-question:before {
1011 | content: "\f128";
1012 | }
1013 | .fa-info:before {
1014 | content: "\f129";
1015 | }
1016 | .fa-exclamation:before {
1017 | content: "\f12a";
1018 | }
1019 | .fa-superscript:before {
1020 | content: "\f12b";
1021 | }
1022 | .fa-subscript:before {
1023 | content: "\f12c";
1024 | }
1025 | .fa-eraser:before {
1026 | content: "\f12d";
1027 | }
1028 | .fa-puzzle-piece:before {
1029 | content: "\f12e";
1030 | }
1031 | .fa-microphone:before {
1032 | content: "\f130";
1033 | }
1034 | .fa-microphone-slash:before {
1035 | content: "\f131";
1036 | }
1037 | .fa-shield:before {
1038 | content: "\f132";
1039 | }
1040 | .fa-calendar-o:before {
1041 | content: "\f133";
1042 | }
1043 | .fa-fire-extinguisher:before {
1044 | content: "\f134";
1045 | }
1046 | .fa-rocket:before {
1047 | content: "\f135";
1048 | }
1049 | .fa-maxcdn:before {
1050 | content: "\f136";
1051 | }
1052 | .fa-chevron-circle-left:before {
1053 | content: "\f137";
1054 | }
1055 | .fa-chevron-circle-right:before {
1056 | content: "\f138";
1057 | }
1058 | .fa-chevron-circle-up:before {
1059 | content: "\f139";
1060 | }
1061 | .fa-chevron-circle-down:before {
1062 | content: "\f13a";
1063 | }
1064 | .fa-html5:before {
1065 | content: "\f13b";
1066 | }
1067 | .fa-css3:before {
1068 | content: "\f13c";
1069 | }
1070 | .fa-anchor:before {
1071 | content: "\f13d";
1072 | }
1073 | .fa-unlock-alt:before {
1074 | content: "\f13e";
1075 | }
1076 | .fa-bullseye:before {
1077 | content: "\f140";
1078 | }
1079 | .fa-ellipsis-h:before {
1080 | content: "\f141";
1081 | }
1082 | .fa-ellipsis-v:before {
1083 | content: "\f142";
1084 | }
1085 | .fa-rss-square:before {
1086 | content: "\f143";
1087 | }
1088 | .fa-play-circle:before {
1089 | content: "\f144";
1090 | }
1091 | .fa-ticket:before {
1092 | content: "\f145";
1093 | }
1094 | .fa-minus-square:before {
1095 | content: "\f146";
1096 | }
1097 | .fa-minus-square-o:before {
1098 | content: "\f147";
1099 | }
1100 | .fa-level-up:before {
1101 | content: "\f148";
1102 | }
1103 | .fa-level-down:before {
1104 | content: "\f149";
1105 | }
1106 | .fa-check-square:before {
1107 | content: "\f14a";
1108 | }
1109 | .fa-pencil-square:before {
1110 | content: "\f14b";
1111 | }
1112 | .fa-external-link-square:before {
1113 | content: "\f14c";
1114 | }
1115 | .fa-share-square:before {
1116 | content: "\f14d";
1117 | }
1118 | .fa-compass:before {
1119 | content: "\f14e";
1120 | }
1121 | .fa-toggle-down:before,
1122 | .fa-caret-square-o-down:before {
1123 | content: "\f150";
1124 | }
1125 | .fa-toggle-up:before,
1126 | .fa-caret-square-o-up:before {
1127 | content: "\f151";
1128 | }
1129 | .fa-toggle-right:before,
1130 | .fa-caret-square-o-right:before {
1131 | content: "\f152";
1132 | }
1133 | .fa-euro:before,
1134 | .fa-eur:before {
1135 | content: "\f153";
1136 | }
1137 | .fa-gbp:before {
1138 | content: "\f154";
1139 | }
1140 | .fa-dollar:before,
1141 | .fa-usd:before {
1142 | content: "\f155";
1143 | }
1144 | .fa-rupee:before,
1145 | .fa-inr:before {
1146 | content: "\f156";
1147 | }
1148 | .fa-cny:before,
1149 | .fa-rmb:before,
1150 | .fa-yen:before,
1151 | .fa-jpy:before {
1152 | content: "\f157";
1153 | }
1154 | .fa-ruble:before,
1155 | .fa-rouble:before,
1156 | .fa-rub:before {
1157 | content: "\f158";
1158 | }
1159 | .fa-won:before,
1160 | .fa-krw:before {
1161 | content: "\f159";
1162 | }
1163 | .fa-bitcoin:before,
1164 | .fa-btc:before {
1165 | content: "\f15a";
1166 | }
1167 | .fa-file:before {
1168 | content: "\f15b";
1169 | }
1170 | .fa-file-text:before {
1171 | content: "\f15c";
1172 | }
1173 | .fa-sort-alpha-asc:before {
1174 | content: "\f15d";
1175 | }
1176 | .fa-sort-alpha-desc:before {
1177 | content: "\f15e";
1178 | }
1179 | .fa-sort-amount-asc:before {
1180 | content: "\f160";
1181 | }
1182 | .fa-sort-amount-desc:before {
1183 | content: "\f161";
1184 | }
1185 | .fa-sort-numeric-asc:before {
1186 | content: "\f162";
1187 | }
1188 | .fa-sort-numeric-desc:before {
1189 | content: "\f163";
1190 | }
1191 | .fa-thumbs-up:before {
1192 | content: "\f164";
1193 | }
1194 | .fa-thumbs-down:before {
1195 | content: "\f165";
1196 | }
1197 | .fa-youtube-square:before {
1198 | content: "\f166";
1199 | }
1200 | .fa-youtube:before {
1201 | content: "\f167";
1202 | }
1203 | .fa-xing:before {
1204 | content: "\f168";
1205 | }
1206 | .fa-xing-square:before {
1207 | content: "\f169";
1208 | }
1209 | .fa-youtube-play:before {
1210 | content: "\f16a";
1211 | }
1212 | .fa-dropbox:before {
1213 | content: "\f16b";
1214 | }
1215 | .fa-stack-overflow:before {
1216 | content: "\f16c";
1217 | }
1218 | .fa-instagram:before {
1219 | content: "\f16d";
1220 | }
1221 | .fa-flickr:before {
1222 | content: "\f16e";
1223 | }
1224 | .fa-adn:before {
1225 | content: "\f170";
1226 | }
1227 | .fa-bitbucket:before {
1228 | content: "\f171";
1229 | }
1230 | .fa-bitbucket-square:before {
1231 | content: "\f172";
1232 | }
1233 | .fa-tumblr:before {
1234 | content: "\f173";
1235 | }
1236 | .fa-tumblr-square:before {
1237 | content: "\f174";
1238 | }
1239 | .fa-long-arrow-down:before {
1240 | content: "\f175";
1241 | }
1242 | .fa-long-arrow-up:before {
1243 | content: "\f176";
1244 | }
1245 | .fa-long-arrow-left:before {
1246 | content: "\f177";
1247 | }
1248 | .fa-long-arrow-right:before {
1249 | content: "\f178";
1250 | }
1251 | .fa-apple:before {
1252 | content: "\f179";
1253 | }
1254 | .fa-windows:before {
1255 | content: "\f17a";
1256 | }
1257 | .fa-android:before {
1258 | content: "\f17b";
1259 | }
1260 | .fa-linux:before {
1261 | content: "\f17c";
1262 | }
1263 | .fa-dribbble:before {
1264 | content: "\f17d";
1265 | }
1266 | .fa-skype:before {
1267 | content: "\f17e";
1268 | }
1269 | .fa-foursquare:before {
1270 | content: "\f180";
1271 | }
1272 | .fa-trello:before {
1273 | content: "\f181";
1274 | }
1275 | .fa-female:before {
1276 | content: "\f182";
1277 | }
1278 | .fa-male:before {
1279 | content: "\f183";
1280 | }
1281 | .fa-gittip:before,
1282 | .fa-gratipay:before {
1283 | content: "\f184";
1284 | }
1285 | .fa-sun-o:before {
1286 | content: "\f185";
1287 | }
1288 | .fa-moon-o:before {
1289 | content: "\f186";
1290 | }
1291 | .fa-archive:before {
1292 | content: "\f187";
1293 | }
1294 | .fa-bug:before {
1295 | content: "\f188";
1296 | }
1297 | .fa-vk:before {
1298 | content: "\f189";
1299 | }
1300 | .fa-weibo:before {
1301 | content: "\f18a";
1302 | }
1303 | .fa-renren:before {
1304 | content: "\f18b";
1305 | }
1306 | .fa-pagelines:before {
1307 | content: "\f18c";
1308 | }
1309 | .fa-stack-exchange:before {
1310 | content: "\f18d";
1311 | }
1312 | .fa-arrow-circle-o-right:before {
1313 | content: "\f18e";
1314 | }
1315 | .fa-arrow-circle-o-left:before {
1316 | content: "\f190";
1317 | }
1318 | .fa-toggle-left:before,
1319 | .fa-caret-square-o-left:before {
1320 | content: "\f191";
1321 | }
1322 | .fa-dot-circle-o:before {
1323 | content: "\f192";
1324 | }
1325 | .fa-wheelchair:before {
1326 | content: "\f193";
1327 | }
1328 | .fa-vimeo-square:before {
1329 | content: "\f194";
1330 | }
1331 | .fa-turkish-lira:before,
1332 | .fa-try:before {
1333 | content: "\f195";
1334 | }
1335 | .fa-plus-square-o:before {
1336 | content: "\f196";
1337 | }
1338 | .fa-space-shuttle:before {
1339 | content: "\f197";
1340 | }
1341 | .fa-slack:before {
1342 | content: "\f198";
1343 | }
1344 | .fa-envelope-square:before {
1345 | content: "\f199";
1346 | }
1347 | .fa-wordpress:before {
1348 | content: "\f19a";
1349 | }
1350 | .fa-openid:before {
1351 | content: "\f19b";
1352 | }
1353 | .fa-institution:before,
1354 | .fa-bank:before,
1355 | .fa-university:before {
1356 | content: "\f19c";
1357 | }
1358 | .fa-mortar-board:before,
1359 | .fa-graduation-cap:before {
1360 | content: "\f19d";
1361 | }
1362 | .fa-yahoo:before {
1363 | content: "\f19e";
1364 | }
1365 | .fa-google:before {
1366 | content: "\f1a0";
1367 | }
1368 | .fa-reddit:before {
1369 | content: "\f1a1";
1370 | }
1371 | .fa-reddit-square:before {
1372 | content: "\f1a2";
1373 | }
1374 | .fa-stumbleupon-circle:before {
1375 | content: "\f1a3";
1376 | }
1377 | .fa-stumbleupon:before {
1378 | content: "\f1a4";
1379 | }
1380 | .fa-delicious:before {
1381 | content: "\f1a5";
1382 | }
1383 | .fa-digg:before {
1384 | content: "\f1a6";
1385 | }
1386 | .fa-pied-piper-pp:before {
1387 | content: "\f1a7";
1388 | }
1389 | .fa-pied-piper-alt:before {
1390 | content: "\f1a8";
1391 | }
1392 | .fa-drupal:before {
1393 | content: "\f1a9";
1394 | }
1395 | .fa-joomla:before {
1396 | content: "\f1aa";
1397 | }
1398 | .fa-language:before {
1399 | content: "\f1ab";
1400 | }
1401 | .fa-fax:before {
1402 | content: "\f1ac";
1403 | }
1404 | .fa-building:before {
1405 | content: "\f1ad";
1406 | }
1407 | .fa-child:before {
1408 | content: "\f1ae";
1409 | }
1410 | .fa-paw:before {
1411 | content: "\f1b0";
1412 | }
1413 | .fa-spoon:before {
1414 | content: "\f1b1";
1415 | }
1416 | .fa-cube:before {
1417 | content: "\f1b2";
1418 | }
1419 | .fa-cubes:before {
1420 | content: "\f1b3";
1421 | }
1422 | .fa-behance:before {
1423 | content: "\f1b4";
1424 | }
1425 | .fa-behance-square:before {
1426 | content: "\f1b5";
1427 | }
1428 | .fa-steam:before {
1429 | content: "\f1b6";
1430 | }
1431 | .fa-steam-square:before {
1432 | content: "\f1b7";
1433 | }
1434 | .fa-recycle:before {
1435 | content: "\f1b8";
1436 | }
1437 | .fa-automobile:before,
1438 | .fa-car:before {
1439 | content: "\f1b9";
1440 | }
1441 | .fa-cab:before,
1442 | .fa-taxi:before {
1443 | content: "\f1ba";
1444 | }
1445 | .fa-tree:before {
1446 | content: "\f1bb";
1447 | }
1448 | .fa-spotify:before {
1449 | content: "\f1bc";
1450 | }
1451 | .fa-deviantart:before {
1452 | content: "\f1bd";
1453 | }
1454 | .fa-soundcloud:before {
1455 | content: "\f1be";
1456 | }
1457 | .fa-database:before {
1458 | content: "\f1c0";
1459 | }
1460 | .fa-file-pdf-o:before {
1461 | content: "\f1c1";
1462 | }
1463 | .fa-file-word-o:before {
1464 | content: "\f1c2";
1465 | }
1466 | .fa-file-excel-o:before {
1467 | content: "\f1c3";
1468 | }
1469 | .fa-file-powerpoint-o:before {
1470 | content: "\f1c4";
1471 | }
1472 | .fa-file-photo-o:before,
1473 | .fa-file-picture-o:before,
1474 | .fa-file-image-o:before {
1475 | content: "\f1c5";
1476 | }
1477 | .fa-file-zip-o:before,
1478 | .fa-file-archive-o:before {
1479 | content: "\f1c6";
1480 | }
1481 | .fa-file-sound-o:before,
1482 | .fa-file-audio-o:before {
1483 | content: "\f1c7";
1484 | }
1485 | .fa-file-movie-o:before,
1486 | .fa-file-video-o:before {
1487 | content: "\f1c8";
1488 | }
1489 | .fa-file-code-o:before {
1490 | content: "\f1c9";
1491 | }
1492 | .fa-vine:before {
1493 | content: "\f1ca";
1494 | }
1495 | .fa-codepen:before {
1496 | content: "\f1cb";
1497 | }
1498 | .fa-jsfiddle:before {
1499 | content: "\f1cc";
1500 | }
1501 | .fa-life-bouy:before,
1502 | .fa-life-buoy:before,
1503 | .fa-life-saver:before,
1504 | .fa-support:before,
1505 | .fa-life-ring:before {
1506 | content: "\f1cd";
1507 | }
1508 | .fa-circle-o-notch:before {
1509 | content: "\f1ce";
1510 | }
1511 | .fa-ra:before,
1512 | .fa-resistance:before,
1513 | .fa-rebel:before {
1514 | content: "\f1d0";
1515 | }
1516 | .fa-ge:before,
1517 | .fa-empire:before {
1518 | content: "\f1d1";
1519 | }
1520 | .fa-git-square:before {
1521 | content: "\f1d2";
1522 | }
1523 | .fa-git:before {
1524 | content: "\f1d3";
1525 | }
1526 | .fa-y-combinator-square:before,
1527 | .fa-yc-square:before,
1528 | .fa-hacker-news:before {
1529 | content: "\f1d4";
1530 | }
1531 | .fa-tencent-weibo:before {
1532 | content: "\f1d5";
1533 | }
1534 | .fa-qq:before {
1535 | content: "\f1d6";
1536 | }
1537 | .fa-wechat:before,
1538 | .fa-weixin:before {
1539 | content: "\f1d7";
1540 | }
1541 | .fa-send:before,
1542 | .fa-paper-plane:before {
1543 | content: "\f1d8";
1544 | }
1545 | .fa-send-o:before,
1546 | .fa-paper-plane-o:before {
1547 | content: "\f1d9";
1548 | }
1549 | .fa-history:before {
1550 | content: "\f1da";
1551 | }
1552 | .fa-circle-thin:before {
1553 | content: "\f1db";
1554 | }
1555 | .fa-header:before {
1556 | content: "\f1dc";
1557 | }
1558 | .fa-paragraph:before {
1559 | content: "\f1dd";
1560 | }
1561 | .fa-sliders:before {
1562 | content: "\f1de";
1563 | }
1564 | .fa-share-alt:before {
1565 | content: "\f1e0";
1566 | }
1567 | .fa-share-alt-square:before {
1568 | content: "\f1e1";
1569 | }
1570 | .fa-bomb:before {
1571 | content: "\f1e2";
1572 | }
1573 | .fa-soccer-ball-o:before,
1574 | .fa-futbol-o:before {
1575 | content: "\f1e3";
1576 | }
1577 | .fa-tty:before {
1578 | content: "\f1e4";
1579 | }
1580 | .fa-binoculars:before {
1581 | content: "\f1e5";
1582 | }
1583 | .fa-plug:before {
1584 | content: "\f1e6";
1585 | }
1586 | .fa-slideshare:before {
1587 | content: "\f1e7";
1588 | }
1589 | .fa-twitch:before {
1590 | content: "\f1e8";
1591 | }
1592 | .fa-yelp:before {
1593 | content: "\f1e9";
1594 | }
1595 | .fa-newspaper-o:before {
1596 | content: "\f1ea";
1597 | }
1598 | .fa-wifi:before {
1599 | content: "\f1eb";
1600 | }
1601 | .fa-calculator:before {
1602 | content: "\f1ec";
1603 | }
1604 | .fa-paypal:before {
1605 | content: "\f1ed";
1606 | }
1607 | .fa-google-wallet:before {
1608 | content: "\f1ee";
1609 | }
1610 | .fa-cc-visa:before {
1611 | content: "\f1f0";
1612 | }
1613 | .fa-cc-mastercard:before {
1614 | content: "\f1f1";
1615 | }
1616 | .fa-cc-discover:before {
1617 | content: "\f1f2";
1618 | }
1619 | .fa-cc-amex:before {
1620 | content: "\f1f3";
1621 | }
1622 | .fa-cc-paypal:before {
1623 | content: "\f1f4";
1624 | }
1625 | .fa-cc-stripe:before {
1626 | content: "\f1f5";
1627 | }
1628 | .fa-bell-slash:before {
1629 | content: "\f1f6";
1630 | }
1631 | .fa-bell-slash-o:before {
1632 | content: "\f1f7";
1633 | }
1634 | .fa-trash:before {
1635 | content: "\f1f8";
1636 | }
1637 | .fa-copyright:before {
1638 | content: "\f1f9";
1639 | }
1640 | .fa-at:before {
1641 | content: "\f1fa";
1642 | }
1643 | .fa-eyedropper:before {
1644 | content: "\f1fb";
1645 | }
1646 | .fa-paint-brush:before {
1647 | content: "\f1fc";
1648 | }
1649 | .fa-birthday-cake:before {
1650 | content: "\f1fd";
1651 | }
1652 | .fa-area-chart:before {
1653 | content: "\f1fe";
1654 | }
1655 | .fa-pie-chart:before {
1656 | content: "\f200";
1657 | }
1658 | .fa-line-chart:before {
1659 | content: "\f201";
1660 | }
1661 | .fa-lastfm:before {
1662 | content: "\f202";
1663 | }
1664 | .fa-lastfm-square:before {
1665 | content: "\f203";
1666 | }
1667 | .fa-toggle-off:before {
1668 | content: "\f204";
1669 | }
1670 | .fa-toggle-on:before {
1671 | content: "\f205";
1672 | }
1673 | .fa-bicycle:before {
1674 | content: "\f206";
1675 | }
1676 | .fa-bus:before {
1677 | content: "\f207";
1678 | }
1679 | .fa-ioxhost:before {
1680 | content: "\f208";
1681 | }
1682 | .fa-angellist:before {
1683 | content: "\f209";
1684 | }
1685 | .fa-cc:before {
1686 | content: "\f20a";
1687 | }
1688 | .fa-shekel:before,
1689 | .fa-sheqel:before,
1690 | .fa-ils:before {
1691 | content: "\f20b";
1692 | }
1693 | .fa-meanpath:before {
1694 | content: "\f20c";
1695 | }
1696 | .fa-buysellads:before {
1697 | content: "\f20d";
1698 | }
1699 | .fa-connectdevelop:before {
1700 | content: "\f20e";
1701 | }
1702 | .fa-dashcube:before {
1703 | content: "\f210";
1704 | }
1705 | .fa-forumbee:before {
1706 | content: "\f211";
1707 | }
1708 | .fa-leanpub:before {
1709 | content: "\f212";
1710 | }
1711 | .fa-sellsy:before {
1712 | content: "\f213";
1713 | }
1714 | .fa-shirtsinbulk:before {
1715 | content: "\f214";
1716 | }
1717 | .fa-simplybuilt:before {
1718 | content: "\f215";
1719 | }
1720 | .fa-skyatlas:before {
1721 | content: "\f216";
1722 | }
1723 | .fa-cart-plus:before {
1724 | content: "\f217";
1725 | }
1726 | .fa-cart-arrow-down:before {
1727 | content: "\f218";
1728 | }
1729 | .fa-diamond:before {
1730 | content: "\f219";
1731 | }
1732 | .fa-ship:before {
1733 | content: "\f21a";
1734 | }
1735 | .fa-user-secret:before {
1736 | content: "\f21b";
1737 | }
1738 | .fa-motorcycle:before {
1739 | content: "\f21c";
1740 | }
1741 | .fa-street-view:before {
1742 | content: "\f21d";
1743 | }
1744 | .fa-heartbeat:before {
1745 | content: "\f21e";
1746 | }
1747 | .fa-venus:before {
1748 | content: "\f221";
1749 | }
1750 | .fa-mars:before {
1751 | content: "\f222";
1752 | }
1753 | .fa-mercury:before {
1754 | content: "\f223";
1755 | }
1756 | .fa-intersex:before,
1757 | .fa-transgender:before {
1758 | content: "\f224";
1759 | }
1760 | .fa-transgender-alt:before {
1761 | content: "\f225";
1762 | }
1763 | .fa-venus-double:before {
1764 | content: "\f226";
1765 | }
1766 | .fa-mars-double:before {
1767 | content: "\f227";
1768 | }
1769 | .fa-venus-mars:before {
1770 | content: "\f228";
1771 | }
1772 | .fa-mars-stroke:before {
1773 | content: "\f229";
1774 | }
1775 | .fa-mars-stroke-v:before {
1776 | content: "\f22a";
1777 | }
1778 | .fa-mars-stroke-h:before {
1779 | content: "\f22b";
1780 | }
1781 | .fa-neuter:before {
1782 | content: "\f22c";
1783 | }
1784 | .fa-genderless:before {
1785 | content: "\f22d";
1786 | }
1787 | .fa-facebook-official:before {
1788 | content: "\f230";
1789 | }
1790 | .fa-pinterest-p:before {
1791 | content: "\f231";
1792 | }
1793 | .fa-whatsapp:before {
1794 | content: "\f232";
1795 | }
1796 | .fa-server:before {
1797 | content: "\f233";
1798 | }
1799 | .fa-user-plus:before {
1800 | content: "\f234";
1801 | }
1802 | .fa-user-times:before {
1803 | content: "\f235";
1804 | }
1805 | .fa-hotel:before,
1806 | .fa-bed:before {
1807 | content: "\f236";
1808 | }
1809 | .fa-viacoin:before {
1810 | content: "\f237";
1811 | }
1812 | .fa-train:before {
1813 | content: "\f238";
1814 | }
1815 | .fa-subway:before {
1816 | content: "\f239";
1817 | }
1818 | .fa-medium:before {
1819 | content: "\f23a";
1820 | }
1821 | .fa-yc:before,
1822 | .fa-y-combinator:before {
1823 | content: "\f23b";
1824 | }
1825 | .fa-optin-monster:before {
1826 | content: "\f23c";
1827 | }
1828 | .fa-opencart:before {
1829 | content: "\f23d";
1830 | }
1831 | .fa-expeditedssl:before {
1832 | content: "\f23e";
1833 | }
1834 | .fa-battery-4:before,
1835 | .fa-battery:before,
1836 | .fa-battery-full:before {
1837 | content: "\f240";
1838 | }
1839 | .fa-battery-3:before,
1840 | .fa-battery-three-quarters:before {
1841 | content: "\f241";
1842 | }
1843 | .fa-battery-2:before,
1844 | .fa-battery-half:before {
1845 | content: "\f242";
1846 | }
1847 | .fa-battery-1:before,
1848 | .fa-battery-quarter:before {
1849 | content: "\f243";
1850 | }
1851 | .fa-battery-0:before,
1852 | .fa-battery-empty:before {
1853 | content: "\f244";
1854 | }
1855 | .fa-mouse-pointer:before {
1856 | content: "\f245";
1857 | }
1858 | .fa-i-cursor:before {
1859 | content: "\f246";
1860 | }
1861 | .fa-object-group:before {
1862 | content: "\f247";
1863 | }
1864 | .fa-object-ungroup:before {
1865 | content: "\f248";
1866 | }
1867 | .fa-sticky-note:before {
1868 | content: "\f249";
1869 | }
1870 | .fa-sticky-note-o:before {
1871 | content: "\f24a";
1872 | }
1873 | .fa-cc-jcb:before {
1874 | content: "\f24b";
1875 | }
1876 | .fa-cc-diners-club:before {
1877 | content: "\f24c";
1878 | }
1879 | .fa-clone:before {
1880 | content: "\f24d";
1881 | }
1882 | .fa-balance-scale:before {
1883 | content: "\f24e";
1884 | }
1885 | .fa-hourglass-o:before {
1886 | content: "\f250";
1887 | }
1888 | .fa-hourglass-1:before,
1889 | .fa-hourglass-start:before {
1890 | content: "\f251";
1891 | }
1892 | .fa-hourglass-2:before,
1893 | .fa-hourglass-half:before {
1894 | content: "\f252";
1895 | }
1896 | .fa-hourglass-3:before,
1897 | .fa-hourglass-end:before {
1898 | content: "\f253";
1899 | }
1900 | .fa-hourglass:before {
1901 | content: "\f254";
1902 | }
1903 | .fa-hand-grab-o:before,
1904 | .fa-hand-rock-o:before {
1905 | content: "\f255";
1906 | }
1907 | .fa-hand-stop-o:before,
1908 | .fa-hand-paper-o:before {
1909 | content: "\f256";
1910 | }
1911 | .fa-hand-scissors-o:before {
1912 | content: "\f257";
1913 | }
1914 | .fa-hand-lizard-o:before {
1915 | content: "\f258";
1916 | }
1917 | .fa-hand-spock-o:before {
1918 | content: "\f259";
1919 | }
1920 | .fa-hand-pointer-o:before {
1921 | content: "\f25a";
1922 | }
1923 | .fa-hand-peace-o:before {
1924 | content: "\f25b";
1925 | }
1926 | .fa-trademark:before {
1927 | content: "\f25c";
1928 | }
1929 | .fa-registered:before {
1930 | content: "\f25d";
1931 | }
1932 | .fa-creative-commons:before {
1933 | content: "\f25e";
1934 | }
1935 | .fa-gg:before {
1936 | content: "\f260";
1937 | }
1938 | .fa-gg-circle:before {
1939 | content: "\f261";
1940 | }
1941 | .fa-tripadvisor:before {
1942 | content: "\f262";
1943 | }
1944 | .fa-odnoklassniki:before {
1945 | content: "\f263";
1946 | }
1947 | .fa-odnoklassniki-square:before {
1948 | content: "\f264";
1949 | }
1950 | .fa-get-pocket:before {
1951 | content: "\f265";
1952 | }
1953 | .fa-wikipedia-w:before {
1954 | content: "\f266";
1955 | }
1956 | .fa-safari:before {
1957 | content: "\f267";
1958 | }
1959 | .fa-chrome:before {
1960 | content: "\f268";
1961 | }
1962 | .fa-firefox:before {
1963 | content: "\f269";
1964 | }
1965 | .fa-opera:before {
1966 | content: "\f26a";
1967 | }
1968 | .fa-internet-explorer:before {
1969 | content: "\f26b";
1970 | }
1971 | .fa-tv:before,
1972 | .fa-television:before {
1973 | content: "\f26c";
1974 | }
1975 | .fa-contao:before {
1976 | content: "\f26d";
1977 | }
1978 | .fa-500px:before {
1979 | content: "\f26e";
1980 | }
1981 | .fa-amazon:before {
1982 | content: "\f270";
1983 | }
1984 | .fa-calendar-plus-o:before {
1985 | content: "\f271";
1986 | }
1987 | .fa-calendar-minus-o:before {
1988 | content: "\f272";
1989 | }
1990 | .fa-calendar-times-o:before {
1991 | content: "\f273";
1992 | }
1993 | .fa-calendar-check-o:before {
1994 | content: "\f274";
1995 | }
1996 | .fa-industry:before {
1997 | content: "\f275";
1998 | }
1999 | .fa-map-pin:before {
2000 | content: "\f276";
2001 | }
2002 | .fa-map-signs:before {
2003 | content: "\f277";
2004 | }
2005 | .fa-map-o:before {
2006 | content: "\f278";
2007 | }
2008 | .fa-map:before {
2009 | content: "\f279";
2010 | }
2011 | .fa-commenting:before {
2012 | content: "\f27a";
2013 | }
2014 | .fa-commenting-o:before {
2015 | content: "\f27b";
2016 | }
2017 | .fa-houzz:before {
2018 | content: "\f27c";
2019 | }
2020 | .fa-vimeo:before {
2021 | content: "\f27d";
2022 | }
2023 | .fa-black-tie:before {
2024 | content: "\f27e";
2025 | }
2026 | .fa-fonticons:before {
2027 | content: "\f280";
2028 | }
2029 | .fa-reddit-alien:before {
2030 | content: "\f281";
2031 | }
2032 | .fa-edge:before {
2033 | content: "\f282";
2034 | }
2035 | .fa-credit-card-alt:before {
2036 | content: "\f283";
2037 | }
2038 | .fa-codiepie:before {
2039 | content: "\f284";
2040 | }
2041 | .fa-modx:before {
2042 | content: "\f285";
2043 | }
2044 | .fa-fort-awesome:before {
2045 | content: "\f286";
2046 | }
2047 | .fa-usb:before {
2048 | content: "\f287";
2049 | }
2050 | .fa-product-hunt:before {
2051 | content: "\f288";
2052 | }
2053 | .fa-mixcloud:before {
2054 | content: "\f289";
2055 | }
2056 | .fa-scribd:before {
2057 | content: "\f28a";
2058 | }
2059 | .fa-pause-circle:before {
2060 | content: "\f28b";
2061 | }
2062 | .fa-pause-circle-o:before {
2063 | content: "\f28c";
2064 | }
2065 | .fa-stop-circle:before {
2066 | content: "\f28d";
2067 | }
2068 | .fa-stop-circle-o:before {
2069 | content: "\f28e";
2070 | }
2071 | .fa-shopping-bag:before {
2072 | content: "\f290";
2073 | }
2074 | .fa-shopping-basket:before {
2075 | content: "\f291";
2076 | }
2077 | .fa-hashtag:before {
2078 | content: "\f292";
2079 | }
2080 | .fa-bluetooth:before {
2081 | content: "\f293";
2082 | }
2083 | .fa-bluetooth-b:before {
2084 | content: "\f294";
2085 | }
2086 | .fa-percent:before {
2087 | content: "\f295";
2088 | }
2089 | .fa-gitlab:before {
2090 | content: "\f296";
2091 | }
2092 | .fa-wpbeginner:before {
2093 | content: "\f297";
2094 | }
2095 | .fa-wpforms:before {
2096 | content: "\f298";
2097 | }
2098 | .fa-envira:before {
2099 | content: "\f299";
2100 | }
2101 | .fa-universal-access:before {
2102 | content: "\f29a";
2103 | }
2104 | .fa-wheelchair-alt:before {
2105 | content: "\f29b";
2106 | }
2107 | .fa-question-circle-o:before {
2108 | content: "\f29c";
2109 | }
2110 | .fa-blind:before {
2111 | content: "\f29d";
2112 | }
2113 | .fa-audio-description:before {
2114 | content: "\f29e";
2115 | }
2116 | .fa-volume-control-phone:before {
2117 | content: "\f2a0";
2118 | }
2119 | .fa-braille:before {
2120 | content: "\f2a1";
2121 | }
2122 | .fa-assistive-listening-systems:before {
2123 | content: "\f2a2";
2124 | }
2125 | .fa-asl-interpreting:before,
2126 | .fa-american-sign-language-interpreting:before {
2127 | content: "\f2a3";
2128 | }
2129 | .fa-deafness:before,
2130 | .fa-hard-of-hearing:before,
2131 | .fa-deaf:before {
2132 | content: "\f2a4";
2133 | }
2134 | .fa-glide:before {
2135 | content: "\f2a5";
2136 | }
2137 | .fa-glide-g:before {
2138 | content: "\f2a6";
2139 | }
2140 | .fa-signing:before,
2141 | .fa-sign-language:before {
2142 | content: "\f2a7";
2143 | }
2144 | .fa-low-vision:before {
2145 | content: "\f2a8";
2146 | }
2147 | .fa-viadeo:before {
2148 | content: "\f2a9";
2149 | }
2150 | .fa-viadeo-square:before {
2151 | content: "\f2aa";
2152 | }
2153 | .fa-snapchat:before {
2154 | content: "\f2ab";
2155 | }
2156 | .fa-snapchat-ghost:before {
2157 | content: "\f2ac";
2158 | }
2159 | .fa-snapchat-square:before {
2160 | content: "\f2ad";
2161 | }
2162 | .fa-pied-piper:before {
2163 | content: "\f2ae";
2164 | }
2165 | .fa-first-order:before {
2166 | content: "\f2b0";
2167 | }
2168 | .fa-yoast:before {
2169 | content: "\f2b1";
2170 | }
2171 | .fa-themeisle:before {
2172 | content: "\f2b2";
2173 | }
2174 | .fa-google-plus-circle:before,
2175 | .fa-google-plus-official:before {
2176 | content: "\f2b3";
2177 | }
2178 | .fa-fa:before,
2179 | .fa-font-awesome:before {
2180 | content: "\f2b4";
2181 | }
2182 | .fa-handshake-o:before {
2183 | content: "\f2b5";
2184 | }
2185 | .fa-envelope-open:before {
2186 | content: "\f2b6";
2187 | }
2188 | .fa-envelope-open-o:before {
2189 | content: "\f2b7";
2190 | }
2191 | .fa-linode:before {
2192 | content: "\f2b8";
2193 | }
2194 | .fa-address-book:before {
2195 | content: "\f2b9";
2196 | }
2197 | .fa-address-book-o:before {
2198 | content: "\f2ba";
2199 | }
2200 | .fa-vcard:before,
2201 | .fa-address-card:before {
2202 | content: "\f2bb";
2203 | }
2204 | .fa-vcard-o:before,
2205 | .fa-address-card-o:before {
2206 | content: "\f2bc";
2207 | }
2208 | .fa-user-circle:before {
2209 | content: "\f2bd";
2210 | }
2211 | .fa-user-circle-o:before {
2212 | content: "\f2be";
2213 | }
2214 | .fa-user-o:before {
2215 | content: "\f2c0";
2216 | }
2217 | .fa-id-badge:before {
2218 | content: "\f2c1";
2219 | }
2220 | .fa-drivers-license:before,
2221 | .fa-id-card:before {
2222 | content: "\f2c2";
2223 | }
2224 | .fa-drivers-license-o:before,
2225 | .fa-id-card-o:before {
2226 | content: "\f2c3";
2227 | }
2228 | .fa-quora:before {
2229 | content: "\f2c4";
2230 | }
2231 | .fa-free-code-camp:before {
2232 | content: "\f2c5";
2233 | }
2234 | .fa-telegram:before {
2235 | content: "\f2c6";
2236 | }
2237 | .fa-thermometer-4:before,
2238 | .fa-thermometer:before,
2239 | .fa-thermometer-full:before {
2240 | content: "\f2c7";
2241 | }
2242 | .fa-thermometer-3:before,
2243 | .fa-thermometer-three-quarters:before {
2244 | content: "\f2c8";
2245 | }
2246 | .fa-thermometer-2:before,
2247 | .fa-thermometer-half:before {
2248 | content: "\f2c9";
2249 | }
2250 | .fa-thermometer-1:before,
2251 | .fa-thermometer-quarter:before {
2252 | content: "\f2ca";
2253 | }
2254 | .fa-thermometer-0:before,
2255 | .fa-thermometer-empty:before {
2256 | content: "\f2cb";
2257 | }
2258 | .fa-shower:before {
2259 | content: "\f2cc";
2260 | }
2261 | .fa-bathtub:before,
2262 | .fa-s15:before,
2263 | .fa-bath:before {
2264 | content: "\f2cd";
2265 | }
2266 | .fa-podcast:before {
2267 | content: "\f2ce";
2268 | }
2269 | .fa-window-maximize:before {
2270 | content: "\f2d0";
2271 | }
2272 | .fa-window-minimize:before {
2273 | content: "\f2d1";
2274 | }
2275 | .fa-window-restore:before {
2276 | content: "\f2d2";
2277 | }
2278 | .fa-times-rectangle:before,
2279 | .fa-window-close:before {
2280 | content: "\f2d3";
2281 | }
2282 | .fa-times-rectangle-o:before,
2283 | .fa-window-close-o:before {
2284 | content: "\f2d4";
2285 | }
2286 | .fa-bandcamp:before {
2287 | content: "\f2d5";
2288 | }
2289 | .fa-grav:before {
2290 | content: "\f2d6";
2291 | }
2292 | .fa-etsy:before {
2293 | content: "\f2d7";
2294 | }
2295 | .fa-imdb:before {
2296 | content: "\f2d8";
2297 | }
2298 | .fa-ravelry:before {
2299 | content: "\f2d9";
2300 | }
2301 | .fa-eercast:before {
2302 | content: "\f2da";
2303 | }
2304 | .fa-microchip:before {
2305 | content: "\f2db";
2306 | }
2307 | .fa-snowflake-o:before {
2308 | content: "\f2dc";
2309 | }
2310 | .fa-superpowers:before {
2311 | content: "\f2dd";
2312 | }
2313 | .fa-wpexplorer:before {
2314 | content: "\f2de";
2315 | }
2316 | .fa-meetup:before {
2317 | content: "\f2e0";
2318 | }
2319 | .sr-only {
2320 | position: absolute;
2321 | width: 1px;
2322 | height: 1px;
2323 | padding: 0;
2324 | margin: -1px;
2325 | overflow: hidden;
2326 | clip: rect(0, 0, 0, 0);
2327 | border: 0;
2328 | }
2329 | .sr-only-focusable:active,
2330 | .sr-only-focusable:focus {
2331 | position: static;
2332 | width: auto;
2333 | height: auto;
2334 | margin: 0;
2335 | overflow: visible;
2336 | clip: auto;
2337 | }
2338 |
--------------------------------------------------------------------------------
/src/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/src/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/src/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/src/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/src/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/src/icons/mac/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/mac/icon.icns
--------------------------------------------------------------------------------
/src/icons/png/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/png/128x128.png
--------------------------------------------------------------------------------
/src/icons/png/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/png/16x16.png
--------------------------------------------------------------------------------
/src/icons/png/24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/png/24x24.png
--------------------------------------------------------------------------------
/src/icons/png/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/png/256x256.png
--------------------------------------------------------------------------------
/src/icons/png/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/png/32x32.png
--------------------------------------------------------------------------------
/src/icons/png/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/png/48x48.png
--------------------------------------------------------------------------------
/src/icons/png/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/png/512x512.png
--------------------------------------------------------------------------------
/src/icons/png/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/png/64x64.png
--------------------------------------------------------------------------------
/src/icons/png/96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/png/96x96.png
--------------------------------------------------------------------------------
/src/icons/win/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/icons/win/icon.ico
--------------------------------------------------------------------------------
/src/images/default/tray-running/iconTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/images/default/tray-running/iconTemplate.png
--------------------------------------------------------------------------------
/src/images/default/tray-running/iconTemplate@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/images/default/tray-running/iconTemplate@2x.png
--------------------------------------------------------------------------------
/src/images/default/tray/iconTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/images/default/tray/iconTemplate.png
--------------------------------------------------------------------------------
/src/images/default/tray/iconTemplate@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/images/default/tray/iconTemplate@2x.png
--------------------------------------------------------------------------------
/src/images/mac/tray-running/iconTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/images/mac/tray-running/iconTemplate.png
--------------------------------------------------------------------------------
/src/images/mac/tray-running/iconTemplate@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/images/mac/tray-running/iconTemplate@2x.png
--------------------------------------------------------------------------------
/src/images/mac/tray/iconTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/images/mac/tray/iconTemplate.png
--------------------------------------------------------------------------------
/src/images/mac/tray/iconTemplate@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/5d487e2ff7831eaf9c3c3c41ffc1c82172f7a626/src/images/mac/tray/iconTemplate@2x.png
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
21 |
22 |
23 |
24 |
34 |
35 | project = v"
41 | @update:resource="v => resource = v"
42 | @update:resource-type="v => resourceType = v"
43 | :resource="resource"
44 | :project="project"
45 | :projects="projects"
46 | :issues="issues"
47 | :merge-requests="mergeRequests"
48 | :resource-type="resourceType"
49 | :starting="starting"
50 | :stopping="stopping"
51 | :cancelling="cancelling"
52 | :running="running"
53 | :loading-projects="loadingProjects"
54 | :loading-resource="loadingResource">
55 |
56 |
57 |
58 |
59 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const electron = require('electron');
2 | const {app} = electron;
3 |
4 | if (require('electron-squirrel-startup')) app.quit();
5 |
6 | const Config = require('./writable-file-config');
7 | const Tasks = require('gitlab-time-tracker/src/include/tasks');
8 | const Base = require('gitlab-time-tracker/src/models/base');
9 | const Frame = require('gitlab-time-tracker/src/models/frame');
10 |
11 | const {dialog, Tray, BrowserWindow, Menu, ipcMain} = electron;
12 | const path = require('path');
13 | const events = require('events');
14 | const chokidar = require('chokidar');
15 | const os = require('os');
16 |
17 | const moment = require('moment');
18 | const log = require('electron-log');
19 | const pjson = require('../package.json');
20 |
21 | let gtt = new events.EventEmitter(),
22 | trayIcon = null,
23 | trayWindow = null,
24 | trayWindowSize = null,
25 | trayWindowOpen = false,
26 | trayPos = null,
27 | contextMenu = null,
28 | settingsWindow = null,
29 | aboutWindow = null,
30 | listWindow = null,
31 | debug = false;
32 |
33 | gtt._app = app;
34 | gtt._version = pjson.version;
35 | gtt._config = new Config(__dirname);
36 |
37 | gtt._api = new Base(gtt._config);
38 | gtt._tasks = new Tasks(gtt._config);
39 | gtt._paused = false;
40 | gtt._syncing = false;
41 | gtt._lastSync = false;
42 | gtt._watchers = {};
43 | gtt._unauthorized = false;
44 | gtt._offline = false;
45 | gtt._platform = {darwin: 'mac', linux: 'linux', win32: 'win'}[os.platform()];
46 | gtt._iconPath = path.join(__dirname, 'images/', gtt._platform == 'mac' ? 'mac' : 'default');
47 |
48 | // Fix app not closing on Mac
49 | app.on('window-all-closed', () => {
50 | if (process.platform != 'darwin') {
51 | app.quit();
52 | }
53 | });
54 |
55 | // Fix transparency issues on Linux
56 | app.disableHardwareAcceleration();
57 |
58 | // Single instance
59 | if (app.makeSingleInstance(() => {
60 | if (trayWindow) {
61 | trayWindow.show();
62 | trayWindow.focus();
63 | }
64 | })) {
65 | app.quit();
66 | process.exit();
67 | }
68 |
69 | app.on('ready', () => {
70 | setTimeout(() => {
71 | gtt._dump('Running on ' + gtt._platform);
72 | gtt.setTrayWindow();
73 | gtt.setTray();
74 | gtt.addApplicationMenu();
75 | gtt._watchers.config.add();
76 | gtt._watchers.frames.add();
77 | }, 100);
78 | });
79 |
80 | /**
81 | * Add application menu
82 | */
83 | gtt.addApplicationMenu = () => {
84 | let template = [{
85 | label: "gtt taskbar",
86 | submenu: [
87 | { label: "About gtt taskbar", selector: "orderFrontStandardAboutPanel:" },
88 | { type: "separator" },
89 | { label: "Quit", accelerator: "Command+Q", click: function() { app.quit(); }}
90 | ]}, {
91 | label: "Edit",
92 | submenu: [
93 | { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
94 | { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
95 | { type: "separator" },
96 | { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
97 | { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
98 | { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
99 | { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
100 | ]}
101 | ];
102 |
103 | Menu.setApplicationMenu(Menu.buildFromTemplate(template));
104 | };
105 |
106 | /**
107 | * Create and open the list window.
108 | */
109 | gtt.openListWindow = () => {
110 | if (listWindow) return listWindow.focus();
111 |
112 | listWindow = new BrowserWindow({
113 | width: 1000,
114 | height: 600,
115 | resizable: true,
116 | fullscreenable: false,
117 | maximizable: true,
118 | minimizable: true,
119 | icon: path.join(__dirname, '/icons/png/64x64.png')
120 | });
121 |
122 | listWindow.setMenu(null);
123 |
124 | listWindow.loadURL('file://' + __dirname + '/log.html');
125 | if (debug) listWindow.openDevTools();
126 |
127 | listWindow.on('closed', function () {
128 | listWindow = null;
129 | });
130 | };
131 |
132 | /**
133 | * Create and open the about window.
134 | */
135 | gtt.openAboutWindow = () => {
136 | if (aboutWindow) return aboutWindow.focus();
137 |
138 | aboutWindow = new BrowserWindow({
139 | width: 350,
140 | height: 350,
141 | resizable: false,
142 | fullscreenable: false,
143 | maximizable: false,
144 | minimizable: false,
145 | icon: path.join(__dirname, '/icons/png/64x64.png')
146 | });
147 |
148 | aboutWindow.setMenu(null);
149 |
150 | aboutWindow.loadURL('file://' + __dirname + '/test.html');
151 | if (debug) aboutWindow.openDevTools();
152 |
153 | aboutWindow.on('closed', function () {
154 | aboutWindow = null;
155 | });
156 | };
157 |
158 | /**
159 | * Create and open the settings window.
160 | */
161 | gtt.openSettingsWindow = () => {
162 | if (settingsWindow) return settingsWindow.focus();
163 |
164 | settingsWindow = new BrowserWindow({
165 | width: 450,
166 | height: 450,
167 | resizable: false,
168 | fullscreenable: false,
169 | maximizable: false,
170 | icon: path.join(__dirname, '/icons/png/64x64.png')
171 | });
172 |
173 | settingsWindow.setMenu(null);
174 |
175 | settingsWindow.loadURL('file://' + __dirname + '/settings.html');
176 | if (debug) settingsWindow.openDevTools();
177 |
178 | settingsWindow.on('closed', function () {
179 | settingsWindow = null;
180 | });
181 | };
182 |
183 | /**
184 | * Create and open the context menu.
185 | */
186 | gtt.openContextMenu = () => {
187 | contextMenu = Menu.buildFromTemplate([
188 | {
189 | label: gtt._syncing ? 'Syncing right now...' : `Last sync: ${gtt._lastSync ? gtt._lastSync.fromNow() : '-'}`,
190 | enabled: false
191 | },
192 | {
193 | label: gtt._paused ? 'Resume Syncing' : 'Pause Syncing',
194 | click() {
195 | gtt._paused = !gtt._paused;
196 | gtt.sync().catch(e => console.log(e));
197 | }
198 | },
199 | {
200 | type: 'separator'
201 | },
202 | {
203 | label: 'Preferences...',
204 | click: gtt.openSettingsWindow
205 | },
206 | {
207 | label: 'About',
208 | click: gtt.openAboutWindow
209 | },
210 | {
211 | type: 'separator'
212 | },
213 | {
214 | label: 'Quit',
215 | click: app.quit
216 | }
217 | ]);
218 |
219 | contextMenu.popup();
220 | };
221 |
222 | /**
223 | * Create and set the tray window.
224 | */
225 | gtt.setTrayWindow = () => {
226 | trayWindowSize = {
227 | w: 370,
228 | h: gtt._platform == 'win' ? 420 : 390,
229 | };
230 |
231 | trayWindow = new BrowserWindow({
232 | width: trayWindowSize.w,
233 | height: trayWindowSize.h,
234 | show: false,
235 | frame: false,
236 | resizable: false,
237 | transparent: true,
238 | skipTaskbar: true,
239 | icon: path.join(__dirname, '/icons/png/64x64.png')
240 | });
241 |
242 | trayWindow.loadURL('file://' + __dirname + '/index.html');
243 | if (debug) trayWindow.openDevTools();
244 |
245 | trayWindow.on('blur', function () {
246 | if (!debug) {
247 | trayWindow.hide();
248 | setTimeout(() => trayWindowOpen = false, 100);
249 | }
250 | });
251 |
252 | trayWindow.on('closed', function () {
253 | trayWindow = null;
254 | });
255 | };
256 |
257 | /**
258 | * Create and set the tray icon.
259 | */
260 | gtt.setTray = () => {
261 | if (gtt._platform == 'mac') app.dock.hide();
262 |
263 | trayIcon = new Tray(path.join(gtt._iconPath, '/tray/iconTemplate.png'));
264 |
265 | if (gtt._platform == 'linux') {
266 | let contextMenu = Menu.buildFromTemplate([
267 | {
268 | label: 'Open GTT', click: gtt.toggleTrayWindow
269 | },
270 | {label: 'Quit', click: app.quit}
271 | ]);
272 |
273 | trayIcon.setContextMenu(contextMenu);
274 | } else {
275 |
276 | trayIcon.on('click', function (event, bounds) {
277 | gtt.toggleTrayWindow(bounds);
278 | });
279 | }
280 | };
281 |
282 | /**
283 | * Toggle tray window.
284 | * @param bounds
285 | */
286 | gtt.toggleTrayWindow = bounds => {
287 | if (!debug && ((trayWindow && trayWindow.isVisible()) || trayWindowOpen)) {
288 | return;
289 | }
290 |
291 | let x, y, trayWindowBounds = trayWindow.getBounds();
292 |
293 | if (gtt._platform != 'linux') {
294 | x = (bounds.x + (bounds.width / 2)) - (trayWindowBounds.width / 2);
295 | y = bounds.y + 10;
296 |
297 | if (gtt._platform == 'win') {
298 | y = bounds.y - 403;
299 | }
300 | } else {
301 | x = electron.screen.getCursorScreenPoint().x - (trayWindowBounds.width / 2);
302 | y = 30;
303 | }
304 |
305 | trayWindow.setPosition(
306 | Math.ceil(x),
307 | Math.ceil(y)
308 | );
309 |
310 | trayWindowOpen = true;
311 | trayWindow.setVisibleOnAllWorkspaces(true);
312 | trayWindow.setSize(0, 0);
313 | trayWindow.show();
314 | trayWindow.setSize(trayWindowSize.w, trayWindowSize.h);
315 | trayWindow.setVisibleOnAllWorkspaces(false);
316 | };
317 |
318 | /**
319 | * Get current time monitoring status.
320 | * @returns {Promise}
321 | */
322 | gtt.status = () => {
323 | gtt._dump(`Queried status`);
324 | return gtt._tasks
325 | .status()
326 | .then(frames => {
327 | if (frames.length === 0) {
328 | trayIcon.setImage(path.join(gtt._iconPath, '/tray/iconTemplate.png'));
329 | return false;
330 | }
331 |
332 | trayIcon.setImage(path.join(gtt._iconPath, '/tray-running/iconTemplate.png'));
333 | return frames;
334 | });
335 | };
336 |
337 | /**
338 | * Get projects from GitLab.
339 | * @returns {Promise}
340 | */
341 | gtt.projects = () => {
342 | gtt._dump(`Queried projects`);
343 | return gtt._api.get(`projects?membership=true`)
344 | .then(projects => projects.body);
345 | };
346 |
347 | /**
348 | * Get issues for the given project from GitLab.
349 | * @param project
350 | * @returns {Promise}
351 | */
352 | gtt.issues = project => {
353 | if (!project) return new Promise(r => r(false));
354 | gtt._dump(`Queried issues for project ${project}`);
355 |
356 | let query = [], menuBar;
357 | if (!(menuBar = gtt._config.get('menubar')) || !menuBar.closed) query.push('state=opened');
358 |
359 | return gtt._api.get(`projects/${encodeURIComponent(project)}/issues?${query.join('&')}`)
360 | .then(issues => ({issues: issues.body, project}));
361 | };
362 |
363 | /**
364 | * Get merge requests for the given project from Gitlab.
365 | * @param project
366 | * @returns {Promise}
367 | */
368 | gtt.mergeRequests = project => {
369 | if (!project) return new Promise(r => r(false));
370 | gtt._dump(`Queried merge requests for project ${project}`);
371 |
372 | let query = [], menuBar;
373 | if (!(menuBar = gtt._config.get('menubar')) || !menuBar.closed) {
374 | query.push('state=opened');
375 | } else {
376 | query.push('state=all')
377 | }
378 |
379 | return gtt._api.get(`projects/${encodeURIComponent(project)}/merge_requests?${query.join('&')}`)
380 | .then(mergeRequests => ({mergeRequests: mergeRequests.body, project}));
381 | };
382 |
383 | /**
384 | * Sync time records to GitLab.
385 | * @returns {Promise}
386 | */
387 | gtt.sync = () => {
388 | if (gtt._paused) return new Promise(r => r(false));
389 | gtt._dump(`Sync started`);
390 | gtt._syncing = true;
391 |
392 | return gtt._tasks.syncInit()
393 | .then(() => gtt._tasks.syncResolve())
394 | .then(() => {
395 | gtt._syncing = false;
396 | gtt._lastSync = moment();
397 | gtt._send('gtt-last-sync', gtt._lastSync.toISOString());
398 |
399 | if (gtt._tasks.sync.frames.length === 0)
400 | return false;
401 |
402 | gtt._tasks.syncNotes().then(() => gtt._tasks.syncUpdate());
403 |
404 | return true;
405 | });
406 | };
407 |
408 | /**
409 | * Get the log
410 | * @returns {Promise}
411 | */
412 | gtt.log = () => {
413 | gtt._dump('Queried log');
414 | return gtt._tasks.log();
415 | };
416 |
417 | /**
418 | * (Re)load the config
419 | * @returns {writableFileConfig}
420 | */
421 | gtt.loadConfig = () => {
422 | gtt._dump(`Loaded config`);
423 | gtt._config = new Config(__dirname);
424 | gtt._api = new Base(gtt._config);
425 | return gtt._config;
426 | };
427 |
428 | /**
429 | * Write the config
430 | * @param config
431 | */
432 | gtt.writeConfig = (config) => {
433 | gtt._dump(`Config written`);
434 | gtt._watchers.config.remove();
435 | gtt._config.write(config, {url: null, token: null, dateFormat: null, timeFormat: null});
436 | gtt._watchers.config.add();
437 | };
438 |
439 | /**
440 | * Cache wrapper.
441 | */
442 | gtt.cache = {
443 | delete: (key) => gtt._config.cache.delete(key),
444 | get: (key) => gtt._config.cache.get(key),
445 | set: (key, value) => gtt._config.cache.set(key, value),
446 | };
447 |
448 | /**
449 | * Dump to console.
450 | *
451 | * @param msg
452 | * @private
453 | */
454 | gtt._dump = (msg) => {
455 | log.info(msg);
456 | };
457 |
458 | gtt._send = (key, val) => {
459 | if (debug) {
460 | gtt._dump(`ipc main send: ${key}, ${val}`);
461 | }
462 |
463 | if (trayWindow)
464 | trayWindow.webContents.send(key, val);
465 |
466 | if (listWindow)
467 | listWindow.webContents.send(key, val);
468 | };
469 |
470 | gtt._watchers.config = {
471 | add() {
472 | gtt._dump('Added config watcher');
473 | this.watcher = chokidar
474 | .watch(gtt._config.global, {
475 | ignoreInitial: true,
476 | awaitWriteFinish: {
477 | stabilityThreshold: 500,
478 | pollInterval: 100,
479 | },
480 | })
481 | .on('change', () => {
482 | gtt.loadConfig();
483 | gtt._unauthorized = false;
484 | gtt._send('gtt-config', gtt._config);
485 | });
486 | },
487 | remove() {
488 | if (!this.watcher) return;
489 | gtt._dump('Removed config watcher');
490 | this.watcher.close();
491 | }
492 | };
493 |
494 | gtt._watchers.frames = {
495 | add() {
496 | gtt._dump('Added frames watcher');
497 | this.watcher = chokidar
498 | .watch(path.join(gtt._config.frameDir, '*.json'), {ignoreInitial: true})
499 | .on('raw', (event, path) => {
500 | if (this.timeout) clearTimeout(this.timeout);
501 | this.timeout = setTimeout(() => {
502 | gtt._dump(`"${path}" changed.`);
503 | gtt.log().then(data => gtt._send('gtt-log', data));
504 | gtt.status().then(status => gtt._send('gtt-status', status));
505 | }, 100);
506 | });
507 | },
508 | remove() {
509 | if (!this.watcher) return;
510 | gtt._dump('Removed frames watcher');
511 | this.watcher.close();
512 | }
513 | };
514 |
515 | gtt._handleError = error => {
516 | if (error.statusCode === 401 && !gtt._unauthorized) {
517 | gtt._unauthorized = true;
518 | if (dialog.showMessageBox(null, {
519 | type: "warning",
520 | title: "Unauthorized",
521 | message: "Cannot connect to GitLab: unauthorized.",
522 | detail: "If this is the first time you start gtt, open the preferences and add your GitLab API token.",
523 | buttons: ["Go to preferences now", "I'll do it later"]
524 | }) === 0) gtt.openSettingsWindow();
525 | }
526 | };
527 |
528 | /**
529 | * IPC Listeners
530 | */
531 | ipcMain.on('gtt-config', event => {
532 | event.returnValue = gtt.loadConfig();
533 | });
534 | ipcMain.on('gtt-version', event => {
535 | event.returnValue = gtt._version;
536 | });
537 | ipcMain.on('gtt-platform', event => {
538 | event.returnValue = gtt._platform;
539 | });
540 |
541 | ipcMain.on('gtt-config-write', (event, config) => {
542 | gtt._unauthorized = false;
543 | gtt.writeConfig(config);
544 | });
545 |
546 | ipcMain.on('gtt-status', event => {
547 | gtt.status()
548 | .then(status => event.sender.send('gtt-status', status))
549 | .catch(e => console.log(e));
550 | });
551 |
552 | ipcMain.on('gtt-projects', event => {
553 | gtt.projects()
554 | .then(projects => event.sender.send('gtt-projects', projects))
555 | .catch(e => {
556 | event.sender.send('gtt-projects', false);
557 | gtt._handleError(e)
558 | });
559 | });
560 | ipcMain.on('gtt-issues', (event, project) => {
561 | gtt.issues(project)
562 | .then(issues => event.sender.send('gtt-issues', issues))
563 | .catch(e => {
564 | event.sender.send('gtt-issues', false);
565 | gtt._handleError(e)
566 | });
567 | });
568 | ipcMain.on('gtt-merge-requests', (event, project) => {
569 | gtt.mergeRequests(project)
570 | .then(mergeRequests => event.sender.send('gtt-merge-requests', mergeRequests))
571 | .catch(e => {
572 | event.sender.send('gtt-merge-requests', false);
573 | gtt._handleError(e)
574 | });
575 | });
576 | ipcMain.on('gtt-sync', event => {
577 | gtt.sync()
578 | .then(result => event.sender.send('gtt-sync', result))
579 | .catch(e => {
580 | event.sender.send('gtt-sync', false);
581 | gtt._handleError(e)
582 | });
583 | });
584 | ipcMain.on('gtt-start', (event, {project, type, id}) => {
585 | gtt._dump(`Started time monitoring for ${project} ${type} ${id}`);
586 | gtt._tasks.start(project, type, id)
587 | .then(frame => event.sender.send('gtt-start', true))
588 | .catch(e => console.log(e));
589 | });
590 | ipcMain.on('gtt-stop', event => {
591 | gtt._dump(`Stopped time monitoring`);
592 | gtt._tasks.stop()
593 | .then(frames => event.sender.send('gtt-stop', !!frames.length))
594 | .catch(e => console.log(e));
595 | });
596 | ipcMain.on('gtt-cancel', event => {
597 | gtt._dump(`Cancelled time monitoring`);
598 | gtt._tasks.cancel()
599 | .then(frames => event.sender.send('gtt-cancel', !!frames.length))
600 | .catch(e => console.log(e));
601 | });
602 | ipcMain.on('gtt-log', event => {
603 | gtt.log()
604 | .then(log => event.sender.send('gtt-log', log))
605 | .catch(e => console.log(e));
606 | });
607 | ipcMain.on('gtt-edit', (event, {frame}) => {
608 | gtt._dump(`Edited frame ${frame.id}`);
609 | frame.start = frame._start;
610 | frame.stop = frame._stop;
611 | Frame.fromJson(gtt._config, frame).write();
612 |
613 | setTimeout(() => {
614 | gtt.sync()
615 | .then(result => event.sender.send('gtt-sync', result))
616 | .catch(e => {
617 | event.sender.send('gtt-sync', false);
618 | gtt._handleError(e);
619 | });
620 | }, 500);
621 | });
622 | ipcMain.on('list-window', () => {
623 | gtt.openListWindow();
624 | });
625 | ipcMain.on('settings-window', () => {
626 | gtt.openSettingsWindow();
627 | });
628 | ipcMain.on('context-menu', () => {
629 | gtt.openContextMenu();
630 | });
631 | ipcMain.on('cache-get', (event, key) => {
632 | try {
633 | event.returnValue = gtt.cache.get(key);
634 | } catch(e) {
635 | gtt.cache.delete(key);
636 | event.returnValue = null;
637 | }
638 | });
639 | ipcMain.on('cache-set', (event, {key, data}) => {
640 | gtt.cache.set(key, data);
641 | });
642 |
643 | process.on('uncaughtException', function (e) {
644 | log.warn("error: " + JSON.stringify(e));
645 |
646 | if (dialog.showMessageBox(null, {
647 | type: "error",
648 | title: "Error",
649 | message: "gtt encountered an error and was terminated!",
650 | detail: "You can send an error report containing the exception message and a stack trace to help improve gtt. No personal information will be transfered.",
651 | buttons: ["Send error report", "Don't send report"]
652 | }) === 0 || gtt._config.get('error-reporting')) {
653 | console.log("Sending error report ...");
654 | var Raven = require('raven');
655 | Raven.config('https://62cb30e3c06945b7960d624de45ed322@sentry.io/1218774').install();
656 | Raven.captureException(e);
657 | console.log("done.");
658 | setTimeout(() => {
659 | app.quit();
660 | process.exit();
661 | }, 5000);
662 | return;
663 | }
664 |
665 | throw e;
666 | });
667 |
--------------------------------------------------------------------------------
/src/log.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | List of time records
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | {{ moment(day).format(dayFormat()) }}
68 | {{ human(log.times[day]) }}
69 |
70 |
71 |
72 |
77 |
78 | {{ human(parseInt(moment(frame.stop).diff(frame.start) / 1000)) }}
79 |
80 |
81 | {{ moment(frame.start).format(timeFormat()) }} - {{ moment(frame.stop).format(timeFormat())
82 | }}
83 |
84 |
85 |
86 |
88 |
90 |
91 |
92 | edit
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/src/render.js:
--------------------------------------------------------------------------------
1 | const {ipcRenderer, shell} = require('electron');
2 |
3 | window.state = {
4 | data: {
5 | running: false,
6 | config: null,
7 | projects: [],
8 | issues: {},
9 | mergeRequests: {},
10 | project: null,
11 | resource: null,
12 | resourceType: true,
13 | loadingProjects: false,
14 | loadingResource: false,
15 | loadingStatus: false,
16 | loadingLog: false,
17 | starting: false,
18 | stopping: false,
19 | cancelling: false,
20 | version: false,
21 | log: null,
22 | platform: null,
23 | lastSync: null,
24 | editing: false,
25 | currentEntry: null,
26 | entry: null,
27 | ready: false
28 | },
29 | ipc: {
30 | sync(key, args) {
31 | return ipcRenderer.sendSync(key, args);
32 | },
33 | send(key, args) {
34 | ipcRenderer.send(key, args);
35 | },
36 | on(key, func) {
37 | ipcRenderer.on(key, func);
38 | }
39 | },
40 | shell: {
41 | open(url) {
42 | shell.openExternal(url);
43 | }
44 | }
45 | };
46 |
47 | let cached = window.state.ipc.sync('cache-get', 'state');
48 | if (cached) window.state.data = Object.assign(window.state.data, cached, {ready: false});
49 |
--------------------------------------------------------------------------------
/src/settings.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Preferences
6 |
7 |
8 |
9 |
10 |
11 |
12 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | About gtt taskbar
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
gtt taskbar
16 | GitLab Time Tracker Taskbar/Menubar application
17 | Version {{ version }}
18 |
19 |
20 |
21 |
22 |
Created by Kris Siepert
23 | Click here for contact and support.
24 |
25 |
26 |
27 |
Uses the following Open Source Software:
28 |
29 |
30 | Electron by GitHub Inc. - License
32 |
33 | moment by JS Foundation and other contributors - License
35 |
36 | vue by Evan You - License
37 |
38 | vue-resource by steffans - License
40 |
41 | vue-select by Jeff Safal & vue-select contributors - License
43 |
44 | vue-js-toggle-button by Yev Vlasenko - License
46 |
47 | milligram-stylus by CJ Patoilo - License
49 |
50 | stylus by Automattic - License
52 |
53 | stylus-loader by Kyle Robinson Young - License
55 |
56 | laravel-mix by Jeffrey Way - License
58 |
59 |
60 | chokidar by Paul Miller & Elan Shanker - License
62 |
63 |
64 | vuejs-datetimepicker by Tosin Adeoye License
66 |
67 | GitLab Time Tracker by Kris Siepert - License
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/src/writable-file-config.js:
--------------------------------------------------------------------------------
1 | const fileConfig = require('gitlab-time-tracker/src/include/file-config');
2 | const yaml = require('write-yaml');
3 | const _ = require('underscore');
4 |
5 | class writableFileConfig extends fileConfig {
6 | /**
7 | *
8 | * @param config
9 | * @param fields
10 | */
11 | write(config, fields = {}) {
12 | let original = Object.assign(fields, this.localExists() ? this.parseLocal() : this.parseGlobal());
13 | let write = {};
14 |
15 | _.each(original, (val, key) => {
16 | write[key] = config.data[key] ? this.data[key] = config.data[key] : this.data[key];
17 | });
18 |
19 | return new Promise((resolve, reject) => {
20 |
21 | yaml.sync(this.global, write, err => reject(err));
22 | resolve();
23 | });
24 | }
25 | }
26 |
27 | module.exports = writableFileConfig;
--------------------------------------------------------------------------------
/webpack.mix.js:
--------------------------------------------------------------------------------
1 | let mix = require('laravel-mix');
2 |
3 | /*
4 | |--------------------------------------------------------------------------
5 | | Mix Asset Management
6 | |--------------------------------------------------------------------------
7 | |
8 | | Mix provides a clean, fluent API for defining some Webpack build steps
9 | | for your Laravel application. By default, we are compiling the Sass
10 | | file for your application, as well as bundling up your JS files.
11 | |
12 | */
13 |
14 | mix.stylus('resources/assets/stylus/app.styl', 'src/build')
15 | .js('resources/assets/js/app.js', 'src/build')
16 | .setPublicPath('src/build')
17 | .options({
18 | processCssUrls: false,
19 | });
20 |
--------------------------------------------------------------------------------