editor_js 0.0.1 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e222430fd6770491df0b11e0c9ded81b5665e1cb210e65e63f007ba4893abe40
4
- data.tar.gz: e4d4ac156b4230316fb879310fd85ffe44dda4557222309c2c4b0264f1055e38
3
+ metadata.gz: 45e691b09b979e4afd888b80ec43cb81888affd974675ac383a3b87f75284f09
4
+ data.tar.gz: 96f7ed2d48805081e9abfd3fd828d90508d59c726301e8cc4b2448ca13758c62
5
5
  SHA512:
6
- metadata.gz: ac578d94be10200139f31fac87453638a9113c3d1fdca9bca500a346e1e3ce0f01b2e46f7fc0d0e1cd3a086027c868cbd571ae969eb9ef9648cb011fae24d8d2
7
- data.tar.gz: 624233c4177d3b6fbcc621dc2033d3300ebe5195c4918b163d13b21b4b43081c8c077fc143778a005f18d0b688ce588f2e9b912fdccb39d90315b9a1fd8e9906
6
+ metadata.gz: '09781ff6957c7bcfaed67300b6e2ad5b6deedb98c642ca58a7f3e0b3c1cbee9bcc7a41dcc96760750f649a60f930deeb288328354b840070b7a70ce8eb20b645'
7
+ data.tar.gz: 064a72ad6b97626c70c6e2a1767ba493d6c2b0c184de93136f4816dc0129130b2e9985777842772e08e136ef65b3eacb137beb51419ef51786d635fcea5222af
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
- --format documentation
2
1
  --color
3
2
  --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.5
data/.travis.yml CHANGED
@@ -5,3 +5,9 @@ cache: bundler
5
5
  rvm:
6
6
  - 2.5.5
7
7
  before_install: gem install bundler -v 2.0.2
8
+ before_script:
9
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
10
+ - chmod +x ./cc-test-reporter
11
+ - ./cc-test-reporter before-build
12
+ after_script:
13
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
data/Gemfile CHANGED
@@ -2,3 +2,9 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in editor_js.gemspec
4
4
  gemspec
5
+
6
+ gem "pry", "~> 0.12.2"
7
+
8
+ gem "rubocop", "~> 0.77.0"
9
+
10
+ gem "simplecov", "~> 0.17.1"
data/Gemfile.lock CHANGED
@@ -1,13 +1,72 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- editor_js (0.0.1)
4
+ editor_js (0.1.0)
5
+ actionview (>= 4)
6
+ activesupport (>= 4)
7
+ coderay (~> 1.1, >= 1.1.2)
8
+ htmlentities (~> 4.3, >= 4.3.4)
9
+ json-schema (~> 2)
10
+ redcarpet (~> 3.5)
11
+ sanitize (~> 5.1)
5
12
 
6
13
  GEM
7
14
  remote: https://rubygems.org/
8
15
  specs:
16
+ actionview (6.0.1)
17
+ activesupport (= 6.0.1)
18
+ builder (~> 3.1)
19
+ erubi (~> 1.4)
20
+ rails-dom-testing (~> 2.0)
21
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
22
+ activesupport (6.0.1)
23
+ concurrent-ruby (~> 1.0, >= 1.0.2)
24
+ i18n (>= 0.7, < 2)
25
+ minitest (~> 5.1)
26
+ tzinfo (~> 1.1)
27
+ zeitwerk (~> 2.2)
28
+ addressable (2.7.0)
29
+ public_suffix (>= 2.0.2, < 5.0)
30
+ ast (2.4.0)
31
+ builder (3.2.4)
32
+ coderay (1.1.2)
33
+ concurrent-ruby (1.1.5)
34
+ crass (1.0.5)
9
35
  diff-lcs (1.3)
