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.
- checksums.yaml +7 -0
- data/Gemfile +14 -0
- data/README.md +321 -0
- data/lib/termgui.rb +1 -0
- data/src/action.rb +58 -0
- data/src/box.rb +90 -0
- data/src/color.rb +174 -0
- data/src/cursor.rb +69 -0
- data/src/editor/editor_base.rb +152 -0
- data/src/editor/editor_base_handlers.rb +116 -0
- data/src/element.rb +61 -0
- data/src/element_bounds.rb +111 -0
- data/src/element_box.rb +64 -0
- data/src/element_render.rb +102 -0
- data/src/element_style.rb +51 -0
- data/src/emitter.rb +102 -0
- data/src/emitter_state.rb +19 -0
- data/src/enterable.rb +93 -0
- data/src/event.rb +92 -0
- data/src/focus.rb +102 -0
- data/src/geometry.rb +53 -0
- data/src/image.rb +60 -0
- data/src/input.rb +85 -0
- data/src/input_grab.rb +17 -0
- data/src/input_time.rb +97 -0
- data/src/key.rb +114 -0
- data/src/log.rb +24 -0
- data/src/node.rb +117 -0
- data/src/node_attributes.rb +27 -0
- data/src/node_visit.rb +52 -0
- data/src/renderer.rb +119 -0
- data/src/renderer_cursor.rb +18 -0
- data/src/renderer_draw.rb +28 -0
- data/src/renderer_image.rb +31 -0
- data/src/renderer_print.rb +40 -0
- data/src/screen.rb +96 -0
- data/src/screen_element.rb +59 -0
- data/src/screen_input.rb +43 -0
- data/src/screen_renderer.rb +53 -0
- data/src/style.rb +149 -0
- data/src/tco/colouring.rb +248 -0
- data/src/tco/config.rb +57 -0
- data/src/tco/palette.rb +603 -0
- data/src/tco/tco_termgui.rb +30 -0
- data/src/termgui.rb +29 -0
- data/src/util/css.rb +98 -0
- data/src/util/css_query.rb +23 -0
- data/src/util/easing.rb +364 -0
- data/src/util/hash_object.rb +131 -0
- data/src/util/imagemagick.rb +27 -0
- data/src/util/justify.rb +20 -0
- data/src/util/unicode-categories.rb +572 -0
- data/src/util/wrap.rb +102 -0
- data/src/util.rb +110 -0
- data/src/widget/button.rb +33 -0
- data/src/widget/checkbox.rb +47 -0
- data/src/widget/col.rb +30 -0
- data/src/widget/image.rb +106 -0
- data/src/widget/inline.rb +40 -0
- data/src/widget/input_number.rb +73 -0
- data/src/widget/inputbox.rb +85 -0
- data/src/widget/label.rb +33 -0
- data/src/widget/modal.rb +69 -0
- data/src/widget/row.rb +26 -0
- data/src/widget/selectbox.rb +100 -0
- data/src/widget/textarea.rb +54 -0
- data/src/xml/xml.rb +80 -0
- data/test/action_test.rb +34 -0
- data/test/box_test.rb +15 -0
- data/test/css_test.rb +39 -0
- data/test/editor/editor_base_test.rb +201 -0
- data/test/element_bounds_test.rb +77 -0
- data/test/element_box_test.rb +8 -0
- data/test/element_render_test.rb +124 -0
- data/test/element_style_test.rb +85 -0
- data/test/element_test.rb +10 -0
- data/test/emitter_test.rb +108 -0
- data/test/event_test.rb +19 -0
- data/test/focus_test.rb +37 -0
- data/test/geometry_test.rb +12 -0
- data/test/input_test.rb +47 -0
- data/test/key_test.rb +14 -0
- data/test/log_test.rb +21 -0
- data/test/node_test.rb +105 -0
- data/test/performance/performance1.rb +48 -0
- data/test/renderer_test.rb +74 -0
- data/test/renderer_test_rect.rb +4 -0
- data/test/screen_test.rb +58 -0
- data/test/style_test.rb +18 -0
- data/test/termgui_test.rb +10 -0
- data/test/test_all.rb +30 -0
- data/test/util_hash_object_test.rb +93 -0
- data/test/util_test.rb +26 -0
- data/test/widget/checkbox_test.rb +99 -0
- data/test/widget/col_test.rb +87 -0
- data/test/widget/inline_test.rb +40 -0
- data/test/widget/label_test.rb +94 -0
- data/test/widget/row_test.rb +40 -0
- data/test/wrap_test.rb +11 -0
- data/test/xml_test.rb +77 -0
- 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
|
data/src/util/easing.rb
ADDED
@@ -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')
|
data/src/util/justify.rb
ADDED
@@ -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
|