dropcaster 0.0.6 → 1.2.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 +5 -5
- data/.gemnasium.yml +1 -0
- data/.gitignore +12 -14
- data/.rubocop.yml +34 -0
- data/.ruby-version +1 -0
- data/.travis.yml +20 -5
- data/CONTRIBUTING.md +18 -0
- data/Dockerfile +4 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +309 -65
- data/Guardfile +8 -6
- data/LICENSE.txt +1 -1
- data/README.markdown +43 -37
- data/Rakefile +46 -2
- data/{TODO → TODO.markdown} +0 -0
- data/VISION.markdown +52 -18
- data/Vagrantfile +10 -0
- data/bin/dropcaster +57 -53
- data/bin/lstags +29 -28
- data/dropcaster.gemspec +31 -20
- data/lib/dropcaster.rb +2 -15
- data/lib/dropcaster/channel.rb +54 -43
- data/lib/dropcaster/channel_file_locator.rb +8 -5
- data/lib/dropcaster/contributors.rb +39 -0
- data/lib/dropcaster/errors.rb +6 -4
- data/lib/dropcaster/item.rb +20 -14
- data/lib/dropcaster/log_formatter.rb +10 -0
- data/lib/dropcaster/logging.rb +13 -0
- data/lib/dropcaster/version.rb +3 -1
- data/templates/channel.html.erb +3 -6
- data/templates/channel.rss.erb +6 -5
- data/test/Vagrantfile +3 -1
- data/test/bin/vagrant-status +37 -31
- data/test/extensions/windows.rb +3 -1
- data/test/fixtures/extension.MP3 +0 -0
- data/test/fixtures/special &.mp3 +0 -0
- data/test/fixtures/test_template.json.erb +3 -4
- data/test/helper.rb +4 -1
- data/test/unit/test_app.rb +29 -24
- data/test/unit/test_channel.rb +9 -5
- data/test/unit/test_channel_locator.rb +8 -5
- data/test/unit/test_channel_xml.rb +29 -9
- data/test/unit/{test_item.rb → test_itunes_item.rb} +5 -6
- data/website/.gitignore +2 -0
- data/website/README.markdown +8 -0
- data/website/_config.yml +14 -0
- data/website/_front_matter/contributing.yaml +5 -0
- data/website/_front_matter/index.yaml +3 -0
- data/website/_front_matter/vision.yaml +5 -0
- data/website/_includes/footer.html +55 -0
- data/website/_includes/head.html +11 -0
- data/website/_includes/header.html +27 -0
- data/website/_layouts/default.html +20 -0
- data/website/_layouts/page.html +14 -0
- data/website/_layouts/post.html +15 -0
- data/website/_sass/_base.scss +204 -0
- data/website/_sass/_layout.scss +236 -0
- data/website/_sass/_syntax-highlighting.scss +67 -0
- data/website/css/main.scss +49 -0
- data/website/deploy.sh +23 -0
- data/website/feed.xml +30 -0
- metadata +150 -23
data/test/unit/test_channel.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'helper'
|
2
4
|
|
3
5
|
class TestChannel < MiniTest::Test
|
@@ -9,7 +11,7 @@ class TestChannel < MiniTest::Test
|
|
9
11
|
end
|
10
12
|
|
11
13
|
def test_item_count
|
12
|
-
assert_equal(
|
14
|
+
assert_equal(NUMBER_OF_MP3_FILES, @channel.items.size)
|
13
15
|
end
|
14
16
|
|
15
17
|
def test_channel
|
@@ -47,7 +49,9 @@ class TestChannel < MiniTest::Test
|
|
47
49
|
end
|
48
50
|
|
49
51
|
def test_channel_explicit_nil
|
50
|
-
|
52
|
+
@options[:explicit] = nil
|
53
|
+
channel = Dropcaster::Channel.new(FIXTURES_DIR, @options)
|
54
|
+
assert_nil(channel.explicit)
|
51
55
|
end
|
52
56
|
|
53
57
|
def test_channel_explicit_clean
|
@@ -62,19 +66,19 @@ class TestChannel < MiniTest::Test
|
|
62
66
|
|
63
67
|
def test_raise_on_missing_title
|
64
68
|
assert_raises Dropcaster::MissingAttributeError do
|
65
|
-
Dropcaster::Channel.new(FIXTURES_DIR, {:
|
69
|
+
Dropcaster::Channel.new(FIXTURES_DIR, { url: 'bar', description: 'foobar' })
|
66
70
|
end
|
67
71
|
end
|
68
72
|
|
69
73
|
def test_raise_on_missing_url
|
70
74
|
assert_raises Dropcaster::MissingAttributeError do
|
71
|
-
Dropcaster::Channel.new(FIXTURES_DIR, {:
|
75
|
+
Dropcaster::Channel.new(FIXTURES_DIR, { title: 'foo', description: 'foobar' })
|
72
76
|
end
|
73
77
|
end
|
74
78
|
|
75
79
|
def test_raise_on_missing_description
|
76
80
|
assert_raises Dropcaster::MissingAttributeError do
|
77
|
-
Dropcaster::Channel.new(FIXTURES_DIR, {:
|
81
|
+
Dropcaster::Channel.new(FIXTURES_DIR, { title: 'foo', url: 'bar' })
|
78
82
|
end
|
79
83
|
end
|
80
84
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'helper'
|
2
4
|
require 'tmpdir'
|
3
5
|
require 'pry'
|
@@ -8,6 +10,7 @@ class TestChannelLocator < MiniTest::Test
|
|
8
10
|
def setup
|
9
11
|
@temp_dir = Dir.mktmpdir
|
10
12
|
end
|
13
|
+
|
11
14
|
def teardown
|
12
15
|
FileUtils.remove_entry_secure(@temp_dir)
|
13
16
|
end
|
@@ -30,7 +33,7 @@ class TestChannelLocator < MiniTest::Test
|
|
30
33
|
end
|
31
34
|
|
32
35
|
def test_array_of_files_same_dir
|
33
|
-
sources =
|
36
|
+
sources = []
|
34
37
|
sources << File.join(@temp_dir, 'file1.mp3')
|
35
38
|
sources << File.join(@temp_dir, 'file2.mp3')
|
36
39
|
sources << File.join(@temp_dir, 'file3.mp3')
|
@@ -39,7 +42,7 @@ class TestChannelLocator < MiniTest::Test
|
|
39
42
|
end
|
40
43
|
|
41
44
|
def test_array_of_files_different_dir
|
42
|
-
sources =
|
45
|
+
sources = []
|
43
46
|
sources << File.join(@temp_dir, 'foo', 'file1.mp3')
|
44
47
|
sources << File.join(@temp_dir, 'bar', 'file1.mp3')
|
45
48
|
sources << File.join(@temp_dir, 'baz', 'file1.mp3')
|
@@ -54,9 +57,9 @@ class TestChannelLocator < MiniTest::Test
|
|
54
57
|
end
|
55
58
|
|
56
59
|
def test_array_with_more_than_a_single_directory
|
57
|
-
Dir.mktmpdir{|tmp_dir1|
|
58
|
-
Dir.mktmpdir{|tmp_dir2|
|
59
|
-
sources =
|
60
|
+
Dir.mktmpdir { |tmp_dir1|
|
61
|
+
Dir.mktmpdir { |tmp_dir2|
|
62
|
+
sources = []
|
60
63
|
sources << File.join(tmp_dir1, 'another_dir')
|
61
64
|
sources << File.join(tmp_dir2, 'another_dir')
|
62
65
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'helper'
|
2
4
|
require 'xml/libxml'
|
3
5
|
require 'pry'
|
@@ -14,7 +16,7 @@ class TestChannelXML < MiniTest::Test
|
|
14
16
|
# Returnes the XML node for the channel passed as XML string
|
15
17
|
#
|
16
18
|
def channel_node(rss)
|
17
|
-
XML::Document.string(rss).find(
|
19
|
+
XML::Document.string(rss).find('//rss/channel').first
|
18
20
|
end
|
19
21
|
|
20
22
|
#
|
@@ -27,11 +29,10 @@ class TestChannelXML < MiniTest::Test
|
|
27
29
|
Dropcaster::Channel.new(FIXTURES_DIR, @options.dup).to_rss
|
28
30
|
end
|
29
31
|
|
30
|
-
def
|
31
|
-
item = @channel.find(
|
32
|
+
def test_itunes_item
|
33
|
+
item = @channel.find('item[title = "iTunes Name"]').first
|
32
34
|
assert(item)
|
33
35
|
|
34
|
-
assert_equal('iTunes Name', item.find('title').first.content)
|
35
36
|
assert_equal('iTunes Artist', item.find('itunes:author', NS_ITUNES).first.content)
|
36
37
|
assert_equal('iTunes Description (Video Pane)', item.find('itunes:summary', NS_ITUNES).first.content)
|
37
38
|
assert_equal('http://www.example.com/podcasts/everything/AllAboutEverything.jpg', item.find('itunes:image', NS_ITUNES).first['href'])
|
@@ -51,12 +52,31 @@ class TestChannelXML < MiniTest::Test
|
|
51
52
|
assert_equal('3', item.find('itunes:duration', NS_ITUNES).first.content)
|
52
53
|
end
|
53
54
|
|
55
|
+
def test_special_ampersand_item
|
56
|
+
# in the actual XML, this is "special &.mp3", but it gets interpreted by XML::Document
|
57
|
+
# if it was just "special &.mp3", it would be invalid XML and we wouldn't get this far
|
58
|
+
item = @channel.find('item[title = "test/fixtures/special &.mp3"]').first
|
59
|
+
assert(item)
|
60
|
+
|
61
|
+
enclosure = item.find('enclosure').first
|
62
|
+
assert(enclosure)
|
63
|
+
assert_equal('http://www.example.com/podcasts/everything/test/fixtures/special%20%26.mp3', enclosure['url'])
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_uppercase_extension_item
|
67
|
+
item = @channel.find('item[title = "test/fixtures/extension.MP3"]').first
|
68
|
+
assert(item)
|
69
|
+
|
70
|
+
enclosure = item.find('enclosure').first
|
71
|
+
assert(enclosure)
|
72
|
+
assert_equal('http://www.example.com/podcasts/everything/test/fixtures/extension.MP3', enclosure['url'])
|
73
|
+
end
|
74
|
+
|
54
75
|
def test_attributes_mandatory
|
55
|
-
options = {:
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
}
|
76
|
+
options = { title: 'Test Channel',
|
77
|
+
url: 'http://www.example.com/',
|
78
|
+
description: 'A test channel',
|
79
|
+
enclosures_url: 'http://www.example.com/foo/bar' }
|
60
80
|
|
61
81
|
channel = channel_node(Dropcaster::Channel.new(FIXTURES_DIR, options).to_rss)
|
62
82
|
assert_equal('Test Channel', channel.find('title').first.content)
|
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'helper'
|
2
4
|
|
3
|
-
class
|
5
|
+
class TestItunesItem < MiniTest::Test
|
4
6
|
include DropcasterTest
|
5
7
|
|
6
8
|
def setup
|
@@ -12,7 +14,7 @@ class TestItem < MiniTest::Test
|
|
12
14
|
assert_equal(58119, @item.file_size)
|
13
15
|
assert_equal('77bf84447c0f69ce4a33a18b0ae1e030b82010de', @item.uuid)
|
14
16
|
assert_equal(File.mtime(FIXTURE_ITUNES_MP3).to_i, @item.pub_date.to_i)
|
15
|
-
assert_equal('test/fixtures/iTunes.mp3', @item.
|
17
|
+
assert_equal('test/fixtures/iTunes.mp3', @item.file_path.to_s)
|
16
18
|
end
|
17
19
|
|
18
20
|
def test_tag
|
@@ -38,14 +40,11 @@ class TestItem < MiniTest::Test
|
|
38
40
|
assert_equal('iTunes Grouping', @item.tag2.TT1)
|
39
41
|
assert_equal('iTunes Description (Video Pane)', @item.tag2.TT3)
|
40
42
|
assert_equal('iTunes Composer', @item.tag2.TCM)
|
43
|
+
assert_equal('iTunes Comments (Info Pane)', @item.tag2.COM[1])
|
41
44
|
end
|
42
45
|
|
43
46
|
def test_lyrics
|
44
47
|
assert_equal(1, @item.lyrics.size)
|
45
48
|
assert_equal("iTunes Lyrics Line 1\niTunes Lyrics Line 2", @item.lyrics['eng'])
|
46
49
|
end
|
47
|
-
|
48
|
-
def test_tag2_comment
|
49
|
-
assert_equal('iTunes Comments (Info Pane)', @item.tag2.COM[1])
|
50
|
-
end
|
51
50
|
end
|
data/website/.gitignore
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
Markdown pages are generated from the README.markdown etc. and then served my Jekyll. Therefore, after each change to the source files, we need to regenerate the pages using the following rake task:
|
2
|
+
|
3
|
+
rake clobber web:generate
|
4
|
+
|
5
|
+
Run local test site:
|
6
|
+
|
7
|
+
# http://jekyllrb.com/docs/github-pages/
|
8
|
+
jekyll serve --baseurl ''
|
data/website/_config.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Site settings
|
2
|
+
title: Dropcaster
|
3
|
+
email: nerab@gmx.at
|
4
|
+
description: > # this means to ignore newlines until "baseurl:"
|
5
|
+
Dropcaster is a podcast feed generator for the command line. It is most
|
6
|
+
simple to use with Dropbox, but works equally well with any other (static
|
7
|
+
file) web hoster.
|
8
|
+
baseurl: "/dropcaster" # the subpath of your site, e.g. /blog/
|
9
|
+
# url: "http://yourdomain.com" # the base hostname & protocol for your site
|
10
|
+
# twitter_username: jekyllrb
|
11
|
+
github_username: nerab
|
12
|
+
|
13
|
+
# Build settings
|
14
|
+
markdown: kramdown
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<footer class="site-footer">
|
2
|
+
|
3
|
+
<div class="wrapper">
|
4
|
+
|
5
|
+
<h2 class="footer-heading">{{ site.title }}</h2>
|
6
|
+
|
7
|
+
<div class="footer-col-wrapper">
|
8
|
+
<div class="footer-col footer-col-1">
|
9
|
+
<ul class="contact-list">
|
10
|
+
<li>{{ site.title }}</li>
|
11
|
+
<li><a href="mailto:{{ site.email }}">{{ site.email }}</a></li>
|
12
|
+
</ul>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="footer-col footer-col-2">
|
16
|
+
<ul class="social-media-list">
|
17
|
+
{% if site.github_username %}
|
18
|
+
<li>
|
19
|
+
<a href="https://github.com/{{ site.github_username }}">
|
20
|
+
<span class="icon icon--github">
|
21
|
+
<svg viewBox="0 0 16 16">
|
22
|
+
<path fill="#828282" d="M7.999,0.431c-4.285,0-7.76,3.474-7.76,7.761 c0,3.428,2.223,6.337,5.307,7.363c0.388,0.071,0.53-0.168,0.53-0.374c0-0.184-0.007-0.672-0.01-1.32 c-2.159,0.469-2.614-1.04-2.614-1.04c-0.353-0.896-0.862-1.135-0.862-1.135c-0.705-0.481,0.053-0.472,0.053-0.472 c0.779,0.055,1.189,0.8,1.189,0.8c0.692,1.186,1.816,0.843,2.258,0.645c0.071-0.502,0.271-0.843,0.493-1.037 C4.86,11.425,3.049,10.76,3.049,7.786c0-0.847,0.302-1.54,0.799-2.082C3.768,5.507,3.501,4.718,3.924,3.65 c0,0,0.652-0.209,2.134,0.796C6.677,4.273,7.34,4.187,8,4.184c0.659,0.003,1.323,0.089,1.943,0.261 c1.482-1.004,2.132-0.796,2.132-0.796c0.423,1.068,0.157,1.857,0.077,2.054c0.497,0.542,0.798,1.235,0.798,2.082 c0,2.981-1.814,3.637-3.543,3.829c0.279,0.24,0.527,0.713,0.527,1.437c0,1.037-0.01,1.874-0.01,2.129 c0,0.208,0.14,0.449,0.534,0.373c3.081-1.028,5.302-3.935,5.302-7.362C15.76,3.906,12.285,0.431,7.999,0.431z"/>
|
23
|
+
</svg>
|
24
|
+
</span>
|
25
|
+
|
26
|
+
<span class="username">{{ site.github_username }}</span>
|
27
|
+
</a>
|
28
|
+
</li>
|
29
|
+
{% endif %}
|
30
|
+
|
31
|
+
{% if site.twitter_username %}
|
32
|
+
<li>
|
33
|
+
<a href="https://twitter.com/{{ site.twitter_username }}">
|
34
|
+
<span class="icon icon--twitter">
|
35
|
+
<svg viewBox="0 0 16 16">
|
36
|
+
<path fill="#828282" d="M15.969,3.058c-0.586,0.26-1.217,0.436-1.878,0.515c0.675-0.405,1.194-1.045,1.438-1.809
|
37
|
+
c-0.632,0.375-1.332,0.647-2.076,0.793c-0.596-0.636-1.446-1.033-2.387-1.033c-1.806,0-3.27,1.464-3.27,3.27 c0,0.256,0.029,0.506,0.085,0.745C5.163,5.404,2.753,4.102,1.14,2.124C0.859,2.607,0.698,3.168,0.698,3.767 c0,1.134,0.577,2.135,1.455,2.722C1.616,6.472,1.112,6.325,0.671,6.08c0,0.014,0,0.027,0,0.041c0,1.584,1.127,2.906,2.623,3.206 C3.02,9.402,2.731,9.442,2.433,9.442c-0.211,0-0.416-0.021-0.615-0.059c0.416,1.299,1.624,2.245,3.055,2.271 c-1.119,0.877-2.529,1.4-4.061,1.4c-0.264,0-0.524-0.015-0.78-0.046c1.447,0.928,3.166,1.469,5.013,1.469 c6.015,0,9.304-4.983,9.304-9.304c0-0.142-0.003-0.283-0.009-0.423C14.976,4.29,15.531,3.714,15.969,3.058z"/>
|
38
|
+
</svg>
|
39
|
+
</span>
|
40
|
+
|
41
|
+
<span class="username">{{ site.twitter_username }}</span>
|
42
|
+
</a>
|
43
|
+
</li>
|
44
|
+
{% endif %}
|
45
|
+
</ul>
|
46
|
+
</div>
|
47
|
+
|
48
|
+
<div class="footer-col footer-col-3">
|
49
|
+
<p class="text">{{ site.description }}</p>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
|
53
|
+
</div>
|
54
|
+
|
55
|
+
</footer>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<head>
|
2
|
+
<meta charset="utf-8">
|
3
|
+
<meta name="viewport" content="width=device-width initial-scale=1" />
|
4
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
5
|
+
|
6
|
+
<title>{{ site.title }}{% if page.title %} - {{ page.title }}{% endif %}</title>
|
7
|
+
<meta name="description" content="{{ site.description }}">
|
8
|
+
|
9
|
+
<link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}">
|
10
|
+
<link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
|
11
|
+
</head>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<header class="site-header">
|
2
|
+
|
3
|
+
<div class="wrapper">
|
4
|
+
|
5
|
+
<a class="site-title" href="{{ site.baseurl }}/">{{ site.title }}</a>
|
6
|
+
|
7
|
+
<nav class="site-nav">
|
8
|
+
<a href="#" class="menu-icon">
|
9
|
+
<svg viewBox="0 0 18 15">
|
10
|
+
<path fill="#424242" d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z"/>
|
11
|
+
<path fill="#424242" d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z"/>
|
12
|
+
<path fill="#424242" d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z"/>
|
13
|
+
</svg>
|
14
|
+
</a>
|
15
|
+
|
16
|
+
<div class="trigger">
|
17
|
+
{% for page in site.pages %}
|
18
|
+
{% if page.title %}
|
19
|
+
<a class="page-link" href="{{ page.url | prepend: site.baseurl }}">{{ page.title }}</a>
|
20
|
+
{% endif %}
|
21
|
+
{% endfor %}
|
22
|
+
</div>
|
23
|
+
</nav>
|
24
|
+
|
25
|
+
</div>
|
26
|
+
|
27
|
+
</header>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
|
4
|
+
{% include head.html %}
|
5
|
+
|
6
|
+
<body>
|
7
|
+
|
8
|
+
{% include header.html %}
|
9
|
+
|
10
|
+
<div class="page-content">
|
11
|
+
<div class="wrapper">
|
12
|
+
{{ content }}
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
{% include footer.html %}
|
17
|
+
|
18
|
+
</body>
|
19
|
+
|
20
|
+
</html>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
layout: default
|
3
|
+
---
|
4
|
+
<div class="post">
|
5
|
+
|
6
|
+
<header class="post-header">
|
7
|
+
<h1 class="post-title">{{ page.title }}</h1>
|
8
|
+
<p class="post-meta">{{ page.date | date: "%b %-d, %Y" }}{% if page.author %} • {{ page.author }}{% endif %}{% if page.meta %} • {{ page.meta }}{% endif %}</p>
|
9
|
+
</header>
|
10
|
+
|
11
|
+
<article class="post-content">
|
12
|
+
{{ content }}
|
13
|
+
</article>
|
14
|
+
|
15
|
+
</div>
|
@@ -0,0 +1,204 @@
|
|
1
|
+
/**
|
2
|
+
* Reset some basic elements
|
3
|
+
*/
|
4
|
+
body, h1, h2, h3, h4, h5, h6,
|
5
|
+
p, blockquote, pre, hr,
|
6
|
+
dl, dd, ol, ul, figure {
|
7
|
+
margin: 0;
|
8
|
+
padding: 0;
|
9
|
+
}
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Basic styling
|
15
|
+
*/
|
16
|
+
body {
|
17
|
+
font-family: $base-font-family;
|
18
|
+
font-size: $base-font-size;
|
19
|
+
line-height: $base-line-height;
|
20
|
+
font-weight: 300;
|
21
|
+
color: $text-color;
|
22
|
+
background-color: $background-color;
|
23
|
+
-webkit-text-size-adjust: 100%;
|
24
|
+
}
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Set `margin-bottom` to maintain vertical rhythm
|
30
|
+
*/
|
31
|
+
h1, h2, h3, h4, h5, h6,
|
32
|
+
p, blockquote, pre,
|
33
|
+
ul, ol, dl, figure,
|
34
|
+
%vertical-rhythm {
|
35
|
+
margin-bottom: $spacing-unit / 2;
|
36
|
+
}
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Images
|
42
|
+
*/
|
43
|
+
img {
|
44
|
+
max-width: 100%;
|
45
|
+
vertical-align: middle;
|
46
|
+
}
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Figures
|
52
|
+
*/
|
53
|
+
figure > img {
|
54
|
+
display: block;
|
55
|
+
}
|
56
|
+
|
57
|
+
figcaption {
|
58
|
+
font-size: $small-font-size;
|
59
|
+
}
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
/**
|
64
|
+
* Lists
|
65
|
+
*/
|
66
|
+
ul, ol {
|
67
|
+
margin-left: $spacing-unit;
|
68
|
+
}
|
69
|
+
|
70
|
+
li {
|
71
|
+
> ul,
|
72
|
+
> ol {
|
73
|
+
margin-bottom: 0;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Headings
|
81
|
+
*/
|
82
|
+
h1, h2, h3, h4, h5, h6 {
|
83
|
+
font-weight: 300;
|
84
|
+
}
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Links
|
90
|
+
*/
|
91
|
+
a {
|
92
|
+
color: $brand-color;
|
93
|
+
text-decoration: none;
|
94
|
+
|
95
|
+
&:visited {
|
96
|
+
color: darken($brand-color, 15%);
|
97
|
+
}
|
98
|
+
|
99
|
+
&:hover {
|
100
|
+
color: $text-color;
|
101
|
+
text-decoration: underline;
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Blockquotes
|
109
|
+
*/
|
110
|
+
blockquote {
|
111
|
+
color: $grey-color;
|
112
|
+
border-left: 4px solid $grey-color-light;
|
113
|
+
padding-left: $spacing-unit / 2;
|
114
|
+
font-size: 18px;
|
115
|
+
letter-spacing: -1px;
|
116
|
+
font-style: italic;
|
117
|
+
|
118
|
+
> :last-child {
|
119
|
+
margin-bottom: 0;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
|
124
|
+
|
125
|
+
/**
|
126
|
+
* Code formatting
|
127
|
+
*/
|
128
|
+
pre,
|
129
|
+
code {
|
130
|
+
font-size: 15px;
|
131
|
+
border: 1px solid $grey-color-light;
|
132
|
+
border-radius: 3px;
|
133
|
+
background-color: #eef;
|
134
|
+
}
|
135
|
+
|
136
|
+
code {
|
137
|
+
padding: 1px 5px;
|
138
|
+
}
|
139
|
+
|
140
|
+
pre {
|
141
|
+
padding: 8px 12px;
|
142
|
+
overflow-x: scroll;
|
143
|
+
|
144
|
+
> code {
|
145
|
+
border: 0;
|
146
|
+
padding-right: 0;
|
147
|
+
padding-left: 0;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
/**
|
154
|
+
* Wrapper
|
155
|
+
*/
|
156
|
+
.wrapper {
|
157
|
+
max-width: -webkit-calc(800px - (#{$spacing-unit} * 2));
|
158
|
+
max-width: calc(800px - (#{$spacing-unit} * 2));
|
159
|
+
margin-right: auto;
|
160
|
+
margin-left: auto;
|
161
|
+
padding-right: $spacing-unit;
|
162
|
+
padding-left: $spacing-unit;
|
163
|
+
@extend %clearfix;
|
164
|
+
|
165
|
+
@include media-query($on-laptop) {
|
166
|
+
max-width: -webkit-calc(800px - (#{$spacing-unit}));
|
167
|
+
max-width: calc(800px - (#{$spacing-unit}));
|
168
|
+
padding-right: $spacing-unit / 2;
|
169
|
+
padding-left: $spacing-unit / 2;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
|
174
|
+
|
175
|
+
/**
|
176
|
+
* Clearfix
|
177
|
+
*/
|
178
|
+
%clearfix {
|
179
|
+
|
180
|
+
&:after {
|
181
|
+
content: "";
|
182
|
+
display: table;
|
183
|
+
clear: both;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
|
188
|
+
|
189
|
+
/**
|
190
|
+
* Icons
|
191
|
+
*/
|
192
|
+
.icon {
|
193
|
+
|
194
|
+
> svg {
|
195
|
+
display: inline-block;
|
196
|
+
width: 16px;
|
197
|
+
height: 16px;
|
198
|
+
vertical-align: middle;
|
199
|
+
|
200
|
+
path {
|
201
|
+
fill: $grey-color;
|
202
|
+
}
|
203
|
+
}
|
204
|
+
}
|