36
+ docile (1.3.2)
37
+ erubi (1.9.0)
38
+ htmlentities (4.3.4)
39
+ i18n (1.7.0)
40
+ concurrent-ruby (~> 1.0)
41
+ jaro_winkler (1.5.4)
42
+ json (2.3.0)
43
+ json-schema (2.8.1)
44
+ addressable (>= 2.4)
45
+ loofah (2.4.0)
46
+ crass (~> 1.0.2)
47
+ nokogiri (>= 1.5.9)
48
+ method_source (0.9.2)
49
+ mini_portile2 (2.4.0)
50
+ minitest (5.13.0)
51
+ nokogiri (1.10.7)
52
+ mini_portile2 (~> 2.4.0)
53
+ nokogumbo (2.0.2)
54
+ nokogiri (~> 1.8, >= 1.8.4)
55
+ parallel (1.19.1)
56
+ parser (2.6.5.0)
57
+ ast (~> 2.4.0)
58
+ pry (0.12.2)
59
+ coderay (~> 1.1.0)
60
+ method_source (~> 0.9.0)
61
+ public_suffix (4.0.1)
62
+ rails-dom-testing (2.0.3)
63
+ activesupport (>= 4.2.0)
64
+ nokogiri (>= 1.6)
65
+ rails-html-sanitizer (1.3.0)
66
+ loofah (~> 2.3)
67
+ rainbow (3.0.0)
10
68
  rake (10.5.0)
69
+ redcarpet (3.5.0)
11
70
  rspec (3.9.0)
12
71
  rspec-core (~> 3.9.0)
13
72
  rspec-expectations (~> 3.9.0)
@@ -21,6 +80,28 @@ GEM
21
80
  diff-lcs (>= 1.2.0, < 2.0)
22
81
  rspec-support (~> 3.9.0)
23
82
  rspec-support (3.9.0)
83
+ rubocop (0.77.0)
84
+ jaro_winkler (~> 1.5.1)
85
+ parallel (~> 1.10)
86
+ parser (>= 2.6)
87
+ rainbow (>= 2.2.2, < 4.0)
88
+ ruby-progressbar (~> 1.7)
89
+ unicode-display_width (>= 1.4.0, < 1.7)
90
+ ruby-progressbar (1.10.1)
91
+ sanitize (5.1.0)
92
+ crass (~> 1.0.2)
93
+ nokogiri (>= 1.8.0)
94
+ nokogumbo (~> 2.0)
95
+ simplecov (0.17.1)
96
+ docile (~> 1.1)
97
+ json (>= 1.8, < 3)
98
+ simplecov-html (~> 0.10.0)
99
+ simplecov-html (0.10.2)
100
+ thread_safe (0.3.6)
101
+ tzinfo (1.2.5)
102
+ thread_safe (~> 0.1)
103
+ unicode-display_width (1.6.0)
104
+ zeitwerk (2.2.2)
24
105
 
25
106
  PLATFORMS
26
107
  ruby
@@ -28,8 +109,11 @@ PLATFORMS
28
109
  DEPENDENCIES
29
110
  bundler (~> 2.0)
30
111
  editor_js!
112
+ pry (~> 0.12.2)
31
113
  rake (~> 10.0)
32
114
  rspec (~> 3.0)
115
+ rubocop (~> 0.77.0)
116
+ simplecov (~> 0.17.1)
33
117
 
34
118
  BUNDLED WITH
