embeddable_content 0.1.19

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.
Files changed (141) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rubocop.yml +62 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +15 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +49 -0
  8. data/Rakefile +22 -0
  9. data/app/assets/config/embeddable_content_manifest.js +0 -0
  10. data/app/assets/images/embeddable_content/.keep +0 -0
  11. data/app/assets/javascripts/desmos/support.js +12 -0
  12. data/app/assets/javascripts/geogebra/support.js +14 -0
  13. data/app/assets/stylesheets/embeddable_content/.keep +0 -0
  14. data/app/controllers/.keep +0 -0
  15. data/app/controllers/concerns/cms/embeddable_content_controller.rb +14 -0
  16. data/app/controllers/concerns/embeddable_content_controller.rb +85 -0
  17. data/app/helpers/.keep +0 -0
  18. data/app/mailers/.keep +0 -0
  19. data/app/models/.keep +0 -0
  20. data/app/models/concerns/provides_embeddable_content.rb +59 -0
  21. data/app/models/embeddable_model_config.rb +55 -0
  22. data/app/models/embedder_config.rb +45 -0
  23. data/app/models/embedding.rb +25 -0
  24. data/app/services/embeddable_content/desmos_files/doc_processor.rb +5 -0
  25. data/app/services/embeddable_content/desmos_files/node_processor.rb +7 -0
  26. data/app/services/embeddable_content/doc_processor.rb +63 -0
  27. data/app/services/embeddable_content/embedded_tag_info.rb +50 -0
  28. data/app/services/embeddable_content/embedded_tags.rb +30 -0
  29. data/app/services/embeddable_content/embedder.rb +89 -0
  30. data/app/services/embeddable_content/embedder_base.rb +68 -0
  31. data/app/services/embeddable_content/fragment_embedder.rb +30 -0
  32. data/app/services/embeddable_content/geogebra_files/doc_processor.rb +5 -0
  33. data/app/services/embeddable_content/geogebra_files/node_processor.rb +11 -0
  34. data/app/services/embeddable_content/html_tags/doc_processor.rb +11 -0
  35. data/app/services/embeddable_content/html_tags/node_processor.rb +26 -0
  36. data/app/services/embeddable_content/images/attributions_processor.rb +60 -0
  37. data/app/services/embeddable_content/images/doc_processor.rb +54 -0
  38. data/app/services/embeddable_content/images/image_downloader.rb +60 -0
  39. data/app/services/embeddable_content/images/img_tag_attributes.rb +125 -0
  40. data/app/services/embeddable_content/images/modal_dialog.rb +74 -0
  41. data/app/services/embeddable_content/images/node_processor.rb +91 -0
  42. data/app/services/embeddable_content/images/shared.rb +11 -0
  43. data/app/services/embeddable_content/node_processor.rb +75 -0
  44. data/app/services/embeddable_content/presentation_tags/doc_processor.rb +5 -0
  45. data/app/services/embeddable_content/presentation_tags/node_processor.rb +35 -0
  46. data/app/services/embeddable_content/record_node_processor.rb +90 -0
  47. data/app/services/embeddable_content/replacement_template_manager.rb +21 -0
  48. data/app/services/embeddable_content/sad_embedded_tags.rb +29 -0
  49. data/app/services/embeddable_content/scrubber.rb +32 -0
  50. data/app/services/embeddable_content/template_based.rb +19 -0
  51. data/app/services/embeddable_content/template_manager.rb +100 -0
  52. data/app/services/embeddable_content/tex/base_renderer.rb +33 -0
  53. data/app/services/embeddable_content/tex/canvas_renderer.rb +15 -0
  54. data/app/services/embeddable_content/tex/doc_processor.rb +55 -0
  55. data/app/services/embeddable_content/tex/mathjax_renderer.rb +11 -0
  56. data/app/services/embeddable_content/tex/mml_renderer.rb +11 -0
  57. data/app/services/embeddable_content/tex/schoology_string_renderer.rb +15 -0
  58. data/app/services/embeddable_content/tex/svg_renderer.rb +11 -0
  59. data/app/services/embeddable_content/token_replacement_map.rb +32 -0
  60. data/app/services/embeddable_content/tree_based_node_processor.rb +23 -0
  61. data/app/services/embeddable_content/video_links/doc_processor.rb +5 -0
  62. data/app/services/embeddable_content/video_links/node_processor.rb +46 -0
  63. data/app/services/embeddable_content/video_links/vimeo_player_settings.rb +19 -0
  64. data/app/services/embeddable_content/visual_element_node_processor.rb +13 -0
  65. data/app/services/embeddable_content/widget_files/doc_processor.rb +5 -0
  66. data/app/services/embeddable_content/widget_files/node_processor.rb +7 -0
  67. data/app/views/.keep +0 -0
  68. data/app/views/embeddable_content/replacements/desmos_files/_applet.html.slim +8 -0
  69. data/app/views/embeddable_content/replacements/desmos_files/_description.html.slim +6 -0
  70. data/app/views/embeddable_content/replacements/desmos_files/cc.html.slim +1 -0
  71. data/app/views/embeddable_content/replacements/desmos_files/cms.html.slim +1 -0
  72. data/app/views/embeddable_content/replacements/desmos_files/editable.html.slim +1 -0
  73. data/app/views/embeddable_content/replacements/desmos_files/exported.html.slim +1 -0
  74. data/app/views/embeddable_content/replacements/desmos_files/kiddom.html.slim +1 -0
  75. data/app/views/embeddable_content/replacements/desmos_files/print.html.slim +1 -0
  76. data/app/views/embeddable_content/replacements/desmos_files/qti.html.slim +1 -0
  77. data/app/views/embeddable_content/replacements/desmos_files/schoology.html.slim +1 -0
  78. data/app/views/embeddable_content/replacements/desmos_files/web.html.slim +1 -0
  79. data/app/views/embeddable_content/replacements/geogebra_files/_applet.html.slim +11 -0
  80. data/app/views/embeddable_content/replacements/geogebra_files/_description.html.slim +9 -0
  81. data/app/views/embeddable_content/replacements/geogebra_files/cc.html.slim +1 -0
  82. data/app/views/embeddable_content/replacements/geogebra_files/cms.html.slim +1 -0
  83. data/app/views/embeddable_content/replacements/geogebra_files/editable.html.slim +1 -0
  84. data/app/views/embeddable_content/replacements/geogebra_files/exported.html.slim +1 -0
  85. data/app/views/embeddable_content/replacements/geogebra_files/kiddom.html.slim +1 -0
  86. data/app/views/embeddable_content/replacements/geogebra_files/print.html.slim +1 -0
  87. data/app/views/embeddable_content/replacements/geogebra_files/qti.html.slim +1 -0
  88. data/app/views/embeddable_content/replacements/geogebra_files/schoology.html.slim +1 -0
  89. data/app/views/embeddable_content/replacements/geogebra_files/web.html.slim +1 -0
  90. data/app/views/embeddable_content/replacements/html_tags/editable.html.slim +16 -0
  91. data/app/views/embeddable_content/replacements/images/_button_close.html.slim +9 -0
  92. data/app/views/embeddable_content/replacements/images/_button_open.html.slim +9 -0
  93. data/app/views/embeddable_content/replacements/images/_image_embed.html.slim +14 -0
  94. data/app/views/embeddable_content/replacements/images/_modal_content.html.slim +30 -0
  95. data/app/views/embeddable_content/replacements/images/_modal_dialog.html.slim +17 -0
  96. data/app/views/embeddable_content/replacements/images/cc.html.slim +12 -0
  97. data/app/views/embeddable_content/replacements/images/cms.html.slim +1 -0
  98. data/app/views/embeddable_content/replacements/images/editable.html.slim +1 -0
  99. data/app/views/embeddable_content/replacements/images/exported.html.slim +1 -0
  100. data/app/views/embeddable_content/replacements/images/kiddom.html.slim +12 -0
  101. data/app/views/embeddable_content/replacements/images/print.html.slim +1 -0
  102. data/app/views/embeddable_content/replacements/images/qti.html.slim +6 -0
  103. data/app/views/embeddable_content/replacements/images/schoology.html.slim +12 -0
  104. data/app/views/embeddable_content/replacements/images/web.html.slim +1 -0
  105. data/app/views/embeddable_content/replacements/presentation_tags/_default.html.slim +6 -0
  106. data/app/views/embeddable_content/replacements/presentation_tags/print.html.slim +1 -0
  107. data/app/views/embeddable_content/replacements/status/warning.html.slim +12 -0
  108. data/app/views/embeddable_content/replacements/video_links/_caption.html.slim +13 -0
  109. data/app/views/embeddable_content/replacements/video_links/_description.html.slim +12 -0
  110. data/app/views/embeddable_content/replacements/video_links/_video_embed.html.slim +13 -0
  111. data/app/views/embeddable_content/replacements/video_links/_video_player.html.slim +6 -0
  112. data/app/views/embeddable_content/replacements/video_links/_vimeo_player.html.slim +6 -0
  113. data/app/views/embeddable_content/replacements/video_links/cc.html.slim +1 -0
  114. data/app/views/embeddable_content/replacements/video_links/cms.html.slim +1 -0
  115. data/app/views/embeddable_content/replacements/video_links/editable.html.slim +1 -0
  116. data/app/views/embeddable_content/replacements/video_links/exported.html.slim +1 -0
  117. data/app/views/embeddable_content/replacements/video_links/kiddom.html.slim +1 -0
  118. data/app/views/embeddable_content/replacements/video_links/print.html.slim +1 -0
  119. data/app/views/embeddable_content/replacements/video_links/qti.html.slim +1 -0
  120. data/app/views/embeddable_content/replacements/video_links/schoology.html.slim +1 -0
  121. data/app/views/embeddable_content/replacements/video_links/web.html.slim +1 -0
  122. data/app/views/embeddable_content/replacements/widget_files/_widget_omitted.html.slim +8 -0
  123. data/app/views/embeddable_content/replacements/widget_files/_widget_script.html.slim +13 -0
  124. data/app/views/embeddable_content/replacements/widget_files/cc.html.slim +1 -0
  125. data/app/views/embeddable_content/replacements/widget_files/cms.html.slim +1 -0
  126. data/app/views/embeddable_content/replacements/widget_files/editable.html.slim +1 -0
  127. data/app/views/embeddable_content/replacements/widget_files/exported.html.slim +1 -0
  128. data/app/views/embeddable_content/replacements/widget_files/kiddom.html.slim +1 -0
  129. data/app/views/embeddable_content/replacements/widget_files/print.html.slim +1 -0
  130. data/app/views/embeddable_content/replacements/widget_files/qti.html.slim +1 -0
  131. data/app/views/embeddable_content/replacements/widget_files/schoology.html.slim +1 -0
  132. data/app/views/embeddable_content/replacements/widget_files/web.html.slim +1 -0
  133. data/bin/rails +25 -0
  134. data/config/initializers/embeddable_content.rb +3 -0
  135. data/config/routes.rb +2 -0
  136. data/embeddable_content.gemspec +48 -0
  137. data/lib/embeddable_content/engine.rb +8 -0
  138. data/lib/embeddable_content/version.rb +3 -0
  139. data/lib/embeddable_content.rb +3 -0
  140. data/lib/tasks/embeddable_content_tasks.rake +4 -0
  141. metadata +309 -0
