├── .gitignore ├── .meteor ├── .finished-upgraders ├── .gitignore ├── .id ├── cordova-plugins ├── packages ├── platforms ├── release └── versions ├── LICENSE.txt ├── README.md ├── client ├── autorun.js ├── helpers.js ├── lib │ ├── custom.bootstrap.import.less │ ├── custom.bootstrap.json │ ├── custom.bootstrap.less │ ├── custom.bootstrap.mixins.import.less │ ├── journal.theme.import.less │ └── journal.variables.import.less ├── subscriptions.js └── views │ ├── home.html │ ├── home.less │ ├── includes │ ├── footer.html │ ├── header.html │ ├── header.js │ ├── header.less │ ├── layout.html │ ├── loading.html │ └── notFound.html │ ├── jobs │ ├── job.html │ ├── job.js │ ├── jobForms.html │ ├── jobForms.js │ ├── jobIncludes.html │ ├── jobIncludes.js │ ├── jobSmall.html │ ├── jobs.html │ ├── jobsRecent.html │ ├── jobsRecent.js │ ├── jobsRecent.less │ └── myJobs.html │ ├── main.html │ ├── main.less │ ├── profiles │ ├── profile.html │ ├── profile.js │ ├── profileForms.html │ ├── profileForms.js │ ├── profileForms.less │ ├── profileSmall.html │ ├── profileSmall.less │ ├── profiles.html │ ├── profilesRecent.html │ └── profilesRecent.less │ └── user │ ├── userProfile.html │ └── userProfile.js ├── collections ├── jobs.js ├── profiles.js └── users.js ├── lib ├── accounts.js ├── admin.js ├── avatar.js ├── constants.js └── helpers.js ├── public └── images │ ├── avatar.png │ ├── favicon.ico │ ├── network.jpg │ └── universe.png ├── router.js ├── server ├── accounts.js ├── api.js ├── cron.js ├── hooks.js ├── lib │ ├── constants.js │ └── helpers.js ├── migrations.js ├── publications.js ├── rss.js └── sitemap.js ├── settings-example.json └── wework.sublime-project /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-workspace 2 | settings.json 3 | 4 | npm-debug.log 5 | node_modules 6 | 7 | .DS_Store 8 | /**/.DS_Store 9 | .demeteorized/ 10 | 11 | *.swp 12 | -------------------------------------------------------------------------------- /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | -------------------------------------------------------------------------------- /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | ecew691gc6vnr8sc76h 8 | -------------------------------------------------------------------------------- /.meteor/cordova-plugins: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # 3 | # 'meteor add' and 'meteor remove' will edit this file for you, 4 | # but you can also edit it by hand. 5 | 6 | meteor-platform 7 | less 8 | service-configuration 9 | accounts-ui 10 | accounts-password 11 | accounts-github 12 | spiderable 13 | iron:router 14 | natestrauser:font-awesome 15 | raix:handlebar-helpers 16 | momentjs:moment 17 | aldeed:autoform 18 | aldeed:collection2 19 | multiply:iron-router-progress 20 | natestrauser:connection-banner 21 | dfischer:phantomjs 22 | lampe:rssfeed 23 | meteorhacks:kadira 24 | aldeed:delete-button 25 | useraccounts:bootstrap 26 | meteorhacks:subs-manager 27 | dburles:collection-helpers 28 | alanning:roles 29 | nemo64:bootstrap 30 | zimme:iron-router-active 31 | percolate:migrations 32 | jparker:crypto-md5 33 | tmeasday:publish-counts 34 | bengott:avatar@=0.7.3 35 | matb33:collection-hooks 36 | percolatestudio:synced-cron 37 | reywood:publish-composite 38 | nimble:restivus 39 | peppelg:bootstrap-3-modal 40 | mpowaga:autoform-summernote 41 | djedi:sanitize-html 42 | email 43 | gadicohen:sitemaps 44 | natestrauser:uploadcare-plus 45 | reactive-var 46 | yogiben:admin@=1.0.9 47 | ongoworks:speakingurl 48 | reywood:iron-router-ga 49 | iron:middleware-stack@1.1.0 50 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | browser 2 | server 3 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.1.0.2 2 | -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.2.0 2 | accounts-github@1.0.4 3 | accounts-oauth@1.1.5 4 | accounts-password@1.1.1 5 | accounts-ui@1.1.5 6 | accounts-ui-unstyled@1.1.7 7 | alanning:roles@1.2.13 8 | aldeed:autoform@4.2.2 9 | aldeed:collection2@2.3.3 10 | aldeed:delete-button@1.0.0 11 | aldeed:simple-schema@1.3.3 12 | aldeed:template-extension@3.1.1 13 | alethes:pages@1.7.2 14 | autoupdate@1.2.1 15 | base64@1.0.3 16 | bengott:avatar@0.7.3 17 | binary-heap@1.0.3 18 | blaze@2.1.2 19 | blaze-tools@1.0.3 20 | boilerplate-generator@1.0.3 21 | callback-hook@1.0.3 22 | check@1.0.5 23 | coffeescript@1.0.6 24 | dburles:collection-helpers@1.0.3 25 | ddp@1.1.0 26 | deps@1.0.7 27 | dfischer:phantomjs@1.0.4 28 | djedi:sanitize-html@1.7.0 29 | ejson@1.0.6 30 | email@1.0.6 31 | fastclick@1.0.3 32 | gadicohen:robots-txt@0.0.9 33 | gadicohen:sitemaps@0.0.21 34 | geojson-utils@1.0.3 35 | github@1.1.3 36 | html-tools@1.0.4 37 | htmljs@1.0.4 38 | http@1.1.0 39 | id-map@1.0.3 40 | iron:controller@1.0.8 41 | iron:core@1.0.11 42 | iron:dynamic-template@1.0.8 43 | iron:layout@1.0.8 44 | iron:location@1.0.9 45 | iron:middleware-stack@1.1.0 46 | iron:router@1.0.9 47 | iron:url@1.0.11 48 | jparker:crypto-core@0.1.0 49 | jparker:crypto-md5@0.1.1 50 | jparker:gravatar@0.3.1 51 | jquery@1.11.3_2 52 | json@1.0.3 53 | lampe:rssfeed@0.1.1 54 | launch-screen@1.0.2 55 | less@1.0.14 56 | livedata@1.0.13 57 | localstorage@1.0.3 58 | logging@1.0.7 59 | matb33:collection-hooks@0.7.13 60 | meteor@1.1.6 61 | meteor-platform@1.2.2 62 | meteorhacks:kadira@2.21.0 63 | meteorhacks:meteorx@1.3.1 64 | meteorhacks:subs-manager@1.4.0 65 | minifiers@1.1.5 66 | minimongo@1.0.8 67 | mobile-status-bar@1.0.3 68 | momentjs:moment@2.9.0 69 | mongo@1.1.0 70 | mongo-livedata@1.0.8 71 | mpowaga:autoform-summernote@0.3.2 72 | multiply:iron-router-progress@1.0.1 73 | natestrauser:connection-banner@0.4.3 74 | natestrauser:font-awesome@4.3.0 75 | natestrauser:uploadcare-plus@2.0.2_3 76 | nemo64:bootstrap@3.3.4_2 77 | nemo64:bootstrap-data@3.3.4_1 78 | nimble:restivus@0.6.6 79 | npm-bcrypt@0.7.8_2 80 | oauth@1.1.4 81 | oauth2@1.1.3 82 | observe-sequence@1.0.6 83 | ongoworks:speakingurl@1.1.0 84 | ordered-dict@1.0.3 85 | peppelg:bootstrap-3-modal@1.0.3 86 | percolate:migrations@0.7.5 87 | percolatestudio:synced-cron@1.1.0 88 | raix:handlebar-helpers@0.2.4 89 | random@1.0.3 90 | reactive-dict@1.1.0 91 | reactive-var@1.0.5 92 | reload@1.1.3 93 | retry@1.0.3 94 | reywood:iron-router-ga@0.6.0 95 | reywood:publish-composite@1.3.6 96 | routepolicy@1.0.5 97 | service-configuration@1.0.4 98 | session@1.1.0 99 | sha@1.0.3 100 | softwarerero:accounts-t9n@1.0.9 101 | spacebars@1.0.6 102 | spacebars-compiler@1.0.6 103 | spiderable@1.0.7 104 | srp@1.0.3 105 | stylus@1.0.7 106 | summernote:standalone@0.6.7 107 | templating@1.1.1 108 | tmeasday:publish-counts@0.4.0 109 | tracker@1.0.7 110 | ui@1.0.6 111 | underscore@1.0.3 112 | underscorestring:underscore.string@3.0.3_1 113 | url@1.0.4 114 | useraccounts:bootstrap@1.11.1 115 | useraccounts:core@1.11.1 116 | webapp@1.2.0 117 | webapp-hashing@1.0.3 118 | yogiben:admin@1.0.9 119 | zimme:iron-router-active@1.0.4 120 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nathan Strauser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Ethereum Jobs 2 | http://jobs.ethercasts.com/ 3 | 4 | # Ethereum Job Board and Expert Directory 5 | 6 | If you have a project or problem you need help with, you can post a job or contact an expert. 7 | 8 | If you are a developer, you can apply to jobs or offer your services for hire. 9 | 10 | Based on We Work Meteor by Nate Strauser. http://www.weworkmeteor.com/ 11 | 12 | ## Settings 13 | This app does need a settings file to run - see `settings-example.json` for the supported values. The actual settings used for wework.meteor.com are kept private 14 | 15 | ## Contributing 16 | If anyone wants to improve on this, please submit a pull request w/ your changes. 17 | 18 | ## Forking 19 | Forking for unique communities is quite welcome and encouraged, but please alter the colors or appearance at least a little bit so all forks don't look exactly the same. 20 | 21 | 22 | #### Forks for other communities 23 | [Ethereum Jobs](http://jobs.ethercasts.com/) by @jorisbontje [Repo](https://github.com/EtherCasts/wework) 24 | -------------------------------------------------------------------------------- /client/autorun.js: -------------------------------------------------------------------------------- 1 | Tracker.autorun(function() { 2 | var current = Router.current(); 3 | 4 | Tracker.afterFlush(function() { 5 | $('.content-inner').scrollTop(0); 6 | $(window).scrollTop(0); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /client/helpers.js: -------------------------------------------------------------------------------- 1 | UI.registerHelper("formatDate", function(timestamp) { 2 | if(timestamp) 3 | return moment(timestamp).format('MMMM Do YYYY'); 4 | }); 5 | 6 | UI.registerHelper("currentUserDisplayName", function() { 7 | return getUserName(Meteor.user()); 8 | }); 9 | 10 | 11 | UI.registerHelper("currentUserEmail", function() { 12 | return getUserEmail(Meteor.user()); 13 | }); 14 | 15 | UI.registerHelper("resizeImageUrl", function(imageUrl, height, width) { 16 | if(imageUrl) 17 | return imageUrl + "-/resize/" + height + "x" + width + "/"; 18 | }); 19 | -------------------------------------------------------------------------------- /client/lib/custom.bootstrap.import.less: -------------------------------------------------------------------------------- 1 | // This File is for you to modify! 2 | // It won't be overwritten as long as it exists. 3 | // You may include this file into your less files to benefit from 4 | // mixins and variables that bootstrap provides. 5 | 6 | @import "custom.bootstrap.mixins.import.less"; 7 | 8 | 9 | // @import "bootstrap/less/variables.less" 10 | // 11 | // Variables 12 | // -------------------------------------------------- 13 | 14 | 15 | //== Colors 16 | // 17 | //## Gray and brand colors for use across Bootstrap. 18 | 19 | @gray-base: #000; 20 | @gray-darker: lighten(@gray-base, 13.5%); // #222 21 | @gray-dark: lighten(@gray-base, 20%); // #333 22 | @gray: lighten(@gray-base, 33.5%); // #555 23 | @gray-light: lighten(@gray-base, 46.7%); // #777 24 | @gray-lighter: lighten(@gray-base, 93.5%); // #eee 25 | 26 | @brand-primary: #428bca; 27 | @brand-success: #5cb85c; 28 | @brand-info: #5bc0de; 29 | @brand-warning: #f0ad4e; 30 | @brand-danger: #d9534f; 31 | 32 | 33 | //== Scaffolding 34 | // 35 | //## Settings for some of the most global styles. 36 | 37 | //** Background color for ``. 38 | @body-bg: #fff; 39 | //** Global text color on ``. 40 | @text-color: @gray-dark; 41 | 42 | //** Global textual link color. 43 | @link-color: @brand-primary; 44 | //** Link hover color set via `darken()` function. 45 | @link-hover-color: darken(@link-color, 15%); 46 | //** Link hover decoration. 47 | @link-hover-decoration: underline; 48 | 49 | 50 | //== Typography 51 | // 52 | //## Font, line-height, and color for body text, headings, and more. 53 | 54 | @font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif; 55 | @font-family-serif: Georgia, "Times New Roman", Times, serif; 56 | //** Default monospace fonts for ``, ``, and `
`.
 57 | @font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace;
 58 | @font-family-base:        @font-family-sans-serif;
 59 | 
 60 | @font-size-base:          14px;
 61 | @font-size-large:         ceil((@font-size-base * 1.25)); // ~18px
 62 | @font-size-small:         ceil((@font-size-base * 0.85)); // ~12px
 63 | 
 64 | @font-size-h1:            floor((@font-size-base * 2.6)); // ~36px
 65 | @font-size-h2:            floor((@font-size-base * 2.15)); // ~30px
 66 | @font-size-h3:            ceil((@font-size-base * 1.7)); // ~24px
 67 | @font-size-h4:            ceil((@font-size-base * 1.25)); // ~18px
 68 | @font-size-h5:            @font-size-base;
 69 | @font-size-h6:            ceil((@font-size-base * 0.85)); // ~12px
 70 | 
 71 | //** Unit-less `line-height` for use in components like buttons.
 72 | @line-height-base:        1.428571429; // 20/14
 73 | //** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
 74 | @line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px
 75 | 
 76 | //** By default, this inherits from the ``.
 77 | @headings-font-family:    inherit;
 78 | @headings-font-weight:    500;
 79 | @headings-line-height:    1.1;
 80 | @headings-color:          inherit;
 81 | 
 82 | 
 83 | //== Iconography
 84 | //
 85 | //## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
 86 | 
 87 | //** Load fonts from this directory.
 88 | @icon-font-path:          "../fonts/";
 89 | //** File name for all font files.
 90 | @icon-font-name:          "glyphicons-halflings-regular";
 91 | //** Element ID within SVG icon file.
 92 | @icon-font-svg-id:        "glyphicons_halflingsregular";
 93 | 
 94 | 
 95 | //== Components
 96 | //
 97 | //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
 98 | 
 99 | @padding-base-vertical:     6px;
100 | @padding-base-horizontal:   12px;
101 | 
102 | @padding-large-vertical:    10px;
103 | @padding-large-horizontal:  16px;
104 | 
105 | @padding-small-vertical:    5px;
106 | @padding-small-horizontal:  10px;
107 | 
108 | @padding-xs-vertical:       1px;
109 | @padding-xs-horizontal:     5px;
110 | 
111 | @line-height-large:         1.3333333; // extra decimals for Win 8.1 Chrome
112 | @line-height-small:         1.5;
113 | 
114 | @border-radius-base:        4px;
115 | @border-radius-large:       6px;
116 | @border-radius-small:       3px;
117 | 
118 | //** Global color for active items (e.g., navs or dropdowns).
119 | @component-active-color:    #fff;
120 | //** Global background color for active items (e.g., navs or dropdowns).
121 | @component-active-bg:       @brand-primary;
122 | 
123 | //** Width of the `border` for generating carets that indicator dropdowns.
124 | @caret-width-base:          4px;
125 | //** Carets increase slightly in size for larger components.
126 | @caret-width-large:         5px;
127 | 
128 | 
129 | //== Tables
130 | //
131 | //## Customizes the `.table` component with basic values, each used across all table variations.
132 | 
133 | //** Padding for ``s and ``s.
134 | @table-cell-padding:            8px;
135 | //** Padding for cells in `.table-condensed`.
136 | @table-condensed-cell-padding:  5px;
137 | 
138 | //** Default background color used for all tables.
139 | @table-bg:                      transparent;
140 | //** Background color used for `.table-striped`.
141 | @table-bg-accent:               #f9f9f9;
142 | //** Background color used for `.table-hover`.
143 | @table-bg-hover:                #f5f5f5;
144 | @table-bg-active:               @table-bg-hover;
145 | 
146 | //** Border color for table and cell borders.
147 | @table-border-color:            #ddd;
148 | 
149 | 
150 | //== Buttons
151 | //
152 | //## For each of Bootstrap's buttons, define text, background and border color.
153 | 
154 | @btn-font-weight:                normal;
155 | 
156 | @btn-default-color:              #333;
157 | @btn-default-bg:                 #fff;
158 | @btn-default-border:             #ccc;
159 | 
160 | @btn-primary-color:              #fff;
161 | @btn-primary-bg:                 @brand-primary;
162 | @btn-primary-border:             darken(@btn-primary-bg, 5%);
163 | 
164 | @btn-success-color:              #fff;
165 | @btn-success-bg:                 @brand-success;
166 | @btn-success-border:             darken(@btn-success-bg, 5%);
167 | 
168 | @btn-info-color:                 #fff;
169 | @btn-info-bg:                    @brand-info;
170 | @btn-info-border:                darken(@btn-info-bg, 5%);
171 | 
172 | @btn-warning-color:              #fff;
173 | @btn-warning-bg:                 @brand-warning;
174 | @btn-warning-border:             darken(@btn-warning-bg, 5%);
175 | 
176 | @btn-danger-color:               #fff;
177 | @btn-danger-bg:                  @brand-danger;
178 | @btn-danger-border:              darken(@btn-danger-bg, 5%);
179 | 
180 | @btn-link-disabled-color:        @gray-light;
181 | 
182 | 
183 | //== Forms
184 | //
185 | //##
186 | 
187 | //** `` background color
188 | @input-bg:                       #fff;
189 | //** `` background color
190 | @input-bg-disabled:              @gray-lighter;
191 | 
192 | //** Text color for ``s
193 | @input-color:                    @gray;
194 | //** `` border color
195 | @input-border:                   #ccc;
196 | 
197 | // TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
198 | //** Default `.form-control` border radius
199 | // This has no effect on ``s in CSS.
200 | @input-border-radius:            @border-radius-base;
201 | //** Large `.form-control` border radius
202 | @input-border-radius-large:      @border-radius-large;
203 | //** Small `.form-control` border radius
204 | @input-border-radius-small:      @border-radius-small;
205 | 
206 | //** Border color for inputs on focus
207 | @input-border-focus:             #66afe9;
208 | 
209 | //** Placeholder text color
210 | @input-color-placeholder:        #999;
211 | 
212 | //** Default `.form-control` height
213 | @input-height-base:              (@line-height-computed + (@padding-base-vertical * 2) + 2);
214 | //** Large `.form-control` height
215 | @input-height-large:             (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
216 | //** Small `.form-control` height
217 | @input-height-small:             (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
218 | 
219 | //** `.form-group` margin
220 | @form-group-margin-bottom:       15px;
221 | 
222 | @legend-color:                   @gray-dark;
223 | @legend-border-color:            #e5e5e5;
224 | 
225 | //** Background color for textual input addons
226 | @input-group-addon-bg:           @gray-lighter;
227 | //** Border color for textual input addons
228 | @input-group-addon-border-color: @input-border;
229 | 
230 | //** Disabled cursor for form controls and buttons.
231 | @cursor-disabled:                not-allowed;
232 | 
233 | 
234 | //== Dropdowns
235 | //
236 | //## Dropdown menu container and contents.
237 | 
238 | //** Background for the dropdown menu.
239 | @dropdown-bg:                    #fff;
240 | //** Dropdown menu `border-color`.
241 | @dropdown-border:                rgba(0,0,0,.15);
242 | //** Dropdown menu `border-color` **for IE8**.
243 | @dropdown-fallback-border:       #ccc;
244 | //** Divider color for between dropdown items.
245 | @dropdown-divider-bg:            #e5e5e5;
246 | 
247 | //** Dropdown link text color.
248 | @dropdown-link-color:            @gray-dark;
249 | //** Hover color for dropdown links.
250 | @dropdown-link-hover-color:      darken(@gray-dark, 5%);
251 | //** Hover background for dropdown links.
252 | @dropdown-link-hover-bg:         #f5f5f5;
253 | 
254 | //** Active dropdown menu item text color.
255 | @dropdown-link-active-color:     @component-active-color;
256 | //** Active dropdown menu item background color.
257 | @dropdown-link-active-bg:        @component-active-bg;
258 | 
259 | //** Disabled dropdown menu item background color.
260 | @dropdown-link-disabled-color:   @gray-light;
261 | 
262 | //** Text color for headers within dropdown menus.
263 | @dropdown-header-color:          @gray-light;
264 | 
265 | //** Deprecated `@dropdown-caret-color` as of v3.1.0
266 | @dropdown-caret-color:           #000;
267 | 
268 | 
269 | //-- Z-index master list
270 | //
271 | // Warning: Avoid customizing these values. They're used for a bird's eye view
272 | // of components dependent on the z-axis and are designed to all work together.
273 | //
274 | // Note: These variables are not generated into the Customizer.
275 | 
276 | @zindex-navbar:            1000;
277 | @zindex-dropdown:          1000;
278 | @zindex-popover:           1060;
279 | @zindex-tooltip:           1070;
280 | @zindex-navbar-fixed:      1030;
281 | @zindex-modal-background:  1040;
282 | @zindex-modal:             1050;
283 | 
284 | 
285 | //== Media queries breakpoints
286 | //
287 | //## Define the breakpoints at which your layout will change, adapting to different screen sizes.
288 | 
289 | // Extra small screen / phone
290 | //** Deprecated `@screen-xs` as of v3.0.1
291 | @screen-xs:                  480px;
292 | //** Deprecated `@screen-xs-min` as of v3.2.0
293 | @screen-xs-min:              @screen-xs;
294 | //** Deprecated `@screen-phone` as of v3.0.1
295 | @screen-phone:               @screen-xs-min;
296 | 
297 | // Small screen / tablet
298 | //** Deprecated `@screen-sm` as of v3.0.1
299 | @screen-sm:                  768px;
300 | @screen-sm-min:              @screen-sm;
301 | //** Deprecated `@screen-tablet` as of v3.0.1
302 | @screen-tablet:              @screen-sm-min;
303 | 
304 | // Medium screen / desktop
305 | //** Deprecated `@screen-md` as of v3.0.1
306 | @screen-md:                  992px;
307 | @screen-md-min:              @screen-md;
308 | //** Deprecated `@screen-desktop` as of v3.0.1
309 | @screen-desktop:             @screen-md-min;
310 | 
311 | // Large screen / wide desktop
312 | //** Deprecated `@screen-lg` as of v3.0.1
313 | @screen-lg:                  1200px;
314 | @screen-lg-min:              @screen-lg;
315 | //** Deprecated `@screen-lg-desktop` as of v3.0.1
316 | @screen-lg-desktop:          @screen-lg-min;
317 | 
318 | // So media queries don't overlap when required, provide a maximum
319 | @screen-xs-max:              (@screen-sm-min - 1);
320 | @screen-sm-max:              (@screen-md-min - 1);
321 | @screen-md-max:              (@screen-lg-min - 1);
322 | 
323 | 
324 | //== Grid system
325 | //
326 | //## Define your custom responsive grid.
327 | 
328 | //** Number of columns in the grid.
329 | @grid-columns:              12;
330 | //** Padding between columns. Gets divided in half for the left and right.
331 | @grid-gutter-width:         30px;
332 | // Navbar collapse
333 | //** Point at which the navbar becomes uncollapsed.
334 | @grid-float-breakpoint:     @screen-sm-min;
335 | //** Point at which the navbar begins collapsing.
336 | @grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
337 | 
338 | 
339 | //== Container sizes
340 | //
341 | //## Define the maximum width of `.container` for different screen sizes.
342 | 
343 | // Small screen / tablet
344 | @container-tablet:             (720px + @grid-gutter-width);
345 | //** For `@screen-sm-min` and up.
346 | @container-sm:                 @container-tablet;
347 | 
348 | // Medium screen / desktop
349 | @container-desktop:            (940px + @grid-gutter-width);
350 | //** For `@screen-md-min` and up.
351 | @container-md:                 @container-desktop;
352 | 
353 | // Large screen / wide desktop
354 | @container-large-desktop:      (1140px + @grid-gutter-width);
355 | //** For `@screen-lg-min` and up.
356 | @container-lg:                 @container-large-desktop;
357 | 
358 | 
359 | //== Navbar
360 | //
361 | //##
362 | 
363 | // Basics of a navbar
364 | @navbar-height:                    50px;
365 | @navbar-margin-bottom:             @line-height-computed;
366 | @navbar-border-radius:             @border-radius-base;
367 | @navbar-padding-horizontal:        floor((@grid-gutter-width / 2));
368 | @navbar-padding-vertical:          ((@navbar-height - @line-height-computed) / 2);
369 | @navbar-collapse-max-height:       340px;
370 | 
371 | @navbar-default-color:             #777;
372 | @navbar-default-bg:                #f8f8f8;
373 | @navbar-default-border:            darken(@navbar-default-bg, 6.5%);
374 | 
375 | // Navbar links
376 | @navbar-default-link-color:                #777;
377 | @navbar-default-link-hover-color:          #333;
378 | @navbar-default-link-hover-bg:             transparent;
379 | @navbar-default-link-active-color:         #555;
380 | @navbar-default-link-active-bg:            darken(@navbar-default-bg, 6.5%);
381 | @navbar-default-link-disabled-color:       #ccc;
382 | @navbar-default-link-disabled-bg:          transparent;
383 | 
384 | // Navbar brand label
385 | @navbar-default-brand-color:               @navbar-default-link-color;
386 | @navbar-default-brand-hover-color:         darken(@navbar-default-brand-color, 10%);
387 | @navbar-default-brand-hover-bg:            transparent;
388 | 
389 | // Navbar toggle
390 | @navbar-default-toggle-hover-bg:           #ddd;
391 | @navbar-default-toggle-icon-bar-bg:        #888;
392 | @navbar-default-toggle-border-color:       #ddd;
393 | 
394 | 
395 | // Inverted navbar
396 | // Reset inverted navbar basics
397 | @navbar-inverse-color:                      lighten(@gray-light, 15%);
398 | @navbar-inverse-bg:                         #222;
399 | @navbar-inverse-border:                     darken(@navbar-inverse-bg, 10%);
400 | 
401 | // Inverted navbar links
402 | @navbar-inverse-link-color:                 lighten(@gray-light, 15%);
403 | @navbar-inverse-link-hover-color:           #fff;
404 | @navbar-inverse-link-hover-bg:              transparent;
405 | @navbar-inverse-link-active-color:          @navbar-inverse-link-hover-color;
406 | @navbar-inverse-link-active-bg:             darken(@navbar-inverse-bg, 10%);
407 | @navbar-inverse-link-disabled-color:        #444;
408 | @navbar-inverse-link-disabled-bg:           transparent;
409 | 
410 | // Inverted navbar brand label
411 | @navbar-inverse-brand-color:                @navbar-inverse-link-color;
412 | @navbar-inverse-brand-hover-color:          #fff;
413 | @navbar-inverse-brand-hover-bg:             transparent;
414 | 
415 | // Inverted navbar toggle
416 | @navbar-inverse-toggle-hover-bg:            #333;
417 | @navbar-inverse-toggle-icon-bar-bg:         #fff;
418 | @navbar-inverse-toggle-border-color:        #333;
419 | 
420 | 
421 | //== Navs
422 | //
423 | //##
424 | 
425 | //=== Shared nav styles
426 | @nav-link-padding:                          10px 15px;
427 | @nav-link-hover-bg:                         @gray-lighter;
428 | 
429 | @nav-disabled-link-color:                   @gray-light;
430 | @nav-disabled-link-hover-color:             @gray-light;
431 | 
432 | //== Tabs
433 | @nav-tabs-border-color:                     #ddd;
434 | 
435 | @nav-tabs-link-hover-border-color:          @gray-lighter;
436 | 
437 | @nav-tabs-active-link-hover-bg:             @body-bg;
438 | @nav-tabs-active-link-hover-color:          @gray;
439 | @nav-tabs-active-link-hover-border-color:   #ddd;
440 | 
441 | @nav-tabs-justified-link-border-color:            #ddd;
442 | @nav-tabs-justified-active-link-border-color:     @body-bg;
443 | 
444 | //== Pills
445 | @nav-pills-border-radius:                   @border-radius-base;
446 | @nav-pills-active-link-hover-bg:            @component-active-bg;
447 | @nav-pills-active-link-hover-color:         @component-active-color;
448 | 
449 | 
450 | //== Pagination
451 | //
452 | //##
453 | 
454 | @pagination-color:                     @link-color;
455 | @pagination-bg:                        #fff;
456 | @pagination-border:                    #ddd;
457 | 
458 | @pagination-hover-color:               @link-hover-color;
459 | @pagination-hover-bg:                  @gray-lighter;
460 | @pagination-hover-border:              #ddd;
461 | 
462 | @pagination-active-color:              #fff;
463 | @pagination-active-bg:                 @brand-primary;
464 | @pagination-active-border:             @brand-primary;
465 | 
466 | @pagination-disabled-color:            @gray-light;
467 | @pagination-disabled-bg:               #fff;
468 | @pagination-disabled-border:           #ddd;
469 | 
470 | 
471 | //== Pager
472 | //
473 | //##
474 | 
475 | @pager-bg:                             @pagination-bg;
476 | @pager-border:                         @pagination-border;
477 | @pager-border-radius:                  15px;
478 | 
479 | @pager-hover-bg:                       @pagination-hover-bg;
480 | 
481 | @pager-active-bg:                      @pagination-active-bg;
482 | @pager-active-color:                   @pagination-active-color;
483 | 
484 | @pager-disabled-color:                 @pagination-disabled-color;
485 | 
486 | 
487 | //== Jumbotron
488 | //
489 | //##
490 | 
491 | @jumbotron-padding:              30px;
492 | @jumbotron-color:                inherit;
493 | @jumbotron-bg:                   @gray-lighter;
494 | @jumbotron-heading-color:        inherit;
495 | @jumbotron-font-size:            ceil((@font-size-base * 1.5));
496 | 
497 | 
498 | //== Form states and alerts
499 | //
500 | //## Define colors for form feedback states and, by default, alerts.
501 | 
502 | @state-success-text:             #3c763d;
503 | @state-success-bg:               #dff0d8;
504 | @state-success-border:           darken(spin(@state-success-bg, -10), 5%);
505 | 
506 | @state-info-text:                #31708f;
507 | @state-info-bg:                  #d9edf7;
508 | @state-info-border:              darken(spin(@state-info-bg, -10), 7%);
509 | 
510 | @state-warning-text:             #8a6d3b;
511 | @state-warning-bg:               #fcf8e3;
512 | @state-warning-border:           darken(spin(@state-warning-bg, -10), 5%);
513 | 
514 | @state-danger-text:              #a94442;
515 | @state-danger-bg:                #f2dede;
516 | @state-danger-border:            darken(spin(@state-danger-bg, -10), 5%);
517 | 
518 | 
519 | //== Tooltips
520 | //
521 | //##
522 | 
523 | //** Tooltip max width
524 | @tooltip-max-width:           200px;
525 | //** Tooltip text color
526 | @tooltip-color:               #fff;
527 | //** Tooltip background color
528 | @tooltip-bg:                  #000;
529 | @tooltip-opacity:             .9;
530 | 
531 | //** Tooltip arrow width
532 | @tooltip-arrow-width:         5px;
533 | //** Tooltip arrow color
534 | @tooltip-arrow-color:         @tooltip-bg;
535 | 
536 | 
537 | //== Popovers
538 | //
539 | //##
540 | 
541 | //** Popover body background color
542 | @popover-bg:                          #fff;
543 | //** Popover maximum width
544 | @popover-max-width:                   276px;
545 | //** Popover border color
546 | @popover-border-color:                rgba(0,0,0,.2);
547 | //** Popover fallback border color
548 | @popover-fallback-border-color:       #ccc;
549 | 
550 | //** Popover title background color
551 | @popover-title-bg:                    darken(@popover-bg, 3%);
552 | 
553 | //** Popover arrow width
554 | @popover-arrow-width:                 10px;
555 | //** Popover arrow color
556 | @popover-arrow-color:                 @popover-bg;
557 | 
558 | //** Popover outer arrow width
559 | @popover-arrow-outer-width:           (@popover-arrow-width + 1);
560 | //** Popover outer arrow color
561 | @popover-arrow-outer-color:           fadein(@popover-border-color, 5%);
562 | //** Popover outer arrow fallback color
563 | @popover-arrow-outer-fallback-color:  darken(@popover-fallback-border-color, 20%);
564 | 
565 | 
566 | //== Labels
567 | //
568 | //##
569 | 
570 | //** Default label background color
571 | @label-default-bg:            @gray-light;
572 | //** Primary label background color
573 | @label-primary-bg:            @brand-primary;
574 | //** Success label background color
575 | @label-success-bg:            @brand-success;
576 | //** Info label background color
577 | @label-info-bg:               @brand-info;
578 | //** Warning label background color
579 | @label-warning-bg:            @brand-warning;
580 | //** Danger label background color
581 | @label-danger-bg:             @brand-danger;
582 | 
583 | //** Default label text color
584 | @label-color:                 #fff;
585 | //** Default text color of a linked label
586 | @label-link-hover-color:      #fff;
587 | 
588 | 
589 | //== Modals
590 | //
591 | //##
592 | 
593 | //** Padding applied to the modal body
594 | @modal-inner-padding:         15px;
595 | 
596 | //** Padding applied to the modal title
597 | @modal-title-padding:         15px;
598 | //** Modal title line-height
599 | @modal-title-line-height:     @line-height-base;
600 | 
601 | //** Background color of modal content area
602 | @modal-content-bg:                             #fff;
603 | //** Modal content border color
604 | @modal-content-border-color:                   rgba(0,0,0,.2);
605 | //** Modal content border color **for IE8**
606 | @modal-content-fallback-border-color:          #999;
607 | 
608 | //** Modal backdrop background color
609 | @modal-backdrop-bg:           #000;
610 | //** Modal backdrop opacity
611 | @modal-backdrop-opacity:      .5;
612 | //** Modal header border color
613 | @modal-header-border-color:   #e5e5e5;
614 | //** Modal footer border color
615 | @modal-footer-border-color:   @modal-header-border-color;
616 | 
617 | @modal-lg:                    900px;
618 | @modal-md:                    600px;
619 | @modal-sm:                    300px;
620 | 
621 | 
622 | //== Alerts
623 | //
624 | //## Define alert colors, border radius, and padding.
625 | 
626 | @alert-padding:               15px;
627 | @alert-border-radius:         @border-radius-base;
628 | @alert-link-font-weight:      bold;
629 | 
630 | @alert-success-bg:            @state-success-bg;
631 | @alert-success-text:          @state-success-text;
632 | @alert-success-border:        @state-success-border;
633 | 
634 | @alert-info-bg:               @state-info-bg;
635 | @alert-info-text:             @state-info-text;
636 | @alert-info-border:           @state-info-border;
637 | 
638 | @alert-warning-bg:            @state-warning-bg;
639 | @alert-warning-text:          @state-warning-text;
640 | @alert-warning-border:        @state-warning-border;
641 | 
642 | @alert-danger-bg:             @state-danger-bg;
643 | @alert-danger-text:           @state-danger-text;
644 | @alert-danger-border:         @state-danger-border;
645 | 
646 | 
647 | //== Progress bars
648 | //
649 | //##
650 | 
651 | //** Background color of the whole progress component
652 | @progress-bg:                 #f5f5f5;
653 | //** Progress bar text color
654 | @progress-bar-color:          #fff;
655 | //** Variable for setting rounded corners on progress bar.
656 | @progress-border-radius:      @border-radius-base;
657 | 
658 | //** Default progress bar color
659 | @progress-bar-bg:             @brand-primary;
660 | //** Success progress bar color
661 | @progress-bar-success-bg:     @brand-success;
662 | //** Warning progress bar color
663 | @progress-bar-warning-bg:     @brand-warning;
664 | //** Danger progress bar color
665 | @progress-bar-danger-bg:      @brand-danger;
666 | //** Info progress bar color
667 | @progress-bar-info-bg:        @brand-info;
668 | 
669 | 
670 | //== List group
671 | //
672 | //##
673 | 
674 | //** Background color on `.list-group-item`
675 | @list-group-bg:                 #fff;
676 | //** `.list-group-item` border color
677 | @list-group-border:             #ddd;
678 | //** List group border radius
679 | @list-group-border-radius:      @border-radius-base;
680 | 
681 | //** Background color of single list items on hover
682 | @list-group-hover-bg:           #f5f5f5;
683 | //** Text color of active list items
684 | @list-group-active-color:       @component-active-color;
685 | //** Background color of active list items
686 | @list-group-active-bg:          @component-active-bg;
687 | //** Border color of active list elements
688 | @list-group-active-border:      @list-group-active-bg;
689 | //** Text color for content within active list items
690 | @list-group-active-text-color:  lighten(@list-group-active-bg, 40%);
691 | 
692 | //** Text color of disabled list items
693 | @list-group-disabled-color:      @gray-light;
694 | //** Background color of disabled list items
695 | @list-group-disabled-bg:         @gray-lighter;
696 | //** Text color for content within disabled list items
697 | @list-group-disabled-text-color: @list-group-disabled-color;
698 | 
699 | @list-group-link-color:         #555;
700 | @list-group-link-hover-color:   @list-group-link-color;
701 | @list-group-link-heading-color: #333;
702 | 
703 | 
704 | //== Panels
705 | //
706 | //##
707 | 
708 | @panel-bg:                    #fff;
709 | @panel-body-padding:          15px;
710 | @panel-heading-padding:       10px 15px;
711 | @panel-footer-padding:        @panel-heading-padding;
712 | @panel-border-radius:         @border-radius-base;
713 | 
714 | //** Border color for elements within panels
715 | @panel-inner-border:          #ddd;
716 | @panel-footer-bg:             #f5f5f5;
717 | 
718 | @panel-default-text:          @gray-dark;
719 | @panel-default-border:        #ddd;
720 | @panel-default-heading-bg:    #f5f5f5;
721 | 
722 | @panel-primary-text:          #fff;
723 | @panel-primary-border:        @brand-primary;
724 | @panel-primary-heading-bg:    @brand-primary;
725 | 
726 | @panel-success-text:          @state-success-text;
727 | @panel-success-border:        @state-success-border;
728 | @panel-success-heading-bg:    @state-success-bg;
729 | 
730 | @panel-info-text:             @state-info-text;
731 | @panel-info-border:           @state-info-border;
732 | @panel-info-heading-bg:       @state-info-bg;
733 | 
734 | @panel-warning-text:          @state-warning-text;
735 | @panel-warning-border:        @state-warning-border;
736 | @panel-warning-heading-bg:    @state-warning-bg;
737 | 
738 | @panel-danger-text:           @state-danger-text;
739 | @panel-danger-border:         @state-danger-border;
740 | @panel-danger-heading-bg:     @state-danger-bg;
741 | 
742 | 
743 | //== Thumbnails
744 | //
745 | //##
746 | 
747 | //** Padding around the thumbnail image
748 | @thumbnail-padding:           4px;
749 | //** Thumbnail background color
750 | @thumbnail-bg:                @body-bg;
751 | //** Thumbnail border color
752 | @thumbnail-border:            #ddd;
753 | //** Thumbnail border radius
754 | @thumbnail-border-radius:     @border-radius-base;
755 | 
756 | //** Custom text color for thumbnail captions
757 | @thumbnail-caption-color:     @text-color;
758 | //** Padding around the thumbnail caption
759 | @thumbnail-caption-padding:   9px;
760 | 
761 | 
762 | //== Wells
763 | //
764 | //##
765 | 
766 | @well-bg:                     #f5f5f5;
767 | @well-border:                 darken(@well-bg, 7%);
768 | 
769 | 
770 | //== Badges
771 | //
772 | //##
773 | 
774 | @badge-color:                 #fff;
775 | //** Linked badge text color on hover
776 | @badge-link-hover-color:      #fff;
777 | @badge-bg:                    @gray-light;
778 | 
779 | //** Badge text color in active nav link
780 | @badge-active-color:          @link-color;
781 | //** Badge background color in active nav link
782 | @badge-active-bg:             #fff;
783 | 
784 | @badge-font-weight:           bold;
785 | @badge-line-height:           1;
786 | @badge-border-radius:         10px;
787 | 
788 | 
789 | //== Breadcrumbs
790 | //
791 | //##
792 | 
793 | @breadcrumb-padding-vertical:   8px;
794 | @breadcrumb-padding-horizontal: 15px;
795 | //** Breadcrumb background color
796 | @breadcrumb-bg:                 #f5f5f5;
797 | //** Breadcrumb text color
798 | @breadcrumb-color:              #ccc;
799 | //** Text color of current page in the breadcrumb
800 | @breadcrumb-active-color:       @gray-light;
801 | //** Textual separator for between breadcrumb elements
802 | @breadcrumb-separator:          "/";
803 | 
804 | 
805 | //== Carousel
806 | //
807 | //##
808 | 
809 | @carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6);
810 | 
811 | @carousel-control-color:                      #fff;
812 | @carousel-control-width:                      15%;
813 | @carousel-control-opacity:                    .5;
814 | @carousel-control-font-size:                  20px;
815 | 
816 | @carousel-indicator-active-bg:                #fff;
817 | @carousel-indicator-border-color:             #fff;
818 | 
819 | @carousel-caption-color:                      #fff;
820 | 
821 | 
822 | //== Close
823 | //
824 | //##
825 | 
826 | @close-font-weight:           bold;
827 | @close-color:                 #000;
828 | @close-text-shadow:           0 1px 0 #fff;
829 | 
830 | 
831 | //== Code
832 | //
833 | //##
834 | 
835 | @code-color:                  #c7254e;
836 | @code-bg:                     #f9f2f4;
837 | 
838 | @kbd-color:                   #fff;
839 | @kbd-bg:                      #333;
840 | 
841 | @pre-bg:                      #f5f5f5;
842 | @pre-color:                   @gray-dark;
843 | @pre-border-color:            #ccc;
844 | @pre-scrollable-max-height:   340px;
845 | 
846 | 
847 | //== Type
848 | //
849 | //##
850 | 
851 | //** Horizontal offset for forms and lists.
852 | @component-offset-horizontal: 180px;
853 | //** Text muted color
854 | @text-muted:                  @gray-light;
855 | //** Abbreviations and acronyms border color
856 | @abbr-border-color:           @gray-light;
857 | //** Headings small color
858 | @headings-small-color:        @gray-light;
859 | //** Blockquote small color
860 | @blockquote-small-color:      @gray-light;
861 | //** Blockquote font size
862 | @blockquote-font-size:        (@font-size-base * 1.25);
863 | //** Blockquote border color
864 | @blockquote-border-color:     @gray-lighter;
865 | //** Page header border color
866 | @page-header-border-color:    @gray-lighter;
867 | //** Width of horizontal description list titles
868 | @dl-horizontal-offset:        @component-offset-horizontal;
869 | //** Horizontal line color.
870 | @hr-border:                   @gray-lighter;
871 | 
872 | @import "journal.variables.import.less";
873 | @import "journal.theme.import.less";
874 | 


--------------------------------------------------------------------------------
/client/lib/custom.bootstrap.json:
--------------------------------------------------------------------------------
 1 | {"modules": {
 2 |   "normalize":            true,
 3 |   "print":                true,
 4 | 
 5 |   "scaffolding":          true,
 6 |   "type":                 true,
 7 |   "code":                 true,
 8 |   "grid":                 true,
 9 |   "tables":               true,
10 |   "forms":                true,
11 |   "buttons":              true,
12 | 
13 |   "glyphicons":           false,
14 |   "button-groups":        true,
15 |   "input-groups":         true,
16 |   "navs":                 true,
17 |   "navbar":               true,
18 |   "breadcrumbs":          true,
19 |   "pagination":           true,
20 |   "pager":                true,
21 |   "labels":               true,
22 |   "badges":               true,
23 |   "jumbotron":            true,
24 |   "thumbnails":           true,
25 |   "alerts":               true,
26 |   "progress-bars":        true,
27 |   "media":                true,
28 |   "list-group":           true,
29 |   "panels":               true,
30 |   "wells":                true,
31 |   "close":                true,
32 | 
33 |   "component-animations": true,
34 |   "dropdowns":            true,
35 |   "modals":               true,
36 |   "tooltip":              true,
37 |   "popovers":             true,
38 |   "carousel":             true,
39 | 
40 |   "affix":                true,
41 |   "alert":                true,
42 |   "button":               true,
43 |   "collapse":             true,
44 |   "scrollspy":            true,
45 |   "tab":                  true,
46 |   "transition":           true,
47 | 
48 |   "utilities":            true,
49 |   "responsive-utilities": true
50 | }}
51 | 


--------------------------------------------------------------------------------
/client/lib/custom.bootstrap.mixins.import.less:
--------------------------------------------------------------------------------
   1 | // THIS FILE IS GENERATED, DO NOT MODIFY IT!
   2 | // These are the mixins bootstrap provides
   3 | // They are included here so you can use them in your less files too,
   4 | // However: you should @import "custom.bootstrap.import.less" instead of this
   5 | 
   6 | 
   7 | // @import "bootstrap/less/mixins.less"
   8 | // Mixins
   9 | // --------------------------------------------------
  10 | 
  11 | // Utilities
  12 | 
  13 | 
  14 | // @import "bootstrap/less/mixins/hide-text.less"
  15 | // CSS image replacement
  16 | //
  17 | // Heads up! v3 launched with with only `.hide-text()`, but per our pattern for
  18 | // mixins being reused as classes with the same name, this doesn't hold up. As
  19 | // of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.
  20 | //
  21 | // Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757
  22 | 
  23 | // Deprecated as of v3.0.1 (will be removed in v4)
  24 | .hide-text() {
  25 |   font: ~"0/0" a;
  26 |   color: transparent;
  27 |   text-shadow: none;
  28 |   background-color: transparent;
  29 |   border: 0;
  30 | }
  31 | 
  32 | // New mixin to use as of v3.0.1
  33 | .text-hide() {
  34 |   .hide-text();
  35 | }
  36 | 
  37 | 
  38 | 
  39 | // @import "bootstrap/less/mixins/opacity.less"
  40 | // Opacity
  41 | 
  42 | .opacity(@opacity) {
  43 |   opacity: @opacity;
  44 |   // IE8 filter
  45 |   @opacity-ie: (@opacity * 100);
  46 |   filter: ~"alpha(opacity=@{opacity-ie})";
  47 | }
  48 | 
  49 | 
  50 | 
  51 | // @import "bootstrap/less/mixins/image.less"
  52 | // Image Mixins
  53 | // - Responsive image
  54 | // - Retina image
  55 | 
  56 | 
  57 | // Responsive image
  58 | //
  59 | // Keep images from scaling beyond the width of their parents.
  60 | .img-responsive(@display: block) {
  61 |   display: @display;
  62 |   max-width: 100%; // Part 1: Set a maximum relative to the parent
  63 |   height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching
  64 | }
  65 | 
  66 | 
  67 | // Retina image
  68 | //
  69 | // Short retina mixin for setting background-image and -size. Note that the
  70 | // spelling of `min--moz-device-pixel-ratio` is intentional.
  71 | .img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {
  72 |   background-image: url("@{file-1x}");
  73 | 
  74 |   @media
  75 |   only screen and (-webkit-min-device-pixel-ratio: 2),
  76 |   only screen and (   min--moz-device-pixel-ratio: 2),
  77 |   only screen and (     -o-min-device-pixel-ratio: 2/1),
  78 |   only screen and (        min-device-pixel-ratio: 2),
  79 |   only screen and (                min-resolution: 192dpi),
  80 |   only screen and (                min-resolution: 2dppx) {
  81 |     background-image: url("@{file-2x}");
  82 |     background-size: @width-1x @height-1x;
  83 |   }
  84 | }
  85 | 
  86 | 
  87 | 
  88 | // @import "bootstrap/less/mixins/labels.less"
  89 | // Labels
  90 | 
  91 | .label-variant(@color) {
  92 |   background-color: @color;
  93 | 
  94 |   &[href] {
  95 |     &:hover,
  96 |     &:focus {
  97 |       background-color: darken(@color, 10%);
  98 |     }
  99 |   }
 100 | }
 101 | 
 102 | 
 103 | 
 104 | // @import "bootstrap/less/mixins/reset-filter.less"
 105 | // Reset filters for IE
 106 | //
 107 | // When you need to remove a gradient background, do not forget to use this to reset
 108 | // the IE filter for IE9 and below.
 109 | 
 110 | .reset-filter() {
 111 |   filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)"));
 112 | }
 113 | 
 114 | 
 115 | 
 116 | // @import "bootstrap/less/mixins/resize.less"
 117 | // Resize anything
 118 | 
 119 | .resizable(@direction) {
 120 |   resize: @direction; // Options: horizontal, vertical, both
 121 |   overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible`
 122 | }
 123 | 
 124 | 
 125 | 
 126 | // @import "bootstrap/less/mixins/responsive-visibility.less"
 127 | // Responsive utilities
 128 | 
 129 | //
 130 | // More easily include all the states for responsive-utilities.less.
 131 | .responsive-visibility() {
 132 |   display: block !important;
 133 |   table&  { display: table; }
 134 |   tr&     { display: table-row !important; }
 135 |   th&,
 136 |   td&     { display: table-cell !important; }
 137 | }
 138 | 
 139 | .responsive-invisibility() {
 140 |   display: none !important;
 141 | }
 142 | 
 143 | 
 144 | 
 145 | // @import "bootstrap/less/mixins/size.less"
 146 | // Sizing shortcuts
 147 | 
 148 | .size(@width; @height) {
 149 |   width: @width;
 150 |   height: @height;
 151 | }
 152 | 
 153 | .square(@size) {
 154 |   .size(@size; @size);
 155 | }
 156 | 
 157 | 
 158 | 
 159 | // @import "bootstrap/less/mixins/tab-focus.less"
 160 | // WebKit-style focus
 161 | 
 162 | .tab-focus() {
 163 |   // Default
 164 |   outline: thin dotted;
 165 |   // WebKit
 166 |   outline: 5px auto -webkit-focus-ring-color;
 167 |   outline-offset: -2px;
 168 | }
 169 | 
 170 | 
 171 | 
 172 | // @import "bootstrap/less/mixins/text-emphasis.less"
 173 | // Typography
 174 | 
 175 | .text-emphasis-variant(@color) {
 176 |   color: @color;
 177 |   a&:hover {
 178 |     color: darken(@color, 10%);
 179 |   }
 180 | }
 181 | 
 182 | 
 183 | 
 184 | // @import "bootstrap/less/mixins/text-overflow.less"
 185 | // Text overflow
 186 | // Requires inline-block or block for proper styling
 187 | 
 188 | .text-overflow() {
 189 |   overflow: hidden;
 190 |   text-overflow: ellipsis;
 191 |   white-space: nowrap;
 192 | }
 193 | 
 194 | 
 195 | 
 196 | // @import "bootstrap/less/mixins/vendor-prefixes.less"
 197 | // Vendor Prefixes
 198 | //
 199 | // All vendor mixins are deprecated as of v3.2.0 due to the introduction of
 200 | // Autoprefixer in our Gruntfile. They will be removed in v4.
 201 | 
 202 | // - Animations
 203 | // - Backface visibility
 204 | // - Box shadow
 205 | // - Box sizing
 206 | // - Content columns
 207 | // - Hyphens
 208 | // - Placeholder text
 209 | // - Transformations
 210 | // - Transitions
 211 | // - User Select
 212 | 
 213 | 
 214 | // Animations
 215 | .animation(@animation) {
 216 |   -webkit-animation: @animation;
 217 |        -o-animation: @animation;
 218 |           animation: @animation;
 219 | }
 220 | .animation-name(@name) {
 221 |   -webkit-animation-name: @name;
 222 |           animation-name: @name;
 223 | }
 224 | .animation-duration(@duration) {
 225 |   -webkit-animation-duration: @duration;
 226 |           animation-duration: @duration;
 227 | }
 228 | .animation-timing-function(@timing-function) {
 229 |   -webkit-animation-timing-function: @timing-function;
 230 |           animation-timing-function: @timing-function;
 231 | }
 232 | .animation-delay(@delay) {
 233 |   -webkit-animation-delay: @delay;
 234 |           animation-delay: @delay;
 235 | }
 236 | .animation-iteration-count(@iteration-count) {
 237 |   -webkit-animation-iteration-count: @iteration-count;
 238 |           animation-iteration-count: @iteration-count;
 239 | }
 240 | .animation-direction(@direction) {
 241 |   -webkit-animation-direction: @direction;
 242 |           animation-direction: @direction;
 243 | }
 244 | .animation-fill-mode(@fill-mode) {
 245 |   -webkit-animation-fill-mode: @fill-mode;
 246 |           animation-fill-mode: @fill-mode;
 247 | }
 248 | 
 249 | // Backface visibility
 250 | // Prevent browsers from flickering when using CSS 3D transforms.
 251 | // Default value is `visible`, but can be changed to `hidden`
 252 | 
 253 | .backface-visibility(@visibility){
 254 |   -webkit-backface-visibility: @visibility;
 255 |      -moz-backface-visibility: @visibility;
 256 |           backface-visibility: @visibility;
 257 | }
 258 | 
 259 | // Drop shadows
 260 | //
 261 | // Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's
 262 | // supported browsers that have box shadow capabilities now support it.
 263 | 
 264 | .box-shadow(@shadow) {
 265 |   -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1
 266 |           box-shadow: @shadow;
 267 | }
 268 | 
 269 | // Box sizing
 270 | .box-sizing(@boxmodel) {
 271 |   -webkit-box-sizing: @boxmodel;
 272 |      -moz-box-sizing: @boxmodel;
 273 |           box-sizing: @boxmodel;
 274 | }
 275 | 
 276 | // CSS3 Content Columns
 277 | .content-columns(@column-count; @column-gap: @grid-gutter-width) {
 278 |   -webkit-column-count: @column-count;
 279 |      -moz-column-count: @column-count;
 280 |           column-count: @column-count;
 281 |   -webkit-column-gap: @column-gap;
 282 |      -moz-column-gap: @column-gap;
 283 |           column-gap: @column-gap;
 284 | }
 285 | 
 286 | // Optional hyphenation
 287 | .hyphens(@mode: auto) {
 288 |   word-wrap: break-word;
 289 |   -webkit-hyphens: @mode;
 290 |      -moz-hyphens: @mode;
 291 |       -ms-hyphens: @mode; // IE10+
 292 |        -o-hyphens: @mode;
 293 |           hyphens: @mode;
 294 | }
 295 | 
 296 | // Placeholder text
 297 | .placeholder(@color: @input-color-placeholder) {
 298 |   // Firefox
 299 |   &::-moz-placeholder {
 300 |     color: @color;
 301 |     opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526
 302 |   }
 303 |   &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+
 304 |   &::-webkit-input-placeholder  { color: @color; } // Safari and Chrome
 305 | }
 306 | 
 307 | // Transformations
 308 | .scale(@ratio) {
 309 |   -webkit-transform: scale(@ratio);
 310 |       -ms-transform: scale(@ratio); // IE9 only
 311 |        -o-transform: scale(@ratio);
 312 |           transform: scale(@ratio);
 313 | }
 314 | .scale(@ratioX; @ratioY) {
 315 |   -webkit-transform: scale(@ratioX, @ratioY);
 316 |       -ms-transform: scale(@ratioX, @ratioY); // IE9 only
 317 |        -o-transform: scale(@ratioX, @ratioY);
 318 |           transform: scale(@ratioX, @ratioY);
 319 | }
 320 | .scaleX(@ratio) {
 321 |   -webkit-transform: scaleX(@ratio);
 322 |       -ms-transform: scaleX(@ratio); // IE9 only
 323 |        -o-transform: scaleX(@ratio);
 324 |           transform: scaleX(@ratio);
 325 | }
 326 | .scaleY(@ratio) {
 327 |   -webkit-transform: scaleY(@ratio);
 328 |       -ms-transform: scaleY(@ratio); // IE9 only
 329 |        -o-transform: scaleY(@ratio);
 330 |           transform: scaleY(@ratio);
 331 | }
 332 | .skew(@x; @y) {
 333 |   -webkit-transform: skewX(@x) skewY(@y);
 334 |       -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+
 335 |        -o-transform: skewX(@x) skewY(@y);
 336 |           transform: skewX(@x) skewY(@y);
 337 | }
 338 | .translate(@x; @y) {
 339 |   -webkit-transform: translate(@x, @y);
 340 |       -ms-transform: translate(@x, @y); // IE9 only
 341 |        -o-transform: translate(@x, @y);
 342 |           transform: translate(@x, @y);
 343 | }
 344 | .translate3d(@x; @y; @z) {
 345 |   -webkit-transform: translate3d(@x, @y, @z);
 346 |           transform: translate3d(@x, @y, @z);
 347 | }
 348 | .rotate(@degrees) {
 349 |   -webkit-transform: rotate(@degrees);
 350 |       -ms-transform: rotate(@degrees); // IE9 only
 351 |        -o-transform: rotate(@degrees);
 352 |           transform: rotate(@degrees);
 353 | }
 354 | .rotateX(@degrees) {
 355 |   -webkit-transform: rotateX(@degrees);
 356 |       -ms-transform: rotateX(@degrees); // IE9 only
 357 |        -o-transform: rotateX(@degrees);
 358 |           transform: rotateX(@degrees);
 359 | }
 360 | .rotateY(@degrees) {
 361 |   -webkit-transform: rotateY(@degrees);
 362 |       -ms-transform: rotateY(@degrees); // IE9 only
 363 |        -o-transform: rotateY(@degrees);
 364 |           transform: rotateY(@degrees);
 365 | }
 366 | .perspective(@perspective) {
 367 |   -webkit-perspective: @perspective;
 368 |      -moz-perspective: @perspective;
 369 |           perspective: @perspective;
 370 | }
 371 | .perspective-origin(@perspective) {
 372 |   -webkit-perspective-origin: @perspective;
 373 |      -moz-perspective-origin: @perspective;
 374 |           perspective-origin: @perspective;
 375 | }
 376 | .transform-origin(@origin) {
 377 |   -webkit-transform-origin: @origin;
 378 |      -moz-transform-origin: @origin;
 379 |       -ms-transform-origin: @origin; // IE9 only
 380 |           transform-origin: @origin;
 381 | }
 382 | 
 383 | 
 384 | // Transitions
 385 | 
 386 | .transition(@transition) {
 387 |   -webkit-transition: @transition;
 388 |        -o-transition: @transition;
 389 |           transition: @transition;
 390 | }
 391 | .transition-property(@transition-property) {
 392 |   -webkit-transition-property: @transition-property;
 393 |           transition-property: @transition-property;
 394 | }
 395 | .transition-delay(@transition-delay) {
 396 |   -webkit-transition-delay: @transition-delay;
 397 |           transition-delay: @transition-delay;
 398 | }
 399 | .transition-duration(@transition-duration) {
 400 |   -webkit-transition-duration: @transition-duration;
 401 |           transition-duration: @transition-duration;
 402 | }
 403 | .transition-timing-function(@timing-function) {
 404 |   -webkit-transition-timing-function: @timing-function;
 405 |           transition-timing-function: @timing-function;
 406 | }
 407 | .transition-transform(@transition) {
 408 |   -webkit-transition: -webkit-transform @transition;
 409 |      -moz-transition: -moz-transform @transition;
 410 |        -o-transition: -o-transform @transition;
 411 |           transition: transform @transition;
 412 | }
 413 | 
 414 | 
 415 | // User select
 416 | // For selecting text on the page
 417 | 
 418 | .user-select(@select) {
 419 |   -webkit-user-select: @select;
 420 |      -moz-user-select: @select;
 421 |       -ms-user-select: @select; // IE10+
 422 |           user-select: @select;
 423 | }
 424 | 
 425 | 
 426 | // Components
 427 | 
 428 | 
 429 | // @import "bootstrap/less/mixins/alerts.less"
 430 | // Alerts
 431 | 
 432 | .alert-variant(@background; @border; @text-color) {
 433 |   background-color: @background;
 434 |   border-color: @border;
 435 |   color: @text-color;
 436 | 
 437 |   hr {
 438 |     border-top-color: darken(@border, 5%);
 439 |   }
 440 |   .alert-link {
 441 |     color: darken(@text-color, 10%);
 442 |   }
 443 | }
 444 | 
 445 | 
 446 | 
 447 | // @import "bootstrap/less/mixins/buttons.less"
 448 | // Button variants
 449 | //
 450 | // Easily pump out default styles, as well as :hover, :focus, :active,
 451 | // and disabled options for all buttons
 452 | 
 453 | .button-variant(@color; @background; @border) {
 454 |   color: @color;
 455 |   background-color: @background;
 456 |   border-color: @border;
 457 | 
 458 |   &:hover,
 459 |   &:focus,
 460 |   &.focus,
 461 |   &:active,
 462 |   &.active,
 463 |   .open > .dropdown-toggle& {
 464 |     color: @color;
 465 |     background-color: darken(@background, 10%);
 466 |         border-color: darken(@border, 12%);
 467 |   }
 468 |   &:active,
 469 |   &.active,
 470 |   .open > .dropdown-toggle& {
 471 |     background-image: none;
 472 |   }
 473 |   &.disabled,
 474 |   &[disabled],
 475 |   fieldset[disabled] & {
 476 |     &,
 477 |     &:hover,
 478 |     &:focus,
 479 |     &.focus,
 480 |     &:active,
 481 |     &.active {
 482 |       background-color: @background;
 483 |           border-color: @border;
 484 |     }
 485 |   }
 486 | 
 487 |   .badge {
 488 |     color: @background;
 489 |     background-color: @color;
 490 |   }
 491 | }
 492 | 
 493 | // Button sizes
 494 | .button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {
 495 |   padding: @padding-vertical @padding-horizontal;
 496 |   font-size: @font-size;
 497 |   line-height: @line-height;
 498 |   border-radius: @border-radius;
 499 | }
 500 | 
 501 | 
 502 | 
 503 | // @import "bootstrap/less/mixins/panels.less"
 504 | // Panels
 505 | 
 506 | .panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {
 507 |   border-color: @border;
 508 | 
 509 |   & > .panel-heading {
 510 |     color: @heading-text-color;
 511 |     background-color: @heading-bg-color;
 512 |     border-color: @heading-border;
 513 | 
 514 |     + .panel-collapse > .panel-body {
 515 |       border-top-color: @border;
 516 |     }
 517 |     .badge {
 518 |       color: @heading-bg-color;
 519 |       background-color: @heading-text-color;
 520 |     }
 521 |   }
 522 |   & > .panel-footer {
 523 |     + .panel-collapse > .panel-body {
 524 |       border-bottom-color: @border;
 525 |     }
 526 |   }
 527 | }
 528 | 
 529 | 
 530 | 
 531 | // @import "bootstrap/less/mixins/pagination.less"
 532 | // Pagination
 533 | 
 534 | .pagination-size(@padding-vertical; @padding-horizontal; @font-size; @border-radius) {
 535 |   > li {
 536 |     > a,
 537 |     > span {
 538 |       padding: @padding-vertical @padding-horizontal;
 539 |       font-size: @font-size;
 540 |     }
 541 |     &:first-child {
 542 |       > a,
 543 |       > span {
 544 |         .border-left-radius(@border-radius);
 545 |       }
 546 |     }
 547 |     &:last-child {
 548 |       > a,
 549 |       > span {
 550 |         .border-right-radius(@border-radius);
 551 |       }
 552 |     }
 553 |   }
 554 | }
 555 | 
 556 | 
 557 | 
 558 | // @import "bootstrap/less/mixins/list-group.less"
 559 | // List Groups
 560 | 
 561 | .list-group-item-variant(@state; @background; @color) {
 562 |   .list-group-item-@{state} {
 563 |     color: @color;
 564 |     background-color: @background;
 565 | 
 566 |     a& {
 567 |       color: @color;
 568 | 
 569 |       .list-group-item-heading {
 570 |         color: inherit;
 571 |       }
 572 | 
 573 |       &:hover,
 574 |       &:focus {
 575 |         color: @color;
 576 |         background-color: darken(@background, 5%);
 577 |       }
 578 |       &.active,
 579 |       &.active:hover,
 580 |       &.active:focus {
 581 |         color: #fff;
 582 |         background-color: @color;
 583 |         border-color: @color;
 584 |       }
 585 |     }
 586 |   }
 587 | }
 588 | 
 589 | 
 590 | 
 591 | // @import "bootstrap/less/mixins/nav-divider.less"
 592 | // Horizontal dividers
 593 | //
 594 | // Dividers (basically an hr) within dropdowns and nav lists
 595 | 
 596 | .nav-divider(@color: #e5e5e5) {
 597 |   height: 1px;
 598 |   margin: ((@line-height-computed / 2) - 1) 0;
 599 |   overflow: hidden;
 600 |   background-color: @color;
 601 | }
 602 | 
 603 | 
 604 | 
 605 | // @import "bootstrap/less/mixins/forms.less"
 606 | // Form validation states
 607 | //
 608 | // Used in forms.less to generate the form validation CSS for warnings, errors,
 609 | // and successes.
 610 | 
 611 | .form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {
 612 |   // Color the label and help text
 613 |   .help-block,
 614 |   .control-label,
 615 |   .radio,
 616 |   .checkbox,
 617 |   .radio-inline,
 618 |   .checkbox-inline,
 619 |   &.radio label,
 620 |   &.checkbox label,
 621 |   &.radio-inline label,
 622 |   &.checkbox-inline label  {
 623 |     color: @text-color;
 624 |   }
 625 |   // Set the border and box shadow on specific inputs to match
 626 |   .form-control {
 627 |     border-color: @border-color;
 628 |     .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
 629 |     &:focus {
 630 |       border-color: darken(@border-color, 10%);
 631 |       @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);
 632 |       .box-shadow(@shadow);
 633 |     }
 634 |   }
 635 |   // Set validation states also for addons
 636 |   .input-group-addon {
 637 |     color: @text-color;
 638 |     border-color: @border-color;
 639 |     background-color: @background-color;
 640 |   }
 641 |   // Optional feedback icon
 642 |   .form-control-feedback {
 643 |     color: @text-color;
 644 |   }
 645 | }
 646 | 
 647 | 
 648 | // Form control focus state
 649 | //
 650 | // Generate a customized focus state and for any input with the specified color,
 651 | // which defaults to the `@input-border-focus` variable.
 652 | //
 653 | // We highly encourage you to not customize the default value, but instead use
 654 | // this to tweak colors on an as-needed basis. This aesthetic change is based on
 655 | // WebKit's default styles, but applicable to a wider range of browsers. Its
 656 | // usability and accessibility should be taken into account with any change.
 657 | //
 658 | // Example usage: change the default blue border and shadow to white for better
 659 | // contrast against a dark gray background.
 660 | .form-control-focus(@color: @input-border-focus) {
 661 |   @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);
 662 |   &:focus {
 663 |     border-color: @color;
 664 |     outline: 0;
 665 |     .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}");
 666 |   }
 667 | }
 668 | 
 669 | // Form control sizing
 670 | //
 671 | // Relative text size, padding, and border-radii changes for form controls. For
 672 | // horizontal sizing, wrap controls in the predefined grid classes. `` background color
