article_json 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/CODE_OF_CONDUCT.md +46 -0
  4. data/README.md +145 -1
  5. data/lib/article_json/article.rb +31 -2
  6. data/lib/article_json/configuration.rb +56 -0
  7. data/lib/article_json/elements/paragraph.rb +14 -0
  8. data/lib/article_json/elements/text.rb +13 -0
  9. data/lib/article_json/export/amp/custom_element_library_resolver.rb +55 -0
  10. data/lib/article_json/export/amp/elements/base.rb +36 -0
  11. data/lib/article_json/export/amp/elements/embed.rb +97 -0
  12. data/lib/article_json/export/amp/elements/heading.rb +11 -0
  13. data/lib/article_json/export/amp/elements/image.rb +30 -0
  14. data/lib/article_json/export/amp/elements/list.rb +11 -0
  15. data/lib/article_json/export/amp/elements/paragraph.rb +11 -0
  16. data/lib/article_json/export/amp/elements/quote.rb +11 -0
  17. data/lib/article_json/export/amp/elements/text.rb +11 -0
  18. data/lib/article_json/export/amp/elements/text_box.rb +11 -0
  19. data/lib/article_json/export/amp/exporter.rb +36 -0
  20. data/lib/article_json/export/common/html/elements/base.rb +111 -0
  21. data/lib/article_json/export/common/html/elements/embed.rb +34 -0
  22. data/lib/article_json/export/common/html/elements/heading.rb +23 -0
  23. data/lib/article_json/export/common/html/elements/image.rb +36 -0
  24. data/lib/article_json/export/common/html/elements/list.rb +36 -0
  25. data/lib/article_json/export/common/html/elements/paragraph.rb +29 -0
  26. data/lib/article_json/export/common/html/elements/quote.rb +32 -0
  27. data/lib/article_json/export/common/html/elements/shared/caption.rb +32 -0
  28. data/lib/article_json/export/common/html/elements/shared/float.rb +19 -0
  29. data/lib/article_json/export/common/html/elements/text.rb +57 -0
  30. data/lib/article_json/export/common/html/elements/text_box.rb +29 -0
  31. data/lib/article_json/export/common/html/exporter.rb +31 -0
  32. data/lib/article_json/export/html/elements/base.rb +14 -43
  33. data/lib/article_json/export/html/elements/embed.rb +1 -18
  34. data/lib/article_json/export/html/elements/heading.rb +1 -9
  35. data/lib/article_json/export/html/elements/image.rb +1 -23
  36. data/lib/article_json/export/html/elements/list.rb +1 -15
  37. data/lib/article_json/export/html/elements/paragraph.rb +1 -7
  38. data/lib/article_json/export/html/elements/quote.rb +1 -19
  39. data/lib/article_json/export/html/elements/text.rb +1 -34
  40. data/lib/article_json/export/html/elements/text_box.rb +1 -15
  41. data/lib/article_json/export/html/exporter.rb +6 -11
  42. data/lib/article_json/import/google_doc/html/embedded_vimeo_video_parser.rb +4 -4
  43. data/lib/article_json/import/google_doc/html/node_analyzer.rb +24 -2
  44. data/lib/article_json/import/google_doc/html/parser.rb +16 -7
  45. data/lib/article_json/import/google_doc/html/shared/caption.rb +7 -0
  46. data/lib/article_json/import/google_doc/html/text_parser.rb +4 -1
  47. data/lib/article_json/utils/additional_element_placer.rb +66 -0
  48. data/lib/article_json/utils/o_embed_resolver/facebook_video.rb +1 -1
  49. data/lib/article_json/utils/o_embed_resolver/slideshare.rb +1 -1
  50. data/lib/article_json/utils/o_embed_resolver/youtube_video.rb +1 -1
  51. data/lib/article_json/utils.rb +1 -0
  52. data/lib/article_json/version.rb +1 -1
  53. data/lib/article_json.rb +25 -2
  54. metadata +31 -6
  55. data/lib/article_json/export/html/elements/shared/caption.rb +0 -22
  56. data/lib/article_json/export/html/elements/shared/float.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 14715c8b4542d6737963ab39e16c97a2060454ab
