ecrire 0.30.3 → 0.31.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +17 -13
- data/Rakefile +1 -8
- data/lib/ecrire.rb +1 -2
- data/lib/ecrire/app/assets/javascripts/admin.js +1 -0
- data/lib/ecrire/app/assets/javascripts/admin/navigation/save.coffee +8 -8
- data/lib/ecrire/app/assets/javascripts/admin/posts/content.coffee +23 -0
- data/lib/ecrire/app/assets/javascripts/admin/posts/filters/tags.coffee +12 -15
- data/lib/ecrire/app/assets/javascripts/admin/posts/header.coffee +13 -12
- data/lib/ecrire/app/assets/javascripts/admin/posts/list.coffee +3 -3
- data/lib/ecrire/app/assets/javascripts/admin/posts/tags.coffee +2 -2
- data/lib/ecrire/app/assets/javascripts/admin/posts/tags/list.coffee +1 -1
- data/lib/ecrire/app/assets/javascripts/admin/posts/title.coffee +2 -2
- data/lib/ecrire/app/assets/javascripts/admin/posts/titles.coffee +2 -2
- data/lib/ecrire/app/assets/javascripts/admin/tags/filter/list.coffee +19 -0
- data/lib/ecrire/app/assets/javascripts/shared/overlay.coffee +1 -1
- data/lib/ecrire/app/assets/javascripts/shared/popup.coffee +2 -2
- data/lib/ecrire/app/assets/stylesheets/admin/posts/search.scss +2 -2
- data/lib/ecrire/app/assets/stylesheets/admin/preview.scss +19 -0
- data/lib/ecrire/app/assets/stylesheets/editor/content.scss +1 -0
- data/lib/ecrire/app/controllers/admin/posts_controller.rb +1 -1
- data/lib/ecrire/app/helpers/admin/images_helper.rb +1 -2
- data/lib/ecrire/app/helpers/admin/posts_helper.rb +8 -7
- data/lib/ecrire/app/helpers/application_helper.rb +1 -1
- data/lib/ecrire/app/models/admin/post.rb +17 -11
- data/lib/ecrire/app/models/post.rb +6 -1
- data/lib/ecrire/app/views/admin/posts/edit.html.erb +7 -2
- data/lib/ecrire/app/views/admin/posts/header/_base.html.erb +0 -3
- data/lib/ecrire/app/views/admin/posts/index.html.erb +3 -3
- data/lib/ecrire/application.rb +1 -0
- data/lib/ecrire/db/migrate/20160501848734_change_content_for_hstore.rb +41 -0
- data/lib/ecrire/db/schema.rb +13 -9
- data/lib/ecrire/theme/template/Gemfile +5 -6
- data/lib/ecrire/version.rb +1 -1
- data/test/editor/models/post_test.rb +54 -21
- data/test/fixtures/posts.yml +4 -2
- data/test/theme/controllers/posts_controller_test.rb +1 -1
- data/test/theme/theme/views/posts/show.html.erb +1 -1
- metadata +6 -32
- data/lib/ecrire/app/assets/javascripts/admin/editor.coffee +0 -1
- data/lib/ecrire/app/assets/javascripts/admin/editor/content.coffee +0 -385
- data/lib/ecrire/app/assets/javascripts/admin/editor/ext.coffee +0 -106
- data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/clipboard.coffee +0 -114
- data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/code.coffee +0 -9
- data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/image.coffee +0 -91
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/code.coffee +0 -97
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/heading.coffee +0 -19
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/image.coffee +0 -20
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/link.coffee +0 -44
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/lists.coffee +0 -48
- data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/word.coffee +0 -56
- data/lib/ecrire/markdown.rb +0 -13
- data/lib/ecrire/markdown/document.rb +0 -42
- data/lib/ecrire/markdown/node.rb +0 -21
- data/lib/ecrire/markdown/nodes/code.rb +0 -63
- data/lib/ecrire/markdown/nodes/heading.rb +0 -15
- data/lib/ecrire/markdown/nodes/image.rb +0 -14
- data/lib/ecrire/markdown/nodes/ordered_list.rb +0 -18
- data/lib/ecrire/markdown/nodes/unordered_list.rb +0 -19
- data/lib/ecrire/markdown/parsers.rb +0 -13
- data/lib/ecrire/markdown/parsers/base.rb +0 -26
- data/lib/ecrire/markdown/parsers/code.rb +0 -62
- data/lib/ecrire/markdown/parsers/heading.rb +0 -19
- data/lib/ecrire/markdown/parsers/image.rb +0 -19
- data/lib/ecrire/markdown/parsers/link.rb +0 -12
- data/lib/ecrire/markdown/parsers/list.rb +0 -33
- data/lib/ecrire/markdown/parsers/word.rb +0 -16
- data/test/markdown/markdown_test.rb +0 -83
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a7e27d92b15dca6a3e8af8205fe5b5b077cb2a8
|
4
|
+
data.tar.gz: 0ae29bd5af4b8bc7c2ded6d07757acb6dc7d3501
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0717cf0826f164e19079fe379db5c1f7add7e4062245de7d31a334ac6b47767c7262e975ead466ac42eabd975e6278707c7115338b52f80d9ec3906f8839e9d0
|
7
|
+
data.tar.gz: 8193ad8ffa679b2294c6330c4ca29b239d1e513207aa11b1c490091d5d005d6ee7b4ffe7a3ff2543b414260856fd46e652d9c2413f100ba18d98f6ecc9d033ef
|
data/Gemfile
CHANGED
@@ -2,22 +2,26 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
gem 'rails'
|
5
|
+
gem 'rails'
|
6
6
|
gem 'observejs', git: 'https://github.com/pothibo/observejs.git'
|
7
|
-
gem '
|
8
|
-
gem '
|
9
|
-
gem '
|
10
|
-
gem '
|
11
|
-
gem '
|
7
|
+
gem 'written'
|
8
|
+
gem 'warden'
|
9
|
+
gem 'bcrypt'
|
10
|
+
gem 'nokogiri'
|
11
|
+
gem 's3'
|
12
|
+
gem 'pg'
|
12
13
|
gem 'pg_search'
|
13
|
-
gem 'kaminari'
|
14
|
+
gem 'kaminari'
|
14
15
|
|
15
|
-
gem 'sprockets-rails'
|
16
|
-
gem 'sass-rails'
|
17
|
-
gem 'coffee-rails'
|
18
|
-
gem 'turbolinks'
|
19
|
-
gem 'bourbon'
|
20
|
-
gem 'uglifier'
|
16
|
+
gem 'sprockets-rails'
|
17
|
+
gem 'sass-rails'
|
18
|
+
gem 'coffee-rails'
|
19
|
+
gem 'turbolinks'
|
20
|
+
gem 'bourbon'
|
21
|
+
gem 'uglifier'
|
21
22
|
gem 'jbuilder'
|
22
23
|
|
24
|
+
gem 'rails_12factor', group: :production
|
25
|
+
|
23
26
|
gem 'byebug'
|
27
|
+
gem 'specterjs'
|
data/Rakefile
CHANGED
@@ -60,17 +60,10 @@ namespace :test do
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
Rake::TestTask.new 'markdown' do |t|
|
64
|
-
t.libs << "test"
|
65
|
-
t.test_files = FileList["test/markdown/**/*_test.rb"]
|
66
|
-
t.verbose = true
|
67
|
-
end
|
68
|
-
|
69
|
-
|
70
63
|
end
|
71
64
|
|
72
65
|
task :test do
|
73
|
-
%w(test:
|
66
|
+
%w(test:editor test:onboarding test:theme).each do |name|
|
74
67
|
Rake::Task[name].invoke
|
75
68
|
end
|
76
69
|
end
|
data/lib/ecrire.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Ecrire
|
2
2
|
|
3
3
|
autoload :Application, 'ecrire/application'
|
4
|
-
autoload :Markdown, 'ecrire/markdown'
|
5
4
|
|
6
5
|
##
|
7
6
|
# Returns true if Ecrire could find
|
@@ -9,7 +8,7 @@ module Ecrire
|
|
9
8
|
#
|
10
9
|
def self.bundle?
|
11
10
|
ENV['BUNDLE_GEMFILE'] ||= Dir.pwd + '/Gemfile'
|
12
|
-
File.
|
11
|
+
File.exist?(ENV['BUNDLE_GEMFILE'])
|
13
12
|
end
|
14
13
|
|
15
14
|
if Ecrire.bundle?
|
@@ -1,11 +1,10 @@
|
|
1
1
|
ObserveJS.bind 'Editor.Save', class
|
2
2
|
loaded: =>
|
3
3
|
@on 'click', @save
|
4
|
-
@
|
5
|
-
@
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@on 'beforeunload', window, @confirm
|
4
|
+
@when 'keydown', @shouldSave
|
5
|
+
@when 'Editor:loaded', @cache
|
6
|
+
@when 'posts:update', @saved
|
7
|
+
@when 'beforeunload', @confirm
|
9
8
|
|
10
9
|
confirm: (e) =>
|
11
10
|
if @cache() != PostBody.instance.toString()
|
@@ -31,7 +30,7 @@ ObserveJS.bind 'Editor.Save', class
|
|
31
30
|
e.preventDefault()
|
32
31
|
e.stopPropagation()
|
33
32
|
|
34
|
-
dialog = @
|
33
|
+
dialog = @template('#SavePost')
|
35
34
|
|
36
35
|
if e.type == 'click'
|
37
36
|
dialog.dataset.preview = true
|
@@ -43,9 +42,10 @@ ObserveJS.bind 'Editor.Save', class
|
|
43
42
|
|
44
43
|
ObserveJS.bind 'Editor.Save.Dialog', class
|
45
44
|
loaded: =>
|
46
|
-
@
|
45
|
+
@when 'posts:update', @saved
|
47
46
|
xhr = new ObserveJS.XHR(@element())
|
48
|
-
xhr.data.set('post[content]', PostBody.instance.toString())
|
47
|
+
xhr.data.set('post[content][raw]', PostBody.instance.history.current.toString())
|
48
|
+
xhr.data.set('post[content][html]', PostBody.instance.history.current.toHTMLString())
|
49
49
|
xhr.data.set('context', 'content')
|
50
50
|
xhr.request.upload.addEventListener 'progress', @upload
|
51
51
|
xhr.request.addEventListener 'progress', @download
|
@@ -0,0 +1,23 @@
|
|
1
|
+
ObserveJS.bind 'Editor.Content', class
|
2
|
+
loaded: =>
|
3
|
+
code = Written.Parsers.Block.get('Code').prototype
|
4
|
+
code.highlight = (element) ->
|
5
|
+
Prism.highlightElement(element, false)
|
6
|
+
|
7
|
+
code = Written.Parsers.Inline.get('Code').prototype
|
8
|
+
code.highlight = (element) ->
|
9
|
+
Prism.highlightElement(element, false)
|
10
|
+
|
11
|
+
if @element().dataset.bucket?
|
12
|
+
uploader = new Written.Uploaders.AWS({
|
13
|
+
bucket: @element().dataset.bucket,
|
14
|
+
namespace: @element().dataset.namespace,
|
15
|
+
url: @element().dataset.url,
|
16
|
+
accessKey: @element().dataset.accessKey,
|
17
|
+
policy: @element().dataset.policy,
|
18
|
+
signature: @element().dataset.signature
|
19
|
+
})
|
20
|
+
|
21
|
+
Written.Parsers.Block.get('Image').uploader(uploader)
|
22
|
+
@written = new Written(this.element())
|
23
|
+
@written.initialize()
|
@@ -2,26 +2,22 @@ ObserveJS.bind 'Posts.Filter.Tags', class
|
|
2
2
|
loaded: =>
|
3
3
|
@on 'tags:index', @show
|
4
4
|
@on 'click', @action
|
5
|
-
@
|
5
|
+
@when 'tags:filter:list:selected', @select
|
6
|
+
@element().appendChild(@template('svg.placeholder'))
|
6
7
|
|
7
8
|
show: (e) =>
|
9
|
+
e.HTML.querySelector('ul')?.setAttribute('as', 'Tags.Filter.List')
|
8
10
|
document.body.appendChild(e.HTML)
|
9
|
-
@on 'click', e.HTML, @select
|
10
11
|
|
11
12
|
select: (e) =>
|
12
|
-
el = e.
|
13
|
-
while el && !(el instanceof HTMLLIElement)
|
14
|
-
el = el.parentElement
|
15
|
-
|
16
|
-
return unless el?
|
17
|
-
|
13
|
+
el = e.detail
|
18
14
|
input = @element().querySelector('input[type=hidden]')
|
19
|
-
span = @
|
15
|
+
span = @template('span.tag')
|
20
16
|
span.textContent = el.dataset.name
|
21
17
|
input.value = el.getAttribute('oid')
|
22
18
|
|
23
|
-
@
|
24
|
-
@element().appendChild(@
|
19
|
+
@element().querySelector('svg.placeholder')?.remove()
|
20
|
+
@element().appendChild(@template('svg.clear'))
|
25
21
|
@element().appendChild(span)
|
26
22
|
@element().classList.add 'tagged'
|
27
23
|
|
@@ -30,10 +26,11 @@ ObserveJS.bind 'Posts.Filter.Tags', class
|
|
30
26
|
@changed(input)
|
31
27
|
|
32
28
|
action: (e) =>
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
@element().
|
29
|
+
clear = @element().querySelector('svg.clear')
|
30
|
+
if clear? && clear.contains(e.target)
|
31
|
+
clear.remove()
|
32
|
+
@element().querySelector('span')?.remove()
|
33
|
+
@element().appendChild(@template('svg.placeholder'))
|
37
34
|
@element().classList.remove 'tagged'
|
38
35
|
input = @element().querySelector('input[type=hidden]')
|
39
36
|
input.value = null
|
@@ -7,12 +7,12 @@ ObserveJS.bind 'Post.Header', class
|
|
7
7
|
@on 'dragleave', @cancel
|
8
8
|
@on 'drop', @drop
|
9
9
|
|
10
|
-
@
|
10
|
+
@when 'ObserveJS:XHR:Failed', @failed
|
11
11
|
|
12
12
|
@on 'images:create', @refresh
|
13
13
|
@on 'images:destroy', @refresh
|
14
14
|
|
15
|
-
@
|
15
|
+
@when 'scroll', @resize
|
16
16
|
|
17
17
|
@maxHeight = parseFloat(window.getComputedStyle(this.element())['height'])
|
18
18
|
|
@@ -55,15 +55,15 @@ ObserveJS.bind 'Post.Header', class
|
|
55
55
|
progress: (e) =>
|
56
56
|
return unless e.lengthComputable
|
57
57
|
percentComplete = e.loaded / e.total * 100.0;
|
58
|
-
progressBar = @
|
58
|
+
progressBar = @element().querySelector('div.status.uploading .progressbar')
|
59
59
|
progressBar.firstElementChild.style.width = "#{percentComplete}%";
|
60
60
|
|
61
61
|
failed: (e) =>
|
62
62
|
errors = JSON.parse(e.response.target.responseText)
|
63
|
-
@hide(@
|
64
|
-
@show(@
|
65
|
-
ul = @
|
66
|
-
@on 'click', @
|
63
|
+
@hide(@template('div.status.uploading'))
|
64
|
+
@show(@template('div.error.status'))
|
65
|
+
ul = @template('div.error.status').querySelector('ul')
|
66
|
+
@on 'click', @template('div.error.status').querySelector('button'), @clear
|
67
67
|
for error in errors
|
68
68
|
ul.insertAdjacentHTML('beforeend', "<li>#{error}</li>")
|
69
69
|
|
@@ -74,12 +74,13 @@ ObserveJS.bind 'Post.Header', class
|
|
74
74
|
xhr = new ObserveJS.XHR(e.currentTarget)
|
75
75
|
xhr.request.upload.onprogress = @progress
|
76
76
|
xhr.send()
|
77
|
+
@show(@template('div.status.uploading'))
|
77
78
|
|
78
79
|
over: (e) =>
|
79
80
|
e.preventDefault()
|
80
81
|
if !@element().classList.contains('image')
|
81
82
|
@element().classList.add 'image'
|
82
|
-
@show(@
|
83
|
+
@show(@template('div.dropping.status'))
|
83
84
|
|
84
85
|
cancel: (e) =>
|
85
86
|
e.preventDefault()
|
@@ -102,19 +103,19 @@ ObserveJS.bind 'Post.Header', class
|
|
102
103
|
if !@element().classList.contains('image')
|
103
104
|
@element().classList.add 'image'
|
104
105
|
|
105
|
-
@show(@
|
106
|
-
@
|
106
|
+
@show(@template('div.status.uploading'))
|
107
|
+
@template('div.status.uploading').querySelector('.progressbar > span').style.width = '0%';
|
107
108
|
xhr = new ObserveJS.XHR(@element())
|
108
109
|
xhr.data.set 'image[file]', file
|
109
110
|
xhr.request.upload.onprogress = @progress
|
110
111
|
xhr.send()
|
111
112
|
|
112
113
|
loading: =>
|
113
|
-
@element().firstElementChild.appendChild(@
|
114
|
+
@element().firstElementChild.appendChild(@template('div.status.uploading'))
|
114
115
|
|
115
116
|
clear: (e) =>
|
116
117
|
@element().classList.remove 'image'
|
117
|
-
for li in @
|
118
|
+
for li in @template('div.error.status').querySelectorAll('li')
|
118
119
|
li.remove()
|
119
120
|
|
120
121
|
for status of @statuses
|
@@ -1,8 +1,8 @@
|
|
1
1
|
ObserveJS.bind 'Posts.List', class
|
2
2
|
loaded: =>
|
3
|
-
@
|
4
|
-
@
|
5
|
-
@
|
3
|
+
@when 'posts:index', @refresh
|
4
|
+
@when 'posts:drafts', @refresh
|
5
|
+
@when 'posts:published', @refresh
|
6
6
|
|
7
7
|
refresh: (e) =>
|
8
8
|
@element().innerHTML = e.HTML.innerHTML
|
@@ -1,8 +1,8 @@
|
|
1
1
|
ObserveJS.bind 'Post.Title', class
|
2
2
|
loaded: =>
|
3
3
|
@on 'titles:index', @show
|
4
|
-
@
|
5
|
-
@
|
4
|
+
@when 'titles:update', @refresh
|
5
|
+
@when 'titles:create', @refresh
|
6
6
|
|
7
7
|
show: (e) =>
|
8
8
|
e.HTML.dataset.y = document.body.scrollTop
|
@@ -1,8 +1,8 @@
|
|
1
1
|
ObserveJS.bind 'Post.Titles', class
|
2
2
|
loaded: =>
|
3
3
|
@on 'submit', @clear
|
4
|
-
@
|
5
|
-
@
|
4
|
+
@when 'titles:update', @refresh
|
5
|
+
@when 'titles:create', @refresh
|
6
6
|
|
7
7
|
@element().querySelector('form input[type=text]').focus()
|
8
8
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
ObserveJS.bind 'Tags.Filter.List', class
|
2
|
+
loaded: =>
|
3
|
+
@on 'click', @select
|
4
|
+
|
5
|
+
select: (e) =>
|
6
|
+
el = e.target
|
7
|
+
while el && !(el instanceof HTMLLIElement)
|
8
|
+
el = el.parentElement
|
9
|
+
|
10
|
+
return unless el?
|
11
|
+
|
12
|
+
event = new CustomEvent('tags:filter:list:selected', {
|
13
|
+
bubbles: true
|
14
|
+
detail: el
|
15
|
+
})
|
16
|
+
|
17
|
+
@element().dispatchEvent(event)
|
18
|
+
|
19
|
+
|
@@ -2,8 +2,8 @@ ObserveJS.bind 'Popup', class
|
|
2
2
|
loaded: =>
|
3
3
|
if btn = @element().querySelector('.close')
|
4
4
|
@on 'click', btn, @remove
|
5
|
-
@
|
6
|
-
@
|
5
|
+
@when 'click', @clicked
|
6
|
+
@when 'keyup', @escaped
|
7
7
|
@on 'dialog:close', @remove
|
8
8
|
|
9
9
|
clicked: (e) =>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#PostsIndexList >
|
1
|
+
#PostsIndexList > form.search {
|
2
2
|
@include display(flex);
|
3
3
|
@include align-items(stretch);
|
4
4
|
|
@@ -8,7 +8,7 @@
|
|
8
8
|
margin: 5%;
|
9
9
|
}
|
10
10
|
|
11
|
-
#PostsIndexList >
|
11
|
+
#PostsIndexList > form.search > input[type=text] {
|
12
12
|
@include flex(1 100%);
|
13
13
|
min-width: 200px;
|
14
14
|
margin: 0 auto;
|
@@ -34,6 +34,25 @@
|
|
34
34
|
}
|
35
35
|
}
|
36
36
|
|
37
|
+
#PostPreviewContent > article > figure {
|
38
|
+
border: 1px solid $gray-blue;
|
39
|
+
|
40
|
+
img {
|
41
|
+
max-width: 100%;
|
42
|
+
margin: 12px;
|
43
|
+
}
|
44
|
+
|
45
|
+
figcaption {
|
46
|
+
text-align: center;
|
47
|
+
font-style: italic;
|
48
|
+
font-size: 0.8em;
|
49
|
+
color: $light-blue;
|
50
|
+
background-color: $gray-blue;
|
51
|
+
padding: 0.2em 0.4em;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
|
37
56
|
#PostPreviewContent pre {
|
38
57
|
background-color: $light-blue;
|
39
58
|
font-size: 0.7em;
|
@@ -98,7 +98,7 @@ module Admin
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def post_params
|
101
|
-
params.require(:post).permit(:
|
101
|
+
params.require(:post).permit(:status, :stylesheet, :javascript, :slug, content: [:raw, :html])
|
102
102
|
end
|
103
103
|
|
104
104
|
def fetch_post
|