179 | @input-bg:                       #fff;
180 | //** `` background color
181 | @input-bg-disabled:              @gray-lighter;
182 | 
183 | //** Text color for ``s
184 | @input-color:                    @text-color;
185 | //** `` border color
186 | @input-border:                   #ccc;
187 | 
188 | // TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
189 | //** Default `.form-control` border radius
190 | // This has no effect on ``s in CSS.
191 | @input-border-radius:            @border-radius-base;
192 | //** Large `.form-control` border radius
193 | @input-border-radius-large:      @border-radius-large;
194 | //** Small `.form-control` border radius
195 | @input-border-radius-small:      @border-radius-small;
196 | 
197 | //** Border color for inputs on focus
198 | @input-border-focus:             #66afe9;
199 | 
200 | //** Placeholder text color
201 | @input-color-placeholder:        @gray-light;
202 | 
203 | //** Default `.form-control` height
204 | @input-height-base:              (@line-height-computed + (@padding-base-vertical * 2) + 2);
205 | //** Large `.form-control` height
206 | @input-height-large:             (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
207 | //** Small `.form-control` height
208 | @input-height-small:             (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
209 | 
210 | //** `.form-group` margin
211 | @form-group-margin-bottom:       15px;
212 | 
213 | @legend-color:                   @text-color;
214 | @legend-border-color:            #e5e5e5;
215 | 
216 | //** Background color for textual input addons
217 | @input-group-addon-bg:           @gray-lighter;
218 | //** Border color for textual input addons
219 | @input-group-addon-border-color: @input-border;
220 | 
221 | //** Disabled cursor for form controls and buttons.
222 | @cursor-disabled:                not-allowed;
223 | 
224 | 
225 | //== Dropdowns
226 | //
227 | //## Dropdown menu container and contents.
228 | 
229 | //** Background for the dropdown menu.
230 | @dropdown-bg:                    #fff;
231 | //** Dropdown menu `border-color`.
232 | @dropdown-border:                rgba(0,0,0,.15);
233 | //** Dropdown menu `border-color` **for IE8**.
234 | @dropdown-fallback-border:       #ccc;
235 | //** Divider color for between dropdown items.
236 | @dropdown-divider-bg:            #e5e5e5;
237 | 
238 | //** Dropdown link text color.
239 | @dropdown-link-color:            @gray-dark;
240 | //** Hover color for dropdown links.
241 | @dropdown-link-hover-color:      #fff;
242 | //** Hover background for dropdown links.
243 | @dropdown-link-hover-bg:         @component-active-bg;
244 | 
245 | //** Active dropdown menu item text color.
246 | @dropdown-link-active-color:     #fff;
247 | //** Active dropdown menu item background color.
248 | @dropdown-link-active-bg:        @component-active-bg;
249 | 
250 | //** Disabled dropdown menu item background color.
251 | @dropdown-link-disabled-color:   @gray-light;
252 | 
253 | //** Text color for headers within dropdown menus.
254 | @dropdown-header-color:          @gray-light;
255 | 
256 | //** Deprecated `@dropdown-caret-color` as of v3.1.0
257 | @dropdown-caret-color:           #000;
258 | 
259 | 
260 | //-- Z-index master list
261 | //
262 | // Warning: Avoid customizing these values. They're used for a bird's eye view
263 | // of components dependent on the z-axis and are designed to all work together.
264 | //
265 | // Note: These variables are not generated into the Customizer.
266 | 
267 | @zindex-navbar:            1000;
268 | @zindex-dropdown:          1000;
269 | @zindex-popover:           1060;
270 | @zindex-tooltip:           1070;
271 | @zindex-navbar-fixed:      1030;
272 | @zindex-modal-background:  1040;
273 | @zindex-modal:             1050;
274 | 
275 | 
276 | //== Media queries breakpoints
277 | //
278 | //## Define the breakpoints at which your layout will change, adapting to different screen sizes.
279 | 
280 | // Extra small screen / phone
281 | //** Deprecated `@screen-xs` as of v3.0.1
282 | @screen-xs:                  480px;
283 | //** Deprecated `@screen-xs-min` as of v3.2.0
284 | @screen-xs-min:              @screen-xs;
285 | //** Deprecated `@screen-phone` as of v3.0.1
286 | @screen-phone:               @screen-xs-min;
287 | 
288 | // Small screen / tablet
289 | //** Deprecated `@screen-sm` as of v3.0.1
290 | @screen-sm:                  768px;
291 | @screen-sm-min:              @screen-sm;
292 | //** Deprecated `@screen-tablet` as of v3.0.1
293 | @screen-tablet:              @screen-sm-min;
294 | 
295 | // Medium screen / desktop
296 | //** Deprecated `@screen-md` as of v3.0.1
297 | @screen-md:                  992px;
298 | @screen-md-min:              @screen-md;
299 | //** Deprecated `@screen-desktop` as of v3.0.1
300 | @screen-desktop:             @screen-md-min;
301 | 
302 | // Large screen / wide desktop
303 | //** Deprecated `@screen-lg` as of v3.0.1
304 | @screen-lg:                  1200px;
305 | @screen-lg-min:              @screen-lg;
306 | //** Deprecated `@screen-lg-desktop` as of v3.0.1
307 | @screen-lg-desktop:          @screen-lg-min;
308 | 
309 | // So media queries don't overlap when required, provide a maximum
310 | @screen-xs-max:              (@screen-sm-min - 1);
311 | @screen-sm-max:              (@screen-md-min - 1);
312 | @screen-md-max:              (@screen-lg-min - 1);
313 | 
314 | 
315 | //== Grid system
316 | //
317 | //## Define your custom responsive grid.
318 | 
319 | //** Number of columns in the grid.
320 | @grid-columns:              12;
321 | //** Padding between columns. Gets divided in half for the left and right.
322 | @grid-gutter-width:         30px;
323 | // Navbar collapse
324 | //** Point at which the navbar becomes uncollapsed.
325 | @grid-float-breakpoint:     @screen-sm-min;
326 | //** Point at which the navbar begins collapsing.
327 | @grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
328 | 
329 | 
330 | //== Container sizes
331 | //
332 | //## Define the maximum width of `.container` for different screen sizes.
333 | 
334 | // Small screen / tablet
335 | @container-tablet:             (720px + @grid-gutter-width);
336 | //** For `@screen-sm-min` and up.
337 | @container-sm:                 @container-tablet;
338 | 
339 | // Medium screen / desktop
340 | @container-desktop:            (940px + @grid-gutter-width);
341 | //** For `@screen-md-min` and up.
342 | @container-md:                 @container-desktop;
343 | 
344 | // Large screen / wide desktop
345 | @container-large-desktop:      (1140px + @grid-gutter-width);
346 | //** For `@screen-lg-min` and up.
347 | @container-lg:                 @container-large-desktop;
348 | 
349 | 
350 | //== Navbar
351 | //
352 | //##
353 | 
354 | // Basics of a navbar
355 | @navbar-height:                    60px;
356 | @navbar-margin-bottom:             @line-height-computed;
357 | @navbar-border-radius:             @border-radius-base;
358 | @navbar-padding-horizontal:        floor((@grid-gutter-width / 2));
359 | @navbar-padding-vertical:          ((@navbar-height - @line-height-computed) / 2);
360 | @navbar-collapse-max-height:       340px;
361 | 
362 | @navbar-default-color:             #000;
363 | @navbar-default-bg:                #fff;
364 | @navbar-default-border:            darken(@navbar-default-bg, 6.5%);
365 | 
366 | // Navbar links
367 | @navbar-default-link-color:                #000;
368 | @navbar-default-link-hover-color:          #000;
369 | @navbar-default-link-hover-bg:             darken(@navbar-default-bg, 6.5%);
370 | @navbar-default-link-active-color:         #000;
371 | @navbar-default-link-active-bg:            darken(@navbar-default-bg, 6.5%);
372 | @navbar-default-link-disabled-color:       #ccc;
373 | @navbar-default-link-disabled-bg:          transparent;
374 | 
375 | // Navbar brand label
376 | @navbar-default-brand-color:               @navbar-default-link-color;
377 | @navbar-default-brand-hover-color:         @navbar-default-link-hover-color;
378 | @navbar-default-brand-hover-bg:            darken(@navbar-default-bg, 6.5%);
379 | 
380 | // Navbar toggle
381 | @navbar-default-toggle-hover-bg:           #ddd;
382 | @navbar-default-toggle-icon-bar-bg:        #ccc;
383 | @navbar-default-toggle-border-color:       #ddd;
384 | 
385 | 
386 | // Inverted navbar
387 | // Reset inverted navbar basics
388 | @navbar-inverse-color:                      #fff;
389 | @navbar-inverse-bg:                         @brand-primary;
390 | @navbar-inverse-border:                     darken(@navbar-inverse-bg, 10%);
391 | 
392 | // Inverted navbar links
393 | @navbar-inverse-link-color:                 #fff;
394 | @navbar-inverse-link-hover-color:           #fff;
395 | @navbar-inverse-link-hover-bg:              darken(@navbar-inverse-bg, 6.5%);
396 | @navbar-inverse-link-active-color:          #fff;
397 | @navbar-inverse-link-active-bg:             darken(@navbar-inverse-bg, 6.5%);
398 | @navbar-inverse-link-disabled-color:        #444;
399 | @navbar-inverse-link-disabled-bg:           transparent;
400 | 
401 | // Inverted navbar brand label
402 | @navbar-inverse-brand-color:                @navbar-inverse-link-color;
403 | @navbar-inverse-brand-hover-color:          #fff;
404 | @navbar-inverse-brand-hover-bg:             darken(@navbar-inverse-bg, 6.5%);
405 | 
406 | // Inverted navbar toggle
407 | @navbar-inverse-toggle-hover-bg:            darken(@navbar-inverse-bg, 10%);
408 | @navbar-inverse-toggle-icon-bar-bg:         #fff;
409 | @navbar-inverse-toggle-border-color:        darken(@navbar-inverse-bg, 10%);
410 | 
411 | 
412 | //== Navs
413 | //
414 | //##
415 | 
416 | //=== Shared nav styles
417 | @nav-link-padding:                          10px 15px;
418 | @nav-link-hover-bg:                         @gray-lighter;
419 | 
420 | @nav-disabled-link-color:                   @gray-light;
421 | @nav-disabled-link-hover-color:             @gray-light;
422 | 
423 | //== Tabs
424 | @nav-tabs-border-color:                     #ddd;
425 | 
426 | @nav-tabs-link-hover-border-color:          @gray-lighter;
427 | 
428 | @nav-tabs-active-link-hover-bg:             @body-bg;
429 | @nav-tabs-active-link-hover-color:          @gray;
430 | @nav-tabs-active-link-hover-border-color:   #ddd;
431 | 
432 | @nav-tabs-justified-link-border-color:            #ddd;
433 | @nav-tabs-justified-active-link-border-color:     @body-bg;
434 | 
435 | //== Pills
436 | @nav-pills-border-radius:                   @border-radius-base;
437 | @nav-pills-active-link-hover-bg:            @component-active-bg;
438 | @nav-pills-active-link-hover-color:         @component-active-color;
439 | 
440 | 
441 | //== Pagination
442 | //
443 | //##
444 | 
445 | @pagination-color:                     @link-color;
446 | @pagination-bg:                        #fff;
447 | @pagination-border:                    #ddd;
448 | 
449 | @pagination-hover-color:               @link-hover-color;
450 | @pagination-hover-bg:                  @gray-lighter;
451 | @pagination-hover-border:              #ddd;
452 | 
453 | @pagination-active-color:              @gray-light;
454 | @pagination-active-bg:                 #f5f5f5;
455 | @pagination-active-border:             #ddd;
456 | 
457 | @pagination-disabled-color:            @gray-light;
458 | @pagination-disabled-bg:               #fff;
459 | @pagination-disabled-border:           #ddd;
460 | 
461 | 
462 | //== Pager
463 | //
464 | //##
465 | 
466 | @pager-bg:                             @pagination-bg;
467 | @pager-border:                         @pagination-border;
468 | @pager-border-radius:                  15px;
469 | 
470 | @pager-hover-bg:                       @pagination-hover-bg;
471 | 
472 | @pager-active-bg:                      @pagination-active-bg;
473 | @pager-active-color:                   @pagination-active-color;
474 | 
475 | @pager-disabled-color:                 @gray-light;
476 | 
477 | 
478 | //== Jumbotron
479 | //
480 | //##
481 | 
482 | @jumbotron-padding:              30px;
483 | @jumbotron-color:                inherit;
484 | @jumbotron-bg:                   @gray-lighter;
485 | @jumbotron-heading-color:        inherit;
486 | @jumbotron-font-size:            ceil((@font-size-base * 1.5));
487 | 
488 | 
489 | //== Form states and alerts
490 | //
491 | //## Define colors for form feedback states and, by default, alerts.
492 | 
493 | @state-success-text:             #468847;
494 | @state-success-bg:               #dff0d8;
495 | @state-success-border:           darken(spin(@state-success-bg, -10), 5%);
496 | 
497 | @state-info-text:                #3a87ad;
498 | @state-info-bg:                  #d9edf7;
499 | @state-info-border:              darken(spin(@state-info-bg, -10), 7%);
500 | 
501 | @state-warning-text:             #c09853;
502 | @state-warning-bg:               #fcf8e3;
503 | @state-warning-border:           darken(spin(@state-warning-bg, -10), 3%);
504 | 
505 | @state-danger-text:              #b94a48;
506 | @state-danger-bg:                #f2dede;
507 | @state-danger-border:            darken(spin(@state-danger-bg, -10), 3%);
508 | 
509 | 
510 | //== Tooltips
511 | //
512 | //##
513 | 
514 | //** Tooltip max width
515 | @tooltip-max-width:           200px;
516 | //** Tooltip text color
517 | @tooltip-color:               #fff;
518 | //** Tooltip background color
519 | @tooltip-bg:                  #000;
520 | @tooltip-opacity:             .9;
521 | 
522 | //** Tooltip arrow width
523 | @tooltip-arrow-width:         5px;
524 | //** Tooltip arrow color
525 | @tooltip-arrow-color:         @tooltip-bg;
526 | 
527 | 
528 | //== Popovers
529 | //
530 | //##
531 | 
532 | //** Popover body background color
533 | @popover-bg:                          #fff;
534 | //** Popover maximum width
535 | @popover-max-width:                   276px;
536 | //** Popover border color
537 | @popover-border-color:                rgba(0,0,0,.2);
538 | //** Popover fallback border color
539 | @popover-fallback-border-color:       #ccc;
540 | 
541 | //** Popover title background color
542 | @popover-title-bg:                    darken(@popover-bg, 3%);
543 | 
544 | //** Popover arrow width
545 | @popover-arrow-width:                 10px;
546 | //** Popover arrow color
547 | @popover-arrow-color:                 @popover-bg;
548 | 
549 | //** Popover outer arrow width
550 | @popover-arrow-outer-width:           (@popover-arrow-width + 1);
551 | //** Popover outer arrow color
552 | @popover-arrow-outer-color:           fadein(@popover-border-color, 5%);
553 | //** Popover outer arrow fallback color
554 | @popover-arrow-outer-fallback-color:  darken(@popover-fallback-border-color, 20%);
555 | 
556 | 
557 | //== Labels
558 | //
559 | //##
560 | 
561 | //** Default label background color
562 | @label-default-bg:            @btn-default-bg;
563 | //** Primary label background color
564 | @label-primary-bg:            @brand-primary;
565 | //** Success label background color
566 | @label-success-bg:            @brand-success;
567 | //** Info label background color
568 | @label-info-bg:               @brand-info;
569 | //** Warning label background color
570 | @label-warning-bg:            @brand-warning;
571 | //** Danger label background color
572 | @label-danger-bg:             @brand-danger;
573 | 
574 | //** Default label text color
575 | @label-color:                 #fff;
576 | //** Default text color of a linked label
577 | @label-link-hover-color:      #fff;
578 | 
579 | 
580 | //== Modals
581 | //
582 | //##
583 | 
584 | //** Padding applied to the modal body
585 | @modal-inner-padding:         20px;
586 | 
587 | //** Padding applied to the modal title
588 | @modal-title-padding:         15px;
589 | //** Modal title line-height
590 | @modal-title-line-height:     @line-height-base;
591 | 
592 | //** Background color of modal content area
593 | @modal-content-bg:                             #fff;
594 | //** Modal content border color
595 | @modal-content-border-color:                   rgba(0,0,0,.2);
596 | //** Modal content border color **for IE8**
597 | @modal-content-fallback-border-color:          #999;
598 | 
599 | //** Modal backdrop background color
600 | @modal-backdrop-bg:           #000;
601 | //** Modal backdrop opacity
602 | @modal-backdrop-opacity:      .5;
603 | //** Modal header border color
604 | @modal-header-border-color:   #e5e5e5;
605 | //** Modal footer border color
606 | @modal-footer-border-color:   @modal-header-border-color;
607 | 
608 | @modal-lg:                    900px;
609 | @modal-md:                    600px;
610 | @modal-sm:                    300px;
611 | 
612 | 
613 | //== Alerts
614 | //
615 | //## Define alert colors, border radius, and padding.
616 | 
617 | @alert-padding:               15px;
618 | @alert-border-radius:         @border-radius-base;
619 | @alert-link-font-weight:      bold;
620 | 
621 | @alert-success-bg:            @state-success-bg;
622 | @alert-success-text:          @state-success-text;
623 | @alert-success-border:        @state-success-border;
624 | 
625 | @alert-info-bg:               @state-info-bg;
626 | @alert-info-text:             @state-info-text;
627 | @alert-info-border:           @state-info-border;
628 | 
629 | @alert-warning-bg:            @state-warning-bg;
630 | @alert-warning-text:          @state-warning-text;
631 | @alert-warning-border:        @state-warning-border;
632 | 
633 | @alert-danger-bg:             @state-danger-bg;
634 | @alert-danger-text:           @state-danger-text;
635 | @alert-danger-border:         @state-danger-border;
636 | 
637 | 
638 | //== Progress bars
639 | //
640 | //##
641 | 
642 | //** Background color of the whole progress component
643 | @progress-bg:                 #f5f5f5;
644 | //** Progress bar text color
645 | @progress-bar-color:          #fff;
646 | //** Variable for setting rounded corners on progress bar.
647 | @progress-border-radius:      @border-radius-base;
648 | 
649 | //** Default progress bar color
650 | @progress-bar-bg:             @brand-primary;
651 | //** Success progress bar color
652 | @progress-bar-success-bg:     @brand-success;
653 | //** Warning progress bar color
654 | @progress-bar-warning-bg:     @brand-warning;
655 | //** Danger progress bar color
656 | @progress-bar-danger-bg:      @brand-danger;
657 | //** Info progress bar color
658 | @progress-bar-info-bg:        @brand-info;
659 | 
660 | 
661 | //== List group
662 | //
663 | //##
664 | 
665 | //** Background color on `.list-group-item`
666 | @list-group-bg:                 #fff;
667 | //** `.list-group-item` border color
668 | @list-group-border:             #ddd;
669 | //** List group border radius
670 | @list-group-border-radius:      @border-radius-base;
671 | 
672 | //** Background color of single list items on hover
673 | @list-group-hover-bg:           #f5f5f5;
674 | //** Text color of active list items
675 | @list-group-active-color:       @component-active-color;
676 | //** Background color of active list items
677 | @list-group-active-bg:          @component-active-bg;
678 | //** Border color of active list elements
679 | @list-group-active-border:      @list-group-active-bg;
680 | //** Text color for content within active list items
681 | @list-group-active-text-color:  lighten(@list-group-active-bg, 40%);
682 | 
683 | //** Text color of disabled list items
684 | @list-group-disabled-color:      @gray-light;
685 | //** Background color of disabled list items
686 | @list-group-disabled-bg:         @gray-lighter;
687 | //** Text color for content within disabled list items
688 | @list-group-disabled-text-color: @list-group-disabled-color;
689 | 
690 | @list-group-link-color:         #555;
691 | @list-group-link-hover-color:   @list-group-link-color;
692 | @list-group-link-heading-color: #333;
693 | 
694 | 
695 | //== Panels
696 | //
697 | //##
698 | 
699 | @panel-bg:                    #fff;
700 | @panel-body-padding:          15px;
701 | @panel-heading-padding:       10px 15px;
702 | @panel-footer-padding:        @panel-heading-padding;
703 | @panel-border-radius:         @border-radius-base;
704 | 
705 | //** Border color for elements within panels
706 | @panel-inner-border:          #ddd;
707 | @panel-footer-bg:             #f5f5f5;
708 | 
709 | @panel-default-text:          @text-color;
710 | @panel-default-border:        #ddd;
711 | @panel-default-heading-bg:    #f5f5f5;
712 | 
713 | @panel-primary-text:          #fff;
714 | @panel-primary-border:        @brand-primary;
715 | @panel-primary-heading-bg:    @brand-primary;
716 | 
717 | @panel-success-text:          @state-success-text;
718 | @panel-success-border:        @brand-success;
719 | @panel-success-heading-bg:    @brand-success;
720 | 
721 | @panel-info-text:             @state-info-text;
722 | @panel-info-border:           @brand-info;
723 | @panel-info-heading-bg:       @brand-info;
724 | 
725 | @panel-warning-text:          @state-warning-text;
726 | @panel-warning-border:        @brand-warning;
727 | @panel-warning-heading-bg:    @brand-warning;
728 | 
729 | @panel-danger-text:           @state-danger-text;
730 | @panel-danger-border:         @brand-danger;
731 | @panel-danger-heading-bg:     @brand-danger;
732 | 
733 | 
734 | //== Thumbnails
735 | //
736 | //##
737 | 
738 | //** Padding around the thumbnail image
739 | @thumbnail-padding:           4px;
740 | //** Thumbnail background color
741 | @thumbnail-bg:                @body-bg;
742 | //** Thumbnail border color
743 | @thumbnail-border:            #ddd;
744 | //** Thumbnail border radius
745 | @thumbnail-border-radius:     @border-radius-base;
746 | 
747 | //** Custom text color for thumbnail captions
748 | @thumbnail-caption-color:     @text-color;
749 | //** Padding around the thumbnail caption
750 | @thumbnail-caption-padding:   9px;
751 | 
752 | 
753 | //== Wells
754 | //
755 | //##
756 | 
757 | @well-bg:                     #f5f5f5;
758 | @well-border:                 darken(@well-bg, 7%);
759 | 
760 | 
761 | //== Badges
762 | //
763 | //##
764 | 
765 | @badge-color:                 #fff;
766 | //** Linked badge text color on hover
767 | @badge-link-hover-color:      #fff;
768 | @badge-bg:                    @link-color;
769 | 
770 | //** Badge text color in active nav link
771 | @badge-active-color:          @link-color;
772 | //** Badge background color in active nav link
773 | @badge-active-bg:             #fff;
774 | 
775 | @badge-font-weight:           bold;
776 | @badge-line-height:           1;
777 | @badge-border-radius:         10px;
778 | 
779 | 
780 | //== Breadcrumbs
781 | //
782 | //##
783 | 
784 | @breadcrumb-padding-vertical:   8px;
785 | @breadcrumb-padding-horizontal: 15px;
786 | //** Breadcrumb background color
787 | @breadcrumb-bg:                 #f5f5f5;
788 | //** Breadcrumb text color
789 | @breadcrumb-color:              #ccc;
790 | //** Text color of current page in the breadcrumb
791 | @breadcrumb-active-color:       @gray-light;
792 | //** Textual separator for between breadcrumb elements
793 | @breadcrumb-separator:          "/";
794 | 
795 | 
796 | //== Carousel
797 | //
798 | //##
799 | 
800 | @carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6);
801 | 
802 | @carousel-control-color:                      #fff;
803 | @carousel-control-width:                      15%;
804 | @carousel-control-opacity:                    .5;
805 | @carousel-control-font-size:                  20px;
806 | 
807 | @carousel-indicator-active-bg:                #fff;
808 | @carousel-indicator-border-color:             #fff;
809 | 
810 | @carousel-caption-color:                      #fff;
811 | 
812 | 
813 | //== Close
814 | //
815 | //##
816 | 
817 | @close-font-weight:           bold;
818 | @close-color:                 #000;
819 | @close-text-shadow:           0 1px 0 #fff;
820 | 
821 | 
822 | //== Code
823 | //
824 | //##
825 | 
826 | @code-color:                  #c7254e;
827 | @code-bg:                     #f9f2f4;
828 | 
829 | @kbd-color:                   #fff;
830 | @kbd-bg:                      #333;
831 | 
832 | @pre-bg:                      #f5f5f5;
833 | @pre-color:                   @gray-dark;
834 | @pre-border-color:            #ccc;
835 | @pre-scrollable-max-height:   340px;
836 | 
837 | 
838 | //== Type
839 | //
840 | //##
841 | 
842 | //** Horizontal offset for forms and lists.
843 | @component-offset-horizontal: 180px;
844 | //** Text muted color
845 | @text-muted:                  @gray-light;
846 | //** Abbreviations and acronyms border color
847 | @abbr-border-color:           @gray-light;
848 | //** Headings small color
849 | @headings-small-color:        @gray-light;
850 | //** Blockquote small color
851 | @blockquote-small-color:      @gray-light;
852 | //** Blockquote font size
853 | @blockquote-font-size:        (@font-size-base * 1.25);
854 | //** Blockquote border color
855 | @blockquote-border-color:     @gray-lighter;
856 | //** Page header border color
857 | @page-header-border-color:    @gray-lighter;
858 | //** Width of horizontal description list titles
859 | @dl-horizontal-offset:        @component-offset-horizontal;
860 | //** Horizontal line color.
861 | @hr-border:                   @gray-lighter;
862 | 


--------------------------------------------------------------------------------
/client/subscriptions.js:
--------------------------------------------------------------------------------
1 | Meteor.subscribe("userData");
2 | Meteor.subscribe("jobCount");
3 | Meteor.subscribe("developerCount");
4 | 


--------------------------------------------------------------------------------
/client/views/home.html:
--------------------------------------------------------------------------------
 1 | 
46 | 


--------------------------------------------------------------------------------
/client/views/home.less:
--------------------------------------------------------------------------------
1 | .jumbotron {
2 |     background: url('/images/network.jpg') no-repeat center center;
3 | }
4 | 


--------------------------------------------------------------------------------
/client/views/includes/footer.html:
--------------------------------------------------------------------------------
 1 | 
27 | 


--------------------------------------------------------------------------------
/client/views/includes/header.html:
--------------------------------------------------------------------------------
 1 | 
73 | 


--------------------------------------------------------------------------------
/client/views/includes/header.js:
--------------------------------------------------------------------------------
 1 | Template.header.helpers({
 2 |   profile: function() {
 3 |     return Profiles.findOne({
 4 |       userId: Meteor.userId()
 5 |     });
 6 |   }
 7 | });
 8 | 
 9 | Template.header.events({
10 |   'click #signOut': function(event, template) {
11 |     Meteor.logout();
12 |     Router.go("/");
13 |   },
14 |   'click .navbar-nav a': function(event, template) {
15 |     var targetButton = document.getElementsByClassName('navbar-toggle')[0];
16 |     var _this = $(event.currentTarget); 
17 | 
18 |     if (window.innerWidth < 768) {
19 |       if( !_this.hasClass('box-user-option') ){
20 |          targetButton.click()
21 |       }
22 |     }
23 |   },
24 |   'click #userProfile': function(event, template) {
25 |     event.preventDefault();
26 |     Modal.show('userProfile');
27 |   }
28 | });
29 | 


--------------------------------------------------------------------------------
/client/views/includes/header.less:
--------------------------------------------------------------------------------
1 | #header{
2 | 	#user-drop-down{
3 | 		.avatar{
4 | 			display: inline-block;
5 | 			margin-right: 10px;
6 | 		}
7 | 	}
8 | }
9 | 


