article_json 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|