travis-yaml 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/.travis.yml +14 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +73 -0
- data/LICENSE +22 -0
- data/README.md +232 -0
- data/Rakefile +3 -0
- data/SPEC.md +1018 -0
- data/bench/parser_bench.rb +54 -0
- data/config.ru +2 -0
- data/lib/travis/yaml.rb +43 -0
- data/lib/travis/yaml/matrix.rb +65 -0
- data/lib/travis/yaml/nodes.rb +40 -0
- data/lib/travis/yaml/nodes/branches.rb +12 -0
- data/lib/travis/yaml/nodes/bundler_args.rb +6 -0
- data/lib/travis/yaml/nodes/cache.rb +29 -0
- data/lib/travis/yaml/nodes/compiler.rb +7 -0
- data/lib/travis/yaml/nodes/compiler_entry.rb +9 -0
- data/lib/travis/yaml/nodes/deploy.rb +7 -0
- data/lib/travis/yaml/nodes/deploy_conditions.rb +12 -0
- data/lib/travis/yaml/nodes/deploy_entry.rb +10 -0
- data/lib/travis/yaml/nodes/env.rb +36 -0
- data/lib/travis/yaml/nodes/fixed_value.rb +60 -0
- data/lib/travis/yaml/nodes/git.rb +9 -0
- data/lib/travis/yaml/nodes/jdk.rb +11 -0
- data/lib/travis/yaml/nodes/language.rb +18 -0
- data/lib/travis/yaml/nodes/language_specific.rb +45 -0
- data/lib/travis/yaml/nodes/mapping.rb +204 -0
- data/lib/travis/yaml/nodes/matrix.rb +36 -0
- data/lib/travis/yaml/nodes/node.rb +102 -0
- data/lib/travis/yaml/nodes/notifications.rb +77 -0
- data/lib/travis/yaml/nodes/open_mapping.rb +18 -0
- data/lib/travis/yaml/nodes/os.rb +21 -0
- data/lib/travis/yaml/nodes/os_entry.rb +19 -0
- data/lib/travis/yaml/nodes/root.rb +44 -0
- data/lib/travis/yaml/nodes/ruby.rb +11 -0
- data/lib/travis/yaml/nodes/scalar.rb +97 -0
- data/lib/travis/yaml/nodes/sequence.rb +84 -0
- data/lib/travis/yaml/nodes/stage.rb +6 -0
- data/lib/travis/yaml/nodes/version.rb +6 -0
- data/lib/travis/yaml/nodes/version_list.rb +7 -0
- data/lib/travis/yaml/nodes/virtual_env.rb +7 -0
- data/lib/travis/yaml/parser.rb +31 -0
- data/lib/travis/yaml/parser/dummy.rb +13 -0
- data/lib/travis/yaml/parser/psych.rb +217 -0
- data/lib/travis/yaml/parser/ruby.rb +77 -0
- data/lib/travis/yaml/secure_string.rb +12 -0
- data/lib/travis/yaml/version.rb +5 -0
- data/play/lint.rb +8 -0
- data/play/spec.rb +183 -0
- data/play/weblint.rb +296 -0
- data/spec/nodes/.rb +0 -0
- data/spec/nodes/branches_spec.rb +45 -0
- data/spec/nodes/bundler_args_spec.rb +9 -0
- data/spec/nodes/cache_spec.rb +55 -0
- data/spec/nodes/compiler_spec.rb +14 -0
- data/spec/nodes/deploy_spec.rb +83 -0
- data/spec/nodes/git_spec.rb +55 -0
- data/spec/nodes/jdk_spec.rb +41 -0
- data/spec/nodes/language_spec.rb +79 -0
- data/spec/nodes/notifications_spec.rb +45 -0
- data/spec/nodes/os_spec.rb +28 -0
- data/spec/nodes/ruby_spec.rb +69 -0
- data/spec/nodes/stage_spec.rb +34 -0
- data/spec/nodes/virtual_env_spec.rb +9 -0
- data/spec/parser/dummy_spec.rb +7 -0
- data/spec/parser/ruby_spec.rb +41 -0
- data/spec/support.rb +3 -0
- data/spec/support/coverage.rb +11 -0
- data/spec/support/environment.rb +1 -0
- data/spec/yaml_spec.rb +26 -0
- data/travis-yaml.gemspec +24 -0
- metadata +207 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Travis::Yaml
|
4
|
+
module Parser
|
5
|
+
class Ruby
|
6
|
+
def self.parses?(value)
|
7
|
+
value.is_a? Hash
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.parse(value)
|
11
|
+
new(value).parse
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(value, implicit = false)
|
15
|
+
@value = value
|
16
|
+
@implicit = implicit
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse(root = nil)
|
20
|
+
root ||= Travis::Yaml::Nodes::Root.new
|
21
|
+
accept(root, @value)
|
22
|
+
root
|
23
|
+
end
|
24
|
+
|
25
|
+
def accept(node, value)
|
26
|
+
case value
|
27
|
+
when Array then node.visit_sequence self, value
|
28
|
+
when Hash then node.visit_mapping self, value
|
29
|
+
when SecureString then node.visit_scalar self, :secure, value, @implicit
|
30
|
+
when String then node.visit_scalar self, :str, value, @implicit
|
31
|
+
when Symbol then node.visit_scalar self, :str, value.to_s, @implicit
|
32
|
+
when Integer then node.visit_scalar self, :int, value, @implicit
|
33
|
+
when Float then node.visit_scalar self, :float, value, @implicit
|
34
|
+
when DateTime, Time, Date then node.visit_scalar self, :time, value, @implicit
|
35
|
+
when true, false then node.visit_scalar self, :bool, value, @implicit
|
36
|
+
when Regexp then node.visit_scalar self, :regexp, value, @implicit
|
37
|
+
when nil then node.visit_scalar self, :null, value, @implicit
|
38
|
+
else node.visit_unexpected self, value
|
39
|
+
end
|
40
|
+
node.verify
|
41
|
+
end
|
42
|
+
|
43
|
+
def cast(type, value)
|
44
|
+
case type
|
45
|
+
when :str then value.to_s
|
46
|
+
when :binary then value.unpack('m').first
|
47
|
+
when :bool then !!value
|
48
|
+
when :float then Float value
|
49
|
+
when :int then Integer value
|
50
|
+
when :time then value.to_time
|
51
|
+
when :secure then SecureString === value ? value : SecureString.new(value.value)
|
52
|
+
when :regexp then Regexp.new(value)
|
53
|
+
when :null then nil
|
54
|
+
else raise ArgumentError, 'unknown scalar type %p' % type
|
55
|
+
end
|
56
|
+
rescue RegexpError => error
|
57
|
+
raise ArgumentError, "broken regular expression - #{error.message}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def apply_mapping(node, value)
|
61
|
+
value.each_pair { |key, value| node.visit_pair(self, key, value) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def apply_sequence(node, value)
|
65
|
+
value.each { |child| node.visit_child(self, child) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_key(node, value)
|
69
|
+
case value
|
70
|
+
when String then value
|
71
|
+
when Symbol then value.to_s
|
72
|
+
else node.visit_unexpected(self, value, "expected string as key")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/play/lint.rb
ADDED
data/play/spec.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'travis/yaml'
|
3
|
+
|
4
|
+
module Travis::Yaml
|
5
|
+
def spec(**options)
|
6
|
+
Nodes::Root.spec(**options)
|
7
|
+
end
|
8
|
+
|
9
|
+
module Nodes
|
10
|
+
TEMPLATE_VARS = Notifications::Template::VARIABLES.map { |v| "`%{#{v}}`"}.join(", ")
|
11
|
+
SPEC_DESCRIPTIONS = {
|
12
|
+
Stage => "Commands that will be run on the VM.",
|
13
|
+
Notifications::Template => "Strings will be interpolated. Available variables: #{TEMPLATE_VARS}.",
|
14
|
+
%w[gemfile] => "Gemfile(s) to use.",
|
15
|
+
"gemfile" => "Gemfile to use."
|
16
|
+
}
|
17
|
+
|
18
|
+
TYPES = {
|
19
|
+
binary: 'binary string',
|
20
|
+
bool: 'boolean value',
|
21
|
+
float: 'float value',
|
22
|
+
int: 'integer value',
|
23
|
+
null: 'null value',
|
24
|
+
str: 'string',
|
25
|
+
time: 'time value',
|
26
|
+
secure: 'encrypted string',
|
27
|
+
regexp: 'regular expression'
|
28
|
+
}
|
29
|
+
|
30
|
+
class Node
|
31
|
+
def self.spec_description(*prefix)
|
32
|
+
description = SPEC_DESCRIPTIONS[prefix] || SPEC_DESCRIPTIONS[prefix.last]
|
33
|
+
ancestors.each { |a| description ||= SPEC_DESCRIPTIONS[a] }
|
34
|
+
description
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.spec_format
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.spec(*prefix, **options)
|
41
|
+
options[:experimental] ||= false
|
42
|
+
options[:required] ||= false
|
43
|
+
[{ key: prefix, description: spec_description(*prefix), format: spec_format, **options }]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Scalar
|
48
|
+
def self.spec_format(append = "")
|
49
|
+
formats = cast.any? ? cast : [default_type]
|
50
|
+
formats.map { |f| TYPES[f] ? TYPES[f]+append : f.to_s }.join(', ').gsub(/, ([^,]+)$/, ' or \1')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Version
|
55
|
+
def self.spec_description(*prefix)
|
56
|
+
super || "`#{prefix.last}` version to use."
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class VersionList
|
61
|
+
def self.spec_description(*prefix)
|
62
|
+
super || "List of `#{prefix.last}` versions to use."
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class FixedValue
|
67
|
+
def self.spec_description(*prefix)
|
68
|
+
super || begin
|
69
|
+
list = valid_values.map { |v| "`#{v}`#{" (default)" if default == v.to_s}" }.join(', ').gsub(/,([^,]+)$/, ' or\1')
|
70
|
+
if aliases.any?
|
71
|
+
alias_list = aliases.map { |k,v| "`#{k}` for `#{v}`" }.join(', ').gsub(/,([^,]+)$/, ' or\1')
|
72
|
+
list += "; or one of the known aliases: #{alias_list}"
|
73
|
+
end
|
74
|
+
"Value has to be #{list}. Setting is#{" not" if ignore_case?} case sensitive."
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Notifications::Notification
|
80
|
+
def self.spec_notification_format
|
81
|
+
"list of strings or encrypted strings"
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.spec_format
|
85
|
+
super + ", or #{spec_notification_format}, or boolean value"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class Notifications::Flowdoc
|
90
|
+
def self.spec_notification_format
|
91
|
+
"string, encrypted string"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class Sequence
|
96
|
+
def self.spec_format
|
97
|
+
"list of " << type.spec_format("s") << "; or a single " << type.spec_format if type.spec_format
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.spec(*prefix, **options)
|
101
|
+
specs = super
|
102
|
+
specs += type.spec(*prefix, '[]') unless type <= Scalar and not type < FixedValue
|
103
|
+
specs
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class Root
|
108
|
+
def self.spec(*)
|
109
|
+
super[1..-1].sort_by { |e| e[:key] }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class OpenMapping
|
114
|
+
def self.spec(*prefix, **options)
|
115
|
+
super + default_type.spec(*prefix, '*')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class Mapping
|
120
|
+
def self.spec_format(append = "")
|
121
|
+
"key value mapping#{append}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.default_spec_description
|
125
|
+
"a key value map"
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.spec_options(key, **options)
|
129
|
+
if self < LanguageSpecific and languages = LanguageSpecific::LANGUAGE_SPECIFIC[key.to_sym]
|
130
|
+
languages = languages.map { |v| "`#{v}`#{" (default)" if v.to_s == 'ruby'}" }.join(', ').gsub(/,([^,]+)$/, ' or\1')
|
131
|
+
end
|
132
|
+
{ required: required.include?(key), experimental: experimental.include?(key), languages: languages }
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.spec_aliases(*prefix, **options)
|
136
|
+
aliases.map { |k,v| { key: [*prefix, k], alias_for: [*prefix, v], **spec_options(k, **options) } }
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.spec(*prefix, **options)
|
140
|
+
specs = mapping.sort_by(&:first).inject(super) { |l, (k,v)| l + v.spec(*prefix, k, **spec_options(k, **options)) }
|
141
|
+
specs + spec_aliases(*prefix, **options)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.format_name(key)
|
148
|
+
key.join('.').gsub('.[]', '[]')
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.format_link(key)
|
152
|
+
key.join.gsub('[]', '')
|
153
|
+
end
|
154
|
+
|
155
|
+
content = <<-MARKDOWN
|
156
|
+
## The `.travis.yml` Format
|
157
|
+
Here is a list of all the options understood by travis-yaml.
|
158
|
+
|
159
|
+
Note that stricitly speaking Travis CI might not have the same understanding of these as travis-yaml has at the moment, since travis-yaml is not yet being used.
|
160
|
+
|
161
|
+
### Available Options
|
162
|
+
MARKDOWN
|
163
|
+
|
164
|
+
Travis::Yaml.spec.each do |entry|
|
165
|
+
content << "#### `" << format_name(entry[:key]) << "`\n"
|
166
|
+
content << "**This setting is only relevant if [`language`](#language) is set to #{entry[:languages]}.**\n\n" if entry[:languages]
|
167
|
+
content << "**This setting is required!**\n\n" if entry[:required]
|
168
|
+
content << "**This setting is experimental and might be removed!**\n\n" if entry[:experimental]
|
169
|
+
if other = entry[:alias_for]
|
170
|
+
content << "Alias for " << "[`#{format_name(other)}`](##{format_link(other)})." << "\n\n"
|
171
|
+
else
|
172
|
+
content << entry[:description] << "\n\n" if entry[:description]
|
173
|
+
content << "**Expected format:** " << entry[:format].capitalize << ".\n\n" if entry[:format]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
content << <<-MARKDOWN
|
178
|
+
## Generating the Specification
|
179
|
+
|
180
|
+
This file is generated. You currently update it by running `play/spec.rb`.
|
181
|
+
MARKDOWN
|
182
|
+
|
183
|
+
File.write('SPEC.md', content)
|
data/play/weblint.rb
ADDED
@@ -0,0 +1,296 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'travis/yaml'
|
3
|
+
require 'sinatra'
|
4
|
+
require 'slim'
|
5
|
+
require 'gh'
|
6
|
+
|
7
|
+
configure :production do
|
8
|
+
GH.set token: ENV.fetch('GITHUB_TOKEN')
|
9
|
+
end
|
10
|
+
|
11
|
+
get '/' do
|
12
|
+
slim ""
|
13
|
+
end
|
14
|
+
|
15
|
+
get '/style.css' do
|
16
|
+
sass :style
|
17
|
+
end
|
18
|
+
|
19
|
+
post '/' do
|
20
|
+
redirect to(params[:repo]) if params[:repo]
|
21
|
+
show_result
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/gist/:id' do
|
25
|
+
file = params[:file] || '.travis.yml'
|
26
|
+
params[:yml] = GH["/gists/#{params[:id]}"]['files'][file]['content']
|
27
|
+
show_result
|
28
|
+
end
|
29
|
+
|
30
|
+
get '/:owner/:name' do
|
31
|
+
params[:repo] = request.path_info[1..-1]
|
32
|
+
show_result
|
33
|
+
end
|
34
|
+
|
35
|
+
error GH::Error, NoMethodError do
|
36
|
+
halt 400, slim("ul.result\n li failed to fetch <b class='error'>.travis.yml</b>")
|
37
|
+
end
|
38
|
+
|
39
|
+
helpers do
|
40
|
+
def show_result
|
41
|
+
halt 400, 'needs repo or yml' unless params[:repo] or params[:yml]
|
42
|
+
branch = params[:branch] || 'master'
|
43
|
+
params[:yml] ||= GH["/repos/#{params[:repo]}/contents/.travis.yml?ref=#{branch}"]['content'].to_s.unpack('m').first
|
44
|
+
@result = Travis::Yaml.parse(params[:yml])
|
45
|
+
@matrix = Travis::Yaml::Matrix.new(@result)
|
46
|
+
slim :result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
__END__
|
51
|
+
|
52
|
+
@@ result
|
53
|
+
|
54
|
+
- if @result.nested_warnings.empty?
|
55
|
+
p.result Hooray, your .travis.yml seems to be solid!
|
56
|
+
- else
|
57
|
+
ul.result
|
58
|
+
- @result.nested_warnings.each do |key, warning|
|
59
|
+
li
|
60
|
+
- if key.any?
|
61
|
+
| in <b class="error">#{key.join('.')}</b> section:
|
62
|
+
= " "
|
63
|
+
== slim('= error', {}, error: warning).gsub(/"(.+?)"/, '<b class="error">\1</b>')
|
64
|
+
|
65
|
+
- if @matrix.size > 1
|
66
|
+
p.jobs It will generate #{@matrix.size} jobs:
|
67
|
+
ul.jobs
|
68
|
+
- @matrix.each do |job|
|
69
|
+
li = job.matrix_attributes.map { |k,v| "%s=%p" % [k,v] if v }.compact.join(', ')
|
70
|
+
|
71
|
+
@@ layout
|
72
|
+
|
73
|
+
html
|
74
|
+
head
|
75
|
+
title Validate your .travis.yml file
|
76
|
+
link rel="stylesheet" type="text/css" href="/style.css"
|
77
|
+
body
|
78
|
+
h1
|
79
|
+
a href="/" Travis WebLint
|
80
|
+
p.tagline
|
81
|
+
| Uses <a href="https://github.com/travis-ci/travis-yaml">travis-yaml</a> to check your .travis.yml config.
|
82
|
+
|
83
|
+
== yield
|
84
|
+
|
85
|
+
form class="first" action="/" method="post" accept-charset="UTF-8"
|
86
|
+
label for="repo" Enter your Github repository
|
87
|
+
input type="text" id="repo" name="repo" maxlength="80" placeholder="travis-ci/travis-yaml" required=true value=params[:repo]
|
88
|
+
input type="submit" value="Validate"
|
89
|
+
|
90
|
+
form action="/" method="post" accept-charset="UTF-8" id="ymlform"
|
91
|
+
label for="yml" Or paste your .travis.yml
|
92
|
+
textarea id="yml" name="yml" maxlength="10000" autofocus=true = params[:yml]
|
93
|
+
input type="submit" value="Validate"
|
94
|
+
|
95
|
+
javascript:
|
96
|
+
var input = document.getElementById("yml");
|
97
|
+
var form = document.getElementById("ymlform");
|
98
|
+
input.onkeydown = function(event) {
|
99
|
+
if ((event.metaKey || event.ctrlKey) && event.keyCode == 13) form.submit();
|
100
|
+
};
|
101
|
+
|
102
|
+
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
103
|
+
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
104
|
+
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
105
|
+
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
106
|
+
|
107
|
+
ga('create', 'UA-24868285-9', 'travis-ci.org');
|
108
|
+
ga('send', 'pageview');
|
109
|
+
|
110
|
+
@@ style
|
111
|
+
|
112
|
+
// http://meyerweb.com/eric/tools/css/reset/
|
113
|
+
// v2.0 | 20110126
|
114
|
+
// License: none (public domain)
|
115
|
+
|
116
|
+
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video
|
117
|
+
margin: 0
|
118
|
+
padding: 0
|
119
|
+
border: 0
|
120
|
+
font-size: 100%
|
121
|
+
font: inherit
|
122
|
+
vertical-align: baseline
|
123
|
+
|
124
|
+
// HTML5 display-role reset for older browsers
|
125
|
+
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section
|
126
|
+
display: block
|
127
|
+
|
128
|
+
body
|
129
|
+
line-height: 1
|
130
|
+
|
131
|
+
ol, ul
|
132
|
+
list-style: none
|
133
|
+
|
134
|
+
blockquote, q
|
135
|
+
quotes: none
|
136
|
+
|
137
|
+
blockquote
|
138
|
+
&:before, &:after
|
139
|
+
content: ''
|
140
|
+
content: none
|
141
|
+
|
142
|
+
q
|
143
|
+
&:before, &:after
|
144
|
+
content: ''
|
145
|
+
content: none
|
146
|
+
|
147
|
+
table
|
148
|
+
border-collapse: collapse
|
149
|
+
border-spacing: 0
|
150
|
+
|
151
|
+
// General
|
152
|
+
|
153
|
+
body
|
154
|
+
margin: 2em auto 2em auto
|
155
|
+
width: 960px
|
156
|
+
font-size: 14px
|
157
|
+
line-height: 1.4286
|
158
|
+
color: #555
|
159
|
+
background: #fff
|
160
|
+
font-family: "Helvetica Neue", Arial, Verdana, sans-serif
|
161
|
+
|
162
|
+
b
|
163
|
+
font-weight: bold
|
164
|
+
|
165
|
+
a
|
166
|
+
color: #36c
|
167
|
+
outline: none
|
168
|
+
text-decoration: underline
|
169
|
+
|
170
|
+
a:visited
|
171
|
+
color: #666
|
172
|
+
|
173
|
+
a:hover
|
174
|
+
color: #6c3
|
175
|
+
text-decoration: none
|
176
|
+
|
177
|
+
h1
|
178
|
+
color: #000
|
179
|
+
font-size: 4em
|
180
|
+
font-weight: bold
|
181
|
+
line-height: 1em
|
182
|
+
|
183
|
+
h1 a:link, h1 a:visited, h1 a:hover, h1 a:active
|
184
|
+
color: #000
|
185
|
+
text-decoration: none
|
186
|
+
|
187
|
+
h2
|
188
|
+
font-size: 2em
|
189
|
+
font-weight: bold
|
190
|
+
line-height: 2em
|
191
|
+
|
192
|
+
p.tagline
|
193
|
+
color: #777
|
194
|
+
display: block
|
195
|
+
font: italic 1.25em Georgia, Times, Serif
|
196
|
+
line-height: 1.67em
|
197
|
+
margin: 1em 0 4em 0
|
198
|
+
padding: 0 0 1.25em 0
|
199
|
+
border-bottom: 1px solid #ccc
|
200
|
+
|
201
|
+
// Result
|
202
|
+
|
203
|
+
.result
|
204
|
+
font-size: 1.5em
|
205
|
+
margin-bottom: 2em
|
206
|
+
|
207
|
+
p.result
|
208
|
+
color: #6c3
|
209
|
+
|
210
|
+
ul.result
|
211
|
+
list-style: none
|
212
|
+
|
213
|
+
ul.result li:before
|
214
|
+
content: ">"
|
215
|
+
display: inline-block
|
216
|
+
background-color: #c00
|
217
|
+
color: #fff
|
218
|
+
width: 1.4em
|
219
|
+
height: 1.4em
|
220
|
+
font-size: 40%
|
221
|
+
margin-right: 1em
|
222
|
+
text-align: center
|
223
|
+
position: relative
|
224
|
+
top: -0.5em
|
225
|
+
|
226
|
+
// jobs
|
227
|
+
|
228
|
+
.jobs
|
229
|
+
font-size: 1.5em
|
230
|
+
|
231
|
+
ul.jobs
|
232
|
+
list-style: none
|
233
|
+
margin-bottom: 2em
|
234
|
+
font-size: 1.25em
|
235
|
+
|
236
|
+
ul.jobs li:before
|
237
|
+
content: ">"
|
238
|
+
display: inline-block
|
239
|
+
background-color: #000
|
240
|
+
color: #fff
|
241
|
+
width: 1.4em
|
242
|
+
height: 1.4em
|
243
|
+
font-size: 48%
|
244
|
+
margin-right: 1em
|
245
|
+
text-align: center
|
246
|
+
position: relative
|
247
|
+
top: -0.5em
|
248
|
+
|
249
|
+
// Form
|
250
|
+
|
251
|
+
form
|
252
|
+
display: inline-block
|
253
|
+
vertical-align: top
|
254
|
+
width: 475px
|
255
|
+
|
256
|
+
form.left
|
257
|
+
margin-right: 55px
|
258
|
+
|
259
|
+
label, input
|
260
|
+
display: block
|
261
|
+
|
262
|
+
label
|
263
|
+
margin-bottom: 0.5em
|
264
|
+
|
265
|
+
input, textarea
|
266
|
+
font-size: 14px
|
267
|
+
line-height: 1.4286
|
268
|
+
color: #555
|
269
|
+
font-family: "Helvetica Neue", Arial, Verdana, sans-serif
|
270
|
+
border: 1px solid #ccc
|
271
|
+
margin: 0
|
272
|
+
|
273
|
+
input[type=text]
|
274
|
+
padding: 4px 8px
|
275
|
+
width: 400px
|
276
|
+
|
277
|
+
input[type=submit]
|
278
|
+
background: #efefef
|
279
|
+
padding: 4px 8px
|
280
|
+
margin-top: 0.5em
|
281
|
+
|
282
|
+
input[type=submit]:hover
|
283
|
+
cursor: pointer
|
284
|
+
|
285
|
+
textarea
|
286
|
+
padding: 4px 8px
|
287
|
+
width: 460px
|
288
|
+
height: 250px
|
289
|
+
|
290
|
+
// Various
|
291
|
+
|
292
|
+
.error
|
293
|
+
color: #c00
|
294
|
+
|
295
|
+
.note
|
296
|
+
margin-top: 5em
|