termgui 0.0.4

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 (101) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +14 -0
  3. data/README.md +321 -0
  4. data/lib/termgui.rb +1 -0
  5. data/src/action.rb +58 -0
  6. data/src/box.rb +90 -0
  7. data/src/color.rb +174 -0
  8. data/src/cursor.rb +69 -0
  9. data/src/editor/editor_base.rb +152 -0
  10. data/src/editor/editor_base_handlers.rb +116 -0
  11. data/src/element.rb +61 -0
  12. data/src/element_bounds.rb +111 -0
  13. data/src/element_box.rb +64 -0
  14. data/src/element_render.rb +102 -0
  15. data/src/element_style.rb +51 -0
  16. data/src/emitter.rb +102 -0
  17. data/src/emitter_state.rb +19 -0
  18. data/src/enterable.rb +93 -0
  19. data/src/event.rb +92 -0
  20. data/src/focus.rb +102 -0
  21. data/src/geometry.rb +53 -0
  22. data/src/image.rb +60 -0
  23. data/src/input.rb +85 -0
  24. data/src/input_grab.rb +17 -0
  25. data/src/input_time.rb +97 -0
  26. data/src/key.rb +114 -0
  27. data/src/log.rb +24 -0
  28. data/src/node.rb +117 -0
  29. data/src/node_attributes.rb +27 -0
  30. data/src/node_visit.rb +52 -0
  31. data/src/renderer.rb +119 -0
  32. data/src/renderer_cursor.rb +18 -0
  33. data/src/renderer_draw.rb +28 -0
  34. data/src/renderer_image.rb +31 -0
  35. data/src/renderer_print.rb +40 -0
  36. data/src/screen.rb +96 -0
  37. data/src/screen_element.rb +59 -0
  38. data/src/screen_input.rb +43 -0
  39. data/src/screen_renderer.rb +53 -0
  40. data/src/style.rb +149 -0
  41. data/src/tco/colouring.rb +248 -0
  42. data/src/tco/config.rb +57 -0
  43. data/src/tco/palette.rb +603 -0
  44. data/src/tco/tco_termgui.rb +30 -0
  45. data/src/termgui.rb +29 -0
  46. data/src/util/css.rb +98 -0
  47. data/src/util/css_query.rb +23 -0
  48. data/src/util/easing.rb +364 -0
  49. data/src/util/hash_object.rb +131 -0
  50. data/src/util/imagemagick.rb +27 -0
  51. data/src/util/justify.rb +20 -0
  52. data/src/util/unicode-categories.rb +572 -0
  53. data/src/util/wrap.rb +102 -0
  54. data/src/util.rb +110 -0
  55. data/src/widget/button.rb +33 -0
  56. data/src/widget/checkbox.rb +47 -0
  57. data/src/widget/col.rb +30 -0
  58. data/src/widget/image.rb +106 -0
  59. data/src/widget/inline.rb +40 -0
  60. data/src/widget/input_number.rb +73 -0
  61. data/src/widget/inputbox.rb +85 -0
  62. data/src/widget/label.rb +33 -0
  63. data/src/widget/modal.rb +69 -0
  64. data/src/widget/row.rb +26 -0
  65. data/src/widget/selectbox.rb +100 -0
  66. data/src/widget/textarea.rb +54 -0
  67. data/src/xml/xml.rb +80 -0
  68. data/test/action_test.rb +34 -0
  69. data/test/box_test.rb +15 -0
  70. data/test/css_test.rb +39 -0
  71. data/test/editor/editor_base_test.rb +201 -0
  72. data/test/element_bounds_test.rb +77 -0
  73. data/test/element_box_test.rb +8 -0
  74. data/test/element_render_test.rb +124 -0
  75. data/test/element_style_test.rb +85 -0
  76. data/test/element_test.rb +10 -0
  77. data/test/emitter_test.rb +108 -0
  78. data/test/event_test.rb +19 -0
  79. data/test/focus_test.rb +37 -0
  80. data/test/geometry_test.rb +12 -0
  81. data/test/input_test.rb +47 -0
  82. data/test/key_test.rb +14 -0
  83. data/test/log_test.rb +21 -0
  84. data/test/node_test.rb +105 -0
  85. data/test/performance/performance1.rb +48 -0
  86. data/test/renderer_test.rb +74 -0
  87. data/test/renderer_test_rect.rb +4 -0
  88. data/test/screen_test.rb +58 -0
  89. data/test/style_test.rb +18 -0
  90. data/test/termgui_test.rb +10 -0
  91. data/test/test_all.rb +30 -0
  92. data/test/util_hash_object_test.rb +93 -0
  93. data/test/util_test.rb +26 -0
  94. data/test/widget/checkbox_test.rb +99 -0
  95. data/test/widget/col_test.rb +87 -0
  96. data/test/widget/inline_test.rb +40 -0
  97. data/test/widget/label_test.rb +94 -0
  98. data/test/widget/row_test.rb +40 -0
  99. data/test/wrap_test.rb +11 -0
  100. data/test/xml_test.rb +77 -0
  101. metadata +148 -0
