ecrire 0.30.3 → 0.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +17 -13
  3. data/Rakefile +1 -8
  4. data/lib/ecrire.rb +1 -2
  5. data/lib/ecrire/app/assets/javascripts/admin.js +1 -0
  6. data/lib/ecrire/app/assets/javascripts/admin/navigation/save.coffee +8 -8
  7. data/lib/ecrire/app/assets/javascripts/admin/posts/content.coffee +23 -0
  8. data/lib/ecrire/app/assets/javascripts/admin/posts/filters/tags.coffee +12 -15
  9. data/lib/ecrire/app/assets/javascripts/admin/posts/header.coffee +13 -12
  10. data/lib/ecrire/app/assets/javascripts/admin/posts/list.coffee +3 -3
  11. data/lib/ecrire/app/assets/javascripts/admin/posts/tags.coffee +2 -2
  12. data/lib/ecrire/app/assets/javascripts/admin/posts/tags/list.coffee +1 -1
  13. data/lib/ecrire/app/assets/javascripts/admin/posts/title.coffee +2 -2
  14. data/lib/ecrire/app/assets/javascripts/admin/posts/titles.coffee +2 -2
  15. data/lib/ecrire/app/assets/javascripts/admin/tags/filter/list.coffee +19 -0
  16. data/lib/ecrire/app/assets/javascripts/shared/overlay.coffee +1 -1
  17. data/lib/ecrire/app/assets/javascripts/shared/popup.coffee +2 -2
  18. data/lib/ecrire/app/assets/stylesheets/admin/posts/search.scss +2 -2
  19. data/lib/ecrire/app/assets/stylesheets/admin/preview.scss +19 -0
  20. data/lib/ecrire/app/assets/stylesheets/editor/content.scss +1 -0
  21. data/lib/ecrire/app/controllers/admin/posts_controller.rb +1 -1
  22. data/lib/ecrire/app/helpers/admin/images_helper.rb +1 -2
  23. data/lib/ecrire/app/helpers/admin/posts_helper.rb +8 -7
  24. data/lib/ecrire/app/helpers/application_helper.rb +1 -1
  25. data/lib/ecrire/app/models/admin/post.rb +17 -11
  26. data/lib/ecrire/app/models/post.rb +6 -1
  27. data/lib/ecrire/app/views/admin/posts/edit.html.erb +7 -2
  28. data/lib/ecrire/app/views/admin/posts/header/_base.html.erb +0 -3
  29. data/lib/ecrire/app/views/admin/posts/index.html.erb +3 -3
  30. data/lib/ecrire/application.rb +1 -0
  31. data/lib/ecrire/db/migrate/20160501848734_change_content_for_hstore.rb +41 -0
  32. data/lib/ecrire/db/schema.rb +13 -9
  33. data/lib/ecrire/theme/template/Gemfile +5 -6
  34. data/lib/ecrire/version.rb +1 -1
  35. data/test/editor/models/post_test.rb +54 -21
  36. data/test/fixtures/posts.yml +4 -2
  37. data/test/theme/controllers/posts_controller_test.rb +1 -1
  38. data/test/theme/theme/views/posts/show.html.erb +1 -1
  39. metadata +6 -32
  40. data/lib/ecrire/app/assets/javascripts/admin/editor.coffee +0 -1
  41. data/lib/ecrire/app/assets/javascripts/admin/editor/content.coffee +0 -385
  42. data/lib/ecrire/app/assets/javascripts/admin/editor/ext.coffee +0 -106
  43. data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/clipboard.coffee +0 -114
  44. data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/code.coffee +0 -9
  45. data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/image.coffee +0 -91
  46. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/code.coffee +0 -97
  47. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/heading.coffee +0 -19
  48. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/image.coffee +0 -20
  49. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/link.coffee +0 -44
  50. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/lists.coffee +0 -48
  51. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/word.coffee +0 -56
  52. data/lib/ecrire/markdown.rb +0 -13
  53. data/lib/ecrire/markdown/document.rb +0 -42
  54. data/lib/ecrire/markdown/node.rb +0 -21
  55. data/lib/ecrire/markdown/nodes/code.rb +0 -63
  56. data/lib/ecrire/markdown/nodes/heading.rb +0 -15
  57. data/lib/ecrire/markdown/nodes/image.rb +0 -14
  58. data/lib/ecrire/markdown/nodes/ordered_list.rb +0 -18
  59. data/lib/ecrire/markdown/nodes/unordered_list.rb +0 -19
  60. data/lib/ecrire/markdown/parsers.rb +0 -13
  61. data/lib/ecrire/markdown/parsers/base.rb +0 -26
  62. data/lib/ecrire/markdown/parsers/code.rb +0 -62
  63. data/lib/ecrire/markdown/parsers/heading.rb +0 -19
  64. data/lib/ecrire/markdown/parsers/image.rb +0 -19
  65. data/lib/ecrire/markdown/parsers/link.rb +0 -12
  66. data/lib/ecrire/markdown/parsers/list.rb +0 -33
  67. data/lib/ecrire/markdown/parsers/word.rb +0 -16
  68. data/test/markdown/markdown_test.rb +0 -83
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 258dfb5d0362d97435137502ae3f3a5c3a21289e
4
- data.tar.gz: 34cc4f333cf6bc2ec832675be825a370430e01f8
3
+ metadata.gz: 6a7e27d92b15dca6a3e8af8205fe5b5b077cb2a8
4
+ data.tar.gz: 0ae29bd5af4b8bc7c2ded6d07757acb6dc7d3501
5
5
  SHA512:
6
- metadata.gz: 68e7c2876eb3029a02a3dff1ddda078a1ea748fdb9d1b02496cb18f0c6d56f4e9b837afccd14b6777673a952bd783c704d9b7c92a035d858c90b7a602f86e3ff
7
- data.tar.gz: 6c8e2ff475b32c8f8d57e60c48194a04db7d797afb50d2faf5b6ea7aa0a8122f87fa349284ac0071245812213b45f0900957ef01441aa76f7aa247c9cec5c1b8
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', '~> 4.2'
5
+ gem 'rails'
6
6
  gem 'observejs', git: 'https://github.com/pothibo/observejs.git'
7
- gem 'warden', '~> 1.2'
8
- gem 'bcrypt', '~> 3.1'
9
- gem 'nokogiri', '~> 1.6'
10
- gem 's3', '~> 0.3'
11
- gem 'pg', '~> 0.17'
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', '~> 0.15'
14
+ gem 'kaminari'
14
15
 
15
- gem 'sprockets-rails', '~> 2.1'
16
- gem 'sass-rails', '~> 4.0', '>= 4.0.3'
17
- gem 'coffee-rails', '~> 4.0'
18
- gem 'turbolinks', '~> 2.2'
19
- gem 'bourbon', '~> 3.2'
20
- gem 'uglifier', '~> 2.5'
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:markdown test:editor test:onboarding test:theme).each do |name|
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.exists?(ENV['BUNDLE_GEMFILE'])
11
+ File.exist?(ENV['BUNDLE_GEMFILE'])
13
12
  end
14
13
 
15
14
  if Ecrire.bundle?
@@ -1,5 +1,6 @@
1
1
  //= require turbolinks
2
2
  //= require observejs
3
+ //= require written
3
4
  //= require_tree ./admin
4
5
  //= require_tree ./vendor
5
6
  //= require_tree ./shared
@@ -1,11 +1,10 @@
1
1
  ObserveJS.bind 'Editor.Save', class
2
2
  loaded: =>
3
3
  @on 'click', @save
4
- @on 'keydown', window, @shouldSave
5
- @on 'Editor:loaded', document, @cache
6
- @on 'Editor:updated', document, @update
7
- @on 'posts:update', document, @saved
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 = @retrieve('#SavePost')
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
- @on 'posts:update', document, @saved
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
- @element().appendChild(@retrieve('svg.placeholder'))
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.target
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 = @retrieve('span.tag')
15
+ span = @template('span.tag')
20
16
  span.textContent = el.dataset.name
21
17
  input.value = el.getAttribute('oid')
22
18
 
23
- @retrieve('svg.placeholder').remove()
24
- @element().appendChild(@retrieve('svg.clear'))
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
- if @retrieve('svg.clear').contains(e.target)
34
- @retrieve('svg.clear').remove()
35
- @retrieve('span').remove()
36
- @element().appendChild(@retrieve('svg.placeholder'))
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
- @on 'ObserveJS:XHR:Failed', @failed
10
+ @when 'ObserveJS:XHR:Failed', @failed
11
11
 
12
12
  @on 'images:create', @refresh
13
13
  @on 'images:destroy', @refresh
14
14
 
15
- @on 'scroll', window, @resize
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 = @retrieve('div.status.uploading').querySelector('.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(@retrieve('div.status.uploading'))
64
- @show(@retrieve('div.error.status'))
65
- ul = @retrieve('div.error.status').querySelector('ul')
66
- @on 'click', @retrieve('div.error.status').querySelector('button'), @clear
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(@retrieve('div.dropping.status'))
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(@retrieve('div.status.uploading'))
106
- @retrieve('div.status.uploading').querySelector('.progressbar > span').style.width = '0%';
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(@retrieve('div.status.uploading'))
114
+ @element().firstElementChild.appendChild(@template('div.status.uploading'))
114
115
 
115
116
  clear: (e) =>
116
117
  @element().classList.remove 'image'
117
- for li in @retrieve('div.error.status').querySelectorAll('li')
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
- @on 'posts:index', document, @refresh
4
- @on 'posts:drafts', document, @refresh
5
- @on 'posts:published', document, @refresh
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,7 +1,7 @@
1
1
  ObserveJS.bind 'Post.Tags', class
2
2
  loaded: =>
3
- @on 'tags:toggle', document, @refresh
4
- @on 'tags:create', document, @refresh
3
+ @when 'tags:toggle', @refresh
4
+ @when 'tags:create', @refresh
5
5
  @on 'tags:index', @show
6
6
 
7
7
  refresh: (e) =>
@@ -1,7 +1,7 @@
1
1
  ObserveJS.bind 'Post.Tags.List', class
2
2
  loaded: =>
3
3
  @on 'mouseover', @clear
4
- @on 'keydown', @element().parentElement, @navigate
4
+ @when 'keydown', @navigate
5
5
 
6
6
  navigate: (e) =>
7
7
  switch e.keyCode
@@ -1,8 +1,8 @@
1
1
  ObserveJS.bind 'Post.Title', class
2
2
  loaded: =>
3
3
  @on 'titles:index', @show
4
- @on 'titles:update', document, @refresh
5
- @on 'titles:create', document, @refresh
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
- @on 'titles:update', document, @refresh
5
- @on 'titles:create', document, @refresh
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
+
@@ -1,7 +1,7 @@
1
1
  ObserveJS.bind 'Overlay', class
2
2
  loaded: =>
3
3
  @on 'click', @clicked
4
- @on 'keyup', document, @escaped
4
+ @when 'keyup', @escaped
5
5
  @on 'dialog:close', @remove
6
6
 
7
7
  @y = document.body.scrollTop
@@ -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
- @on 'click', document, @clicked
6
- @on 'keyup', document, @escaped
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 > div.search {
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 > div.search > input[type=text] {
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;
@@ -36,6 +36,7 @@ article.content {
36
36
  margin-left: 2em;
37
37
  li {
38
38
  margin: 0.2em 0;
39
+ padding: 0;
39
40
  }
40
41
  }
41
42
 
@@ -98,7 +98,7 @@ module Admin
98
98
  end
99
99
 
100
100
  def post_params
101
- params.require(:post).permit(:content, :status, :stylesheet, :javascript, :slug)
101
+ params.require(:post).permit(:status, :stylesheet, :javascript, :slug, content: [:raw, :html])
102
102
  end
103
103
 
104
104
  def fetch_post