scarpe-components 0.2.2 → 0.4.0

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -1
  3. data/Gemfile.lock +85 -0
  4. data/README.md +2 -2
  5. data/assets/bootstrap-themes/bootstrap-cerulean.css +12229 -0
  6. data/assets/bootstrap-themes/bootstrap-cosmo.css +11810 -0
  7. data/assets/bootstrap-themes/bootstrap-cyborg.css +12210 -0
  8. data/assets/bootstrap-themes/bootstrap-darkly.css +12153 -0
  9. data/assets/bootstrap-themes/bootstrap-flatly.css +12126 -0
  10. data/assets/bootstrap-themes/bootstrap-icons.min.css +5 -0
  11. data/assets/bootstrap-themes/bootstrap-journal.css +12099 -0
  12. data/assets/bootstrap-themes/bootstrap-litera.css +12211 -0
  13. data/assets/bootstrap-themes/bootstrap-lumen.css +12369 -0
  14. data/assets/bootstrap-themes/bootstrap-lux.css +11928 -0
  15. data/assets/bootstrap-themes/bootstrap-materia.css +13184 -0
  16. data/assets/bootstrap-themes/bootstrap-minty.css +12177 -0
  17. data/assets/bootstrap-themes/bootstrap-morph.css +12750 -0
  18. data/assets/bootstrap-themes/bootstrap-pulse.css +11890 -0
  19. data/assets/bootstrap-themes/bootstrap-quartz.css +12622 -0
  20. data/assets/bootstrap-themes/bootstrap-sandstone.css +12201 -0
  21. data/assets/bootstrap-themes/bootstrap-simplex.css +12186 -0
  22. data/assets/bootstrap-themes/bootstrap-sketchy.css +12451 -0
  23. data/assets/bootstrap-themes/bootstrap-slate.css +12492 -0
  24. data/assets/bootstrap-themes/bootstrap-solar.css +12149 -0
  25. data/assets/bootstrap-themes/bootstrap-spacelab.css +12266 -0
  26. data/assets/bootstrap-themes/bootstrap-superhero.css +12216 -0
  27. data/assets/bootstrap-themes/bootstrap-united.css +12077 -0
  28. data/assets/bootstrap-themes/bootstrap-vapor.css +12549 -0
  29. data/assets/bootstrap-themes/bootstrap-yeti.css +12325 -0
  30. data/assets/bootstrap-themes/bootstrap-zephyr.css +12283 -0
  31. data/assets/bootstrap-themes/bootstrap.bundle.min.js +7 -0
  32. data/lib/scarpe/components/asset_server.rb +219 -0
  33. data/lib/scarpe/components/base64.rb +23 -5
  34. data/lib/scarpe/components/calzini/alert.rb +49 -0
  35. data/lib/scarpe/components/calzini/art_drawables.rb +227 -0
  36. data/lib/scarpe/components/calzini/border.rb +38 -0
  37. data/lib/scarpe/components/calzini/button.rb +37 -0
  38. data/lib/scarpe/components/calzini/misc.rb +136 -0
  39. data/lib/scarpe/components/calzini/para.rb +237 -0
  40. data/lib/scarpe/components/calzini/slots.rb +109 -0
  41. data/lib/scarpe/components/calzini.rb +236 -0
  42. data/lib/scarpe/components/errors.rb +24 -0
  43. data/lib/scarpe/components/file_helpers.rb +1 -0
  44. data/lib/scarpe/components/html.rb +134 -0
  45. data/lib/scarpe/components/minitest_export_reporter.rb +83 -0
  46. data/lib/scarpe/components/minitest_import_runnable.rb +98 -0
  47. data/lib/scarpe/components/minitest_result.rb +127 -0
  48. data/lib/scarpe/components/modular_logger.rb +5 -5
  49. data/lib/scarpe/components/print_logger.rb +22 -3
  50. data/lib/scarpe/components/process_helpers.rb +37 -0
  51. data/lib/scarpe/components/promises.rb +14 -14
  52. data/lib/scarpe/components/segmented_file_loader.rb +36 -17
  53. data/lib/scarpe/components/string_helpers.rb +10 -0
  54. data/lib/scarpe/components/tiranti.rb +167 -0
  55. data/lib/scarpe/components/unit_test_helpers.rb +48 -6
  56. data/lib/scarpe/components/version.rb +2 -2
  57. metadata +48 -4
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scarpe::Components::Calzini
4
+ def arc_element(props, &block)
5
+ dc = props["draw_context"] || {}
6
+ rotate = dc["rotate"]
7
+ HTML.render do |h|
8
+ h.div(id: html_id, style: arc_style(props)) do
9
+ h.svg(width: props["width"], height: props["height"]) do
10
+ h.path(d: arc_path(props), transform: "rotate(#{rotate}, #{props["width"] / 2}, #{props["height"] / 2})")
11
+ end
12
+ block.call(h) if block_given?
13
+ end
14
+ end
15
+ end
16
+
17
+ def rect_element(props)
18
+ dc = props["draw_context"] || {}
19
+ rotate = dc["rotate"]
20
+ HTML.render do |h|
21
+ h.div(id: html_id, style: drawable_style(props)) do
22
+ width = props["width"].to_i
23
+ height = props["height"].to_i
24
+ if props["curve"]
25
+ width += 2 * props["curve"].to_i
26
+ height += 2 * props["curve"].to_i
27
+ end
28
+ h.svg(width:, height:) do
29
+ attrs = { x: props["left"], y: props["top"], width: props["width"], height: props["height"], style: rect_svg_style(props) }
30
+ attrs[:rx] = props["curve"] if props["curve"]
31
+
32
+ h.rect(**attrs, transform: "rotate(#{rotate} #{width / 2} #{height / 2})")
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def line_element(props)
39
+ HTML.render do |h|
40
+ h.div(id: html_id, style: line_div_style(props)) do
41
+ h.svg(width: props["x2"], height: props["y2"]) do
42
+ h.line(x1: props["left"], y1: props["top"], x2: props["x2"], y2: props["y2"], style: line_svg_style(props))
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def star_element(props, &block)
49
+ dc = props["draw_context"] || {}
50
+ fill = first_color_of(props["fill"], dc["fill"], "black")
51
+ stroke = first_color_of(props["stroke"], dc["stroke"], "black")
52
+ HTML.render do |h|
53
+ h.div(id: html_id, style: star_style(props)) do
54
+ h.svg(width: props["outer"], height: props["outer"], style: "fill:#{fill}") do
55
+ h.polygon(points: star_points(props), style: "stroke:#{stroke};stroke-width:2")
56
+ end
57
+ block.call(h) if block_given?
58
+ end
59
+ end
60
+ end
61
+
62
+ def oval_element(props, &block)
63
+ dc = props["draw_context"] || {}
64
+ fill = first_color_of(props["fill"], dc["fill"], "black")
65
+ stroke = first_color_of(props["stroke"], dc["stroke"], "black")
66
+ strokewidth = props["strokewidth"] || dc["strokewidth"] || "2"
67
+ radius = props["radius"]
68
+ width = radius * 2
69
+ height = props["height"] || radius * 2 # If there's a height, it's an oval, if not, circle
70
+ center = props["center"] || false
71
+ HTML.render do |h|
72
+ h.div(id: html_id, style: oval_style(props)) do
73
+ h.svg(width: width, height: height, style: "fill:#{fill};") do
74
+ h.ellipse(
75
+ cx: center ? radius : 0,
76
+ cy: center ? height / 2 : 0,
77
+ rx: width ? width / 2 : radius,
78
+ ry: height ? height / 2 : radius,
79
+ style: "stroke:#{stroke};stroke-width:#{strokewidth};",
80
+ )
81
+ end
82
+ block.call(h) if block_given?
83
+ end
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def arc_style(props)
90
+ drawable_style(props).merge({
91
+ left: "#{props["left"]}px",
92
+ top: "#{props["top"]}px",
93
+ width: "#{props["width"]}px",
94
+ height: "#{props["height"]}px",
95
+ })
96
+ end
97
+
98
+ def arc_path(props)
99
+ center_x = props["width"] / 2
100
+ center_y = props["height"] / 2
101
+ radius_x = props["width"] / 2
102
+ radius_y = props["height"] / 2
103
+ start_angle_degrees = radians_to_degrees(props["angle1"]) % 360
104
+ end_angle_degrees = radians_to_degrees(props["angle2"]) % 360
105
+ large_arc_flag = (end_angle_degrees - start_angle_degrees) % 360 > 180 ? 1 : 0
106
+
107
+ "M#{center_x} #{center_y} L#{props["width"]} #{center_y} " \
108
+ "A#{radius_x} #{radius_y} 0 #{large_arc_flag} 0 " \
109
+ "#{center_x + radius_x * Math.cos(degrees_to_radians(end_angle_degrees))} " \
110
+ "#{center_y + radius_y * Math.sin(degrees_to_radians(end_angle_degrees))} Z"
111
+ end
112
+
113
+ def line_div_style(props)
114
+ drawable_style(props).merge({
115
+ left: "#{props["left"]}px",
116
+ top: "#{props["top"]}px",
117
+ })
118
+ end
119
+
120
+ def line_svg_style(props)
121
+ {
122
+
123
+ "stroke": first_color_of(props["stroke"], (props["draw_context"] || {})["stroke"], "black"),
124
+ "stroke-width": "4",
125
+ }.compact
126
+ end
127
+
128
+ def rect_svg_style(props)
129
+ {
130
+ stroke: first_color_of(props["stroke"], (props["draw_context"] || {})["stroke"]),
131
+ #"stroke-width": "1",
132
+ fill: first_color_of(props["fill"], (props["draw_context"] || {})["fill"]),
133
+ }.compact
134
+ end
135
+
136
+ def star_style(props)
137
+ drawable_style(props).merge({
138
+ width: dimensions_length(props["width"]),
139
+ height: dimensions_length(props["height"]),
140
+ }).compact
141
+ end
142
+
143
+ def oval_style(props)
144
+ drawable_style(props).merge({
145
+ width: dimensions_length(props["width"]),
146
+ height: dimensions_length(props["height"]),
147
+ })
148
+ end
149
+
150
+ def star_points(props)
151
+ angle = 2 * Math::PI / props["points"]
152
+ coordinates = []
153
+
154
+ props["points"].times do |i|
155
+ outer_angle = i * angle
156
+ inner_angle = outer_angle + angle / 2
157
+
158
+ coordinates.concat(star_get_coordinates(outer_angle, inner_angle, props))
159
+ end
160
+
161
+ coordinates.join(",")
162
+ end
163
+
164
+ def star_get_coordinates(outer_angle, inner_angle, props)
165
+ outer_x = props["outer"] / 2 + Math.cos(outer_angle) * props["outer"] / 2
166
+ outer_y = props["outer"] / 2 + Math.sin(outer_angle) * props["outer"] / 2
167
+
168
+ inner_x = props["outer"] / 2 + Math.cos(inner_angle) * props["inner"] / 2
169
+ inner_y = props["outer"] / 2 + Math.sin(inner_angle) * props["inner"] / 2
170
+
171
+ [outer_x, outer_y, inner_x, inner_y]
172
+ end
173
+
174
+ def arrow_element(props)
175
+ left = props["left"]
176
+ top = props["top"]
177
+ width = props["width"]
178
+ end_x = left + width
179
+ end_y = top
180
+ stroke_width = width / 2
181
+ dc = props["draw_context"] || {}
182
+ fill = first_color_of(props["fill"], dc["fill"], "black")
183
+ stroke = first_color_of(props["stroke"], dc["stroke"], "black")
184
+ rotate = dc["rotate"]
185
+
186
+ stroke_width = width / 4
187
+
188
+ HTML.render do |h|
189
+ h.div(id: html_id, style: arrow_div_style(props)) do
190
+ h.svg do
191
+ h.defs do
192
+ h.marker(
193
+ id: "head",
194
+ viewBox: "0 0 70 70",
195
+ markerWidth: stroke_width.to_s,
196
+ markerHeight: stroke_width.to_s,
197
+ refX: "5",
198
+ refY: "5",
199
+ orient: "auto-start-reverse",
200
+ ) do
201
+ h.path(d: "M 0 0 L 10 5 L 0 10 z", fill: fill)
202
+ end
203
+ end
204
+
205
+ h.line(
206
+ x2: left.to_s,
207
+ y2: top.to_s,
208
+ x1: end_x.to_s,
209
+ y1: end_y.to_s,
210
+ fill: fill,
211
+ stroke: stroke,
212
+ "stroke-width" => stroke_width.to_s,
213
+ "marker-end" => "url(#head)",
214
+ transform: "rotate(#{rotate}, #{left + width / 2}, #{top})",
215
+ )
216
+ end
217
+ end
218
+ end
219
+ end
220
+ def arrow_div_style(props)
221
+ drawable_style(props).merge({
222
+ position: "absolute",
223
+ left: "#{props["left"]}px",
224
+ top: "#{props["top"]}px",
225
+ })
226
+ end
227
+ end
@@ -0,0 +1,38 @@
1
+ module Scarpe::Components::Calzini
2
+ def border_element(props)
3
+ HTML.render do |h|
4
+ h.div(id: html_id, style: style(props))
5
+ end
6
+ end
7
+
8
+ private
9
+
10
+ def style(props)
11
+ styles = {
12
+ height: :inherit,
13
+ width: :inherit,
14
+ position: :absolute,
15
+ top: 0,
16
+ left: 0,
17
+ 'box-sizing': 'border-box'
18
+ }
19
+
20
+ bc = props["stroke"]
21
+
22
+ border_style_hash = case bc
23
+ when Range
24
+ { "border-image": "linear-gradient(45deg, #{bc.first}, #{bc.last})" }
25
+ when Array
26
+ { "border-color": "rgba(#{bc.join(", ")})" }
27
+ else
28
+ { "border-color": bc }
29
+ end
30
+ styles = styles.merge(
31
+ "border-style": "solid",
32
+ "border-width": "#{props["strokewidth"]}px",
33
+ "border-radius": "#{props["curve"]}px",
34
+ ).merge(border_style_hash)
35
+
36
+ styles.compact
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scarpe::Components::Calzini
4
+ def button_element(props)
5
+ HTML.render do |h|
6
+ button_props = {
7
+ id: html_id,
8
+ onclick: handler_js_code("click"),
9
+ onmouseover: handler_js_code("hover"),
10
+ style: button_style(props),
11
+ class: props["html_class"],
12
+ title: props["tooltip"],
13
+ }.compact
14
+ h.button(**button_props) do
15
+ props["text"]
16
+ end
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def button_style(props)
23
+ styles = drawable_style(props)
24
+
25
+ styles[:"background-color"] = props["color"] if props["color"]
26
+ styles[:"padding-top"] = props["padding_top"] if props["padding_top"]
27
+ styles[:"padding-bottom"] = props["padding_bottom"] if props["padding_bottom"]
28
+ styles[:color] = props["text_color"] if props["text_color"]
29
+
30
+ styles[:"font-size"] = props["font_size"] if props["font_size"]
31
+ styles[:"font-size"] = dimensions_length(text_size(props["size"])) if props["size"]
32
+
33
+ styles[:"font-family"] = props["font"] if props["font"]
34
+
35
+ styles
36
+ end
37
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scarpe::Components::Calzini
4
+ def check_element(props)
5
+ HTML.render do |h|
6
+ h.input type: :checkbox,
7
+ id: html_id,
8
+ onclick: handler_js_code("click"),
9
+ value: props["text"],
10
+ checked: props["checked"],
11
+ style: drawable_style(props)
12
+ end
13
+ end
14
+
15
+ def edit_box_element(props)
16
+ oninput = handler_js_code("change", "this.value")
17
+
18
+ HTML.render do |h|
19
+ h.textarea(id: html_id, oninput: oninput,onmouseover: handler_js_code("hover"), style: edit_box_style(props),title: props["tooltip"]) { props["text"] }
20
+ end
21
+ end
22
+
23
+ def edit_line_element(props)
24
+ oninput = handler_js_code("change", "this.value")
25
+
26
+ HTML.render do |h|
27
+ h.input(id: html_id, oninput: oninput, onmouseover: handler_js_code("hover"), value: props["text"], style: edit_line_style(props),title: props["tooltip"])
28
+ end
29
+ end
30
+
31
+ def image_element(props)
32
+ style = drawable_style(props)
33
+
34
+ if props["click"]
35
+ HTML.render do |h|
36
+ h.a(id: html_id, href: props["click"]) { h.img(id: html_id, src: props["url"], style:) }
37
+ end
38
+ else
39
+ HTML.render do |h|
40
+ h.img(id: html_id, src: props["url"], style:)
41
+ end
42
+ end
43
+ end
44
+
45
+ def list_box_element(props)
46
+ onchange = handler_js_code("change", "this.options[this.selectedIndex].value")
47
+
48
+ # Is this useful at all? Is it overridden below completely?
49
+ option_attrs = { value: nil, selected: false }
50
+
51
+ HTML.render do |h|
52
+ h.select(id: html_id, onchange:, style: list_box_style(props)) do
53
+ (props["items"] || []).each do |item|
54
+ option_attrs = {
55
+ value: item,
56
+ }
57
+ if item == props["choose"]
58
+ option_attrs[:selected] = "true"
59
+ end
60
+ h.option(**option_attrs) do
61
+ item
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def radio_element(props)
69
+ # This is wrong - need to default to the parent slot -- maybe its linkable ID?
70
+ group_name = props["group"] || "no_group"
71
+
72
+ HTML.render do |h|
73
+ h.input(
74
+ type: :radio,
75
+ id: html_id,
76
+ onclick: handler_js_code("click"),
77
+ name: group_name,
78
+ value: props["text"],
79
+ checked: props["checked"],
80
+ style: drawable_style(props),
81
+ )
82
+ end
83
+ end
84
+
85
+ def video_element(props)
86
+ HTML.render do |h|
87
+ h.video(id: html_id, style: drawable_style(props), controls: true) do
88
+ h.source(src: @url, type: props["format"])
89
+ end
90
+ end
91
+ end
92
+
93
+ def progress_element(props)
94
+ HTML.render do |h|
95
+ h.progress(
96
+ id: html_id,
97
+ style: drawable_style(props),
98
+ role: "progressbar",
99
+ "aria-valuenow": props["fraction"],
100
+ "aria-valuemin": 0.0,
101
+ "aria-valuemax": 1.0,
102
+ max: 1,
103
+ value: props["fraction"],
104
+ )
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def edit_box_style(props)
111
+ drawable_style(props).merge({
112
+ height: dimensions_length(props["height"]),
113
+ width: dimensions_length(props["width"]),
114
+ font: props["font"]? parse_font(props) : nil
115
+ }.compact)
116
+ end
117
+
118
+ def edit_line_style(props)
119
+ styles = drawable_style(props)
120
+
121
+ styles[:font] = props["font"]? parse_font(props) : nil
122
+ styles[:width] = dimensions_length(props["width"]) if props["width"]
123
+ styles[:color] = rgb_to_hex(props["stroke"])
124
+
125
+ styles
126
+ end
127
+
128
+ def list_box_style(props)
129
+ styles = drawable_style(props)
130
+
131
+ styles[:height] = dimensions_length(props["height"]) if props["height"]
132
+ styles[:width] = dimensions_length(props["width"]) if props["width"]
133
+
134
+ styles
135
+ end
136
+ end
@@ -0,0 +1,237 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scarpe::Components::Calzini
4
+ def para_element(props, &block)
5
+ # Align requires an extra wrapping div.
6
+
7
+ # Stacking strikethrough with underline requires multiple elements.
8
+ # We handle this by making strikethrough part of the main element,
9
+ # but using an extra wrapping element for underline.
10
+
11
+ tag = props["tag"] || "p"
12
+
13
+ para_styles, extra_styles = para_style(props)
14
+
15
+ HTML.render do |h|
16
+ if extra_styles.empty?
17
+ h.send(tag, id: html_id, style: para_styles, &block)
18
+ else
19
+ h.div(id: html_id, style: extra_styles.merge(width: "100%")) do
20
+ h.send(tag, style: para_styles, &block)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def para_style(props)
29
+
30
+ ds = drawable_style(props)
31
+ s1, s2 = text_specific_styles(props)
32
+ [ds.merge(s1), s2]
33
+ end
34
+
35
+ def text_specific_styles(props)
36
+ # Shoes3 allows align: right on TextDrawables like em(), but it does
37
+ # nothing. We can ignore it or (maybe in future?) warn if we see it.
38
+
39
+ strikethrough = props["strikethrough"]
40
+ strikethrough = nil if strikethrough == "" || strikethrough == "none"
41
+ s1 = {
42
+ "font-variant": props["font_variant"],
43
+ "color": rgb_to_hex(props["stroke"]),
44
+ "background-color": rgb_to_hex(props["fill"]),
45
+ "font-size": para_font_size(props),
46
+ "font-family": props["family"],
47
+ "text-decoration-line": strikethrough ? "line-through" : nil,
48
+ "text-decoration-color": props["strikecolor"] ? rgb_to_hex(props["strikecolor"]) : nil,
49
+ "font-weight": props["font_weight"]? props["font_weight"] : nil,
50
+ :'font-style' => case props["emphasis"]
51
+ when "normal"
52
+ "normal"
53
+ when "oblique"
54
+ "oblique"
55
+ when "italic"
56
+ "italic"
57
+ else
58
+ nil
59
+ end,
60
+ :'letter-spacing' => props["kerning"] ? "#{props["kerning"]}px" : nil,
61
+ :'vertical-align' => props["rise"] ? "#{props["rise"]}px" : nil
62
+ }.compact
63
+
64
+ s2 = {}
65
+ if props["align"] && props["align"] != ""
66
+ s2[:"text-align"] = props["align"]
67
+ end
68
+
69
+ unless [nil, "none"].include?(props["underline"])
70
+ undercolor = rgb_to_hex props["undercolor"]
71
+ s2["text-decoration-color"] = undercolor if undercolor
72
+ end
73
+
74
+ # [nil, "none", "single", "double", "low", "error"]
75
+ case props["underline"]
76
+ when nil, "none"
77
+ # Do nothing
78
+ when "single"
79
+ s2["text-decoration-line"] = "underline"
80
+ when "double"
81
+ s2["text-decoration-line"] = "underline"
82
+ s2["text-decoration-style"] = "double"
83
+ when "error"
84
+ s2["text-decoration-line"] = "underline"
85
+ s2["text-decoration-style"] = "wavy"
86
+ when "low"
87
+ s2["text-decoration-line"] = "underline"
88
+ s2["text-underline-offset"] = "0.3rem"
89
+ else
90
+ # This should normally be unreachable
91
+ raise Shoes::Errors::InvalidAttributeValueError, "Unexpected underline type #{props["underline"].inspect}!"
92
+ end
93
+
94
+ [s1, s2]
95
+ end
96
+
97
+
98
+ def parse_font(props)
99
+
100
+ def contains_number?(str)
101
+ !!(str =~ /\d/)
102
+ end
103
+ def contains_only_numbers?(string)
104
+ /^\d+\z/ =~ string
105
+ end
106
+
107
+
108
+ input = props["font"]
109
+ regex = /\s+(?=(?:[^']*'[^']*')*[^']*$)(?![^']*,[^']*')/
110
+ result = input.split(regex)
111
+
112
+ fs = "normal"
113
+ fv = "normal"
114
+ fw = "normal"
115
+ fss = "medium"
116
+ ff = "Arial"
117
+
118
+ fos = ["italic", "oblique"]
119
+ fov = ["small-caps", "initial", "inherit"]
120
+ fow = ["bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900"]
121
+ foss = ["xx-small", "x-small", "small","large", "x-large", "xx-large", "smaller", "larger"]
122
+
123
+ result.each do |i|
124
+ if fos.include?(i)
125
+ fs = i
126
+ next
127
+ elsif fov.include?(i)
128
+ fv = i
129
+ next
130
+ elsif fow.include?(i)
131
+ fw = i
132
+ next
133
+ elsif foss.include?(i)
134
+ fss = i
135
+ next
136
+ else
137
+ if contains_number?(i)
138
+
139
+ if contains_only_numbers?(i)
140
+ fss = i + "px"
141
+ else
142
+ fss = i
143
+ end
144
+
145
+ elsif i != "normal" && i != "medium" && i.strip != ""
146
+
147
+ if ff == "Arial"
148
+
149
+ ff = i
150
+
151
+ else
152
+
153
+ ff = ff+ i
154
+
155
+ end
156
+ end
157
+ end
158
+
159
+ end
160
+
161
+
162
+ "#{fs} #{fv} #{fw} #{fss} #{ff}"
163
+
164
+ end
165
+
166
+ def para_font_size(props)
167
+ return nil unless props["size"]
168
+
169
+ sz = props["size"].to_s
170
+ font_size = SIZES[sz.to_sym] || sz.to_i
171
+
172
+ dimensions_length(font_size)
173
+ end
174
+
175
+ public
176
+
177
+ # The text element is used to render the equivalent of Shoes cText,
178
+ # which includes em, strong, span, link and so on. We use a
179
+ # "content" tag for it which alternates plaintext with a hash of
180
+ # properties.
181
+ def text_drawable_element(prop_array)
182
+ out = String.new # Need unfrozen string
183
+
184
+ # Each item should be a String or a property Hash
185
+ # :items, :html_id, :tag, :props
186
+ prop_array.each do |item|
187
+ if item.is_a?(String)
188
+ out << item.gsub("\n", "<br/>")
189
+ else
190
+ s, extra = text_drawable_style(item[:props])
191
+ out << HTML.render do |h|
192
+ if extra.empty?
193
+ h.send(
194
+ item[:tag] || "span",
195
+ class: "id_#{item[:html_id]}",
196
+ style: s,
197
+ **text_drawable_attrs(item[:props])
198
+ ) do
199
+ text_drawable_element(item[:items])
200
+ end
201
+ else
202
+ h.span(class: "id_#{item[:html_id]}", style: extra) do
203
+ h.send(
204
+ item[:tag] || "span",
205
+ class: "id_#{item[:html_id]}",
206
+ style: s,
207
+ **text_drawable_attrs(item[:props])
208
+ ) do
209
+ text_drawable_element(item[:items])
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ out
218
+ end
219
+
220
+ private
221
+
222
+ def text_drawable_attrs(props)
223
+ {
224
+ # These properties will normally only be set by link()
225
+ href: props["click"],
226
+ onclick: props["has_block"] ? handler_js_code("click") : nil,
227
+ }.compact
228
+ end
229
+
230
+ def text_drawable_style(props)
231
+ s, extra_s = text_specific_styles(props)
232
+
233
+ # Add hover styles, perhaps with CSS pseudo-class
234
+
235
+ [drawable_style(props).merge(s), extra_s]
236
+ end
237
+ end