data/src/util/css.rb ADDED
@@ -0,0 +1,98 @@
1
+ # css like language parser
2
+ # TODO: move this to its own project
3
+
4
+ module TermGui
5
+ # given a string like `foo {bg: red; padding-top: 3} .bar .primary {}`
6
+ # Rules:
7
+ # * values cannot contain any of the following chars: `:;{}`
8
+ # * No inmediate children ('>') supported. Example, `a b` is supported. `a>b` is not supported.
9
+ class CSSParser
10
+ def parse(code)
11
+ rules = parse_rules code
12
+ rules = parse_rules_properties rules
13
+ rules = parse_rules_selectors rules
14
+ rules
15
+ end
16
+
17
+ def parse_rules(code)
18
+ code.split('}').map do |rule|
19
+ a = rule.split('{')
20
+ throw "Syntax error in rule '#{rule}'" if a.length != 2
21
+ {
22
+ selector: a[0].strip,
23
+ body: a[1].strip
24
+ }
25
+ end
26
+ end
27
+
28
+ # given rules are the output of `parse_rules`. It will return a similar object but for each rule, instead `body` string have name-value `properies`
29
+ def parse_rules_properties(rules)
30
+ rules.map do |rule|
31
+ properties = rule[:body].split(';').map(&:strip)
32
+ properties = properties.map do |property|
33
+ a = property.split(':').map(&:strip)
34
+ throw "Syntax error in property '#{property}', rule: '#{rule}'" if a.length != 2
35
+ {
36
+ name: a[0],
37
+ value: a[1]
38
+ }
39
+ end
40
+ {
41
+ selector: rule[:selector],
42
+ properties: properties
43
+ }
44
+ end
45
+ end
46
+
47
+ # given rules are the output of `parse_rules_properties`, it returns a similar object but for each rule, instead `selector` string have
48
+ # a parsed selector object: `Array<Array<{name: string, operator: nil|'>'|' '}>>`
49
+ def parse_rules_selectors(rules)
50
+ rules.map do |rule|
51
+ selectors = rule[:selector].split(',').map(&:strip)
52
+ selectors = selectors.map do |s|
53
+ parse_selector s
54
+ end
55
+ {
56
+ selectors: selectors,
57
+ properies: rule[:properties]
58
+ }
59
+ end
60
+ end
61
+
62
+ # independent method to parse string selectors like 'a>b c' to `Array<{name: string, operator: nil|'>'|' '}>`
63
+ def parse_selector(s)
64
+ a = s.split(/([\s>])/)
65
+ i = 0
66
+ results = []
67
+ while i < a.length
68
+ results.push(name: a[i], operator: a[i + 1])
69
+ i += 2
70
+ end
71
+ results = results.reject { |result| result[:name].strip == '' }
72
+ results
73
+ end
74
+ end
75
+ end
76
+
77
+ CSSParser = TermGui::CSSParser
78
+
79
+ # p = CSSParser.new
80
+ # # p.parse('foo {bg: red; padding-top: 3} .bar .primary {border: double black}; .sidebar .container { padding: 1}')
81
+ # s = 'a> b c>d f'
82
+ # p CSSParser.new.parse_selector 'a> b c>d f'
83
+ # a = s.split(/([\s>])/) # .select{|s|s.strip!=''}
84
+ # p a
85
+ # i = 0
86
+ # results = []
87
+ # while i < a.length
88
+ # results.push(name: a[i], operator: a[i + 1])
89
+ # i += 2
90
+ # end
91
+ # results = results.reject { |result| result[:name].strip == '' }
92
+ # p results
93
+
94
+ # parser = CSSParser.new
95
+ # expected = '[{:selector=>"foo", :properties=>[{:name=>"bg", :value=>"red"}, {:name=>"padding-top", :value=>"3"}]}, {:selector=>".bar .primary", :properties=>[{:name=>"border", :value=>"double black"}]}, {:selector=>"; .sidebar .container", :properties=>[{:name=>"padding", :value=>"1"}]}]'
96
+ # rules = parser.parse_rules_properties(parser.parse_rules('foo {bg: red; padding-top: 3} .bar .primary {border: double black}; .sidebar .container { padding: 1}'))
97
+ # actual = parser.parse_rules_selectors(rules)
98
+ # p actual
@@ -0,0 +1,23 @@
1
+ # will select descendants from given `root` Node that matches given string selector (like `a>.b c`).
2
+ # The semantic mapping between ' ' and '>' "operators" and Node attributes can be overriden by subclassing
3
+ module NodeSelector
4
+ # def initialize
5
+ # @parser = CSSParser.new
6
+ # end
7
+ def css(root, selector)
8
+ @parser |= CSSParser.new
9
+ parsed = @parser.parse_selector(selector)
10
+ results = []
11
+ css_impl([root], parsed, results)
12
+ end
13
+
14
+ protected
15
+
16
+ # actual recursive css() implementation. each recursion will consume the first parsed passing the rest to the next call.
17
+ # roots will be also updated accordingly to current matches.
18
+ # The recursion condition is meet when parsed.length==1 (the last match step) which will return total matched elements
19
+ # flatten storing it on given imput result argument.
20
+ def css_impl(_roots, _parsed)
21
+ throw 'TODO'
22
+ end
23
+ end
@@ -0,0 +1,364 @@
1
+ # module TermGui
2
+ # module Easing
3
+ # def self.animate(
4
+ # duration: 1000,
5
+ # draw: nil, # (n: number) => void
6
+ # timing: nil, # (n: number, c?: number, d?: number, x?: number, y?: number) => number
7
+ # lapse: 0.1
8
+ # )
9
+ # end
10
+ # end
11
+ # end
12
+
13
+ # # import { isObject } from 'misc-utils-of-mine-generic'
14
+
15
+ # # type Timing = (n: number, c?: number, d?: number, x?: number, y?: number) => number
16
+ # # type TimingObject = { fn: (duration: number) => Timing }
17
+ # # export function animate({ duration, draw, timing, lapse }: {
18
+ # # duration: number
19
+ # # draw: (n: number) => void
20
+ # # timing: Timing | TimingObject
21
+ # # lapse?: number
22
+ # # }) {
23
+ # # if (isObject(timing)) {
24
+ # # timing = (timing as any).fn(duration)
25
+ # # }
26
+ # # let start = Date.now()
27
+ # # let progress = 0
28
+ # # requestAnimationFrame(function anim(time) {
29
+ # # let timeFraction = (time - start) / duration
30
+ # # if (timeFraction > 1) timeFraction = 1
31
+ # # progress = (timing as Timing)(timeFraction, time - start, duration)
32
+ # # draw(progress)
33
+ # # if (timeFraction < 1) {
34
+ # # requestAnimationFrame(anim, lapse)
35
+ # # }
36
+ # # })
37
+ # # }
38
+
39
+ # # function requestAnimationFrame(f: (...args: any[]) => any, lapse = 0) {
40
+ # # setTimeout(() => f(Date.now()), lapse)
41
+ # # }
42
+
43
+ # # export namespace easing {
44
+ # # function makeEaseOut(timing: Timing) {
45
+ # # return function(timeFraction: any) {
46
+ # # return 1 - timing(1 - timeFraction)
47
+ # # }
48
+ # # }
49
+
50
+ # # function bounceFn(timeFraction: number) {
51
+ # # for (let a = 0, b = 1, result; 1; a += b, b /= 2) {
52
+ # # if (timeFraction >= (7 - 4 * a) / 11) {
53
+ # # return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)
54
+ # # }
55
+ # # }
56
+ # # }
57
+
58
+ # # /** Imagine we are dropping a ball. It falls down, then bounces back a few times and stops. The bounce function does the same, but in the reverse order: “bouncing” starts immediately. */
59
+ # # export const bounceEasyOut: () => Timing = () => makeEaseOut(bounceFn as any)
60
+
61
+ # # /** parabolic curve */
62
+ # # export function quad(timeFraction: number) {
63
+ # # return Math.pow(timeFraction, 2)
64
+ # # }
65
+ # # // export const swing = t => 0.5 - Math.cos(t * Math.PI) / 2
66
+
67
+ # # /**
68
+ # # * “bow shooting”. First we “pull the bowstring”, and then “shoot”.
69
+ # # * @param x “elasticity coefficient”. The distance of “bowstring pulling” is defined by it. Default value 1.5.
70
+ # # */
71
+ # # export const back = (x = 1.5) => (timeFraction: number) => Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x)
72
+ # # /**
73
+ # # * @param x “initial range”
74
+ # # */
75
+ # # export const elastic = (x = 1.5) => (timeFraction: number) =>
76
+ # # Math.pow(2, 10 * (timeFraction - 1)) * Math.cos(((20 * Math.PI * x) / 3) * timeFraction)
77
+
78
+ # # function makeEaseInOut(timing: Timing) {
79
+ # # return function(timeFraction: number) {
80
+ # # if (timeFraction < 0.5) return timing(2 * timeFraction) / 2
81
+ # # else return (2 - timing(2 * (1 - timeFraction))) / 2
82
+ # # }
83
+ # # }
84
+
85
+ # # export const bounceEaseInOut: () => Timing = () => makeEaseInOut(bounceFn as any)
86
+
87
+ # # let c = 1,
88
+ # # b = 0
89
+ # # export const easeInQuad: () => Timing = () =>
90
+ # # ({ fn: (d: number) => (x: number, t: number) => c * (t /= d) * t + b } as any)
91
+
92
+ # # export const easeOutQuad: () => Timing = () =>
93
+ # # ({ fn: (d: number) => (x: number, t: number) => -c * (t /= d) * (t - 2) + b } as any)
94
+ # # export const easeInOutQuad: () => Timing = () =>
95
+ # # ({
96
+ # # fn: (d: number) => (x: number, t: number) => {
97
+ # # if ((t /= d / 2) < 1) return (c / 2) * t * t + b
98
+ # # return (-c / 2) * (--t * (t - 2) - 1) + b
99
+ # # }
100
+ # # } as any)
101
+ # # export const easeInElastic: () => Timing = () =>
102
+ # # ({
103
+ # # fn: (d: number) => (x: number, t: number) => {
104
+ # # let s = 1.70158
105
+ # # let p = 0
106
+ # # let a = c
107
+ # # if (t == 0) return b
108
+ # # if ((t /= d) == 1) return b + c
109
+ # # if (!p) p = d * 0.3
110
+ # # if (a < Math.abs(c)) {
111
+ # # a = c
112
+ # # let s = p / 4
113
+ # # } else {
114
+ # # s = (p / (2 * Math.PI)) * Math.asin(c / a)
115
+ # # }
116
+ # # return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin(((t * d - s) * (2 * Math.PI)) / p)) + b
117
+ # # }
118
+ # # } as any)
119
+
120
+ # # export const easeOutBounce: () => TimingObject = () =>
121
+ # # ({
122
+ # # fn: (d: number) => (x: number, t: number) => {
123
+ # # if ((t /= d) < 1 / 2.75) {
124
+ # # return c * (7.5625 * t * t) + b
125
+ # # } else if (t < 2 / 2.75) {
126
+ # # return c * (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) + b
127
+ # # } else if (t < 2.5 / 2.75) {
128
+ # # return c * (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) + b
129
+ # # } else {
130
+ # # return c * (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) + b
131
+ # # }
132
+ # # }
133
+ # # } as any)
134
+
135
+ # # export const easeInCubic: () => Timing = () =>
136
+ # # ({
137
+ # # fn: (d: number) => (x: number, t: number) => {
138
+ # # return c * (t /= d) * t * t + b
139
+ # # }
140
+ # # } as any)
141
+
142
+ # # export const easeOutCubic: () => Timing = () =>
143
+ # # ({
144
+ # # fn: (d: number) => (x: number, t: number) => {
145
+ # # return c * ((t = t / d - 1) * t * t + 1) + b
146
+ # # }
147
+ # # } as any)
148
+
149
+ # # export const easeInOutCubic: () => Timing = () =>
150
+ # # ({
151
+ # # fn: (d: number) => (x: number, t: number) => {
152
+ # # if ((t /= d / 2) < 1) return (c / 2) * t * t * t + b
153
+ # # return (c / 2) * ((t -= 2) * t * t + 2) + b
154
+ # # }
155
+ # # } as any)
156
+
157
+ # # export const easeInQuart: Timing = () =>
158
+ # # ({
159
+ # # fn: (d: number) => (x: number, t: number) => {
160
+ # # return c * (t /= d) * t * t * t + b
161
+ # # }
162
+ # # } as any)
163
+
164
+ # # export const easeInOutQuart: Timing = () =>
165
+ # # ({
166
+ # # fn: (d: number) => (x: number, t: number) => {
167
+ # # if ((t /= d / 2) < 1) return (c / 2) * t * t * t * t + b
168
+ # # return (-c / 2) * ((t -= 2) * t * t * t - 2) + b
169
+ # # }
170
+ # # } as any)
171
+
172
+ # # export const easeInQuint: Timing = () =>
173
+ # # ({
174
+ # # fn: (d: number) => (x: number, t: number) => {
175
+ # # return c * (t /= d) * t * t * t * t + b
176
+ # # }
177
+ # # } as any)
178
+
179
+ # # export const easeOutQuint: () => Timing = () =>
180
+ # # ({
181
+ # # fn: (d: number) => (x: number, t: number) => {
182
+ # # return c * ((t = t / d - 1) * t * t * t * t + 1) + b
183
+ # # }
184
+ # # } as any)
185
+
186
+ # # export const easeInExpo: () => Timing = () =>
187
+ # # ({
188
+ # # fn: (d: number) => (x: number, t: number) => {
189
+ # # return t == 0 ? b : c * Math.pow(2, 10 * (t / d - 1)) + b
190
+ # # }
191
+ # # } as any)
192
+
193
+ # # export const easeInOutQuint: () => Timing = () =>
194
+ # # ({
195
+ # # fn: (d: number) => (x: number, t: number) => {
196
+ # # if ((t /= d / 2) < 1) return (c / 2) * t * t * t * t * t + b
197
+ # # return (c / 2) * ((t -= 2) * t * t * t * t + 2) + b
198
+ # # }
199
+ # # } as any)
200
+
201
+ # # export const easeInSine: () => Timing = () =>
202
+ # # ({
203
+ # # fn: (d: number) => (x: number, t: number) => {
204
+ # # return -c * Math.cos((t / d) * (Math.PI / 2)) + c + b
205
+ # # }
206
+ # # } as any)
207
+ # # export const easeInOutElastic: () => Timing = () =>
208
+ # # ({
209
+ # # fn: (d: number) => (x: number, t: number) => {
210
+ # # let s = 1.70158
211
+ # # let p = 0
212
+ # # let a = c
213
+ # # if (t == 0) return b
214
+ # # if ((t /= d / 2) == 2) return b + c
215
+ # # if (!p) p = d * (0.3 * 1.5)
216
+ # # if (a < Math.abs(c)) {
217
+ # # a = c
218
+ # # let s = p / 4
219
+ # # } else {
220
+ # # s = (p / (2 * Math.PI)) * Math.asin(c / a)
221
+ # # }
222
+ # # if (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin(((t * d - s) * (2 * Math.PI)) / p)) + b
223
+ # # return a * Math.pow(2, -10 * (t -= 1)) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) * 0.5 + c + b
224
+ # # }
225
+ # # } as any)
226
+
227
+ # # export const easeOutElastic: () => Timing = () =>
228
+ # # ({
229
+ # # fn: (d: number) => (x: number, t: number) => {
230
+ # # let s = 1.70158
231
+ # # let p = 0
232
+ # # let a = c
233
+ # # if (t == 0) return b
234
+ # # if ((t /= d) == 1) return b + c
235
+ # # if (!p) p = d * 0.3
236
+ # # if (a < Math.abs(c)) {
237
+ # # a = c
238
+ # # let s = p / 4
239
+ # # } else {
240
+ # # s = (p / (2 * Math.PI)) * Math.asin(c / a)
241
+ # # }
242
+ # # return a * Math.pow(2, -10 * t) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) + c + b
243
+ # # }
244
+ # # } as any)
245
+ # # export const easeInOutExpo: () => Timing = () =>
246
+ # # ({
247
+ # # fn: (d: number) => (x: number, t: number) => {
248
+ # # if (t == 0) return b
249
+ # # if (t == d) return b + c
250
+ # # if ((t /= d / 2) < 1) return (c / 2) * Math.pow(2, 10 * (t - 1)) + b
251
+ # # return (c / 2) * (-Math.pow(2, -10 * --t) + 2) + b
252
+ # # }
253
+ # # } as any)
254
+
255
+ # # export const easeInOutBack: () => Timing = () =>
256
+ # # ({
257
+ # # fn: (d: number) => (x: number, t: number) => {
258
+ # # let s = 1.70158
259
+ # # if ((t /= d / 2) < 1) return (c / 2) * (t * t * (((s *= 1.525) + 1) * t - s)) + b
260
+ # # return (c / 2) * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b
261
+ # # }
262
+ # # } as any)
263
+
264
+ # # export const easeOutBack: () => Timing = () =>
265
+ # # ({
266
+ # # fn: (d: number) => (x: number, t: number, d: number) => {
267
+ # # let s = 1.70158
268
+ # # return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b
269
+ # # }
270
+ # # } as any)
271
+
272
+ # # export const easeInBounce: () => TimingObject = () =>
273
+ # # ({
274
+ # # fn: (d: number) => (x: number, t: number, d: number) => {
275
+ # # return c - easeOutBounce().fn(d)(x, d - t, 0, c, d) + b
276
+ # # }
277
+ # # } as any)
278
+
279
+ # # export const easeInOutBounce: () => Timing = () =>
280
+ # # ({
281
+ # # fn: (d: number) => (x: number, t: number, d: number) => {
282
+ # # if (t < d / 2) return easeInBounce().fn(d)(x, t * 2, 0, c, d) * 0.5 + b
283
+ # # return easeOutBounce().fn(d)(x, t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b
284
+ # # }
285
+ # # } as any)
286
+ # # }
287
+
288
+ # # // animate({
289
+ # # // duration: 2000,
290
+ # # // timing: easeInQuad(),
291
+ # # // lapse: 20,
292
+ # # // draw(progress) {
293
+ # # // console.log(progress * 100 + '%');
294
+ # # // }
295
+ # # // });
296
+
297
+ # # // TODO:
298
+ # # // easeInOutSine: function (x, t, b, c, d) {
299
+ # # // return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
300
+ # # // },
301
+ # # // easeOutExpo: function (x, t, b, c, d) {
302
+ # # // return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
303
+ # # // },
304
+ # # // easeInCirc: function (x, t, b, c, d) {
305
+ # # // return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
306
+ # # // },
307
+ # # // easeOutCirc: function (x, t, b, c, d) {
308
+ # # // return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
309
+ # # // },
310
+ # # // easeInOutCirc: function (x, t, b, c, d) {
311
+ # # // if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
312
+ # # // return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
313
+ # # // },
314
+ # # // easeInBack: function (x, t, b, c, d, s) {
315
+ # # // if (s == undefined) s = 1.70158;
316
+ # # // return c*(t/=d)*t*((s+1)*t - s) + b;
317
+ # # // },
318
+ # # // easeInBounce: function (x, t, b, c, d) {
319
+ # # // return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
320
+ # # // },
321
+ # # // easeInOutBounce: function (x, t, b, c, d) {
322
+ # # // if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
323
+ # # // return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
324
+ # # // }
325
+
326
+ # # // animate({
327
+ # # // duration: 1000,
328
+ # # // timing(timeFraction) {
329
+ # # // return timeFraction;
330
+ # # // },
331
+ # # // draw(progress) {
332
+ # # // console.log(progress * 100 + '%');
333
+
334
+ # # // }
335
+ # # // });
336
+
337
+ # # // setImmediate((t)=>{
338
+ # # // console.log(t);
339
+
340
+ # # // })
341
+
342
+ # # // function requestAnimationFrame(f){
343
+ # # // setImmediate(()=>f(Date.now()))
344
+ # # // }
345
+
346
+ # # // function animate({duration, draw, timing}) {
347
+
348
+ # # // let start = performance.now();
349
+
350
+ # # // requestAnimationFrame(function animate(time) {
351
+ # # // let timeFraction = (time - start) / duration;
352
+ # # // if (timeFraction > 1) timeFraction = 1;
353
+
354
+ # # // let progress = timing(timeFraction)
355
+
356
+ # # // draw(progress);
357
+
358
+ # # // if (timeFraction < 1) {
359
+ # # // requestAnimationFrame(animate);
360
+ # # // }
361
+
362
+ # # // });
363
+ # # // }
364
+ # # // The code for it:
@@ -0,0 +1,131 @@
1
+ require_relative '../util'
2
+
3
+ # Adds hash compatibility using instance_variable
4
+ module HashObject
5
+ # Assigns this instance's properties from given Hash or HashObject instance.
6
+ def assign(hash)
7
+ object_assign(self, hash)
8
+ end
9
+
10
+ # def clone
11
+ # new_from_hash(self)
12
+ # end
13
+
14
+ # alias merge assign
15
+
16
+ # creates a new empty instance and assign values in given hash or instance.
17
+ # This could be class method, but we want to be available to user classes.
18
+ # Warning: since `new` is called variables will be initialized even though not present in given hash.
19
+ def new_from_hash(hash)
20
+ if hash == nil
21
+ self.class.new.assign(self)
22
+ else
23
+ hash_obj = hash
24
+ if hash.instance_of?(Hash)
25
+ hash_obj = self.class.new
26
+ merge_hash_into_object(hash, hash_obj)
27
+ end
28
+ instance = self.class.new
29
+ object_assign(instance, hash_obj)
30
+ end
31
+ end
32
+
33
+ def get(name)
34
+ get_instance_variable self, name
35
+ end
36
+
37
+ def to_s
38
+ to_hash.to_s
39
+ end
40
+
41
+ def to_hash
42
+ object_variables_to_hash self
43
+ end
44
+ end
45
+
46
+ def object_variables_to_hash(obj)
47
+ return obj if obj.is_a?(Hash)
48
+
49
+ hash = {}
50
+ obj.instance_variables.each do |name|
51
+ s = name.to_s
52
+ key = s.slice(1, s.length).to_sym
53
+ hash[key] = obj.instance_variable_get(name)
54
+ end
55
+ hash
56
+ end
57
+
58
+ def merge_hash_into_object(hash, obj)
59
+ hash.keys.each do |key|
60
+ name = variable_name key
61
+ if obj.instance_variable_defined? name
62
+ value = hash[key]
63
+ obj.instance_variable_set(name, value) unless value == nil
64
+ end
65
+ end
66
+ obj
67
+ end
68
+
69
+ # assign properties from `src` to `dest`. by default it ignores nil properties.
70
+ # TODO: option to recurse on object descendants
71
+ def object_assign(dest, src, ignore_nil = true)
72
+ unless src.nil?
73
+ dest.instance_variables.each do |name|
74
+ val = nil
75
+ if src.instance_of? Hash
76
+ key = variable_key(name)
77
+ val = src[key]
78
+ else
79
+ val = src.instance_variable_get(name)
80
+ end
81
+ dest.instance_variable_set name, val unless ignore_nil && val == nil
82
+ end
83
+ end
84
+ dest
85
+ end
86
+
87
+ # returns true if given objects have same keys and values (using != for comparisson)
88
+ def object_equal(obj1, obj2)
89
+ vars1 = obj1.instance_variables
90
+ vars2 = obj2.instance_variables
91
+ return false unless vars1.length == vars2.length
92
+ # if they don't have exactly the same instance_variables names then return false
93
+ return false unless vars1.map(&:to_s).sort.to_s == vars2.map(&:to_s).sort.to_s
94
+
95
+ equal = true
96
+ some(vars1, proc { |v|
97
+ val1 = obj1.instance_variable_get(v)
98
+ val2 = obj2.instance_variable_get(v)
99
+ if val1 != val2
100
+ equal = false
101
+ true
102
+ else
103
+ false
104
+ end
105
+ })
106
+ equal
107
+ end
108
+
109
+ # uses obj.instance_variable_get but supports the variable name in these formats "foo", :foo, :@foo and "@foo"
110
+ def get_instance_variable(obj, name)
111
+ obj.instance_variable_get(variable_name(name))
112
+ end
113
+
114
+ # uses obj.instance_variable_set but supports the variable name in these formats "foo", :foo, :@foo and "@foo"
115
+ def set_instance_variable(obj, name)
116
+ obj.instance_variable_set(variable_name(name))
117
+ end
118
+
119
+ # given a name in any of these formats "foo", :foo, :@foo and "@foo" it returns a valid instance_variable name (symbol), i.e. :@foo
120
+ def variable_name(name)
121
+ s = name.to_s
122
+ s = "@#{s}" unless s.start_with? '@'
123
+ s.to_sym
124
+ end
125
+
126
+ # does the opossite as variable_name : given a variable name like "a", "@a", :@a, :a it will return a valid symbol hash key, like :a
127
+ def variable_key(name)
128
+ s = name.to_s
129
+ s = s.slice(1) if s.start_with? '@'
130
+ s.to_sym
131
+ end
@@ -0,0 +1,27 @@
1
+ def command_error(c)
2
+ `#{c}`
3
+ false
4
+ rescue StandardError
5
+ true
6
+ end
7
+
8
+ def image_magick_available
9
+ !command_error('convert --version') && !command_error('identify --version')
10
+ end
11
+
12
+ require 'fileutils'
13
+ def app_folder(name = 'termgui')
14
+ folder = File.join ENV['HOME'], ".#{name}"
15
+ FileUtils.mkdir_p folder unless File.directory?(folder)
16
+ folder
17
+ end
18
+
19
+ def convert(input, format = 'png')
20
+ throw 'Refusing to convert : imagemagick not available' unless image_magick_available
21
+ output = File.join(app_folder, File.basename(input, '.*') + Time.now.to_i.to_s + '.' + format)
22
+ throw 'Refusing to convert : input file do not exists or output file exists' if !File.exist?(input) || File.exist?(output)
23
+ `convert "#{input}" "#{output}"`
24
+ output
25
+ end
26
+
27
+ # p convert('probes/assets/brazil.png')
@@ -0,0 +1,20 @@
1
+ # def justify(string, len = 80)
2
+ # if string.length < len
3
+ # string
4
+ # else
5
+ # words = string.gsub("\n", ' ').scan(/[\w.-]+/)
6
+ # actual_len = 0
7
+ # output = []
8
+ # words.each do |w|
9
+ # output.push w
10
+ # actual_len += w.length
11
+ # if actual_len >= len
12
+ # output.push "\n"
13
+ # actual_len = 0
14
+ # else
15
+ # output .push ' '
16
+ # end
17
+ # end
18
+ # output.join('')
19
+ # end
20
+ # end