embeddable_content 0.1.19

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