--------------------------------------------------------------------------------
/client/views/includes/layout.html:
--------------------------------------------------------------------------------
 1 | 
 8 | 
 9 | 
16 | 


--------------------------------------------------------------------------------
/client/views/includes/loading.html:
--------------------------------------------------------------------------------
 1 | 
11 | 


--------------------------------------------------------------------------------
/client/views/includes/notFound.html:
--------------------------------------------------------------------------------
 1 | 
14 | 


--------------------------------------------------------------------------------
/client/views/jobs/job.html:
--------------------------------------------------------------------------------
 1 | 
78 | 


--------------------------------------------------------------------------------
/client/views/jobs/job.js:
--------------------------------------------------------------------------------
 1 | Template.job.helpers({
 2 |   beforeRemove: function() {
 3 |     return function(collection, id) {
 4 |       var doc = collection.findOne(id);
 5 |       if (confirm('Really delete "' + doc.title + '"?')) {
 6 |         this.remove();
 7 |         ga("send", "event", "job", "remove", doc.title);
 8 |         Router.go('myJobs');
 9 |       }
10 |     };
11 |   }
12 | });
13 | 


--------------------------------------------------------------------------------
/client/views/jobs/jobForms.html:
--------------------------------------------------------------------------------
 1 | 
23 | 
24 | 
44 | 
45 | 
55 | 


