├── .codeclimate.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc.yml ├── .gitignore ├── .hound.yml ├── .node-version ├── .overcommit.yml ├── .ruby-version ├── .travis.yml ├── CHANGELOG ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── app ├── app.js ├── assets │ ├── css │ │ ├── app.css │ │ ├── photon.css │ │ └── photon.min.css │ ├── fonts │ │ ├── photon-entypo.eot │ │ ├── photon-entypo.svg │ │ ├── photon-entypo.ttf │ │ └── photon-entypo.woff │ └── images │ │ ├── background.png │ │ ├── icon.png │ │ └── icons.png ├── background.js ├── env.js ├── helpers │ ├── app_menu_template.js │ ├── context_menu.js │ ├── dev_menu_template.js │ ├── external_links.js │ └── window.js ├── package.json ├── pages │ ├── app.html │ └── success.html ├── src │ ├── controllers │ │ ├── auth.js │ │ └── success.js │ └── services │ │ └── uber.js └── test │ ├── bootstrap.spec.js │ ├── environment.spec.js │ └── test_helper.js ├── config ├── env_development.json ├── env_production.json └── env_test.json ├── gulpfile.js ├── package.json ├── resources ├── banner.png ├── bitcoin-address.png ├── icon.png ├── linux │ ├── DEBIAN │ │ └── control │ └── app.desktop ├── logo.png ├── osx │ ├── Info.plist │ ├── appdmg.json │ ├── child.plist │ ├── dmg-background.png │ ├── dmg-background@2x.png │ ├── dmg-icon.icns │ ├── helper_apps │ │ ├── Info EH.plist │ │ ├── Info NP.plist │ │ └── Info.plist │ ├── icon.icns │ └── parent.plist ├── screenshot.png └── windows │ ├── icon.ico │ ├── installer.nsi │ ├── setup-banner.bmp │ └── setup-icon.ico ├── script ├── deploy.sh ├── install.sh ├── keychain.sh ├── postinstall.sh └── test.sh └── tasks ├── build ├── build.js ├── bundle.js └── generate_spec_imports.js ├── install_native_module.js ├── rebuild_native_modules.js ├── release ├── linux.js ├── osx.js ├── release.js └── windows.js ├── start.js └── utils.js /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | eslint: 2 | enabled: true 3 | config: 4 | config: .eslintrc.yml 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.json] 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | app/node_modules 4 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | root: true 3 | rules: {} 4 | env: 5 | es6: true 6 | amd: true 7 | node: true 8 | browser: true 9 | mocha: true 10 | extends: "eslint:recommended" 11 | ecmaFeatures: 12 | sourceType: "module" 13 | parserOptions: 14 | sourceType: "module" 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | release-message.md 5 | Thumbs.db 6 | *.autogenerated 7 | /build/ 8 | /releases/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | eslint: 2 | enabled: true 3 | fail_on_violations: true 4 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 6.3.0 2 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | CommitMsg: 2 | SingleLineSubject: 3 | enabled: true 4 | MessageFormat: 5 | enabled: false 6 | RussianNovel: 7 | enabled: true 8 | HardTabs: 9 | enabled: true 10 | EmptyMessage: 11 | enabled: true 12 | CapitalizedSubject: 13 | enabled: true 14 | TrailingPeriod: 15 | enabled: true 16 | PreCommit: 17 | AuthorEmail: 18 | enabled: true 19 | AuthorName: 20 | enabled: true 21 | BrokenSymlinks: 22 | enabled: true 23 | CaseConflicts: 24 | enabled: true 25 | MergeConflicts: 26 | enabled: true 27 | ShellCheck: 28 | enabled: true 29 | JsonSyntaxSyntax: 30 | enabled: true 31 | YamlSyntax: 32 | enabled: true 33 | TravisLint: 34 | enabled: true 35 | EsLint: 36 | enabled: true 37 | command: ["eslint", "--config .eslintrc.yml", "--ignore-path .eslintignore", "."] 38 | PostCommit: 39 | GitGuilt: 40 | enabled: false 41 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | sudo: false 3 | osx_image: xcode7.3 4 | env: 5 | - NODE_VERSION="6.3.0" 6 | cache: 7 | directories: 8 | - node_modules 9 | - app/node_modules 10 | install: 11 | - script/install.sh 12 | script: 13 | - npm test 14 | before_deploy: 15 | - script/keychain.sh 16 | - script/deploy.sh 17 | deploy: 18 | provider: releases 19 | api_key: 20 | secure: niIOlK6vmmH1VRDICCr+swpNmJ4Lo70/8eipPvxNCwp74S4ubqSfoxLmibWgDCCrkNZFXLLOqy515AOee20JcECEPdh+nWA/sF9eRyfGgtCrmfi+B3LlDxvJoR37e9wvoVSHkqV8VcCa0a3r3xkCkYAp3yxt2U/08x4PR/qS7AqaYVDrzJgniegLjwGm7BQNwaB87gW0It5TOTGg3/nVsnCFPQI/cyza/3TARFSsrOUtX4/mjtOhrbQyrJUkRsoC8SXYUKd9BX16jax1rPZPWMpOqGNJ6eug+5DU+OmirK+eUbX6rV3e1yEtnlxKqJ0bpE2sh9b/ZgZnFZC73sLqS0oquqm9NnmNKul1RXaxLuP7qoPgO8uQ2ekBXeDqMIB6mUNlLQiH5MEZv+6o9QLDFmF/6SVXLwe5wXLTceTbTcC11q8B69Stvguj0WdidYsnNEanrxdZy2vGjK54YLy1iMriB4Cen+XDOcSlWwqLevVVxvMWLRV+Up8RP0px9pqDFiVBs7wxDKrMfP/Ru2X/UtJuPRmXkeY6gq6Nw9j9DqSV2I1YCxpEi/dxyCXYRI0w8zkV6e1R6SiXGHTuI+jEsJReD9Bblj8Ne0rNfeMXNGKNKE4CTlxIUDOMyTz9QWCrPjlh62c7T5tYqdgsu40alidUfqokyeptQZDqaehZ0X8= 21 | file: "releases/ubauth-darwin-x64.dmg" 22 | skip_cleanup: true 23 | overwrite: true 24 | on: 25 | tags: true 26 | after_deploy: 27 | - App deployed to GitHub releases successfully. 28 | addons: 29 | code_climate: 30 | repo_token: 46f180c24d35db4d6b082aa5511a76d7f00af72909889d96cd6e8ed41a099e79 31 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | v1.0.0: 2 | date: 2016-07-24 3 | changes: 4 | - Initial release. 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source "https://rubygems.org" 3 | ruby "2.3.1" 4 | 5 | gem "rake" 6 | gem "overcommit" 7 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | childprocess (0.5.9) 5 | ffi (~> 1.0, >= 1.0.11) 6 | ffi (1.9.14) 7 | iniparse (1.4.2) 8 | overcommit (0.34.2) 9 | childprocess (~> 0.5.8) 10 | iniparse (~> 1.4) 11 | rake (11.2.2) 12 | 13 | PLATFORMS 14 | ruby 15 | 16 | DEPENDENCIES 17 | overcommit 18 | rake 19 | 20 | RUBY VERSION 21 | ruby 2.3.1p112 22 | 23 | BUNDLED WITH 24 | 1.12.5 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2016 Christopher EnyTC 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
UbAuth