4
- data.tar.gz: ae62279949f41500e34cb18bd1642b73c6bbb86a
3
+ metadata.gz: 416c6c5505374ac9303aa393dcfbfb361276fa45
4
+ data.tar.gz: 5e22d9856b75f0d16203678d2b00e9faa05831b5
5
5
  SHA512:
6
- metadata.gz: 877ad153850744cd40a169240b1151ee09612775eb7bc4e53f5cebc60b0fc9c859e3861ebc4b1c3b119a618c905242945a681d1f749b1fedbfff69281ed5cd9c
7
- data.tar.gz: a636ea3a56f54700275e31a38df2b41ed23f1c9d8199e5b5408b078c3e33fc999872406194bddc238bd57352a52f0e22c6ca3bef34ad43942b2a0eb06ed7d6f5
6
+ metadata.gz: 015714d59724701d0fc6edda9a7257315d903055c6d5c3a7c233e4f0780e2f6cb227e2c02c0f30df5999d6d8bc75da643c59bda757858386c55f604f8166b601
7
+ data.tar.gz: dd5b53abece174c23c2639ae80ef53f28bd79004249c46dc4aec26e1c0de4fa9bb91512be40625394b3e7c740bd3229e2596fba0655d2837d0cca5e0636db651
data/CHANGELOG.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # Changelog
2
2
 
3
- ## ? - WIP
3
+ ## 0.2.0 - 2017/11/03
4
+ In this second release we **added support** to:
5
+ - Export AMP along with required libraries for AMP rendering
6
+ - Configure custom HTML and AMP element exporters
7
+ - Resolve oembed elements in HTML export
8
+
9
+ One potentially **breaking change** was added:
10
+ - Export quotes as `<div>` instead of `<aside>`
11
+
12
+ **Fixes**:
13
+ - Support Vimeo videos with old flash player URLs
14
+ - Make Google Parser more fault tolerant
15
+ - Respect linebreaks when importing Google Docs
16
+ - Export linebreaks in JSON to `<br>` tags in HTML / AMP
4
17
 
5
18
  ## 0.1.0 - 2017/09/20
6
19
  This is the very first release, with the following functionality:
@@ -0,0 +1,46 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ ## Our Standards
8
+
9
+ Examples of behavior that contributes to creating a positive environment include:
10
+
11
+ * Using welcoming and inclusive language
12
+ * Being respectful of differing viewpoints and experiences
13
+ * Gracefully accepting constructive criticism
14
+ * Focusing on what is best for the community
15
+ * Showing empathy towards other community members
16
+
17
+ Examples of unacceptable behavior by participants include:
18
+
19
+ * The use of sexualized language or imagery and unwelcome sexual attention or advances
20
+ * Trolling, insulting/derogatory comments, and personal or political attacks
21
+ * Public or private harassment
22
+ * Publishing others' private information, such as a physical or electronic address, without explicit permission
23
+ * Other conduct which could reasonably be considered inappropriate in a professional setting
24
+
25
+ ## Our Responsibilities
26
+
27
+ Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28
+
29
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30
+
31
+ ## Scope
32
+
33
+ This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34
+
35
+ ## Enforcement
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at info@devex.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38
+
39
+ Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40
+
41
+ ## Attribution
42
+
43
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44
+
45
+ [homepage]: http://contributor-covenant.org
46
+ [version]: http://contributor-covenant.org/version/1/4/
data/README.md CHANGED
@@ -27,6 +27,11 @@ article = ArticleJSON::Article.from_hash(parsed_json)
27
27
  # export article as HTML
28
28
  puts article.to_html
29
29
 
30
+ # export article as AMP
31
+ puts article.to_amp
32
+ # get javascript libraries needed for the AMP article
33
+ puts article.amp_exporter.amp_libraries
34
+
30
35
  # export article as JSON
31
36
  puts article.to_json