--------------------------------------------------------------------------------
/client/views/jobs/jobForms.js:
--------------------------------------------------------------------------------
 1 | AutoForm.addHooks(['jobNew', 'jobEdit'], {
 2 | 	after: {
 3 | 		insert: function(error, result) {
 4 | 			if (error) {
 5 | 				console.log("Insert Error:", error);
 6 | 			} else {
 7 | 				ga("send", "event", "job", "insert", result.title);
 8 | 				Router.go('job', {_id:result});
 9 | 			}
10 | 		},
11 | 		update: function(error, result) {
12 | 			if (error) {
13 | 				console.log("Update Error:", error);
14 | 			} else {
15 | 				ga("send", "event", "job", "update", result.title);
16 | 				Router.go('job', {_id: Router.current().params._id});
17 | 			}
18 | 		}
19 | 	}
20 | });
21 | 
22 | Template.jobEdit.events({
23 | 	'click #cancel':function(event, template){
24 | 		event.preventDefault();
25 | 		Router.go("job",{_id:this.job._id});
26 | 	}
27 | });
28 | 
29 | Template.jobLabels.helpers({
30 |     isBounty: function () {
31 |         return this.jobtype === 'Bounty';
32 |     }
33 | });
34 | 


--------------------------------------------------------------------------------
/client/views/jobs/jobIncludes.html:
--------------------------------------------------------------------------------
 1 | 
 7 | 
 8 | 
