shoes-manual 4.0.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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +69 -0
  6. data/Rakefile +2 -0
  7. data/lib/shoes/manual.rb +45 -0
  8. data/lib/shoes/manual/app.rb +512 -0
  9. data/lib/shoes/manual/version.rb +5 -0
  10. data/shoes-manual.gemspec +27 -0
  11. data/static/PKGBUILD +47 -0
  12. data/static/Shoes.icns +0 -0
  13. data/static/avatar.png +0 -0
  14. data/static/code_highlighter.js +188 -0
  15. data/static/code_highlighter_ruby.js +26 -0
  16. data/static/downloading.png +0 -0
  17. data/static/icon-debug.png +0 -0
  18. data/static/icon-error.png +0 -0
  19. data/static/icon-info.png +0 -0
  20. data/static/icon-warn.png +0 -0
  21. data/static/listbox_button1.png +0 -0
  22. data/static/listbox_button2.png +0 -0
  23. data/static/man-app.png +0 -0
  24. data/static/man-builds.png +0 -0
  25. data/static/man-builds1.png +0 -0
  26. data/static/man-editor-notepad.png +0 -0
  27. data/static/man-editor-osx.png +0 -0
  28. data/static/man-ele-background.png +0 -0
  29. data/static/man-ele-border.png +0 -0
  30. data/static/man-ele-button.png +0 -0
  31. data/static/man-ele-check.png +0 -0
  32. data/static/man-ele-editbox.png +0 -0
  33. data/static/man-ele-editline.png +0 -0
  34. data/static/man-ele-image.png +0 -0
  35. data/static/man-ele-listbox.png +0 -0
  36. data/static/man-ele-progress.png +0 -0
  37. data/static/man-ele-radio.png +0 -0
  38. data/static/man-ele-shape.png +0 -0
  39. data/static/man-ele-textblock.png +0 -0
  40. data/static/man-ele-video.png +0 -0
  41. data/static/man-intro-dmg.png +0 -0
  42. data/static/man-intro-exe.png +0 -0
  43. data/static/man-look-tiger.png +0 -0
  44. data/static/man-look-ubuntu.png +0 -0
  45. data/static/man-look-vista.png +0 -0
  46. data/static/man-run-osx.png +0 -0
  47. data/static/man-run-vista.png +0 -0
  48. data/static/man-run-xp.png +0 -0
  49. data/static/man-shot1.png +0 -0
  50. data/static/manual-en.txt +3531 -0
  51. data/static/manual-ja.txt +2829 -0
  52. data/static/manual.css +184 -0
  53. data/static/menu-corner1.png +0 -0
  54. data/static/menu-corner2.png +0 -0
  55. data/static/menu-gray.png +0 -0
  56. data/static/menu-left.png +0 -0
  57. data/static/menu-right.png +0 -0
  58. data/static/menu-top.png +0 -0
  59. data/static/shoes-dmg.jpg +0 -0
  60. data/static/shoes-icon-blue.png +0 -0
  61. data/static/shoes-icon-brown.png +0 -0
  62. data/static/shoes-icon.png +0 -0
  63. data/static/shoes-manual-apps.gif +0 -0
  64. data/static/shoes-manual-apps.png +0 -0
  65. data/static/shoes_main_window.png +0 -0
  66. data/static/stripe.png +0 -0
  67. data/static/tutor-back.png +0 -0
  68. metadata +193 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6bcd1dbda40ae455475b3c20d72de2a001c78fa2