@@ -0,0 +1,91 @@
1
+ module EmbeddableContent
2
+ module Images
3
+ class NodeProcessor < EmbeddableContent::VisualElementNodeProcessor
4
+ delegate :target, :image_catalog, to: :embedder
5
+ delegate :attribution, :long_description, :label, :alt_text,
6
+ to: :record
7
+
8
+ def modal_dialog
9
+ @modal_dialog = ModalDialog.new self
10
+ end
11
+
12
+ def img_tag_attrs
13
+ @img_tag_attrs ||= ImgTagAttributes.new(self).to_h
14
+ end
15
+
16
+ TARGETS_THAT_DISPLAY_A_MODAL_DIALOG = %i[web].freeze
17
+ def display_modal_dialog?
18
+ TARGETS_THAT_DISPLAY_A_MODAL_DIALOG.include? target.to_sym
19
+ end
20
+
21
+ TARGETS_THAT_USE_DIV_TAGS = %i[editable].freeze
22
+ TARGETS_THAT_USE_FIGURE_TAGS = %i[cms print web exported qti cc schoology]
23
+ .freeze
24
+ def figure_tag
25
+ case target
26
+ when *TARGETS_THAT_USE_DIV_TAGS then :div
27
+ when *TARGETS_THAT_USE_FIGURE_TAGS then :figure
28
+ else raise "Undefined tag for target: #{target}"
29
+ end
30
+ end
31
+
32
+ def node_can_be_removed?
33
+ false
34
+ end
35
+
36
+ def node_selector_class
37
+ [super, 'embedded-content-image'].join ' '
38
+ end
39
+
40
+ private
41
+
42
+ def catalog_image
43
+ image_catalog << record
44
+ end
45
+
46
+ def replace_node
47
+ record.render_svg if svg_should_be_rendered?
48
+ catalog_image if image_should_be_catalogued?
49
+
50
+ super
51
+ end
52
+
53
+ def svg_should_be_rendered?
54
+ config.render_images? &&
55
+ record_model.requires_rendering? &&
56
+ attachment_status_matters? &&
57
+ attachment_unavailable?
58
+ end
59
+
60
+ def attachment_unavailable?
61
+ !attached_file.attached? ||
62
+ !attachment_url_response_status_ok?
63
+ end
64
+
65
+ def attachment_url_response_status_ok?
66
+ attachment_url_response.instance_of? Net::HTTPOK
67
+ end
68
+
69
+ def attachment_url_response
70
+ @attachment_url_response ||= Net::HTTP.get_response s3_ttl_service_uri
71
+ end
72
+
73
+ def s3_ttl_service_uri
74
+ @s3_ttl_service_uri ||= URI.parse s3_ttl_service_url
75
+ end
76
+
77
+ def attachment_status_matters?
78
+ Rails.env.production? &&
79
+ ENV['VERIFY_ATTACHMENT_STATUS'].eql?('true')
80
+ end
81
+
82
+ def image_should_be_catalogued?
83
+ record.present? && attribution.present?
84
+ end
85
+
86
+ def route_keys
87
+ %i[tikz_files image_files]
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,11 @@
1
+ module EmbeddableContent
2
+ module Images
3
+ module Shared
4
+ private
5
+
6
+ def strip_tags(html)
7
+ Nokogiri::HTML.fragment(html).text.strip
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,75 @@
1
+ module EmbeddableContent
2
+ class NodeProcessor < EmbedderBase
3
+ NODE_CAN_BE_REMOVED_BY_DEFAULT = true
4
+ SELECTOR_CLASS_BLANK_BY_DEFAULT = nil
5
+ RECORD_BLANK_BY_DEFAULT = nil
6
+ ALL_TARGETS_PROCESSED_BY_DEFAULT = EmbedderConfig::ALL_TARGETS
7
+ DEFAULT_TEMPLATE_MANAGER_CLASS = TemplateManager
8
+ ADDED_CLASSES_BLANK_BY_DEFAULT = [].freeze
9
+
10
+ attr_reader :node, :node_index, :doc_processor, :error
11
+
12
+ delegate :embedder, to: :doc_processor
13
+ delegate :warning_node, to: :template_manager
14
+
15
+ def initialize(doc_processor, node, node_index)
16
+ @doc_processor = doc_processor
17
+ @node = node
18
+ @node_index = node_index
19
+ super embedder.config, embedder.options
20
+ end
21
+
22
+ def process!
23
+ process_node || remove_node
24
+ rescue StandardError => e
25
+ @error = e
26
+ node.replace warning_node
27
+ end
28
+
29
+ def node_selector_class
30
+ SELECTOR_CLASS_BLANK_BY_DEFAULT
31
+ end
32
+
33
+ def node_added_classes
34
+ ADDED_CLASSES_BLANK_BY_DEFAULT
35
+ end
36
+
37
+ def record
38
+ RECORD_BLANK_BY_DEFAULT
39
+ end
40
+
41
+ private
42
+
43
+ def process_node
44
+ replace_node if node_should_be_replaced?
45
+ end
46
+
47
+ def node_should_be_replaced?
48
+ targets_that_require_processing.include? target
49
+ end
50
+
51
+ def targets_that_require_processing
52
+ ALL_TARGETS_PROCESSED_BY_DEFAULT
53
+ end
54
+
55
+ def replace_node
56
+ raise 'implement in a subclass'
57
+ end
58
+
59
+ def remove_node
60
+ node.remove if node_can_be_removed?
61
+ end
62
+
63
+ def node_can_be_removed?
64
+ NODE_CAN_BE_REMOVED_BY_DEFAULT
65
+ end
66
+
67
+ def template_manager
68
+ @template_manager ||= template_manager_class.new self
69
+ end
70
+
71
+ def template_manager_class
72
+ DEFAULT_TEMPLATE_MANAGER_CLASS
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,5 @@
1
+ module EmbeddableContent
2
+ module PresentationTags
3
+ class DocProcessor < EmbeddableContent::DocProcessor; end
4
+ end
5
+ end
@@ -0,0 +1,35 @@
1
+ module EmbeddableContent
2
+ module PresentationTags
3
+ class NodeProcessor < EmbeddableContent::TreeBasedNodeProcessor
4
+ include TemplateBased
5
+
6
+ alias presentation_tag record
7
+
8
+ def node_added_classes
9
+ ['presentation-tag-classes-added',
10
+ presentation_tag.added_class].join ' '
11
+ end
12
+
13
+ private
14
+
15
+ delegate :template_based?, to: :presentation_tag
16
+
17
+ def replace_node
18
+ template_based? ? super : modify_parent
19
+ end
20
+
21
+ def modify_parent
22
+ parent_node.add_class node_added_classes
23
+ remove_node
24
+ end
25
+
26
+ def parent_node
27
+ @parent_node ||= node.parent
28
+ end
29
+
30
+ def targets_that_require_processing
31
+ %i[print]
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,90 @@
1
+ module EmbeddableContent
2
+ class RecordNodeProcessor < NodeProcessor
3
+ delegate :title, to: :record
4
+
5
+ def record_id
6
+ @record_id ||= tag_match_data[:id] if tag_references_record?
7
+ end
8
+
9
+ def record_model
10
+ @record_model ||= tag_match_data[:model].classify.constantize if
11
+ tag_references_record?
12
+ end
13
+
14
+ def record_css_id_for(type)
15
+ "#{type}-#{record_model}-#{record_id}"
16
+ end
17
+
18
+ def record
19
+ @record ||= target_scope.find record_id
20
+ end
21
+
22
+ def s3_url(with_extension: false)
23
+ with_extension ? s3_url_with_extension : bare_s3_url
24
+ end
25
+
26
+ def cms_url
27
+ src_url
28
+ end
29
+
30
+ def s3_ttl_service_url
31
+ if Rails.env.test?
32
+ Pathname
33
+ .new(ActiveStorage::Blob.service.send(:path_for, blob.key))
34
+ .relative_path_from Rails.root
35
+ else
36
+ blob.service_url(content_type: content_type)
37
+ end
38
+ end
39
+
40
+ def attached_file
41
+ record.file
42
+ end
43
+
44
+ def node_selector_class
45
+ "embedded-content-#{record_model.model_name.param_key.dasherize}"
46
+ end
47
+
48
+ private
49
+
50
+ delegate :s3_bucket, to: :embedder
51
+ delegate :blob, to: :attached_file
52
+ delegate :content_type, to: :blob
53
+
54
+ def bare_s3_url
55
+ "https://s3.amazonaws.com/#{s3_bucket}/#{blob_key}"
56
+ end
57
+
58
+ def s3_url_with_extension
59
+ "#{bare_s3_url}.#{blob.filename.extension}"
60
+ end
61
+
62
+ def blob_key
63
+ blob.key
64
+ end
65
+
66
+ def src_url
67
+ @src_url ||= node.attr 'src'
68
+ end
69
+
70
+ def tag_match_data
71
+ @tag_match_data ||= regex_src_url.match src_url
72
+ end
73
+
74
+ def regex_src_url
75
+ %r{\/(?<model>#{route_keys.join('|')})\/(?<id>\d+)\.(?<ext>\w+)}
76
+ end
77
+
78
+ def target_scope
79
+ record_model
80
+ end
81
+
82
+ def tag_references_record?
83
+ tag_match_data.present?
84
+ end
85
+
86
+ def node_should_be_replaced?
87
+ super && tag_match_data.present?
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,21 @@
1
+ module EmbeddableContent
2
+ class ReplacementTemplateManager < TemplateManager
3
+ def replacement_node
4
+ render_target || warning_node
5
+ end
6
+
7
+ private
8
+
9
+ def node_templates_subdir
10
+ @node_templates_subdir ||= node_type.underscore
11
+ end
12
+
13
+ def node_templates_path
14
+ @node_templates_path ||= templates_dir.join node_templates_subdir
15
+ end
16
+
17
+ def target_path
18
+ @target_path ||= node_templates_path.join target.to_s
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ module EmbeddableContent
2
+ class SadEmbeddedTags
3
+ attr_accessor :ed_node, :sad_related_records, :embedded_models
4
+
5
+ def initialize(ed_node, embedded_models)
6
+ @ed_node = ed_node
7
+ @sad_related_records = SadRelatedRecords.new ed_node
8
+ @embedded_models = embedded_models
9
+ end
10
+
11
+ def each
12
+ @ed_node.tags.each do |tag|
13
+ tag.html_attrs.each do |attr|
14
+ EmbeddedTags.new(tag, attr, embedded_models).each do |info|
15
+ yield nil, nil, info
16
+ end
17
+ end
18
+ end
19
+
20
+ sad_related_records.each do |node, node_address, related_record|
21
+ related_record.html_attrs.each do |attr|
22
+ EmbeddedTags.new(related_record, attr, embedded_models).each do |info|
23
+ yield node, node_address, info
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ module EmbeddableContent
2
+ class Scrubber < EmbedderBase
3
+ def scrub(document)
4
+ document.search(scrubbed_nodes_selector).remove
5
+ document
6
+ end
7
+
8
+ private
9
+
10
+ def scrubbed_nodes_selector
11
+ all_selectors.join ','
12
+ end
13
+
14
+ def all_selectors
15
+ selectors_for_other_targets <<
16
+ '.removed-for-all-embedder-targets' <<
17
+ selector_for_current_target
18
+ end
19
+
20
+ def selector_for_current_target
21
+ ".removed-for-#{target}-embedder-target"
22
+ end
23
+
24
+ def selectors_for_other_targets
25
+ all_other_targets.map { |tgt| selector_for_other_target tgt }
26
+ end
27
+
28
+ def selector_for_other_target(tgt)
29
+ ".removed-for-all-but-#{tgt}-embedder-target"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ module EmbeddableContent
2
+ module TemplateBased
3
+ delegate :replacement_node, to: :template_manager
4
+
5
+ def node_type
6
+ @node_type ||= embedding_module.underscore
7
+ end
8
+
9
+ private
10
+
11
+ def replace_node
12
+ node.replace replacement_node
13
+ end
14
+
15
+ def template_manager_class
16
+ ReplacementTemplateManager
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,100 @@
1
+ module EmbeddableContent
2
+ class TemplateManager < EmbedderBase
3
+ DEFAULT_NUM_BACKTRACE_LINES = 10
4
+ NUM_BACKTRACE_LINES =
5
+ ENV.fetch('NUM_BACKTRACE_LINES', DEFAULT_NUM_BACKTRACE_LINES).try :to_i
6
+
7
+ attr_reader :node_processor
8
+
9
+ delegate :record, :node, :node_type, :error, :node_selector_class,
10
+ :node_added_classes, :node_index,
11
+ to: :node_processor
12
+
13
+ def initialize(node_processor)
14
+ @node_processor = node_processor
15
+ super node_processor.config, node_processor.options
16
+ end
17
+
18
+ def warning_node
19
+ render_path warning_node_path
20
+ end
21
+
22
+ def selected_backtrace_lines
23
+ return if error.nil?
24
+
25
+ error.backtrace.first(NUM_BACKTRACE_LINES).join "\n"
26
+ end
27
+
28
+ def node_attrs
29
+ @node_attrs ||= { class: node_classes,
30
+ data: node_data }.compact
31
+ end
32
+
33
+ def target_attrs
34
+ @target_attrs ||= { id: node_id }.compact
35
+ end
36
+
37
+ def node_id
38
+ return if record.nil?
39
+
40
+ "#{record_model}-#{record.id}-#{node_index}"
41
+ end
42
+
43
+ private
44
+
45
+ def node_data
46
+ @node_data ||= { 'embedder-target': target }.merge record_node_data
47
+ end
48
+
49
+ def record_node_data
50
+ return {} if record.nil?
51
+
52
+ { 'record-model': record_model,
53
+ 'record-id': record.id }
54
+ end
55
+
56
+ def record_model
57
+ return if record.nil?
58
+
59
+ @record_model ||= record.model_name.name
60
+ end
61
+
62
+ def node_classes
63
+ ['embedded-content',
64
+ node_selector_class,
65
+ node_added_classes].flatten.compact
66
+ end
67
+
68
+ def render_target
69
+ I18n.with_locale(locale) { render_path target_path }
70
+ end
71
+
72
+ def render_path(path)
73
+ ApplicationController.render rendering_options_for path
74
+ end
75
+
76
+ def rendering_options_for(path)
77
+ { assigns: assigns, template: path, layout: false }
78
+ end
79
+
80
+ def assigns
81
+ { template_manager: self }
82
+ end
83
+
84
+ def warning_node_path
85
+ @warning_node_path ||= status_template_path 'warning'
86
+ end
87
+
88
+ def status_template_path(status)
89
+ status_dir.join status
90
+ end
91
+
92
+ def status_dir
93
+ @status_dir ||= templates_dir.join 'status'
94
+ end
95
+
96
+ def templates_dir
97
+ @templates_dir ||= Pathname.new('embeddable_content').join 'replacements'
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,33 @@
1
+ require 'open3'
2
+
3
+ module EmbeddableContent
4
+ module Tex
5
+ class BaseRenderer
6
+ RENDER_TEX_JS_PATH = Pathname.new('lib').join 'tasks/render_tex.js'
7
+
8
+ attr_reader :html
9
+
10
+ def initialize(html)
11
+ @html = html
12
+ end
13
+
14
+ def render
15
+ html.replace render_format(target_format) if target_format.present?
16
+ end
17
+
18
+ private
19
+
20
+ def render_format(format)
21
+ output, status = Open3.capture2(render_tex(format), stdin_data: html)
22
+ return output if status.success?
23
+
24
+ Rails.logger.warn "Error calling #{render_tex(format)}: #{status}"
25
+ html
26
+ end
27
+
28
+ def render_tex(format)
29
+ [RENDER_TEX_JS_PATH, '--output', format].join(' ')
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ require 'open3'
2
+
3
+ module EmbeddableContent
4
+ module Tex
5
+ class CanvasRenderer < BaseRenderer
6
+ def target_format
7
+ :canvas
8
+ end
9
+
10
+ def render_format(_format)
11
+ Mathjax::CanvasRenderer.new(html).render
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,55 @@
1
+ module EmbeddableContent
2
+ module Tex
3
+ class DocProcessor < EmbeddableContent::DocProcessor
4
+ REPAIRED_MATH_CSS_CLASS = 'math-repaired'.freeze
5
+ REPAIRED_MATH_CSS_SELECTOR = ".#{REPAIRED_MATH_CSS_CLASS}".freeze
6
+
7
+ delegate :remove_repaired_math_spans?, to: :embedder
8
+
9
+ private
10
+
11
+ def process_nodes?
12
+ remove_repaired_math_spans?
13
+ end
14
+
15
+ def process_node(node, _node_index)
16
+ remove_repaired_math_span node
17
+ end
18
+
19
+ def remove_repaired_math_span(node)
20
+ node.replace node.content
21
+ end
22
+
23
+ def post_process
24
+ refresh_html
25
+ render_tex
26
+ fix_mml_fragments if fix_mml_fragments?
27
+ rebuild_document
28
+ end
29
+
30
+ def node_selector
31
+ REPAIRED_MATH_CSS_SELECTOR
32
+ end
33
+
34
+ def repaired_math_nodes
35
+ @repaired_math_nodes ||= document.css(REPAIRED_MATH_CSS_SELECTOR)
36
+ end
37
+
38
+ def render_tex
39
+ tex_renderer.new(html).render
40
+ end
41
+
42
+ def fix_mml_fragments?
43
+ embedder.fragment? && embedder.xml?
44
+ end
45
+
46
+ def fix_mml_fragments
47
+ html.replace Nokogiri::HTML(html).at('body').children.to_xml
48
+ end
49
+
50
+ def tex_renderer
51
+ "#{module_name}::#{tex_output_format.classify}Renderer".constantize
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ require 'open3'
2
+
3
+ module EmbeddableContent
4
+ module Tex
5
+ class MathjaxRenderer < BaseRenderer
6
+ def target_format
7
+ nil
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'open3'
2
+
3
+ module EmbeddableContent
4
+ module Tex
5
+ class MmlRenderer < BaseRenderer
6
+ def target_format
7
+ :mml
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ require 'open3'
2
+
3
+ module EmbeddableContent
4
+ module Tex
5
+ class SchoologyStringRenderer < BaseRenderer
6
+ def target_format
7
+ :schoology_string
8
+ end
9
+
10
+ def render_format(_format)
11
+ Mathjax::SchoologyStringRenderer.new(html).render
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ require 'open3'
2
+
3
+ module EmbeddableContent
4
+ module Tex
5
+ class SvgRenderer < BaseRenderer
6
+ def target_format
7
+ :svg
8
+ end
9
+ end
10
+ end
11
+ end