18 | 
19 | 
32 | 
33 | 
46 | 


--------------------------------------------------------------------------------
/client/views/jobs/jobIncludes.js:
--------------------------------------------------------------------------------
 1 | Template.jobExpiredAlert.helpers({
 2 |   expired: function() {
 3 |     if (this.userId === Meteor.userId()) {
 4 |       if ((this.createdAt < daysUntilExpiration()) && (this.updatedAt < daysUntilExpiration())) {
 5 |         return true;
 6 |       } else if ((this.createdAt < daysUntilExpiration()) && (this.updatedAt === undefined)) {
 7 |         return true;
 8 |       } else {
 9 |         return false;
10 |       }
11 |     } else {
12 |       return false;
13 |     }
14 |   }
15 | });
16 | 
17 | Template.jobStatusToggle.helpers({
18 |   "statuses": function() {
19 |     return STATUSES;
20 |   }
21 | });
22 | 
23 | Template.jobStatusToggle.events({
24 |   "click .set-status": function(event, template) {
25 |     event.preventDefault();
26 |     Jobs.update({
27 |       _id: Router.current().params._id
28 |     }, {
29 |       $set: {
30 |         status: String(this)
31 |       }
32 |     });
33 |   }
34 | });
35 | 


--------------------------------------------------------------------------------
/client/views/jobs/jobSmall.html:
--------------------------------------------------------------------------------
 1 | 