32
37
  ```
@@ -42,6 +47,44 @@ $ ./bin/article_json_export_google_doc.rb $DOC_ID \
42
47
  | ./bin/article_json_export_html.rb
43
48
  ```
44
49
 
50
+ ### Configuration
51
+ There are some configuration options that allow a more tailored usage of the
52
+ `article_json` gem. The following code snippet gives an example for every
53
+ available setting:
54
+
55
+ ```ruby
56
+ ArticleJSON.configure do |config|
57
+ # set a custom user agent used for o-embed API calls
58
+ config.oembed_user_agent = 'devex oembed (+https://www.devex.com/)'
59
+
60
+ # Register additional html exporters, just make sure that it complies with the
61
+ # interface of other element exporter classes (extend Base, implement #export)
62
+ config.register_element_exporters(
63
+ :html,
64
+ advertisement: ArticleJSON::Export::HTML::Elements::Advertisement
65
+ )
66
+
67
+ # You can also overwrite existing exporters:
68
+ config.register_element_exporters(
69
+ :html,
70
+ image: ArticleJSON::Export::HTML::Elements::ScaledImage
71
+ )
72
+
73
+ # And you can define multiple custom exporters:
74
+ config.register_element_exporters(
75
+ :html,
76
+ advertisement: ArticleJSON::Export::HTML::Elements::Advertisement,
77
+ image: ArticleJSON::Export::HTML::Elements::ScaledImage
78
+ )
79
+
80
+ # It works the same way for custom AMP exporters:
81
+ config.register_element_exporters(
82
+ :amp,
83
+ image: ArticleJSON::Export::AMP::Elements::ScaledImage
84
+ )
85
+ end
86
+ ```
87
+
45
88
  ## Format
46
89
  A full example of the format can be found in the test fixtures:
47
90
  [Parsed Reference Document](https://github.com/Devex/article_json/blob/master/spec/fixtures/reference_document_parsed.json)
@@ -51,12 +94,113 @@ A full example of the format can be found in the test fixtures:
51
94
  This [Reference Document](https://docs.google.com/document/d/1E4lncZE2jDkbE34eDyYQmXKA9O26BHUiwguz4S9qyE8/edit?usp=sharing)
52
95
  lists contains all supported formatting along with some descriptions.
53
96
 
97
+ ## Add custom elements
98
+ Sometimes you might want to place additional elements into the article, like e.g. advertisements.
99
+ `article_json` supports this via `article.place_additional_elements` which accepts an array of elements that you can define in your own code.
100
+ Each element that is added this way will directly get placed in between paragraphs of the article.
101
+ The method ensures that an additional element is never added before or after any node other than paragraphs (e.g. an image).
102
+ The elements are added in the order you pass them into the method.
103
+ If the article should not have enough spaces to place all the provided elements, they will be placed after the last element in the article.
104
+
105
+ You can pass any type of element into this method.
106
+ If the objects you pass in are instances of elements defined within this gem (e.g. `ArticleJSON::Elements::Image`), you won't have to do anything else to get them rendered.
107
+ If you pass in an instance of a custom class (e.g. `MyAdvertisement`), make sure to register an exporter for this type (check the _Configuration_ section for more details).
108
+
109
+ Example using only existing elements:
110
+ ```ruby
111
+ # Create your article instance as you normally do
112
+ article = ArticleJSON::Article.from_hash(parsed_json)
113
+
114
+ # Within your code, create additional elements you would like to add
115
+ image_advertisement =
116
+ ArticleJSON::Elements::Image.new(source_url: 'https://robohash.org/great-ad',
117
+ caption: ArticleJSON::Elements::Text.new(
118
+ content: 'Buy more robots!',
119
+ href: '/robot-sale'
120
+ ))
121
+ text_box_similar_articles =
122
+ ArticleJSON::Elements::TextBox.new(content: [
123
+ ArticleJSON::Elements::Heading.new(level: 3, content: 'Read more...'),
124
+ ArticleJSON::Elements::List.new(content: [
125
+ ArticleJSON::Elements::Paragraph(content: [
126
+ ArticleJSON::Elements::Text.new(content: 'Very similar article',
127
+ href: '/news/123'),
128
+ ]),
129
+ ArticleJSON::Elements::Paragraph(content: [
130
+ ArticleJSON::Elements::Text.new(content: 'Great article!',
131
+ href: '/news/42'),
132
+ ]),
133
+ ]),
134
+ ])
135
+
136
+ # Add these elements to the article
137
+ article.place_additional_elements([image_advertisement,
138
+ text_box_similar_articles])
139
+
140
+ # Export the article to the different formats as you would normally do
141
+ article.to_html # this will now include the custom elements
142
+ ```
143
+
144
+ Example with custom advertisement elements:
145
+ ```ruby
146
+ # Define your custom element class
147
+ class MyAdvertisement
148
+ attr_reader :url
149
+
150
+ def initialize(url:)
151
+ @url = url
152
+ end
153
+
154
+ def type
155
+ :my_advertisement
156
+ end
157
+ end
158
+
159
+ # Define an exporter for your class, we only use HTML in this example but this
160
+ # would work similarly for AMP or other formats
161
+ class MyAdvertisementExporter <
162
+ ArticleJSON::Export::HTML::Elements::Base
163
+
164
+ # Needs to implement the `#export` method
165
+ def export
166
+ create_element(:iframe, src: @element.url)
167
+ end
168
+ end
169
+
170
+ # Register your custom exporter for your element type
171
+ config.register_element_exporters(
172
+ :html,
173
+ my_advertisement: MyAdvertisementExporter
174
+ )
175
+
176
+ # Create the elements you want to add
177
+ ad_1 = MyAdvertisement.new(url: '/my_first_ad')
178
+ ad_2 = MyAdvertisement.new(url: '/my_second_ad')
179
+ ad_3 = MyAdvertisement.new(url: '/my_last_ad')
180
+
181
+ # Add them to the article
182
+ article.place_additional_elements([ad_1, ad_2, ad_3])
183
+
184
+ # And again, export the article as you would normally do it
185
+ article.to_html
186
+ ```
187
+
54
188
  ## Export
55
189
  ### HTML
56
190
  The HTML exporter generates a HTML string for a list of elements. An example of
57
191
  the HTML export for the parsed reference document can be found
58
192
  [here](https://github.com/Devex/article_json/blob/master/spec/fixtures/reference_document_exported.html).
59
193
 
194
+ ### AMP
195
+ The AMP exporter generates an AMP HTML representation of the elements.
196
+
197
+ AMP uses [custom HTML tags](https://www.ampproject.org/docs/reference/components), some of which require additional Javascript libraries.
198
+ If you have an `article` (see code example in _Usage_ section), you can get a list of the custom tags required by this article by calling `article.amp_exporter.custom_element_tags` and by calling `article.amp_exporter.amp_libraries` you get a list of `<script>` tags that can directly be included on your page to render the AMP article.
199
+
200
+ An example of
201
+ the AMP HTML export for the parsed reference document can be found
202
+ [here](https://github.com/Devex/article_json/blob/master/spec/fixtures/reference_document_exported.amp.html).
203
+
60
204
  ## Contributing
61
205
  - Fork this repository
62
206
  - Implement your feature or fix including Tests
@@ -72,7 +216,7 @@ See the
72
216
  ### Tests
73
217
  For the whole test suite, run `bundle exec rspec`.
74
218
 
75
- For individual tests, run `bundle exec rspec spec/article_json/version_spec.rb`.
219
+ For individual tests, run `bundle exec rspec spec/article_json/version_spec.rb`.
76
220
 
77
221
  ## License
78
222
  MIT License, see the [license file](LICENSE).
@@ -2,7 +2,7 @@ module ArticleJSON
2
2
  class Article
3
3
  attr_reader :elements
4
4
 
5
- # @param [Arra[ArticleJSON::Elements::Base]] elements
5
+ # @param [Array[ArticleJSON::Elements::Base]] elements
6
6
  def initialize(elements)
7
7
  @elements = elements
8
8
  end
@@ -22,10 +22,39 @@ module ArticleJSON
22
22
  to_h.to_json
23
23
  end
24
24
 
25
+ # Exporter instance for HTML
26
+ # @return [ArticleJSON::Export::HTML::Exporter]
27
+ def html_exporter
28
+ @html_exporter = ArticleJSON::Export::HTML::Exporter.new(@elements)
29
+ end
30
+
25
31
  # HTML export of the article
26
32
  # @return [String]
27
33
  def to_html
28
- ArticleJSON::Export::HTML::Exporter.new(@elements).html
34
+ html_exporter.html
35
+ end
36
+
37
+ # Exporter instance for AMP
38
+ # @return [ArticleJSON::Export::AMP::Exporter]
39
+ def amp_exporter
40
+ ArticleJSON::Export::AMP::Exporter.new(@elements)
41
+ end
42
+
43
+ # AMP export of the article
44
+ # @return [String]
45
+ def to_amp
46
+ amp_exporter.html
47
+ end
48
+
49
+ # Distribute passed elements evenly throughout the article. All passed
50
+ # elements need to have an exporter to be represented in the rendered
51
+ # article.
52
+ # @param [Object] additional_elements
53
+ def place_additional_elements(additional_elements)
54
+ @elements =
55
+ ArticleJSON::Utils::AdditionalElementPlacer
56
+ .new(self, additional_elements)
57
+ .merge_elements
29
58
  end
30
59
 
31
60
  class << self
@@ -18,6 +18,62 @@ module ArticleJSON
18
18
 
19
19
  def initialize
20
20
  @oembed_user_agent = nil
21
+ @custom_element_exporters = {}
22
+ end
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
+ # Register new element exporters or overwrite existing ones for a given
47
+ # exporter type.
48
+ # Usage example:
49
+ # register_element_exporters(:html,
50
+ # image: MyImageExporter,
51
+ # advertisement: MyAdExporter)
52
+ # @param [Symbol] exporter
53
+ # @param [Hash[Symbol => Class]] type_class_mapping
54
+ def register_element_exporters(exporter, type_class_mapping)
55
+ unless %i(html amp).include?(exporter)
56
+ raise ArgumentError, '`exporter` needs to be either `:html` or `:amp` '\
57
+ "but is `#{exporter.inspect}`"
58
+ end
59
+ if !type_class_mapping.is_a?(Hash) ||
60
+ type_class_mapping.keys.any? { |key| !key.is_a? Symbol } ||
61
+ type_class_mapping.values.any? { |value| !value.is_a? Class }
62
+ raise ArgumentError, '`type_class_mapping` has to be a Hash with '\
63
+ 'symbolized keys and classes as values but is '\
64
+ "`#{type_class_mapping.inspect}`"
65
+ end
66
+
67
+ @custom_element_exporters[exporter.to_sym] ||= {}
68
+ @custom_element_exporters[exporter.to_sym].merge!(type_class_mapping)
69
+ end
70
+
71
+ # Get custom exporter class for a given exporter and element type
72
+ # @param [Symbol] exporter_type
73
+ # @param [Symbol] element_type
74
+ # @return [Class|nil]
75
+ def element_exporter_for(exporter_type, element_type)
76
+ @custom_element_exporters.dig(exporter_type, element_type)
21
77
  end
22
78
  end
23
79
  end
@@ -18,6 +18,20 @@ module ArticleJSON
18
18
  }
19
19
  end
20
20
 
21
+ # Return `true` if the paragraph has no elements
22
+ # @return [Boolean]
23
+ def empty?
24
+ !content || content.empty?
25
+ end
26
+
27
+ # Return `true` if the paragraph is empty or if all elements are blank
28
+ # @return [Boolean]
29
+ def blank?
30
+ empty? || content.all? do |element|
31
+ element.respond_to?(:blank?) && element.blank?
32
+ end
33
+ end
34
+
21
35
  class << self
22
36
  # Create a paragraph element from Hash
23
37
  # @return [ArticleJSON::Elements::Paragraph]
@@ -27,6 +27,19 @@ module ArticleJSON
27
27
  }
