article_json 0.2.1 → 0.3.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 +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +34 -2
- data/lib/article_json/article.rb +50 -10
- data/lib/article_json/configuration.rb +6 -27
- data/lib/article_json/elements/paragraph.rb +10 -0
- data/lib/article_json/elements/text.rb +8 -0
- data/lib/article_json/export/amp/elements/embed.rb +4 -1
- data/lib/article_json/export/common/elements/base.rb +84 -0
- data/lib/article_json/export/common/html/elements/base.rb +2 -70
- data/lib/article_json/export/common/html/elements/embed.rb +5 -2
- data/lib/article_json/export/common/html/elements/image.rb +3 -1
- data/lib/article_json/export/common/html/elements/quote.rb +16 -2
- data/lib/article_json/export/facebook_instant_article/elements/base.rb +30 -0
- data/lib/article_json/export/facebook_instant_article/elements/embed.rb +44 -0
- data/lib/article_json/export/facebook_instant_article/elements/heading.rb +11 -0
- data/lib/article_json/export/facebook_instant_article/elements/image.rb +11 -0
- data/lib/article_json/export/facebook_instant_article/elements/list.rb +11 -0
- data/lib/article_json/export/facebook_instant_article/elements/paragraph.rb +11 -0
- data/lib/article_json/export/facebook_instant_article/elements/quote.rb +30 -0
- data/lib/article_json/export/facebook_instant_article/elements/text.rb +11 -0
- data/lib/article_json/export/facebook_instant_article/elements/text_box.rb +40 -0
- data/lib/article_json/export/facebook_instant_article/exporter.rb +17 -0
- data/lib/article_json/export/plain_text/elements/base.rb +53 -0
- data/lib/article_json/export/plain_text/elements/embed.rb +16 -0
- data/lib/article_json/export/plain_text/elements/heading.rb +27 -0
- data/lib/article_json/export/plain_text/elements/image.rb +16 -0
- data/lib/article_json/export/plain_text/elements/list.rb +50 -0
- data/lib/article_json/export/plain_text/elements/paragraph.rb +33 -0
- data/lib/article_json/export/plain_text/elements/quote.rb +35 -0
- data/lib/article_json/export/plain_text/elements/text.rb +16 -0
- data/lib/article_json/export/plain_text/elements/text_box.rb +16 -0
- data/lib/article_json/export/plain_text/exporter.rb +23 -0
- data/lib/article_json/import/google_doc/html/shared/caption.rb +4 -3
- data/lib/article_json/utils/additional_element_placer.rb +69 -42
- data/lib/article_json/version.rb +1 -1
- data/lib/article_json.rb +24 -0
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3ea32ede59212aaedc27f897922412e70bc1545
|
4
|
+
data.tar.gz: e95562bb9e98e42707a6f9a02f20595faf858d4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8c1fa89e91fdb144be43cba372c2c59b96f9536031f4302eecd172593e0d0be0e4eb4310183e79beebbb4155d644a251a9c6663178cc23165337cfd97953570
|
7
|
+
data.tar.gz: b24715ad37d108a683a3baecc09acc586be9779c48d46c6007df141c45490afaa766f612d4f32c2ee894a45f5b0c4e99d8b1d38f9dc18e8fbcfc5f1cca5bbb6b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.3.0 - 2017/11/21
|
4
|
+
In this third bigger release we **added support**:
|
5
|
+
- For exporting articles in the Facebook Instant Article format
|
6
|
+
- For exporting articles in a plain text format
|
7
|
+
- To all exporters for `caption` elements that are an empty array
|
8
|
+
- For `[no-caption]` text in _Google Documents_ below elements (like images or embed URLs), this now returns empty caption for element
|
9
|
+
|
10
|
+
**Improvements** were done regarding additional element placement:
|
11
|
+
- Rework algorithm to place additional elements to better support placing a single element
|
12
|
+
- Improve behavior of multiple calls to `Article#place_additional_elements`
|
13
|
+
|
14
|
+
One potentially **breaking change** was added:
|
15
|
+
- Remove deprecated `#register_html_element_exporter`, use `#register_element_exporters` instead
|
16
|
+
|
17
|
+
**Fixes**:
|
18
|
+
- Fix AMP export of Twitter tweets
|
19
|
+
|
3
20
|
## 0.2.1 - 2017/11/08
|
4
21
|
**Fix**: Handle non-successful OEmbed responses by rendering message
|
5
22
|
|
data/README.md
CHANGED
@@ -32,6 +32,12 @@ puts article.to_amp
|
|
32
32
|
# get javascript libraries needed for the AMP article
|
33
33
|
puts article.amp_exporter.amp_libraries
|
34
34
|
|
35
|
+
# export article as Facebook Instant Article HTML
|
36
|
+
puts article.to_facebook_instant_article
|
37
|
+
|
38
|
+
# export article as plain text
|
39
|
+
puts article.to_plain_text
|
40
|
+
|
35
41
|
# export article as JSON
|
36
42
|
puts article.to_json
|
37
43
|
```
|
@@ -77,9 +83,10 @@ ArticleJSON.configure do |config|
|
|
77
83
|
image: ArticleJSON::Export::HTML::Elements::ScaledImage
|
78
84
|
)
|
79
85
|
|
80
|
-
# It works the same way for custom AMP
|
86
|
+
# It works the same way for custom AMP, FacebookInstantArticle, or
|
87
|
+
# PlainText exporters:
|
81
88
|
config.register_element_exporters(
|
82
|
-
:amp,
|
89
|
+
:amp, # Or change this for `:facebook_instant_article` or `:plain_text`
|
83
90
|
image: ArticleJSON::Export::AMP::Elements::ScaledImage
|
84
91
|
)
|
85
92
|
end
|
@@ -201,6 +208,31 @@ An example of
|
|
201
208
|
the AMP HTML export for the parsed reference document can be found
|
202
209
|
[here](https://github.com/Devex/article_json/blob/master/spec/fixtures/reference_document_exported.amp.html).
|
203
210
|
|
211
|
+
### Facebook Instant Articles
|
212
|
+
The `FacebookInstantArticle` exporter generates a custom HTML string for a list
|
213
|
+
of elements. An example of the Facebook Instant Article export for the parsed
|
214
|
+
reference document can be found
|
215
|
+
[here](https://github.com/Devex/article_json/blob/master/spec/fixtures/reference_document_exported.html).
|
216
|
+
|
217
|
+
To learn more about the Facebook Instant Article HTML format see have a look at
|
218
|
+
the [Facebook Developer Documentation](https://developers.facebook.com/docs/instant-articles/guides/format-overview).
|
219
|
+
|
220
|
+
### Plain Text
|
221
|
+
As the name suggests, this exporter generates a plain text version of the article.
|
222
|
+
Rich text elements like images, embeds or even text boxes are not being rendered.
|
223
|
+
|
224
|
+
The reference document rendered as plain text can be found
|
225
|
+
[here](https://github.com/Devex/article_json/blob/master/spec/fixtures/reference_document_exported.txt).
|
226
|
+
|
227
|
+
Usage:
|
228
|
+
```ruby
|
229
|
+
# Create your article instance as you normally do
|
230
|
+
article = ArticleJSON::Article.from_hash(parsed_json)
|
231
|
+
|
232
|
+
# Then simply call `#to_plain_text` on it
|
233
|
+
article.to_plain_text
|
234
|
+
```
|
235
|
+
|
204
236
|
## Contributing
|
205
237
|
- Fork this repository
|
206
238
|
- Implement your feature or fix including Tests
|
data/lib/article_json/article.rb
CHANGED
@@ -1,10 +1,26 @@
|
|
1
1
|
module ArticleJSON
|
2
2
|
class Article
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :article_elements, :additional_elements
|
4
4
|
|
5
5
|
# @param [Array[ArticleJSON::Elements::Base]] elements
|
6
6
|
def initialize(elements)
|
7
|
-
@
|
7
|
+
@article_elements = elements
|
8
|
+
@additional_elements = []
|
9
|
+
end
|
10
|
+
|
11
|
+
# All elements of this article with optional additional elements placed in
|
12
|
+
# between
|
13
|
+
# @return [Array[ArticleJSON::Elements::Base]]
|
14
|
+
def elements
|
15
|
+
@elements ||= begin
|
16
|
+
if @additional_elements.any?
|
17
|
+
ArticleJSON::Utils::AdditionalElementPlacer
|
18
|
+
.new(@article_elements, @additional_elements)
|
19
|
+
.merge_elements
|
20
|
+
else
|
21
|
+
@article_elements
|
22
|
+
end
|
23
|
+
end
|
8
24
|
end
|
9
25
|
|
10
26
|
# Hash representation of the article
|
@@ -12,7 +28,7 @@ module ArticleJSON
|
|
12
28
|
def to_h
|
13
29
|
{
|
14
30
|
article_json_version: VERSION,
|
15
|
-
content:
|
31
|
+
content: elements.map(&:to_h),
|
16
32
|
}
|
17
33
|
end
|
18
34
|
|
@@ -25,7 +41,7 @@ module ArticleJSON
|
|
25
41
|
# Exporter instance for HTML
|
26
42
|
# @return [ArticleJSON::Export::HTML::Exporter]
|
27
43
|
def html_exporter
|
28
|
-
|
44
|
+
ArticleJSON::Export::HTML::Exporter.new(elements)
|
29
45
|
end
|
30
46
|
|
31
47
|
# HTML export of the article
|
@@ -37,7 +53,7 @@ module ArticleJSON
|
|
37
53
|
# Exporter instance for AMP
|
38
54
|
# @return [ArticleJSON::Export::AMP::Exporter]
|
39
55
|
def amp_exporter
|
40
|
-
ArticleJSON::Export::AMP::Exporter.new(
|
56
|
+
ArticleJSON::Export::AMP::Exporter.new(elements)
|
41
57
|
end
|
42
58
|
|
43
59
|
# AMP export of the article
|
@@ -46,15 +62,39 @@ module ArticleJSON
|
|
46
62
|
amp_exporter.html
|
47
63
|
end
|
48
64
|
|
65
|
+
# Exporter instance for FacebookInstantArticle
|
66
|
+
# @return [ArticleJSON::Export::FacebookInstantArticle::Exporter]
|
67
|
+
def facebook_instant_article_exporter
|
68
|
+
ArticleJSON::Export::FacebookInstantArticle::Exporter.new(elements)
|
69
|
+
end
|
70
|
+
|
71
|
+
# FacebookInstantArticle export of the article
|
72
|
+
# @return [String]
|
73
|
+
def to_facebook_instant_article
|
74
|
+
facebook_instant_article_exporter.html
|
75
|
+
end
|
76
|
+
|
77
|
+
# Exporter instance for plain text
|
78
|
+
# @return [ArticleJSON::Export::PlainText::Exporter]
|
79
|
+
def plain_text_exporter
|
80
|
+
ArticleJSON::Export::PlainText::Exporter.new(elements)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Plain text export of the article
|
84
|
+
# @return [String]
|
85
|
+
def to_plain_text
|
86
|
+
plain_text_exporter.text
|
87
|
+
end
|
88
|
+
|
49
89
|
# Distribute passed elements evenly throughout the article. All passed
|
50
90
|
# elements need to have an exporter to be represented in the rendered
|
51
|
-
# article.
|
91
|
+
# article. If the method is called multiple times, the order of additional
|
92
|
+
# elements is maintained.
|
52
93
|
# @param [Object] additional_elements
|
53
94
|
def place_additional_elements(additional_elements)
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
.merge_elements
|
95
|
+
# Reset the `#elements` method memoization
|
96
|
+
@elements = nil
|
97
|
+
@additional_elements.concat(additional_elements)
|
58
98
|
end
|
59
99
|
|
60
100
|
class << self
|
@@ -21,40 +21,19 @@ module ArticleJSON
|
|
21
21
|
@custom_element_exporters = {}
|
22
22
|
end
|
23
23
|
|
24
|
-
# Register a new HTML element exporter or overwrite existing ones.
|
25
|
-
# @param [Symbol] type
|
26
|
-
# @param [Class] klass
|
27
|
-
# @deprecated Use `#register_element_exporters_for(:html, ...)` instead
|
28
|
-
def register_html_element_exporter(type, klass)
|
29
|
-
register_element_exporters(:html, type => klass)
|
30
|
-
end
|
31
|
-
|
32
|
-
# Return custom HTML exporters
|
33
|
-
# @return [Hash[Symbol => Class]]
|
34
|
-
# @deprecated use `#exporter_for` instead
|
35
|
-
def html_element_exporters
|
36
|
-
@custom_element_exporters[:html] || {}
|
37
|
-
end
|
38
|
-
|
39
|
-
# Set custom HTML exporters
|
40
|
-
# @param [Hash[Symbol => Class]] value
|
41
|
-
# @deprecated use `#register_element_exporters(:html, ...)` instead
|
42
|
-
def html_element_exporters=(value)
|
43
|
-
@custom_element_exporters[:html] = value
|
44
|
-
end
|
45
|
-
|
46
24
|
# Register new element exporters or overwrite existing ones for a given
|
47
25
|
# exporter type.
|
48
26
|
# Usage example:
|
49
27
|
# register_element_exporters(:html,
|
50
|
-
#
|
51
|
-
#
|
28
|
+
# image: MyImageExporter,
|
29
|
+
# advertisement: MyAdExporter)
|
52
30
|
# @param [Symbol] exporter
|
53
31
|
# @param [Hash[Symbol => Class]] type_class_mapping
|
54
32
|
def register_element_exporters(exporter, type_class_mapping)
|
55
|
-
|
56
|
-
|
57
|
-
|
33
|
+
valid_exporters = %i(html amp facebook_instant_article plain_text)
|
34
|
+
unless valid_exporters.include?(exporter)
|
35
|
+
raise ArgumentError, '`exporter` needs to be one of ' \
|
36
|
+
"#{valid_exporters} but is `#{exporter.inspect}`"
|
58
37
|
end
|
59
38
|
if !type_class_mapping.is_a?(Hash) ||
|
60
39
|
type_class_mapping.keys.any? { |key| !key.is_a? Symbol } ||
|
@@ -32,6 +32,16 @@ module ArticleJSON
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
# Return the sum of all characters within the content's text elements
|
36
|
+
# @return [Integer]
|
37
|
+
def length
|
38
|
+
return 0 if empty?
|
39
|
+
@content.reduce(0) do |sum, element|
|
40
|
+
sum + (element.respond_to?(:length) ? element.length : 0)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
alias size length
|
44
|
+
|
35
45
|
class << self
|
36
46
|
# Create a paragraph element from Hash
|
37
47
|
# @return [ArticleJSON::Elements::Paragraph]
|
@@ -40,6 +40,14 @@ module ArticleJSON
|
|
40
40
|
empty? || content.gsub(/[\s\u00A0]/, '').empty?
|
41
41
|
end
|
42
42
|
|
43
|
+
# Get the number of characters contained by this text element
|
44
|
+
# @return [Integer]
|
45
|
+
def length
|
46
|
+
return 0 if blank?
|
47
|
+
content.length
|
48
|
+
end
|
49
|
+
alias size length
|
50
|
+
|
43
51
|
class << self
|
44
52
|
# Create a text element from Hash
|
45
53
|
# @return [ArticleJSON::Elements::Text]
|
@@ -55,8 +55,11 @@ module ArticleJSON
|
|
55
55
|
|
56
56
|
# @return [Nokogiri::XML::Element]
|
57
57
|
def tweet_node
|
58
|
+
# The embed_id of a tweet is stored as "<handle>/<tweet_id>" but
|
59
|
+
# the `amp-twitter` tag only takes the `tweet_id` part
|
60
|
+
tweet_id = @element.embed_id.split('/').last
|
58
61
|
create_element('amp-twitter',
|
59
|
-
'data-tweetid'
|
62
|
+
'data-tweetid': tweet_id,
|
60
63
|
width: default_width,
|
61
64
|
height: default_height)
|
62
65
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module ArticleJSON
|
2
|
+
module Export
|
3
|
+
module Common
|
4
|
+
module Elements
|
5
|
+
module Base
|
6
|
+
# Extend `base` class with `ClassMethods` upon inclusion
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param [ArticleJSON::Elements::Base] element
|
12
|
+
def initialize(element)
|
13
|
+
@element = element
|
14
|
+
end
|
15
|
+
|
16
|
+
# Export the given element. Dynamically looks up the right
|
17
|
+
# export-element-class, instantiates it and then calls the `#export`
|
18
|
+
# method.
|
19
|
+
# @return [Object]
|
20
|
+
def export
|
21
|
+
exporter.export unless exporter.nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Get the right exporter class for the given element
|
27
|
+
# @return [ArticleJSON::Export::Common::Elements::Base]
|
28
|
+
def exporter
|
29
|
+
@exporter ||=
|
30
|
+
self.class.base_class? ? self.class.build(@element) : self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return the base class for the current element instance
|
34
|
+
# @return [ArticleJSON::Export::Common::Elements::Base]
|
35
|
+
def base_class
|
36
|
+
self.class.namespace::Base
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
# Instantiate the correct sub class for a given element
|
41
|
+
# @param [ArticleJSON::Elements::Base] element
|
42
|
+
# @return [ArticleJSON::Export::Common::Elements::Base]
|
43
|
+
def build(element)
|
44
|
+
klass = exporter_by_type(element.type)
|
45
|
+
klass.new(element) unless klass.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Look up the correct exporter class based on the element type
|
49
|
+
# @param [Symbol] type
|
50
|
+
# @return [ArticleJSON::Export::Common::Elements::Base]
|
51
|
+
def exporter_by_type(type)
|
52
|
+
key = type.to_sym
|
53
|
+
custom_class = ArticleJSON.configuration.element_exporter_for(
|
54
|
+
export_format, key
|
55
|
+
)
|
56
|
+
custom_class || default_exporter_mapping[key]
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check if the current class is the base class a child class.
|
60
|
+
# Since this common module is in a different namespace, a simple
|
61
|
+
# `self == Base` check does not work.
|
62
|
+
# @return [Boolean]
|
63
|
+
def base_class?
|
64
|
+
self == namespace::Base
|
65
|
+
end
|
66
|
+
|
67
|
+
def default_exporter_mapping
|
68
|
+
{
|
69
|
+
text: namespace::Text,
|
70
|
+
paragraph: namespace::Paragraph,
|
71
|
+
heading: namespace::Heading,
|
72
|
+
list: namespace::List,
|
73
|
+
quote: namespace::Quote,
|
74
|
+
image: namespace::Image,
|
75
|
+
embed: namespace::Embed,
|
76
|
+
text_box: namespace::TextBox,
|
77
|
+
}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -4,22 +4,9 @@ module ArticleJSON
|
|
4
4
|
module HTML
|
5
5
|
module Elements
|
6
6
|
module Base
|
7
|
-
#
|
7
|
+
# Also include the generic base element concern
|
8
8
|
def self.included(base)
|
9
|
-
base.
|
10
|
-
end
|
11
|
-
|
12
|
-
# @param [ArticleJSON::Elements::Base] element
|
13
|
-
def initialize(element)
|
14
|
-
@element = element
|
15
|
-
end
|
16
|
-
|
17
|
-
# Export a HTML node out of the given element
|
18
|
-
# Dynamically looks up the right export-element-class, instantiates it
|
19
|
-
# and then calls the #build method.
|
20
|
-
# @return [Nokogiri::XML::NodeSet]
|
21
|
-
def export
|
22
|
-
exporter.export unless exporter.nil?
|
9
|
+
base.include ArticleJSON::Export::Common::Elements::Base
|
23
10
|
end
|
24
11
|
|
25
12
|
private
|
@@ -48,61 +35,6 @@ module ArticleJSON
|
|
48
35
|
elements.each { |element| node_set.push(element) }
|
49
36
|
end
|
50
37
|
end
|
51
|
-
|
52
|
-
# Get the right exporter class for the given element
|
53
|
-
# @return [ArticleJSON::Export::Common::HTML::Elements::Base]
|
54
|
-
def exporter
|
55
|
-
@exporter ||=
|
56
|
-
self.class.base_class? ? self.class.build(@element) : self
|
57
|
-
end
|
58
|
-
|
59
|
-
# Return the base class for the current element instance
|
60
|
-
# @return [ArticleJSON::Export::Common::HTML::Elements::Base]
|
61
|
-
def base_class
|
62
|
-
self.class.namespace::Base
|
63
|
-
end
|
64
|
-
|
65
|
-
module ClassMethods
|
66
|
-
# Instantiate the correct sub class for a given element
|
67
|
-
# @param [ArticleJSON::Elements::Base] element
|
68
|
-
# @return [ArticleJSON::Export::Common::HTML::Elements::Base]
|
69
|
-
def build(element)
|
70
|
-
klass = exporter_by_type(element.type)
|
71
|
-
klass.new(element) unless klass.nil?
|
72
|
-
end
|
73
|
-
|
74
|
-
# Look up the correct exporter class based on the element type
|
75
|
-
# @param [Symbol] type
|
76
|
-
# @return [ArticleJSON::Export::Common::HTML::Elements::Base]
|
77
|
-
def exporter_by_type(type)
|
78
|
-
key = type.to_sym
|
79
|
-
custom_class = ArticleJSON.configuration.element_exporter_for(
|
80
|
-
export_format, key
|
81
|
-
)
|
82
|
-
custom_class || default_exporter_mapping[key]
|
83
|
-
end
|
84
|
-
|
85
|
-
# Check if the current class is the base class a child class.
|
86
|
-
# Since this common module is in a different namespace, a simple
|
87
|
-
# `self == Base` check does not work.
|
88
|
-
# @return [Boolean]
|
89
|
-
def base_class?
|
90
|
-
self == namespace::Base
|
91
|
-
end
|
92
|
-
|
93
|
-
def default_exporter_mapping
|
94
|
-
{
|
95
|
-
text: namespace::Text,
|
96
|
-
paragraph: namespace::Paragraph,
|
97
|
-
heading: namespace::Heading,
|
98
|
-
list: namespace::List,
|
99
|
-
quote: namespace::Quote,
|
100
|
-
image: namespace::Image,
|
101
|
-
embed: namespace::Embed,
|
102
|
-
text_box: namespace::TextBox,
|
103
|
-
}
|
104
|
-
end
|
105
|
-
end
|
106
38
|
end
|
107
39
|
end
|
108
40
|
end
|
@@ -11,14 +11,17 @@ module ArticleJSON
|
|
11
11
|
def export
|
12
12
|
create_element(:figure) do |figure|
|
13
13
|
figure.add_child(embed_node)
|
14
|
-
|
14
|
+
if @element.caption&.any?
|
15
|
+
figure.add_child(caption_node(:figcaption))
|
16
|
+
end
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
20
|
private
|
19
21
|
|
20
22
|
def embed_node
|
21
|
-
|
23
|
+
type = @element.embed_type.to_s.tr('_','-')
|
24
|
+
create_element(:div, class: "embed #{type}") do |div|
|
22
25
|
div.add_child(embedded_object)
|
23
26
|
end
|
24
27
|
end
|
@@ -12,7 +12,9 @@ module ArticleJSON
|
|
12
12
|
def export
|
13
13
|
create_element(:figure, node_opts) do |figure|
|
14
14
|
figure.add_child(image_node)
|
15
|
-
|
15
|
+
if @element.caption&.any?
|
16
|
+
figure.add_child(caption_node(:figcaption))
|
17
|
+
end
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
@@ -10,11 +10,13 @@ module ArticleJSON
|
|
10
10
|
# Generate the quote node with all its containing text elements
|
11
11
|
# @return [Nokogiri::XML::NodeSet]
|
12
12
|
def export
|
13
|
-
create_element(
|
13
|
+
create_element(quote_tag, node_opts) do |div|
|
14
14
|
@element.content.each do |child_element|
|
15
15
|
div.add_child(base_class.new(child_element).export)
|
16
16
|
end
|
17
|
-
|
17
|
+
if @element.caption&.any?
|
18
|
+
div.add_child(caption_node(caption_tag))
|
19
|
+
end
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
@@ -24,6 +26,18 @@ module ArticleJSON
|
|
24
26
|
def node_opts
|
25
27
|
{ class: ['quote', floating_class].compact.join(' ') }
|
26
28
|
end
|
29
|
+
|
30
|
+
# HTML tag for the wrapping node
|
31
|
+
# @return [Symbol]
|
32
|
+
def quote_tag
|
33
|
+
:div
|
34
|
+
end
|
35
|
+
|
36
|
+
# HTML tag for the node containing the caption
|
37
|
+
# @return [Symbol]
|
38
|
+
def caption_tag
|
39
|
+
:small
|
40
|
+
end
|
27
41
|
end
|
28
42
|
end
|
29
43
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ArticleJSON
|
2
|
+
module Export
|
3
|
+
module FacebookInstantArticle
|
4
|
+
module Elements
|
5
|
+
class Base
|
6
|
+
include ArticleJSON::Export::Common::HTML::Elements::Base
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Return the module namespace this class and its subclasses are
|
10
|
+
# nested in
|
11
|
+
# @return [Module]
|
12
|
+
def namespace
|
13
|
+
ArticleJSON::Export::FacebookInstantArticle::Elements
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# The format this exporter is returning. This is used to determine
|
19
|
+
# which custom element exporters should be applied from the
|
20
|
+
# configuration.
|
21
|
+
# @return [Symbol]
|
22
|
+
def export_format
|
23
|
+
:facebook_instant_article
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ArticleJSON
|
2
|
+
module Export
|
3
|
+
module FacebookInstantArticle
|
4
|
+
module Elements
|
5
|
+
class Embed < Base
|
6
|
+
include ArticleJSON::Export::Common::HTML::Elements::Shared::Caption
|
7
|
+
|
8
|
+
# Generate the embedded element node
|
9
|
+
# @return [Nokogiri::XML::NodeSet]
|
10
|
+
def export
|
11
|
+
create_element(:figure, class: 'op-interactive') do |figure|
|
12
|
+
figure.add_child(embed_node)
|
13
|
+
if @element.caption&.any?
|
14
|
+
figure.add_child(caption_node(:figcaption))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Type specific object that should be embedded
|
22
|
+
# @return [Nokogiri::XML::Element]
|
23
|
+
def embed_node
|
24
|
+
if %i(facebook_video tweet).include? @element.embed_type.to_sym
|
25
|
+
iframe_node
|
26
|
+
else
|
27
|
+
embedded_object
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def iframe_node
|
32
|
+
create_element(:iframe) do |div|
|
33
|
+
div.add_child(embedded_object)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def embedded_object
|
38
|
+
Nokogiri::HTML.fragment(@element.oembed_data[:html])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|