22 | 


--------------------------------------------------------------------------------
/client/views/jobs/jobs.html:
--------------------------------------------------------------------------------
 1 | 
12 | 


--------------------------------------------------------------------------------
/client/views/jobs/jobsRecent.html:
--------------------------------------------------------------------------------
 1 | 
28 | 


--------------------------------------------------------------------------------
/client/views/jobs/jobsRecent.js:
--------------------------------------------------------------------------------
1 | Template.jobsRecent.helpers({
2 | 	'timeFromLastJob':function(){
3 | 		var mostRecentJob = _.first(Jobs.find({},{sort:{createdAt:-1},limit:1}).fetch());
4 | 		if(mostRecentJob)
5 | 			return moment(mostRecentJob.createdAt).fromNow();
6 | 	}
7 | });
8 | 


--------------------------------------------------------------------------------
/client/views/jobs/jobsRecent.less:
--------------------------------------------------------------------------------
1 | #jobsRecent{
2 | 	margin-top: 20px;
3 | 	h2{
4 | 		margin-top: -10px;
5 | 		margin-bottom: 20px;
6 | 	}
7 | }
8 | 


--------------------------------------------------------------------------------
/client/views/jobs/myJobs.html:
--------------------------------------------------------------------------------
 1 | 
13 | 


--------------------------------------------------------------------------------
/client/views/main.html:
--------------------------------------------------------------------------------
1 | 
2 |   Ethereum Jobs - Job board and developer listing just for Ethereum
3 |   
4 | 	
5 | 	 
6 | 	
7 | 
8 | 


--------------------------------------------------------------------------------
/client/views/main.less:
--------------------------------------------------------------------------------
  1 | @import "../lib/journal.variables.import.less";
  2 | 
  3 | h1,h2,h3,h4,h5{
  4 |   &.top-flush{
  5 |     margin-top: 0px;
  6 |   }
  7 | }
  8 | 
  9 | #content-wrapper{
 10 |     min-height: 800px;
 11 | 
 12 |     a:not(.btn):visited {
 13 |         color: @text-color;
 14 |     }
 15 | }
 16 | 
 17 | #iron-router-progress {
 18 |     background-color : @brand-primary;
 19 |     box-shadow       : 0 0 5px @brand-primary;
 20 | }
 21 | 
 22 | .nav > li > p {
 23 |     margin: 0;
 24 |     padding: 10px 15px;
 25 | }
 26 | 
 27 | #content-wrapper > div.jumbotron a.col-sm-4 {
 28 |     margin-top: 10px;
 29 | }
 30 | 
 31 | @media screen and (min-width:768px) {
 32 |     .nav > li > p {
 33 |         padding: 15px;
 34 |     }
 35 | 
 36 |     #content-wrapper > div.jumbotron a.col-sm-4 {
 37 |         margin-top: 0;
 38 |     }
 39 | }
 40 | 
 41 | #callToAction{
 42 |     margin-top: 20px;
 43 |     a{
 44 |        font-size: 125%;
 45 |     }
 46 | } 
 47 | 
 48 | #connection-lost-banner{
 49 |     text-align: center;
 50 | }
 51 | 
 52 | nav.navbar, .btn, li a, .input-group-addon, .jumbotron, .panel, .label{
 53 |   border-radius: 0px !important; 
 54 | }
 55 | 
 56 | .form-control{
 57 |   -webkit-border-radius: 0;
 58 |      -moz-border-radius: 0;
 59 |           border-radius: 0;
 60 |    &:focus {
 61 |     border: 1px solid #ccc;
 62 |     box-shadow: none;
 63 |     -webkit-box-shadow: none;
 64 |     -moz-box-shadow: none;
 65 |     -moz-transition: none;
 66 |     -webkit-transition: none;
 67 |   }
 68 | }
 69 | 
 70 | .btn {
 71 |   outline: none !important;
 72 | }
 73 | 
 74 | .label{
 75 |     padding-top:0.5rem;
 76 | }
 77 | 
 78 | footer, .jumbotron {
 79 |     background-color: #27272b;
 80 |     color:white;
 81 | }
 82 | 
 83 | #content-wrapper .jumbotron{
 84 |     margin-top:-20px;
 85 |     #buttons{
 86 |          @media only screen and (min-width : @screen-md-max) { padding-top:50px; }
 87 |     }
 88 |     a:visited {
 89 |         color: @brand-primary;
 90 |     }
 91 |     a.btn, a.btn:visited {
 92 |         color: #fff;
 93 |     }
 94 | }
 95 | 
 96 | nav.navbar {
 97 |     .open .dropdown-toggle{
 98 |         background:none;
 99 |         color:@brand-primary;
100 |     }
101 |     .navbar-brand{
102 |         margin-right:15px;
103 |         &:hover{
104 |             background:none;
105 |         }
106 |     }
107 |     .nav.navbar-nav li{
108 |         
109 |         p.navbar-text{
110 |            font-size: 15px;
111 |             font-weight: normal;
112 |             padding-top:19.5px;
113 |             line-height: 21px;
114 |         }
115 |         &.active a{
116 |              background:none;
117 |              color:@brand-primary;
118 |         }
119 | 
120 |             a {
121 |             font-size: 15px;
122 |             font-weight: normal;
123 |             &:hover{
124 |                 background:none;
125 |                 color:@brand-primary;
126 |             }
127 |             
128 |         } 
129 |     }
130 | }
131 | 
132 | footer {
133 |     border-top: 2px solid @brand-primary;
134 |     margin-top:20px;
135 |     padding: 20px 0px 20px 0px;    
136 | 
137 |     a:visited {
138 |         color: @brand-primary;
139 |     }
140 | 
141 |     #disclaimer{
142 |         font-size: 50%;
143 |         padding-top: 15px;
144 |     }
145 | }
146 | 
147 | .avatar-large{
148 |     height: 200px;
149 |     width:200px;
150 | }
151 | 
152 | .note-editor .btn-toolbar{
153 |     margin-left: 0px;
154 | }
155 | 


--------------------------------------------------------------------------------
/client/views/profiles/profile.html:
--------------------------------------------------------------------------------
  1 | 
103 | 


--------------------------------------------------------------------------------
/client/views/profiles/profile.js:
--------------------------------------------------------------------------------
 1 | Template.profile.helpers({
 2 |   beforeRemove: function() {
 3 |     return function(collection, id) {
 4 |       var doc = collection.findOne(id);
 5 |       if (confirm('Really delete "' + doc.title + '"?')) {
 6 |         this.remove();
 7 |         ga("send", "event", "profile", "remove", doc.title);
 8 |         Router.go('profiles');
 9 |       }
10 |     };
11 |   },
12 |   splitInterestedIn: function() {
13 |     if (interestedIn)
14 |       return interestedIn.split(",");
15 |   }
16 | });
17 | 


--------------------------------------------------------------------------------
/client/views/profiles/profileForms.html:
--------------------------------------------------------------------------------
 1 | 
20 | 
21 | 
41 | 
42 | 
89 | 


--------------------------------------------------------------------------------
/client/views/profiles/profileForms.js:
--------------------------------------------------------------------------------
 1 | AutoForm.addHooks(['profileNew', 'profileEdit'], {
 2 |   after: {
 3 |     insert: function(error, result) {
 4 |       if (error) {
 5 |         console.log("Insert Error:", error);
 6 |       } else {
 7 |         ga("send", "event", "profile", "insert", result.title);
 8 |         Router.go('profile', {
 9 |           _id: result
10 |         });
11 |       }
12 |     },
13 |     update: function(error, result) {
14 |       if (error) {
15 |         console.log("Update Error:", error);
16 |       } else {
17 |         ga("send", "event", "profile", "update", result.title);
18 |         Router.go('profile', {
19 |           _id: Router.current().params._id
20 |         });
21 |       }
22 |     }
23 |   }
24 | });
25 | 
26 | Template.profileEdit.events({
27 |   'click #cancel': function(event, template) {
28 |     event.preventDefault();
29 |     Router.go("profile", {
30 |       _id: this.profile._id
31 |     });
32 |   }
33 | })
34 | 
35 | var customImagePreviewUrl = new ReactiveVar();
36 | 
37 | Template.profileFields.rendered = function() {
38 |   var interval;
39 |   var template = this;
40 |   interval = Meteor.setInterval(function() {
41 |     if (typeof uploadcare !== "undefined") {
42 |       Meteor.clearInterval(interval);
43 |       var widget = uploadcare.SingleWidget('#custom-image');
44 |       
45 |       if(template.data.profile && template.data.profile.customImageUrl){
46 |         var customImage = template.data.profile.customImageUrl;
47 |         if(customImage){
48 |           widget.value(customImage);
49 |           customImagePreviewUrl.set(customImage);
50 |         }
51 |       }
52 | 
53 |       widget.onChange(function(file) {
54 |         if (file) {
55 |           file.done(function(info) {
56 |             customImagePreviewUrl.set(info.cdnUrl);
57 |             ga("send", "event", "profile", "imageUploaded", template.data.profile.title);
58 |         
59 |           });
60 |         } else if(customImagePreviewUrl.get()){
61 |           	customImagePreviewUrl.set(null);
62 |         }
63 |       });
64 |     }
65 |   }, 10);
66 | };
67 | 
68 | Template.profileFields.helpers({
69 |   "customImagePreviewUrl": function(event, template) {
70 |     if(customImagePreviewUrl.get())
71 |     	return customImagePreviewUrl.get();
72 |   }
73 | });
74 | 


--------------------------------------------------------------------------------
/client/views/profiles/profileForms.less:
--------------------------------------------------------------------------------
1 | #profileAvatarPreview{
2 | 	margin-bottom: 20px;
3 | }
4 | 


--------------------------------------------------------------------------------
/client/views/profiles/profileSmall.html:
--------------------------------------------------------------------------------
 1 | 
37 | 


--------------------------------------------------------------------------------
/client/views/profiles/profileSmall.less:
--------------------------------------------------------------------------------
 1 | @import "../../lib/journal.variables.import.less";
 2 | 
 3 | .developer-grid.panel{
 4 | 	
 5 | 	@media only screen and (max-width : @screen-md-max) { height:310px; }
 6 |     @media only screen and (min-width : @screen-md-max) { height:275px; }
 7 | 
 8 | 	.name{
 9 | 		margin-top: 5px;
10 | 		margin-bottom: 5px;
11 | 		height:20px;
12 | 		overflow: hidden;
13 | 		@media only screen and (max-width : @screen-md-max) { 
14 | 			height:40px; 
15 | 			overflow: visible;
16 | 		}
17 | 	}
18 | 	.labels{
19 | 		height:30px;
20 | 	}
21 | 	.title{
22 | 		height:70px;
23 | 		@media only screen and (max-width : @screen-md-max) { height:90px; }
24 | 	}
25 | 	.location{
26 | 		height:25px;
27 | 		overflow: hidden;
28 | 	}
29 | 	.avatar{
30 | 		height:100px;
31 | 		width:100px;
32 | 	}
33 | }
34 | 


--------------------------------------------------------------------------------
/client/views/profiles/profiles.html:
--------------------------------------------------------------------------------
 1 | 
10 | 


--------------------------------------------------------------------------------
/client/views/profiles/profilesRecent.html:
--------------------------------------------------------------------------------
 1 | 
23 | 


--------------------------------------------------------------------------------
/client/views/profiles/profilesRecent.less:
--------------------------------------------------------------------------------
1 | #profilesRecent{
2 | 	margin-top: 20px;
3 | 	h2{
4 | 		margin-top: -10px;
5 | 		margin-bottom: 20px;
6 | 	}
7 | }
8 | 


--------------------------------------------------------------------------------
/client/views/user/userProfile.html:
--------------------------------------------------------------------------------
 1 | 
28 | 


--------------------------------------------------------------------------------
/client/views/user/userProfile.js:
--------------------------------------------------------------------------------
 1 | AutoForm.addHooks(['userProfileEdit'], {
 2 | 	after: {
 3 | 		update: function(error, result) {
 4 | 			if (error) {
 5 | 				console.log(error);
 6 | 			} else {
 7 | 				ga("send", "event", "userProfile", "edit", getUserName(Meteor.user()));
 8 | 				Modal.hide("userProfile");
 9 | 			}
10 | 		}
11 | 	}
12 | });
13 | 
14 | Template.userProfile.events({
15 | 	'click #cancel':function(event, template){
16 | 		event.preventDefault();
17 |     	Modal.hide("userProfile");
18 | 	}
19 | });
20 | 


--------------------------------------------------------------------------------
/collections/jobs.js:
--------------------------------------------------------------------------------
  1 | Jobs = new Mongo.Collection("jobs");
  2 | 
  3 | Jobs.attachSchema(
  4 |   new SimpleSchema({
  5 |     title: {
  6 |       type: String,
  7 |       label: "Job Title",
  8 |       max: 128
  9 |     },
 10 |     company: {
 11 |       type: String,
 12 |       label: "Company",
 13 |       max: 128,
 14 |       optional: true
 15 |     },
 16 |     location: {
 17 |       type: String,
 18 |       label: "Location",
 19 |       max: 128,
 20 |       optional: true
 21 |     },
 22 |     url: {
 23 |       type: String,
 24 |       label: "URL",
 25 |       max: 256,
 26 |       optional: true,
 27 |       regEx: SimpleSchema.RegEx.Url
 28 |     },
 29 |     contact: {
 30 |       type: String,
 31 |       label: "Contact Info",
 32 |       max: 128
 33 |     },
 34 |     jobtype: {
 35 |       type: String,
 36 |       label: "Job Type",
 37 |       allowedValues: JOB_TYPES
 38 |     },
 39 |     remote: {
 40 |       type: Boolean,
 41 |       label: "This is a remote position."
 42 |     },
 43 |     userId: {
 44 |       type: String,
 45 |       label: "User Id",
 46 |       autoValue: function() {
 47 |         if (this.isInsert) {
 48 |           return Meteor.userId();
 49 |         } else if (this.isUpsert) {
 50 |           return {
 51 |             $setOnInsert: Meteor.userId()
 52 |           };
 53 |         } else {
 54 |           this.unset();
 55 |         }
 56 |       },
 57 |       denyUpdate: true
 58 |     },
 59 |     userName: {
 60 |       type: String,
 61 |       label: "User Name",
 62 |       autoValue: function() {
 63 |         if (this.isInsert) {
 64 |           return getUserName(Meteor.user());
 65 |         } else if (this.isUpsert) {
 66 |           return {
 67 |             $setOnInsert: getUserName(Meteor.user())
 68 |           };
 69 |         } else {
 70 |           this.unset();
 71 |         }
 72 |       }
 73 |     },
 74 |     description: {
 75 |       type: String,
 76 |       label: "Job Description",
 77 |       max: 20000,
 78 |       autoform: {
 79 |         afFieldInput: SUMMERNOTE_OPTIONS
 80 |       }
 81 |     },
 82 |     status: {
 83 |       type: String,
 84 |       allowedValues: STATUSES,
 85 |       autoValue: function() {
 86 |         if (this.isInsert) {
 87 |           return 'pending';
 88 |         } else if (this.isUpsert) {
 89 |           return {
 90 |             $setOnInsert: 'pending'
 91 |           };
 92 |         }
 93 |       },
 94 |     },
 95 |     // Automatically set HTML content based on markdown content
 96 |     // whenever the markdown content is set.
 97 |     htmlDescription: {
 98 |       type: String,
 99 |       optional: true,
100 |       autoValue: function(doc) {
101 |         var htmlContent = this.field("description");
102 |         if (Meteor.isServer && htmlContent.isSet) {
103 |           return cleanHtml(htmlContent.value);
104 |         }
105 |       }
106 |     },
107 |     // Force value to be current date (on server) upon insert
108 |     // and prevent updates thereafter.
109 |     createdAt: {
110 |       type: Date,
111 |       autoValue: function() {
112 |         if (this.isInsert) {
113 |           return new Date();
114 |         } else if (this.isUpsert) {
115 |           return {
116 |             $setOnInsert: new Date()
117 |           };
118 |         } else {
119 |           this.unset();
120 |         }
121 |       },
122 |       denyUpdate: true
123 |     },
124 |     // Force value to be current date (on server) upon update
125 |     // and don't allow it to be set upon insert.
126 |     updatedAt: {
127 |       type: Date,
128 |       autoValue: function() {
129 |         if (this.isUpdate) {
130 |           return new Date();
131 |         }
132 |       },
133 |       denyInsert: true,
134 |       optional: true
135 |     }
136 |   })
137 | );
138 | 
139 | Jobs.helpers({
140 |   path: function() {
141 |     return 'jobs/' + this._id + '/' + this.slug();
142 |   },
143 |   slug: function() {
144 |     return getSlug(this.title);
145 |   }
146 | });
147 | 
148 | Jobs.allow({
149 |   insert: function(userId, doc) {
150 |     return userId && doc && userId === doc.userId;
151 |   },
152 |   update: function(userId, doc, fieldNames, modifier) {
153 |     return Roles.userIsInRole(userId, ['admin']) || (!_.contains(fieldNames, 'htmlDescription') && !_.contains(fieldNames, 'status') && userId && doc && userId === doc.userId);
154 |   },
155 |   remove: function(userId, doc) {
156 |     return Roles.userIsInRole(userId, ['admin']) || (userId && doc && userId === doc.userId);
157 |   },
158 |   fetch: ['userId']
159 | });
160 | 


