ecrire 0.26.3 → 0.27.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 +4 -4
- data/.gitignore +3 -1
- data/bin/ecrire +4 -0
- data/lib/ecrire/app/assets/javascripts/admin/editor/content.coffee +20 -2
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/code.coffee +78 -24
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/heading.coffee +1 -1
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/image.coffee +1 -1
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/link.coffee +1 -1
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/lists.coffee +1 -1
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/word.coffee +1 -1
- data/lib/ecrire/app/assets/javascripts/admin/posts/header.coffee +1 -2
- data/lib/ecrire/app/assets/stylesheets/admin/base.css.scss +12 -13
- data/lib/ecrire/app/assets/stylesheets/admin/header.css.scss +5 -1
- data/lib/ecrire/app/assets/stylesheets/admin/navigation.css.scss +60 -49
- data/lib/ecrire/app/assets/stylesheets/admin/posts.css.scss +2 -2
- data/lib/ecrire/app/assets/stylesheets/editor/code.css.scss +21 -2
- data/lib/ecrire/app/assets/stylesheets/vendor/prism.scss +0 -13
- data/lib/ecrire/app/models/admin/post.rb +8 -1
- data/lib/ecrire/app/models/post.rb +1 -0
- data/lib/ecrire/app/views/admin/posts/titles/_title.html.erb +4 -2
- data/lib/ecrire/commands/server.rb +1 -0
- data/lib/ecrire/markdown/node.rb +1 -1
- data/lib/ecrire/markdown/nodes/code.rb +63 -0
- data/lib/ecrire/markdown/parsers/code.rb +39 -18
- data/lib/ecrire/onboarding/assets/stylesheets/browser/base.scss +0 -0
- data/lib/ecrire/onboarding/assets/stylesheets/browser.scss +3 -0
- data/lib/ecrire/onboarding/assets/stylesheets/mobile/base.scss +0 -0
- data/lib/ecrire/onboarding/assets/stylesheets/mobile.scss +3 -0
- data/lib/ecrire/onboarding/assets/stylesheets/{theme.css.scss → shared/base.scss} +1 -0
- data/lib/ecrire/onboarding/assets/stylesheets/{welcome.css.scss → shared/welcome.scss} +1 -0
- data/lib/ecrire/onboarding/assets/stylesheets/tablet/base.scss +0 -0
- data/lib/ecrire/onboarding/assets/stylesheets/tablet.scss +4 -0
- data/lib/ecrire/onboarding/views/layouts/application.html.erb +6 -4
- data/lib/ecrire/theme/template/Gemfile +1 -1
- data/lib/ecrire/theme/template/assets/javascripts/application.js.coffee +1 -0
- data/lib/ecrire/version.rb +1 -1
- data/test/editor/models/post_test.rb +65 -0
- data/test/markdown/markdown_test.rb +13 -0
- metadata +18 -11
- data/lib/ecrire/markdown/nodes/code_block.rb +0 -30
- /data/lib/ecrire/onboarding/assets/javascripts/{theme.js → application.js} +0 -0
- /data/lib/ecrire/onboarding/assets/stylesheets/{complete.css.scss → shared/complete.scss} +0 -0
- /data/lib/ecrire/onboarding/assets/stylesheets/{fonts.css.scss → shared/fonts.scss} +0 -0
- /data/lib/ecrire/onboarding/assets/stylesheets/{form.css.scss → shared/form.scss} +0 -0
- /data/lib/ecrire/onboarding/assets/stylesheets/{message.css.scss → shared/message.scss} +0 -0
- /data/lib/ecrire/onboarding/assets/stylesheets/{terminal.css.scss → shared/terminal.scss} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6ba5385d6716080f7627c1fca5a2a6402f86a0d3
|
|
4
|
+
data.tar.gz: 8da73ff93cd99d67cce527fe12e826a19bdceb12
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dfe75295b9fd1430d0d5a6e35486c0f928c81f695da95dac37810386daa8db00a7077e88de1c6e0495ffa707d159f1bda1783dc1b9b6e76a759455c749fc6ed2
|
|
7
|
+
data.tar.gz: 89a2986375f70b026f629647ac4a2f8fa84400c37432bc4fe97fb9c48ef475753cc9d2d321ead18b634638001f2adaa1599d2d3067af3a17ce770bbcbda58748
|
data/.gitignore
CHANGED
data/bin/ecrire
CHANGED
|
@@ -21,6 +21,10 @@ parser = OptionParser.new do |opts|
|
|
|
21
21
|
options[:Port] = port
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
opts.on '-b IP', '--binding=IP', "Binds Rails to the specified IP.", "Default: localhost" do |ip|
|
|
25
|
+
options[:Host] = ip
|
|
26
|
+
end
|
|
27
|
+
|
|
24
28
|
opts.separator ''
|
|
25
29
|
opts.separator 'ecrire console'
|
|
26
30
|
|
|
@@ -3,7 +3,7 @@ ObserveJS.bind 'Editor.Content', class @Editor
|
|
|
3
3
|
loaded: =>
|
|
4
4
|
@on 'keydown', @linefeed
|
|
5
5
|
|
|
6
|
-
@parsers = Editor.Parsers
|
|
6
|
+
@parsers = Editor.Parsers.sort()
|
|
7
7
|
|
|
8
8
|
@extensions = Editor.Extensions.map (ext) =>
|
|
9
9
|
new ext(this)
|
|
@@ -357,5 +357,23 @@ class Mutations
|
|
|
357
357
|
else
|
|
358
358
|
'none'
|
|
359
359
|
|
|
360
|
-
|
|
360
|
+
class Parsers
|
|
361
|
+
constructor: ->
|
|
362
|
+
@store = {}
|
|
363
|
+
|
|
364
|
+
add: (name, kls) =>
|
|
365
|
+
@store[name] = kls
|
|
366
|
+
|
|
367
|
+
sort: =>
|
|
368
|
+
[
|
|
369
|
+
@store.lists
|
|
370
|
+
@store.code
|
|
371
|
+
@store.image
|
|
372
|
+
@store.headers
|
|
373
|
+
@store.link
|
|
374
|
+
@store.word
|
|
375
|
+
]
|
|
376
|
+
|
|
377
|
+
Editor.Parsers = new Parsers()
|
|
378
|
+
|
|
361
379
|
Editor.Extensions = []
|
|
@@ -1,43 +1,97 @@
|
|
|
1
|
-
Editor.Parsers.
|
|
2
|
-
|
|
1
|
+
Editor.Parsers.add 'code', class
|
|
2
|
+
rules:
|
|
3
|
+
start: /((~{3,})([a-z]+)?)(.+)?/i
|
|
3
4
|
|
|
4
5
|
constructor: (node) ->
|
|
5
|
-
@
|
|
6
|
-
@tildeCount = 0
|
|
6
|
+
@walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT)
|
|
7
7
|
|
|
8
8
|
isMatched: =>
|
|
9
|
-
@
|
|
10
|
-
if @match? && @match[0]?
|
|
11
|
-
@tildeCount = @match[2].length
|
|
12
|
-
@collectSiblingsUntilMatched(@tildeCount, @nodes[0])
|
|
9
|
+
@matches().length > 0
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
matches: =>
|
|
12
|
+
matches = []
|
|
13
|
+
while node = @walker.nextNode()
|
|
14
|
+
match = @extract(node)
|
|
15
|
+
if match?
|
|
16
|
+
matches.push match
|
|
17
|
+
@walker.currentNode = match.contentNodes[match.contentNodes.length - 1]
|
|
18
|
+
|
|
19
|
+
@matches = ->
|
|
20
|
+
matches
|
|
21
|
+
@matches()
|
|
22
|
+
|
|
23
|
+
extract: (node) =>
|
|
24
|
+
match = @rules.start.exec(node.textContent)
|
|
25
|
+
|
|
26
|
+
if !match?
|
|
27
|
+
return null
|
|
28
|
+
|
|
29
|
+
return @split(match, node)
|
|
30
|
+
|
|
31
|
+
split: (match, node) ->
|
|
32
|
+
node = node.splitText(match.index)
|
|
33
|
+
match.contentNodes = [node]
|
|
34
|
+
|
|
35
|
+
if match.index > 0
|
|
36
|
+
match.inline = true
|
|
37
|
+
|
|
38
|
+
match.node = node
|
|
39
|
+
match.tildeCount = match[2].length
|
|
40
|
+
match.contentNodes.push node.splitText(match.tildeCount)
|
|
41
|
+
|
|
42
|
+
if m = @findCloseTag(match.contentNodes[1], match.tildeCount)
|
|
43
|
+
match.contentNodes[1].splitText(m.index + m[0].length)
|
|
44
|
+
match.closeNode = match.contentNodes[match.contentNodes.length - 1]
|
|
45
|
+
|
|
46
|
+
if match.closeNode?
|
|
47
|
+
match.inline = true
|
|
48
|
+
return match
|
|
49
|
+
|
|
50
|
+
node = @walker.root.nextSibling
|
|
16
51
|
while node
|
|
17
|
-
|
|
18
|
-
|
|
52
|
+
|
|
53
|
+
sibling = node.nextSibling
|
|
19
54
|
node.remove()
|
|
20
55
|
|
|
21
|
-
|
|
56
|
+
textNode = document.createTextNode('\n' + node.textContent)
|
|
57
|
+
match.contentNodes.push textNode
|
|
22
58
|
|
|
23
|
-
if
|
|
59
|
+
if m = @findCloseTag(textNode, match.tildeCount)
|
|
60
|
+
match.closeNode = node
|
|
24
61
|
break
|
|
25
62
|
|
|
26
|
-
node =
|
|
63
|
+
node = sibling
|
|
64
|
+
|
|
65
|
+
return match
|
|
66
|
+
|
|
67
|
+
findCloseTag: (node, tildeCount) =>
|
|
68
|
+
rule = new RegExp("(~{#{tildeCount},})", 'i')
|
|
69
|
+
rule.exec(node.textContent)
|
|
70
|
+
|
|
27
71
|
|
|
28
72
|
|
|
29
73
|
render: =>
|
|
30
|
-
|
|
31
|
-
|
|
74
|
+
root = @walker.root
|
|
75
|
+
|
|
76
|
+
for match in @matches()
|
|
77
|
+
|
|
78
|
+
code = "<code as='Editor.Code'>".toHTML()
|
|
79
|
+
match.node.parentElement.replaceChild(code, match.node)
|
|
80
|
+
|
|
81
|
+
if match.inline?
|
|
82
|
+
parent = match.node.parentElement
|
|
83
|
+
|
|
84
|
+
code.appendChild(line) for line in match.contentNodes
|
|
32
85
|
|
|
33
|
-
if @match[3]?
|
|
34
|
-
code.classList.add("language-#{@match[3]}")
|
|
35
86
|
|
|
36
|
-
|
|
87
|
+
if match[3]?
|
|
88
|
+
code.classList.add("language-#{match[3]}")
|
|
37
89
|
|
|
38
|
-
|
|
90
|
+
Prism.highlightElement(code)
|
|
39
91
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
92
|
+
if !match.inline?
|
|
93
|
+
pre = "<pre>".toHTML()
|
|
94
|
+
pre.appendChild(code)
|
|
95
|
+
root = pre
|
|
43
96
|
|
|
97
|
+
root
|
|
@@ -9,8 +9,6 @@ ObserveJS.bind 'Post.Header', class
|
|
|
9
9
|
|
|
10
10
|
@on 'ObserveJS:XHR:Failed', @failed
|
|
11
11
|
|
|
12
|
-
@on 'click', @retrieve('div.error.status').querySelector('button'), @clear
|
|
13
|
-
|
|
14
12
|
@on 'images:create', @refresh
|
|
15
13
|
@on 'images:destroy', @refresh
|
|
16
14
|
@on 'titles:index', @popup
|
|
@@ -50,6 +48,7 @@ ObserveJS.bind 'Post.Header', class
|
|
|
50
48
|
@hide(@retrieve('div.status.uploading'))
|
|
51
49
|
@show(@retrieve('div.error.status'))
|
|
52
50
|
ul = @retrieve('div.error.status').querySelector('ul')
|
|
51
|
+
@on 'click', @retrieve('div.error.status').querySelector('button'), @clear
|
|
53
52
|
for error in errors
|
|
54
53
|
ul.insertAdjacentHTML('beforeend', "<li>#{error}</li>")
|
|
55
54
|
|
|
@@ -101,8 +101,8 @@ main {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
.button {
|
|
104
|
-
@include transition(
|
|
105
|
-
border: 1px solid
|
|
104
|
+
@include transition(border-color 0.1s, background-color 0.1s, box-shadow 0.2s, color 0.1s);
|
|
105
|
+
border: 1px solid rgba(black, 0.25);
|
|
106
106
|
border-radius: 2px;
|
|
107
107
|
text-decoration: none;
|
|
108
108
|
padding: 4px 8px;
|
|
@@ -110,26 +110,25 @@ main {
|
|
|
110
110
|
font-size: 0.8em;
|
|
111
111
|
text-transform: uppercase;
|
|
112
112
|
font-weight: bold;
|
|
113
|
-
background-color:
|
|
114
|
-
|
|
113
|
+
background-color: rgba(white, 0.05);
|
|
115
114
|
color: lighten($navy-blue, 100%);
|
|
116
|
-
box-shadow: inset 0 1px 0 0
|
|
115
|
+
box-shadow: inset 0 1px 0 0 transparent, 0 0 2px 0 transparent;
|
|
117
116
|
|
|
118
117
|
&.selected {
|
|
119
|
-
background-color:
|
|
120
|
-
border-color:
|
|
118
|
+
background-color: rgba(black, 0.3);
|
|
119
|
+
border-color: rgba(black, 0.3);
|
|
121
120
|
box-shadow: none;
|
|
122
121
|
}
|
|
123
122
|
|
|
124
123
|
&:hover:not(.selected) {
|
|
125
|
-
background-color:
|
|
126
|
-
color:
|
|
127
|
-
border-color:
|
|
128
|
-
box-shadow: inset 0 1px 0 0
|
|
124
|
+
background-color: rgba(white, 0.05);
|
|
125
|
+
color: white;
|
|
126
|
+
border-color: rgba(black, 0.3);
|
|
127
|
+
box-shadow: inset 0 1px 0 0 rgba(white, 0.35), 0 0 2px 0 rgba(black, 0.2);
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
&:active:not(.selected) {
|
|
132
|
-
box-shadow: inset 0 1px 2px 0
|
|
133
|
-
background-color:
|
|
131
|
+
box-shadow: inset 0 1px 2px 0 rgba(black, 0.3), 0 0 2px 0 transparent;
|
|
132
|
+
background-color: rgba(black, 0.3);
|
|
134
133
|
}
|
|
135
134
|
}
|
|
@@ -4,11 +4,15 @@ main.posts > header {
|
|
|
4
4
|
@include align-items(center);
|
|
5
5
|
@include justify-content(center);
|
|
6
6
|
@include flex-direction(column);
|
|
7
|
+
|
|
7
8
|
background-color: $bright-blue;
|
|
8
9
|
background-size: cover;
|
|
10
|
+
background-position: center left;
|
|
11
|
+
|
|
9
12
|
color: lighten($bright-blue, 50%);
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
height: 30vh;
|
|
15
|
+
min-height: 200px;
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
main.edit.posts > header > div.title {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
nav#Menu.admin section {
|
|
2
|
+
@include display(flex);
|
|
3
|
+
height: 100%;
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
nav#Menu.admin {
|
|
2
7
|
@include display(flex);
|
|
3
8
|
@include align-content(flex-start);
|
|
@@ -5,87 +10,93 @@ nav#Menu.admin {
|
|
|
5
10
|
@include justify-content(space-between);
|
|
6
11
|
@include flex-wrap(wrap);
|
|
7
12
|
|
|
8
|
-
height:
|
|
13
|
+
height: 3em;
|
|
9
14
|
font-family: $fonts;
|
|
10
15
|
font-size: 16px;
|
|
11
16
|
font-weight: bold;
|
|
12
|
-
padding: 0
|
|
17
|
+
padding: 0 2em;
|
|
13
18
|
z-index: 10;
|
|
14
19
|
|
|
15
|
-
background-color:
|
|
16
|
-
color:
|
|
20
|
+
background-color: lighten($gray-blue, 25%);
|
|
21
|
+
color: darken($gray-blue, 30%);
|
|
17
22
|
border-bottom: 1px solid darken($gray-blue, 75%);
|
|
18
|
-
box-shadow: inset 0 -1px 0 0 darken($gray-blue, 15%);
|
|
19
|
-
text-shadow: 0 -1px darken($light-blue, 75%);
|
|
20
23
|
|
|
21
24
|
.button {
|
|
22
|
-
@include
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
@include display(flex);
|
|
26
|
+
@include flex(0 0 auto);
|
|
27
|
+
@include align-items(center);
|
|
28
|
+
@include justify-content(center);
|
|
29
|
+
|
|
30
|
+
border-radius: 0;
|
|
31
|
+
border: none;
|
|
32
|
+
background: none;
|
|
33
|
+
|
|
34
|
+
height: 100%;
|
|
35
|
+
|
|
25
36
|
text-decoration: none;
|
|
26
|
-
padding: 4px
|
|
27
|
-
margin: 0
|
|
37
|
+
padding: 4px 2em;
|
|
38
|
+
margin: 0;
|
|
28
39
|
font-size: 0.8em;
|
|
29
40
|
text-transform: uppercase;
|
|
30
|
-
font-weight:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
color: lighten($navy-blue, 60%);
|
|
34
|
-
text-shadow: 0 1px lighten($navy-blue, 25%);
|
|
41
|
+
font-weight: lighter;
|
|
42
|
+
color: inherit;
|
|
43
|
+
text-shadow: none;
|
|
35
44
|
|
|
36
45
|
&.selected {
|
|
37
|
-
background-color: lighten($navy-blue, 25%);
|
|
38
|
-
border-color: lighten($navy-blue, 25%);
|
|
39
46
|
color: lighten($navy-blue, 100%);
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
&:hover:not(.selected) {
|
|
43
|
-
|
|
50
|
+
text-decoration: underline;
|
|
51
|
+
box-shadow: none;
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
&:active:not(.selected) {
|
|
47
55
|
color: lighten($navy-blue, 100%);
|
|
48
|
-
text-
|
|
49
|
-
box-shadow:
|
|
50
|
-
border-color: darken($navy-blue, 15%);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
&.home {
|
|
54
|
-
margin: 0 2em;
|
|
56
|
+
text-decoration: underline;
|
|
57
|
+
box-shadow: none;
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
nav#Menu.admin form.logout {
|
|
60
|
-
@include
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
padding: 4px 8px;
|
|
65
|
-
cursor: pointer;
|
|
66
|
-
outline: none;
|
|
67
|
-
background-color: lighten($red, 5%);
|
|
68
|
-
color: lighten($red, 30%);
|
|
69
|
-
box-shadow: inset 0 1px 0 0 lighten($red, 10%), 0 1px 1px darken($red, 20%);
|
|
70
|
-
|
|
71
|
-
&:hover {
|
|
72
|
-
border-color: darken($red, 10%);
|
|
73
|
-
}
|
|
63
|
+
@include display(flex);
|
|
64
|
+
@include flex(0 0 auto);
|
|
65
|
+
@include align-items(center);
|
|
66
|
+
@include justify-content(center);
|
|
74
67
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
68
|
+
margin: 0;
|
|
69
|
+
height: 100%;
|
|
78
70
|
|
|
79
71
|
input {
|
|
80
|
-
|
|
72
|
+
@include transition(box-shadow 0.1s ease, background-color 0.1s ease);
|
|
73
|
+
|
|
74
|
+
background-color: lighten($red, 15%);
|
|
75
|
+
color: darken($red, 25%);
|
|
76
|
+
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
outline: none;
|
|
79
|
+
|
|
80
|
+
padding: 6px 10px;
|
|
81
81
|
margin: 0;
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
border: none;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
font-
|
|
84
|
+
border-radius: 2px;
|
|
85
|
+
|
|
86
|
+
font-size: 0.9em;
|
|
87
87
|
font-family: inherit;
|
|
88
|
-
|
|
88
|
+
font-weight: bold;
|
|
89
|
+
|
|
90
|
+
box-shadow: 0 1px 1px 0 transparent;
|
|
91
|
+
|
|
92
|
+
&:hover {
|
|
93
|
+
box-shadow: 0 0.5px 0.5px 0 darken($red, 15%);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&:active {
|
|
97
|
+
box-shadow: 0 1px 1px 0 transparent;
|
|
98
|
+
background-color: darken($red, 5%);
|
|
99
|
+
}
|
|
89
100
|
}
|
|
90
101
|
|
|
91
102
|
}
|
|
@@ -51,7 +51,7 @@ main.posts > header > nav > div.publish.state {
|
|
|
51
51
|
margin: 0 2em;
|
|
52
52
|
width: 10em;
|
|
53
53
|
height: 30px;
|
|
54
|
-
background-color:
|
|
54
|
+
background-color: rgba(black, 0.2);
|
|
55
55
|
color: darken($bright-blue, 25%);
|
|
56
56
|
border-radius: 60px;
|
|
57
57
|
cursor: pointer;
|
|
@@ -93,7 +93,7 @@ main.posts > header > nav > div.publish.state {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
&[published='true'] {
|
|
96
|
-
background-color:
|
|
96
|
+
background-color: rgba(black, 0.5);
|
|
97
97
|
|
|
98
98
|
label:first-child {
|
|
99
99
|
background-color: darken($bright-blue, 25%);
|
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
article.content pre {
|
|
1
|
+
article.content code, article.content pre {
|
|
2
|
+
border-radius: .3em;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
article.content code {
|
|
2
6
|
font-size: 0.7em;
|
|
3
|
-
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
article.content pre {
|
|
10
|
+
background: lighten($light-blue, 5%);
|
|
11
|
+
border-top: 1px solid $light-blue;
|
|
12
|
+
padding: 0.7em;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
article.content pre > code {
|
|
16
|
+
background: none;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
article.content :not(pre) > code {
|
|
20
|
+
background: lighten($light-blue, 5%);
|
|
21
|
+
border-top: 1px solid $light-blue;
|
|
22
|
+
padding: 0.2em 0.5em;
|
|
4
23
|
}
|
|
@@ -37,19 +37,6 @@ code::selection, code ::selection {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
/* Code blocks */
|
|
41
|
-
pre {
|
|
42
|
-
padding: 1em;
|
|
43
|
-
margin: .5em 0;
|
|
44
|
-
overflow: auto;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/* Inline code */
|
|
48
|
-
:not(pre) > code {
|
|
49
|
-
padding: .1em;
|
|
50
|
-
border-radius: .3em;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
40
|
.token.comment,
|
|
54
41
|
.token.prolog,
|
|
55
42
|
.token.doctype,
|
|
@@ -42,7 +42,14 @@ module Admin
|
|
|
42
42
|
html.xpath("//img").each do |img|
|
|
43
43
|
img.remove
|
|
44
44
|
end
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
valid_elements = %w(p ul ol li).freeze
|
|
47
|
+
|
|
48
|
+
elements = html.xpath('//body').children[0..4].take_while do |el|
|
|
49
|
+
valid_elements.include?(el.name)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
self.compiled_excerpt = elements.map(&:to_s).join
|
|
46
53
|
end
|
|
47
54
|
|
|
48
55
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
<% post = Post.new(titles: [title], published_at: title.post.published_at) %>
|
|
2
1
|
<li>
|
|
3
2
|
<%= content_tag :p, title.name %>
|
|
4
|
-
|
|
3
|
+
<% if title.post.published? %>
|
|
4
|
+
<% post = Post.new(titles: [title], published_at: title.post.published_at) %>
|
|
5
|
+
<%= link_to url(Ecrire::Theme::Engine.post_path, post: post, absolute_path: true), url(Ecrire::Theme::Engine.post_path, post: post, absolute_path: true) %>
|
|
6
|
+
<% end %>
|
|
5
7
|
</li>
|
data/lib/ecrire/markdown/node.rb
CHANGED
|
@@ -3,7 +3,7 @@ module Ecrire::Markdown
|
|
|
3
3
|
autoload :Image, 'ecrire/markdown/nodes/image'
|
|
4
4
|
autoload :UnorderedList, 'ecrire/markdown/nodes/unordered_list'
|
|
5
5
|
autoload :OrderedList, 'ecrire/markdown/nodes/ordered_list'
|
|
6
|
-
autoload :
|
|
6
|
+
autoload :Code, 'ecrire/markdown/nodes/code'
|
|
7
7
|
autoload :Heading, 'ecrire/markdown/nodes/heading'
|
|
8
8
|
end
|
|
9
9
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'active_support/core_ext/string'
|
|
2
|
+
|
|
3
|
+
module Ecrire::Markdown
|
|
4
|
+
module Nodes
|
|
5
|
+
class Code < Node
|
|
6
|
+
|
|
7
|
+
attr_reader :offset
|
|
8
|
+
|
|
9
|
+
attr_accessor :title, :block
|
|
10
|
+
|
|
11
|
+
def initialize(data)
|
|
12
|
+
|
|
13
|
+
@offset = data.offset(0)[0]
|
|
14
|
+
@language = (data[3] || "").strip
|
|
15
|
+
|
|
16
|
+
@content = data[4]
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def block?
|
|
21
|
+
@block == true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def content=(new_content)
|
|
25
|
+
if new_content.is_a?(Array)
|
|
26
|
+
@content = ERB::Util.html_escape(new_content.join("\n"))
|
|
27
|
+
else
|
|
28
|
+
@content = new_content
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def code_element
|
|
33
|
+
str = "<code"
|
|
34
|
+
|
|
35
|
+
unless @language.nil?
|
|
36
|
+
str << " class='language-#{@language}'>"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
str << @content
|
|
40
|
+
str << "</code>"
|
|
41
|
+
str
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def to_s
|
|
45
|
+
|
|
46
|
+
str = String.new
|
|
47
|
+
|
|
48
|
+
unless @title.nil?
|
|
49
|
+
str << "<header>#{@title}</header>"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
str << code_element
|
|
53
|
+
|
|
54
|
+
if block?
|
|
55
|
+
return "<pre>#{str}</pre>"
|
|
56
|
+
else
|
|
57
|
+
return str
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
require 'byebug'
|
|
2
|
+
|
|
1
3
|
module Ecrire::Markdown::Parsers
|
|
2
4
|
class Code < Base
|
|
3
|
-
OPENING_TAG =
|
|
5
|
+
OPENING_TAG = /((~{3,})([a-z]+)?)(.+)?/i
|
|
6
|
+
attr_accessor :open, :close
|
|
4
7
|
|
|
5
8
|
def parse!
|
|
6
9
|
|
|
@@ -8,33 +11,51 @@ module Ecrire::Markdown::Parsers
|
|
|
8
11
|
return @node
|
|
9
12
|
end
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
nodes = extract_code_nodes!(count)
|
|
16
|
-
@node = Ecrire::Markdown::Nodes::CodeBlock.new(language, title, nodes)
|
|
17
|
-
@document.nodes[@index] = @node
|
|
14
|
+
while m = OPENING_TAG.match(@node.content)
|
|
15
|
+
node = Ecrire::Markdown::Nodes::Code.new(m)
|
|
16
|
+
@node.content.slice!(m.offset(1)[0], m[1].size)
|
|
17
|
+
close(node, m[2].size)
|
|
18
18
|
end
|
|
19
|
+
|
|
19
20
|
return @node
|
|
20
21
|
end
|
|
21
22
|
|
|
22
|
-
def
|
|
23
|
-
regex = Regexp.new("
|
|
24
|
-
|
|
23
|
+
def close(node, tildeCount)
|
|
24
|
+
regex = Regexp.new("(~{#{tildeCount},})", Regexp::IGNORECASE)
|
|
25
|
+
|
|
26
|
+
if match = regex.match(node.content)
|
|
27
|
+
@node.content.slice!(node.offset, match.offset(1)[1])
|
|
28
|
+
node.content.slice!(match.offset(0)[0], node.content.length - match.offset(0)[0])
|
|
29
|
+
node.content.lstrip!
|
|
30
|
+
@node.content.insert(node.offset, node.to_s)
|
|
31
|
+
|
|
32
|
+
elsif node.offset == 0
|
|
33
|
+
extract_siblings(node, regex)
|
|
34
|
+
@node = node
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def extract_siblings(node, regex)
|
|
25
39
|
i = @index + 1
|
|
26
40
|
nodes = []
|
|
41
|
+
node.title = (node.content || "").strip
|
|
27
42
|
|
|
28
|
-
while !
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if
|
|
32
|
-
|
|
43
|
+
while !(n = @document.nodes[i]).nil?
|
|
44
|
+
match = regex.match(n.content)
|
|
45
|
+
@document.nodes.delete_at(i)
|
|
46
|
+
if match.nil?
|
|
47
|
+
nodes.push n
|
|
33
48
|
else
|
|
34
|
-
|
|
49
|
+
break
|
|
35
50
|
end
|
|
36
51
|
end
|
|
37
|
-
|
|
52
|
+
|
|
53
|
+
node.content = nodes
|
|
54
|
+
node.block = true
|
|
55
|
+
|
|
56
|
+
@document.nodes[@index] = node
|
|
57
|
+
|
|
58
|
+
return node
|
|
38
59
|
end
|
|
39
60
|
|
|
40
61
|
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
|
|
4
4
|
<head>
|
|
5
5
|
<%= title_tag 'Before you start blogging...' %>
|
|
6
|
-
<%= stylesheet_link_tag "ecrire", media: "all", "data-turbolinks-track" => true %>
|
|
7
|
-
<%= javascript_include_tag "ecrire", "base", "data-turbolinks-track" => true %>
|
|
8
6
|
|
|
9
|
-
<%=
|
|
10
|
-
|
|
7
|
+
<%= javascript_include_tag "ecrire", "application", "data-turbolinks-track" => true %>
|
|
8
|
+
|
|
9
|
+
<%= stylesheet_link_tag "browser", media: "(min-width: 1025px)", "data-turbolinks-track" => true %>
|
|
10
|
+
<%= stylesheet_link_tag "tablet", media: "(min-width: 641px) and (max-width: 1024px)", "data-turbolinks-track" => true %>
|
|
11
|
+
<%= stylesheet_link_tag "mobile", media: "(max-width: 640px)", "data-turbolinks-track" => true %>
|
|
12
|
+
|
|
11
13
|
<%= csrf_meta_tags %>
|
|
12
14
|
<%= favicon_tag %>
|
|
13
15
|
</head>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#= require_tree .
|
data/lib/ecrire/version.rb
CHANGED
|
@@ -89,4 +89,69 @@ class PostTest < ActiveSupport::TestCase
|
|
|
89
89
|
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
test "excerpt is generated until it reaches 5 elements" do
|
|
93
|
+
post = Admin::Post.new({
|
|
94
|
+
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
|
95
|
+
Nunc malesuada diam id fringilla varius.
|
|
96
|
+
Suspendisse ultricies sem ac enim pulvinar luctus.
|
|
97
|
+
Pellentesque a nunc in libero convallis fringilla.
|
|
98
|
+
Praesent nec ipsum ut turpis feugiat semper.
|
|
99
|
+
Integer quis magna quis nisi porta hendrerit in a lorem.
|
|
100
|
+
Nam dignissim sapien nec feugiat cursus.
|
|
101
|
+
In tincidunt orci eget est scelerisque, a consequat massa consectetur.
|
|
102
|
+
Donec et nunc at justo facilisis congue.
|
|
103
|
+
Cras mollis orci ac arcu consectetur, quis bibendum leo gravida.
|
|
104
|
+
Vivamus dapibus dolor eu tortor molestie, non pulvinar risus dignissim.
|
|
105
|
+
Aliquam vitae lectus vehicula, euismod ex at, accumsan quam.
|
|
106
|
+
Ut vulputate mauris hendrerit mauris placerat tempus quis eget diam.
|
|
107
|
+
Sed vestibulum lacus eu vehicula finibus.
|
|
108
|
+
Praesent non diam vitae eros congue pretium.
|
|
109
|
+
Nulla tincidunt justo sit amet aliquet porta.
|
|
110
|
+
Nam vel elit vitae diam mattis mattis.
|
|
111
|
+
Phasellus in lacus eget sem tempus elementum.
|
|
112
|
+
Integer gravida diam sit amet massa egestas convallis.
|
|
113
|
+
Vestibulum dignissim odio sit amet sem condimentum, ut pharetra erat bibendum.
|
|
114
|
+
Cras molestie tellus id convallis lobortis."
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
post.titles << Admin::Title.new(name: "Post Excerpt test.", post: post)
|
|
118
|
+
|
|
119
|
+
assert post.save, post.errors.full_messages.to_sentence
|
|
120
|
+
|
|
121
|
+
html = Nokogiri::HTML(post.compiled_excerpt)
|
|
122
|
+
|
|
123
|
+
assert html.css('body > *').length == 5
|
|
124
|
+
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
test "excerpt generation stops when it reaches an element that can't be part of it" do
|
|
128
|
+
post = Admin::Post.new({
|
|
129
|
+
content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
|
130
|
+
[Nunc](http://google.com) malesuada diam id fringilla varius.
|
|
131
|
+
Nam dignissim sapien nec feugiat cursus.
|
|
132
|
+
- In tincidunt orci eget est scelerisque, a consequat massa consectetur.
|
|
133
|
+
- Donec et nunc at justo facilisis congue.
|
|
134
|
+
- Cras mollis orci ac arcu consectetur, quis bibendum leo gravida.
|
|
135
|
+
Vivamus dapibus dolor eu tortor molestie, non pulvinar risus dignissim.
|
|
136
|
+
Aliquam vitae lectus vehicula, euismod ex at, accumsan quam.
|
|
137
|
+
# Ut vulputate mauris hendrerit mauris placerat tempus quis eget diam.
|
|
138
|
+
Sed vestibulum lacus eu vehicula finibus.
|
|
139
|
+
Praesent non diam vitae eros congue pretium.
|
|
140
|
+
Nulla tincidunt justo sit amet aliquet porta.
|
|
141
|
+
Nam vel elit vitae diam mattis mattis.
|
|
142
|
+
Phasellus in lacus eget sem tempus elementum.
|
|
143
|
+
Integer gravida diam sit amet massa egestas convallis.
|
|
144
|
+
Vestibulum dignissim odio sit amet sem condimentum, ut pharetra erat bibendum.
|
|
145
|
+
Cras molestie tellus id convallis lobortis."
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
post.titles << Admin::Title.new(name: "Post Excerpt test.", post: post)
|
|
149
|
+
|
|
150
|
+
assert post.save, post.errors.full_messages.to_sentence
|
|
151
|
+
|
|
152
|
+
html = Nokogiri::HTML(post.compiled_excerpt)
|
|
153
|
+
|
|
154
|
+
assert html.css('body > *').length == 5
|
|
155
|
+
end
|
|
156
|
+
|
|
92
157
|
end
|
|
@@ -35,6 +35,14 @@ class MarkdownTest < Minitest::Test
|
|
|
35
35
|
assert_equal "<p><strong>bold</strong> and <em>italic</em></p>", document.to_html
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
def test_code_inline
|
|
39
|
+
document = Ecrire::Markdown.parse("This is a ~~~ruby inline code~~~ snippet")
|
|
40
|
+
assert_equal "<p>This is a <code class='language-ruby'>inline code</code> snippet</p>", document.to_html
|
|
41
|
+
|
|
42
|
+
document = Ecrire::Markdown.parse("This is a ~~~ruby inline code~~~ snippet. Multiple ~~~ruby instance~~~ can coexist on the same line")
|
|
43
|
+
assert_equal "<p>This is a <code class='language-ruby'>inline code</code> snippet. Multiple <code class='language-ruby'>instance</code> can coexist on the same line</p>", document.to_html
|
|
44
|
+
end
|
|
45
|
+
|
|
38
46
|
def test_code_blocks
|
|
39
47
|
document = Ecrire::Markdown.parse("~~~ruby\n# A comment\nRails.application\n~~~")
|
|
40
48
|
assert_equal "<pre><header></header><code class='language-ruby'># A comment\nRails.application</code></pre>", document.to_html
|
|
@@ -58,6 +66,11 @@ class MarkdownTest < Minitest::Test
|
|
|
58
66
|
assert_equal '<ol><li>Ruby</li><li><strong>Go</strong></li></ol><ul><li>Test</li><li>123</li></ul>', document.to_html
|
|
59
67
|
end
|
|
60
68
|
|
|
69
|
+
def test_lists_with_links
|
|
70
|
+
document = Ecrire::Markdown.parse("1. [Ruby](http://ruby.com)\n")
|
|
71
|
+
assert_equal "<ol><li><a href='http://ruby.com'>Ruby</a></li></ol>", document.to_html
|
|
72
|
+
end
|
|
73
|
+
|
|
61
74
|
def test_image
|
|
62
75
|
document = Ecrire::Markdown.parse('')
|
|
63
76
|
assert_equal "<figure><img src='http://bla.com' /><figcaption>An Image</figcaption></figure>", document.to_html
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ecrire
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.27.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pier-Olivier Thibault
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-
|
|
11
|
+
date: 2015-06-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Blog engine on Rails
|
|
14
14
|
email: pothibo@gmail.com
|
|
@@ -181,7 +181,7 @@ files:
|
|
|
181
181
|
- lib/ecrire/markdown.rb
|
|
182
182
|
- lib/ecrire/markdown/document.rb
|
|
183
183
|
- lib/ecrire/markdown/node.rb
|
|
184
|
-
- lib/ecrire/markdown/nodes/
|
|
184
|
+
- lib/ecrire/markdown/nodes/code.rb
|
|
185
185
|
- lib/ecrire/markdown/nodes/heading.rb
|
|
186
186
|
- lib/ecrire/markdown/nodes/image.rb
|
|
187
187
|
- lib/ecrire/markdown/nodes/ordered_list.rb
|
|
@@ -199,17 +199,23 @@ files:
|
|
|
199
199
|
- lib/ecrire/onboarding/assets/images/github-logo.png
|
|
200
200
|
- lib/ecrire/onboarding/assets/images/profile.png
|
|
201
201
|
- lib/ecrire/onboarding/assets/images/twitter-logo.png
|
|
202
|
+
- lib/ecrire/onboarding/assets/javascripts/application.js
|
|
202
203
|
- lib/ecrire/onboarding/assets/javascripts/databases/information.js.coffee
|
|
203
204
|
- lib/ecrire/onboarding/assets/javascripts/message.js.coffee
|
|
204
|
-
- lib/ecrire/onboarding/assets/
|
|
205
|
-
- lib/ecrire/onboarding/assets/stylesheets/
|
|
206
|
-
- lib/ecrire/onboarding/assets/stylesheets/
|
|
207
|
-
- lib/ecrire/onboarding/assets/stylesheets/
|
|
208
|
-
- lib/ecrire/onboarding/assets/stylesheets/
|
|
209
|
-
- lib/ecrire/onboarding/assets/stylesheets/
|
|
210
|
-
- lib/ecrire/onboarding/assets/stylesheets/
|
|
205
|
+
- lib/ecrire/onboarding/assets/stylesheets/browser.scss
|
|
206
|
+
- lib/ecrire/onboarding/assets/stylesheets/browser/base.scss
|
|
207
|
+
- lib/ecrire/onboarding/assets/stylesheets/mobile.scss
|
|
208
|
+
- lib/ecrire/onboarding/assets/stylesheets/mobile/base.scss
|
|
209
|
+
- lib/ecrire/onboarding/assets/stylesheets/shared/base.scss
|
|
210
|
+
- lib/ecrire/onboarding/assets/stylesheets/shared/complete.scss
|
|
211
|
+
- lib/ecrire/onboarding/assets/stylesheets/shared/fonts.scss
|
|
212
|
+
- lib/ecrire/onboarding/assets/stylesheets/shared/form.scss
|
|
213
|
+
- lib/ecrire/onboarding/assets/stylesheets/shared/message.scss
|
|
214
|
+
- lib/ecrire/onboarding/assets/stylesheets/shared/terminal.scss
|
|
215
|
+
- lib/ecrire/onboarding/assets/stylesheets/shared/welcome.scss
|
|
216
|
+
- lib/ecrire/onboarding/assets/stylesheets/tablet.scss
|
|
217
|
+
- lib/ecrire/onboarding/assets/stylesheets/tablet/base.scss
|
|
211
218
|
- lib/ecrire/onboarding/assets/stylesheets/variables.css.scss
|
|
212
|
-
- lib/ecrire/onboarding/assets/stylesheets/welcome.css.scss
|
|
213
219
|
- lib/ecrire/onboarding/controllers/databases_controller.rb
|
|
214
220
|
- lib/ecrire/onboarding/controllers/onboarding_controller.rb
|
|
215
221
|
- lib/ecrire/onboarding/controllers/users_controller.rb
|
|
@@ -228,6 +234,7 @@ files:
|
|
|
228
234
|
- lib/ecrire/theme/template/Procfile
|
|
229
235
|
- lib/ecrire/theme/template/Rakefile
|
|
230
236
|
- lib/ecrire/theme/template/assets/images/.keep
|
|
237
|
+
- lib/ecrire/theme/template/assets/javascripts/application.js.coffee
|
|
231
238
|
- lib/ecrire/theme/template/assets/stylesheets/browser.css.scss
|
|
232
239
|
- lib/ecrire/theme/template/assets/stylesheets/browser/base.css.scss
|
|
233
240
|
- lib/ecrire/theme/template/assets/stylesheets/mobile.css.scss
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
require 'active_support/core_ext/string'
|
|
2
|
-
|
|
3
|
-
module Ecrire::Markdown
|
|
4
|
-
module Nodes
|
|
5
|
-
class CodeBlock < Node
|
|
6
|
-
def initialize(language, title, nodes)
|
|
7
|
-
@content = ERB::Util.html_escape(nodes.join("\n"))
|
|
8
|
-
@title = title
|
|
9
|
-
@language = language
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def to_s
|
|
13
|
-
str = "<pre>"
|
|
14
|
-
str << "<header>#{@title}</header>"
|
|
15
|
-
|
|
16
|
-
str << "<code"
|
|
17
|
-
|
|
18
|
-
unless @language.nil?
|
|
19
|
-
str << " class='language-#{@language}'>"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
str << @content
|
|
24
|
-
str << "</code></pre>"
|
|
25
|
-
str
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|