wovnrb 3.10.3 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,222 +1,222 @@
1
- module Wovnrb
2
- class HtmlConverter
3
- def initialize(dom, store, headers, url_lang_switcher)
4
- @dom = dom
5
- @headers = headers
6
- @store = store
7
- @url_lang_switcher = url_lang_switcher
8
- end
9
-
10
- def build
11
- transform_html
12
- html
13
- end
14
-
15
- def build_api_compatible_html
16
- marker = HtmlReplaceMarker.new
17
- converted_html = replace_dom(marker)
18
- converted_html = remove_backend_wovn_ignore_comments(converted_html, marker)
19
- [converted_html, marker]
20
- end
21
-
22
- private
23
-
24
- def remove_backend_wovn_ignore_comments(html, marker)
25
- backend_ignore_regex = /(<!--\s*backend-wovn-ignore\s*-->)(.+?)(<!--\s*\/backend-wovn-ignore\s*-->)/m
26
- html.gsub(backend_ignore_regex) do |_match|
27
- comment_start = Regexp.last_match(1)
28
- ignored_content = Regexp.last_match(2)
29
- comment_end = Regexp.last_match(3)
30
- key = marker.add_comment_value(ignored_content)
31
- comment_start + key + comment_end
32
- end
33
- end
34
-
35
- def html
36
- # Ensure a Content-Type declaration in the header. This mimics Nokogumbo
37
- # 1.5.0 default serialization behavior.
38
- @dom.meta_encoding = 'UTF-8' if @dom.respond_to?(:meta_encoding=)
39
-
40
- @dom.to_html(save_with: 0).strip
41
- end
42
-
43
- def transform_html
44
- replace_snippet
45
- replace_hreflangs if @store.settings['insert_hreflangs']
46
- inject_lang_html_tag
47
- translate_canonical_tag if @store.settings['translate_canonical_tag']
48
- end
49
-
50
- def replace_snippet
51
- strip_snippet
52
- insert_snippet
53
- end
54
-
55
- def replace_dom(marker)
56
- strip_snippet
57
- strip_hreflangs if @store.settings['insert_hreflangs']
58
-
59
- @dom.traverse { |node| transform_node(node, marker) }
60
-
61
- insert_snippet(adds_backend_error_mark: true)
62
- insert_hreflang_tags if @store.settings['insert_hreflangs']
63
- inject_lang_html_tag
64
- translate_canonical_tag if @store.settings['translate_canonical_tag']
65
-
66
- html
67
- end
68
-
69
- def transform_node(node, marker)
70
- strip_wovn_ignore(node, marker)
71
- strip_custom_ignore(node, marker)
72
- strip_form(node, marker)
73
- strip_script(node, marker)
74
- end
75
-
76
- def strip_script(node, marker)
77
- put_replace_marker(node, marker) if node.name.casecmp('script').zero?
78
- end
79
-
80
- def strip_form(node, marker)
81
- if node.name.casecmp('form').zero?
82
- put_replace_marker(node, marker)
83
- return
84
- end
85
-
86
- if node.name.casecmp('input').zero? && node.get_attribute('type') == 'hidden'
87
- original_text = node.get_attribute('value')
88
- return if original_text.nil?
89
- return if original_text.include?(HtmlReplaceMarker::KEY_PREFIX)
90
-
91
- node.set_attribute('value', marker.add_value(original_text))
92
- end
93
- end
94
-
95
- def strip_custom_ignore(node, marker)
96
- classes = node.get_attribute('class')
97
- return unless classes.present?
98
-
99
- ignored_classes = @store.settings['ignore_class']
100
- should_be_ignored = (ignored_classes & classes.split).present?
101
-
102
- put_replace_marker(node, marker) if should_be_ignored
103
- end
104
-
105
- def strip_wovn_ignore(node, marker)
106
- put_replace_marker(node, marker) if node && (node.get_attribute('wovn-ignore') || node.get_attribute('data-wovn-ignore'))
107
- end
108
-
109
- def put_replace_marker(node, marker)
110
- original_text = node.inner_html
111
- return if original_text.include?(HtmlReplaceMarker::KEY_PREFIX)
112
-
113
- node.inner_html = marker.add_comment_value(original_text)
114
- end
115
-
116
- def strip_hreflangs
117
- supported_langs = @store.supported_langs
118
- @dom.xpath('//link') do |node|
119
- node.remove if node['hreflang'] && supported_langs.include?(Lang.iso_639_1_normalization(node['hreflang']))
120
- end
121
- end
122
-
123
- def inject_lang_html_tag
124
- root = @dom.at_css('html')
125
- return unless root
126
- return if root['lang']
127
-
128
- root['lang'] = @store.default_lang
129
- end
130
-
131
- def replace_hreflangs
132
- strip_hreflang_tags
133
- insert_hreflang_tags
134
- end
135
-
136
- def strip_hreflang_tags
137
- @dom.xpath('//link').each do |node|
138
- node.remove if node['hreflang'] && @store.supported_langs.include?(Lang.iso_639_1_normalization(node['hreflang']))
139
- end
140
- end
141
-
142
- def insert_hreflang_tags
143
- parent_node = @dom.at_css('head') || @dom.at_css('body') || @dom.at_css('html')
144
- return unless parent_node
145
-
146
- @store.supported_langs.each do |lang_code|
147
- insert_node = Nokogiri::XML::Node.new('link', @dom)
148
- insert_node['rel'] = 'alternate'
149
- insert_node['hreflang'] = Lang.iso_639_1_normalization(lang_code)
150
- insert_node['href'] = @headers.redirect_location(lang_code)
151
-
152
- parent_node.add_child(insert_node.to_s)
153
- end
154
- end
155
-
156
- def translate_canonical_tag
157
- canonical_node = @dom.at_css('link[rel="canonical"]')
158
- return unless canonical_node
159
-
160
- lang_code = @headers.lang_code
161
- return if lang_code == @store.settings['default_lang'] && @store.settings['custom_lang_aliases'][lang_code].nil?
162
-
163
- canonical_url = canonical_node['href']
164
-
165
- translated_canonical_url = @url_lang_switcher.add_lang_code(canonical_url, lang_code, @headers)
166
- canonical_node['href'] = translated_canonical_url
167
- end
168
-
169
- # Remove wovn snippet code from dom
170
- def strip_snippet
171
- @dom.xpath('//script').each do |script_node|
172
- script_node.remove if (script_node['src'] && widget_urls.any? { |url| script_node['src'].include? url }) || script_node['data-wovnio'].present?
173
- end
174
- end
175
-
176
- def widget_urls
177
- ["#{@store.settings['api_url']}/widget", 'j.wovn.io', 'j.dev-wovn.io:3000']
178
- end
179
-
180
- def insert_snippet(adds_backend_error_mark: true)
181
- parent_node = @dom.at_css('head') || @dom.at_css('body') || @dom.at_css('html')
182
- return unless parent_node
183
-
184
- insert_node = Nokogiri::XML::Node.new('script', @dom)
185
- insert_node['src'] = @store.widget_url
186
- insert_node['async'] = true
187
- insert_node['data-wovnio'] = data_wovnio
188
- insert_node['data-wovnio-type'] = 'fallback_snippet' if adds_backend_error_mark
189
- # do this so that there will be a closing tag (better compatibility with browsers)
190
- insert_node.content = ''
191
-
192
- if parent_node.children.empty?
193
- parent_node.add_child(insert_node)
194
- else
195
- parent_node.children.first.add_previous_sibling(insert_node)
196
- end
197
- end
198
-
199
- def data_wovnio
200
- token = @store.settings['project_token']
201
- current_lang = @headers.lang_code
202
- default_lang = @store.settings['default_lang']
203
- url_pattern = @store.settings['url_pattern']
204
- lang_code_aliases_json = JSON.generate(@store.settings['custom_lang_aliases'])
205
- lang_param_name = @store.settings['lang_param_name']
206
- custom_domain_langs = @store.custom_domain_langs.to_html_swapper_hash
207
-
208
- result = [
209
- "key=#{token}",
210
- 'backend=true',
211
- "currentLang=#{current_lang}",
212
- "defaultLang=#{default_lang}",
213
- "urlPattern=#{url_pattern}",
214
- "langCodeAliases=#{lang_code_aliases_json}",
215
- "langParamName=#{lang_param_name}",
216
- "version=WOVN.rb_#{VERSION}"
217
- ]
218
- result << "customDomainLangs=#{JSON.generate(custom_domain_langs)}" unless custom_domain_langs.empty?
219
- result.join('&')
220
- end
221
- end
222
- end
1
+ module Wovnrb
2
+ class HtmlConverter
3
+ def initialize(dom, store, headers, url_lang_switcher)
4
+ @dom = dom
5
+ @headers = headers
6
+ @store = store
7
+ @url_lang_switcher = url_lang_switcher
8
+ end
9
+
10
+ def build
11
+ transform_html
12
+ html
13
+ end
14
+
15
+ def build_api_compatible_html
16
+ marker = HtmlReplaceMarker.new
17
+ converted_html = replace_dom(marker)
18
+ converted_html = remove_backend_wovn_ignore_comments(converted_html, marker)
19
+ [converted_html, marker]
20
+ end
21
+
22
+ private
23
+
24
+ def remove_backend_wovn_ignore_comments(html, marker)
25
+ backend_ignore_regex = /(<!--\s*backend-wovn-ignore\s*-->)(.+?)(<!--\s*\/backend-wovn-ignore\s*-->)/m
26
+ html.gsub(backend_ignore_regex) do |_match|
27
+ comment_start = Regexp.last_match(1)
28
+ ignored_content = Regexp.last_match(2)
29
+ comment_end = Regexp.last_match(3)
30
+ key = marker.add_comment_value(ignored_content)
31
+ comment_start + key + comment_end
32
+ end
33
+ end
34
+
35
+ def html
36
+ # Ensure a Content-Type declaration in the header. This mimics Nokogumbo
37
+ # 1.5.0 default serialization behavior.
38
+ @dom.meta_encoding = 'UTF-8' if @dom.respond_to?(:meta_encoding=)
39
+
40
+ @dom.to_html(save_with: 0).strip
41
+ end
42
+
43
+ def transform_html
44
+ replace_snippet
45
+ replace_hreflangs if @store.settings['insert_hreflangs']
46
+ inject_lang_html_tag
47
+ translate_canonical_tag if @store.settings['translate_canonical_tag']
48
+ end
49
+
50
+ def replace_snippet
51
+ strip_snippet
52
+ insert_snippet
53
+ end
54
+
55
+ def replace_dom(marker)
56
+ strip_snippet
57
+ strip_hreflangs if @store.settings['insert_hreflangs']
58
+
59
+ @dom.traverse { |node| transform_node(node, marker) }
60
+
61
+ insert_snippet(adds_backend_error_mark: true)
62
+ insert_hreflang_tags if @store.settings['insert_hreflangs']
63
+ inject_lang_html_tag
64
+ translate_canonical_tag if @store.settings['translate_canonical_tag']
65
+
66
+ html
67
+ end
68
+
69
+ def transform_node(node, marker)
70
+ strip_wovn_ignore(node, marker)
71
+ strip_custom_ignore(node, marker)
72
+ strip_form(node, marker)
73
+ strip_script(node, marker)
74
+ end
75
+
76
+ def strip_script(node, marker)
77
+ put_replace_marker(node, marker) if node.name.casecmp('script').zero?
78
+ end
79
+
80
+ def strip_form(node, marker)
81
+ if node.name.casecmp('form').zero?
82
+ put_replace_marker(node, marker)
83
+ return
84
+ end
85
+
86
+ if node.name.casecmp('input').zero? && node.get_attribute('type') == 'hidden'
87
+ original_text = node.get_attribute('value')
88
+ return if original_text.nil?
89
+ return if original_text.include?(HtmlReplaceMarker::KEY_PREFIX)
90
+
91
+ node.set_attribute('value', marker.add_value(original_text))
92
+ end
93
+ end
94
+
95
+ def strip_custom_ignore(node, marker)
96
+ classes = node.get_attribute('class')
97
+ return unless classes.present?
98
+
99
+ ignored_classes = @store.settings['ignore_class']
100
+ should_be_ignored = (ignored_classes & classes.split).present?
101
+
102
+ put_replace_marker(node, marker) if should_be_ignored
103
+ end
104
+
105
+ def strip_wovn_ignore(node, marker)
106
+ put_replace_marker(node, marker) if node && (node.get_attribute('wovn-ignore') || node.get_attribute('data-wovn-ignore'))
107
+ end
108
+
109
+ def put_replace_marker(node, marker)
110
+ original_text = node.inner_html
111
+ return if original_text.include?(HtmlReplaceMarker::KEY_PREFIX)
112
+
113
+ node.inner_html = marker.add_comment_value(original_text)
114
+ end
115
+
116
+ def strip_hreflangs
117
+ supported_langs = @store.supported_langs
118
+ @dom.xpath('//link') do |node|
119
+ node.remove if node['hreflang'] && supported_langs.include?(Lang.iso_639_1_normalization(node['hreflang']))
120
+ end
121
+ end
122
+
123
+ def inject_lang_html_tag
124
+ root = @dom.at_css('html')
125
+ return unless root
126
+ return if root['lang']
127
+
128
+ root['lang'] = @store.default_lang
129
+ end
130
+
131
+ def replace_hreflangs
132
+ strip_hreflang_tags
133
+ insert_hreflang_tags
134
+ end
135
+
136
+ def strip_hreflang_tags
137
+ @dom.xpath('//link').each do |node|
138
+ node.remove if node['hreflang'] && @store.supported_langs.include?(Lang.iso_639_1_normalization(node['hreflang']))
139
+ end
140
+ end
141
+
142
+ def insert_hreflang_tags
143
+ parent_node = @dom.at_css('head') || @dom.at_css('body') || @dom.at_css('html')
144
+ return unless parent_node
145
+
146
+ @store.supported_langs.each do |lang_code|
147
+ insert_node = Nokogiri::XML::Node.new('link', @dom)
148
+ insert_node['rel'] = 'alternate'
149
+ insert_node['hreflang'] = Lang.iso_639_1_normalization(lang_code)
150
+ insert_node['href'] = @headers.redirect_location(lang_code)
151
+
152
+ parent_node.add_child(insert_node.to_s)
153
+ end
154
+ end
155
+
156
+ def translate_canonical_tag
157
+ canonical_node = @dom.at_css('link[rel="canonical"]')
158
+ return unless canonical_node
159
+
160
+ lang_code = @headers.lang_code
161
+ return if lang_code == @store.settings['default_lang'] && @store.settings['custom_lang_aliases'][lang_code].nil?
162
+
163
+ canonical_url = canonical_node['href']
164
+
165
+ translated_canonical_url = @url_lang_switcher.add_lang_code(canonical_url, lang_code, @headers)
166
+ canonical_node['href'] = translated_canonical_url
167
+ end
168
+
169
+ # Remove wovn snippet code from dom
170
+ def strip_snippet
171
+ @dom.xpath('//script').each do |script_node|
172
+ script_node.remove if (script_node['src'] && widget_urls.any? { |url| script_node['src'].include? url }) || script_node['data-wovnio'].present?
173
+ end
174
+ end
175
+
176
+ def widget_urls
177
+ ["#{@store.settings['api_url']}/widget", 'j.wovn.io', 'j.dev-wovn.io:3000']
178
+ end
179
+
180
+ def insert_snippet(adds_backend_error_mark: true)
181
+ parent_node = @dom.at_css('head') || @dom.at_css('body') || @dom.at_css('html')
182
+ return unless parent_node
183
+
184
+ insert_node = Nokogiri::XML::Node.new('script', @dom)
185
+ insert_node['src'] = @store.widget_url
186
+ insert_node['async'] = true
187
+ insert_node['data-wovnio'] = data_wovnio
188
+ insert_node['data-wovnio-type'] = 'fallback_snippet' if adds_backend_error_mark
189
+ # do this so that there will be a closing tag (better compatibility with browsers)
190
+ insert_node.content = ''
191
+
192
+ if parent_node.children.empty?
193
+ parent_node.add_child(insert_node)
194
+ else
195
+ parent_node.children.first.add_previous_sibling(insert_node)
196
+ end
197
+ end
198
+
199
+ def data_wovnio
200
+ token = @store.settings['project_token']
201
+ current_lang = @headers.lang_code
202
+ default_lang = @store.settings['default_lang']
203
+ url_pattern = @store.settings['url_pattern']
204
+ lang_code_aliases_json = JSON.generate(@store.settings['custom_lang_aliases'])
205
+ lang_param_name = @store.settings['lang_param_name']
206
+ custom_domain_langs = @store.custom_domain_langs.to_html_swapper_hash
207
+
208
+ result = [
209
+ "key=#{token}",
210
+ 'backend=true',
211
+ "currentLang=#{current_lang}",
212
+ "defaultLang=#{default_lang}",
213
+ "urlPattern=#{url_pattern}",
214
+ "langCodeAliases=#{lang_code_aliases_json}",
215
+ "langParamName=#{lang_param_name}",
216
+ "version=WOVN.rb_#{VERSION}"
217
+ ]
218
+ result << "customDomainLangs=#{JSON.generate(custom_domain_langs)}" unless custom_domain_langs.empty?
219
+ result.join('&')
220
+ end
221
+ end
222
+ end
@@ -1,48 +1,48 @@
1
- module Wovnrb
2
- class HtmlReplaceMarker
3
- KEY_PREFIX = '__wovn-backend-ignored-key-'.freeze
4
-
5
- def initialize
6
- @current_key_number = 0
7
- @mapped_values = []
8
- end
9
-
10
- # Add argument's value to mapping information with comment style key
11
- def add_comment_value(value)
12
- key = "<!-- #{generate_key} -->"
13
- @mapped_values << [key, value]
14
-
15
- key
16
- end
17
-
18
- def add_value(value)
19
- key = generate_key
20
- @mapped_values << [key, value]
21
-
22
- key
23
- end
24
-
25
- def revert(marked_html)
26
- i = @mapped_values.size
27
- while i > 0
28
- i -= 1
29
- key, value = @mapped_values[i]
30
- marked_html = marked_html.sub(key, value)
31
- end
32
- marked_html
33
- end
34
-
35
- def keys
36
- @mapped_values.map { |v| v[0] }
37
- end
38
-
39
- private
40
-
41
- def generate_key
42
- next_key = "#{KEY_PREFIX}#{@current_key_number}"
43
- @current_key_number += 1
44
-
45
- next_key
46
- end
47
- end
48
- end
1
+ module Wovnrb
2
+ class HtmlReplaceMarker
3
+ KEY_PREFIX = '__wovn-backend-ignored-key-'.freeze
4
+
5
+ def initialize
6
+ @current_key_number = 0
7
+ @mapped_values = []
8
+ end
9
+
10
+ # Add argument's value to mapping information with comment style key
11
+ def add_comment_value(value)
12
+ key = "<!-- #{generate_key} -->"
13
+ @mapped_values << [key, value]
14
+
15
+ key
16
+ end
17
+
18
+ def add_value(value)
19
+ key = generate_key
20
+ @mapped_values << [key, value]
21
+
22
+ key
23
+ end
24
+
25
+ def revert(marked_html)
26
+ i = @mapped_values.size
27
+ while i > 0
28
+ i -= 1
29
+ key, value = @mapped_values[i]
30
+ marked_html = marked_html.sub(key, value)
31
+ end
32
+ marked_html
33
+ end
34
+
35
+ def keys
36
+ @mapped_values.map { |v| v[0] }
37
+ end
38
+
39
+ private
40
+
41
+ def generate_key
42
+ next_key = "#{KEY_PREFIX}#{@current_key_number}"
43
+ @current_key_number += 1
44
+
45
+ next_key
46
+ end
47
+ end
48
+ end