2 | 3 | > A desktop app to help developers create access tokens for Uber applications with OAuth 2.0. 4 | 5 | ## Status 6 | 7 | # [![Build Status](https://img.shields.io/travis/chrisenytc/ubauth/master.svg?maxAge=2592000g)](http://travis-ci.org/chrisenytc/ubauth) [![Github All Releases](https://img.shields.io/github/downloads/chrisenytc/ubauth/total.svg?maxAge=2592000)](https://github.com/chrisenytc/ubauth/releases) [![Maintenance](https://img.shields.io/maintenance/yes/2017.svg?maxAge=2592000)]() [![GitHub release](https://img.shields.io/github/release/chrisenytc/ubauth.svg?maxAge=2592000)]() [![Code Climate](https://codeclimate.com/github/chrisenytc/ubauth/badges/gpa.svg)](https://codeclimate.com/github/chrisenytc/ubauth) [![License](https://img.shields.io/github/license/chrisenytc/ubauth.svg?maxAge=2592000)](https://github.com/chrisenytc/ubauth/blob/master/LICENSE) [![Twitter Follow](https://img.shields.io/twitter/follow/chrisenytc.svg?style=social&label=Follow&maxAge=2592000)](http://twitter.com/chrisenytc) [![Twitter URL](https://img.shields.io/twitter/url/http/shields.io.svg?style=social&maxAge=2592000)](https://twitter.com/intent/tweet?text=Awesome%20https://github.com/chrisenytc/ubauth%20via%20@chrisenytc) 8 | 9 | ## Getting Started 10 | 11 | 1º Clone ubauth repo 12 | 13 | ```bash 14 | $ git clone git@github.com:chrisenytc/ubauth.git 15 | ``` 16 | 17 | 2º Enter in ubauth directory 18 | ```bash 19 | $ cd ubauth 20 | ``` 21 | 22 | 3º Install dependencies 23 | 24 | ```bash 25 | $ npm install 26 | ``` 27 | 28 | 4º Run app 29 | 30 | ```bash 31 | $ npm start 32 | ``` 33 | 34 | 5º Test the app 35 | 36 | ```bash 37 | $ npm test 38 | ``` 39 | 40 | 6º Making releases 41 | 42 | ```bash 43 | $ npm run release -- --sign identity_here 44 | ``` 45 | 46 | ## Screenshot 47 | 48 | [![Screenshot](resources/screenshot.png)](https://raw.githubusercontent.com/chrisenytc/ubauth/master/resources/screenshot.png) 49 | 50 | ## Downloads 51 | 52 | **UbAuth** is available for macOS. Download [here](https://github.com/chrisenytc/ubauth/releases/latest). 53 | 54 | ## Donate 55 | 56 | If **UbAuth** was helpful for you, send a donation as a thank you. :) 57 | 58 | ![Bitcoin](resources/bitcoin-address.png) 59 | 60 | **Bitcoin Adddress**: `3QbTQcSfWAUntPwTrWNQ3aHYYmvJS5HoUY` 61 | 62 | ## Contributing 63 | 64 | Bug reports and pull requests are welcome on GitHub at [https://github.com/chrisenytc/ubauth](https://github.com/chrisenytc/ubauth). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 65 | 66 | 1. Fork it [chrisenytc/ubauth](https://github.com/chrisenytc/ubauth/fork) 67 | 2. Create your feature branch (`git checkout -b my-new-feature`) 68 | 3. Commit your changes (`git commit -am "Add some feature"`) 69 | 4. Push to the branch (`git push origin my-new-feature`) 70 | 5. Create new Pull Request 71 | 72 | ## Support 73 | If you have any problem or suggestion please open an issue [here](https://github.com/chrisenytc/ubauth/issues). 74 | 75 | ## License 76 | 77 | Check [here](LICENSE). 78 | -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/app/app.js -------------------------------------------------------------------------------- /app/assets/css/app.css: -------------------------------------------------------------------------------- 1 | button, input, p { 2 | -webkit-app-region: no-drag; 3 | } 4 | 5 | label { 6 | color: #ffffff; 7 | } 8 | 9 | .titlebar { 10 | -webkit-user-select: none; 11 | -webkit-app-region: drag; 12 | } 13 | 14 | .background { 15 | background: url(../images/background.png) no-repeat center center fixed; 16 | -webkit-background-size: cover; 17 | -moz-background-size: cover; 18 | -o-background-size: cover; 19 | background-size: cover; 20 | } 21 | 22 | .css-form input.ng-invalid.ng-touched { 23 | background-color: #FA787E; 24 | } 25 | 26 | .css-form input.ng-valid.ng-touched { 27 | background-color: #78FA89; 28 | } 29 | 30 | .text-danger { 31 | color: #c81717 32 | } 33 | 34 | .alert-message { 35 | display: block; 36 | padding: 13px 12px 12px; 37 | font-weight: bold; 38 | font-size: 14px; 39 | color: white; 40 | background-color: #2ba6cb; 41 | border: 1px solid rgba(0, 0, 0, 0.1); 42 | margin-bottom: 12px; 43 | -webkit-border-radius: 3px; 44 | -moz-border-radius: 3px; 45 | -ms-border-radius: 3px; 46 | -o-border-radius: 3px; 47 | border-radius: 3px; 48 | text-shadow: 0 -1px rgba(0, 0, 0, 0.3); 49 | position: relative; 50 | } 51 | 52 | .alert-message .box-icon { 53 | display: block; 54 | float: left; 55 | background-image: url('../images/icons.png'); 56 | width: 30px; 57 | height: 25px; 58 | margin-top: -2px; 59 | background-position: -8px -8px; 60 | } 61 | 62 | .alert-message p { 63 | margin: 0px; 64 | } 65 | 66 | .alert-message.success { 67 | background-color: #5da423; 68 | color: #fff; 69 | text-shadow: 0 -1px rgba(0, 0, 0, 0.3); 70 | } 71 | 72 | .alert-message.success .box-icon { 73 | background-position: -48px -8px; 74 | } 75 | 76 | .alert-message.warning { 77 | background-color: #e3b000; 78 | color: #fff; 79 | text-shadow: 0 -1px rgba(0, 0, 0, 0.3); 80 | } 81 | 82 | .alert-message.warning .box-icon { 83 | background-position: -88px -8px; 84 | } 85 | 86 | .alert-message.error { 87 | background-color: #c60f13; 88 | color: #fff; 89 | text-shadow: 0 -1px rgba(0, 0, 0, 0.3); 90 | } 91 | 92 | .alert-message.error .box-icon { 93 | background-position: -128px -8px; 94 | } 95 | 96 | .alert-message a.close { 97 | color: #333; 98 | position: absolute; 99 | right: 4px; 100 | top: -1px; 101 | font-size: 17px; 102 | opacity: 0.2; 103 | padding: 4px; 104 | } 105 | 106 | .alert-message a.close:hover, .alert-box a.close:focus { 107 | opacity: 0.4; 108 | } 109 | -------------------------------------------------------------------------------- /app/assets/css/photon.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * ===================================================== 3 | * Photon v0.1.2 4 | * Copyright 2015 Connor Sears 5 | * Licensed under MIT (https://github.com/connors/proton/blob/master/LICENSE) 6 | * 7 | * v0.1.2 designed by @connors. 8 | * ===================================================== 9 | */ 10 | 11 | @charset "UTF-8"; 12 | audio, 13 | canvas, 14 | progress, 15 | video { 16 | vertical-align: baseline; 17 | } 18 | 19 | audio:not([controls]) { 20 | display: none; 21 | } 22 | 23 | a:active, 24 | a:hover { 25 | outline: 0; 26 | } 27 | 28 | abbr[title] { 29 | border-bottom: 1px dotted; 30 | } 31 | 32 | b, 33 | strong { 34 | font-weight: bold; 35 | } 36 | 37 | dfn { 38 | font-style: italic; 39 | } 40 | 41 | h1 { 42 | font-size: 2em; 43 | margin: 0.67em 0; 44 | } 45 | 46 | small { 47 | font-size: 80%; 48 | } 49 | 50 | sub, 51 | sup { 52 | font-size: 75%; 53 | line-height: 0; 54 | position: relative; 55 | vertical-align: baseline; 56 | } 57 | 58 | sup { 59 | top: -0.5em; 60 | } 61 | 62 | sub { 63 | bottom: -0.25em; 64 | } 65 | 66 | pre { 67 | overflow: auto; 68 | } 69 | 70 | code, 71 | kbd, 72 | pre, 73 | samp { 74 | font-family: monospace, monospace; 75 | font-size: 1em; 76 | } 77 | 78 | button, 79 | input, 80 | optgroup, 81 | select, 82 | textarea { 83 | color: inherit; 84 | font: inherit; 85 | margin: 0; 86 | } 87 | 88 | input[type="number"]::-webkit-inner-spin-button, 89 | input[type="number"]::-webkit-outer-spin-button { 90 | height: auto; 91 | } 92 | 93 | input[type="search"] { 94 | -webkit-appearance: textfield; 95 | box-sizing: content-box; 96 | } 97 | 98 | input[type="search"]::-webkit-search-cancel-button, 99 | input[type="search"]::-webkit-search-decoration { 100 | -webkit-appearance: none; 101 | } 102 | 103 | fieldset { 104 | border: 1px solid #c0c0c0; 105 | margin: 0 2px; 106 | padding: 0.35em 0.625em 0.75em; 107 | } 108 | 109 | legend { 110 | border: 0; 111 | padding: 0; 112 | } 113 | 114 | table { 115 | border-collapse: collapse; 116 | border-spacing: 0; 117 | } 118 | 119 | td, 120 | th { 121 | padding: 0; 122 | } 123 | 124 | * { 125 | cursor: default; 126 | -webkit-user-drag: text; 127 | -webkit-user-select: none; 128 | -webkit-box-sizing: border-box; 129 | box-sizing: border-box; 130 | } 131 | 132 | html { 133 | height: 100%; 134 | width: 100%; 135 | overflow: hidden; 136 | } 137 | 138 | body { 139 | height: 100%; 140 | padding: 0; 141 | margin: 0; 142 | font-family: system, -apple-system, ".SFNSDisplay-Regular", "Helvetica Neue", Helvetica, "Segoe UI", sans-serif; 143 | font-size: 13px; 144 | line-height: 1.6; 145 | color: #333; 146 | background-color: transparent; 147 | } 148 | 149 | hr { 150 | margin: 15px 0; 151 | overflow: hidden; 152 | background: transparent; 153 | border: 0; 154 | border-bottom: 1px solid #ddd; 155 | } 156 | 157 | h1, h2, h3, h4, h5, h6 { 158 | margin-top: 20px; 159 | margin-bottom: 10px; 160 | font-weight: 500; 161 | white-space: nowrap; 162 | overflow: hidden; 163 | text-overflow: ellipsis; 164 | } 165 | 166 | h1 { 167 | font-size: 36px; 168 | } 169 | 170 | h2 { 171 | font-size: 30px; 172 | } 173 | 174 | h3 { 175 | font-size: 24px; 176 | } 177 | 178 | h4 { 179 | font-size: 18px; 180 | } 181 | 182 | h5 { 183 | font-size: 14px; 184 | } 185 | 186 | h6 { 187 | font-size: 12px; 188 | } 189 | 190 | .window { 191 | position: absolute; 192 | top: 0; 193 | right: 0; 194 | bottom: 0; 195 | left: 0; 196 | display: flex; 197 | flex-direction: column; 198 | background-color: #fff; 199 | } 200 | 201 | .window-content { 202 | position: relative; 203 | overflow-y: auto; 204 | display: flex; 205 | flex: 1; 206 | } 207 | 208 | .selectable-text { 209 | cursor: text; 210 | -webkit-user-select: text; 211 | } 212 | 213 | .text-center { 214 | text-align: center; 215 | } 216 | 217 | .text-right { 218 | text-align: right; 219 | } 220 | 221 | .text-left { 222 | text-align: left; 223 | } 224 | 225 | .pull-left { 226 | float: left; 227 | } 228 | 229 | .pull-right { 230 | float: right; 231 | } 232 | 233 | .padded { 234 | padding: 10px; 235 | } 236 | 237 | .padded-less { 238 | padding: 5px; 239 | } 240 | 241 | .padded-more { 242 | padding: 20px; 243 | } 244 | 245 | .padded-vertically { 246 | padding-top: 10px; 247 | padding-bottom: 10px; 248 | } 249 | 250 | .padded-vertically-less { 251 | padding-top: 5px; 252 | padding-bottom: 5px; 253 | } 254 | 255 | .padded-vertically-more { 256 | padding-top: 20px; 257 | padding-bottom: 20px; 258 | } 259 | 260 | .padded-horizontally { 261 | padding-right: 10px; 262 | padding-left: 10px; 263 | } 264 | 265 | .padded-horizontally-less { 266 | padding-right: 5px; 267 | padding-left: 5px; 268 | } 269 | 270 | .padded-horizontally-more { 271 | padding-right: 20px; 272 | padding-left: 20px; 273 | } 274 | 275 | .padded-top { 276 | padding-top: 10px; 277 | } 278 | 279 | .padded-top-less { 280 | padding-top: 5px; 281 | } 282 | 283 | .padded-top-more { 284 | padding-top: 80px; 285 | } 286 | 287 | .padded-bottom { 288 | padding-bottom: 10px; 289 | } 290 | 291 | .padded-bottom-less { 292 | padding-bottom: 5px; 293 | } 294 | 295 | .padded-bottom-more { 296 | padding-bottom: 20px; 297 | } 298 | 299 | .sidebar { 300 | background-color: #f5f5f4; 301 | } 302 | 303 | .draggable { 304 | -webkit-app-region: drag; 305 | } 306 | 307 | .clearfix:before, .clearfix:after { 308 | display: table; 309 | content: " "; 310 | } 311 | .clearfix:after { 312 | clear: both; 313 | } 314 | 315 | .btn { 316 | display: inline-block; 317 | padding: 3px 8px; 318 | margin-bottom: 0; 319 | font-size: 12px; 320 | line-height: 1.4; 321 | text-align: center; 322 | white-space: nowrap; 323 | vertical-align: middle; 324 | cursor: default; 325 | background-image: none; 326 | border: 1px solid transparent; 327 | border-radius: 4px; 328 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.06); 329 | -webkit-app-region: no-drag; 330 | } 331 | .btn:focus { 332 | outline: none; 333 | box-shadow: none; 334 | } 335 | 336 | .btn-mini { 337 | padding: 2px 6px; 338 | } 339 | 340 | .btn-large { 341 | padding: 12px 24px; 342 | } 343 | 344 | .btn-form { 345 | width: 100% 346 | } 347 | 348 | .btn-default { 349 | color: #333; 350 | border-top-color: #c2c0c2; 351 | border-right-color: #c2c0c2; 352 | border-bottom-color: #a19fa1; 353 | border-left-color: #c2c0c2; 354 | background-color: #fcfcfc; 355 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fcfcfc), color-stop(100%, #f1f1f1)); 356 | background-image: -webkit-linear-gradient(top, #fcfcfc 0%, #f1f1f1 100%); 357 | background-image: linear-gradient(to bottom, #fcfcfc 0%, #f1f1f1 100%); 358 | } 359 | .btn-default:active { 360 | background-color: #ddd; 361 | background-image: none; 362 | } 363 | 364 | .btn-primary, 365 | .btn-positive, 366 | .btn-negative, 367 | .btn-warning { 368 | color: #fff; 369 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); 370 | } 371 | 372 | .btn-primary { 373 | border-color: #388df8; 374 | border-bottom-color: #0866dc; 375 | background-color: #6eb4f7; 376 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #6eb4f7), color-stop(100%, #1a82fb)); 377 | background-image: -webkit-linear-gradient(top, #6eb4f7 0%, #1a82fb 100%); 378 | background-image: linear-gradient(to bottom, #6eb4f7 0%, #1a82fb 100%); 379 | } 380 | .btn-primary:active { 381 | background-color: #3e9bf4; 382 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #3e9bf4), color-stop(100%, #0469de)); 383 | background-image: -webkit-linear-gradient(top, #3e9bf4 0%, #0469de 100%); 384 | background-image: linear-gradient(to bottom, #3e9bf4 0%, #0469de 100%); 385 | } 386 | 387 | .btn-positive { 388 | border-color: #29a03b; 389 | border-bottom-color: #248b34; 390 | background-color: #5bd46d; 391 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bd46d), color-stop(100%, #29a03b)); 392 | background-image: -webkit-linear-gradient(top, #5bd46d 0%, #29a03b 100%); 393 | background-image: linear-gradient(to bottom, #5bd46d 0%, #29a03b 100%); 394 | } 395 | .btn-positive:active { 396 | background-color: #34c84a; 397 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #34c84a), color-stop(100%, #248b34)); 398 | background-image: -webkit-linear-gradient(top, #34c84a 0%, #248b34 100%); 399 | background-image: linear-gradient(to bottom, #34c84a 0%, #248b34 100%); 400 | } 401 | 402 | .btn-negative { 403 | border-color: #fb2f29; 404 | border-bottom-color: #fb1710; 405 | background-color: #fd918d; 406 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fd918d), color-stop(100%, #fb2f29)); 407 | background-image: -webkit-linear-gradient(top, #fd918d 0%, #fb2f29 100%); 408 | background-image: linear-gradient(to bottom, #fd918d 0%, #fb2f29 100%); 409 | } 410 | .btn-negative:active { 411 | background-color: #fc605b; 412 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fc605b), color-stop(100%, #fb1710)); 413 | background-image: -webkit-linear-gradient(top, #fc605b 0%, #fb1710 100%); 414 | background-image: linear-gradient(to bottom, #fc605b 0%, #fb1710 100%); 415 | } 416 | 417 | .btn-warning { 418 | border-color: #fcaa0e; 419 | border-bottom-color: #ee9d02; 420 | background-color: #fece72; 421 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fece72), color-stop(100%, #fcaa0e)); 422 | background-image: -webkit-linear-gradient(top, #fece72 0%, #fcaa0e 100%); 423 | background-image: linear-gradient(to bottom, #fece72 0%, #fcaa0e 100%); 424 | } 425 | .btn-warning:active { 426 | background-color: #fdbc40; 427 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fdbc40), color-stop(100%, #ee9d02)); 428 | background-image: -webkit-linear-gradient(top, #fdbc40 0%, #ee9d02 100%); 429 | background-image: linear-gradient(to bottom, #fdbc40 0%, #ee9d02 100%); 430 | } 431 | 432 | .btn .icon { 433 | float: left; 434 | width: 14px; 435 | height: 14px; 436 | margin-top: 1px; 437 | margin-bottom: 1px; 438 | color: #737475; 439 | font-size: 14px; 440 | line-height: 1; 441 | } 442 | 443 | .btn .icon-text { 444 | margin-right: 5px; 445 | } 446 | 447 | .btn-dropdown:after { 448 | font-family: "photon-entypo"; 449 | margin-left: 5px; 450 | content: ""; 451 | } 452 | 453 | .btn-group { 454 | position: relative; 455 | display: inline-block; 456 | vertical-align: middle; 457 | -webkit-app-region: no-drag; 458 | } 459 | .btn-group .btn { 460 | position: relative; 461 | float: left; 462 | } 463 | .btn-group .btn:focus, .btn-group .btn:active { 464 | z-index: 2; 465 | } 466 | .btn-group .btn.active { 467 | z-index: 3; 468 | } 469 | 470 | .btn-group .btn + .btn, 471 | .btn-group .btn + .btn-group, 472 | .btn-group .btn-group + .btn, 473 | .btn-group .btn-group + .btn-group { 474 | margin-left: -1px; 475 | } 476 | .btn-group > .btn:first-child { 477 | border-top-right-radius: 0; 478 | border-bottom-right-radius: 0; 479 | } 480 | .btn-group > .btn:last-child { 481 | border-top-left-radius: 0; 482 | border-bottom-left-radius: 0; 483 | } 484 | .btn-group > .btn:not(:first-child):not(:last-child) { 485 | border-radius: 0; 486 | } 487 | .btn-group .btn + .btn { 488 | border-left: 1px solid #c2c0c2; 489 | } 490 | .btn-group .btn + .btn.active { 491 | border-left: 0; 492 | } 493 | .btn-group .active { 494 | color: #fff; 495 | border: 1px solid transparent; 496 | background-color: #6d6c6d; 497 | background-image: none; 498 | } 499 | .btn-group .active .icon { 500 | color: #fff; 501 | } 502 | 503 | .toolbar { 504 | min-height: 22px; 505 | box-shadow: inset 0 1px 0 #f5f4f5; 506 | background-color: #e8e6e8; 507 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #e8e6e8), color-stop(100%, #d1cfd1)); 508 | background-image: -webkit-linear-gradient(top, #e8e6e8 0%, #d1cfd1 100%); 509 | background-image: linear-gradient(to bottom, #e8e6e8 0%, #d1cfd1 100%); 510 | } 511 | .toolbar:before, .toolbar:after { 512 | display: table; 513 | content: " "; 514 | } 515 | .toolbar:after { 516 | clear: both; 517 | } 518 | 519 | .toolbar-header { 520 | border-bottom: 1px solid #c2c0c2; 521 | } 522 | .toolbar-header .title { 523 | margin-top: 1px; 524 | } 525 | 526 | .toolbar-footer { 527 | border-top: 1px solid #c2c0c2; 528 | -webkit-app-region: drag; 529 | } 530 | 531 | .title { 532 | margin: 0; 533 | font-size: 12px; 534 | font-weight: 400; 535 | text-align: center; 536 | color: #555; 537 | cursor: default; 538 | } 539 | 540 | .toolbar-borderless { 541 | border-top: 0; 542 | border-bottom: 0; 543 | } 544 | 545 | .toolbar-actions { 546 | margin-top: 4px; 547 | margin-bottom: 3px; 548 | padding-right: 3px; 549 | padding-left: 3px; 550 | padding-bottom: 3px; 551 | -webkit-app-region: drag; 552 | } 553 | .toolbar-actions:before, .toolbar-actions:after { 554 | display: table; 555 | content: " "; 556 | } 557 | .toolbar-actions:after { 558 | clear: both; 559 | } 560 | .toolbar-actions > .btn, 561 | .toolbar-actions > .btn-group { 562 | margin-left: 4px; 563 | margin-right: 4px; 564 | } 565 | 566 | label { 567 | display: inline-block; 568 | font-size: 13px; 569 | margin-bottom: 5px; 570 | white-space: nowrap; 571 | overflow: hidden; 572 | text-overflow: ellipsis; 573 | } 574 | 575 | input[type="search"] { 576 | box-sizing: border-box; 577 | } 578 | 579 | input[type="radio"], 580 | input[type="checkbox"] { 581 | margin: 4px 0 0; 582 | line-height: normal; 583 | } 584 | 585 | .form-control { 586 | display: inline-block; 587 | width: 100%; 588 | min-height: 25px; 589 | padding: 10px 20px; 590 | font-size: 16px; 591 | line-height: 1.6; 592 | background-color: #fff; 593 | border: 1px solid #ddd; 594 | border-radius: 4px; 595 | outline: none; 596 | } 597 | .form-control:focus { 598 | border-color: #6db3fd; 599 | box-shadow: 3px 3px 0 #6db3fd, -3px -3px 0 #6db3fd, -3px 3px 0 #6db3fd, 3px -3px 0 #6db3fd; 600 | } 601 | 602 | .form-control-large { 603 | display: inline-block; 604 | width: 100%; 605 | min-height: 25px; 606 | padding: 20px 30px; 607 | font-size: 18px; 608 | line-height: 1.6; 609 | background-color: #fff; 610 | border: 1px solid #ddd; 611 | border-radius: 4px; 612 | outline: none; 613 | } 614 | .form-control-large:focus { 615 | border-color: #6db3fd; 616 | box-shadow: 3px 3px 0 #6db3fd, -3px -3px 0 #6db3fd, -3px 3px 0 #6db3fd, 3px -3px 0 #6db3fd; 617 | } 618 | 619 | textarea { 620 | height: auto; 621 | } 622 | 623 | .form-group { 624 | margin-bottom: 10px; 625 | } 626 | 627 | .radio, 628 | .checkbox { 629 | position: relative; 630 | display: block; 631 | margin-top: 10px; 632 | margin-bottom: 10px; 633 | } 634 | .radio label, 635 | .checkbox label { 636 | padding-left: 20px; 637 | margin-bottom: 0; 638 | font-weight: normal; 639 | } 640 | 641 | .radio input[type="radio"], 642 | .radio-inline input[type="radio"], 643 | .checkbox input[type="checkbox"], 644 | .checkbox-inline input[type="checkbox"] { 645 | position: absolute; 646 | margin-left: -20px; 647 | margin-top: 4px; 648 | } 649 | 650 | .form-actions .btn { 651 | margin-right: 10px; 652 | } 653 | .form-actions .btn:last-child { 654 | margin-right: 0; 655 | } 656 | 657 | .pane-group { 658 | position: absolute; 659 | top: 0; 660 | right: 0; 661 | bottom: 0; 662 | left: 0; 663 | display: flex; 664 | } 665 | 666 | .pane { 667 | position: relative; 668 | overflow-y: auto; 669 | flex: 1; 670 | border-left: 1px solid #ddd; 671 | } 672 | .pane:first-child { 673 | border-left: 0; 674 | } 675 | 676 | .pane-sm { 677 | max-width: 220px; 678 | min-width: 150px; 679 | } 680 | 681 | .pane-mini { 682 | width: 80px; 683 | flex: none; 684 | } 685 | 686 | .pane-one-fourth { 687 | width: 25%; 688 | flex: none; 689 | } 690 | 691 | .pane-one-third { 692 | width: 33.3%; 693 | } 694 | 695 | img { 696 | -webkit-user-drag: text; 697 | } 698 | 699 | .img-circle { 700 | border-radius: 50%; 701 | } 702 | 703 | .img-rounded { 704 | border-radius: 4px; 705 | } 706 | 707 | .list-group { 708 | width: 100%; 709 | list-style: none; 710 | margin: 0; 711 | padding: 0; 712 | } 713 | .list-group * { 714 | margin: 0; 715 | white-space: nowrap; 716 | overflow: hidden; 717 | text-overflow: ellipsis; 718 | } 719 | 720 | .list-group-item { 721 | padding: 10px; 722 | font-size: 12px; 723 | color: #414142; 724 | border-top: 1px solid #ddd; 725 | } 726 | .list-group-item:first-child { 727 | border-top: 0; 728 | } 729 | .list-group-item.active, .list-group-item.selected { 730 | color: #fff; 731 | background-color: #116cd6; 732 | } 733 | 734 | .list-group-header { 735 | padding: 10px; 736 | } 737 | 738 | .media-object { 739 | margin-top: 3px; 740 | } 741 | 742 | .media-object.pull-left { 743 | margin-right: 10px; 744 | } 745 | 746 | .media-object.pull-right { 747 | margin-left: 10px; 748 | } 749 | 750 | .media-body { 751 | overflow: hidden; 752 | } 753 | 754 | .nav-group { 755 | font-size: 14px; 756 | } 757 | 758 | .nav-group-item { 759 | padding: 2px 10px 2px 25px; 760 | display: block; 761 | color: #333; 762 | text-decoration: none; 763 | white-space: nowrap; 764 | overflow: hidden; 765 | text-overflow: ellipsis; 766 | } 767 | .nav-group-item:active, .nav-group-item.active { 768 | background-color: #dcdfe1; 769 | } 770 | .nav-group-item .icon { 771 | width: 19px; 772 | height: 18px; 773 | float: left; 774 | color: #737475; 775 | margin-top: -3px; 776 | margin-right: 7px; 777 | font-size: 18px; 778 | text-align: center; 779 | } 780 | 781 | .nav-group-title { 782 | margin: 0; 783 | padding: 10px 10px 2px; 784 | font-size: 12px; 785 | font-weight: 500; 786 | color: #666666; 787 | } 788 | 789 | @font-face { 790 | font-family: "photon-entypo"; 791 | src: url("../fonts/photon-entypo.eot"); 792 | src: url("../fonts/photon-entypo.eot?#iefix") format("eot"), url("../fonts/photon-entypo.woff") format("woff"), url("../fonts/photon-entypo.ttf") format("truetype"); 793 | font-weight: normal; 794 | font-style: normal; 795 | } 796 | .icon:before { 797 | position: relative; 798 | display: inline-block; 799 | font-family: "photon-entypo"; 800 | speak: none; 801 | font-size: 100%; 802 | font-style: normal; 803 | font-weight: normal; 804 | font-variant: normal; 805 | text-transform: none; 806 | line-height: 1; 807 | -webkit-font-smoothing: antialiased; 808 | -moz-osx-font-smoothing: grayscale; 809 | } 810 | 811 | .icon-note:before { 812 | content: '\e800'; 813 | } 814 | 815 | /* '' */ 816 | .icon-note-beamed:before { 817 | content: '\e801'; 818 | } 819 | 820 | /* '' */ 821 | .icon-music:before { 822 | content: '\e802'; 823 | } 824 | 825 | /* '' */ 826 | .icon-search:before { 827 | content: '\e803'; 828 | } 829 | 830 | /* '' */ 831 | .icon-flashlight:before { 832 | content: '\e804'; 833 | } 834 | 835 | /* '' */ 836 | .icon-mail:before { 837 | content: '\e805'; 838 | } 839 | 840 | /* '' */ 841 | .icon-heart:before { 842 | content: '\e806'; 843 | } 844 | 845 | /* '' */ 846 | .icon-heart-empty:before { 847 | content: '\e807'; 848 | } 849 | 850 | /* '' */ 851 | .icon-star:before { 852 | content: '\e808'; 853 | } 854 | 855 | /* '' */ 856 | .icon-star-empty:before { 857 | content: '\e809'; 858 | } 859 | 860 | /* '' */ 861 | .icon-user:before { 862 | content: '\e80a'; 863 | } 864 | 865 | /* '' */ 866 | .icon-users:before { 867 | content: '\e80b'; 868 | } 869 | 870 | /* '' */ 871 | .icon-user-add:before { 872 | content: '\e80c'; 873 | } 874 | 875 | /* '' */ 876 | .icon-video:before { 877 | content: '\e80d'; 878 | } 879 | 880 | /* '' */ 881 | .icon-picture:before { 882 | content: '\e80e'; 883 | } 884 | 885 | /* '' */ 886 | .icon-camera:before { 887 | content: '\e80f'; 888 | } 889 | 890 | /* '' */ 891 | .icon-layout:before { 892 | content: '\e810'; 893 | } 894 | 895 | /* '' */ 896 | .icon-menu:before { 897 | content: '\e811'; 898 | } 899 | 900 | /* '' */ 901 | .icon-check:before { 902 | content: '\e812'; 903 | } 904 | 905 | /* '' */ 906 | .icon-cancel:before { 907 | content: '\e813'; 908 | } 909 | 910 | /* '' */ 911 | .icon-cancel-circled:before { 912 | content: '\e814'; 913 | } 914 | 915 | /* '' */ 916 | .icon-cancel-squared:before { 917 | content: '\e815'; 918 | } 919 | 920 | /* '' */ 921 | .icon-plus:before { 922 | content: '\e816'; 923 | } 924 | 925 | /* '' */ 926 | .icon-plus-circled:before { 927 | content: '\e817'; 928 | } 929 | 930 | /* '' */ 931 | .icon-plus-squared:before { 932 | content: '\e818'; 933 | } 934 | 935 | /* '' */ 936 | .icon-minus:before { 937 | content: '\e819'; 938 | } 939 | 940 | /* '' */ 941 | .icon-minus-circled:before { 942 | content: '\e81a'; 943 | } 944 | 945 | /* '' */ 946 | .icon-minus-squared:before { 947 | content: '\e81b'; 948 | } 949 | 950 | /* '' */ 951 | .icon-help:before { 952 | content: '\e81c'; 953 | } 954 | 955 | /* '' */ 956 | .icon-help-circled:before { 957 | content: '\e81d'; 958 | } 959 | 960 | /* '' */ 961 | .icon-info:before { 962 | content: '\e81e'; 963 | } 964 | 965 | /* '' */ 966 | .icon-info-circled:before { 967 | content: '\e81f'; 968 | } 969 | 970 | /* '' */ 971 | .icon-back:before { 972 | content: '\e820'; 973 | } 974 | 975 | /* '' */ 976 | .icon-home:before { 977 | content: '\e821'; 978 | } 979 | 980 | /* '' */ 981 | .icon-link:before { 982 | content: '\e822'; 983 | } 984 | 985 | /* '' */ 986 | .icon-attach:before { 987 | content: '\e823'; 988 | } 989 | 990 | /* '' */ 991 | .icon-lock:before { 992 | content: '\e824'; 993 | } 994 | 995 | /* '' */ 996 | .icon-lock-open:before { 997 | content: '\e825'; 998 | } 999 | 1000 | /* '' */ 1001 | .icon-eye:before { 1002 | content: '\e826'; 1003 | } 1004 | 1005 | /* '' */ 1006 | .icon-tag:before { 1007 | content: '\e827'; 1008 | } 1009 | 1010 | /* '' */ 1011 | .icon-bookmark:before { 1012 | content: '\e828'; 1013 | } 1014 | 1015 | /* '' */ 1016 | .icon-bookmarks:before { 1017 | content: '\e829'; 1018 | } 1019 | 1020 | /* '' */ 1021 | .icon-flag:before { 1022 | content: '\e82a'; 1023 | } 1024 | 1025 | /* '' */ 1026 | .icon-thumbs-up:before { 1027 | content: '\e82b'; 1028 | } 1029 | 1030 | /* '' */ 1031 | .icon-thumbs-down:before { 1032 | content: '\e82c'; 1033 | } 1034 | 1035 | /* '' */ 1036 | .icon-download:before { 1037 | content: '\e82d'; 1038 | } 1039 | 1040 | /* '' */ 1041 | .icon-upload:before { 1042 | content: '\e82e'; 1043 | } 1044 | 1045 | /* '' */ 1046 | .icon-upload-cloud:before { 1047 | content: '\e82f'; 1048 | } 1049 | 1050 | /* '' */ 1051 | .icon-reply:before { 1052 | content: '\e830'; 1053 | } 1054 | 1055 | /* '' */ 1056 | .icon-reply-all:before { 1057 | content: '\e831'; 1058 | } 1059 | 1060 | /* '' */ 1061 | .icon-forward:before { 1062 | content: '\e832'; 1063 | } 1064 | 1065 | /* '' */ 1066 | .icon-quote:before { 1067 | content: '\e833'; 1068 | } 1069 | 1070 | /* '' */ 1071 | .icon-code:before { 1072 | content: '\e834'; 1073 | } 1074 | 1075 | /* '' */ 1076 | .icon-export:before { 1077 | content: '\e835'; 1078 | } 1079 | 1080 | /* '' */ 1081 | .icon-pencil:before { 1082 | content: '\e836'; 1083 | } 1084 | 1085 | /* '' */ 1086 | .icon-feather:before { 1087 | content: '\e837'; 1088 | } 1089 | 1090 | /* '' */ 1091 | .icon-print:before { 1092 | content: '\e838'; 1093 | } 1094 | 1095 | /* '' */ 1096 | .icon-retweet:before { 1097 | content: '\e839'; 1098 | } 1099 | 1100 | /* '' */ 1101 | .icon-keyboard:before { 1102 | content: '\e83a'; 1103 | } 1104 | 1105 | /* '' */ 1106 | .icon-comment:before { 1107 | content: '\e83b'; 1108 | } 1109 | 1110 | /* '' */ 1111 | .icon-chat:before { 1112 | content: '\e83c'; 1113 | } 1114 | 1115 | /* '' */ 1116 | .icon-bell:before { 1117 | content: '\e83d'; 1118 | } 1119 | 1120 | /* '' */ 1121 | .icon-attention:before { 1122 | content: '\e83e'; 1123 | } 1124 | 1125 | /* '' */ 1126 | .icon-alert:before { 1127 | content: '\e83f'; 1128 | } 1129 | 1130 | /* '' */ 1131 | .icon-vcard:before { 1132 | content: '\e840'; 1133 | } 1134 | 1135 | /* '' */ 1136 | .icon-address:before { 1137 | content: '\e841'; 1138 | } 1139 | 1140 | /* '' */ 1141 | .icon-location:before { 1142 | content: '\e842'; 1143 | } 1144 | 1145 | /* '' */ 1146 | .icon-map:before { 1147 | content: '\e843'; 1148 | } 1149 | 1150 | /* '' */ 1151 | .icon-direction:before { 1152 | content: '\e844'; 1153 | } 1154 | 1155 | /* '' */ 1156 | .icon-compass:before { 1157 | content: '\e845'; 1158 | } 1159 | 1160 | /* '' */ 1161 | .icon-cup:before { 1162 | content: '\e846'; 1163 | } 1164 | 1165 | /* '' */ 1166 | .icon-trash:before { 1167 | content: '\e847'; 1168 | } 1169 | 1170 | /* '' */ 1171 | .icon-doc:before { 1172 | content: '\e848'; 1173 | } 1174 | 1175 | /* '' */ 1176 | .icon-docs:before { 1177 | content: '\e849'; 1178 | } 1179 | 1180 | /* '' */ 1181 | .icon-doc-landscape:before { 1182 | content: '\e84a'; 1183 | } 1184 | 1185 | /* '' */ 1186 | .icon-doc-text:before { 1187 | content: '\e84b'; 1188 | } 1189 | 1190 | /* '' */ 1191 | .icon-doc-text-inv:before { 1192 | content: '\e84c'; 1193 | } 1194 | 1195 | /* '' */ 1196 | .icon-newspaper:before { 1197 | content: '\e84d'; 1198 | } 1199 | 1200 | /* '' */ 1201 | .icon-book-open:before { 1202 | content: '\e84e'; 1203 | } 1204 | 1205 | /* '' */ 1206 | .icon-book:before { 1207 | content: '\e84f'; 1208 | } 1209 | 1210 | /* '' */ 1211 | .icon-folder:before { 1212 | content: '\e850'; 1213 | } 1214 | 1215 | /* '' */ 1216 | .icon-archive:before { 1217 | content: '\e851'; 1218 | } 1219 | 1220 | /* '' */ 1221 | .icon-box:before { 1222 | content: '\e852'; 1223 | } 1224 | 1225 | /* '' */ 1226 | .icon-rss:before { 1227 | content: '\e853'; 1228 | } 1229 | 1230 | /* '' */ 1231 | .icon-phone:before { 1232 | content: '\e854'; 1233 | } 1234 | 1235 | /* '' */ 1236 | .icon-cog:before { 1237 | content: '\e855'; 1238 | } 1239 | 1240 | /* '' */ 1241 | .icon-tools:before { 1242 | content: '\e856'; 1243 | } 1244 | 1245 | /* '' */ 1246 | .icon-share:before { 1247 | content: '\e857'; 1248 | } 1249 | 1250 | /* '' */ 1251 | .icon-shareable:before { 1252 | content: '\e858'; 1253 | } 1254 | 1255 | /* '' */ 1256 | .icon-basket:before { 1257 | content: '\e859'; 1258 | } 1259 | 1260 | /* '' */ 1261 | .icon-bag:before { 1262 | content: '\e85a'; 1263 | } 1264 | 1265 | /* '' */ 1266 | .icon-calendar:before { 1267 | content: '\e85b'; 1268 | } 1269 | 1270 | /* '' */ 1271 | .icon-login:before { 1272 | content: '\e85c'; 1273 | } 1274 | 1275 | /* '' */ 1276 | .icon-logout:before { 1277 | content: '\e85d'; 1278 | } 1279 | 1280 | /* '' */ 1281 | .icon-mic:before { 1282 | content: '\e85e'; 1283 | } 1284 | 1285 | /* '' */ 1286 | .icon-mute:before { 1287 | content: '\e85f'; 1288 | } 1289 | 1290 | /* '' */ 1291 | .icon-sound:before { 1292 | content: '\e860'; 1293 | } 1294 | 1295 | /* '' */ 1296 | .icon-volume:before { 1297 | content: '\e861'; 1298 | } 1299 | 1300 | /* '' */ 1301 | .icon-clock:before { 1302 | content: '\e862'; 1303 | } 1304 | 1305 | /* '' */ 1306 | .icon-hourglass:before { 1307 | content: '\e863'; 1308 | } 1309 | 1310 | /* '' */ 1311 | .icon-lamp:before { 1312 | content: '\e864'; 1313 | } 1314 | 1315 | /* '' */ 1316 | .icon-light-down:before { 1317 | content: '\e865'; 1318 | } 1319 | 1320 | /* '' */ 1321 | .icon-light-up:before { 1322 | content: '\e866'; 1323 | } 1324 | 1325 | /* '' */ 1326 | .icon-adjust:before { 1327 | content: '\e867'; 1328 | } 1329 | 1330 | /* '' */ 1331 | .icon-block:before { 1332 | content: '\e868'; 1333 | } 1334 | 1335 | /* '' */ 1336 | .icon-resize-full:before { 1337 | content: '\e869'; 1338 | } 1339 | 1340 | /* '' */ 1341 | .icon-resize-small:before { 1342 | content: '\e86a'; 1343 | } 1344 | 1345 | /* '' */ 1346 | .icon-popup:before { 1347 | content: '\e86b'; 1348 | } 1349 | 1350 | /* '' */ 1351 | .icon-publish:before { 1352 | content: '\e86c'; 1353 | } 1354 | 1355 | /* '' */ 1356 | .icon-window:before { 1357 | content: '\e86d'; 1358 | } 1359 | 1360 | /* '' */ 1361 | .icon-arrow-combo:before { 1362 | content: '\e86e'; 1363 | } 1364 | 1365 | /* '' */ 1366 | .icon-down-circled:before { 1367 | content: '\e86f'; 1368 | } 1369 | 1370 | /* '' */ 1371 | .icon-left-circled:before { 1372 | content: '\e870'; 1373 | } 1374 | 1375 | /* '' */ 1376 | .icon-right-circled:before { 1377 | content: '\e871'; 1378 | } 1379 | 1380 | /* '' */ 1381 | .icon-up-circled:before { 1382 | content: '\e872'; 1383 | } 1384 | 1385 | /* '' */ 1386 | .icon-down-open:before { 1387 | content: '\e873'; 1388 | } 1389 | 1390 | /* '' */ 1391 | .icon-left-open:before { 1392 | content: '\e874'; 1393 | } 1394 | 1395 | /* '' */ 1396 | .icon-right-open:before { 1397 | content: '\e875'; 1398 | } 1399 | 1400 | /* '' */ 1401 | .icon-up-open:before { 1402 | content: '\e876'; 1403 | } 1404 | 1405 | /* '' */ 1406 | .icon-down-open-mini:before { 1407 | content: '\e877'; 1408 | } 1409 | 1410 | /* '' */ 1411 | .icon-left-open-mini:before { 1412 | content: '\e878'; 1413 | } 1414 | 1415 | /* '' */ 1416 | .icon-right-open-mini:before { 1417 | content: '\e879'; 1418 | } 1419 | 1420 | /* '' */ 1421 | .icon-up-open-mini:before { 1422 | content: '\e87a'; 1423 | } 1424 | 1425 | /* '' */ 1426 | .icon-down-open-big:before { 1427 | content: '\e87b'; 1428 | } 1429 | 1430 | /* '' */ 1431 | .icon-left-open-big:before { 1432 | content: '\e87c'; 1433 | } 1434 | 1435 | /* '' */ 1436 | .icon-right-open-big:before { 1437 | content: '\e87d'; 1438 | } 1439 | 1440 | /* '' */ 1441 | .icon-up-open-big:before { 1442 | content: '\e87e'; 1443 | } 1444 | 1445 | /* '' */ 1446 | .icon-down:before { 1447 | content: '\e87f'; 1448 | } 1449 | 1450 | /* '' */ 1451 | .icon-left:before { 1452 | content: '\e880'; 1453 | } 1454 | 1455 | /* '' */ 1456 | .icon-right:before { 1457 | content: '\e881'; 1458 | } 1459 | 1460 | /* '' */ 1461 | .icon-up:before { 1462 | content: '\e882'; 1463 | } 1464 | 1465 | /* '' */ 1466 | .icon-down-dir:before { 1467 | content: '\e883'; 1468 | } 1469 | 1470 | /* '' */ 1471 | .icon-left-dir:before { 1472 | content: '\e884'; 1473 | } 1474 | 1475 | /* '' */ 1476 | .icon-right-dir:before { 1477 | content: '\e885'; 1478 | } 1479 | 1480 | /* '' */ 1481 | .icon-up-dir:before { 1482 | content: '\e886'; 1483 | } 1484 | 1485 | /* '' */ 1486 | .icon-down-bold:before { 1487 | content: '\e887'; 1488 | } 1489 | 1490 | /* '' */ 1491 | .icon-left-bold:before { 1492 | content: '\e888'; 1493 | } 1494 | 1495 | /* '' */ 1496 | .icon-right-bold:before { 1497 | content: '\e889'; 1498 | } 1499 | 1500 | /* '' */ 1501 | .icon-up-bold:before { 1502 | content: '\e88a'; 1503 | } 1504 | 1505 | /* '' */ 1506 | .icon-down-thin:before { 1507 | content: '\e88b'; 1508 | } 1509 | 1510 | /* '' */ 1511 | .icon-left-thin:before { 1512 | content: '\e88c'; 1513 | } 1514 | 1515 | /* '' */ 1516 | .icon-right-thin:before { 1517 | content: '\e88d'; 1518 | } 1519 | 1520 | /* '' */ 1521 | .icon-up-thin:before { 1522 | content: '\e88e'; 1523 | } 1524 | 1525 | /* '' */ 1526 | .icon-ccw:before { 1527 | content: '\e88f'; 1528 | } 1529 | 1530 | /* '' */ 1531 | .icon-cw:before { 1532 | content: '\e890'; 1533 | } 1534 | 1535 | /* '' */ 1536 | .icon-arrows-ccw:before { 1537 | content: '\e891'; 1538 | } 1539 | 1540 | /* '' */ 1541 | .icon-level-down:before { 1542 | content: '\e892'; 1543 | } 1544 | 1545 | /* '' */ 1546 | .icon-level-up:before { 1547 | content: '\e893'; 1548 | } 1549 | 1550 | /* '' */ 1551 | .icon-shuffle:before { 1552 | content: '\e894'; 1553 | } 1554 | 1555 | /* '' */ 1556 | .icon-loop:before { 1557 | content: '\e895'; 1558 | } 1559 | 1560 | /* '' */ 1561 | .icon-switch:before { 1562 | content: '\e896'; 1563 | } 1564 | 1565 | /* '' */ 1566 | .icon-play:before { 1567 | content: '\e897'; 1568 | } 1569 | 1570 | /* '' */ 1571 | .icon-stop:before { 1572 | content: '\e898'; 1573 | } 1574 | 1575 | /* '' */ 1576 | .icon-pause:before { 1577 | content: '\e899'; 1578 | } 1579 | 1580 | /* '' */ 1581 | .icon-record:before { 1582 | content: '\e89a'; 1583 | } 1584 | 1585 | /* '' */ 1586 | .icon-to-end:before { 1587 | content: '\e89b'; 1588 | } 1589 | 1590 | /* '' */ 1591 | .icon-to-start:before { 1592 | content: '\e89c'; 1593 | } 1594 | 1595 | /* '' */ 1596 | .icon-fast-forward:before { 1597 | content: '\e89d'; 1598 | } 1599 | 1600 | /* '' */ 1601 | .icon-fast-backward:before { 1602 | content: '\e89e'; 1603 | } 1604 | 1605 | /* '' */ 1606 | .icon-progress-0:before { 1607 | content: '\e89f'; 1608 | } 1609 | 1610 | /* '' */ 1611 | .icon-progress-1:before { 1612 | content: '\e8a0'; 1613 | } 1614 | 1615 | /* '' */ 1616 | .icon-progress-2:before { 1617 | content: '\e8a1'; 1618 | } 1619 | 1620 | /* '' */ 1621 | .icon-progress-3:before { 1622 | content: '\e8a2'; 1623 | } 1624 | 1625 | /* '' */ 1626 | .icon-target:before { 1627 | content: '\e8a3'; 1628 | } 1629 | 1630 | /* '' */ 1631 | .icon-palette:before { 1632 | content: '\e8a4'; 1633 | } 1634 | 1635 | /* '' */ 1636 | .icon-list:before { 1637 | content: '\e8a5'; 1638 | } 1639 | 1640 | /* '' */ 1641 | .icon-list-add:before { 1642 | content: '\e8a6'; 1643 | } 1644 | 1645 | /* '' */ 1646 | .icon-signal:before { 1647 | content: '\e8a7'; 1648 | } 1649 | 1650 | /* '' */ 1651 | .icon-trophy:before { 1652 | content: '\e8a8'; 1653 | } 1654 | 1655 | /* '' */ 1656 | .icon-battery:before { 1657 | content: '\e8a9'; 1658 | } 1659 | 1660 | /* '' */ 1661 | .icon-back-in-time:before { 1662 | content: '\e8aa'; 1663 | } 1664 | 1665 | /* '' */ 1666 | .icon-monitor:before { 1667 | content: '\e8ab'; 1668 | } 1669 | 1670 | /* '' */ 1671 | .icon-mobile:before { 1672 | content: '\e8ac'; 1673 | } 1674 | 1675 | /* '' */ 1676 | .icon-network:before { 1677 | content: '\e8ad'; 1678 | } 1679 | 1680 | /* '' */ 1681 | .icon-cd:before { 1682 | content: '\e8ae'; 1683 | } 1684 | 1685 | /* '' */ 1686 | .icon-inbox:before { 1687 | content: '\e8af'; 1688 | } 1689 | 1690 | /* '' */ 1691 | .icon-install:before { 1692 | content: '\e8b0'; 1693 | } 1694 | 1695 | /* '' */ 1696 | .icon-globe:before { 1697 | content: '\e8b1'; 1698 | } 1699 | 1700 | /* '' */ 1701 | .icon-cloud:before { 1702 | content: '\e8b2'; 1703 | } 1704 | 1705 | /* '' */ 1706 | .icon-cloud-thunder:before { 1707 | content: '\e8b3'; 1708 | } 1709 | 1710 | /* '' */ 1711 | .icon-flash:before { 1712 | content: '\e8b4'; 1713 | } 1714 | 1715 | /* '' */ 1716 | .icon-moon:before { 1717 | content: '\e8b5'; 1718 | } 1719 | 1720 | /* '' */ 1721 | .icon-flight:before { 1722 | content: '\e8b6'; 1723 | } 1724 | 1725 | /* '' */ 1726 | .icon-paper-plane:before { 1727 | content: '\e8b7'; 1728 | } 1729 | 1730 | /* '' */ 1731 | .icon-leaf:before { 1732 | content: '\e8b8'; 1733 | } 1734 | 1735 | /* '' */ 1736 | .icon-lifebuoy:before { 1737 | content: '\e8b9'; 1738 | } 1739 | 1740 | /* '' */ 1741 | .icon-mouse:before { 1742 | content: '\e8ba'; 1743 | } 1744 | 1745 | /* '' */ 1746 | .icon-briefcase:before { 1747 | content: '\e8bb'; 1748 | } 1749 | 1750 | /* '' */ 1751 | .icon-suitcase:before { 1752 | content: '\e8bc'; 1753 | } 1754 | 1755 | /* '' */ 1756 | .icon-dot:before { 1757 | content: '\e8bd'; 1758 | } 1759 | 1760 | /* '' */ 1761 | .icon-dot-2:before { 1762 | content: '\e8be'; 1763 | } 1764 | 1765 | /* '' */ 1766 | .icon-dot-3:before { 1767 | content: '\e8bf'; 1768 | } 1769 | 1770 | /* '' */ 1771 | .icon-brush:before { 1772 | content: '\e8c0'; 1773 | } 1774 | 1775 | /* '' */ 1776 | .icon-magnet:before { 1777 | content: '\e8c1'; 1778 | } 1779 | 1780 | /* '' */ 1781 | .icon-infinity:before { 1782 | content: '\e8c2'; 1783 | } 1784 | 1785 | /* '' */ 1786 | .icon-erase:before { 1787 | content: '\e8c3'; 1788 | } 1789 | 1790 | /* '' */ 1791 | .icon-chart-pie:before { 1792 | content: '\e8c4'; 1793 | } 1794 | 1795 | /* '' */ 1796 | .icon-chart-line:before { 1797 | content: '\e8c5'; 1798 | } 1799 | 1800 | /* '' */ 1801 | .icon-chart-bar:before { 1802 | content: '\e8c6'; 1803 | } 1804 | 1805 | /* '' */ 1806 | .icon-chart-area:before { 1807 | content: '\e8c7'; 1808 | } 1809 | 1810 | /* '' */ 1811 | .icon-tape:before { 1812 | content: '\e8c8'; 1813 | } 1814 | 1815 | /* '' */ 1816 | .icon-graduation-cap:before { 1817 | content: '\e8c9'; 1818 | } 1819 | 1820 | /* '' */ 1821 | .icon-language:before { 1822 | content: '\e8ca'; 1823 | } 1824 | 1825 | /* '' */ 1826 | .icon-ticket:before { 1827 | content: '\e8cb'; 1828 | } 1829 | 1830 | /* '' */ 1831 | .icon-water:before { 1832 | content: '\e8cc'; 1833 | } 1834 | 1835 | /* '' */ 1836 | .icon-droplet:before { 1837 | content: '\e8cd'; 1838 | } 1839 | 1840 | /* '' */ 1841 | .icon-air:before { 1842 | content: '\e8ce'; 1843 | } 1844 | 1845 | /* '' */ 1846 | .icon-credit-card:before { 1847 | content: '\e8cf'; 1848 | } 1849 | 1850 | /* '' */ 1851 | .icon-floppy:before { 1852 | content: '\e8d0'; 1853 | } 1854 | 1855 | /* '' */ 1856 | .icon-clipboard:before { 1857 | content: '\e8d1'; 1858 | } 1859 | 1860 | /* '' */ 1861 | .icon-megaphone:before { 1862 | content: '\e8d2'; 1863 | } 1864 | 1865 | /* '' */ 1866 | .icon-database:before { 1867 | content: '\e8d3'; 1868 | } 1869 | 1870 | /* '' */ 1871 | .icon-drive:before { 1872 | content: '\e8d4'; 1873 | } 1874 | 1875 | /* '' */ 1876 | .icon-bucket:before { 1877 | content: '\e8d5'; 1878 | } 1879 | 1880 | /* '' */ 1881 | .icon-thermometer:before { 1882 | content: '\e8d6'; 1883 | } 1884 | 1885 | /* '' */ 1886 | .icon-key:before { 1887 | content: '\e8d7'; 1888 | } 1889 | 1890 | /* '' */ 1891 | .icon-flow-cascade:before { 1892 | content: '\e8d8'; 1893 | } 1894 | 1895 | /* '' */ 1896 | .icon-flow-branch:before { 1897 | content: '\e8d9'; 1898 | } 1899 | 1900 | /* '' */ 1901 | .icon-flow-tree:before { 1902 | content: '\e8da'; 1903 | } 1904 | 1905 | /* '' */ 1906 | .icon-flow-line:before { 1907 | content: '\e8db'; 1908 | } 1909 | 1910 | /* '' */ 1911 | .icon-flow-parallel:before { 1912 | content: '\e8dc'; 1913 | } 1914 | 1915 | /* '' */ 1916 | .icon-rocket:before { 1917 | content: '\e8dd'; 1918 | } 1919 | 1920 | /* '' */ 1921 | .icon-gauge:before { 1922 | content: '\e8de'; 1923 | } 1924 | 1925 | /* '' */ 1926 | .icon-traffic-cone:before { 1927 | content: '\e8df'; 1928 | } 1929 | 1930 | /* '' */ 1931 | .icon-cc:before { 1932 | content: '\e8e0'; 1933 | } 1934 | 1935 | /* '' */ 1936 | .icon-cc-by:before { 1937 | content: '\e8e1'; 1938 | } 1939 | 1940 | /* '' */ 1941 | .icon-cc-nc:before { 1942 | content: '\e8e2'; 1943 | } 1944 | 1945 | /* '' */ 1946 | .icon-cc-nc-eu:before { 1947 | content: '\e8e3'; 1948 | } 1949 | 1950 | /* '' */ 1951 | .icon-cc-nc-jp:before { 1952 | content: '\e8e4'; 1953 | } 1954 | 1955 | /* '' */ 1956 | .icon-cc-sa:before { 1957 | content: '\e8e5'; 1958 | } 1959 | 1960 | /* '' */ 1961 | .icon-cc-nd:before { 1962 | content: '\e8e6'; 1963 | } 1964 | 1965 | /* '' */ 1966 | .icon-cc-pd:before { 1967 | content: '\e8e7'; 1968 | } 1969 | 1970 | /* '' */ 1971 | .icon-cc-zero:before { 1972 | content: '\e8e8'; 1973 | } 1974 | 1975 | /* '' */ 1976 | .icon-cc-share:before { 1977 | content: '\e8e9'; 1978 | } 1979 | 1980 | /* '' */ 1981 | .icon-cc-remix:before { 1982 | content: '\e8ea'; 1983 | } 1984 | 1985 | /* '' */ 1986 | .icon-github:before { 1987 | content: '\e8eb'; 1988 | } 1989 | 1990 | /* '' */ 1991 | .icon-github-circled:before { 1992 | content: '\e8ec'; 1993 | } 1994 | 1995 | /* '' */ 1996 | .icon-flickr:before { 1997 | content: '\e8ed'; 1998 | } 1999 | 2000 | /* '' */ 2001 | .icon-flickr-circled:before { 2002 | content: '\e8ee'; 2003 | } 2004 | 2005 | /* '' */ 2006 | .icon-vimeo:before { 2007 | content: '\e8ef'; 2008 | } 2009 | 2010 | /* '' */ 2011 | .icon-vimeo-circled:before { 2012 | content: '\e8f0'; 2013 | } 2014 | 2015 | /* '' */ 2016 | .icon-twitter:before { 2017 | content: '\e8f1'; 2018 | } 2019 | 2020 | /* '' */ 2021 | .icon-twitter-circled:before { 2022 | content: '\e8f2'; 2023 | } 2024 | 2025 | /* '' */ 2026 | .icon-facebook:before { 2027 | content: '\e8f3'; 2028 | } 2029 | 2030 | /* '' */ 2031 | .icon-facebook-circled:before { 2032 | content: '\e8f4'; 2033 | } 2034 | 2035 | /* '' */ 2036 | .icon-facebook-squared:before { 2037 | content: '\e8f5'; 2038 | } 2039 | 2040 | /* '' */ 2041 | .icon-gplus:before { 2042 | content: '\e8f6'; 2043 | } 2044 | 2045 | /* '' */ 2046 | .icon-gplus-circled:before { 2047 | content: '\e8f7'; 2048 | } 2049 | 2050 | /* '' */ 2051 | .icon-pinterest:before { 2052 | content: '\e8f8'; 2053 | } 2054 | 2055 | /* '' */ 2056 | .icon-pinterest-circled:before { 2057 | content: '\e8f9'; 2058 | } 2059 | 2060 | /* '' */ 2061 | .icon-tumblr:before { 2062 | content: '\e8fa'; 2063 | } 2064 | 2065 | /* '' */ 2066 | .icon-tumblr-circled:before { 2067 | content: '\e8fb'; 2068 | } 2069 | 2070 | /* '' */ 2071 | .icon-linkedin:before { 2072 | content: '\e8fc'; 2073 | } 2074 | 2075 | /* '' */ 2076 | .icon-linkedin-circled:before { 2077 | content: '\e8fd'; 2078 | } 2079 | 2080 | /* '' */ 2081 | .icon-dribbble:before { 2082 | content: '\e8fe'; 2083 | } 2084 | 2085 | /* '' */ 2086 | .icon-dribbble-circled:before { 2087 | content: '\e8ff'; 2088 | } 2089 | 2090 | /* '' */ 2091 | .icon-stumbleupon:before { 2092 | content: '\e900'; 2093 | } 2094 | 2095 | /* '' */ 2096 | .icon-stumbleupon-circled:before { 2097 | content: '\e901'; 2098 | } 2099 | 2100 | /* '' */ 2101 | .icon-lastfm:before { 2102 | content: '\e902'; 2103 | } 2104 | 2105 | /* '' */ 2106 | .icon-lastfm-circled:before { 2107 | content: '\e903'; 2108 | } 2109 | 2110 | /* '' */ 2111 | .icon-rdio:before { 2112 | content: '\e904'; 2113 | } 2114 | 2115 | /* '' */ 2116 | .icon-rdio-circled:before { 2117 | content: '\e905'; 2118 | } 2119 | 2120 | /* '' */ 2121 | .icon-spotify:before { 2122 | content: '\e906'; 2123 | } 2124 | 2125 | /* '' */ 2126 | .icon-spotify-circled:before { 2127 | content: '\e907'; 2128 | } 2129 | 2130 | /* '' */ 2131 | .icon-qq:before { 2132 | content: '\e908'; 2133 | } 2134 | 2135 | /* '' */ 2136 | .icon-instagram:before { 2137 | content: '\e909'; 2138 | } 2139 | 2140 | /* '' */ 2141 | .icon-dropbox:before { 2142 | content: '\e90a'; 2143 | } 2144 | 2145 | /* '' */ 2146 | .icon-evernote:before { 2147 | content: '\e90b'; 2148 | } 2149 | 2150 | /* '' */ 2151 | .icon-flattr:before { 2152 | content: '\e90c'; 2153 | } 2154 | 2155 | /* '' */ 2156 | .icon-skype:before { 2157 | content: '\e90d'; 2158 | } 2159 | 2160 | /* '' */ 2161 | .icon-skype-circled:before { 2162 | content: '\e90e'; 2163 | } 2164 | 2165 | /* '' */ 2166 | .icon-renren:before { 2167 | content: '\e90f'; 2168 | } 2169 | 2170 | /* '' */ 2171 | .icon-sina-weibo:before { 2172 | content: '\e910'; 2173 | } 2174 | 2175 | /* '' */ 2176 | .icon-paypal:before { 2177 | content: '\e911'; 2178 | } 2179 | 2180 | /* '' */ 2181 | .icon-picasa:before { 2182 | content: '\e912'; 2183 | } 2184 | 2185 | /* '' */ 2186 | .icon-soundcloud:before { 2187 | content: '\e913'; 2188 | } 2189 | 2190 | /* '' */ 2191 | .icon-mixi:before { 2192 | content: '\e914'; 2193 | } 2194 | 2195 | /* '' */ 2196 | .icon-behance:before { 2197 | content: '\e915'; 2198 | } 2199 | 2200 | /* '' */ 2201 | .icon-google-circles:before { 2202 | content: '\e916'; 2203 | } 2204 | 2205 | /* '' */ 2206 | .icon-vkontakte:before { 2207 | content: '\e917'; 2208 | } 2209 | 2210 | /* '' */ 2211 | .icon-smashing:before { 2212 | content: '\e918'; 2213 | } 2214 | 2215 | /* '' */ 2216 | .icon-sweden:before { 2217 | content: '\e919'; 2218 | } 2219 | 2220 | /* '' */ 2221 | .icon-db-shape:before { 2222 | content: '\e91a'; 2223 | } 2224 | 2225 | /* '' */ 2226 | .icon-logo-db:before { 2227 | content: '\e91b'; 2228 | } 2229 | 2230 | /* '' */ 2231 | table { 2232 | width: 100%; 2233 | border: 0; 2234 | border-collapse: separate; 2235 | font-size: 12px; 2236 | text-align: left; 2237 | } 2238 | 2239 | thead { 2240 | background-color: #f5f5f4; 2241 | } 2242 | 2243 | tbody { 2244 | background-color: #fff; 2245 | } 2246 | 2247 | .table-striped tr:nth-child(even) { 2248 | background-color: #f5f5f4; 2249 | } 2250 | 2251 | tr:active, 2252 | .table-striped tr:active:nth-child(even) { 2253 | color: #fff; 2254 | background-color: #116cd6; 2255 | } 2256 | 2257 | thead tr:active { 2258 | color: #333; 2259 | background-color: #f5f5f4; 2260 | } 2261 | 2262 | th { 2263 | font-weight: normal; 2264 | border-right: 1px solid #ddd; 2265 | border-bottom: 1px solid #ddd; 2266 | } 2267 | 2268 | th, 2269 | td { 2270 | padding: 2px 15px; 2271 | white-space: nowrap; 2272 | overflow: hidden; 2273 | text-overflow: ellipsis; 2274 | } 2275 | th:last-child, 2276 | td:last-child { 2277 | border-right: 0; 2278 | } 2279 | 2280 | .tab-group { 2281 | margin-top: -1px; 2282 | display: flex; 2283 | border-top: 1px solid #989698; 2284 | border-bottom: 1px solid #989698; 2285 | } 2286 | 2287 | .tab-item { 2288 | position: relative; 2289 | flex: 1; 2290 | padding: 3px; 2291 | font-size: 12px; 2292 | text-align: center; 2293 | border-left: 1px solid #989698; 2294 | background-color: #b8b6b8; 2295 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #b8b6b8), color-stop(100%, #b0aeb0)); 2296 | background-image: -webkit-linear-gradient(top, #b8b6b8 0%, #b0aeb0 100%); 2297 | background-image: linear-gradient(to bottom, #b8b6b8 0%, #b0aeb0 100%); 2298 | } 2299 | .tab-item:first-child { 2300 | border-left: 0; 2301 | } 2302 | .tab-item.active { 2303 | background-color: #d4d2d4; 2304 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #d4d2d4), color-stop(100%, #cccacc)); 2305 | background-image: -webkit-linear-gradient(top, #d4d2d4 0%, #cccacc 100%); 2306 | background-image: linear-gradient(to bottom, #d4d2d4 0%, #cccacc 100%); 2307 | } 2308 | .tab-item .icon-close-tab { 2309 | position: absolute; 2310 | top: 50%; 2311 | left: 5px; 2312 | width: 15px; 2313 | height: 15px; 2314 | font-size: 15px; 2315 | line-height: 15px; 2316 | text-align: center; 2317 | color: #666; 2318 | opacity: 0; 2319 | transition: opacity .1s linear, background-color .1s linear; 2320 | border-radius: 3px; 2321 | transform: translateY(-50%); 2322 | z-index: 10; 2323 | } 2324 | .tab-item:after { 2325 | position: absolute; 2326 | top: 0; 2327 | right: 0; 2328 | bottom: 0; 2329 | left: 0; 2330 | content: ""; 2331 | background-color: rgba(0, 0, 0, 0.08); 2332 | opacity: 0; 2333 | transition: opacity .1s linear; 2334 | z-index: 1; 2335 | } 2336 | .tab-item:hover:not(.active):after { 2337 | opacity: 1; 2338 | } 2339 | .tab-item:hover .icon-close-tab { 2340 | opacity: 1; 2341 | } 2342 | .tab-item .icon-close-tab:hover { 2343 | background-color: rgba(0, 0, 0, 0.08); 2344 | } 2345 | 2346 | .tab-item-fixed { 2347 | flex: none; 2348 | padding: 3px 10px; 2349 | } 2350 | -------------------------------------------------------------------------------- /app/assets/css/photon.min.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";/*! 2 | * ===================================================== 3 | * Photon v0.1.2 4 | * Copyright 2015 Connor Sears 5 | * Licensed under MIT (https://github.com/connors/proton/blob/master/LICENSE) 6 | * 7 | * v0.1.2 designed by @connors. 8 | * ===================================================== 9 | */audio,canvas,progress,sub,sup,video{vertical-align:baseline}body,html{height:100%}hr,html,label{overflow:hidden}.clearfix:after,.toolbar-actions:after,.toolbar:after{clear:both}*,img{-webkit-user-drag:text}.list-group *,.nav-group-item,h1,h2,h3,h4,h5,h6,label,td,th{white-space:nowrap;text-overflow:ellipsis}audio:not([controls]){display:none}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:36px}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sup{top:-.5em}.pane-group,.window{top:0;left:0;right:0}sub{bottom:-.25em}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}*{cursor:default;-webkit-user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box}html{width:100%}body{padding:0;margin:0;font-family:system,-apple-system,".SFNSDisplay-Regular","Helvetica Neue",Helvetica,"Segoe UI",sans-serif;font-size:13px;line-height:1.6;color:#333;background-color:transparent}.btn-dropdown:after,.icon:before{font-family:photon-entypo}hr{margin:15px 0;background:0 0;border:0;border-bottom:1px solid #ddd}h1,h2,h3,h4,h5,h6{margin-top:20px;margin-bottom:10px;font-weight:500;overflow:hidden}.btn .icon,.toolbar-header .title{margin-top:1px}h2{font-size:30px}h3{font-size:24px}h4{font-size:18px}h5{font-size:14px}.btn,h6{font-size:12px}.window{position:absolute;bottom:0;display:flex;flex-direction:column;background-color:#fff}.window-content{position:relative;overflow-y:auto;display:flex;flex:1}.selectable-text{cursor:text;-webkit-user-select:text}.btn,.title{cursor:default}.text-center{text-align:center}.text-right{text-align:right}.text-left{text-align:left}.btn,.title{text-align:center}.pull-left{float:left}.pull-right{float:right}.padded{padding:10px}.padded-less{padding:5px}.padded-more{padding:20px}.padded-vertically{padding-top:10px;padding-bottom:10px}.padded-vertically-less{padding-top:5px;padding-bottom:5px}.padded-vertically-more{padding-top:20px;padding-bottom:20px}.padded-horizontally{padding-right:10px;padding-left:10px}.padded-horizontally-less{padding-right:5px;padding-left:5px}.padded-horizontally-more{padding-right:20px;padding-left:20px}.padded-top{padding-top:10px}.padded-top-less{padding-top:5px}.padded-top-more{padding-top:20px}.padded-bottom{padding-bottom:10px}.padded-bottom-less{padding-bottom:5px}.padded-bottom-more{padding-bottom:20px}.sidebar{background-color:#f5f5f4}.draggable{-webkit-app-region:drag}.btn,.btn-group{vertical-align:middle;-webkit-app-region:no-drag}.clearfix:after,.clearfix:before{display:table;content:" "}.btn{display:inline-block;padding:3px 8px;margin-bottom:0;line-height:1.4;white-space:nowrap;background-image:none;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,.06)}.btn:focus{outline:0;box-shadow:none}.btn-mini{padding:2px 6px}.btn-large{padding:6px 12px}.btn-form{padding-right:20px;padding-left:20px}.btn-default{color:#333;background-color:#fcfcfc;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fcfcfc),color-stop(100%,#f1f1f1));background-image:-webkit-linear-gradient(top,#fcfcfc 0,#f1f1f1 100%);background-image:linear-gradient(to bottom,#fcfcfc 0,#f1f1f1 100%);border-color:#c2c0c2 #c2c0c2 #a19fa1}.btn-default:active{background-color:#ddd;background-image:none}.btn-negative,.btn-positive,.btn-primary,.btn-warning{color:#fff;text-shadow:0 1px 1px rgba(0,0,0,.1)}.btn-primary{border-color:#388df8 #388df8 #0866dc;background-color:#6eb4f7;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#6eb4f7),color-stop(100%,#1a82fb));background-image:-webkit-linear-gradient(top,#6eb4f7 0,#1a82fb 100%);background-image:linear-gradient(to bottom,#6eb4f7 0,#1a82fb 100%)}.btn-primary:active{background-color:#3e9bf4;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3e9bf4),color-stop(100%,#0469de));background-image:-webkit-linear-gradient(top,#3e9bf4 0,#0469de 100%);background-image:linear-gradient(to bottom,#3e9bf4 0,#0469de 100%)}.btn-positive{border-color:#29a03b #29a03b #248b34;background-color:#5bd46d;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#5bd46d),color-stop(100%,#29a03b));background-image:-webkit-linear-gradient(top,#5bd46d 0,#29a03b 100%);background-image:linear-gradient(to bottom,#5bd46d 0,#29a03b 100%)}.btn-positive:active{background-color:#34c84a;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#34c84a),color-stop(100%,#248b34));background-image:-webkit-linear-gradient(top,#34c84a 0,#248b34 100%);background-image:linear-gradient(to bottom,#34c84a 0,#248b34 100%)}.btn-negative{border-color:#fb2f29 #fb2f29 #fb1710;background-color:#fd918d;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fd918d),color-stop(100%,#fb2f29));background-image:-webkit-linear-gradient(top,#fd918d 0,#fb2f29 100%);background-image:linear-gradient(to bottom,#fd918d 0,#fb2f29 100%)}.btn-negative:active{background-color:#fc605b;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fc605b),color-stop(100%,#fb1710));background-image:-webkit-linear-gradient(top,#fc605b 0,#fb1710 100%);background-image:linear-gradient(to bottom,#fc605b 0,#fb1710 100%)}.btn-warning{border-color:#fcaa0e #fcaa0e #ee9d02;background-color:#fece72;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fece72),color-stop(100%,#fcaa0e));background-image:-webkit-linear-gradient(top,#fece72 0,#fcaa0e 100%);background-image:linear-gradient(to bottom,#fece72 0,#fcaa0e 100%)}.btn-warning:active{background-color:#fdbc40;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fdbc40),color-stop(100%,#ee9d02));background-image:-webkit-linear-gradient(top,#fdbc40 0,#ee9d02 100%);background-image:linear-gradient(to bottom,#fdbc40 0,#ee9d02 100%)}.btn .icon{float:left;width:14px;height:14px;margin-bottom:1px;color:#737475;font-size:14px;line-height:1}.btn .icon-text{margin-right:5px}.btn-dropdown:after{margin-left:5px;content:""}.btn-group{position:relative;display:inline-block}.toolbar-actions:after,.toolbar-actions:before,.toolbar:after,.toolbar:before{display:table;content:" "}.btn-group .btn{position:relative;float:left}.btn-group .btn:active,.btn-group .btn:focus{z-index:2}.btn-group .btn.active{z-index:3}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-group>.btn:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group .btn+.btn{border-left:1px solid #c2c0c2}.btn-group .btn+.btn.active{border-left:0}.btn-group .active{color:#fff;border:1px solid transparent;background-color:#6d6c6d;background-image:none}.btn-group .active .icon{color:#fff}.toolbar{min-height:22px;box-shadow:inset 0 1px 0 #f5f4f5;background-color:#e8e6e8;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#e8e6e8),color-stop(100%,#d1cfd1));background-image:-webkit-linear-gradient(top,#e8e6e8 0,#d1cfd1 100%);background-image:linear-gradient(to bottom,#e8e6e8 0,#d1cfd1 100%)}.toolbar-header{border-bottom:1px solid #c2c0c2}.toolbar-footer{border-top:1px solid #c2c0c2;-webkit-app-region:drag}.title{margin:0;font-size:12px;font-weight:400;color:#555}.toolbar-borderless{border-top:0;border-bottom:0}.toolbar-actions{margin-top:4px;margin-bottom:3px;padding-right:3px;padding-left:3px;padding-bottom:3px;-webkit-app-region:drag}.form-control,label{display:inline-block;font-size:13px}.toolbar-actions>.btn,.toolbar-actions>.btn-group{margin-left:4px;margin-right:4px}label{margin-bottom:5px}input[type=search]{-webkit-appearance:textfield;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;line-height:normal}.checkbox,.form-group,.radio{margin-bottom:10px}.form-control{width:100%;min-height:25px;padding:5px 10px;line-height:1.6;background-color:#fff;border:1px solid #ddd;border-radius:4px;outline:0}.form-control:focus{border-color:#6db3fd;box-shadow:3px 3px 0 #6db3fd,-3px -3px 0 #6db3fd,-3px 3px 0 #6db3fd,3px -3px 0 #6db3fd}textarea{height:auto}.checkbox,.radio{position:relative;display:block;margin-top:10px}.checkbox label,.radio label{padding-left:20px;margin-bottom:0;font-weight:400}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-left:-20px;margin-top:4px}.form-actions .btn{margin-right:10px}.form-actions .btn:last-child{margin-right:0}.pane-group{position:absolute;bottom:0;display:flex}.icon:before,.pane,.tab-item{position:relative}.pane{overflow-y:auto;flex:1;border-left:1px solid #ddd}.list-group *,.media-body,.nav-group-item,td,th{overflow:hidden}.pane:first-child{border-left:0}.pane-sm{max-width:220px;min-width:150px}.pane-mini{width:80px;flex:none}.pane-one-fourth{width:25%;flex:none}.pane-one-third{width:33.3%}.img-circle{border-radius:50%}.img-rounded{border-radius:4px}.list-group{width:100%;list-style:none;margin:0;padding:0}.list-group *{margin:0}.list-group-item{padding:10px;font-size:12px;color:#414142;border-top:1px solid #ddd}.list-group-item:first-child{border-top:0}.list-group-item.active,.list-group-item.selected{color:#fff;background-color:#116cd6}.list-group-header{padding:10px}.media-object{margin-top:3px}.media-object.pull-left{margin-right:10px}.media-object.pull-right{margin-left:10px}.nav-group{font-size:14px}.nav-group-item{padding:2px 10px 2px 25px;display:block;color:#333;text-decoration:none}.nav-group-item.active,.nav-group-item:active{background-color:#dcdfe1}.nav-group-item .icon{width:19px;height:18px;float:left;color:#737475;margin-top:-3px;margin-right:7px;font-size:18px;text-align:center}.nav-group-title{margin:0;padding:10px 10px 2px;font-size:12px;font-weight:500;color:#666}.icon:before,th{font-weight:400}@font-face{font-family:photon-entypo;src:url(../fonts/photon-entypo.eot);src:url(../fonts/photon-entypo.eot?#iefix) format("eot"),url(../fonts/photon-entypo.woff) format("woff"),url(../fonts/photon-entypo.ttf) format("truetype");font-weight:400;font-style:normal}.icon:before{display:inline-block;speak:none;font-size:100%;font-style:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-note:before{content:'\e800'}.icon-note-beamed:before{content:'\e801'}.icon-music:before{content:'\e802'}.icon-search:before{content:'\e803'}.icon-flashlight:before{content:'\e804'}.icon-mail:before{content:'\e805'}.icon-heart:before{content:'\e806'}.icon-heart-empty:before{content:'\e807'}.icon-star:before{content:'\e808'}.icon-star-empty:before{content:'\e809'}.icon-user:before{content:'\e80a'}.icon-users:before{content:'\e80b'}.icon-user-add:before{content:'\e80c'}.icon-video:before{content:'\e80d'}.icon-picture:before{content:'\e80e'}.icon-camera:before{content:'\e80f'}.icon-layout:before{content:'\e810'}.icon-menu:before{content:'\e811'}.icon-check:before{content:'\e812'}.icon-cancel:before{content:'\e813'}.icon-cancel-circled:before{content:'\e814'}.icon-cancel-squared:before{content:'\e815'}.icon-plus:before{content:'\e816'}.icon-plus-circled:before{content:'\e817'}.icon-plus-squared:before{content:'\e818'}.icon-minus:before{content:'\e819'}.icon-minus-circled:before{content:'\e81a'}.icon-minus-squared:before{content:'\e81b'}.icon-help:before{content:'\e81c'}.icon-help-circled:before{content:'\e81d'}.icon-info:before{content:'\e81e'}.icon-info-circled:before{content:'\e81f'}.icon-back:before{content:'\e820'}.icon-home:before{content:'\e821'}.icon-link:before{content:'\e822'}.icon-attach:before{content:'\e823'}.icon-lock:before{content:'\e824'}.icon-lock-open:before{content:'\e825'}.icon-eye:before{content:'\e826'}.icon-tag:before{content:'\e827'}.icon-bookmark:before{content:'\e828'}.icon-bookmarks:before{content:'\e829'}.icon-flag:before{content:'\e82a'}.icon-thumbs-up:before{content:'\e82b'}.icon-thumbs-down:before{content:'\e82c'}.icon-download:before{content:'\e82d'}.icon-upload:before{content:'\e82e'}.icon-upload-cloud:before{content:'\e82f'}.icon-reply:before{content:'\e830'}.icon-reply-all:before{content:'\e831'}.icon-forward:before{content:'\e832'}.icon-quote:before{content:'\e833'}.icon-code:before{content:'\e834'}.icon-export:before{content:'\e835'}.icon-pencil:before{content:'\e836'}.icon-feather:before{content:'\e837'}.icon-print:before{content:'\e838'}.icon-retweet:before{content:'\e839'}.icon-keyboard:before{content:'\e83a'}.icon-comment:before{content:'\e83b'}.icon-chat:before{content:'\e83c'}.icon-bell:before{content:'\e83d'}.icon-attention:before{content:'\e83e'}.icon-alert:before{content:'\e83f'}.icon-vcard:before{content:'\e840'}.icon-address:before{content:'\e841'}.icon-location:before{content:'\e842'}.icon-map:before{content:'\e843'}.icon-direction:before{content:'\e844'}.icon-compass:before{content:'\e845'}.icon-cup:before{content:'\e846'}.icon-trash:before{content:'\e847'}.icon-doc:before{content:'\e848'}.icon-docs:before{content:'\e849'}.icon-doc-landscape:before{content:'\e84a'}.icon-doc-text:before{content:'\e84b'}.icon-doc-text-inv:before{content:'\e84c'}.icon-newspaper:before{content:'\e84d'}.icon-book-open:before{content:'\e84e'}.icon-book:before{content:'\e84f'}.icon-folder:before{content:'\e850'}.icon-archive:before{content:'\e851'}.icon-box:before{content:'\e852'}.icon-rss:before{content:'\e853'}.icon-phone:before{content:'\e854'}.icon-cog:before{content:'\e855'}.icon-tools:before{content:'\e856'}.icon-share:before{content:'\e857'}.icon-shareable:before{content:'\e858'}.icon-basket:before{content:'\e859'}.icon-bag:before{content:'\e85a'}.icon-calendar:before{content:'\e85b'}.icon-login:before{content:'\e85c'}.icon-logout:before{content:'\e85d'}.icon-mic:before{content:'\e85e'}.icon-mute:before{content:'\e85f'}.icon-sound:before{content:'\e860'}.icon-volume:before{content:'\e861'}.icon-clock:before{content:'\e862'}.icon-hourglass:before{content:'\e863'}.icon-lamp:before{content:'\e864'}.icon-light-down:before{content:'\e865'}.icon-light-up:before{content:'\e866'}.icon-adjust:before{content:'\e867'}.icon-block:before{content:'\e868'}.icon-resize-full:before{content:'\e869'}.icon-resize-small:before{content:'\e86a'}.icon-popup:before{content:'\e86b'}.icon-publish:before{content:'\e86c'}.icon-window:before{content:'\e86d'}.icon-arrow-combo:before{content:'\e86e'}.icon-down-circled:before{content:'\e86f'}.icon-left-circled:before{content:'\e870'}.icon-right-circled:before{content:'\e871'}.icon-up-circled:before{content:'\e872'}.icon-down-open:before{content:'\e873'}.icon-left-open:before{content:'\e874'}.icon-right-open:before{content:'\e875'}.icon-up-open:before{content:'\e876'}.icon-down-open-mini:before{content:'\e877'}.icon-left-open-mini:before{content:'\e878'}.icon-right-open-mini:before{content:'\e879'}.icon-up-open-mini:before{content:'\e87a'}.icon-down-open-big:before{content:'\e87b'}.icon-left-open-big:before{content:'\e87c'}.icon-right-open-big:before{content:'\e87d'}.icon-up-open-big:before{content:'\e87e'}.icon-down:before{content:'\e87f'}.icon-left:before{content:'\e880'}.icon-right:before{content:'\e881'}.icon-up:before{content:'\e882'}.icon-down-dir:before{content:'\e883'}.icon-left-dir:before{content:'\e884'}.icon-right-dir:before{content:'\e885'}.icon-up-dir:before{content:'\e886'}.icon-down-bold:before{content:'\e887'}.icon-left-bold:before{content:'\e888'}.icon-right-bold:before{content:'\e889'}.icon-up-bold:before{content:'\e88a'}.icon-down-thin:before{content:'\e88b'}.icon-left-thin:before{content:'\e88c'}.icon-right-thin:before{content:'\e88d'}.icon-up-thin:before{content:'\e88e'}.icon-ccw:before{content:'\e88f'}.icon-cw:before{content:'\e890'}.icon-arrows-ccw:before{content:'\e891'}.icon-level-down:before{content:'\e892'}.icon-level-up:before{content:'\e893'}.icon-shuffle:before{content:'\e894'}.icon-loop:before{content:'\e895'}.icon-switch:before{content:'\e896'}.icon-play:before{content:'\e897'}.icon-stop:before{content:'\e898'}.icon-pause:before{content:'\e899'}.icon-record:before{content:'\e89a'}.icon-to-end:before{content:'\e89b'}.icon-to-start:before{content:'\e89c'}.icon-fast-forward:before{content:'\e89d'}.icon-fast-backward:before{content:'\e89e'}.icon-progress-0:before{content:'\e89f'}.icon-progress-1:before{content:'\e8a0'}.icon-progress-2:before{content:'\e8a1'}.icon-progress-3:before{content:'\e8a2'}.icon-target:before{content:'\e8a3'}.icon-palette:before{content:'\e8a4'}.icon-list:before{content:'\e8a5'}.icon-list-add:before{content:'\e8a6'}.icon-signal:before{content:'\e8a7'}.icon-trophy:before{content:'\e8a8'}.icon-battery:before{content:'\e8a9'}.icon-back-in-time:before{content:'\e8aa'}.icon-monitor:before{content:'\e8ab'}.icon-mobile:before{content:'\e8ac'}.icon-network:before{content:'\e8ad'}.icon-cd:before{content:'\e8ae'}.icon-inbox:before{content:'\e8af'}.icon-install:before{content:'\e8b0'}.icon-globe:before{content:'\e8b1'}.icon-cloud:before{content:'\e8b2'}.icon-cloud-thunder:before{content:'\e8b3'}.icon-flash:before{content:'\e8b4'}.icon-moon:before{content:'\e8b5'}.icon-flight:before{content:'\e8b6'}.icon-paper-plane:before{content:'\e8b7'}.icon-leaf:before{content:'\e8b8'}.icon-lifebuoy:before{content:'\e8b9'}.icon-mouse:before{content:'\e8ba'}.icon-briefcase:before{content:'\e8bb'}.icon-suitcase:before{content:'\e8bc'}.icon-dot:before{content:'\e8bd'}.icon-dot-2:before{content:'\e8be'}.icon-dot-3:before{content:'\e8bf'}.icon-brush:before{content:'\e8c0'}.icon-magnet:before{content:'\e8c1'}.icon-infinity:before{content:'\e8c2'}.icon-erase:before{content:'\e8c3'}.icon-chart-pie:before{content:'\e8c4'}.icon-chart-line:before{content:'\e8c5'}.icon-chart-bar:before{content:'\e8c6'}.icon-chart-area:before{content:'\e8c7'}.icon-tape:before{content:'\e8c8'}.icon-graduation-cap:before{content:'\e8c9'}.icon-language:before{content:'\e8ca'}.icon-ticket:before{content:'\e8cb'}.icon-water:before{content:'\e8cc'}.icon-droplet:before{content:'\e8cd'}.icon-air:before{content:'\e8ce'}.icon-credit-card:before{content:'\e8cf'}.icon-floppy:before{content:'\e8d0'}.icon-clipboard:before{content:'\e8d1'}.icon-megaphone:before{content:'\e8d2'}.icon-database:before{content:'\e8d3'}.icon-drive:before{content:'\e8d4'}.icon-bucket:before{content:'\e8d5'}.icon-thermometer:before{content:'\e8d6'}.icon-key:before{content:'\e8d7'}.icon-flow-cascade:before{content:'\e8d8'}.icon-flow-branch:before{content:'\e8d9'}.icon-flow-tree:before{content:'\e8da'}.icon-flow-line:before{content:'\e8db'}.icon-flow-parallel:before{content:'\e8dc'}.icon-rocket:before{content:'\e8dd'}.icon-gauge:before{content:'\e8de'}.icon-traffic-cone:before{content:'\e8df'}.icon-cc:before{content:'\e8e0'}.icon-cc-by:before{content:'\e8e1'}.icon-cc-nc:before{content:'\e8e2'}.icon-cc-nc-eu:before{content:'\e8e3'}.icon-cc-nc-jp:before{content:'\e8e4'}.icon-cc-sa:before{content:'\e8e5'}.icon-cc-nd:before{content:'\e8e6'}.icon-cc-pd:before{content:'\e8e7'}.icon-cc-zero:before{content:'\e8e8'}.icon-cc-share:before{content:'\e8e9'}.icon-cc-remix:before{content:'\e8ea'}.icon-github:before{content:'\e8eb'}.icon-github-circled:before{content:'\e8ec'}.icon-flickr:before{content:'\e8ed'}.icon-flickr-circled:before{content:'\e8ee'}.icon-vimeo:before{content:'\e8ef'}.icon-vimeo-circled:before{content:'\e8f0'}.icon-twitter:before{content:'\e8f1'}.icon-twitter-circled:before{content:'\e8f2'}.icon-facebook:before{content:'\e8f3'}.icon-facebook-circled:before{content:'\e8f4'}.icon-facebook-squared:before{content:'\e8f5'}.icon-gplus:before{content:'\e8f6'}.icon-gplus-circled:before{content:'\e8f7'}.icon-pinterest:before{content:'\e8f8'}.icon-pinterest-circled:before{content:'\e8f9'}.icon-tumblr:before{content:'\e8fa'}.icon-tumblr-circled:before{content:'\e8fb'}.icon-linkedin:before{content:'\e8fc'}.icon-linkedin-circled:before{content:'\e8fd'}.icon-dribbble:before{content:'\e8fe'}.icon-dribbble-circled:before{content:'\e8ff'}.icon-stumbleupon:before{content:'\e900'}.icon-stumbleupon-circled:before{content:'\e901'}.icon-lastfm:before{content:'\e902'}.icon-lastfm-circled:before{content:'\e903'}.icon-rdio:before{content:'\e904'}.icon-rdio-circled:before{content:'\e905'}.icon-spotify:before{content:'\e906'}.icon-spotify-circled:before{content:'\e907'}.icon-qq:before{content:'\e908'}.icon-instagram:before{content:'\e909'}.icon-dropbox:before{content:'\e90a'}.icon-evernote:before{content:'\e90b'}.icon-flattr:before{content:'\e90c'}.icon-skype:before{content:'\e90d'}.icon-skype-circled:before{content:'\e90e'}.icon-renren:before{content:'\e90f'}.icon-sina-weibo:before{content:'\e910'}.icon-paypal:before{content:'\e911'}.icon-picasa:before{content:'\e912'}.icon-soundcloud:before{content:'\e913'}.icon-mixi:before{content:'\e914'}.icon-behance:before{content:'\e915'}.icon-google-circles:before{content:'\e916'}.icon-vkontakte:before{content:'\e917'}.icon-smashing:before{content:'\e918'}.icon-sweden:before{content:'\e919'}.icon-db-shape:before{content:'\e91a'}.icon-logo-db:before{content:'\e91b'}table{border-spacing:0;width:100%;border:0;border-collapse:separate;font-size:12px;text-align:left}.table-striped tr:nth-child(even),thead{background-color:#f5f5f4}tbody{background-color:#fff}.table-striped tr:active:nth-child(even),tr:active{color:#fff;background-color:#116cd6}thead tr:active{color:#333;background-color:#f5f5f4}th{border-right:1px solid #ddd;border-bottom:1px solid #ddd}td,th{padding:2px 15px}td:last-child,th:last-child{border-right:0}.tab-group{margin-top:-1px;display:flex;border-top:1px solid #989698;border-bottom:1px solid #989698}.tab-item{flex:1;padding:3px;font-size:12px;text-align:center;border-left:1px solid #989698;background-color:#b8b6b8;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#b8b6b8),color-stop(100%,#b0aeb0));background-image:-webkit-linear-gradient(top,#b8b6b8 0,#b0aeb0 100%);background-image:linear-gradient(to bottom,#b8b6b8 0,#b0aeb0 100%)}.tab-item:first-child{border-left:0}.tab-item.active{background-color:#d4d2d4;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#d4d2d4),color-stop(100%,#cccacc));background-image:-webkit-linear-gradient(top,#d4d2d4 0,#cccacc 100%);background-image:linear-gradient(to bottom,#d4d2d4 0,#cccacc 100%)}.tab-item .icon-close-tab:hover,.tab-item:after{background-color:rgba(0,0,0,.08)}.tab-item .icon-close-tab{position:absolute;top:50%;left:5px;width:15px;height:15px;font-size:15px;line-height:15px;text-align:center;color:#666;opacity:0;transition:opacity .1s linear,background-color .1s linear;border-radius:3px;transform:translateY(-50%);z-index:10}.tab-item:after{position:absolute;top:0;right:0;bottom:0;left:0;content:"";opacity:0;transition:opacity .1s linear;z-index:1}.tab-item:hover .icon-close-tab,.tab-item:hover:not(.active):after{opacity:1}.tab-item-fixed{flex:none;padding:3px 10px} -------------------------------------------------------------------------------- /app/assets/fonts/photon-entypo.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/app/assets/fonts/photon-entypo.eot -------------------------------------------------------------------------------- /app/assets/fonts/photon-entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/app/assets/fonts/photon-entypo.ttf -------------------------------------------------------------------------------- /app/assets/fonts/photon-entypo.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/app/assets/fonts/photon-entypo.woff -------------------------------------------------------------------------------- /app/assets/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/app/assets/images/background.png -------------------------------------------------------------------------------- /app/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/app/assets/images/icon.png -------------------------------------------------------------------------------- /app/assets/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/app/assets/images/icons.png -------------------------------------------------------------------------------- /app/background.js: -------------------------------------------------------------------------------- 1 | // This is main process of Electron, started as first thing when your 2 | // app starts. This script is running through entire life of your application. 3 | // It doesn't have any windows which you can see on screen, but we can open 4 | // window from here. 5 | 6 | import { app, dialog, ipcMain, Menu } from 'electron'; 7 | import devMenuTemplate from './helpers/dev_menu_template'; 8 | import appMenuTemplate from './helpers/app_menu_template'; 9 | import createWindow from './helpers/window'; 10 | 11 | // Special module holding environment letiables which you declared 12 | // in config/env_xxx.json file. 13 | import env from './env'; 14 | 15 | let pkg = require('./package.json'); 16 | 17 | let mainWindow; 18 | 19 | let setApplicationMenu = () => { 20 | let menus = appMenuTemplate; 21 | if (env.name !== 'production') { 22 | menus.push(devMenuTemplate); 23 | } 24 | Menu.setApplicationMenu(Menu.buildFromTemplate(menus)); 25 | }; 26 | 27 | let handleError = (win, msg) => { 28 | const options = { 29 | type: 'info', 30 | title: 'Error', 31 | message: msg, 32 | buttons: ['Reload', 'Close'] 33 | } 34 | 35 | dialog.showMessageBox(options, (index) => { 36 | if (index === 0) { 37 | win.reload(); 38 | } 39 | else { 40 | win.close(); 41 | } 42 | }) 43 | }; 44 | 45 | app.on('ready', () => { 46 | setApplicationMenu(); 47 | 48 | let mainWindow = createWindow('main', { 49 | minWidth: 300, 50 | minHeight: 700, 51 | width: 350, 52 | height: 830, 53 | titleBarStyle: 'hidden' 54 | }); 55 | 56 | mainWindow.setTitle(pkg.productName); 57 | 58 | mainWindow.webContents.on('crashed', () => { 59 | return handleError('An unexpected error has occurred.', mainWindow); 60 | }); 61 | 62 | mainWindow.on('unresponsive', () => { 63 | return handleError('The app is not responding.', mainWindow); 64 | }); 65 | 66 | mainWindow.loadURL('file://' + __dirname + '/pages/app.html'); 67 | 68 | if (env.name == 'development') { 69 | mainWindow.openDevTools({ mode: 'detach' }); 70 | require('devtron').install(); 71 | } 72 | 73 | ipcMain.on('open-error-dialog', () => { 74 | dialog.showErrorBox('Error', 'Oops! Something went wrong and we couldn\'t log you in using Uber. Please try again.'); 75 | }); 76 | 77 | ipcMain.on('load-success-page', (event, access_token) => { 78 | mainWindow.loadURL('file://' + __dirname + '/pages/success.html'); 79 | mainWindow.webContents.on('did-finish-load', () => { 80 | mainWindow.webContents.send('show-access-token', access_token); 81 | }); 82 | }); 83 | }); 84 | 85 | app.on('window-all-closed', () => { 86 | app.quit(); 87 | }); 88 | 89 | process.on('uncaughtException', () => { 90 | return handleError('Oops! Something went wrong.', mainWindow); 91 | }); 92 | -------------------------------------------------------------------------------- /app/env.js: -------------------------------------------------------------------------------- 1 | // Simple wrapper exposing environment variables to rest of the code. 2 | 3 | import jetpack from 'fs-jetpack'; 4 | 5 | // The variables have been written to `env.json` by the build process. 6 | var env = jetpack.cwd(__dirname).read('env.json', 'json'); 7 | 8 | export default env; 9 | -------------------------------------------------------------------------------- /app/helpers/app_menu_template.js: -------------------------------------------------------------------------------- 1 | import { app, shell } from 'electron'; 2 | 3 | let template = [ 4 | { 5 | label: 'Edit', 6 | submenu: [ 7 | { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" }, 8 | { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" }, 9 | { type: "separator" }, 10 | { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" }, 11 | { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" }, 12 | { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" }, 13 | { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" } 14 | ] 15 | }, 16 | { 17 | role: 'window', 18 | submenu: [ 19 | { label: 'Close', accelerator: 'CmdOrCtrl+W', role: 'close' }, 20 | { label: 'Minimize', accelerator: 'CmdOrCtrl+M', role: 'minimize' }, 21 | { type: 'separator' }, 22 | { label: 'Bring All to Front', role: 'front' } 23 | ] 24 | }, 25 | { 26 | role: 'help', 27 | submenu: [ 28 | { label: 'Donate', click() { shell.openExternal('https://github.com/chrisenytc/ubauth#donate'); } }, 29 | { label: 'Author', click() { shell.openExternal('https://github.com/chrisenytc'); } }, 30 | { label: 'Contribute', click() { shell.openExternal('https://github.com/chrisenytc/ubauth#contributing'); } }, 31 | { label: 'Learn More', click() { shell.openExternal('http://ubauth.enytc.com'); } } 32 | ] 33 | }, 34 | ]; 35 | 36 | if (process.platform === 'darwin') { 37 | let name = app.getName(); 38 | 39 | template.unshift({ 40 | label: name, 41 | submenu: [ 42 | { 43 | role: 'about' 44 | }, 45 | { 46 | type: 'separator' 47 | }, 48 | { 49 | role: 'services', 50 | submenu: [] 51 | }, 52 | { 53 | type: 'separator' 54 | }, 55 | { 56 | role: 'hide' 57 | }, 58 | { 59 | role: 'hideothers' 60 | }, 61 | { 62 | role: 'unhide' 63 | }, 64 | { 65 | type: 'separator' 66 | }, 67 | { 68 | role: 'quit' 69 | }, 70 | ] 71 | }); 72 | } 73 | 74 | export default template; 75 | -------------------------------------------------------------------------------- /app/helpers/context_menu.js: -------------------------------------------------------------------------------- 1 | // This gives you default context menu (cut, copy, paste) 2 | // in all input fields and textareas across your app. 3 | 4 | (function () { 5 | 'use strict'; 6 | 7 | var remote = require('electron').remote; 8 | var Menu = remote.Menu; 9 | var MenuItem = remote.MenuItem; 10 | 11 | var isAnyTextSelected = function () { 12 | return window.getSelection().toString() !== ''; 13 | }; 14 | 15 | var cut = new MenuItem({ 16 | label: "Cut", 17 | click: function () { 18 | document.execCommand("cut"); 19 | } 20 | }); 21 | 22 | var copy = new MenuItem({ 23 | label: "Copy", 24 | click: function () { 25 | document.execCommand("copy"); 26 | } 27 | }); 28 | 29 | var paste = new MenuItem({ 30 | label: "Paste", 31 | click: function () { 32 | document.execCommand("paste"); 33 | } 34 | }); 35 | 36 | var normalMenu = new Menu(); 37 | normalMenu.append(copy); 38 | 39 | var textEditingMenu = new Menu(); 40 | textEditingMenu.append(cut); 41 | textEditingMenu.append(copy); 42 | textEditingMenu.append(paste); 43 | 44 | document.addEventListener('contextmenu', function (e) { 45 | switch (e.target.nodeName) { 46 | case 'TEXTAREA': 47 | case 'INPUT': 48 | e.preventDefault(); 49 | textEditingMenu.popup(remote.getCurrentWindow()); 50 | break; 51 | default: 52 | if (isAnyTextSelected()) { 53 | e.preventDefault(); 54 | normalMenu.popup(remote.getCurrentWindow()); 55 | } 56 | } 57 | }, false); 58 | 59 | }()); 60 | -------------------------------------------------------------------------------- /app/helpers/dev_menu_template.js: -------------------------------------------------------------------------------- 1 | import { app, BrowserWindow } from 'electron'; 2 | 3 | export default { 4 | label: 'Development', 5 | submenu: [{ 6 | label: 'Reload', 7 | accelerator: 'CmdOrCtrl+R', 8 | click: function () { 9 | BrowserWindow.getFocusedWindow().webContents.reloadIgnoringCache(); 10 | } 11 | },{ 12 | label: 'Toggle DevTools', 13 | accelerator: 'Alt+CmdOrCtrl+I', 14 | click: function () { 15 | BrowserWindow.getFocusedWindow().toggleDevTools(); 16 | } 17 | },{ 18 | label: 'Quit', 19 | accelerator: 'CmdOrCtrl+Q', 20 | click: function () { 21 | app.quit(); 22 | } 23 | }] 24 | }; 25 | -------------------------------------------------------------------------------- /app/helpers/external_links.js: -------------------------------------------------------------------------------- 1 | // Convenient way for opening links in external browser, not in the app. 2 | // Useful especially if you have a lot of links to deal with. 3 | // 4 | // Usage: 5 | // 6 | // Every link with class ".js-external-link" will be opened in external browser. 7 | // google 8 | // 9 | // The same behaviour for many links can be achieved by adding 10 | // this class to any parent tag of an anchor tag. 11 | // 15 | 16 | (function () { 17 | 'use strict'; 18 | 19 | var shell = require('electron').shell; 20 | 21 | var supportExternalLinks = function (e) { 22 | var href; 23 | var isExternal = false; 24 | 25 | var checkDomElement = function (element) { 26 | if (element.nodeName === 'A') { 27 | href = element.getAttribute('href'); 28 | } 29 | if (element.classList.contains('js-external-link')) { 30 | isExternal = true; 31 | } 32 | if (href && isExternal) { 33 | shell.openExternal(href); 34 | e.preventDefault(); 35 | } else if (element.parentElement) { 36 | checkDomElement(element.parentElement); 37 | } 38 | }; 39 | 40 | checkDomElement(e.target); 41 | }; 42 | 43 | document.addEventListener('click', supportExternalLinks, false); 44 | }()); 45 | -------------------------------------------------------------------------------- /app/helpers/window.js: -------------------------------------------------------------------------------- 1 | // This helper remembers the size and position of your windows (and restores 2 | // them in that place after app relaunch). 3 | // Can be used for more than one window, just construct many 4 | // instances of it and give each different name. 5 | 6 | import { app, BrowserWindow, screen } from 'electron'; 7 | import jetpack from 'fs-jetpack'; 8 | 9 | export default function (name, options) { 10 | 11 | var userDataDir = jetpack.cwd(app.getPath('userData')); 12 | var stateStoreFile = 'window-state-' + name +'.json'; 13 | var defaultSize = { 14 | width: options.width, 15 | height: options.height 16 | }; 17 | var state = {}; 18 | var win; 19 | 20 | var restore = function () { 21 | var restoredState = {}; 22 | try { 23 | restoredState = userDataDir.read(stateStoreFile, 'json'); 24 | } catch (err) { 25 | // For some reason json can't be read (might be corrupted). 26 | // No worries, we have defaults. 27 | } 28 | return Object.assign({}, defaultSize, restoredState); 29 | }; 30 | 31 | var getCurrentPosition = function () { 32 | var position = win.getPosition(); 33 | var size = win.getSize(); 34 | return { 35 | x: position[0], 36 | y: position[1], 37 | width: size[0], 38 | height: size[1] 39 | }; 40 | }; 41 | 42 | var windowWithinBounds = function (windowState, bounds) { 43 | return windowState.x >= bounds.x && 44 | windowState.y >= bounds.y && 45 | windowState.x + windowState.width <= bounds.x + bounds.width && 46 | windowState.y + windowState.height <= bounds.y + bounds.height; 47 | }; 48 | 49 | var resetToDefaults = function () { 50 | var bounds = screen.getPrimaryDisplay().bounds; 51 | return Object.assign({}, defaultSize, { 52 | x: (bounds.width - defaultSize.width) / 2, 53 | y: (bounds.height - defaultSize.height) / 2 54 | }); 55 | }; 56 | 57 | var ensureVisibleOnSomeDisplay = function (windowState) { 58 | var visible = screen.getAllDisplays().some(function (display) { 59 | return windowWithinBounds(windowState, display.bounds); 60 | }); 61 | if (!visible) { 62 | // Window is partially or fully not visible now. 63 | // Reset it to safe defaults. 64 | return resetToDefaults(windowState); 65 | } 66 | return windowState; 67 | }; 68 | 69 | var saveState = function () { 70 | if (!win.isMinimized() && !win.isMaximized()) { 71 | Object.assign(state, getCurrentPosition()); 72 | } 73 | userDataDir.write(stateStoreFile, state, { atomic: true }); 74 | }; 75 | 76 | state = ensureVisibleOnSomeDisplay(restore()); 77 | 78 | win = new BrowserWindow(Object.assign({}, options, state)); 79 | 80 | win.on('close', saveState); 81 | 82 | return win; 83 | } 84 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ubauth", 3 | "productName": "UbAuth", 4 | "description": "A desktop app to help developers create access tokens for Uber applications with OAuth 2.0.", 5 | "version": "1.0.0", 6 | "homepage": "https://github.com/chrisenytc/ubauth", 7 | "author": { 8 | "name": "Christopher EnyTC", 9 | "email": "chris@enytc.com" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/chrisenytc/ubauth.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/chrisenytc/ubauth/issues" 17 | }, 18 | "license": "MIT", 19 | "private": true, 20 | "copyright": "Copyright © 2016 Christopher EnyTC. \nAll rights reserved.", 21 | "main": "background.js", 22 | "dependencies": { 23 | "angular": "^1.5.8", 24 | "fs-jetpack": "^0.9.0" 25 | }, 26 | "packageNameTemplate": "{{name}}-{{platform}}-{{arch}}", 27 | "osx": { 28 | "build": "1", 29 | "identifier": "com.enytc.ubauth", 30 | "LSApplicationCategoryType": "public.app-category.developer-tools" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/pages/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 51 | 52 | 53 | 54 |
55 |
56 | UbAuth 57 |
58 | 59 |
60 |
61 | 62 | 63 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /app/pages/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 29 | 30 | 31 | 32 |
33 |
34 | UbAuth 35 |
36 | 37 |
38 |
39 | 40 | 41 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/controllers/auth.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Controller: authCtrl 3 | */ 4 | 5 | export default (app, window) => { 6 | return app.controller('authCtrl', ['$scope', 'Uber', function authCtrl($scope, Uber) { 7 | $scope.submit = () => { 8 | if ($scope.authForm.$valid) { 9 | var options = $scope.options; 10 | return Uber.authorize(options); 11 | } else { 12 | window.angular.forEach($scope.authForm.$error.required, function(field) { 13 | field.$setDirty(); 14 | }); 15 | } 16 | }; 17 | }]); 18 | }; 19 | -------------------------------------------------------------------------------- /app/src/controllers/success.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Controller: successCtrl 3 | */ 4 | 5 | import { clipboard } from 'electron'; 6 | 7 | export default (app, window, access_token) => { 8 | return app.controller('successCtrl', ['$scope', function successCtrl($scope) { 9 | $scope.access_token = access_token; 10 | 11 | $scope.copy = () => { 12 | clipboard.writeText(access_token); 13 | 14 | new Notification('Success', { 15 | body: 'Copied to clipboard.' 16 | }); 17 | }; 18 | }]); 19 | }; 20 | -------------------------------------------------------------------------------- /app/src/services/uber.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Service: Uber 3 | */ 4 | 5 | import crypto from 'crypto'; 6 | import querystring from 'querystring'; 7 | import { remote, ipcRenderer } from 'electron'; 8 | 9 | const BrowserWindow = remote.BrowserWindow; 10 | 11 | export default (app) => { 12 | return app.factory('Uber', ($http) => { 13 | return { 14 | authorize: (options) => { 15 | //Build the OAuth consent page URL 16 | let authWindow = new BrowserWindow({ 17 | minWidth: 600, 18 | minHeight: 835, 19 | width: 600, 20 | height: 835, 21 | show: true, 22 | center: true, 23 | resizable: true, 24 | webPreferences: { 25 | nodeIntegration: false 26 | }, 27 | titleBarStyle: 'hidden' 28 | }); 29 | 30 | // Generate request state 31 | let state = crypto.randomBytes(256).toString('hex') 32 | // Populate data 33 | let data = { 34 | response_type: 'code', 35 | client_id: options.client_id, 36 | scope: options.scope, 37 | state: state, 38 | redirect_uri: options.redirect_url, 39 | }; 40 | 41 | let qs = querystring.stringify(data) 42 | let authUrl = 'https://login.uber.com/oauth/v2/authorize?' + qs; 43 | 44 | // Get token 45 | let getAccessToken = (code) => { 46 | let data = { 47 | grant_type: 'authorization_code', 48 | client_id: options.client_id, 49 | client_secret: options.client_secret, 50 | redirect_uri: options.redirect_url, 51 | code: code 52 | }; 53 | 54 | let request_data = { 55 | method: 'POST', 56 | url: 'https://login.uber.com/oauth/v2/token', 57 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 58 | transformRequest: function(obj) { 59 | var str = []; 60 | for(var p in obj) 61 | str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); 62 | return str.join("&"); 63 | }, 64 | data: data 65 | }; 66 | 67 | return $http(request_data) 68 | .success((data, status) => { 69 | if (status === 200) { 70 | return ipcRenderer.send('load-success-page', data.access_token); 71 | } 72 | }) 73 | .error(() => { 74 | return ipcRenderer.send('open-error-dialog'); 75 | }); 76 | }; 77 | 78 | let handleCallback = (url) => { 79 | let raw_code = /code=([^&]*)/.exec(url) || null; 80 | let code = (raw_code && raw_code.length > 1) ? raw_code[1] : null; 81 | let error = /\?error=(.+)$/.exec(url); 82 | 83 | if (!code && error) { 84 | // Close the browser if has an error 85 | authWindow.destroy(); 86 | return ipcRenderer.send('open-error-dialog'); 87 | } 88 | 89 | // If there is a code, proceed to get token from github 90 | if (code) { 91 | authWindow.destroy(); 92 | return getAccessToken(code); 93 | } 94 | }; 95 | 96 | authWindow.loadURL(authUrl); 97 | 98 | authWindow.webContents.on('will-navigate', (event, url) => { 99 | return handleCallback(url); 100 | }); 101 | 102 | authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => { 103 | return handleCallback(newUrl); 104 | }); 105 | 106 | // If "Done" button is pressed, hide "Loading" 107 | authWindow.on('close', () => { 108 | authWindow = null; 109 | }); 110 | } 111 | }; 112 | }); 113 | }; 114 | -------------------------------------------------------------------------------- /app/test/bootstrap.spec.js: -------------------------------------------------------------------------------- 1 | import './test_helper'; 2 | 3 | let pkg = require('./package.json'); 4 | 5 | describe("Bootstrap", function() { 6 | 7 | describe('#MainWindow', function() { 8 | 9 | beforeEach(function() { 10 | return this.app.client.waitUntilWindowLoaded(); 11 | }); 12 | 13 | it('should open only 1', function() { 14 | return this.app.client.getWindowCount() 15 | .should.eventually.equal(1); 16 | }); 17 | 18 | it('should not be minimized', function() { 19 | return this.app.client.browserWindow.isMinimized() 20 | .should.eventually.be.false; 21 | }); 22 | 23 | it('should not open the DevTools window', function() { 24 | return this.app.client.browserWindow.isDevToolsOpened() 25 | .should.eventually.be.false; 26 | }); 27 | 28 | it('should be visiable', function() { 29 | return this.app.client.browserWindow.isVisible() 30 | .should.eventually.be.true; 31 | }); 32 | 33 | it('should be focused', function() { 34 | return this.app.client.browserWindow.isFocused() 35 | .should.eventually.be.true; 36 | }); 37 | 38 | it('should have title equals to \'' + pkg.productName + '\'', function() { 39 | return this.app.client.browserWindow.getTitle() 40 | .should.eventually.be.equal(pkg.productName); 41 | }); 42 | 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /app/test/environment.spec.js: -------------------------------------------------------------------------------- 1 | import './test_helper'; 2 | import env from '../env'; 3 | 4 | describe('Environment', function() { 5 | 6 | it('environment variables should be on their place', function() { 7 | return env.name.should.be.equal('test'); 8 | }); 9 | 10 | }); 11 | -------------------------------------------------------------------------------- /app/test/test_helper.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import electron from 'electron-prebuilt'; 3 | import chai from 'chai'; 4 | import chaiAsPromised from 'chai-as-promised'; 5 | import { Application } from 'spectron'; 6 | 7 | chai.use(chaiAsPromised); 8 | 9 | chai.should(); 10 | 11 | beforeEach(function () { 12 | let appPath = path.join(__dirname, '..', 'build'); 13 | 14 | this.app = new Application({ 15 | path: electron, 16 | args: [appPath], 17 | startTimeout: 8000, 18 | chromeDriverLogPath: '/tmp/chromedriver.log', 19 | nodePath: process.env.NODE_PATH 20 | }); 21 | 22 | return this.app.start(); 23 | }); 24 | 25 | beforeEach(function () { 26 | chaiAsPromised.transferPromiseness = this.app.transferPromiseness; 27 | }); 28 | 29 | afterEach(function () { 30 | if (this.app && this.app.isRunning()) { 31 | return this.app.stop(); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /config/env_development.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "development" 3 | } 4 | -------------------------------------------------------------------------------- /config/env_production.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "production" 3 | } 4 | -------------------------------------------------------------------------------- /config/env_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test" 3 | } 4 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('./tasks/build/build'); 4 | require('./tasks/release/release'); 5 | require('./tasks/start'); 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "asar": "^0.11.0", 4 | "chai": "^3.5.0", 5 | "chai-as-promised": "^5.3.0", 6 | "codeclimate-test-reporter": "^0.3.3", 7 | "devtron": "^1.2.1", 8 | "electron-mocha": "^2.0.0", 9 | "electron-prebuilt": "^1.0.1", 10 | "eslint": "^3.1.1", 11 | "eslint-config-airbnb": "^9.0.1", 12 | "eslint-plugin-import": "^1.11.1", 13 | "fs-jetpack": "^0.9.0", 14 | "gulp": "^3.9.0", 15 | "gulp-batch": "^1.0.5", 16 | "gulp-util": "^3.0.6", 17 | "gulp-watch": "^4.3.5", 18 | "istanbul": "^0.4.4", 19 | "q": "^1.4.1", 20 | "rollup": "^0.26.3", 21 | "spectron": "^3.2.6", 22 | "yargs": "^4.2.0" 23 | }, 24 | "optionalDependencies": { 25 | "appdmg": "^0.3.2", 26 | "rcedit": "^0.5.0" 27 | }, 28 | "scripts": { 29 | "postinstall": "script/postinstall.sh", 30 | "build": "gulp build", 31 | "release": "gulp release --env=production", 32 | "start": "gulp start", 33 | "pretest": "gulp build --env=test", 34 | "test": "script/test.sh", 35 | "install-native": "node ./tasks/install_native_module", 36 | "clear": "rm -rf '/Users/chrisenytc/Library/Application Support/UbAuth Dev' && rm -rf '/Users/chrisenytc/Library/Application Support/UbAuth'" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /resources/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/banner.png -------------------------------------------------------------------------------- /resources/bitcoin-address.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/bitcoin-address.png -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/icon.png -------------------------------------------------------------------------------- /resources/linux/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: {{name}} 2 | Version: {{version}} 3 | Maintainer: {{author}} 4 | Priority: optional 5 | Architecture: amd64 6 | Installed-Size: {{size}} 7 | Description: {{description}} 8 | -------------------------------------------------------------------------------- /resources/linux/app.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Type=Application 4 | Encoding=UTF-8 5 | Name={{productName}} 6 | Comment={{description}} 7 | Exec=/opt/{{name}}/{{name}} 8 | Path=/opt/{{name}}/ 9 | Icon=/opt/{{name}}/icon.png 10 | Terminal=false 11 | Categories=Application; 12 | -------------------------------------------------------------------------------- /resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/logo.png -------------------------------------------------------------------------------- /resources/osx/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | {{productName}} 7 | CFBundleExecutable 8 | {{productName}} 9 | CFBundleIconFile 10 | icon.icns 11 | CFBundleIdentifier 12 | {{identifier}} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | {{productName}} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleVersion 20 | {{build}} 21 | CFBundleVersionString 22 | {{build}} 23 | CFBundleShortVersionString 24 | {{version}} 25 | CFBundleGetInfoString 26 | {{version}} 27 | LSMinimumSystemVersion 28 | 10.8.0 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | AtomApplication 33 | NSSupportsAutomaticGraphicsSwitching 34 | 35 | NSHumanReadableCopyright 36 | {{copyright}} 37 | LSApplicationCategoryType 38 | {{LSApplicationCategoryType}} 39 | 40 | 41 | -------------------------------------------------------------------------------- /resources/osx/appdmg.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "{{productName}}", 3 | "icon": "{{dmgIcon}}", 4 | "background": "{{dmgBackground}}", 5 | "icon-size": 128, 6 | "contents": [ 7 | { "x": 410, "y": 220, "type": "link", "path": "/Applications" }, 8 | { "x": 130, "y": 220, "type": "file", "path": "{{appPath}}" } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /resources/osx/child.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.inherit 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/osx/dmg-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/osx/dmg-background.png -------------------------------------------------------------------------------- /resources/osx/dmg-background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/osx/dmg-background@2x.png -------------------------------------------------------------------------------- /resources/osx/dmg-icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/osx/dmg-icon.icns -------------------------------------------------------------------------------- /resources/osx/helper_apps/Info EH.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | {{productName}} Helper EH 7 | CFBundleExecutable 8 | {{productName}} Helper EH 9 | CFBundleIdentifier 10 | {{identifier}}.helper.EH 11 | CFBundleName 12 | {{productName}} Helper EH 13 | CFBundlePackageType 14 | APPL 15 | DTSDKName 16 | macosx 17 | LSUIElement 18 | 19 | NSSupportsAutomaticGraphicsSwitching 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /resources/osx/helper_apps/Info NP.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | {{productName}} Helper NP 7 | CFBundleExecutable 8 | {{productName}} Helper NP 9 | CFBundleIdentifier 10 | {{identifier}}.helper.NP 11 | CFBundleName 12 | {{productName}} Helper NP 13 | CFBundlePackageType 14 | APPL 15 | DTSDKName 16 | macosx 17 | LSUIElement 18 | 19 | NSSupportsAutomaticGraphicsSwitching 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /resources/osx/helper_apps/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | {{identifier}}.helper 7 | CFBundleName 8 | {{productName}} Helper 9 | CFBundlePackageType 10 | APPL 11 | DTSDKName 12 | macosx 13 | LSUIElement 14 | 15 | NSSupportsAutomaticGraphicsSwitching 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /resources/osx/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/osx/icon.icns -------------------------------------------------------------------------------- /resources/osx/parent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.device.camera 8 | 9 | com.apple.security.device.microphone 10 | 11 | com.apple.security.files.user-selected.read-only 12 | 13 | com.apple.security.files.user-selected.read-write 14 | 15 | com.apple.security.network.client 16 | 17 | com.apple.security.network.server 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /resources/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/screenshot.png -------------------------------------------------------------------------------- /resources/windows/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/windows/icon.ico -------------------------------------------------------------------------------- /resources/windows/installer.nsi: -------------------------------------------------------------------------------- 1 | ; NSIS packaging/install script 2 | ; Docs: http://nsis.sourceforge.net/Docs/Contents.html 3 | 4 | !include LogicLib.nsh 5 | !include nsDialogs.nsh 6 | 7 | ; -------------------------------- 8 | ; Variables 9 | ; -------------------------------- 10 | 11 | !define dest "{{dest}}" 12 | !define src "{{src}}" 13 | !define name "{{name}}" 14 | !define productName "{{productName}}" 15 | !define author "{{author}}" 16 | !define version "{{version}}" 17 | !define icon "{{icon}}" 18 | !define setupIcon "{{setupIcon}}" 19 | !define banner "{{banner}}" 20 | 21 | !define exec "{{productName}}.exe" 22 | 23 | !define regkey "Software\${productName}" 24 | !define uninstkey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${productName}" 25 | 26 | !define uninstaller "uninstall.exe" 27 | 28 | ; -------------------------------- 29 | ; Installation 30 | ; -------------------------------- 31 | 32 | Unicode true 33 | SetCompressor /SOLID lzma 34 | 35 | Name "${productName}" 36 | Icon "${setupIcon}" 37 | OutFile "${dest}" 38 | InstallDir "$PROGRAMFILES\${productName}" 39 | InstallDirRegKey HKLM "${regkey}" "" 40 | 41 | RequestExecutionLevel admin 42 | CRCCheck on 43 | SilentInstall normal 44 | 45 | XPStyle on 46 | ShowInstDetails nevershow 47 | AutoCloseWindow false 48 | WindowIcon off 49 | 50 | Caption "${productName} Setup" 51 | ; Don't add sub-captions to title bar 52 | SubCaption 3 " " 53 | SubCaption 4 " " 54 | 55 | Page custom welcome 56 | Page instfiles 57 | 58 | Var Image 59 | Var ImageHandle 60 | 61 | Function .onInit 62 | 63 | ; Extract banner image for welcome page 64 | InitPluginsDir 65 | ReserveFile "${banner}" 66 | File /oname=$PLUGINSDIR\banner.bmp "${banner}" 67 | 68 | FunctionEnd 69 | 70 | ; Custom welcome page 71 | Function welcome 72 | 73 | nsDialogs::Create 1018 74 | 75 | ${NSD_CreateLabel} 185 1u 210 100% "Welcome to ${productName} version ${version} installer.$\r$\n$\r$\nClick install to begin." 76 | 77 | ${NSD_CreateBitmap} 0 0 170 210 "" 78 | Pop $Image 79 | ${NSD_SetImage} $Image $PLUGINSDIR\banner.bmp $ImageHandle 80 | 81 | nsDialogs::Show 82 | 83 | ${NSD_FreeImage} $ImageHandle 84 | 85 | FunctionEnd 86 | 87 | ; Installation declarations 88 | Section "Install" 89 | 90 | WriteRegStr HKLM "${regkey}" "Install_Dir" "$INSTDIR" 91 | WriteRegStr HKLM "${uninstkey}" "DisplayName" "${productName}" 92 | WriteRegStr HKLM "${uninstkey}" "DisplayIcon" '"$INSTDIR\icon.ico"' 93 | WriteRegStr HKLM "${uninstkey}" "UninstallString" '"$INSTDIR\${uninstaller}"' 94 | WriteRegStr HKLM "${uninstkey}" "Publisher" "${author}" 95 | WriteRegStr HKLM "${uninstkey}" "DisplayVersion" "${version}" 96 | 97 | ; Remove all application files copied by previous installation 98 | RMDir /r "$INSTDIR" 99 | 100 | SetOutPath $INSTDIR 101 | 102 | ; Include all files from /build directory 103 | File /r "${src}\*" 104 | 105 | ; Create start menu shortcut 106 | SetShellVarContext all 107 | CreateShortCut "$SMPROGRAMS\${productName}.lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico" 108 | ; Create desktop shortcut 109 | CreateShortCut "$DESKTOP\${productName}.lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico" 110 | 111 | WriteUninstaller "${uninstaller}" 112 | 113 | SectionEnd 114 | 115 | ; -------------------------------- 116 | ; Uninstaller 117 | ; -------------------------------- 118 | 119 | ShowUninstDetails nevershow 120 | 121 | UninstallCaption "Uninstall ${productName}" 122 | UninstallText "Don't like ${productName} anymore? Hit uninstall button." 123 | UninstallIcon "${icon}" 124 | 125 | UninstPage custom un.confirm un.confirmOnLeave 126 | UninstPage instfiles 127 | 128 | Var RemoveAppDataCheckbox 129 | Var RemoveAppDataCheckbox_State 130 | 131 | ; Custom uninstall confirm page 132 | Function un.confirm 133 | 134 | nsDialogs::Create 1018 135 | 136 | ${NSD_CreateLabel} 1u 1u 100% 24u "If you really want to remove ${productName} from your computer press uninstall button." 137 | 138 | ${NSD_CreateCheckbox} 1u 35u 100% 10u "Remove also my ${productName} personal data" 139 | Pop $RemoveAppDataCheckbox 140 | 141 | nsDialogs::Show 142 | 143 | FunctionEnd 144 | 145 | Function un.confirmOnLeave 146 | 147 | ; Save checkbox state on page leave 148 | ${NSD_GetState} $RemoveAppDataCheckbox $RemoveAppDataCheckbox_State 149 | 150 | FunctionEnd 151 | 152 | ; Uninstall declarations 153 | Section "Uninstall" 154 | 155 | DeleteRegKey HKLM "${uninstkey}" 156 | DeleteRegKey HKLM "${regkey}" 157 | 158 | SetShellVarContext all 159 | Delete "$SMPROGRAMS\${productName}.lnk" 160 | ; Remove desktop shortcut 161 | Delete "$DESKTOP\${productName}.lnk" 162 | ; Remove whole directory from Program Files 163 | RMDir /r "$INSTDIR" 164 | 165 | ; Remove also appData directory generated by your app if user checked this option 166 | ${If} $RemoveAppDataCheckbox_State == ${BST_CHECKED} 167 | RMDir /r "$APPDATA\${productName}" 168 | ${EndIf} 169 | 170 | SectionEnd 171 | -------------------------------------------------------------------------------- /resources/windows/setup-banner.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/windows/setup-banner.bmp -------------------------------------------------------------------------------- /resources/windows/setup-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisenytc/ubauth/296ed101bfa403cee42896add89ed3e3590da5ef/resources/windows/setup-icon.ico -------------------------------------------------------------------------------- /script/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Building the package" 4 | 5 | npm run release -- --sign "$CODESIGN_KEY" 6 | 7 | echo "Loading package file" 8 | 9 | PKG=$(cat ./app/package.json) 10 | 11 | echo "Getting app version" 12 | 13 | VERSION=$(echo "$PKG" | jq '.version') 14 | 15 | CLEANED_VERSION=${VERSION//\"} 16 | 17 | echo "Exporting app version" 18 | 19 | export APP_VERSION=${CLEANED_VERSION} 20 | 21 | echo "Deploying version ${APP_VERSION} to GitHub releases. ¯\\_(ツ)_/¯" 22 | -------------------------------------------------------------------------------- /script/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Updating brew" 4 | 5 | brew update 6 | 7 | echo "Installing jq module" 8 | 9 | brew install jq 10 | 11 | if [[ "$TRAVIS" == true ]]; then 12 | echo "Installing NVM" 13 | 14 | NVM_ROOT="${HOME}/.nvm" 15 | rm -rf "$NVM_ROOT" 16 | git clone https://github.com/creationix/nvm.git "$NVM_ROOT" 17 | # shellcheck disable=SC1090 18 | source "${NVM_ROOT}/nvm.sh" 19 | 20 | echo "Installing node.js" 21 | 22 | nvm install "$NODE_VERSION" 23 | node --version 24 | npm --version 25 | else 26 | bundle install 27 | overcommit --install 28 | fi 29 | 30 | echo "Installing development dependencies" 31 | 32 | npm install 33 | -------------------------------------------------------------------------------- /script/keychain.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 4 | echo "Exporting Certificate file" 5 | export CERTIFICATE_P12=${TRAVIS_BUILD_DIR}/Certificate.p12; 6 | echo "Decoding Certificate" 7 | echo "$CERTIFICATE_OSX_P12" | base64 -D > "$CERTIFICATE_P12"; 8 | echo "Exporting Keychain file" 9 | export KEYCHAIN=${TRAVIS_BUILD_DIR}/build.keychain; 10 | echo "Creating Keychain" 11 | security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"; 12 | echo "Setting Keychain as default" 13 | security default-keychain -s "$KEYCHAIN"; 14 | echo "Unlocking Keychain" 15 | security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"; 16 | echo "Importing '${CERTIFICATE_P12}' to '${KEYCHAIN}'" 17 | security import "$CERTIFICATE_P12" -k "$KEYCHAIN" -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign; 18 | echo "Certificate imported to '${KEYCHAIN}'" 19 | fi 20 | -------------------------------------------------------------------------------- /script/postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Entering in app directory" 4 | 5 | cd app || exit 6 | 7 | echo "Installing app dependencies" 8 | 9 | npm install 10 | -------------------------------------------------------------------------------- /script/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Getting node executable path" 4 | 5 | NODE_EXEC_PATH=$(which node) 6 | 7 | echo "Exporting node executable path" 8 | 9 | export NODE_PATH=$NODE_EXEC_PATH 10 | 11 | echo "Running tests" 12 | 13 | istanbul cover electron-mocha -- --ui bdd --timeout 80000 --reporter spec --inline-diffs --bail build 14 | 15 | if [ -f coverage/lcov.info ]; then 16 | codeclimate-test-reporter < coverage/lcov.info 17 | fi 18 | -------------------------------------------------------------------------------- /tasks/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Q = require('q'); 4 | var gulp = require('gulp'); 5 | var watch = require('gulp-watch'); 6 | var batch = require('gulp-batch'); 7 | var jetpack = require('fs-jetpack'); 8 | var asar = require('asar'); 9 | 10 | var bundle = require('./bundle'); 11 | var generateSpecImportsFile = require('./generate_spec_imports'); 12 | var utils = require('../utils'); 13 | var pkg = require('../../app/package.json'); 14 | 15 | var projectDir = jetpack; 16 | var srcDir = projectDir.cwd('./app'); 17 | var destDir = projectDir.cwd('./build'); 18 | 19 | var paths = { 20 | copyFromAppDir: [ 21 | './node_modules/**', 22 | './helpers/**', 23 | './assets/**', 24 | './**/*.html', 25 | './**/*.+(jpg|png|svg)' 26 | ], 27 | }; 28 | 29 | // ------------------------------------- 30 | // Tasks 31 | // ------------------------------------- 32 | 33 | gulp.task('clean', function () { 34 | return destDir.dirAsync('.', { empty: true }); 35 | }); 36 | 37 | var copyTask = function () { 38 | return projectDir.copyAsync('app', destDir.path(), { 39 | overwrite: true, 40 | matching: paths.copyFromAppDir 41 | }); 42 | }; 43 | 44 | gulp.task('copy', ['clean'], copyTask); 45 | gulp.task('copy-watch', ['copy', 'generate']); 46 | 47 | var generateTask = function () { 48 | if (utils.getEnvName() != 'production') { 49 | var deferred = Q.defer(); 50 | asar.createPackage(destDir.path(), destDir.path(pkg.productName + '.asar'), function() { 51 | deferred.resolve(); 52 | }); 53 | return deferred.promise; 54 | } 55 | 56 | return; 57 | }; 58 | 59 | gulp.task('generate', ['copy'], generateTask); 60 | 61 | var bundleApplication = function () { 62 | return Q.all([ 63 | bundle(srcDir.path('background.js'), destDir.path('background.js')), 64 | bundle(srcDir.path('app.js'), destDir.path('app.js')), 65 | bundle(srcDir.path('src', 'controllers', 'auth.js'), destDir.path('controllers', 'auth.js')), 66 | bundle(srcDir.path('src', 'controllers', 'success.js'), destDir.path('controllers', 'success.js')), 67 | bundle(srcDir.path('src', 'services', 'uber.js'), destDir.path('services', 'uber.js')) 68 | ]); 69 | }; 70 | 71 | var bundleSpecs = function () { 72 | return generateSpecImportsFile().then(function (specEntryPointPath) { 73 | return bundle(specEntryPointPath, destDir.path('spec.js')); 74 | }); 75 | }; 76 | 77 | var bundleTask = function () { 78 | if (utils.getEnvName() === 'test') { 79 | return bundleSpecs() 80 | .then(bundleApplication()); 81 | } 82 | return bundleApplication(); 83 | }; 84 | 85 | gulp.task('bundle', ['clean'], bundleTask); 86 | gulp.task('bundle-watch', ['bundle']); 87 | 88 | gulp.task('environment', ['clean'], function () { 89 | var configFile = 'config/env_' + utils.getEnvName() + '.json'; 90 | projectDir.copy(configFile, destDir.path('env.json')); 91 | }); 92 | 93 | gulp.task('package-json', ['clean'], function () { 94 | var manifest = srcDir.read('package.json', 'json'); 95 | 96 | // Add "dev" suffix to name, so Electron will write all data like cookies 97 | // and localStorage in separate places for production and development. 98 | if (utils.getEnvName() === 'development') { 99 | manifest.name += '-dev'; 100 | manifest.productName += ' Dev'; 101 | } 102 | 103 | destDir.write('package.json', manifest); 104 | }); 105 | 106 | gulp.task('watch', function () { 107 | watch('app/**/*.js', batch(function (events, done) { 108 | gulp.start('bundle-watch', done); 109 | })); 110 | watch(paths.copyFromAppDir, { cwd: 'app' }, batch(function (events, done) { 111 | gulp.start('copy-watch', done); 112 | })); 113 | }); 114 | 115 | gulp.task('build', ['bundle', 'environment', 'package-json', 'generate']); 116 | -------------------------------------------------------------------------------- /tasks/build/bundle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var pathUtil = require('path'); 4 | var jetpack = require('fs-jetpack'); 5 | var rollup = require('rollup'); 6 | var Q = require('q'); 7 | 8 | var nodeBuiltInModules = ['assert', 'buffer', 'child_process', 'cluster', 9 | 'console', 'constants', 'crypto', 'dgram', 'dns', 'domain', 'events', 10 | 'fs', 'http', 'https', 'module', 'net', 'os', 'path', 'process', 'punycode', 11 | 'querystring', 'readline', 'repl', 'stream', 'string_decoder', 'timers', 12 | 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'zlib']; 13 | 14 | var electronBuiltInModules = ['electron']; 15 | 16 | var npmModulesUsedInApp = function () { 17 | var appManifest = require('../../app/package.json'); 18 | return Object.keys(appManifest.dependencies); 19 | }; 20 | 21 | var generateExternalModulesList = function () { 22 | return [].concat(nodeBuiltInModules, electronBuiltInModules, npmModulesUsedInApp()); 23 | }; 24 | 25 | module.exports = function (src, dest) { 26 | var deferred = Q.defer(); 27 | 28 | rollup.rollup({ 29 | entry: src, 30 | external: generateExternalModulesList(), 31 | }).then(function (bundle) { 32 | var jsFile = pathUtil.basename(dest); 33 | var result = bundle.generate({ 34 | format: 'cjs', 35 | sourceMap: true, 36 | sourceMapFile: jsFile, 37 | }); 38 | // Wrap code in self invoking function so the variables don't 39 | // pollute the global namespace. 40 | var isolatedCode = '(function () {' + result.code + '\n}());'; 41 | return Q.all([ 42 | jetpack.writeAsync(dest, isolatedCode + '\n//# sourceMappingURL=' + jsFile + '.map'), 43 | jetpack.writeAsync(dest + '.map', result.map.toString()), 44 | ]); 45 | }).then(function () { 46 | deferred.resolve(); 47 | }).catch(function (err) { 48 | deferred.reject(err); 49 | }); 50 | 51 | return deferred.promise; 52 | }; 53 | -------------------------------------------------------------------------------- /tasks/build/generate_spec_imports.js: -------------------------------------------------------------------------------- 1 | // Spec files are scattered through the whole project. Here we're searching 2 | // for them and generate one entry file which will run all the tests. 3 | 4 | 'use strict'; 5 | 6 | var jetpack = require('fs-jetpack'); 7 | var srcDir = jetpack.cwd('app'); 8 | 9 | var fileName = 'spec.js.autogenerated'; 10 | var fileBanner = "// This file is generated automatically.\n" 11 | + "// All your modifications to it will be lost (so don't do it).\n"; 12 | var whatToInclude = [ 13 | '*.spec.js', 14 | '!node_modules/**', 15 | ]; 16 | 17 | module.exports = function () { 18 | return srcDir.findAsync('.', { matching: whatToInclude }) 19 | .then(function (specPaths) { 20 | var fileContent = specPaths.map(function (path) { 21 | return 'import "./' + path.replace(/\\/g, '/') + '";'; 22 | }).join('\n'); 23 | return srcDir.writeAsync(fileName, fileBanner + fileContent); 24 | }) 25 | .then(function () { 26 | return srcDir.path(fileName); 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /tasks/install_native_module.js: -------------------------------------------------------------------------------- 1 | // Install native module from npm and compile it for Electron. 2 | // Usage: npm run install-native -- name_of_native_module 3 | 4 | 'use strict'; 5 | 6 | var childProcess = require('child_process'); 7 | var Q = require('q'); 8 | var appDir = require('fs-jetpack').cwd(__dirname, '../app'); 9 | var utils = require('./utils'); 10 | 11 | var ensureElectronRebuildInstalled = function () { 12 | var deferred = Q.defer(); 13 | 14 | try { 15 | // If require is successful it means module is already installed. 16 | require('electron-rebuild'); 17 | deferred.resolve(); 18 | } catch (err) { 19 | childProcess.spawn(utils.spawnablePath('npm'), [ 20 | 'install', '--save-dev', 'electron-rebuild' 21 | ], { 22 | stdio: 'inherit' 23 | }) 24 | .on('error', deferred.reject) 25 | .on('close', deferred.resolve); 26 | } 27 | 28 | return deferred.promise; 29 | }; 30 | 31 | var ensurePostinstallRunsElectronRebuild = function () { 32 | var postinstallScript = 'node ../tasks/rebuild_native_modules'; 33 | 34 | var appManifest = appDir.read('package.json', 'json'); 35 | 36 | if (typeof appManifest.scripts === 'undefined') { 37 | appManifest.scripts = {}; 38 | } 39 | 40 | // Let's do it 100% bulletproof and check if programmer didn't 41 | // pust some custom stuff into postinstall script already. 42 | if (typeof appManifest.scripts.postinstall === 'undefined') { 43 | appManifest.scripts.postinstall = postinstallScript; 44 | appDir.write('package.json', appManifest); 45 | } else if (appManifest.scripts.postinstall.indexOf(postinstallScript) === -1) { 46 | appManifest.scripts.postinstall += ' && ' + postinstallScript; 47 | appDir.write('package.json', appManifest); 48 | } 49 | 50 | return Q(); 51 | }; 52 | 53 | var installNativeModule = function () { 54 | var deferred = Q.defer(); 55 | var moduleToInstallAndOtherParams = process.argv.slice(2); 56 | 57 | if (!moduleToInstallAndOtherParams.length === 0) { 58 | deferred.reject('Module name not specified! Correct usage is "npm run install-native -- name_of_native_module" (remember about space after "--").'); 59 | } else { 60 | childProcess.spawn( 61 | utils.spawnablePath('npm'), 62 | ['install', '--save'].concat(moduleToInstallAndOtherParams), 63 | { 64 | cwd: appDir.cwd(), 65 | stdio: 'inherit' 66 | } 67 | ) 68 | .on('error', deferred.reject) 69 | .on('close', deferred.resolve); 70 | } 71 | 72 | return deferred.promise; 73 | }; 74 | 75 | var runRebuild = function () { 76 | var deferred = Q.defer(); 77 | 78 | childProcess.spawn(utils.spawnablePath('npm'), [ 79 | 'run', 'postinstall' 80 | ], { 81 | cwd: appDir.cwd(), 82 | stdio: 'inherit' 83 | }) 84 | .on('error', deferred.reject) 85 | .on('close', deferred.resolve); 86 | 87 | return deferred.promise; 88 | }; 89 | 90 | ensureElectronRebuildInstalled() 91 | .then(ensurePostinstallRunsElectronRebuild) 92 | .then(installNativeModule) 93 | .then(runRebuild) 94 | .catch(function (err) { 95 | console.error(err); // eslint-disable-line no-console 96 | }); 97 | -------------------------------------------------------------------------------- /tasks/rebuild_native_modules.js: -------------------------------------------------------------------------------- 1 | // Rebuilds native node modules for Electron. 2 | // More: https://github.com/atom/electron/blob/master/docs/tutorial/using-native-node-modules.md 3 | 4 | 'use strict'; 5 | 6 | var path = require('path'); 7 | var electron = require('electron-prebuilt'); 8 | var electronPackage = require('electron-prebuilt/package.json'); 9 | var rebuild = require('electron-rebuild'); 10 | 11 | var pathToElectronNativeModules = path.join(__dirname, '../app/node_modules'); 12 | 13 | rebuild.shouldRebuildNativeModules(electron) 14 | .then(function (shouldBuild) { 15 | if (!shouldBuild) { 16 | return true; 17 | } 18 | 19 | console.log('Rebuilding native modules for Electron...'); // eslint-disable-line no-console 20 | 21 | return rebuild.installNodeHeaders(electronPackage.version) 22 | .then(function () { 23 | return rebuild.rebuildNativeModules(electronPackage.version, pathToElectronNativeModules); 24 | }); 25 | }) 26 | .then(function () { 27 | console.log('Rebuilding complete.'); // eslint-disable-line no-console 28 | }) 29 | .catch(function (err) { 30 | console.error("Rebuilding error!"); // eslint-disable-line no-console 31 | console.error(err); // eslint-disable-line no-console 32 | }); 33 | -------------------------------------------------------------------------------- /tasks/release/linux.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Q = require('q'); 4 | var gulpUtil = require('gulp-util'); 5 | var childProcess = require('child_process'); 6 | var jetpack = require('fs-jetpack'); 7 | var asar = require('asar'); 8 | var utils = require('../utils'); 9 | 10 | var projectDir; 11 | var releasesDir; 12 | var packName; 13 | var packDir; 14 | var tmpDir; 15 | var readyAppDir; 16 | var manifest; 17 | 18 | var init = function () { 19 | projectDir = jetpack; 20 | tmpDir = projectDir.dir('./tmp', { empty: true }); 21 | releasesDir = projectDir.dir('./releases'); 22 | manifest = projectDir.read('app/package.json', 'json'); 23 | packName = utils.getReleasePackageName(manifest); 24 | packDir = tmpDir.dir(packName); 25 | readyAppDir = packDir.cwd('opt', manifest.name); 26 | 27 | return new Q(); 28 | }; 29 | 30 | var copyRuntime = function () { 31 | return projectDir.copyAsync('node_modules/electron-prebuilt/dist', readyAppDir.path(), { overwrite: true }); 32 | }; 33 | 34 | var packageBuiltApp = function () { 35 | var deferred = Q.defer(); 36 | 37 | asar.createPackageWithOptions(projectDir.path('build'), readyAppDir.path('resources/app.asar'), { 38 | dot: true 39 | }, function () { 40 | deferred.resolve(); 41 | }); 42 | 43 | return deferred.promise; 44 | }; 45 | 46 | var finalize = function () { 47 | // Create .desktop file from the template 48 | var desktop = projectDir.read('resources/linux/app.desktop'); 49 | desktop = utils.replace(desktop, { 50 | name: manifest.name, 51 | productName: manifest.productName, 52 | description: manifest.description, 53 | version: manifest.version, 54 | author: manifest.author 55 | }); 56 | packDir.write('usr/share/applications/' + manifest.name + '.desktop', desktop); 57 | 58 | // Copy icon 59 | projectDir.copy('resources/icon.png', readyAppDir.path('icon.png')); 60 | 61 | return new Q(); 62 | }; 63 | 64 | var renameApp = function () { 65 | return readyAppDir.renameAsync('electron', manifest.name); 66 | }; 67 | 68 | var packToDebFile = function () { 69 | var deferred = Q.defer(); 70 | 71 | var debFileName = packName + '.deb'; 72 | var debPath = releasesDir.path(debFileName); 73 | 74 | gulpUtil.log('Creating DEB package... (' + debFileName + ')'); 75 | 76 | // Counting size of the app in KiB 77 | var appSize = Math.round(readyAppDir.inspectTree('.').size / 1024); 78 | 79 | // Preparing debian control file 80 | var control = projectDir.read('resources/linux/DEBIAN/control'); 81 | control = utils.replace(control, { 82 | name: manifest.name, 83 | description: manifest.description, 84 | version: manifest.version, 85 | author: manifest.author, 86 | size: appSize 87 | }); 88 | packDir.write('DEBIAN/control', control); 89 | 90 | // Build the package... 91 | childProcess.exec('fakeroot dpkg-deb -Zxz --build ' + packDir.path().replace(/\s/g, '\\ ') + ' ' + debPath.replace(/\s/g, '\\ '), 92 | function (error, stdout, stderr) { 93 | if (error || stderr) { 94 | console.log('ERROR while building DEB package:'); // eslint-disable-line no-console 95 | console.log(error); // eslint-disable-line no-console 96 | console.log(stderr); // eslint-disable-line no-console 97 | } else { 98 | gulpUtil.log('DEB package ready!', debPath); 99 | } 100 | deferred.resolve(); 101 | }); 102 | 103 | return deferred.promise; 104 | }; 105 | 106 | var cleanClutter = function () { 107 | return tmpDir.removeAsync('.'); 108 | }; 109 | 110 | module.exports = function () { 111 | return init() 112 | .then(copyRuntime) 113 | .then(packageBuiltApp) 114 | .then(finalize) 115 | .then(renameApp) 116 | .then(packToDebFile) 117 | .then(cleanClutter) 118 | .catch(console.error); // eslint-disable-line no-console 119 | }; 120 | -------------------------------------------------------------------------------- /tasks/release/osx.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Q = require('q'); 4 | var gulpUtil = require('gulp-util'); 5 | var jetpack = require('fs-jetpack'); 6 | var asar = require('asar'); 7 | var utils = require('../utils'); 8 | var child_process = require('child_process'); 9 | 10 | var projectDir; 11 | var releasesDir; 12 | var tmpDir; 13 | var finalAppDir; 14 | var manifest; 15 | 16 | var init = function () { 17 | projectDir = jetpack; 18 | tmpDir = projectDir.dir('./tmp', { empty: true }); 19 | releasesDir = projectDir.dir('./releases'); 20 | manifest = projectDir.read('app/package.json', 'json'); 21 | finalAppDir = tmpDir.cwd(manifest.productName + '.app'); 22 | 23 | return new Q(); 24 | }; 25 | 26 | var copyRuntime = function () { 27 | return projectDir.copyAsync('node_modules/electron-prebuilt/dist/Electron.app', finalAppDir.path()); 28 | }; 29 | 30 | var cleanupRuntime = function () { 31 | finalAppDir.remove('Contents/Resources/default_app'); 32 | finalAppDir.remove('Contents/Resources/atom.icns'); 33 | return new Q(); 34 | }; 35 | 36 | var packageBuiltApp = function () { 37 | var deferred = Q.defer(); 38 | 39 | asar.createPackageWithOptions(projectDir.path('build'), finalAppDir.path('Contents/Resources/app.asar'), { 40 | dot: true 41 | }, function () { 42 | deferred.resolve(); 43 | }); 44 | 45 | return deferred.promise; 46 | }; 47 | 48 | var finalize = function () { 49 | // Prepare main Info.plist 50 | var info = projectDir.read('resources/osx/Info.plist'); 51 | info = utils.replace(info, { 52 | productName: manifest.productName, 53 | identifier: manifest.osx.identifier, 54 | version: manifest.version, 55 | build: manifest.osx.build, 56 | copyright: manifest.copyright, 57 | LSApplicationCategoryType: manifest.osx.LSApplicationCategoryType 58 | }); 59 | finalAppDir.write('Contents/Info.plist', info); 60 | 61 | // Prepare Info.plist of Helper apps 62 | [' EH', ' NP', ''].forEach(function (helper_suffix) { 63 | info = projectDir.read('resources/osx/helper_apps/Info' + helper_suffix + '.plist'); 64 | info = utils.replace(info, { 65 | productName: manifest.productName, 66 | identifier: manifest.identifier 67 | }); 68 | finalAppDir.write('Contents/Frameworks/Electron Helper' + helper_suffix + '.app/Contents/Info.plist', info); 69 | }); 70 | 71 | // Copy icon 72 | projectDir.copy('resources/osx/icon.icns', finalAppDir.path('Contents/Resources/icon.icns')); 73 | 74 | return new Q(); 75 | }; 76 | 77 | var renameApp = function () { 78 | // Rename helpers 79 | [' Helper EH', ' Helper NP', ' Helper'].forEach(function (helper_suffix) { 80 | finalAppDir.rename('Contents/Frameworks/Electron' + helper_suffix + '.app/Contents/MacOS/Electron' + helper_suffix, manifest.productName + helper_suffix ); 81 | finalAppDir.rename('Contents/Frameworks/Electron' + helper_suffix + '.app', manifest.productName + helper_suffix + '.app'); 82 | }); 83 | // Rename application 84 | finalAppDir.rename('Contents/MacOS/Electron', manifest.productName); 85 | return new Q(); 86 | }; 87 | 88 | var signApp = function () { 89 | var identity = utils.getSigningId(manifest); 90 | var MASIdentity = utils.getMASSigningId(manifest); 91 | var MASInstallerIdentity = utils.getMASInstallerSigningId(manifest); 92 | 93 | if (utils.releaseForMAS()) { 94 | if (!MASIdentity || !MASInstallerIdentity) { 95 | gulpUtil.log('--mas-sign and --mas-installer-sign are required to release for Mac App Store!'); 96 | process.exit(0); 97 | } 98 | var cmds = [ 99 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib"', 100 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libnode.dylib"', 101 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/Electron Framework.framework/Versions/A"', 102 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/' + manifest.productName + ' Helper.app/"', 103 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/' + manifest.productName + ' Helper EH.app/"', 104 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/' + manifest.productName + ' Helper NP.app/"' 105 | ]; 106 | 107 | if (finalAppDir.exists('Contents/Frameworks/Squirrel.framework/Versions/A')) { 108 | // # Signing a non-MAS build. 109 | cmds.push('codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist "' + finalAppDir.path() + '/Contents/Frameworks/Mantle.framework/Versions/A"'); 110 | cmds.push('codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist "' + finalAppDir.path() + '/Contents/Frameworks/ReactiveCocoa.framework/Versions/A"'); 111 | cmds.push('codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist "' + finalAppDir.path() + '/Contents/Frameworks/Squirrel.framework/Versions/A"'); 112 | } 113 | 114 | cmds.push('codesign -f -s "' + MASIdentity + '" --entitlements resources/osx/parent.plist -v "' + finalAppDir.path() + '"'); 115 | 116 | cmds.push('productbuild --component "' + finalAppDir.path() + '" /Applications --sign "' + MASInstallerIdentity + '" "' + releasesDir.path(manifest.productName + '.pkg') + '"'); 117 | 118 | /* eslint-disable */ 119 | var result = new Q(); 120 | cmds.forEach(function (cmd) { 121 | result = result.then(function(result) { 122 | gulpUtil.log('Signing with:', cmd); 123 | return Q.nfcall(child_process.exec, cmd); 124 | }); 125 | }); 126 | result = result.then(function(result) { 127 | return new Q(); 128 | }); 129 | return result; 130 | /* eslint-disable */ 131 | 132 | } else if (identity) { 133 | var cmd = 'codesign --deep --force --sign "' + identity + '" "' + finalAppDir.path() + '"'; 134 | gulpUtil.log('Signing with:', cmd); 135 | return Q.nfcall(child_process.exec, cmd); 136 | } else { 137 | return new Q(); 138 | } 139 | }; 140 | 141 | var packToDmgFile = function () { 142 | if (utils.releaseForMAS()) { 143 | return new Q(); 144 | } 145 | 146 | var deferred = Q.defer(); 147 | 148 | var appdmg = require('appdmg'); 149 | var dmgName = utils.getReleasePackageName(manifest) + '.dmg'; 150 | 151 | // Prepare appdmg config 152 | var dmgManifest = projectDir.read('resources/osx/appdmg.json'); 153 | dmgManifest = utils.replace(dmgManifest, { 154 | productName: manifest.productName, 155 | appPath: finalAppDir.path(), 156 | dmgIcon: projectDir.path("resources/osx/dmg-icon.icns"), 157 | dmgBackground: projectDir.path("resources/osx/dmg-background.png") 158 | }); 159 | tmpDir.write('appdmg.json', dmgManifest); 160 | 161 | // Delete DMG file with this name if already exists 162 | releasesDir.remove(dmgName); 163 | 164 | gulpUtil.log('Packaging to DMG file... (' + dmgName + ')'); 165 | 166 | var readyDmgPath = releasesDir.path(dmgName); 167 | appdmg({ 168 | source: tmpDir.path('appdmg.json'), 169 | target: readyDmgPath 170 | }) 171 | .on('error', function (err) { 172 | console.error(err); // eslint-disable-line no-console 173 | }) 174 | .on('finish', function () { 175 | gulpUtil.log('DMG file ready!', readyDmgPath); 176 | deferred.resolve(); 177 | }); 178 | 179 | return deferred.promise; 180 | }; 181 | 182 | var cleanClutter = function () { 183 | return tmpDir.removeAsync('.'); 184 | }; 185 | 186 | module.exports = function () { 187 | return init() 188 | .then(copyRuntime) 189 | .then(cleanupRuntime) 190 | .then(packageBuiltApp) 191 | .then(finalize) 192 | .then(renameApp) 193 | .then(signApp) 194 | .then(packToDmgFile) 195 | .then(cleanClutter) 196 | .catch(console.error); // eslint-disable-line no-console 197 | }; 198 | -------------------------------------------------------------------------------- /tasks/release/release.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var utils = require('../utils'); 5 | 6 | var releaseForOs = { 7 | osx: require('./osx'), 8 | linux: require('./linux'), 9 | windows: require('./windows'), 10 | }; 11 | 12 | gulp.task('release', ['build'], function () { 13 | return releaseForOs[utils.os()](); 14 | }); 15 | -------------------------------------------------------------------------------- /tasks/release/windows.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Q = require('q'); 4 | var gulpUtil = require('gulp-util'); 5 | var childProcess = require('child_process'); 6 | var jetpack = require('fs-jetpack'); 7 | var asar = require('asar'); 8 | var utils = require('../utils'); 9 | 10 | var projectDir; 11 | var tmpDir; 12 | var releasesDir; 13 | var readyAppDir; 14 | var manifest; 15 | 16 | var init = function () { 17 | projectDir = jetpack; 18 | tmpDir = projectDir.dir('./tmp', { empty: true }); 19 | releasesDir = projectDir.dir('./releases'); 20 | manifest = projectDir.read('app/package.json', 'json'); 21 | readyAppDir = tmpDir.cwd(manifest.name); 22 | 23 | return new Q(); 24 | }; 25 | 26 | var copyRuntime = function () { 27 | return projectDir.copyAsync('node_modules/electron-prebuilt/dist', readyAppDir.path(), { overwrite: true }); 28 | }; 29 | 30 | var cleanupRuntime = function () { 31 | return readyAppDir.removeAsync('resources/default_app'); 32 | }; 33 | 34 | var packageBuiltApp = function () { 35 | var deferred = Q.defer(); 36 | 37 | asar.createPackageWithOptions(projectDir.path('build'), readyAppDir.path('resources/app.asar'), { 38 | dot: true 39 | }, function () { 40 | deferred.resolve(); 41 | }); 42 | 43 | return deferred.promise; 44 | }; 45 | 46 | var finalize = function () { 47 | var deferred = Q.defer(); 48 | 49 | projectDir.copy('resources/windows/icon.ico', readyAppDir.path('icon.ico')); 50 | 51 | // Replace Electron icon for your own. 52 | var rcedit = require('rcedit'); 53 | rcedit(readyAppDir.path('electron.exe'), { 54 | 'icon': projectDir.path('resources/windows/icon.ico'), 55 | 'version-string': { 56 | 'ProductName': manifest.productName, 57 | 'FileDescription': manifest.description, 58 | 'ProductVersion': manifest.version, 59 | 'CompanyName': manifest.author, // it might be better to add another field to package.json for this 60 | 'LegalCopyright': manifest.copyright, 61 | 'OriginalFilename': manifest.productName + '.exe' 62 | } 63 | }, function (err) { 64 | if (!err) { 65 | deferred.resolve(); 66 | } 67 | }); 68 | 69 | return deferred.promise; 70 | }; 71 | 72 | var renameApp = function () { 73 | return readyAppDir.renameAsync('electron.exe', manifest.productName + '.exe'); 74 | }; 75 | 76 | var createInstaller = function () { 77 | var deferred = Q.defer(); 78 | 79 | var finalPackageName = utils.getReleasePackageName(manifest) + '.exe'; 80 | var installScript = projectDir.read('resources/windows/installer.nsi'); 81 | 82 | installScript = utils.replace(installScript, { 83 | name: manifest.name, 84 | productName: manifest.productName, 85 | author: manifest.author, 86 | version: manifest.version, 87 | src: readyAppDir.path(), 88 | dest: releasesDir.path(finalPackageName), 89 | icon: readyAppDir.path('icon.ico'), 90 | setupIcon: projectDir.path('resources/windows/setup-icon.ico'), 91 | banner: projectDir.path('resources/windows/setup-banner.bmp'), 92 | }); 93 | tmpDir.write('installer.nsi', installScript); 94 | 95 | gulpUtil.log('Building installer with NSIS... (' + finalPackageName + ')'); 96 | 97 | // Remove destination file if already exists. 98 | releasesDir.remove(finalPackageName); 99 | 100 | // Note: NSIS have to be added to PATH (environment variables). 101 | var nsis = childProcess.spawn('makensis', [ 102 | tmpDir.path('installer.nsi') 103 | ], { 104 | stdio: 'inherit' 105 | }); 106 | nsis.on('error', function (err) { 107 | if (err.message === 'spawn makensis ENOENT') { 108 | throw "Can't find NSIS. Are you sure you've installed it and" 109 | + " added to PATH environment variable?"; 110 | } else { 111 | throw err; 112 | } 113 | }); 114 | nsis.on('close', function () { 115 | gulpUtil.log('Installer ready!', releasesDir.path(finalPackageName)); 116 | deferred.resolve(); 117 | }); 118 | 119 | return deferred.promise; 120 | }; 121 | 122 | var cleanClutter = function () { 123 | return tmpDir.removeAsync('.'); 124 | }; 125 | 126 | module.exports = function () { 127 | return init() 128 | .then(copyRuntime) 129 | .then(cleanupRuntime) 130 | .then(packageBuiltApp) 131 | .then(finalize) 132 | .then(renameApp) 133 | .then(createInstaller) 134 | .then(cleanClutter) 135 | .catch(console.error); // eslint-disable-line no-console 136 | }; 137 | -------------------------------------------------------------------------------- /tasks/start.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var childProcess = require('child_process'); 4 | var electron = require('electron-prebuilt'); 5 | var gulp = require('gulp'); 6 | var pkg = require('../app/package.json'); 7 | 8 | gulp.task('start', ['build', 'watch'], function () { 9 | childProcess.spawn(electron, ['./build/' + pkg.productName + '.asar'], { 10 | stdio: 'inherit' 11 | }) 12 | .on('close', function () { 13 | // User closed the app. Kill the host process. 14 | process.exit(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /tasks/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var argv = require('yargs').argv; 4 | var os = require('os'); 5 | 6 | module.exports.os = function () { 7 | switch (os.platform()) { 8 | case 'darwin': 9 | return 'osx'; 10 | case 'linux': 11 | return 'linux'; 12 | case 'win32': 13 | return 'windows'; 14 | } 15 | return 'unsupported'; 16 | }; 17 | 18 | module.exports.replace = function (str, patterns) { 19 | Object.keys(patterns).forEach(function (pattern) { 20 | var matcher = new RegExp('{{' + pattern + '}}', 'g'); 21 | str = str.replace(matcher, patterns[pattern]); 22 | }); 23 | return str; 24 | }; 25 | 26 | module.exports.getReleasePackageName = function(manifest) { 27 | return module.exports.replace(manifest.packageNameTemplate, { 28 | name: manifest.name, 29 | version: manifest.version, 30 | build: manifest.build, 31 | productName: manifest.productName, 32 | platform: process.platform, 33 | arch: process.arch 34 | }); 35 | }; 36 | 37 | module.exports.getEnvName = function () { 38 | return argv.env || 'development'; 39 | }; 40 | 41 | module.exports.getSigningId = function (manifest) { 42 | return argv.sign || (manifest.osx.codeSignIdentitiy ? manifest.osx.codeSignIdentitiy.dmg : undefined); 43 | }; 44 | 45 | module.exports.getMASSigningId = function (manifest) { 46 | return argv['mas-sign'] || (manifest.osx.codeSignIdentitiy ? manifest.osx.codeSignIdentitiy.MAS : undefined); 47 | }; 48 | 49 | module.exports.getMASInstallerSigningId = function (manifest) { 50 | return argv['mas-installer-sign'] || (manifest.osx.codeSignIdentitiy ? manifest.osx.codeSignIdentitiy.MASInstaller : undefined); 51 | }; 52 | 53 | module.exports.releaseForMAS = function () { 54 | return !!argv.mas; 55 | }; 56 | 57 | // Fixes https://github.com/nodejs/node-v0.x-archive/issues/2318 58 | module.exports.spawnablePath = function (path) { 59 | if (process.platform === 'win32') { 60 | return path + '.cmd'; 61 | } 62 | return path; 63 | }; 64 | --------------------------------------------------------------------------------