4
+ data.tar.gz: b8c8f08bd75ade2cb6f3e5e77b928010d6f012bf
5
+ SHA512:
6
+ metadata.gz: 6acd6cabb8c4077a6ce5ed904199b3deaa3e80651da61c4504e402af58a122e94e823bf8e3db2c334abcb0331db5bc539f3db1e386c54f172c3b5835128bde9e
7
+ data.tar.gz: 1fc71d34d204980f8b60e9254151852848eb24c3df6fb1531d81e881c5c098b0baa4b1ae5944a3d0f2e1f32cf7b9a239a4a22bd39ef48b079fcda4e8f88fc8c7
@@ -0,0 +1,15 @@
1
+ .DS_Store
2
+ /.bundle/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Jason R. Clark
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,69 @@
1
+ # Shoes::Manual
2
+
3
+ This gem represents both the content of the Shoes manual, and the necessary
4
+ Shoes code to run it.
5
+
6
+ Because of how the manual is used, this gem does not depend directly on Shoes
7
+ itself. The content can be accessed without requiring `shoes-core`, but you
8
+ won't be able to actually run the manual app without using this gem in a full
9
+ Shoes installation.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'shoes-manual'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install shoes-manual
26
+
27
+ ## Content
28
+
29
+ The content of the Shoes manual can be directly accessed via this method:
30
+
31
+ ```
32
+ Shoes::Manual.load_docs('en')
33
+ ```
34
+
35
+ 'en' here designates the language. There is currently an (out of date) copy in
36
+ Japanese alongside the English content.
37
+
38
+ That method returns a structured output of the full manual:
39
+
40
+ ```
41
+ {
42
+ :description=> "\n\nShoes is a tiny graphics toolkit...."
43
+ :sections=>
44
+ [["Introducing",
45
+ {
46
+ :title=>"Introducing Shoes",
47
+ :section=>"Hello!",
48
+ :description=>"\n\nHow does Shoes look on OS X and Windows?..."
49
+ ...
50
+ }
51
+ ]]
52
+ }
53
+ ```
54
+
55
+ ## Running the Manual
56
+ If you have an application which includes Shoes, you can also run the manual
57
+ app by the following call:
58
+
59
+ ```
60
+ Shoes::Manual.run
61
+ ```
62
+
63
+ ## Contributing
64
+
65
+ 1. Fork it ( https://github.com/shoes/shoes-manual/fork )
66
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
67
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
68
+ 4. Push to the branch (`git push origin my-new-feature`)
69
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,45 @@
1
+ require "shoes/manual/version"
2
+
3
+ class Array
4
+ def /(len)
5
+ a = []
6
+ each_with_index do |x, i|
7
+ a << [] if i % len == 0
8
+ a.last << x
9
+ end
10
+ a
11
+ end
12
+ end
13
+
14
+ class Shoes
15
+ module Manual
16
+ ROOT_DIR = File.expand_path(File.join(__FILE__, "../../.."))
17
+
18
+ def self.run(lang = 'English')
19
+ $lang = lang
20
+ load 'shoes/manual/app.rb'
21
+ rescue LoadError
22
+ puts "Wasn't able to load Shoes to run the manual. Does your app have Shoes required?"
23
+ raise
24
+ end
25
+
26
+ def self.load_docs(lang)
27
+ @docs ||= load_path(lang =~ /\.txt$/ ? lang : File.join(ROOT_DIR, "static/manual-#{lang}.txt"))
28
+ end
29
+
30
+ def self.load_path(path)
31
+ str = IO.read(path).force_encoding("UTF-8")
32
+ (str.split(/^= (.+?) =/)[1..-1] / 2).map do |k, v|
33
+ sparts = v.split(/^== (.+?) ==/)
34
+ sections = (sparts[1..-1] / 2).map do |k2, v2|
35
+ meth = v2.split(/^=== (.+?) ===/)
36
+ k2t = k2[/^(?:The )?([\-\w]+)/, 1]
37
+ h = { title: k2, section: k, description: meth[0], methods: (meth[1..-1] / 2) }
38
+ [k2t, h]
39
+ end
40
+ h = { description: sparts[0], sections: sections, class: "toc" + k.downcase.gsub(/\W+/, '') }
41
+ [k, h]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,512 @@
1
+ require 'nokogiri'
2
+ require 'nkf'
3
+ require 'shoes/version'
4
+ require 'shoes/highlighter'
5
+ require 'shoes/manual'
6
+
7
+ class Array
8
+ def dark?
9
+ r, g, b = self
10
+ r + g + b < 0x55 * 3
11
+ end
12
+
13
+ def light?
14
+ r, g, b = self
15
+ r + g + b > 0xAA * 3
16
+ end
17
+ end
18
+
19
+ class Manual < Shoes
20
+ url '/', :index
21
+ url '/manual/(\d+)', :index
22
+
23
+ include Shoes::Highlighter::Markup
24
+
25
+ def index(pnum = 0)
26
+ # font LANG == 'ja' ? 'MS UI Gothic' : 'Arial'
27
+ # style Link, underline: false, weight: 'bold'
28
+ # style LinkHover, stroke: '#06E'
29
+ self.scroll_top = 0
30
+ TOC.clear; TOC_LIST.clear
31
+ table_of_contents.each { |toc| TOC << toc }
32
+ pnum == '999' ? mk_search_page : manual(*get_title_and_desc(pnum.to_i))
33
+ end
34
+
35
+ def get_title_and_desc(pnum)
36
+ chapter, section = PNUMS[pnum]
37
+ return nil unless chapter
38
+ if section
39
+ [pnum, DOCS[chapter][1][:sections][section][1][:title],
40
+ DOCS[chapter][1][:sections][section][1][:description],
41
+ DOCS[chapter][1][:sections][section][1][:methods]]
42
+ else
43
+ [pnum, DOCS[chapter][0], DOCS[chapter][1][:description], []]
44
+ end
45
+ end
46
+
47
+ def table_of_contents
48
+ PNUMS.map.with_index do |e, pnum|
49
+ chapter, section = e
50
+ title = section ? DOCS[chapter][1][:sections][section][1][:title] : DOCS[chapter][0]
51
+ title = title.sub('The', '').split(' ').first
52
+ TOC_LIST << [title, section]
53
+ section ? [' ', link(title) { visit "/manual/#{pnum}" }, "\n"] : [link(fg(title, magenta)) { visit "/manual/#{pnum}" }, "\n"]
54
+ end.flatten
55
+ end
56
+
57
+ def manual(pnum, docs_title, docs_description, docs_methods)
58
+ flow do
59
+ show_header docs_title
60
+ show_toc
61
+ paras = mk_paras docs_description
62
+ flow width: 0.75, margin: [20, 0, 20, 0] do
63
+ show_page paras, true
64
+ show_methods docs_methods
65
+ para link('top') { visit "/manual/0" }, " ",
66
+ link('prev') { visit "/manual/#{(pnum - 1) % PEND}" }, " ",
67
+ link('next') { visit "/manual/#{(pnum + 1) % PEND}" }, " ",
68
+ link('end') { visit "/manual/#{PEND - 1}" }
69
+ end
70
+ end
71
+ end
72
+
73
+ def show_header(docs_title)
74
+ background snow..white, angle: 90
75
+ background midnightblue..black, height: 90
76
+ flow width: 500 do
77
+ para fg("The Shoes 4 Manual #{VERSION}", gray), left: 120, top: 10
78
+ title fg(docs_title, white), left: 120, top: 30, font: 'Coolvetica'
79
+ end
80
+ image(File.join(DIR, 'static/shoes-icon.png'), width: 110, height: 110).move 5, -12
81
+ end
82
+
83
+ def show_toc
84
+ s = self
85
+ stack height: 30
86
+ flow width: 0.2, margin_left: 10 do
87
+ flow(margin_right: 20) do
88
+ background black(0.7), curve: 5
89
+ inscription "Not findng it?\n", 'Try ', link(fg 'Search', white) { visit '/manual/999' }, '!', align: 'center', stroke: lightgray
90
+ end
91
+ stack(height: 10) {}
92
+ para(*TOC)
93
+ para link(fg 'to_html', green) { s.html_manual }
94
+ end
95
+ end
96
+
97
+ def show_methods(docs_methods, term = nil)
98
+ docs_methods.each do |m, d|
99
+ flow do
100
+ background rgb(60, 60, 60), curve: 5
101
+ n = m.index("\u00BB")
102
+ if n
103
+ para ' ', fg(strong(m[0...n]), white), fg(strong(m[n..-1]), rgb(160, 160, 160))
104
+ else
105
+ para ' ', fg(strong(m), white)
106
+ end
107
+ end
108
+ para NL
109
+ show_page mk_paras(d), false, term
110
+ end
111
+ end
112
+
113
+ def show_page(paras, intro = false, term = nil)
114
+ s = self
115
+ paras.each_with_index do |text, i|
116
+ if text =~ CODE_RE
117
+ text.gsub CODE_RE do |lines|
118
+ lines = lines.split NL
119
+ n = lines[1] =~ /\#\!ruby/ ? 2 : 1
120
+ code = lines[n...-1].join(NL + ' ')
121
+ flow do
122
+ background rgb(190, 190, 190), curve: 5
123
+ inscription link(fg('Run this', magenta)) { eval s.mk_executable(code), TOPLEVEL_BINDING }, ' ', align: 'right'
124
+ if code.include? 'te-su-to'
125
+ para fg(code(' ' + code), maroon), NL, margin: [-10, 10, 0, 20]
126
+ else
127
+ para(*highlight(' ' + code, nil).map { |e| code e }, NL * 2, margin: [-10, 10, 0, 20])
128
+ end
129
+ end
130
+ fill_rest_of_line
131
+ para NL
132
+ end
133
+ next
134
+ end
135
+
136
+ if text =~ /\A \* (.+)/m
137
+ Regexp.last_match[1].split(/^ \* /).each do |txt|
138
+ image File.join(DIR, 'static/shoes-icon.png'), width: 20, height: 20
139
+ flow(width: 510) { show_paragraph txt, intro, i, term }
140
+ para NL
141
+ end
142
+ else
143
+ show_paragraph text, intro, i, term
144
+ end
145
+ end
146
+ end
147
+
148
+ def show_paragraph(txt, intro, i, term = nil)
149
+ txt = txt.gsub("\n", ' ').gsub(/\^(.+?)\^/m, '\1').gsub(/\[\[BR\]\]/i, "\n")
150
+ txts = txt.split(/(\[\[\S+?\]\])/m).map { |s| s.split(/(\[\[\S+? .+?\]\])/m) }.flatten
151
+ case txts[0]
152
+ when /\A==== (.+) ====/ then caption(*marker(Regexp.last_match[1], term), size: 24); para NL
153
+ when /\A=== (.+) ===/ then tagline(*marker(Regexp.last_match[1], term), size: 12, weight: 'bold')
154
+ when /\A== (.+) ==/ then subtitle(*marker(Regexp.last_match[1], term))
155
+ when /\A= (.+) =/ then title(*marker(Regexp.last_match[1], term))
156
+ when /\A\{COLORS\}/ then flow { color_page }
157
+ when /\A\{SAMPLES\}/ then flow { sample_page }
158
+ else
159
+ para(*mk_deco(mk_links(txts, term).flatten), NL, (intro && i.zero?) ? { size: 16 } : '')
160
+ txt.gsub IMAGE_RE do
161
+ image File.join(Shoes::Manual::ROOT_DIR, "static/#{Regexp.last_match[3]}"), eval("{#{Regexp.last_match[2] || 'margin_left: 50'}}")
162
+ fill_rest_of_line
163
+ end
164
+ end
165
+ end
166
+
167
+ def mk_links(txts, term = nil)
168
+ txts.map { |txt| txt.gsub(IMAGE_RE, '') }
169
+ .map { |txt| txt =~ /\[\[(\S+?)\]\]/m ? (t = Regexp.last_match[1].split('.'); link(ins(*marker(t.last, term))) { visit "/manual/#{find_pnum t.first}" }) : txt }
170
+ .map do|txt|
171
+ txt =~ /\[\[(\S+?) (.+?)\]\]/m ? (url = Regexp.last_match[1]; link(ins(*marker(Regexp.last_match[2], term))) { visit url =~ /^http/ ? url : "/manual/#{find_pnum url}" }) :
172
+ (txt.is_a?(String) ? marker(txt, term) : txt)
173
+ end
174
+ end
175
+
176
+ def mk_paras(str)
177
+ str.split(PARA_RE) - ['']
178
+ end
179
+
180
+ def mk_executable(code)
181
+ if code =~ /\# Not yet available/
182
+ "Shoes.app{para 'Sorry, not yet available...'}"
183
+ else
184
+ code
185
+ end
186
+ end
187
+
188
+ def color_page
189
+ COLORS.each do |color, v|
190
+ r, g, b = v.red, v.green, v.blue
191
+ c = v.dark? ? white : black
192
+ stack width: 0.33, height: 55, margin_top: 5 do
193
+ clr = send(color)
194
+ background clr
195
+ # para fg(strong(color), c), align: 'center'
196
+ para fg('. ', clr), fg(strong(color), c), fg(' .', clr), align: 'center'
197
+ para fg("rgb(#{r}, #{g}, #{b})", c), align: 'center'
198
+ end
199
+ end
200
+ para NL
201
+ end
202
+
203
+ def sample_page
204
+ mk_sample_names.each do |file|
205
+ stack width: 80 do
206
+ inscription file[0...-3]
207
+ image File.join(DIR, "snapshots/#{file[0..-3]}png"), width: 50, height: 50 do
208
+ Dir.chdir(File.join DIR, 'samples') { instance_eval(IO.read(file), file, 0) }
209
+ end
210
+ para NL
211
+ end
212
+ end
213
+ end
214
+
215
+ def mk_sample_names
216
+ Dir[File.join(DIR, 'samples/sample*.rb')].map do |file|
217
+ orig_name = File.basename file
218
+ dummy_name = orig_name.sub(/sample(.*)\.rb/)do
219
+ first, second = Regexp.last_match[1].split('-')
220
+ "%02d%s%s" % [first.to_i, ('-' if second), second]
221
+ end
222
+ [dummy_name, orig_name]
223
+ end.sort.map(&:last)
224
+ end
225
+
226
+ def find_pnum(page)
227
+ return 999 if page == 'Search'
228
+ TOC_LIST.each_with_index do |e, i|
229
+ title = e
230
+ return i if title == page
231
+ end
232
+ end
233
+
234
+ def self.mk_page_numbers(docs)
235
+ pnum = []
236
+ docs.length.times do |i|
237
+ pnum << [i, nil]
238
+ docs[i][1][:sections].length.times do |j|
239
+ pnum << [i, j]
240
+ end
241
+ end
242
+ pnum
243
+ end
244
+
245
+ def html_manual
246
+ dir = ask_save_folder
247
+ return unless dir
248
+ FileUtils.mkdir_p File.join(dir, 'static')
249
+ FileUtils.mkdir_p File.join(dir, 'snapshots')
250
+ %w(shoes-icon.png shoes-manual-apps.png manual.css code_highlighter.js code_highlighter_ruby.js)
251
+ .each { |x| FileUtils.cp "#{Shoes::Manual::ROOT_DIR}/static/#{x}", "#{dir}/static" }
252
+ Dir[File.join Shoes::Manual::ROOT_DIR, 'static/man-*.png'].each { |x| FileUtils.cp x, "#{dir}/static" }
253
+ Dir[File.join DIR, 'snapshots/sample*.png'].each { |x| FileUtils.cp x, "#{dir}/snapshots" }
254
+
255
+ TOC_LIST.length.times do |n|
256
+ num, title, desc, methods = get_title_and_desc n
257
+ open File.join(dir, "#{TOC_LIST[n][0]}.html"), 'wb:utf-8' do |f|
258
+ html = mk_html(title, desc, methods, TOC_LIST[n + 1], get_title_and_desc(n + 1), mk_sidebar_list(num))
259
+ f.puts html.force_encoding('UTF-8')
260
+ end
261
+ end
262
+ end
263
+
264
+ def mk_html(title, desc, methods, next_file, next_title, menu)
265
+ man = self
266
+ html = Nokogiri::HTML::Builder.new do |h|
267
+ h.html(lang: Manual::LANG) do
268
+ h.head do
269
+ h.meta charset: 'utf-8'
270
+ h.title "The Shoes 4 Manual // #{title}"
271
+ h.script type: "text/javascript", src: "static/code_highlighter.js"
272
+ h.script type: "text/javascript", src: "static/code_highlighter_ruby.js"
273
+ h.style type: "text/css" do
274
+ h.text "@import 'static/manual.css';"
275
+ end
276
+ end
277
+ h.body do
278
+ h.div.main! do
279
+ h.div.manual! do
280
+ h.h2 "The Shoes 4 Manual #{VERSION}"
281
+ h.h1 title
282
+
283
+ paras = man.mk_paras desc
284
+ h.div(class: 'intro') { h << man.manual_p(paras.shift) }
285
+
286
+ html_paragraph = proc do
287
+ paras.each do |str|
288
+ if str =~ CODE_RE
289
+ h.pre { h.code(class: 'rb') { h.text Regexp.last_match[1].gsub(/^\s*?\n/, '') } }
290
+ else
291
+ cmd, str = case str
292
+ when /\A==== (.+) ====/ then [:h4, Regexp.last_match[1]]
293
+ when /\A=== (.+) ===/ then [:h3, Regexp.last_match[1]]
294
+ when /\A== (.+) ==/ then [:h2, Regexp.last_match[1]]
295
+ when /\A= (.+) =/ then [:h1, Regexp.last_match[1]]
296
+ when /\A\{COLORS\}/
297
+ COLORS.each do |color, v|
298
+ f = v.dark? ? "white" : "black"
299
+ h.div(class: 'color', style: "background: #{color}; color: #{f}") { h.h3 color.to_s; h.p("rgb(%d, %d, %d)" % [v.red, v.green, v.blue]) }
300
+ end
301
+ when /\A\{SAMPLES\}/
302
+ man.mk_sample_names.each do |name|
303
+ name = name[0...-3]
304
+ h.div(class: 'sample') do
305
+ h.h3 name
306
+ h.text '<a href="snapshots/%s.png"><img src="snapshots/%s.png" alt="%s" border=0 width=50 height=50></a>' % [name, name, name]
307
+ end
308
+ end
309
+ when /\A \* (.+)/m
310
+ h.ul { Regexp.last_match[1].split(/^ \* /).each { |x| h << man.manual_p(x) } }; nil
311
+ else
312
+ [:p_, str]
313
+ end
314
+ h.send(cmd) { h << man.manual_p(str) } if cmd.is_a?(Symbol)
315
+ end
316
+ end
317
+ end
318
+
319
+ html_paragraph.call
320
+
321
+ methods.each do |m, d|
322
+ n = m.index("\u00BB")
323
+ n ? (sig, val = m[0...n - 1], m[n - 1..-1]) : (sig, val = m, nil)
324
+ aname = sig[/^[^(=]+=?/].gsub(/\s/, '').downcase
325
+ h.a(name: aname)
326
+ h.div(class: 'divmethod') do
327
+ h.a sig, href: "##{aname}"
328
+ h.text val if val
329
+ end
330
+ h.div(class: 'desc') do
331
+ paras = man.mk_paras d
332
+ html_paragraph.call
333
+ end
334
+ end
335
+
336
+ h.p(class: 'next') { h.text "Next: "; h.a(href: "#{next_file[0]}.html") { h.text next_title[1] } } if next_title
337
+ end
338
+ h.div(class: 'sidebar') do
339
+ h.img src: "static/shoes-icon.png"
340
+ h.ul do
341
+ h.li { h.a(class: 'prime', href: "./") { h.text "HELP" } }
342
+ menu.each do |m|
343
+ h.li do
344
+ unless m.is_a?(Array)
345
+ h.a(href: "#{m}.html") { h.text m }
346
+ else
347
+ h.ul(class: 'sub') do
348
+ m.each do |sm |
349
+ h.li { h.a(href: "#{sm}.html") { h.text sm } }
350
+ end
351
+ end
352
+ end
353
+ end
354
+ end
355
+ end
356
+ end
357
+ end
358
+ end
359
+ end
360
+ end.to_html
361
+
362
+ "<!DOCTYPE html>\n#{html}"
363
+ end
364
+
365
+ def mk_sidebar_list(num)
366
+ toc = []
367
+ [0..3, 4..9, 10..16, 17..32, 33..37].each do |r|
368
+ toc.push TOC_LIST[r.first][0]
369
+ toc.push(TOC_LIST[r.first + 1..r.last].to_a.map(&:first)) if r.include?(num)
370
+ end
371
+ toc
372
+ end
373
+
374
+ def manual_p(str)
375
+ str.gsub(/\n+\s*/, " ")
376
+ .gsub(/&/, '&amp;').gsub(/>/, '&gt;').gsub(/>/, '&lt;').gsub(/"/, '&quot;')
377
+ .gsub(/`(.+?)`/m, '<code>\1</code>').gsub(/\[\[BR\]\]/i, "<br />\n")
378
+ .gsub(/\^(.+?)\^/m, '\1')
379
+ .gsub(/'''(.+?)'''/m, '<strong>\1</strong>').gsub(/''(.+?)''/m, '<em>\1</em>')
380
+ .gsub(/\[\[((http|https):\/\/\S+?)\]\]/m, '<a href="\1" target="_new">\1</a>')
381
+ .gsub(/\[\[((http|https):\/\/\S+?) (.+?)\]\]/m, '<a href="\1" target="_new">\3</a>')
382
+ .gsub(/\[\[(\S+?)\]\]/m) do
383
+ ms, mn = Regexp.last_match[1].split(".", 2)
384
+ if mn
385
+ '<a href="' + ms + '.html#' + mn + '">' + mn + '</a>'
386
+ else
387
+ '<a href="' + ms + '.html">' + ms + '</a>'
388
+ end
389
+ end
390
+ .gsub(/\[\[(\S+?) (.+?)\]\]/m, '<a href="\1.html">\2</a>')
391
+ .gsub(/\!(\{[^}\n]+\})?([^!\n]+\.\w+)\!/) do
392
+ '<img src="' + "static/#Regexp.last_match[2]" + '" />'
393
+ end
394
+ end
395
+
396
+ def mk_search_page
397
+ s = self
398
+ flow do
399
+ show_header 'Search'
400
+ show_toc
401
+
402
+ flow width: 0.8, margin: [10, 0, 20, 0] do
403
+ el = edit_line width: 300
404
+ tagline link('search'){
405
+ term = el.text.strip
406
+ unless term.empty?
407
+ descs, methods = s.search term
408
+ @f.clear { s.show_search_result term, descs, methods }
409
+ end
410
+ }
411
+ stack(height: 20) {}
412
+ @f = flow {}
413
+ end
414
+ end
415
+ end
416
+
417
+ def search(term)
418
+ descs, methods = [], []
419
+ PNUMS.each_with_index do |(chapter, section), pnum|
420
+ _pnum, docs_title, docs_description, docs_methods = get_title_and_desc(pnum)
421
+ paras = mk_paras(docs_description)
422
+ descs << [chapter, section, docs_title, paras] if paras.map { |txt| txt.gsub(CODE_RE, '').gsub(IMAGE_RE, '') }.join(' ').index(term)
423
+ docs_methods.each do |docs_method|
424
+ m, d = docs_method
425
+ methods << [chapter, section, docs_title, docs_method] if m.index(term) || d.gsub(CODE_RE, '').gsub(IMAGE_RE, '').index(term)
426
+ end
427
+ end
428
+ [descs, methods]
429
+ end
430
+
431
+ def show_search_result(term, descs, methods)
432
+ s = self
433
+ if descs.empty? && methods.empty?
434
+ subtitle 'Not Found'
435
+ else
436
+ methods.each do |(chapter, _section, docs_title, docs_method)|
437
+ flow margin: [10, 10, 0, 5] do
438
+ background rgb(200, 200, 200), curve: 5
439
+ para "#{DOCS[chapter][0]}: #{docs_title.sub('The', '').split(' ').first}: ",
440
+ link(docs_method[0]) { @f.clear { title docs_title; s.show_methods [docs_method], term } }, NL
441
+ end
442
+ stack(height: 2) {}
443
+ end
444
+ descs.each do |(chapter, section, docs_title, paras)|
445
+ flow margin_left: 10 do
446
+ if section
447
+ background gray, curve: 5
448
+ tagline link(fg(docs_title, white)) { @f.clear { title docs_title; s.show_page paras, true, term } }, width: 320
449
+ inscription "Sub-Section under #{DOCS[chapter][0]}", stroke: lightgray, width: 180
450
+ else
451
+ background black(0.8), curve: 5
452
+ subtitle link(fg(docs_title, white)) { @f.clear { title docs_title; s.show_page paras, true, term } }, width: 320
453
+ inscription 'Section Header', stroke: lightgray, width: 100
454
+ end
455
+ end
456
+ stack(height: 2) {}
457
+ end
458
+ para NL
459
+ end
460
+ end
461
+
462
+ def marker(txt, term)
463
+ if term && txt
464
+ tmp = txt.split(term).map { |s| [s, bg(term, yellow)] }.flatten
465
+ txt =~ /#{term}$/ ? tmp : tmp[0...-1]
466
+ else
467
+ [txt]
468
+ end
469
+ end
470
+
471
+ def mk_deco(datas)
472
+ datas = decoration(datas, /`(.+?)`/m) { |s| fg code(s), rgb(255, 30, 0) }
473
+ datas = decoration(datas, /'''(.+?)'''/m) { |s| strong s }
474
+ decoration(datas, /''(.+?)''/m) { |s| em s }
475
+ end
476
+
477
+ def decoration(datas, re, &blk)
478
+ datas.map do |data|
479
+ if data.is_a? String
480
+ txts = [data]
481
+ data.match re do |md|
482
+ n = data.index md[0]
483
+ txts = [data[0...n], blk[md[1]], decoration([data[n + md[0].length..-1]], re, &blk)]
484
+ end
485
+ txts
486
+ else
487
+ data
488
+ end
489
+ end.flatten
490
+ end
491
+
492
+ # Hack to consume remaining space to the right of a flow.
493
+ # Used to rely on NL's, but with new text flowing, that doesn't work anymore.
494
+ def fill_rest_of_line
495
+ flow width: 1.0 do
496
+ end
497
+ end
498
+
499
+ IMAGE_RE = /\!(\{([^}\n]+)\})?([^!\n]+\.\w+)\!/
500
+ CODE_RE = /\{{3}(?:\s*\#![^\n]+)?(.+?)\}{3}/m
501
+ PARA_RE = /\s*?(\{{3}(?:.+?)\}{3})|\n\n/m
502
+ NL = "\n"
503
+ LANG = $lang.downcase[0, 2]
504
+ DOCS = Shoes::Manual.load_docs(LANG)
505
+ PNUMS = mk_page_numbers DOCS
506
+ PEND = PNUMS.length
507
+ TOC, TOC_LIST = [], []
508
+ COLORS = Shoes::COLORS
509
+ VERSION = Shoes::VERSION
510
+ end
511
+
512
+ Shoes.app title: 'The Shoes 4 Manual', width: 720, height: 640