├── .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 | ![gtt](https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/master/resources/images/preview/preview.png) 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 | ![Linux, Mac OS, Windows](https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker-taskbar/master/resources/images/preview/os.png) 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 | 17 | 18 | -------------------------------------------------------------------------------- /resources/assets/js/components/track.vue: -------------------------------------------------------------------------------- 1 | 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 | 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 |
14 | 17 | 18 | 19 |
20 | 21 |
22 |
23 | 24 |
25 | 27 | 28 | 29 | 31 | 32 | 33 |
34 |
35 | 55 |
56 | 57 |
58 | 59 |
60 | 63 | 64 | 65 |
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 |
73 | {{ frame.project }} {{ frame.resource.type }} #{{ 75 | frame.resource.id }} 76 |
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 |
13 |
14 |
15 |
16 |
17 |
18 | 19 |
20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 |
28 | 29 | You can create a personal access token in your profile settings. 32 | 33 |
34 |
35 | 36 |
37 |
38 | 39 | More about date formats here. 40 |
41 |
42 | 43 |
44 |
45 | 46 | More about time formats here. 47 |
48 |
49 |
50 |
51 |
52 |
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 | --------------------------------------------------------------------------------