35
119
  2.0.2
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # EditorJs
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/editor_js.svg)](https://badge.fury.io/rb/editor_js) [![Build Status](https://travis-ci.org/xiaohui-zhangxh/editor_js.svg?branch=master)](https://travis-ci.org/xiaohui-zhangxh/editor_js) [![Maintainability](https://api.codeclimate.com/v1/badges/e26bf8e27fb3a33735fd/maintainability)](https://codeclimate.com/github/xiaohui-zhangxh/editor_js/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/e26bf8e27fb3a33735fd/test_coverage)](https://codeclimate.com/github/xiaohui-zhangxh/editor_js/test_coverage) ![Downloads](https://ruby-gem-downloads-badge.herokuapp.com/editor_js?type=total)
4
+
3
5
  Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/editor_js`. To experiment with that code, run `bin/console` for an interactive prompt.
4
6
 
5
7
  TODO: Delete this and the text above, and describe your gem
@@ -22,7 +24,15 @@ Or install it yourself as:
22
24
 
23
25
  ## Usage
24
26
 
25
- TODO: Write usage instructions here
27
+ **Parse document:**
28
+
29
+ ```ruby
30
+ doc = EditorJs::Document.new(editor_js_output)
31
+ doc.render # [String] render HTML for display
32
+ doc.plain # [String] render text for full-text searching
33
+ doc.output # [Hash] return sanitized data
34
+
35
+ ```
26
36
 
27
37
  ## Development
28
38
 
data/bin/console CHANGED
@@ -7,8 +7,8 @@ require "editor_js"
7
7
  # with your gem easier. You can also use a different console, if you like.
8
8
 
9
9
  # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
10
+ require "pry"
11
+ Pry.start
12
12
 
13
- require "irb"
14
- IRB.start(__FILE__)
13
+ # require "irb"
14
+ # IRB.start(__FILE__)
data/editor_js.gemspec CHANGED
@@ -25,6 +25,13 @@ Gem::Specification.new do |spec|
25
25
 
26
26
  spec.require_paths = ["lib"]
27
27
 
28
+ spec.add_dependency 'actionview', '>= 4'
29
+ spec.add_dependency 'activesupport', '>= 4'
30
+ spec.add_dependency 'json-schema', '~> 2'
31
+ spec.add_dependency 'sanitize', '~> 5.1'
32
+ spec.add_dependency 'htmlentities', '~> 4.3', '>= 4.3.4'
33
+ spec.add_dependency 'redcarpet', '~> 3.5'
34
+ spec.add_dependency 'coderay', '~> 1.1', '>= 1.1.2'
28
35
  spec.add_development_dependency "bundler", "~> 2.0"
29
36
  spec.add_development_dependency "rake", "~> 10.0"
30
37
  spec.add_development_dependency "rspec", "~> 3.0"
data/lib/editor_js.rb CHANGED
@@ -1,6 +1,40 @@
1
- require "editor_js/version"
1
+ require 'json'
2
+ require 'active_support/core_ext/hash/keys'
3
+ require 'active_support/core_ext/string/inflections'
4
+ require 'active_support/core_ext/string/output_safety'
5
+ require 'active_support/core_ext/object/deep_dup'
6
+ require 'action_view'
7
+ require 'action_view/helpers'
8
+ require 'json-schema'
9
+ require 'sanitize'
10
+ require 'htmlentities'
11
+ require 'redcarpet'
12
+ require 'coderay'
13
+ require 'editor_js/version'
14
+ require 'editor_js/blocks/base'
15
+ require 'editor_js/blocks/checklist_block'
16
+ require 'editor_js/blocks/code_block'
17
+ require 'editor_js/blocks/delimiter_block'
18
+ require 'editor_js/blocks/embed_block'
19
+ require 'editor_js/blocks/header_block'
20
+ require 'editor_js/blocks/image_block'
21
+ require 'editor_js/blocks/list_block'
22
+ require 'editor_js/blocks/markdown_block'
23
+ require 'editor_js/blocks/paragraph_block'
24
+ require 'editor_js/blocks/qiniu_image_block'
25
+ require 'editor_js/blocks/quote_block'
26
+ require 'editor_js/blocks/table_block'
27
+ require 'editor_js/document'
2
28
 
3
29
  module EditorJs
4
- class Error < StandardError; end
5
- # Your code goes here...
30
+ class << self
31
+ # css name prefix of blocks
32
+ def css_name_prefix
33
+ @css_name_prefix ||= 'editor_js--'
34
+ end
35
+
36
+ def css_name_prefix=(prefix)
37
+ @css_name_prefix = prefix
38
+ end
39
+ end
6
40
  end
@@ -0,0 +1,114 @@
1
+ module EditorJs
2
+ module Blocks
3
+ class Base
4
+ InvalidBlockDataError = Class.new(StandardError)
5
+ InvalidBlockTypeError = Class.new(StandardError)
6
+ include ActionView::Helpers::TagHelper
7
+ include ActionView::Helpers::TextHelper
8
+ include ERB::Util
9
+
10
+ # ActionView::Helpers::TagHelper requires output_buffer accessor
11
+ attr_accessor :raw, :output_buffer
12
+
13
+ def initialize(raw)
14
+ @raw = raw
15
+ @content = cast_block_data_to_hash(raw.deep_dup)
16
+ sanitize!
17
+ end
18
+
19
+ # Define JSON format of data
20
+ def schema
21
+ raise NotImplementedError
22
+ end
23
+
24
+ # Render HTML
25
+ def render(_options = {})
26
+ raise NotImplementedError
27
+ end
28
+
29
+ # Sanitize content of data
30
+ def sanitize!; end
31
+
32
+ # Render plain text, for full-text searching
33
+ def plain; end
34
+
35
+ # Validate data
36
+ def valid?
37
+ JSON::Validator.validate(schema, data)
38
+ end
39
+
40
+ def type
41
+ self.class.type
42
+ end
43
+
44
+ def data
45
+ @content['data']
46
+ end
47
+
48
+ def decode_html(string)
49
+ html_decoder.decode(string)
50
+ end
51
+
52
+ def css_name(name = nil)
53
+ "#{css_prefix}#{name}"
54
+ end
55
+
56
+ def output
57
+ @content
58
+ end
59
+
60
+ def self.type
61
+ @type ||= self.to_s.underscore.split('/').last.gsub('_block', '')
62
+ end
63
+
64
+ def self.inherited(parent)
65
+ @registry ||= {}
66
+ @registry[parent.type] = parent
67
+ super
68
+ end
69
+
70
+ def self.load(block_data)
71
+ block_data = JSON.parse(block_data) unless block_data.is_a?(Hash)
72
+ klass = @registry[block_data['type']]
73
+ raise InvalidBlockTypeError, block_data['type'] if klass.nil?
74
+
75
+ klass.new(block_data)
76
+ end
77
+
78
+ private
79
+
80
+ def cast_block_data_to_hash(block_data)
81
+ raise InvalidBlockDataError, block_data unless block_data.is_a?(Hash)
82
+
83
+ block_data = block_data.deep_stringify_keys
84
+ raise InvalidBlockDataError, "block type <#{block_data['type']}> doesn't match <#{type}>" unless block_data['type'] == type
85
+
86
+ block_data
87
+ rescue JSON::ParserError => _e
88
+ raise InvalidBlockDataError, "Invalid JSON: #{block_data}"
89
+ end
90
+
91
+ def css_prefix
92
+ @css_prefix ||= "#{EditorJs.css_name_prefix}#{type}"
93
+ end
94
+
95
+ def html_decoder
96
+ @html_decoder ||= begin
97
+ with_customized_html_mappings do
98
+ HTMLEntities::Decoder.new('expanded')
99
+ end
100
+ end
101
+ end
102
+
103
+ def with_customized_html_mappings
104
+ original_mappings = HTMLEntities::MAPPINGS['expanded']
105
+ customized_mappings = original_mappings.dup
106
+ customized_mappings['nbsp'] = 32
107
+ HTMLEntities::MAPPINGS['expanded'] = customized_mappings
108
+ yield
109
+ ensure
110
+ HTMLEntities::MAPPINGS['expanded'] = original_mappings
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class ChecklistBlock < Base
6
+ def schema
7
+ YAML.safe_load(<<~YAML)
8
+ type: object
9
+ additionalProperties: false
10
+ properties:
11
+ items:
12
+ type: array
13
+ items:
14
+ type: object
15
+ additionalProperties: false
16
+ properties:
17
+ text:
18
+ type: string
19
+ checked:
20
+ type: boolean
21
+ required:
22
+ - text
23
+ YAML
24
+ end
25
+
26
+ def render(_options = {})
27
+ content_tag :div, class: css_name do
28
+ data['items'].each do |item|
29
+ concat content_tag(:input, item['text'], type: 'checkbox', disabled: true, checked: item['checked'])
30
+ end
31
+ end
32
+ end
33
+
34
+ def sanitize!
35
+ data['items'].each do |item|
36
+ item['text'] = Sanitize.fragment(item['text'], remove_contents: true).strip
37
+ end
38
+ end
39
+
40
+ def plain
41
+ data['items'].map { |item| decode_html(item['text']).strip }.join(', ')
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class CodeBlock < Base
6
+ def schema
7
+ YAML.safe_load(<<~YAML)
8
+ type: object
9
+ additionalProperties: false
10
+ properties:
11
+ code:
12
+ type: string
13
+ required:
14
+ - code
15
+ YAML
16
+ end
17
+
18
+ def render(_options = {})
19
+ content_tag :code, class: css_name do
20
+ data['code']
21
+ end
22
+ end
23
+
24
+ def plain
25
+ data['code'].strip
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class DelimiterBlock < Base
6
+ def schema
7
+ YAML.safe_load(<<~YAML)
8
+ type: object
9
+ additionalProperties: false
10
+ YAML
11
+ end
12
+
13
+ def render(_options = {})
14
+ content_tag :hr, '', class: css_name
15
+ end
16
+
17
+ def plain
18
+ ''
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class EmbedBlock < Base
6
+ def schema
7
+ YAML.safe_load(<<~YAML)
8
+ type: object
9
+ additionalProperties: false
10
+ properties:
11
+ caption:
12
+ type: string
13
+ embed:
14
+ type: string
15
+ height:
16
+ type:
17
+ - number
18
+ - string
19
+ service:
20
+ type: string
21
+ source:
22
+ type: string
23
+ width:
24
+ type:
25
+ - number
26
+ - string
27
+ required:
28
+ - embed
29
+ - service
30
+ - source
31
+ - width
32
+ - height
33
+ YAML
34
+ end
35
+
36
+ def render(options = {})
37
+ content_tag :div, class: css_name do
38
+ concat content_tag(:iframe, '',
39
+ src: data['embed'],
40
+ width: data['width'],
41
+ height: data['height'],
42
+ frameborder: options.fetch('frameborder', '0'),
43
+ allowfullscreen: options.fetch('allowfullscreen', true))
44
+ concat content_tag(:span, data['caption'])
45
+ end
46
+ end
47
+
48
+ def sanitize
49
+ %w(caption embed height service source width).each do |key|
50
+ data[key] = Sanitize.fragment(data[key], remove_contents: true).strip
51
+ end
52
+ end
53
+
54
+ def plain
55
+ data['caption'].strip
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class HeaderBlock < Base
6
+ def schema
7
+ YAML.safe_load(<<~YAML)
8
+ type: object
9
+ additionalProperties: false
10
+ properties:
11
+ text:
12
+ type: string
13
+ level:
14
+ type: number
15
+ enum: [1,2,3,4,5,6]
16
+ required:
17
+ - text
18
+ - level
19
+ YAML
20
+ end
21
+
22
+ def render(_options = {})
23
+ content_tag(:"h#{data['level']}", data['text'].html_safe, class: css_name)
24
+ end
25
+
26
+ def sanitize!
27
+ data['text'] = Sanitize.fragment(data['text'], remove_contents: true).strip
28
+ end
29
+
30
+ def plain
31
+ decode_html data['text'].strip
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class ImageBlock < Base
6
+ def schema
7
+ YAML.safe_load(<<~YAML)
8
+ type: object
9
+ additionalProperties: false
10
+ properties:
11
+ caption:
12
+ type: string
13
+ url:
14
+ type: string
15
+ stretched:
16
+ type: boolean
17
+ withBackground:
18
+ type: boolean
19
+ withBorder:
20
+ type: boolean
21
+ required:
22
+ - url
23
+ YAML
24
+ end
25
+
26
+ def render(_options = {})
27
+ content_tag :div, class: css_name do
28
+ url = data['url']
29
+ caption = data['caption']
30
+ withBorder = data['withBorder']
31
+ withBackground = data['withBackground']
32
+ stretched = data['stretched']
33
+
34
+ html_class = "#{css_name}__picture"
35
+ html_class += " #{css_name}__picture--stretched" if stretched
36
+ html_class += " #{css_name}__picture--with-background" if withBackground
37
+ html_class += " #{css_name}__picture--with-border" if withBorder
38
+
39
+ html_str = content_tag :div, class: html_class do
40
+ content_tag :img, '', src: url
41
+ end
42
+ html_str << content_tag(:div, caption.html_safe, class: "#{css_name}__caption").html_safe
43
+ end
44
+ end
45
+
46
+ def sanitize!
47
+ data['caption'] = Sanitize.fragment(data['caption'], remove_contents: true)
48
+ end
49
+
50
+ def plain
51
+ decode_html data['caption'].strip
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class ListBlock < Base
6
+ def schema
7
+ YAML.safe_load(<<~YAML)
8
+ type: object
9
+ additionalProperties: false
10
+ properties:
11
+ style:
12
+ type: string
13
+ pattern: ^(un)?ordered$
14
+ items:
15
+ type: array
16
+ items:
17
+ type: string
18
+ YAML
19
+ end
20
+
21
+ def render(_options = {})
22
+ tag = data['style'] == 'unordered' ? :ul : :ol
23
+ content_tag(tag, class: css_name) do
24
+ children_tag_string = ''
25
+ data['items'].each do |v|
26
+ children_tag_string += content_tag(:li, v.html_safe)
27
+ end
28
+ children_tag_string.html_safe
29
+ end
30
+ end
31
+
32
+ def sanitize!
33
+ safe_tags = {
34
+ 'b' => nil,
35
+ 'i' => nil,
36
+ 'a' => ['href'],
37
+ 'mark' => ['class'],
38
+ 'code' => ['class']
39
+ }
40
+
41
+ data['items'] = data['items'].map do |text|
42
+ Sanitize.fragment(
43
+ text,
44
+ elements: safe_tags.keys,
45
+ attributes: safe_tags.select {|k, v| v},
46
+ remove_contents: true
47
+ )
48
+ end
49
+ end
50
+
51
+ def plain
52
+ data['items'].map do |text|
53
+ decode_html(Sanitize.fragment(text)).strip
54
+ end.join(', ')
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class HTMLwithCodeRay < Redcarpet::Render::HTML
6
+ def block_code(code, language)
7
+ CodeRay.scan(code, language || :text).div(css: :class)
8
+ end
9
+
10
+ def list_item(text, list_type)
11
+ if text.start_with?("[x]", "[X]")
12
+ text[0..2] = %(<input type='checkbox' checked='checked' disabled>)
13
+ elsif text.start_with?("[ ]")
14
+ text[0..2] = %(<input type='checkbox' disabled>)
15
+ end
16
+ %(<li>#{text}</li>)
17
+ end
18
+ end
19
+
20
+ class MarkdownBlock < Base
21
+ def schema
22
+ YAML.safe_load(<<~YAML)
23
+ type: object
24
+ additionalProperties: false
25
+ properties:
26
+ text:
27
+ type: string
28
+ YAML
29
+ end
30
+
31
+ def render(_options = {})
32
+ content_tag :div, class: css_name do
33
+ content_text = data['text'] || ''
34
+
35
+ render_options = {
36
+ escape_html: true,
37
+ hard_wrap: true,
38
+ with_toc_data: true,
39
+ link_attributes: { rel: 'nofollow', target: '_blank' }
40
+ }
41
+ renderer = HTMLwithCodeRay.new(render_options)
42
+
43
+ options = {
44
+ autolink: true,
45
+ fenced_code_blocks: true,
46
+ lax_spacing: true,
47
+ no_intra_emphasis: true,
48
+ strikethrough: true,
49
+ tables: true,
50
+ superscript: true,
51
+ highlight: true,
52
+ quote: true,
53
+ footnotes: true
54
+ }
55
+ markdown_to_html = Redcarpet::Markdown.new(renderer, options)
56
+
57
+ markdown_to_html.render(content_text).html_safe
58
+ end
59
+ end
60
+
61
+ def plain
62
+ data['text'].strip
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class ParagraphBlock < Base
6
+ def schema
7
+ YAML.safe_load(<<~YAML)
8
+ type: object
9
+ additionalProperties: false
10
+ properties:
11
+ text:
12
+ type: string
13
+ YAML
14
+ end
15
+
16
+ def render(_options = {})
17
+ content_tag(:div, class: css_name) { data['text'].html_safe }
18
+ end
19
+
20
+ def sanitize!
21
+ safe_tags = {
22
+ 'b' => nil,
23
+ 'i' => nil,
24
+ 'a' => ['href'],
25
+ 'mark' => ['class'],
26
+ 'code' => ['class']
27
+ }
28
+
29
+ data['text'] = Sanitize.fragment(
30
+ data['text'],
31
+ elements: safe_tags.keys,
32
+ attributes: safe_tags.select {|k, v| v},
33
+ remove_contents: true
34
+ )
35
+ end
36
+
37
+ def plain
38
+ decode_html(Sanitize.fragment data['text']).strip
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class QiniuImageBlock < ImageBlock
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class QuoteBlock < Base
6
+ def schema
7
+ YAML.safe_load(<<~YAML)
8
+ type: object
9
+ additionalProperties: false
10
+ properties:
11
+ text:
12
+ type: string
13
+ caption:
14
+ type: string
15
+ alignment:
16
+ type: string
17
+ YAML
18
+ end
19
+
20
+ def render(_options = {})
21
+ text = data['text'].html_safe
22
+ caption = data['caption'].presence&.html_safe
23
+
24
+ content_tag :div, class: css_name do
25
+ html_str = content_tag :div, text, class: "#{css_name}__text"
26
+ html_str << content_tag(:div, caption, class: "#{css_name}__caption") if caption
27
+ html_str
28
+ end
29
+ end
30
+
31
+ def sanitize!
32
+ safe_tags = {
33
+ 'b' => nil,
34
+ 'i' => nil,
35
+ 'a' => ['href'],
36
+ 'mark' => ['class'],
37
+ 'code' => ['class']
38
+ }
39
+
40
+ %w(text caption).each do |key|
41
+ data[key] = Sanitize.fragment(
42
+ data[key],
43
+ elements: safe_tags.keys,
44
+ attributes: safe_tags.select {|k, v| v},
45
+ remove_contents: true
46
+ )
47
+ end
48
+ data['alignment'] = Sanitize.fragment(data['alignment'], remove_contents: true)
49
+ end
50
+
51
+ def plain
52
+ string = [
53
+ Sanitize.fragment(data['text']).strip,
54
+ Sanitize.fragment(data['caption']).strip
55
+ ].join(', ')
56
+ decode_html(string)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EditorJs
4
+ module Blocks
5
+ class TableBlock < Base
6
+ def schema
7
+ YAML.safe_load(<<~YAML)
8
+ type: object
9
+ additionalProperties: false
10
+ properties:
11
+ content:
12
+ type: array
13
+ items:
14
+ type: array
15
+ items:
16
+ type: string
17
+ YAML
18
+ end
19
+
20
+ def render(_options = {})
21
+ content_tag(:div, class: css_name) do
22
+ content_tag(:table) do
23
+ data['content'].map do |row|
24
+ content_tag(:tr) do
25
+ row.map { |col| content_tag :td, col.html_safe }.join().html_safe
26
+ end
27
+ end.join().html_safe
28
+ end
29
+ end
30
+ end
31
+
32
+ def sanitize!
33
+ data['content'] = data['content'].map do |row|
34
+ row = (row || []).map do |cell_value|
35
+ Sanitize.fragment(cell_value, remove_contents: true)
36
+ end
37
+ end
38
+ end
39
+
40
+ def plain
41
+ decode_html data['content'].flatten.join(', ').gsub(/(, )+/, ', ')
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,58 @@
1
+ require 'json'
2
+ require 'yaml'
3
+
4
+ module EditorJs
5
+ class Document
6
+ SCHEMA = YAML.safe_load(<<~YAML)
7
+ type: object
8
+ additionalProperties: false
9
+ properties:
10
+ time:
11
+ type: number
12
+ blocks:
13
+ type: array
14
+ items:
15
+ type: object
16
+ version:
17
+ type: string
18
+ YAML
19
+
20
+ def initialize(str_or_hash)
21
+ str_or_hash = JSON.parse(str_or_hash) unless str_or_hash.is_a?(Hash)
22
+ @content = str_or_hash
23
+ @blocks = []
24
+ end
25
+
26
+ def valid?
27
+ return @valid if instance_variable_defined?(:@valid)
28
+
29
+ @valid = JSON::Validator.validate(SCHEMA, @content)
30
+ return false unless @valid
31
+
32
+ blocks = @content['blocks'].map do |blk_data|
33
+ EditorJs::Blocks::Base.load(blk_data)
34
+ end
35
+ @valid = blocks.all?(&:valid?)
36
+ @blocks = blocks if @valid
37
+ @valid
38
+ end
39
+
40
+ def render
41
+ return @renderred_html if instance_variable_defined?(:@renderred_html)
42
+
43
+ @renderred_html = valid? ? @blocks.map(&:render).join : ''
44
+ end
45
+
46
+ def plain
47
+ return @renderred_plain if instance_variable_defined?(:@renderred_plain)
48
+
49
+ @renderred_plain = valid? ? @blocks.map(&:plain).join('. ') : ''
50
+ end
51
+
52
+ def output
53
+ return @output if instance_variable_defined?(:@output)
54
+
55
+ @output = valid? ? @content.merge('blocks' => @blocks.map(&:output)) : {}
56
+ end
57
+ end
58
+ end
@@ -1,3 +1,3 @@
1
1
  module EditorJs
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
metadata CHANGED
@@ -1,15 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: editor_js
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - xiaohui
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-05 00:00:00.000000000 Z
11
+ date: 2019-12-13 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: actionview
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: json-schema
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sanitize
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: htmlentities
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.3'
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 4.3.4
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '4.3'
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 4.3.4
89
+ - !ruby/object:Gem::Dependency
90
+ name: redcarpet
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.5'
96
+ type: :runtime
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.5'
103
+ - !ruby/object:Gem::Dependency
104
+ name: coderay
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.1'
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 1.1.2
113
+ type: :runtime
114
+ prerelease: false
115
+ version_requirements: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '1.1'
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 1.1.2
13
123
  - !ruby/object:Gem::Dependency
14
124
  name: bundler
15
125
  requirement: !ruby/object:Gem::Requirement
@@ -61,6 +171,7 @@ extra_rdoc_files: []
61
171
  files:
62
172
  - ".gitignore"
63
173
  - ".rspec"
174
+ - ".rubocop.yml"
64
175
  - ".travis.yml"
65
176
  - Gemfile
66
177
  - Gemfile.lock
@@ -71,6 +182,20 @@ files:
71
182
  - bin/setup
72
183
  - editor_js.gemspec
73
184
  - lib/editor_js.rb
185
+ - lib/editor_js/blocks/base.rb
186
+ - lib/editor_js/blocks/checklist_block.rb
187
+ - lib/editor_js/blocks/code_block.rb
188
+ - lib/editor_js/blocks/delimiter_block.rb
189
+ - lib/editor_js/blocks/embed_block.rb
190
+ - lib/editor_js/blocks/header_block.rb
191
+ - lib/editor_js/blocks/image_block.rb
192
+ - lib/editor_js/blocks/list_block.rb
193
+ - lib/editor_js/blocks/markdown_block.rb
194
+ - lib/editor_js/blocks/paragraph_block.rb
195
+ - lib/editor_js/blocks/qiniu_image_block.rb
196
+ - lib/editor_js/blocks/quote_block.rb
197
+ - lib/editor_js/blocks/table_block.rb
198
+ - lib/editor_js/document.rb
74
199
  - lib/editor_js/version.rb
75
200
  homepage: https://github.com/xiaohui-zhangxh/editor_js
76
201
  licenses: