ecrire 0.30.3 → 0.31.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.
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