ecrire 0.20.0 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +47 -45
- data/lib/ecrire/app/assets/javascripts/admin/editor/content.coffee +103 -48
- data/lib/ecrire/app/assets/javascripts/admin/editor/ext.coffee +9 -3
- data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/clipboard.coffee +26 -10
- data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/code.coffee +10 -0
- data/lib/ecrire/app/assets/javascripts/admin/editor/navigation/save.js.coffee +5 -8
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/code.coffee +7 -3
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/image.coffee +1 -1
- data/lib/ecrire/app/assets/stylesheets/admin/editor/content.css.scss +30 -1
- data/lib/ecrire/app/assets/stylesheets/shared/popup.css.scss +5 -0
- data/lib/ecrire/app/controllers/ecrire/posts_controller.rb +1 -1
- data/lib/ecrire/app/views/admin/posts/_documentation.html.erb +1 -1
- data/lib/ecrire/db/schema.rb +8 -13
- data/lib/ecrire/tasks/assets.rake +1 -1
- data/lib/ecrire/tasks/database.rake +6 -0
- data/lib/ecrire/template/Gemfile +1 -1
- data/lib/ecrire/template/Procfile +2 -0
- data/lib/ecrire/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9187d5d3cf47bd6d98975d2c7e98f78fca7e724
|
4
|
+
data.tar.gz: bb54f17491247dfe317ca7ba9ef3fe78089936c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7887948635cf02da8caaed5c802c34e2f8ca0baa3e6d53f8cf04caccec39444a542a67a45a9b2026b739b9ca5ff290544f0908e9b3fefb96b4724e2ab8c9d60
|
7
|
+
data.tar.gz: b12bfee9bd7bde93f8d37b514df86f01add1c972884998af6fa05a930aae3e610eee4b1f9000fe1c5ac281940dc0b42fa9f40bc288498c617c2741965e1375d5
|
data/README.md
CHANGED
@@ -1,62 +1,64 @@
|
|
1
1
|
# Ecrire
|
2
2
|
|
3
|
-
|
3
|
+
Ecrire is a blog built *on top* of Ruby on Rails. The goal of this blog engine is to make it **easy** to start a blog while keeping control over the content. You can see this as an alternative to WordPress.
|
4
4
|
|
5
|
-
|
5
|
+
## The editor
|
6
|
+
The editor was built around the Markdown syntax. The content change as you type to offer you a very good approximation of how your post will look like once publish.
|
6
7
|
|
7
|
-
|
8
|
+
Here's the available feature:
|
9
|
+
- Headers
|
10
|
+
- Unordered list
|
11
|
+
- Ordered list
|
12
|
+
- Code with syntax highlighting
|
13
|
+
- Image with auto-upload to S3
|
14
|
+
- Links
|
15
|
+
- Bold and Italic words
|
8
16
|
|
9
|
-
|
17
|
+
More feature will be implemented as the Editor mature.
|
10
18
|
|
11
|
-
|
19
|
+
## Theme
|
20
|
+
When you start a new blog with Ecrire, it will generate a folder for you. Everything in that folder is for you to modify. You won't break anything. It also features a few characteristic that you may recognize if you are a Rails developer.
|
12
21
|
|
13
|
-
|
22
|
+
- SASS
|
23
|
+
- Coffeescript
|
24
|
+
- Assets caching through Sprockets
|
25
|
+
- View using layouts, views and partials
|
26
|
+
- Controllers
|
27
|
+
- Helpers
|
28
|
+
- Static pages
|
14
29
|
|
15
|
-
|
30
|
+
When you install your theme, the documentation will be available direclty within your blog so you can go back to it when you need it.
|
16
31
|
|
17
|
-
|
32
|
+
## How to install
|
18
33
|
|
34
|
+
```bash
|
35
|
+
$ gem install ecrire
|
36
|
+
$ ecrire new my.blog.com
|
37
|
+
$ cd my.blog.com/
|
38
|
+
$ ecrire server
|
39
|
+
```
|
19
40
|
|
20
|
-
|
41
|
+
From there, you can access your new blog via the browser and start configuring your database.
|
21
42
|
|
22
|
-
|
43
|
+
## Heroku
|
23
44
|
|
24
|
-
|
25
|
-
``` gem install ecrire ```
|
45
|
+
Once you have finished your changes in your local environment, here's how you can publish your blog on Heroku.
|
26
46
|
|
27
|
-
|
28
|
-
|
47
|
+
~~~bash
|
48
|
+
$ heroku git:remote -a name-of-your-app-on-heroku
|
49
|
+
$ git push origin heroku
|
50
|
+
$ heroku run rake db:migrate
|
51
|
+
~~~
|
29
52
|
|
30
|
-
|
31
|
-
``` bundle install ```
|
53
|
+
Your blog is now up and running on Heroku! But you need to create a user on Heroku for now, here's how you do it.
|
32
54
|
|
33
|
-
|
55
|
+
~~~ruby
|
56
|
+
$ heroku run ecrire console
|
57
|
+
irb(main)> user = User.new
|
58
|
+
irb(main)> user.email = "your@email.com"
|
59
|
+
irb(main)> user.password = "yourpassword"
|
60
|
+
irb(main)> user.save!
|
61
|
+
irb(main)> exit
|
62
|
+
~~~
|
34
63
|
|
35
|
-
|
36
|
-
|
37
|
-
You can access the blog & configuration setup with your browser by going at the following address.
|
38
|
-
``` http://localhost:3000 ```
|
39
|
-
|
40
|
-
### Licence
|
41
|
-
|
42
|
-
The MIT License (MIT)
|
43
|
-
|
44
|
-
Copyright (c) 2014 pothibo
|
45
|
-
|
46
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
47
|
-
of this software and associated documentation files (the "Software"), to deal
|
48
|
-
in the Software without restriction, including without limitation the rights
|
49
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
50
|
-
copies of the Software, and to permit persons to whom the Software is
|
51
|
-
furnished to do so, subject to the following conditions:
|
52
|
-
|
53
|
-
The above copyright notice and this permission notice shall be included in
|
54
|
-
all copies or substantial portions of the Software.
|
55
|
-
|
56
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
57
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
58
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
59
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
60
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
61
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
62
|
-
THE SOFTWARE.
|
64
|
+
You can now log in to your blog in production and start writing for real!
|
@@ -59,21 +59,23 @@ Joint.bind 'Editor.Content', class @Editor
|
|
59
59
|
|
60
60
|
sel = window.getSelection()
|
61
61
|
node = focusNode = sel.focusNode
|
62
|
-
offset = sel.focusOffset
|
63
|
-
|
64
|
-
str = focusNode.textContent
|
65
|
-
focusNode.textContent = str.substr(0, offset) + "\n" + str.substr(offset)
|
66
62
|
|
67
63
|
while node? && node.parentElement != @element()
|
68
64
|
node = node.parentElement
|
69
|
-
|
70
|
-
offset = @lineOffset(node, focusNode, offset) + 1
|
71
65
|
|
72
|
-
|
66
|
+
offset = @lineOffset(node, focusNode, sel.focusOffset)
|
67
|
+
|
68
|
+
lineFeed = new LineFeed(@cloneNodesFrom(node))
|
69
|
+
lineFeed.injectAt(offset)
|
70
|
+
|
71
|
+
cursor = new Editor.Cursor(offset + 1)
|
72
|
+
|
73
|
+
lines = @parse(lineFeed.fragment)
|
73
74
|
|
74
75
|
@observer.hold =>
|
75
|
-
|
76
|
-
@
|
76
|
+
line = cursor.focus(@updateDOM(node, lines)[0])
|
77
|
+
cursor.update(@walker(line))
|
78
|
+
@scrollLineIntoView(cursor.focus())
|
77
79
|
|
78
80
|
|
79
81
|
event = new CustomEvent('Editor:updated', {bubbles: true})
|
@@ -97,13 +99,12 @@ Joint.bind 'Editor.Content', class @Editor
|
|
97
99
|
offset += node.toString().length + 1
|
98
100
|
|
99
101
|
lines = @parse(@cloneNodesFrom(node))
|
102
|
+
cursor = new Editor.Cursor(offset)
|
100
103
|
|
101
104
|
@observer.hold =>
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
@setCursorAt(lines[0], offset)
|
106
|
-
break
|
105
|
+
line = cursor.focus(@updateDOM(node, lines)[0])
|
106
|
+
cursor.update(@walker(line))
|
107
|
+
@scrollLineIntoView(cursor.focus())
|
107
108
|
|
108
109
|
|
109
110
|
removed: (node, line) =>
|
@@ -116,18 +117,19 @@ Joint.bind 'Editor.Content', class @Editor
|
|
116
117
|
if line?
|
117
118
|
sel = window.getSelection()
|
118
119
|
offset = @lineOffset(line, sel.focusNode, sel.focusOffset)
|
120
|
+
cursor = new Editor.Cursor(offset)
|
119
121
|
|
120
122
|
lines = @parse(@cloneNodesFrom(line))
|
121
123
|
|
122
124
|
@observer.hold =>
|
123
|
-
|
124
|
-
|
125
|
-
@setCursorAt(lines[0], offset)
|
125
|
+
line = cursor.focus(@updateDOM(line, lines)[0])
|
126
|
+
cursor.update(@walker(line))
|
126
127
|
|
127
128
|
if @element().childNodes.length == 0
|
128
129
|
p = "<p>".toHTML()
|
129
130
|
@element().appendChild(p)
|
130
|
-
|
131
|
+
cursor = new Editor.Cursor(offset)
|
132
|
+
cursor.update(@walker(p), 0)
|
131
133
|
|
132
134
|
|
133
135
|
|
@@ -152,16 +154,25 @@ Joint.bind 'Editor.Content', class @Editor
|
|
152
154
|
|
153
155
|
sel = window.getSelection()
|
154
156
|
offset = @lineOffset(el, sel.focusNode, sel.focusOffset)
|
157
|
+
cursor = new Editor.Cursor(offset)
|
155
158
|
|
156
159
|
lines = @parse(@cloneNodesFrom(node))
|
157
160
|
|
158
161
|
@observer.hold =>
|
159
162
|
lines = @updateDOM(node, lines)
|
160
163
|
if node != lines[0]
|
161
|
-
@
|
164
|
+
cursor.update(@walker(lines[0]))
|
165
|
+
@scrollLineIntoView(lines[0])
|
162
166
|
|
163
167
|
|
164
168
|
|
169
|
+
scrollLineIntoView: (line) =>
|
170
|
+
height = window.innerHeight
|
171
|
+
rect = line.getBoundingClientRect()
|
172
|
+
|
173
|
+
if rect.bottom > height
|
174
|
+
line.scrollIntoView()
|
175
|
+
|
165
176
|
updateDOM: (anchor, fragment) =>
|
166
177
|
return unless anchor?
|
167
178
|
current = anchor.parentElement.lastChild
|
@@ -213,6 +224,10 @@ Joint.bind 'Editor.Content', class @Editor
|
|
213
224
|
|
214
225
|
|
215
226
|
lineOffset: (line, node, offset) =>
|
227
|
+
|
228
|
+
if node.nodeType == document.ELEMENT_NODE
|
229
|
+
return node.textContent.length
|
230
|
+
|
216
231
|
if node.textContent.length == 0
|
217
232
|
return 0
|
218
233
|
|
@@ -221,34 +236,6 @@ Joint.bind 'Editor.Content', class @Editor
|
|
221
236
|
|
222
237
|
|
223
238
|
|
224
|
-
setCursorAt: (line, offset) ->
|
225
|
-
sel = window.getSelection()
|
226
|
-
|
227
|
-
while true
|
228
|
-
length = line.toString().length
|
229
|
-
if length >= offset
|
230
|
-
break
|
231
|
-
offset -= Math.max(length + 1, 1)
|
232
|
-
|
233
|
-
if line.nextSibling?
|
234
|
-
line = line.nextSibling
|
235
|
-
continue
|
236
|
-
break
|
237
|
-
|
238
|
-
walker = @walker(line)
|
239
|
-
range = line.getRange(offset , walker)
|
240
|
-
|
241
|
-
height = window.innerHeight
|
242
|
-
rect = line.getBoundingClientRect()
|
243
|
-
|
244
|
-
if rect.bottom > height
|
245
|
-
line.scrollIntoView()
|
246
|
-
|
247
|
-
sel.removeAllRanges()
|
248
|
-
sel.addRange(range)
|
249
|
-
|
250
|
-
|
251
|
-
|
252
239
|
cloneNodesFrom: (node) =>
|
253
240
|
fragment = document.createDocumentFragment()
|
254
241
|
while node
|
@@ -313,7 +300,75 @@ Joint.bind 'Editor.Content', class @Editor
|
|
313
300
|
|
314
301
|
texts.join '\n'
|
315
302
|
|
303
|
+
class @Editor.Cursor
|
304
|
+
constructor: (offset) ->
|
305
|
+
@selection = window.getSelection()
|
306
|
+
@origin = @selection.focusNode
|
307
|
+
|
308
|
+
@offset = offset
|
309
|
+
|
310
|
+
focus: (line) ->
|
311
|
+
while true
|
312
|
+
length = line.toString().length
|
313
|
+
if length >= @offset
|
314
|
+
break
|
315
|
+
@offset -= Math.max(length + 1, 1)
|
316
|
+
|
317
|
+
if line.nextSibling?
|
318
|
+
line = line.nextSibling
|
319
|
+
continue
|
320
|
+
break
|
321
|
+
|
322
|
+
@focus = ->
|
323
|
+
line
|
324
|
+
|
325
|
+
@focus()
|
326
|
+
|
327
|
+
update: (walker, force = false) ->
|
328
|
+
line = walker.root
|
329
|
+
|
330
|
+
if !force && @selection.focusNode == @origin && line.contains(@origin)
|
331
|
+
return
|
332
|
+
|
333
|
+
range = line.getRange(@offset, walker)
|
334
|
+
|
335
|
+
@selection.removeAllRanges()
|
336
|
+
@selection.addRange(range)
|
337
|
+
|
338
|
+
class LineFeed
|
339
|
+
constructor: (@fragment) ->
|
340
|
+
|
341
|
+
injectAt: (offset) ->
|
342
|
+
node = @fragment.firstChild
|
343
|
+
while node && offset > 0
|
344
|
+
length = node.textContent.length
|
345
|
+
if offset > length
|
346
|
+
offset -= length + 1
|
347
|
+
node = node.nextSibling
|
348
|
+
else
|
349
|
+
break
|
350
|
+
|
351
|
+
if !node?
|
352
|
+
@append(@fragment.lastChild)
|
353
|
+
else
|
354
|
+
@insert(node, offset)
|
355
|
+
|
356
|
+
append: (lastChild) ->
|
357
|
+
node = lastChild.cloneNode(true)
|
358
|
+
node.textContent = ''
|
359
|
+
@fragment.appendChild(node)
|
360
|
+
|
361
|
+
insert: (root, offset) ->
|
362
|
+
node = root.cloneNode(true)
|
363
|
+
|
364
|
+
root.textContent = root.textContent.substr(0, offset)
|
365
|
+
node.textContent = node.textContent.substr(offset)
|
366
|
+
|
367
|
+
if root.nextSibling?
|
368
|
+
@fragment.insertBefore(node, root.nextSibling)
|
369
|
+
else
|
370
|
+
@fragment.appendChild(node)
|
371
|
+
|
316
372
|
|
317
373
|
Editor.Parsers = []
|
318
374
|
Editor.Extensions = []
|
319
|
-
|
@@ -14,10 +14,16 @@ HTMLOListElement::toString = HTMLUListElement::toString = ->
|
|
14
14
|
li.textContent
|
15
15
|
texts.join("\n")
|
16
16
|
|
17
|
-
|
18
|
-
@
|
17
|
+
HTMLDivElement::toString = ->
|
18
|
+
if @classList.contains('image')
|
19
|
+
@lastChild.textContent
|
20
|
+
else
|
21
|
+
@textContent
|
22
|
+
|
23
|
+
HTMLPreElement::toString = ->
|
24
|
+
@textContent
|
19
25
|
|
20
|
-
|
26
|
+
HTMLHeadingElement::toString = HTMLParagraphElement::toString = ->
|
21
27
|
@textContent
|
22
28
|
|
23
29
|
HTMLElement::offset = (node, walker) ->
|
@@ -42,7 +42,7 @@ Editor.Extensions.push class ClipBoard
|
|
42
42
|
|
43
43
|
|
44
44
|
replace: (texts, sel) =>
|
45
|
-
|
45
|
+
|
46
46
|
node = sel.extentNode
|
47
47
|
line = node
|
48
48
|
nodes = [node]
|
@@ -62,14 +62,15 @@ Editor.Extensions.push class ClipBoard
|
|
62
62
|
t.textContent
|
63
63
|
).join()
|
64
64
|
|
65
|
-
|
65
|
+
|
66
66
|
line.textContent = line.textContent.substr(0, offsets.start) + text + line.textContent.substr(offsets.end)
|
67
67
|
|
68
68
|
fragment = @editor.parse(@editor.cloneNodesFrom(line))
|
69
69
|
|
70
70
|
@editor.updateDOM(line, fragment)
|
71
71
|
|
72
|
-
|
72
|
+
cursor = new Editor.Cursor(offsets.start + text.length)
|
73
|
+
cursor.update(@editor.walker(cursor.focus(line)), true)
|
73
74
|
|
74
75
|
|
75
76
|
insert: (texts, sel) =>
|
@@ -84,15 +85,30 @@ Editor.Extensions.push class ClipBoard
|
|
84
85
|
node.textContent += text
|
85
86
|
node.textContent += str.substr(sel.anchorOffset)
|
86
87
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
line = node
|
89
|
+
while line.parentElement != @editor.element()
|
90
|
+
line = line.parentElement
|
91
|
+
|
92
|
+
offset = @nodeOffset(node,line) + sel.anchorOffset + text.length
|
93
|
+
|
94
|
+
fragment = @editor.parse(@editor.cloneNodesFrom(line))
|
91
95
|
|
92
|
-
|
96
|
+
lines = @editor.updateDOM(line, fragment)
|
93
97
|
|
94
|
-
|
98
|
+
cursor = new Editor.Cursor(offset)
|
99
|
+
cursor.update(@editor.walker(cursor.focus(lines[0])), true)
|
95
100
|
|
96
|
-
|
101
|
+
nodeOffset: (node, line) ->
|
102
|
+
if !line.contains(node)
|
103
|
+
raise "Looking for a node that is not inside the given line"
|
104
|
+
return
|
97
105
|
|
106
|
+
offset = 0
|
107
|
+
|
108
|
+
for n in line.childNodes
|
109
|
+
if n == node
|
110
|
+
break
|
111
|
+
else
|
112
|
+
offset += n.textContent.length
|
98
113
|
|
114
|
+
offset
|
@@ -9,7 +9,7 @@ Joint.bind 'Editor.Save', class
|
|
9
9
|
@on 'click', @button, @save
|
10
10
|
@on 'beforeunload', window, @confirm
|
11
11
|
|
12
|
-
@button.
|
12
|
+
@button.textContent = @button.getAttribute('persisted')
|
13
13
|
@refresh()
|
14
14
|
@cache()
|
15
15
|
@interval = setInterval(@refresh, 1000)
|
@@ -28,7 +28,7 @@ Joint.bind 'Editor.Save', class
|
|
28
28
|
if refresh
|
29
29
|
cache = PostBody.instance.toString()
|
30
30
|
@button.setAttribute('disabled', 'disabled')
|
31
|
-
@button.
|
31
|
+
@button.textContent = @button.getAttribute('persisted')
|
32
32
|
cache
|
33
33
|
@cache()
|
34
34
|
|
@@ -42,10 +42,7 @@ Joint.bind 'Editor.Save', class
|
|
42
42
|
xhr.send()
|
43
43
|
|
44
44
|
saved: (e) =>
|
45
|
-
if e.
|
46
|
-
event = new CustomEvent('Editor:message', { bubbles: true})
|
47
|
-
event.MessageHTML = e.MessageHTML
|
48
|
-
@element().dispatchEvent(event)
|
45
|
+
if e.UpdatedAtTime
|
49
46
|
@time.setAttribute('time', e.UpdatedAtTime)
|
50
47
|
@refresh()
|
51
48
|
@cache(true)
|
@@ -54,8 +51,8 @@ Joint.bind 'Editor.Save', class
|
|
54
51
|
return unless @cache()?
|
55
52
|
if @cache() != PostBody.instance.toString()
|
56
53
|
@button.removeAttribute('disabled')
|
57
|
-
@button.
|
54
|
+
@button.textContent = @button.getAttribute('dirty')
|
58
55
|
else
|
59
56
|
@button.setAttribute('disabled', 'disabled')
|
60
|
-
@button.
|
57
|
+
@button.textContent = @button.getAttribute('persisted')
|
61
58
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
Editor.Parsers.push class
|
2
|
-
rule: /^((~{3,})([a-z]+)
|
2
|
+
rule: /^((~{3,})([a-z]+)?)(.+)?(~{3,}$)?/mi
|
3
3
|
|
4
4
|
constructor: (node) ->
|
5
5
|
@nodes = [node]
|
@@ -28,11 +28,15 @@ Editor.Parsers.push class
|
|
28
28
|
|
29
29
|
render: =>
|
30
30
|
pre = "<pre>".toHTML()
|
31
|
-
code = "<code>".toHTML()
|
31
|
+
code = "<code as='Editor.Code'>".toHTML()
|
32
|
+
|
32
33
|
if @match[3]?
|
33
34
|
code.classList.add("language-#{@match[3]}")
|
35
|
+
|
34
36
|
texts = @nodes.map (n) -> n.textContent
|
35
|
-
|
37
|
+
|
38
|
+
code.textContent = texts.join('\n')
|
39
|
+
|
36
40
|
Prism.highlightElement(code)
|
37
41
|
pre.appendChild(code)
|
38
42
|
pre
|
@@ -14,7 +14,7 @@ Editor.Parsers.push class
|
|
14
14
|
@uploader = new Editor.ImageUploader(@show)
|
15
15
|
|
16
16
|
|
17
|
-
@picture = "<
|
17
|
+
@picture = "<div class='image' as='Editor.Image'>".toHTML()
|
18
18
|
|
19
19
|
@title = "<em></em>".toHTML()
|
20
20
|
@title.appendChild document.createTextNode(@match[1])
|
@@ -3,6 +3,31 @@ body.edit.posts > main > section {
|
|
3
3
|
@include flex(1);
|
4
4
|
outline: none;
|
5
5
|
font-size: 1.2em;
|
6
|
+
padding-bottom: 10em;
|
7
|
+
|
8
|
+
h1 {
|
9
|
+
font-size: 2.6em;
|
10
|
+
}
|
11
|
+
|
12
|
+
h2 {
|
13
|
+
font-size: 2.3em;
|
14
|
+
}
|
15
|
+
|
16
|
+
h3 {
|
17
|
+
font-size: 2.0em;
|
18
|
+
}
|
19
|
+
|
20
|
+
h4 {
|
21
|
+
font-size: 1.7em;
|
22
|
+
}
|
23
|
+
|
24
|
+
h5 {
|
25
|
+
font-size: 1.6em;
|
26
|
+
}
|
27
|
+
|
28
|
+
h6 {
|
29
|
+
font-size: 1.3em;
|
30
|
+
}
|
6
31
|
|
7
32
|
& > *, & > ul > li, & > ol > li {
|
8
33
|
white-space: pre-wrap;
|
@@ -15,7 +40,11 @@ body.edit.posts > main > section {
|
|
15
40
|
margin-left: 2em;
|
16
41
|
}
|
17
42
|
|
18
|
-
& >
|
43
|
+
& > pre {
|
44
|
+
background: rgba(0,0,0, 0.1);
|
45
|
+
}
|
46
|
+
|
47
|
+
& > div.image {
|
19
48
|
@include display(flex);
|
20
49
|
@include flex-direction(column);
|
21
50
|
@include transition(background-color 0.3s);
|
data/lib/ecrire/db/schema.rb
CHANGED
@@ -17,14 +17,9 @@ ActiveRecord::Schema.define(version: 20150120093481) do
|
|
17
17
|
enable_extension "plpgsql"
|
18
18
|
enable_extension "hstore"
|
19
19
|
|
20
|
-
create_table "abtests", force: :cascade do |t|
|
21
|
-
t.datetime "created_at"
|
22
|
-
t.datetime "updated_at"
|
23
|
-
end
|
24
|
-
|
25
20
|
create_table "images", force: :cascade do |t|
|
26
|
-
t.string "url"
|
27
|
-
t.string "key"
|
21
|
+
t.string "url"
|
22
|
+
t.string "key"
|
28
23
|
t.integer "post_id"
|
29
24
|
t.datetime "created_at"
|
30
25
|
t.datetime "updated_at"
|
@@ -32,7 +27,7 @@ ActiveRecord::Schema.define(version: 20150120093481) do
|
|
32
27
|
end
|
33
28
|
|
34
29
|
create_table "labels", force: :cascade do |t|
|
35
|
-
t.string "name",
|
30
|
+
t.string "name", null: false
|
36
31
|
t.datetime "created_at"
|
37
32
|
t.datetime "updated_at"
|
38
33
|
end
|
@@ -42,15 +37,15 @@ ActiveRecord::Schema.define(version: 20150120093481) do
|
|
42
37
|
create_table "partials", force: :cascade do |t|
|
43
38
|
t.datetime "created_at"
|
44
39
|
t.datetime "updated_at"
|
45
|
-
t.string "title",
|
40
|
+
t.string "title", null: false
|
46
41
|
t.text "content"
|
47
42
|
t.text "stylesheet"
|
48
43
|
t.text "javascript"
|
49
44
|
end
|
50
45
|
|
51
46
|
create_table "posts", force: :cascade do |t|
|
52
|
-
t.string "title",
|
53
|
-
t.string "slug",
|
47
|
+
t.string "title", null: false
|
48
|
+
t.string "slug", null: false
|
54
49
|
t.text "content"
|
55
50
|
t.text "stylesheet"
|
56
51
|
t.datetime "published_at"
|
@@ -65,8 +60,8 @@ ActiveRecord::Schema.define(version: 20150120093481) do
|
|
65
60
|
add_index "posts", ["title"], name: "index_posts_on_title", unique: true, using: :btree
|
66
61
|
|
67
62
|
create_table "users", force: :cascade do |t|
|
68
|
-
t.string "email",
|
69
|
-
t.string "encrypted_password",
|
63
|
+
t.string "email", null: false
|
64
|
+
t.string "encrypted_password", null: false
|
70
65
|
t.datetime "created_at"
|
71
66
|
t.datetime "updated_at"
|
72
67
|
end
|
@@ -8,7 +8,7 @@ module Sprockets
|
|
8
8
|
module Rails
|
9
9
|
class Task < Rake::SprocketsTask
|
10
10
|
def output
|
11
|
-
File.join(Ecrire::Railtie.paths['public'].
|
11
|
+
File.join(Ecrire::Railtie.paths['public'].expanded.first, app.config.assets.prefix)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/ecrire/template/Gemfile
CHANGED
data/lib/ecrire/version.rb
CHANGED
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.21.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-02-
|
11
|
+
date: 2015-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Blog engine on Rails
|
14
14
|
email: pothibo@gmail.com
|
@@ -29,6 +29,7 @@ files:
|
|
29
29
|
- lib/ecrire/app/assets/javascripts/admin/editor/content.coffee
|
30
30
|
- lib/ecrire/app/assets/javascripts/admin/editor/ext.coffee
|
31
31
|
- lib/ecrire/app/assets/javascripts/admin/editor/extensions/clipboard.coffee
|
32
|
+
- lib/ecrire/app/assets/javascripts/admin/editor/extensions/code.coffee
|
32
33
|
- lib/ecrire/app/assets/javascripts/admin/editor/extensions/image.coffee
|
33
34
|
- lib/ecrire/app/assets/javascripts/admin/editor/navigation/delete.js.coffee
|
34
35
|
- lib/ecrire/app/assets/javascripts/admin/editor/navigation/draft.js.coffee
|
@@ -192,8 +193,10 @@ files:
|
|
192
193
|
- lib/ecrire/railtie/onboarding.rb
|
193
194
|
- lib/ecrire/railtie/theme.rb
|
194
195
|
- lib/ecrire/tasks/assets.rake
|
196
|
+
- lib/ecrire/tasks/database.rake
|
195
197
|
- lib/ecrire/tasks/routes.rake
|
196
198
|
- lib/ecrire/template/Gemfile
|
199
|
+
- lib/ecrire/template/Procfile
|
197
200
|
- lib/ecrire/template/Rakefile
|
198
201
|
- lib/ecrire/template/assets/images/.keep
|
199
202
|
- lib/ecrire/template/assets/javascripts/base.js.coffee
|
@@ -268,7 +271,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
271
|
version: '0'
|
269
272
|
requirements: []
|
270
273
|
rubyforge_project:
|
271
|
-
rubygems_version: 2.
|
274
|
+
rubygems_version: 2.2.2
|
272
275
|
signing_key:
|
273
276
|
specification_version: 4
|
274
277
|
summary: Blog engine
|