28
28
  end
29
29
 
30
+ # Returns `true` if `content` has a length of zero or is `nil`
31
+ # @return [Boolean]
32
+ def empty?
33
+ !content || content.empty?
34
+ end
35
+
36
+ # Returns `true` if `content` is empty (see `#empty?`) or only contains
37
+ # whitespace (including non-breaking whitespace) characters
38
+ # @return [Boolean]
39
+ def blank?
40
+ empty? || content.gsub(/[\s\u00A0]/, '').empty?
41
+ end
42
+
30
43
  class << self
31
44
  # Create a text element from Hash
32
45
  # @return [ArticleJSON::Elements::Text]
@@ -0,0 +1,55 @@
1
+ module ArticleJSON
2
+ module Export
3
+ module AMP
4
+ # AMP uses custom HTML tags for elements like iframes or embedded youtube
5
+ # videos. These elements each require a javascript to be loaded to be
6
+ # properly rendered by the browser. This class resolves the custom tags to
7
+ # a list of javascript libraries which then can be included on the page.
8
+ class CustomElementLibraryResolver
9
+ # @param [Array[Symbol]] custom_element_tags
10
+ def initialize(custom_element_tags)
11
+ @custom_element_tags = custom_element_tags
12
+ end
13
+
14
+ # List of all custom tags with their library source URI
15
+ # @return [Hash[Symbol => String]]
16
+ def sources
17
+ @custom_element_tags.each_with_object({}) do |custom_element, mapping|
18
+ src = custom_element_script_mapping(custom_element)
19
+ mapping[custom_element] = src if src
20
+ end
21
+ end
22
+
23
+ # Return all custom library script tags required for the given custom
24
+ # element tags
25
+ # @return [Array[String]]
26
+ def script_tags
27
+ sources.map do |custom_element_tag, src|
28
+ <<-HTML.gsub(/\s+/, ' ').strip
29
+ <script async
30
+ custom-element="#{custom_element_tag}"
31
+ src="#{src}"></script>
32
+ HTML
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # Given a custom_element identifier, get the script script source
39
+ # @param [Symbol] custom_element_tag
40
+ # @return [String]
41
+ def custom_element_script_mapping(custom_element_tag)
42
+ {
43
+ 'amp-iframe': 'https://cdn.ampproject.org/v0/amp-iframe-0.1.js',
44
+ 'amp-twitter': 'https://cdn.ampproject.org/v0/amp-twitter-0.1.js',
45
+ 'amp-youtube': 'https://cdn.ampproject.org/v0/amp-youtube-0.1.js',
46
+ 'amp-vimeo': 'https://cdn.ampproject.org/v0/amp-vimeo-0.1.js',
47
+ 'amp-facebook':
48
+ 'https://cdn.ampproject.org/v0/amp-facebook-0.1.js',
49
+ }[custom_element_tag]
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,36 @@
1
+ module ArticleJSON
2
+ module Export
3
+ module AMP
4
+ module Elements
5
+ class Base
6
+ include ArticleJSON::Export::Common::HTML::Elements::Base
7
+
8
+ # List of custom element tags used by this element
9
+ # @return [Array[Symbol]]
10
+ def custom_element_tags
11
+ []
12
+ end
13
+
14
+ class << self
15
+ # Return the module namespace this class and its subclasses are
16
+ # nested in
17
+ # @return [Module]
18
+ def namespace
19
+ ArticleJSON::Export::AMP::Elements
20
+ end
21
+
22
+ private
23
+
24
+ # The format this exporter is returning. This is used to determine
25
+ # which custom element exporters should be applied from the
26
+ # configuration.
27
+ # @return [Symbol]
28
+ def export_format
29
+ :amp
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,97 @@
1
+ module ArticleJSON
2
+ module Export
3
+ module AMP
4
+ module Elements
5
+ class Embed < Base
6
+ include ArticleJSON::Export::Common::HTML::Elements::Embed
7
+
8
+ # Custom element tags required for this embedded element
9
+ # @return [Array[Symbol]]
10
+ def custom_element_tags
11
+ case @element.embed_type.to_sym
12
+ when :youtube_video then %i(amp-youtube)
13
+ when :vimeo_video then %i(amp-vimeo)
14
+ when :facebook_video then %i(amp-facebook)
15
+ when :tweet then %i(amp-twitter)
16
+ when :slideshare then %i(amp-iframe)
17
+ else []
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ # Type specific object that should be embedded
24
+ # @return [Nokogiri::XML::Element|nil]
25
+ def embedded_object
26
+ case @element.embed_type.to_sym
27
+ when :youtube_video
28
+ youtube_node
29
+ when :vimeo_video
30
+ vimeo_node
31
+ when :facebook_video
32
+ facebook_node
33
+ when :tweet
34
+ tweet_node
35
+ when :slideshare
36
+ slideshare_node
37
+ end
38
+ end
39
+
40
+ # @return [Nokogiri::XML::Element]
41
+ def youtube_node
42
+ create_element('amp-youtube',
43
+ 'data-videoid' => @element.embed_id,
44
+ width: default_width,
45
+ height: default_height)
46
+ end
47
+
48
+ # @return [Nokogiri::XML::Element]
49
+ def vimeo_node
50
+ create_element('amp-vimeo',
51
+ 'data-videoid' => @element.embed_id,
52
+ width: default_width,
53
+ height: default_height)
54
+ end
55
+
56
+ # @return [Nokogiri::XML::Element]
57
+ def tweet_node
58
+ create_element('amp-twitter',
59
+ 'data-tweetid' => @element.embed_id,
60
+ width: default_width,
61
+ height: default_height)
62
+ end
63
+
64
+ # @return [Nokogiri::XML::Element]
65
+ def facebook_node
66
+ url = "#{@element.oembed_data[:author_url]}videos/#{@element.embed_id}"
67
+ create_element('amp-facebook',
68
+ 'data-embedded-as' => 'video',
69
+ 'data-href' => url,
70
+ width: default_width,
71
+ height: default_height)
72
+ end
73
+
74
+ # @return [Nokogiri::XML::Element]
75
+ def slideshare_node
76
+ node = Nokogiri::HTML(@element.oembed_data[:html]).xpath('//iframe')
77
+ create_element('amp-iframe',
78
+ src: node.attribute('src').value,
79
+ width: node.attribute('width').value,
80
+ height: node.attribute('height').value,
81
+ frameborder: '0',)
82
+ end
83
+
84
+ # @return [String]
85
+ def default_width
86
+ '560'
87
+ end
88
+
89
+ # @return [String]
90
+ def default_height
91
+ '315'
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,11 @@
1
+ module ArticleJSON
2
+ module Export
3
+ module AMP
4
+ module Elements
5
+ class Heading < Base
6
+ include ArticleJSON::Export::Common::HTML::Elements::Heading
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ module ArticleJSON
2
+ module Export
3
+ module AMP
4
+ module Elements
5
+ class Image < Base
6
+ include ArticleJSON::Export::Common::HTML::Elements::Image
7
+
8
+ private
9
+
10
+ # @return [Nokogiri::HTML::NodeSet]
11
+ def image_node
12
+ create_element('amp-img',
13
+ src: @element.source_url,
14
+ width: default_width,
15
+ height: default_height,
16
+ layout: :responsive)
17
+ end
18
+
19
+ def default_width
20
+ '640'
21
+ end
22
+
23
+ def default_height
24
+ '480'
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ module ArticleJSON
2
+ module Export
3
+ module AMP
4
+ module Elements
5
+ class List < Base
6
+ include ArticleJSON::Export::Common::HTML::Elements::List
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module ArticleJSON
2
+ module Export
3
+ module AMP
4
+ module Elements
5
+ class Paragraph < Base
6
+ include ArticleJSON::Export::Common::HTML::Elements::Paragraph
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end