shoes-manual 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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