article_json 0.3.8 → 0.4.1
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 +53 -0
- data/README.md +127 -79
- data/bin/article_json_export_amp.rb +1 -0
- data/bin/{article_json_export_facebook.rb → article_json_export_apple_news.rb} +4 -4
- data/bin/article_json_export_html.rb +1 -0
- data/bin/article_json_export_plain_text.rb +1 -0
- data/bin/article_json_parse_google_doc.rb +1 -0
- data/bin/check_google_doc_export.rb +41 -0
- data/bin/update_oembed_request-stubs.sh +1 -3
- data/bin/update_reference_document.sh +3 -3
- data/lib/article_json/article.rb +17 -9
- data/lib/article_json/configuration.rb +6 -5
- data/lib/article_json/elements/base.rb +0 -1
- data/lib/article_json/elements/heading.rb +0 -1
- data/lib/article_json/elements/image.rb +0 -1
- data/lib/article_json/elements/list.rb +0 -1
- data/lib/article_json/elements/paragraph.rb +1 -1
- data/lib/article_json/elements/quote.rb +0 -1
- data/lib/article_json/elements/text.rb +1 -1
- data/lib/article_json/elements/text_box.rb +0 -1
- data/lib/article_json/export/amp/custom_element_library_resolver.rb +0 -1
- data/lib/article_json/export/amp/elements/embed.rb +43 -31
- data/lib/article_json/export/amp/elements/image.rb +7 -5
- data/lib/article_json/export/amp/exporter.rb +4 -2
- data/lib/article_json/export/apple_news/elements/base.rb +53 -0
- data/lib/article_json/export/apple_news/elements/embed.rb +130 -0
- data/lib/article_json/export/apple_news/elements/heading.rb +32 -0
- data/lib/article_json/export/apple_news/elements/image.rb +59 -0
- data/lib/article_json/export/apple_news/elements/list.rb +67 -0
- data/lib/article_json/export/apple_news/elements/paragraph.rb +36 -0
- data/lib/article_json/export/apple_news/elements/quote.rb +60 -0
- data/lib/article_json/export/apple_news/elements/text.rb +55 -0
- data/lib/article_json/export/apple_news/elements/text_box.rb +51 -0
- data/lib/article_json/export/apple_news/exporter.rb +37 -0
- data/lib/article_json/export/common/html/elements/embed.rb +2 -1
- data/lib/article_json/export/common/html/elements/image.rb +2 -1
- data/lib/article_json/export/common/html/elements/text.rb +2 -0
- data/lib/article_json/import/google_doc/html/embedded_parser.rb +1 -0
- data/lib/article_json/import/google_doc/html/heading_parser.rb +5 -5
- data/lib/article_json/import/google_doc/html/image_parser.rb +18 -2
- data/lib/article_json/import/google_doc/html/list_parser.rb +2 -2
- data/lib/article_json/import/google_doc/html/node_analyzer.rb +25 -3
- data/lib/article_json/import/google_doc/html/parser.rb +7 -1
- data/lib/article_json/import/google_doc/html/shared/caption.rb +1 -0
- data/lib/article_json/import/google_doc/html/shared/float.rb +2 -0
- data/lib/article_json/import/google_doc/html/text_box_parser.rb +2 -1
- data/lib/article_json/import/google_doc/html/text_parser.rb +2 -0
- data/lib/article_json/utils/additional_element_placer.rb +2 -0
- data/lib/article_json/utils/o_embed_resolver/base.rb +14 -4
- data/lib/article_json/utils/o_embed_resolver/facebook_video.rb +17 -1
- data/lib/article_json/utils/o_embed_resolver/slideshare.rb +2 -2
- data/lib/article_json/utils/o_embed_resolver/youtube_video.rb +14 -1
- data/lib/article_json/version.rb +1 -1
- data/lib/article_json.rb +11 -11
- metadata +29 -26
- data/lib/article_json/export/facebook_instant_article/elements/base.rb +0 -30
- data/lib/article_json/export/facebook_instant_article/elements/embed.rb +0 -44
- data/lib/article_json/export/facebook_instant_article/elements/heading.rb +0 -11
- data/lib/article_json/export/facebook_instant_article/elements/image.rb +0 -11
- data/lib/article_json/export/facebook_instant_article/elements/list.rb +0 -11
- data/lib/article_json/export/facebook_instant_article/elements/paragraph.rb +0 -11
- data/lib/article_json/export/facebook_instant_article/elements/quote.rb +0 -30
- data/lib/article_json/export/facebook_instant_article/elements/text.rb +0 -11
- data/lib/article_json/export/facebook_instant_article/elements/text_box.rb +0 -40
- data/lib/article_json/export/facebook_instant_article/exporter.rb +0 -17
data/lib/article_json/article.rb
CHANGED
|
@@ -14,7 +14,7 @@ module ArticleJSON
|
|
|
14
14
|
def elements
|
|
15
15
|
@elements ||= begin
|
|
16
16
|
if @additional_elements.any?
|
|
17
|
-
|
|
17
|
+
@additional_element_placer_class
|
|
18
18
|
.new(@article_elements, @additional_elements)
|
|
19
19
|
.merge_elements
|
|
20
20
|
else
|
|
@@ -62,16 +62,16 @@ module ArticleJSON
|
|
|
62
62
|
amp_exporter.html
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
# Exporter instance for
|
|
66
|
-
# @return [ArticleJSON::Export::
|
|
67
|
-
def
|
|
68
|
-
ArticleJSON::Export::
|
|
65
|
+
# Exporter instance for AppleNews
|
|
66
|
+
# @return [ArticleJSON::Export::AppleNews::Exporter]
|
|
67
|
+
def apple_news_exporter
|
|
68
|
+
ArticleJSON::Export::AppleNews::Exporter.new(elements)
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
#
|
|
71
|
+
# AppleNews export of the article
|
|
72
72
|
# @return [String]
|
|
73
|
-
def
|
|
74
|
-
|
|
73
|
+
def to_apple_news
|
|
74
|
+
apple_news_exporter.to_json
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
# Exporter instance for plain text
|
|
@@ -91,10 +91,18 @@ module ArticleJSON
|
|
|
91
91
|
# article. If the method is called multiple times, the order of additional
|
|
92
92
|
# elements is maintained.
|
|
93
93
|
# @param [Object] additional_elements
|
|
94
|
-
|
|
94
|
+
# @param [Class<#merge_elements>] with - The passes class's `#initialize` method needs
|
|
95
|
+
# to accept two lists of elements. See
|
|
96
|
+
# `ArticleJSON::Utils::AdditionalElementPlacer`
|
|
97
|
+
# for reference.
|
|
98
|
+
def place_additional_elements(
|
|
99
|
+
additional_elements,
|
|
100
|
+
with: ArticleJSON::Utils::AdditionalElementPlacer
|
|
101
|
+
)
|
|
95
102
|
# Reset the `#elements` method memoization
|
|
96
103
|
@elements = nil
|
|
97
104
|
@additional_elements.concat(additional_elements)
|
|
105
|
+
@additional_element_placer_class = with
|
|
98
106
|
end
|
|
99
107
|
|
|
100
108
|
class << self
|
|
@@ -14,10 +14,11 @@ module ArticleJSON
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
class Configuration
|
|
17
|
-
attr_accessor :oembed_user_agent
|
|
17
|
+
attr_accessor :oembed_user_agent, :facebook_token
|
|
18
18
|
|
|
19
19
|
def initialize
|
|
20
20
|
@oembed_user_agent = nil
|
|
21
|
+
@facebook_token = nil
|
|
21
22
|
@custom_element_exporters = {}
|
|
22
23
|
end
|
|
23
24
|
|
|
@@ -30,7 +31,7 @@ module ArticleJSON
|
|
|
30
31
|
# @param [Symbol] exporter
|
|
31
32
|
# @param [Hash[Symbol => Class]] type_class_mapping
|
|
32
33
|
def register_element_exporters(exporter, type_class_mapping)
|
|
33
|
-
valid_exporters = %i(html amp
|
|
34
|
+
valid_exporters = %i(html amp apple_news plain_text)
|
|
34
35
|
unless valid_exporters.include?(exporter)
|
|
35
36
|
raise ArgumentError, '`exporter` needs to be one of ' \
|
|
36
37
|
"#{valid_exporters} but is `#{exporter.inspect}`"
|
|
@@ -38,8 +39,9 @@ module ArticleJSON
|
|
|
38
39
|
if !type_class_mapping.is_a?(Hash) ||
|
|
39
40
|
type_class_mapping.keys.any? { |key| !key.is_a? Symbol } ||
|
|
40
41
|
type_class_mapping.values.any? { |value| !value.is_a? Class }
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
raise ArgumentError, '`type_class_mapping` has to be a Hash with ' \
|
|
44
|
+
'symbolized keys and classes as values but is ' \
|
|
43
45
|
"`#{type_class_mapping.inspect}`"
|
|
44
46
|
end
|
|
45
47
|
|
|
@@ -56,4 +58,3 @@ module ArticleJSON
|
|
|
56
58
|
end
|
|
57
59
|
end
|
|
58
60
|
end
|
|
59
|
-
|
|
@@ -42,18 +42,22 @@ module ArticleJSON
|
|
|
42
42
|
|
|
43
43
|
# @return [Nokogiri::XML::Element]
|
|
44
44
|
def youtube_node
|
|
45
|
-
create_element(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
create_element(
|
|
46
|
+
'amp-youtube',
|
|
47
|
+
'data-videoid' => @element.embed_id,
|
|
48
|
+
width: default_width,
|
|
49
|
+
height: default_height
|
|
50
|
+
)
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
# @return [Nokogiri::XML::Element]
|
|
52
54
|
def vimeo_node
|
|
53
|
-
create_element(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
create_element(
|
|
56
|
+
'amp-vimeo',
|
|
57
|
+
'data-videoid' => @element.embed_id,
|
|
58
|
+
width: default_width,
|
|
59
|
+
height: default_height
|
|
60
|
+
)
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
# @return [Nokogiri::XML::Element]
|
|
@@ -61,42 +65,50 @@ module ArticleJSON
|
|
|
61
65
|
# The embed_id of a tweet is stored as "<handle>/<tweet_id>" but
|
|
62
66
|
# the `amp-twitter` tag only takes the `tweet_id` part
|
|
63
67
|
tweet_id = @element.embed_id.split('/').last
|
|
64
|
-
create_element(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
create_element(
|
|
69
|
+
'amp-twitter',
|
|
70
|
+
'data-tweetid': tweet_id,
|
|
71
|
+
width: default_width,
|
|
72
|
+
height: default_height
|
|
73
|
+
)
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
# @return [Nokogiri::XML::Element]
|
|
71
77
|
def facebook_node
|
|
72
|
-
url = "#{@element.oembed_data[:author_url]}videos/#{@element.embed_id}"
|
|
73
|
-
create_element(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
url = "#{@element.oembed_data[:author_url]}/videos/#{@element.embed_id}"
|
|
79
|
+
create_element(
|
|
80
|
+
'amp-facebook',
|
|
81
|
+
'data-embedded-as' => 'video',
|
|
82
|
+
'data-href' => url,
|
|
83
|
+
width: default_width,
|
|
84
|
+
height: default_height
|
|
85
|
+
)
|
|
78
86
|
end
|
|
79
87
|
|
|
80
88
|
def soundcloud_node
|
|
81
89
|
src = Nokogiri::HTML(@element.oembed_data[:html])
|
|
82
|
-
|
|
83
|
-
track_id = src.match(
|
|
84
|
-
create_element(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
.xpath('//iframe/@src').first.value
|
|
91
|
+
track_id = src.match(%r{tracks%2F(\d+)})[1]
|
|
92
|
+
create_element(
|
|
93
|
+
'amp-soundcloud',
|
|
94
|
+
layout: 'fixed-height',
|
|
95
|
+
'data-trackid': track_id,
|
|
96
|
+
'data-visual': true,
|
|
97
|
+
width: 'auto',
|
|
98
|
+
height: default_height
|
|
99
|
+
)
|
|
90
100
|
end
|
|
91
101
|
|
|
92
102
|
# @return [Nokogiri::XML::Element]
|
|
93
103
|
def iframe_node
|
|
94
104
|
node = Nokogiri::HTML(@element.oembed_data[:html]).xpath('//iframe')
|
|
95
|
-
create_element(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
105
|
+
create_element(
|
|
106
|
+
'amp-iframe',
|
|
107
|
+
src: node.attribute('src').value,
|
|
108
|
+
width: node.attribute('width').value,
|
|
109
|
+
height: node.attribute('height').value,
|
|
110
|
+
frameborder: '0'
|
|
111
|
+
)
|
|
100
112
|
end
|
|
101
113
|
|
|
102
114
|
# @return [String]
|
|
@@ -9,11 +9,13 @@ module ArticleJSON
|
|
|
9
9
|
|
|
10
10
|
# @return [Nokogiri::HTML::NodeSet]
|
|
11
11
|
def image_node
|
|
12
|
-
create_element(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
create_element(
|
|
13
|
+
'amp-img',
|
|
14
|
+
src: @element.source_url,
|
|
15
|
+
width: default_width,
|
|
16
|
+
height: default_height,
|
|
17
|
+
layout: :responsive
|
|
18
|
+
)
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def default_width
|
|
@@ -8,10 +8,11 @@ module ArticleJSON
|
|
|
8
8
|
# @return [Array[Symbol]]
|
|
9
9
|
def custom_element_tags
|
|
10
10
|
return @custom_element_tags if defined? @custom_element_tags
|
|
11
|
+
|
|
11
12
|
@custom_element_tags =
|
|
12
13
|
element_exporters
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
.flat_map { |element| element.custom_element_tags }
|
|
15
|
+
.uniq
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
# Return an array with all the javascript libraries needed for some
|
|
@@ -19,6 +20,7 @@ module ArticleJSON
|
|
|
19
20
|
# @return [Array<String>]
|
|
20
21
|
def amp_libraries
|
|
21
22
|
return @amp_libraries if defined? @amp_libraries
|
|
23
|
+
|
|
22
24
|
@amp_libraries =
|
|
23
25
|
CustomElementLibraryResolver.new(custom_element_tags).script_tags
|
|
24
26
|
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module ArticleJSON
|
|
2
|
+
module Export
|
|
3
|
+
module AppleNews
|
|
4
|
+
module Elements
|
|
5
|
+
class Base
|
|
6
|
+
include ArticleJSON::Export::Common::Elements::Base
|
|
7
|
+
|
|
8
|
+
# Export the given element. Dynamically looks up the right
|
|
9
|
+
# export-element-class, instantiates it and then calls the `#export`
|
|
10
|
+
# method.
|
|
11
|
+
# Defaults to nil, e.g. if no exporter is specified for the given
|
|
12
|
+
# type.
|
|
13
|
+
# @return [String]
|
|
14
|
+
def export
|
|
15
|
+
super || nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# Return the module namespace this class and its subclasses are
|
|
20
|
+
# nested within.
|
|
21
|
+
# @return [Module]
|
|
22
|
+
def namespace
|
|
23
|
+
ArticleJSON::Export::AppleNews::Elements
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
# The format this exporter is returning. This is used to determine
|
|
29
|
+
# which custom element exporters should be applied from the
|
|
30
|
+
# configuration.
|
|
31
|
+
# @return [Symbol]
|
|
32
|
+
def export_format
|
|
33
|
+
:apple_news
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def default_exporter_mapping
|
|
37
|
+
{
|
|
38
|
+
text: namespace::Text,
|
|
39
|
+
paragraph: namespace::Paragraph,
|
|
40
|
+
heading: namespace::Heading,
|
|
41
|
+
quote: namespace::Quote,
|
|
42
|
+
list: namespace::List,
|
|
43
|
+
image: namespace::Image,
|
|
44
|
+
embed: namespace::Embed,
|
|
45
|
+
text_box: namespace::TextBox,
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
module ArticleJSON
|
|
2
|
+
module Export
|
|
3
|
+
module AppleNews
|
|
4
|
+
module Elements
|
|
5
|
+
class Embed < Base
|
|
6
|
+
# Embed| Embed, Caption
|
|
7
|
+
# @return [Hash, Array<Hash>]
|
|
8
|
+
def export
|
|
9
|
+
caption_text.nil? ? embed : [embed, caption]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
# Embed
|
|
15
|
+
# @return [Hash]
|
|
16
|
+
def embed
|
|
17
|
+
{
|
|
18
|
+
role: role,
|
|
19
|
+
URL: source_url,
|
|
20
|
+
caption: caption_text,
|
|
21
|
+
}.compact
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Caption
|
|
25
|
+
# @return [Hash]
|
|
26
|
+
def caption
|
|
27
|
+
{
|
|
28
|
+
role: 'caption',
|
|
29
|
+
text: caption_text,
|
|
30
|
+
format: 'html',
|
|
31
|
+
layout: 'captionLayout',
|
|
32
|
+
textStyle: 'captionStyle',
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get the exporter class for text elements
|
|
37
|
+
# @return [ArticleJSON::Export::Common::HTML::Elements::Base]
|
|
38
|
+
def text_exporter
|
|
39
|
+
self.class.exporter_by_type(:text)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Caption Text
|
|
43
|
+
# @return [String]
|
|
44
|
+
def caption_text
|
|
45
|
+
return nil if role.nil? # Do not show captions for unsupported components
|
|
46
|
+
|
|
47
|
+
text.empty? ? nil : text
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @return [String]
|
|
51
|
+
def text
|
|
52
|
+
@element.caption.map do |child_element|
|
|
53
|
+
text_exporter.new(child_element)
|
|
54
|
+
.export
|
|
55
|
+
end.join
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def role
|
|
59
|
+
@role ||=
|
|
60
|
+
case embed_type
|
|
61
|
+
when :youtube_video, :vimeo_video, :dailymotion_video
|
|
62
|
+
:embedwebvideo
|
|
63
|
+
when :facebook_video
|
|
64
|
+
:facebook_post
|
|
65
|
+
when :tweet
|
|
66
|
+
:tweet
|
|
67
|
+
when :slideshare
|
|
68
|
+
nil
|
|
69
|
+
when :soundcloud
|
|
70
|
+
nil
|
|
71
|
+
else
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def source_url
|
|
77
|
+
case embed_type
|
|
78
|
+
when :youtube_video
|
|
79
|
+
build_embeded_youtube_url
|
|
80
|
+
when :vimeo_video
|
|
81
|
+
build_embeded_vimeo_url
|
|
82
|
+
when :dailymotion_video
|
|
83
|
+
build_embeded_vimeo_url
|
|
84
|
+
when :facebook_video
|
|
85
|
+
build_facebook_video_url
|
|
86
|
+
when :tweet
|
|
87
|
+
build_twitter_url
|
|
88
|
+
when :slideshare
|
|
89
|
+
nil
|
|
90
|
+
when :soundcloud
|
|
91
|
+
nil
|
|
92
|
+
else
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def build_embeded_youtube_url
|
|
98
|
+
"https://www.youtube.com/embed/#{embed_id}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def build_embeded_vimeo_url
|
|
102
|
+
"https://player.vimeo.com/video/#{embed_id}"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def build_embeded_dailymotion_url
|
|
106
|
+
"https://geo.dailymotion.com/player.html?video=#{embed_id}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def build_facebook_video_url
|
|
110
|
+
username, id = embed_id.to_s.split("/", 2)
|
|
111
|
+
"https://www.facebook.com/#{username}/videos/#{id}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def build_twitter_url
|
|
115
|
+
username, id = embed_id.to_s.split("/", 2)
|
|
116
|
+
"https://twitter.com/#{username}/status/#{id}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def embed_type
|
|
120
|
+
@embed_type ||= @element.embed_type.to_sym
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def embed_id
|
|
124
|
+
@embed_id ||= @element.embed_id.to_sym
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module ArticleJSON
|
|
2
|
+
module Export
|
|
3
|
+
module AppleNews
|
|
4
|
+
module Elements
|
|
5
|
+
class Heading < Base
|
|
6
|
+
# Headline
|
|
7
|
+
# @return [Hash]
|
|
8
|
+
def export
|
|
9
|
+
{
|
|
10
|
+
role: role,
|
|
11
|
+
text: @element.content,
|
|
12
|
+
layout: 'titleLayout',
|
|
13
|
+
textStyle: 'defaultTitle',
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# The role of text component for adding a heading. (Required) Always
|
|
20
|
+
# one of these roles for this component: heading, heading1, heading2,
|
|
21
|
+
# heading3, heading4, heading5, or heading6.
|
|
22
|
+
# @return [String]
|
|
23
|
+
def role
|
|
24
|
+
return 'heading' if @element.level.nil?
|
|
25
|
+
|
|
26
|
+
"heading#{@element.level}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module ArticleJSON
|
|
2
|
+
module Export
|
|
3
|
+
module AppleNews
|
|
4
|
+
module Elements
|
|
5
|
+
class Image < Base
|
|
6
|
+
# Image | Image, Caption
|
|
7
|
+
# @return [Hash, Array<Hash>]
|
|
8
|
+
def export
|
|
9
|
+
caption_text.nil? ? image : [image, caption]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
# Image
|
|
15
|
+
# @return [Hash]
|
|
16
|
+
def image
|
|
17
|
+
{
|
|
18
|
+
role: 'image',
|
|
19
|
+
URL: @element.source_url,
|
|
20
|
+
caption: caption_text,
|
|
21
|
+
}.compact
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Caption
|
|
25
|
+
# @return [Hash]
|
|
26
|
+
def caption
|
|
27
|
+
{
|
|
28
|
+
role: 'caption',
|
|
29
|
+
text: caption_text,
|
|
30
|
+
format: 'html',
|
|
31
|
+
layout: 'captionLayout',
|
|
32
|
+
textStyle: 'captionStyle',
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get the exporter class for text elements
|
|
37
|
+
# @return [ArticleJSON::Export::Common::HTML::Elements::Base]
|
|
38
|
+
def text_exporter
|
|
39
|
+
self.class.exporter_by_type(:text)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Caption Text
|
|
43
|
+
# @return [String]
|
|
44
|
+
def caption_text
|
|
45
|
+
text.empty? ? nil : text
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [String]
|
|
49
|
+
def text
|
|
50
|
+
@element.caption.map do |child_element|
|
|
51
|
+
text_exporter.new(child_element)
|
|
52
|
+
.export
|
|
53
|
+
end.join
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module ArticleJSON
|
|
2
|
+
module Export
|
|
3
|
+
module AppleNews
|
|
4
|
+
module Elements
|
|
5
|
+
class List < Base
|
|
6
|
+
# List
|
|
7
|
+
# @return [Hash]
|
|
8
|
+
def export
|
|
9
|
+
{
|
|
10
|
+
role: 'body',
|
|
11
|
+
text: list_text,
|
|
12
|
+
format: 'html',
|
|
13
|
+
layout: 'bodyLayout',
|
|
14
|
+
textStyle: 'bodyStyle',
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# Get the exporter class for text elements
|
|
21
|
+
#
|
|
22
|
+
# @return [ArticleJSON::Export::Common::HTML::Elements::<Class>]
|
|
23
|
+
def text_exporter
|
|
24
|
+
self.class.exporter_by_type(:text)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# When it is an unordered list wrap it in <ul></ul>
|
|
28
|
+
# When it is an ordered list wrap it in <ol></ol>
|
|
29
|
+
#
|
|
30
|
+
# List Text
|
|
31
|
+
# @return [String]
|
|
32
|
+
def list_text
|
|
33
|
+
prepend_list_tag + list + append_list_tag
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Each list item should be wrapped in <li></li>
|
|
37
|
+
#
|
|
38
|
+
# @return [String]
|
|
39
|
+
def list
|
|
40
|
+
@element.content.map do |paragraph_element|
|
|
41
|
+
line_item = paragraph_element.content.map do |text_element|
|
|
42
|
+
text_exporter.new(text_element).export
|
|
43
|
+
end.join
|
|
44
|
+
|
|
45
|
+
"<li>#{line_item}</li>"
|
|
46
|
+
end.join
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [String]
|
|
50
|
+
def prepend_list_tag
|
|
51
|
+
ordered_list? ? '<ol>' : '<ul>'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @return [String]
|
|
55
|
+
def append_list_tag
|
|
56
|
+
ordered_list? ? '</ol>' : '</ul>'
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @return [Boolean]
|
|
60
|
+
def ordered_list?
|
|
61
|
+
@element.list_type == :ordered
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|