--------------------------------------------------------------------------------
/collections/profiles.js:
--------------------------------------------------------------------------------
  1 | Profiles = new Mongo.Collection("experts"); //todo - rename underlying collection to reflect code refactor
  2 | 
  3 | Profiles.attachSchema(
  4 |   new SimpleSchema({
  5 |     userId: {
  6 |       type: String,
  7 |       autoValue: function() {
  8 |         if (this.isInsert) {
  9 |           return Meteor.userId();
 10 |         } else if (this.isUpsert) {
 11 |           return {
 12 |             $setOnInsert: Meteor.userId()
 13 |           };
 14 |         } else {
 15 |           this.unset();
 16 |         }
 17 |       },
 18 |       denyUpdate: true
 19 |     },
 20 |     userName: {
 21 |       type: String,
 22 |       label: "User Name",
 23 |       autoValue: function() {
 24 |         if (this.isInsert) {
 25 |           return getUserName(Meteor.user());
 26 |         } else if (this.isUpsert) {
 27 |           return {
 28 |             $setOnInsert: getUserName(Meteor.user())
 29 |           };
 30 |         } else {
 31 |           this.unset();
 32 |         }
 33 |       }
 34 |     },
 35 |     customImageUrl: {
 36 |       type: String,
 37 |       optional: true
 38 |     },
 39 |     name: {
 40 |       type: String,
 41 |       label: "Name",
 42 |       max: 128
 43 |     },
 44 |     type: {
 45 |       type: String,
 46 |       label: "Individual or Company",
 47 |       allowedValues: ["Individual", "Company"]
 48 |     },
 49 |     title: {
 50 |       type: String,
 51 |       label: "Title",
 52 |       max: 128
 53 |     },
 54 |     location: {
 55 |       type: String,
 56 |       label: "Location",
 57 |       max: 256
 58 |     },
 59 |     description: {
 60 |       type: String,
 61 |       label: "Description",
 62 |       max: 10000,
 63 |       autoform: {
 64 |         afFieldInput: SUMMERNOTE_OPTIONS
 65 |       }
 66 |     },
 67 |     // Automatically set HTML content based on markdown content
 68 |     // whenever the markdown content is set.
 69 |     htmlDescription: {
 70 |       type: String,
 71 |       optional: true,
 72 |       autoValue: function(doc) {
 73 |         var htmlContent = this.field("description");
 74 |         if (Meteor.isServer && htmlContent.isSet) {
 75 |           return cleanHtml(htmlContent.value);
 76 |         }
 77 |       }
 78 |     },
 79 |     availableForHire: {
 80 |       type: Boolean,
 81 |       label: "Currently Available For Hire",
 82 |       defaultValue: false
 83 |     },
 84 |     interestedIn: {
 85 |       type: [String],
 86 |       label: "Interested In",
 87 |       allowedValues: JOB_TYPES,
 88 |       optional: true
 89 |     },
 90 |     contact: {
 91 |       type: String,
 92 |       label: "Contact Info",
 93 |       max: 1024,
 94 |       optional: true
 95 |     },
 96 |     url: {
 97 |       type: String,
 98 |       label: "Personal URL",
 99 |       max: 1024,
100 |       optional: true,
101 |       regEx: SimpleSchema.RegEx.Url
102 |     },
103 |     resumeUrl: {
104 |       type: String,
105 |       label: "Resume URL",
106 |       max: 1024,
107 |       optional: true,
108 |       regEx: SimpleSchema.RegEx.Url
109 |     },
110 |     githubUrl: {
111 |       type: String,
112 |       label: "GitHub URL",
113 |       max: 1024,
114 |       optional: true,
115 |       regEx: SimpleSchema.RegEx.Url
116 |     },
117 |     linkedinUrl: {
118 |       type: String,
119 |       label: "LinkedIn URL",
120 |       max: 1024,
121 |       optional: true,
122 |       regEx: SimpleSchema.RegEx.Url
123 |     },
124 |     stackoverflowUrl: {
125 |       type: String,
126 |       label: "Stackoverflow URL",
127 |       max: 1024,
128 |       optional: true,
129 |       regEx: SimpleSchema.RegEx.Url
130 |     },
131 |     randomSorter: {
132 |       type: Number,
133 |       defaultValue: Math.floor(Math.random() * 10000)
134 |     },
135 |     status: {
136 |       type: String,
137 |       allowedValues: STATUSES,
138 |       defaultValue:"active"
139 |     },
140 |     // Force value to be current date (on server) upon insert
141 |     // and prevent updates thereafter.
142 |     createdAt: {
143 |       type: Date,
144 |       autoValue: function() {
145 |         if (this.isInsert) {
146 |           return new Date();
147 |         } else if (this.isUpsert) {
148 |           return {
149 |             $setOnInsert: new Date()
150 |           };
151 |         } else {
152 |           this.unset();
153 |         }
154 |       },
155 |       denyUpdate: true
156 |     },
157 |     // Force value to be current date (on server) upon update
158 |     // and don't allow it to be set upon insert.
159 |     updatedAt: {
160 |       type: Date,
161 |       autoValue: function() {
162 |         if (this.isUpdate) {
163 |           return new Date();
164 |         }
165 |       },
166 |       denyInsert: true,
167 |       optional: true
168 |     }
169 |   })
170 | );
171 | 
172 | Profiles.helpers({
173 |   displayName: function() {
174 |     return this.name || this.userName;
175 |   },
176 |   path: function() {
177 |     return 'profiles/' + this._id + '/' + this.slug();
178 |   },
179 |   slug: function() {
180 |     return getSlug(this.displayName() + ' ' + this.title);
181 |   }
182 | });
183 | 
184 | Profiles.allow({
185 |   insert: function(userId, doc) {
186 |     return userId && doc && userId === doc.userId;
187 |   },
188 |   update: function(userId, doc, fieldNames, modifier) {
189 |     return Roles.userIsInRole(userId, ['admin']) || (!_.contains(fieldNames, 'randomSorter') && !_.contains(fieldNames, 'htmlDescription') && !_.contains(fieldNames, 'status') && userId && doc && userId === doc.userId);
190 |   },
191 |   remove: function(userId, doc) {
192 |     return Roles.userIsInRole(userId, ['admin']) || (userId && doc && userId === doc.userId);
193 |   },
194 |   fetch: ['userId']
195 | });
196 | 


--------------------------------------------------------------------------------
/collections/users.js:
--------------------------------------------------------------------------------
 1 | Users = Meteor.users;
 2 | 
 3 | UserProfileSchema = new SimpleSchema({
 4 |   name: {
 5 |     type: String,
 6 |     label: "Full Name",
 7 |     max: 64,
 8 |     optional: true
 9 |   }
10 | });
11 | 
12 | UserSchema = new SimpleSchema({
13 |   _id: {
14 |     type: String,
15 |     regEx: SimpleSchema.RegEx.Id
16 |   },
17 |   username: {
18 |     type: String,
19 |     optional: true
20 |   },
21 |   emails: {
22 |     type: [Object],
23 |     // this must be optional if you also use other login services like facebook,
24 |     // but if you use only accounts-password, then it can be required
25 |     optional: true
26 |   },
27 |   "emails.$.address": {
28 |     type: String,
29 |     regEx: SimpleSchema.RegEx.Email,
30 |     label: "Email Address"
31 |   },
32 |   "emails.$.verified": {
33 |     type: Boolean,
34 |     defaultValue: false
35 |   },
36 |   emailHash: {
37 |     type: String,
38 |     optional: true
39 |   },
40 |   isDeveloper: {
41 |     type: Boolean,
42 |     defaultValue: false
43 |   },
44 |   createdAt: {
45 |     type: Date
46 |   },
47 |   profile: {
48 |     type: UserProfileSchema,
49 |     optional: true
50 |   },
51 |   services: {
52 |     type: Object,
53 |     optional: true,
54 |     blackbox: true
55 |   },
56 |   roles: {
57 |     type: Array,
58 |     optional: true
59 |   },
60 |   "roles.$": {
61 |     type: String
62 |   }
63 | });
64 | 
65 | Users.attachSchema(UserSchema);
66 | 
67 | Users.allow({
68 |   insert: function(userId, doc) {
69 |     return false;
70 |   },
71 |   update: function(userId, doc, fieldNames, modifier) {
72 |     return Roles.userIsInRole(userId, ['admin']) || (!_.contains(fieldNames, 'roles') && userId && doc && userId === doc.userId);
73 |   },
74 |   remove: function(userId, doc) {
75 |     return Roles.userIsInRole(userId, ['admin']);
76 |   },
77 |   fetch: ['userId']
78 | });
79 | 


--------------------------------------------------------------------------------
/lib/accounts.js:
--------------------------------------------------------------------------------
 1 | AccountsTemplates.configureRoute("forgotPwd");
 2 | AccountsTemplates.configureRoute("resetPwd");
 3 | AccountsTemplates.configureRoute("signIn", {
 4 |     name: 'signIn',
 5 |     path: '/sign-in',
 6 |     redirect: '/',
 7 | });
 8 | AccountsTemplates.configureRoute("signUp", {
 9 |     name: 'signUp',
10 |     path: '/sign-up',
11 |     redirect: '/',
12 | });
13 | AccountsTemplates.configureRoute("verifyEmail");
14 | 
15 | AccountsTemplates.configure({
16 |     // Behaviour
17 |     confirmPassword: true,
18 |     enablePasswordChange: true,
19 |     forbidClientAccountCreation: false,
20 |     overrideLoginErrors: true,
21 |     sendVerificationEmail: false,
22 | 
23 |     // Appearance
24 |     showForgotPasswordLink: true,
25 |     showLabels: false,
26 |     showPlaceholders: true,
27 | 
28 |     // Client-side Validation
29 |     continuousValidation: true,
30 |     negativeFeedback: false,
31 |     negativeValidation: true,
32 |     positiveValidation: false,
33 |     positiveFeedback: false,
34 | 
35 |     // Redirects
36 |     homeRoutePath: '/',
37 |     redirectTimeout: 2000,
38 | });
39 | 


--------------------------------------------------------------------------------
/lib/admin.js:
--------------------------------------------------------------------------------
 1 | AdminConfig = {
 2 |   name: 'Ethereum Jobs',
 3 |   collections: {
 4 |     Jobs: {
 5 |       icon: 'briefcase',
 6 |       tableColumns: [
 7 |         {label: 'ID', name: '_id'},
 8 |         {label: 'Title', name: 'title'},
 9 |         {label: 'User Name', name: 'userName'},
10 |         {label: 'Status', name: 'status'}
11 |       ],
12 |       color: 'red'
13 |     },
14 |     Profiles: {
15 |       icon: 'file-text-o',
16 |       tableColumns: [
17 |         {label: 'ID', name: '_id'},
18 |         {label: 'Title', name: 'title'},
19 |         {label: 'User Name', name: 'userName'},
20 |         {label: 'Status', name: 'status'}
21 |       ],
22 |       color: 'green'
23 |     },
24 |   },
25 |   autoForm:{
26 |     omitFields: ['createdAt','updatedAt']
27 |   }
28 | };
29 | 


--------------------------------------------------------------------------------
/lib/avatar.js:
--------------------------------------------------------------------------------
1 | Avatar.options = {
2 |   fallbackType: "default image",
3 |   emailHashProperty: "emailHash",
4 |   defaultImageUrl: "/images/avatar.png",
5 |   gravatarDefault: "mm"
6 | };
7 | 


--------------------------------------------------------------------------------
/lib/constants.js:
--------------------------------------------------------------------------------
 1 | JOB_TYPES = ["Full Time", "Hourly Contract", "Term Contract", "Mentoring", "Bounty", "Open Source", "Volunteer", "Other"];
 2 | 
 3 | SUMMERNOTE_OPTIONS = {
 4 |   type: 'summernote',
 5 |   height: 300,
 6 |   minHeight: 300,
 7 |   toolbar: [
 8 |     ['style', ['style']],
 9 |     ['font', ['bold', 'italic', 'underline', 'clear']],
10 |     ['para', ['ul', 'ol']],
11 |     ['insert', ['link','hr']],
12 |     ['misc', ['codeview']]
13 |   ],
14 |   styleWithSpan: false
15 | };
16 | 
17 | STATUSES = ["pending","active","flagged"];
18 | 


--------------------------------------------------------------------------------
/lib/helpers.js:
--------------------------------------------------------------------------------
 1 | getUserName = function(user){
 2 | 	if (!user)
 3 | 		return '';
 4 | 
 5 | 	if (user.profile && user.profile.name)
 6 | 		return user.profile.name;
 7 | 	if (user.profile && user.profile.firstName && user.profile.lastName)
 8 | 		return user.profile.firstName + " " + user.profile.lastName;
 9 | 	if (user.username)
10 | 		return user.username;
11 | 	if (user.emails && user.emails[0] && user.emails[0].address)
12 | 		return user.emails[0].address;
13 | 
14 | 	return '';
15 | };
16 | 
17 | getUserEmail = function(user){
18 | 	if (user && user.emails && user.emails[0] && user.emails[0].address)
19 | 		return user.emails[0].address;
20 | 	else if (user && user.services && user.services.facebook && user.services.facebook.email)
21 | 		return user.services.facebook.email;
22 | 	else if (user && user.services && user.services.google && user.services.google.email)
23 | 		return user.services.google.email;
24 | 	else if (user && user.services && user.services.github && user.services.github.email)
25 | 		return user.services.github.email;
26 | 	else if (user && user.services && user.services.linkedin && user.services.linkedin.email)
27 | 		return user.services.linkedin.email;
28 | 	else if (user && user.services && user.services.twitter && user.services.twitter.email)
29 | 		return user.services.twitter.email;
30 | 	else if (user && user.services && user.services.meteor && user.services.meteor.email)
31 | 		return user.services.meteor.email;
32 | };
33 | 
34 | daysUntilExpiration = function() {
35 |     var daysToWait = 90;
36 |     var daysAgo = new Date();
37 |     daysAgo.setDate(daysAgo.getDate()-daysToWait);
38 |     return daysAgo;
39 | }
40 | 


--------------------------------------------------------------------------------
/public/images/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherCasts/wework/c56ba844ca5956e1d2eaaa8fa62a1c36737cca1d/public/images/avatar.png


--------------------------------------------------------------------------------
/public/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherCasts/wework/c56ba844ca5956e1d2eaaa8fa62a1c36737cca1d/public/images/favicon.ico


--------------------------------------------------------------------------------
/public/images/network.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherCasts/wework/c56ba844ca5956e1d2eaaa8fa62a1c36737cca1d/public/images/network.jpg


--------------------------------------------------------------------------------
/public/images/universe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherCasts/wework/c56ba844ca5956e1d2eaaa8fa62a1c36737cca1d/public/images/universe.png


--------------------------------------------------------------------------------
/router.js:
--------------------------------------------------------------------------------
  1 | Router.configure({
  2 |   layoutTemplate: 'layout',
  3 |   loadingTemplate: 'loading',
  4 |   yieldTemplates: {
  5 |     header: {
  6 |       to: 'header'
  7 |     },
  8 |     footer: {
  9 |       to: 'footer'
 10 |     }
 11 |   },
 12 |   progressSpinner: false,
 13 |   trackPageView: true
 14 | });
 15 | 
 16 | var subs = new SubsManager({
 17 |   cacheLimit: 20,
 18 |   expireIn: 5
 19 | });
 20 | 
 21 | Router.map(function() {
 22 |   this.route('home', {
 23 |     path: '/',
 24 |     layoutTemplate:'layoutNoContainer',
 25 |     data: function() {
 26 |       return {
 27 |         jobs: Jobs.find({
 28 |           status:"active"
 29 |         }, {
 30 |           sort: {
 31 |             createdAt: -1
 32 |           },
 33 |           limit: 10
 34 |         }),
 35 |         profiles: Profiles.find({}, {
 36 |           sort: {
 37 |             availableForHire: -1,
 38 |             randomSorter: 1
 39 |           },
 40 |           limit: 8
 41 |         }),
 42 |         profile: Profiles.findOne({
 43 |           userId: Meteor.userId()
 44 |         })
 45 |       };
 46 |     },
 47 |     subscriptions: function() {
 48 |       return [subs.subscribe('homeJobs'), subs.subscribe('homeDevelopers')];
 49 |     }
 50 |   });
 51 | 
 52 |   this.route('jobs', {
 53 |     path: '/jobs',
 54 |     data: function() {
 55 |       return {
 56 |         jobs: Jobs.find({
 57 |           status:"active"
 58 |         }, {
 59 |           sort: {
 60 |             createdAt: -1
 61 |           }
 62 |         })
 63 |       };
 64 |     },
 65 |     waitOn: function() {
 66 |       return subs.subscribe('jobs');
 67 |     }
 68 |   });
 69 | 
 70 |   this.route('myJobs', {
 71 |     path: '/myjobs',
 72 |     data: function() {
 73 |       return {
 74 |         jobs: Jobs.find({
 75 |           userId:Meteor.userId()
 76 |         }, {
 77 |           sort: {
 78 |             createdAt: -1
 79 |           }
 80 |         })
 81 |       };
 82 |     },
 83 |     waitOn: function() {
 84 |       return subs.subscribe('my_jobs');
 85 |     }
 86 |   });
 87 | 
 88 |   this.route('job', {
 89 |     path: '/jobs/:_id/:slug?',
 90 |     data: function() {
 91 |       return Jobs.findOne({
 92 |         _id: this.params._id
 93 |       });
 94 |     },
 95 |     waitOn: function() {
 96 |       return subs.subscribe("job", this.params._id);
 97 |     },
 98 |     onBeforeAction: function() {
 99 |         var expectedSlug = this.data().slug();
100 |         if (this.params.slug !== expectedSlug) {
101 |             this.redirect("job", {_id:this.params._id, slug:expectedSlug});
102 |         } else {
103 |             this.next();
104 |         }
105 |     }
106 |   });
107 | 
108 |   this.route('jobNew', {
109 |     path: '/job'
110 |   });
111 | 
112 |   this.route('jobEdit', {
113 |     path: '/jobs/:_id/:slug/edit',
114 |     data: function() {
115 |       return {
116 |         job: Jobs.findOne({
117 |           _id: this.params._id
118 |         })
119 |       };
120 |     },
121 |     waitOn: function() {
122 |       return subs.subscribe("job", this.params._id);
123 |     }
124 |   });
125 | 
126 |   this.route('profiles', {
127 |     path: '/profiles',
128 |     data: function() {
129 |       return {
130 |         profiles: Profiles.find({}, {
131 |           sort: {
132 |             randomSorter: 1
133 |           }
134 |         })
135 |       };
136 |     },
137 |     waitOn: function() {
138 |       return subs.subscribe('profiles');
139 |     }
140 |   });
141 | 
142 |   this.route('profile', {
143 |     path: '/profiles/:_id/:slug?',
144 |     data: function() {
145 |       return Profiles.findOne({
146 |           _id: this.params._id
147 |         });
148 |     },
149 |     waitOn: function() {
150 |       return subs.subscribe('profile', this.params._id);
151 |     },
152 |     onBeforeAction: function() {
153 |         var expectedSlug = this.data().slug();
154 |         if (this.params.slug !== expectedSlug) {
155 |             this.redirect("profile", {_id:this.params._id, slug:expectedSlug});
156 |         } else {
157 |             this.next();
158 |         }
159 |     }
160 |   });
161 | 
162 |   this.route('profileNew', {
163 |     path: '/profile',
164 |     onBeforeAction: function() {
165 |       if (Meteor.user().isDeveloper) {
166 |         Router.go('profile', Profiles.findOne({
167 |           userId: Meteor.userId()
168 |         }));
169 |       } else {
170 |         this.next();
171 |       }
172 |     }
173 |   });
174 | 
175 |   this.route('profileEdit', {
176 |     path: '/profiles/:_id/:slug/edit',
177 |     data: function() {
178 |       return {
179 |         profile: Profiles.findOne({
180 |           _id: this.params._id
181 |         })
182 |       };
183 |     },
184 |     waitOn: function() {
185 |       return subs.subscribe('profile', this.params._id);
186 |     }
187 |   });
188 | 
189 |   //legacy url redirects
190 |   this.route('experts', function(){
191 |     this.redirect("profiles");
192 |   });
193 |   this.route('experts/:_id', function(){
194 |     this.redirect("profile", {_id:this.params._id});
195 |   });
196 | });
197 | 
198 | Router.plugin('ensureSignedIn', {
199 |   only: ['profileEdit', 'profileNew', 'jobEdit', 'jobNew']
200 | });
201 | 
202 | 
203 | Router.onBeforeAction(function(){
204 |   loadUploadcare();
205 |   this.next();
206 | },{only:['profileEdit','profileNew','jobEdit','jobNew']});
207 | 
208 | Router.plugin('dataNotFound', {notFoundTemplate: 'notFound'});
209 | 


