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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +69 -0
- data/Rakefile +2 -0
- data/lib/shoes/manual.rb +45 -0
- data/lib/shoes/manual/app.rb +512 -0
- data/lib/shoes/manual/version.rb +5 -0
- data/shoes-manual.gemspec +27 -0
- data/static/PKGBUILD +47 -0
- data/static/Shoes.icns +0 -0
- data/static/avatar.png +0 -0
- data/static/code_highlighter.js +188 -0
- data/static/code_highlighter_ruby.js +26 -0
- data/static/downloading.png +0 -0
- data/static/icon-debug.png +0 -0
- data/static/icon-error.png +0 -0
- data/static/icon-info.png +0 -0
- data/static/icon-warn.png +0 -0
- data/static/listbox_button1.png +0 -0
- data/static/listbox_button2.png +0 -0
- data/static/man-app.png +0 -0
- data/static/man-builds.png +0 -0
- data/static/man-builds1.png +0 -0
- data/static/man-editor-notepad.png +0 -0
- data/static/man-editor-osx.png +0 -0
- data/static/man-ele-background.png +0 -0
- data/static/man-ele-border.png +0 -0
- data/static/man-ele-button.png +0 -0
- data/static/man-ele-check.png +0 -0
- data/static/man-ele-editbox.png +0 -0
- data/static/man-ele-editline.png +0 -0
- data/static/man-ele-image.png +0 -0
- data/static/man-ele-listbox.png +0 -0
- data/static/man-ele-progress.png +0 -0
- data/static/man-ele-radio.png +0 -0
- data/static/man-ele-shape.png +0 -0
- data/static/man-ele-textblock.png +0 -0
- data/static/man-ele-video.png +0 -0
- data/static/man-intro-dmg.png +0 -0
- data/static/man-intro-exe.png +0 -0
- data/static/man-look-tiger.png +0 -0
- data/static/man-look-ubuntu.png +0 -0
- data/static/man-look-vista.png +0 -0
- data/static/man-run-osx.png +0 -0
- data/static/man-run-vista.png +0 -0
- data/static/man-run-xp.png +0 -0
- data/static/man-shot1.png +0 -0
- data/static/manual-en.txt +3531 -0
- data/static/manual-ja.txt +2829 -0
- data/static/manual.css +184 -0
- data/static/menu-corner1.png +0 -0
- data/static/menu-corner2.png +0 -0
- data/static/menu-gray.png +0 -0
- data/static/menu-left.png +0 -0
- data/static/menu-right.png +0 -0
- data/static/menu-top.png +0 -0
- data/static/shoes-dmg.jpg +0 -0
- data/static/shoes-icon-blue.png +0 -0
- data/static/shoes-icon-brown.png +0 -0
- data/static/shoes-icon.png +0 -0
- data/static/shoes-manual-apps.gif +0 -0
- data/static/shoes-manual-apps.png +0 -0
- data/static/shoes_main_window.png +0 -0
- data/static/stripe.png +0 -0
- data/static/tutor-back.png +0 -0
- metadata +193 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
data/lib/shoes/manual.rb
ADDED
@@ -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(/&/, '&').gsub(/>/, '>').gsub(/>/, '<').gsub(/"/, '"')
|
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
|