--------------------------------------------------------------------------------
/server/accounts.js:
--------------------------------------------------------------------------------
 1 | Accounts.emailTemplates.siteName = "Ethereum Jobs";
 2 | Accounts.emailTemplates.from = FROM_EMAIL;
 3 | 
 4 | Accounts.onCreateUser(function(options, user) {
 5 |   if (options.profile)
 6 |     user.profile = options.profile;
 7 | 
 8 |   var email = getUserEmail(user);
 9 |   if(email){
10 |   	user.emailHash = CryptoJS.MD5(email.trim().toLowerCase()).toString();
11 |   }
12 |   
13 |   return user;
14 | });
15 | 


--------------------------------------------------------------------------------
/server/api.js:
--------------------------------------------------------------------------------
 1 | // API must be configured and built after startup!
 2 | Meteor.startup(function() {
 3 | 
 4 |   // Global configuration
 5 |   Restivus.configure({
 6 |     useAuth: false,
 7 |     prettyJson: false
 8 |   });
 9 | 
10 |   Restivus.addRoute('jobs', {
11 |     get: function() {
12 |       return {
13 |         status: "success",
14 |         data: Jobs.find({
15 |           createdAt: {
16 |             $gte: daysUntilExpiration()
17 |           },
18 |           status: "active"
19 |         }, {
20 |           sort: {
21 |             createdAt: -1
22 |           },
23 |           fields: {
24 |             userId: false,
25 |             userName: false
26 |           },
27 |           transform: function(doc) {
28 |             doc.siteUrl = Meteor.absoluteUrl("jobs/" + doc._id + "/" + getSlug(doc.title));
29 |             return doc;
30 |           }
31 |         }).fetch()
32 |       };
33 |     }
34 |   });
35 | 
36 |   Restivus.addRoute('profiles', {
37 |     get: function() {
38 |       return {
39 |         status: "success",
40 |         data: Profiles.find({
41 |           status: "active"
42 |         }, {
43 |           sort: {
44 |             createdAt: -1
45 |           },
46 |           fields: {
47 |             userId: false,
48 |             userName: false
49 |           },
50 |           transform: function(doc) {
51 |             doc.siteUrl = Meteor.absoluteUrl("profiles/" + doc._id + "/" + getSlug((doc.name || doc.userName) + " " + doc.title));
52 |             return doc;
53 |           }
54 |         }).fetch()
55 |       };
56 |     }
57 |   });
58 | });
59 | 


--------------------------------------------------------------------------------
/server/cron.js:
--------------------------------------------------------------------------------
 1 | SyncedCron.add({
 2 |   name: 'Randomize Profile Random Sorter',
 3 |   schedule: function(parser) {
 4 |     // parser is a later.parse object
 5 |     return parser.text('every 2 hours');
 6 |   }, 
 7 |   job: function() {
 8 |     Profiles.find({}).forEach(function(profile){
 9 |       Profiles.update({_id:profile._id},{$set:{
10 |             randomSorter:Math.floor(Math.random()*10000)
11 |         }});
12 |     });
13 |   }
14 | });
15 | 
16 | SyncedCron.options = {
17 |   //Log job run details to console
18 |   log: true,
19 | 
20 |   //Name of collection to use for synchronisation and logging
21 |   collectionName: 'cronHistory',
22 | 
23 |   //Default to using localTime
24 |   utc: true, 
25 | 
26 |   //TTL in seconds for history records in collection to expire
27 |   //NOTE: Unset to remove expiry but ensure you remove the index from 
28 |   //mongo by hand
29 |   collectionTTL: 172800
30 | };
31 | 
32 | SyncedCron.start();
33 | 


--------------------------------------------------------------------------------
/server/hooks.js:
--------------------------------------------------------------------------------
 1 | Profiles.after.insert(function(userId, doc) {
 2 |   Users.update({
 3 |     _id: doc.userId
 4 |   }, {
 5 |     $set: {
 6 |       isDeveloper: true
 7 |     }
 8 |   });
 9 | });
10 | 
11 | Profiles.after.remove(function(userId, doc) {
12 |   Users.update({
13 |     _id: doc.userId
14 |   }, {
15 |     $set: {
16 |       isDeveloper: false
17 |     }
18 |   });
19 | });
20 | 
21 | Jobs.after.insert(function(userId, doc){
22 | 	var admin = Users.findOne({roles:"admin"});
23 | 	Email.send({
24 |       to: getUserEmail(admin),
25 |       from: FROM_EMAIL,
26 |       subject: "New Job Posted - " + doc.title,
27 |       text: "Job needs to be approved before it is live:\n\n" 
28 |             + Meteor.absoluteUrl("jobs/"+doc._id) + "\n\n\n\n\n\n" 
29 |             + EJSON.stringify(doc, null, 4)
30 |     });
31 | });
32 | 


--------------------------------------------------------------------------------
/server/lib/constants.js:
--------------------------------------------------------------------------------
1 | FROM_EMAIL = "Ethereum Jobs ";
2 | 


--------------------------------------------------------------------------------
/server/lib/helpers.js:
--------------------------------------------------------------------------------
 1 | cleanHtml = function (s) {
 2 |   
 3 |   var s = sanitizeHtml(s, {
 4 |     allowedTags: [
 5 |       'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul',
 6 |       'ol', 'nl', 'li', 'b', 'i', 'strong', 'em', 'strike',
 7 |       'code', 'hr', 'br', 'pre'
 8 |     ]
 9 |   });
10 | 
11 |   return s;
12 | };
13 | 


--------------------------------------------------------------------------------
/server/migrations.js:
--------------------------------------------------------------------------------
  1 | Migrations.add({
  2 |   version: 1,
  3 |   name: 'Adds emailHash for all existing users',
  4 |   up: function() {
  5 |     Users.find({}).forEach(function(user) {
  6 |       var email = getUserEmail(user);
  7 |       if (email)
  8 |         Users.update({
  9 |           _id: user._id
 10 |         }, {
 11 |           $set: {
 12 |             emailHash: CryptoJS.MD5(email.trim().toLowerCase()).toString()
 13 |           }
 14 |         });
 15 |     });
 16 |   },
 17 |   down: function() {}
 18 | });
 19 | 
 20 | Migrations.add({
 21 |   version: 2,
 22 |   name: 'Adds isDeveloper for all existing users',
 23 |   up: function() {
 24 |     var profileUserIds = _.pluck(Profiles.find().fetch(), 'userId');
 25 |     Users.update({
 26 |       _id: {
 27 |         $in: profileUserIds
 28 |       }
 29 |     }, {
 30 |       $set: {
 31 |         isDeveloper: true
 32 |       }
 33 |     }, {
 34 |       multi: true
 35 |     });
 36 |     Users.update({
 37 |       _id: {
 38 |         $nin: profileUserIds
 39 |       }
 40 |     }, {
 41 |       $set: {
 42 |         isDeveloper: false
 43 |       }
 44 |     }, {
 45 |       multi: true
 46 |     });
 47 |   },
 48 |   down: function() {}
 49 | });
 50 | 
 51 | Migrations.add({
 52 |   version: 3,
 53 |   name: 'Adds randomSorter for all developers',
 54 |   up: function() {
 55 |     Profiles.find({}).forEach(function(profile) {
 56 |       Profiles.update({
 57 |         _id: profile._id
 58 |       }, {
 59 |         $set: {
 60 |           randomSorter: Math.floor(Math.random() * 10000)
 61 |         }
 62 |       });
 63 |     });
 64 |   },
 65 |   down: function() {}
 66 | });
 67 | 
 68 | Migrations.add({
 69 |   version: 4,
 70 |   name: 'Copy htmlDescription over to description',
 71 |   up: function() {
 72 |     Profiles.find({}).forEach(function(profile) {
 73 |       if(profile.htmlDescription)
 74 |         Profiles.update({
 75 |           _id: profile._id
 76 |         }, {
 77 |           $set: {
 78 |             description: profile.htmlDescription,
 79 |             htmlDescription: cleanHtml(profile.htmlDescription)
 80 |           }
 81 |         });
 82 |     });
 83 | 
 84 |     Jobs.find({}).forEach(function(job) {
 85 |       if(job.htmlDescription)
 86 |         Jobs.update({
 87 |           _id: job._id
 88 |         }, {
 89 |           $set: {
 90 |             description: job.htmlDescription,
 91 |             htmlDescription: cleanHtml(job.htmlDescription)
 92 |           }
 93 |         });
 94 |     });
 95 | 
 96 |   },
 97 |   down: function() {}
 98 | });
 99 | 
100 | Migrations.add({
101 |   version: 5,
102 |   name: 'Set status for all profiles/jobs',
103 |   up: function() {
104 |     Profiles.update({},{$set:{status:"active"}}, {multi:true});
105 |     Jobs.update({},{$set:{status:"active"}}, {multi:true});
106 |   },
107 |   down: function() {}
108 | });
109 | 
110 | Migrations.migrateTo('latest');
111 | 


--------------------------------------------------------------------------------
/server/publications.js:
--------------------------------------------------------------------------------
  1 | Meteor.publish("userData", function() {
  2 |   check(arguments, [Match.Any]);
  3 |   if (this.userId) {
  4 |     return [
  5 |       Users.find({
  6 |         _id: this.userId
  7 |       }),
  8 |       Profiles.find({
  9 |         userId: this.userId
 10 |       })
 11 |     ];
 12 |   }
 13 |   this.ready();
 14 | });
 15 | 
 16 | Meteor.publish('jobCount', function() {
 17 |   Counts.publish(this, 'jobs', Jobs.find({
 18 |     createdAt: {
 19 |       $gte: daysUntilExpiration()
 20 |     },
 21 |     status: "active"
 22 |   }));
 23 | });
 24 | 
 25 | Meteor.publish('developerCount', function() {
 26 |   Counts.publish(this, 'developers', Profiles.find({
 27 |     status: "active"
 28 |   }));
 29 | });
 30 | 
 31 | Meteor.publish("homeJobs", function() {
 32 |   check(arguments, [Match.Any]);
 33 |   return [
 34 |     Jobs.find({
 35 |       createdAt: {
 36 |         $gte: daysUntilExpiration()
 37 |       },
 38 |       status: "active"
 39 |     }, {
 40 |       sort: {
 41 |         createdAt: -1
 42 |       },
 43 |       limit: 10,
 44 |       fields: {
 45 |         title: true,
 46 |         company: true,
 47 |         location: true,
 48 |         createdAt: true,
 49 |         updatedAt: true,
 50 |         remote: true,
 51 |         jobtype: true,
 52 |         status:true
 53 |       }
 54 |     })
 55 |   ];
 56 | });
 57 | 
 58 | Meteor.publishComposite('homeDevelopers', {
 59 |   find: function() {
 60 |     return Profiles.find({
 61 |     	status: "active"
 62 |     }, {
 63 |       sort: {
 64 |         availableForHire: -1,
 65 |         randomSorter: 1
 66 |       },
 67 |       limit: 8,
 68 |       fields: {
 69 |         userId: true,
 70 |         title: true,
 71 |         location: true,
 72 |         availableForHire: true,
 73 |         randomSorter: true,
 74 |         type: true,
 75 |         name: true,
 76 |         userName: true,
 77 |         status:true,
 78 |         customImageUrl:true
 79 |       }
 80 |     });
 81 |   },
 82 |   children: [{
 83 |     find: function(profile) {
 84 |       return Users.find({
 85 |         _id: profile.userId
 86 |       }, {
 87 |         fields: {
 88 |           "emailHash": true,
 89 |           "services.facebook.id": true,
 90 |           "services.twitter.profile_image_url": true,
 91 |           "services.facebook.id": true,
 92 |           "services.google.picture": true,
 93 |           "services.github.username": true
 94 |         }
 95 |       });
 96 |     }
 97 |   }]
 98 | });
 99 | 
100 | Meteor.publish("jobs", function() {
101 |   check(arguments, [Match.Any]);
102 |   return [
103 |     Jobs.find({
104 |       createdAt: {
105 |         $gte: daysUntilExpiration()
106 |       },
107 |       status: "active"
108 |     }, {
109 |       fields: {
110 |         title: true,
111 |         company: true,
112 |         location: true,
113 |         createdAt: true,
114 |         updatedAt: true,
115 |         remote: true,
116 |         jobtype: true,
117 |         status:true
118 |       }
119 |     })
120 |   ];
121 | });
122 | 
123 | Meteor.publish("my_jobs", function() {
124 |   check(arguments, [Match.Any]);
125 |   if (this.userId) {
126 |     return [
127 |       Jobs.find({
128 |         userId: this.userId
129 |       })
130 |     ];
131 |   }
132 |   this.ready();
133 | });
134 | 
135 | Meteor.publish("job", function(jobId) {
136 |   check(arguments, [Match.Any]);
137 |   return [
138 |     Jobs.find({
139 |       _id: jobId
140 |     })
141 |   ];
142 | });
143 | 
144 | Meteor.publishComposite('profile', function(profileId) {
145 |   return {
146 |     find: function() {
147 |       return Profiles.find({
148 |         _id: profileId
149 |       })
150 |     },
151 |     children: [{
152 |       find: function(profile) {
153 |         return Users.find({
154 |           _id: profile.userId
155 |         }, {
156 |           fields: {
157 |             "emailHash": true,
158 |             "services.facebook.id": true,
159 |             "services.twitter.profile_image_url": true,
160 |             "services.facebook.id": true,
161 |             "services.google.picture": true,
162 |             "services.github.username": true
163 |           }
164 |         });
165 |       }
166 |     }]
167 |   }
168 | });
169 | 
170 | Meteor.publish("profiles", function() {
171 |   check(arguments, [Match.Any]);
172 |   return [
173 |     Profiles.find({
174 |     	status: "active"
175 |     }, {
176 |       fields: {
177 |         userId: true,
178 |         title: true,
179 |         location: true,
180 |         availableForHire: true,
181 |         randomSorter: true,
182 |         type: true,
183 |         name: true,
184 |         userName: true,
185 |         status:true,
186 |         customImageUrl:true
187 |       }
188 |     }),
189 |     Users.find({  //this may publish users for not active status profiles - could be resolved with publish composite, but performance is slow with so many children lookups
190 |       isDeveloper: true
191 |     }, {
192 |       fields: {
193 |         "emailHash": true,
194 |         "services.facebook.id": true,
195 |         "services.twitter.profile_image_url": true,
196 |         "services.facebook.id": true,
197 |         "services.google.picture": true,
198 |         "services.github.username": true
199 |       }
200 |     })
201 |   ];
202 | });
203 | 


--------------------------------------------------------------------------------
/server/rss.js:
--------------------------------------------------------------------------------
 1 | RssFeed.publish('jobs', function(query) {
 2 |   var self = this;
 3 |   var pubDate = new Date();
 4 |   var lastBuildDate = new Date();
 5 |   var mostRecent = Jobs.findOne({}, {
 6 |     sort: {
 7 |       createdAt: -1
 8 |     }
 9 |   });
10 |   var secondMostRecent = Jobs.findOne({}, {
11 |     sort: {
12 |       createdAt: -1
13 |     },
14 |     skip: 1
15 |   });
16 |   if (mostRecent)
17 |     pubDate = mostRecent.createdAt;
18 |   if (secondMostRecent)
19 |     lastBuildDate = secondMostRecent.createdAt;
20 | 
21 |   self.setValue('title', self.cdata('Ethereum Jobs - Recent Jobs'));
22 |   self.setValue('description', self.cdata('This is a feed of recent jobs posted to Ethereum Jobs.'));
23 |   self.setValue('link', Meteor.absoluteUrl());
24 |   self.setValue('lastBuildDate', lastBuildDate);
25 |   self.setValue('pubDate', pubDate);
26 |   self.setValue('ttl', 1);
27 | 
28 |   Jobs.find({
29 |     status: "active"
30 |   }, {
31 |     sort: {
32 |       createdAt: -1
33 |     }
34 |   }).forEach(function(job) {
35 |     self.addItem({
36 |       title: self.cdata(job.title),
37 |       description: self.cdata(job.htmlDescription),
38 |       link: Meteor.absoluteUrl(job.path()),
39 |       guid: Meteor.absoluteUrl(job.path()),
40 |       pubDate: job.createdAt
41 |     });
42 |   });
43 | });
44 | 
45 | var profileRss = function(query) {
46 |   var self = this;
47 |   var pubDate = new Date();
48 |   var lastBuildDate = new Date();
49 |   var mostRecent = Profiles.findOne({}, {
50 |     sort: {
51 |       createdAt: -1
52 |     }
53 |   });
54 |   var secondMostRecent = Profiles.findOne({}, {
55 |     sort: {
56 |       createdAt: -1
57 |     },
58 |     skip: 1
59 |   });
60 |   if (mostRecent)
61 |     pubDate = mostRecent.createdAt;
62 |   if (secondMostRecent)
63 |     lastBuildDate = secondMostRecent.createdAt;
64 | 
65 |   self.setValue('title', self.cdata('Ethereum Jobs - Recent Profiles'));
66 |   self.setValue('description', self.cdata('This is a feed of recent profiles listed on Ethereum Jobs.'));
67 |   self.setValue('link', Meteor.absoluteUrl());
68 |   self.setValue('lastBuildDate', lastBuildDate);
69 |   self.setValue('pubDate', pubDate);
70 |   self.setValue('ttl', 1);
71 | 
72 |   Profiles.find({
73 |     status: "active"
74 |   }, {
75 |     sort: {
76 |       createdAt: -1
77 |     }
78 |   }).forEach(function(profile) {
79 |     self.addItem({
80 |       title: self.cdata(profile.title),
81 |       description: self.cdata(profile.htmlDescription),
82 |       link: Meteor.absoluteUrl(profile.path()),
83 |       guid: Meteor.absoluteUrl(profile.path()),
84 |       pubDate: profile.createdAt
85 |     });
86 |   });
87 | };
88 | 
89 | RssFeed.publish('profiles', profileRss)
90 | 
91 | RssFeed.publish('experts', profileRss);
92 | 


--------------------------------------------------------------------------------
/server/sitemap.js:
--------------------------------------------------------------------------------
 1 | sitemaps.add('/sitemap.xml', function() {
 2 |   var out = [];
 3 |   var jobs = Jobs.find({status: "active"}, {sort: {createdAt: -1}}).fetch();
 4 |   _.each(jobs, function(job) {
 5 |     out.push({
 6 |       page: job.path(),
 7 |       lastmod: job.updatedAt
 8 |     });
 9 |   });
10 | 
11 |   var profiles = Profiles.find({status:"active"}, {sort: {createdAt: -1}}).fetch();
12 |   _.each(profiles, function(profile) {
13 |     out.push({
14 |       page: profile.path(),
15 |       lastmod: profile.updatedAt
16 |     });
17 |   });
18 |   return out;
19 | });
20 | 


--------------------------------------------------------------------------------
/settings-example.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "public" : {
 3 |     "ga": {
 4 |       "account":"GA ID"
 5 |     }
 6 |   },
 7 |   "apm":{
 8 |   	"id":"APM ID",
 9 |   	"secret":"APM Secret"
10 |   }
11 | }


--------------------------------------------------------------------------------
/wework.sublime-project:
--------------------------------------------------------------------------------
 1 | {
 2 | 	"folders":
 3 | 	[
 4 | 		{
 5 | 			"path": "."
 6 | 		},
 7 | 		{
 8 | 			"path": "../wework-config"
 9 | 		}
10 | 	],
11 | 	"settings":
12 | 	{
13 | 		"tabSize": 4,
14 | 		"translateTabsToSpaces": false,
15 | 		"trim_trailing_white_space_on_save": true,
16 | 		"useTabStops": true
17 | 	}
18 | }
19 | 


--------------------------------------------------------------------------------