bookchef 0.1.6

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.
@@ -0,0 +1,88 @@
1
+ body { font-family: "Liberation Serif", "Times New Roman"; line-height: 150%; }
2
+
3
+ code, .code.inline { font-family: "Monaco"; font-size: 80%; background-color: #eee; }
4
+ code { display: block; clear: both; margin: 1em 0; padding: 0.5em; white-space: pre; line-height: 120%; }
5
+
6
+ @mixin end_of_section_block_container {
7
+ font-size: 80%; margin: 1em; border: dotted 1px #aaa; padding: 1em 1em 0 1em;
8
+ }
9
+ @mixin end_of_section_block {
10
+ margin-bottom: 1em;
11
+ }
12
+
13
+ p { margin: 0 0 0 0; text-indent: 1em; text-align: justify; }
14
+ span.name, span.filename, span.keyboard { font-family: "Monaco"; font-size: 80%; }
15
+ img { clear: both; display: block; margin: 2em 1em; width: 80%; page-break-inside: avoid; }
16
+
17
+ .footnotes {
18
+ @include end_of_section_block_container; page-break-inside: avoid;
19
+ .footnote { @include end_of_section_block; }
20
+ }
21
+ .references {
22
+ font-style: italic; @include end_of_section_block_container; page-break-inside: avoid;
23
+ img {margin: 0 0.5em 0 0; margin-bottom: -0.3em;display: inline; width: 16px; }
24
+ .reference { @include end_of_section_block; }
25
+ }
26
+
27
+ .footnotes, .references { font-size: 70%; line-height: 120%; }
28
+
29
+ h1 { page-break-inside: avoid; }
30
+ .chapter {
31
+ page-break-before: always;
32
+ h1 { font-size: 270%; line-height: 100%; }
33
+ .section {
34
+ margin-top: 2em;
35
+ h1 { font-size: 220%; }
36
+ .section {
37
+ margin-top: 1.7em;
38
+ h1 { font-size: 190%; }
39
+ .section {
40
+ margin-top: 1.5em;
41
+ h1 { font-size: 170%; }
42
+ .section {
43
+ margin-top: 1.3em;
44
+ h1 { font-size: 140%; }
45
+ .section {
46
+ margin-top: 1.1em;
47
+ h1 { font-size: 125%; }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ p.frameDescription { font-style: italic; font-size: 80%; margin: 1.5em 0 -0.5em 0; text-indent: 0px; }
56
+
57
+ .cover {
58
+ page-break-after: always;
59
+ img { width: 100%; }
60
+ }
61
+
62
+ .tableOfContents {
63
+ page-break-after: always; font-size: 120%;
64
+ ul { list-style-type: none; margin: 0 0 0 0; padding: 0 0 0 0; }
65
+ }
66
+
67
+ table {
68
+ margin: 1.5em 1em; page-break-inside: avoid;
69
+ border-collapse: collapse;
70
+ td { vertical-align: top; }
71
+ td, th { border: solid 1px #aaa; padding: 0.5em; margin-top: 1.5em; font-size: 80%; }
72
+ }
73
+
74
+ ul, ol {
75
+ margin-left: 2em;
76
+ li { margin: 0.5em 0; }
77
+ }
78
+
79
+ sup { height: 0; line-height: 1; vertical-align: baseline; _vertical-align: bottom; position: relative; bottom: 1ex; }
80
+
81
+ .footer {
82
+ border-top: solid 1px #ccc;
83
+ paddign-top: 1em; font-size: 80%; line-height: 100%;
84
+ .text { float: left; width: 80%; color: #aaa; }
85
+ .info { float: right; width: 20%; text-align: right; font-weight: bold; }
86
+ }
87
+
88
+ .insertPageBreak { page-break-after: always; height: 1px; }
@@ -0,0 +1,238 @@
1
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common">
2
+ <xsl:output indent="yes" omit-xml-declaration="yes"/>
3
+
4
+ <xsl:param name="gem_path">#{gem_path}</xsl:param>
5
+
6
+ <!-- Identity template -->
7
+ <xsl:template match="@* | node()">
8
+ <xsl:copy>
9
+ <xsl:apply-templates select="@* | node()"/>
10
+ </xsl:copy>
11
+ </xsl:template>
12
+
13
+ <xsl:template name="string-replace-all">
14
+ <xsl:param name="text" />
15
+ <xsl:param name="replace" />
16
+ <xsl:param name="by" />
17
+ <xsl:choose>
18
+ <xsl:when test="contains($text, $replace)">
19
+ <xsl:value-of select="substring-before($text,$replace)" />
20
+ <xsl:value-of select="$by" />
21
+ <xsl:call-template name="string-replace-all">
22
+ <xsl:with-param name="text" select="substring-after($text,$replace)" />
23
+ <xsl:with-param name="replace" select="$replace" />
24
+ <xsl:with-param name="by" select="$by" />
25
+ </xsl:call-template>
26
+ </xsl:when>
27
+ <xsl:otherwise>
28
+ <xsl:value-of select="$text" />
29
+ </xsl:otherwise>
30
+ </xsl:choose>
31
+ </xsl:template>
32
+
33
+ <xsl:template match="text()">
34
+ <xsl:variable name="mdash"><xsl:text disable-output-escaping="yes"><![CDATA[#mdash;]]></xsl:text></xsl:variable>
35
+ <xsl:call-template name="string-replace-all">
36
+ <xsl:with-param name="text" select="." />
37
+ <xsl:with-param name="replace" select='" - "'/>
38
+ <xsl:with-param name="by" select='$mdash' />
39
+ </xsl:call-template>
40
+ </xsl:template>
41
+
42
+ <xsl:template match="/book">
43
+ <html>
44
+ <head>
45
+ <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
46
+ <link href="{$gem_path}/stylesheets/css/default.css" rel="stylesheet" type="text/css"/>
47
+ <xsl:copy-of select="/book/settings/*"/>
48
+ </head>
49
+ <body>
50
+ <xsl:apply-templates select="cover" mode="cover" />
51
+ <div class="tableOfContents">
52
+ <h2>Table of contents</h2>
53
+ <ul><xsl:apply-templates select="chapter" mode="table_of_contents" /></ul>
54
+ </div>
55
+ <xsl:apply-templates select="node()" />
56
+ </body>
57
+ </html>
58
+ </xsl:template>
59
+
60
+ <xsl:template match="cover" mode="cover">
61
+ <div class="cover"><xsl:apply-templates select="@* | node()" /></div>
62
+ </xsl:template>
63
+ <xsl:template match="cover">
64
+ </xsl:template>
65
+
66
+ <xsl:template match="InsertBookVersion">
67
+ <xsl:value-of select="/book/@version" />
68
+ </xsl:template>
69
+
70
+ <xsl:template match="/book/settings">
71
+ </xsl:template>
72
+
73
+ <xsl:template match="section/title">
74
+ <h1 id="{../@id}"><xsl:value-of select="." /></h1>
75
+ </xsl:template>
76
+ <xsl:template match="chapter/title">
77
+ <h1 id="{../@id}">Chapter <xsl:number count="chapter"/>. <xsl:value-of select="." /></h1>
78
+ </xsl:template>
79
+
80
+ <xsl:template match="li/title">
81
+ <b><xsl:value-of select="." /></b>
82
+ </xsl:template>
83
+
84
+ <xsl:template match="chapter">
85
+ <div class="chapter">
86
+ <xsl:apply-templates select="@* |node()" />
87
+ </div>
88
+ </xsl:template>
89
+ <xsl:template match="chapter/@id">
90
+ </xsl:template>
91
+
92
+ <xsl:template match="chapter" mode="table_of_contents">
93
+ <li>Chapter <xsl:number count="chapter"/>. <a href="#{@id}"><xsl:value-of select="./title"/></a></li>
94
+ </xsl:template>
95
+
96
+ <xsl:template match="section">
97
+ <div class="section">
98
+ <xsl:apply-templates select="@* |node()" />
99
+ </div>
100
+ </xsl:template>
101
+ <xsl:template match="section/@id">
102
+ </xsl:template>
103
+
104
+ <xsl:template match="pagebreak">
105
+ <div class="insertPageBreak"></div>
106
+ </xsl:template>
107
+
108
+ <xsl:template match="footnotes">
109
+ <div class="footnotes">
110
+ <xsl:apply-templates select="@* |node()" />
111
+ </div>
112
+ </xsl:template>
113
+
114
+ <xsl:template match="footnote">
115
+ <xsl:param name="id" select="./@id"/>
116
+ <div class="footnote" id="{$id}">
117
+ <sup><xsl:number count="footnote" level="single"/></sup><xsl:text> </xsl:text>
118
+ <xsl:apply-templates select="node()"/>
119
+ </div>
120
+ </xsl:template>
121
+
122
+
123
+ <xsl:template match="references">
124
+ <div class="references">
125
+ <xsl:apply-templates select="@* |node()" />
126
+ </div>
127
+ </xsl:template>
128
+
129
+ <xsl:template match="reference">
130
+ <xsl:param name="id" select="./@id"/>
131
+ <xsl:param name="type" select="./@type"/>
132
+ <xsl:param name="url" select="./@url"/>
133
+ <div class="reference" id="{$id}">
134
+ <img src="{$gem_path}/images/{$type}_link.png" />
135
+ [<xsl:number count="reference" level="single"/>]<xsl:text> </xsl:text>
136
+ <a href="{$url}"><xsl:value-of select="."/></a>
137
+ </div>
138
+ </xsl:template>
139
+
140
+ <xsl:template match="code">
141
+ <p class="frameDescription"><xsl:value-of select="./@description"/><xsl:text> </xsl:text></p>
142
+ <xsl:element name="{name()}">
143
+ <xsl:copy-of select="./node()"/>
144
+ </xsl:element>
145
+ </xsl:template>
146
+
147
+ <xsl:template match="table">
148
+ <p class="frameDescription"><xsl:value-of select="./@description"/><xsl:text> </xsl:text></p>
149
+ <table><xsl:apply-templates select="./node()" /></table>
150
+ </xsl:template>
151
+
152
+ <xsl:template match="code-inline" >
153
+ <span class="code inline"><xsl:apply-templates select="@* |node()" /></span>
154
+ </xsl:template>
155
+
156
+ <xsl:template match="name" >
157
+ <span class="name"><xsl:apply-templates select="@* |node()" /></span>
158
+ </xsl:template>
159
+
160
+ <xsl:template match="filename" >
161
+ <span class="filename"><xsl:apply-templates select="@* |node()" /></span>
162
+ </xsl:template>
163
+
164
+ <xsl:template match="keyboard" >
165
+ <span class="keyboard"><xsl:apply-templates select="@* |node()" /></span>
166
+ </xsl:template>
167
+
168
+ <xsl:template match="a[not(node())]">
169
+ <xsl:param name="href" select="./@href"/>
170
+ <xsl:param name="reference" select="./@reference"/>
171
+
172
+ <xsl:choose>
173
+ <xsl:when test="$href != ''">
174
+ <xsl:variable name="name"><xsl:value-of select='translate($href, "#", "")'/></xsl:variable>
175
+ <a href="{$href}"><xsl:value-of select="//*[@id=$name]/title"/></a>
176
+ </xsl:when>
177
+ <xsl:when test="$reference != ''">
178
+ <xsl:variable name="title"><xsl:value-of select='//reference[@id=$reference]'/></xsl:variable>
179
+ <xsl:variable name="url"><xsl:value-of select='//reference[@id=$reference]/@url'/></xsl:variable>
180
+ <a href="{$url}"><xsl:value-of select="$title"/></a>
181
+ </xsl:when>
182
+ </xsl:choose>
183
+
184
+ </xsl:template>
185
+
186
+ <xsl:template match="term" name="term">
187
+ <i><xsl:apply-templates select="@* |node()"/></i>
188
+ </xsl:template>
189
+
190
+ <xsl:template match="*[@reference]" >
191
+ <xsl:param name="href" select="./@reference"/>
192
+ <xsl:choose>
193
+
194
+ <xsl:when test="name() = 'span'">
195
+ <span class="referenceSelection">
196
+ <xsl:value-of select="."/>
197
+ </span>
198
+ </xsl:when>
199
+
200
+ <xsl:otherwise>
201
+ <xsl:variable name="reference_content">
202
+ <xsl:element name="{name()}">
203
+ <xsl:value-of select="."/>
204
+ </xsl:element>
205
+ </xsl:variable>
206
+ <xsl:apply-templates select="exslt:node-set($reference_content)"/>
207
+ </xsl:otherwise>
208
+
209
+ </xsl:choose>
210
+
211
+ <xsl:text> </xsl:text><a href="#{$href}">[<xsl:value-of select="./@number"/>]</a>
212
+
213
+ </xsl:template>
214
+
215
+ <xsl:template match="*[@footnote]" >
216
+ <xsl:param name="href" select="./@footnote"/>
217
+
218
+ <xsl:choose>
219
+ <xsl:when test="name() = 'span'">
220
+ <span class="footnoteSelection">
221
+ <xsl:value-of select="."/>
222
+ </span>
223
+ </xsl:when>
224
+ <xsl:otherwise>
225
+ <xsl:variable name="footnote_content">
226
+ <xsl:element name="{name()}">
227
+ <xsl:value-of select="."/>
228
+ </xsl:element>
229
+ </xsl:variable>
230
+ <xsl:apply-templates select="exslt:node-set($footnote_content)"/>
231
+ </xsl:otherwise>
232
+ </xsl:choose>
233
+
234
+ <a href="#{$href}"><sup><xsl:value-of select="./@number"/></sup></a>
235
+
236
+ </xsl:template>
237
+
238
+ </xsl:stylesheet>
@@ -0,0 +1,26 @@
1
+ <html>
2
+
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
5
+ <script type="text/javascript">
6
+ function subst() {
7
+ var vars={};
8
+ var x=document.location.search.substring(1).split('&');
9
+ for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
10
+ var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
11
+ for(var i in x) {
12
+ var y = document.getElementsByClassName(x[i]);
13
+ for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
14
+ }
15
+ }
16
+ </script>
17
+ </head>
18
+
19
+ <body onload="subst();">
20
+ <div class="footer">
21
+ <div class="text">#{footer_text}</div>
22
+ <div class="info"><span class="page"></span></div>
23
+ </div>
24
+ </body>
25
+
26
+ </html>
@@ -0,0 +1,192 @@
1
+ class BookChef
2
+
3
+ class TreeMerger
4
+
5
+ attr_reader :document, :version
6
+
7
+ class LinkLevelOutOfReach < Exception; end
8
+
9
+ def initialize(path, fn)
10
+ @path = File.expand_path(path)
11
+ @filename = fn
12
+ @document = File.read("#@path/#@filename").gsub(/<%(.*?)%>/, '&lt;%\1%&gt;')
13
+ @document = Nokogiri::XML.parse @document
14
+ insert_version_from_git_tag!
15
+ if File.exists?("#@path/settings.xml")
16
+ @settings = File.read("#@path/settings.xml")
17
+ @document.root << @settings
18
+ end
19
+ end
20
+
21
+ def run
22
+ @documents = []
23
+ @document = process_level(@document)
24
+ end
25
+
26
+ def save_to(fn)
27
+ f = File.open(fn, "w")
28
+ f.write @document.to_s
29
+ f.close
30
+ end
31
+
32
+ private
33
+
34
+ def process_level(level_document, current_path="")
35
+ @documents << level_document
36
+ sourced_sections = level_document.xpath('//section[@src]|//chapter[@src]')
37
+
38
+ normalize_code! level_document
39
+ convert_links! level_document, current_path
40
+ make_image_paths_absolute! level_document, current_path
41
+
42
+ sourced_sections.each do |s|
43
+ current_fn = filename_or_index(s[:src])
44
+ current_dir = s[:src].sub(/\/?[^\/]*\.xml\Z/, '')
45
+ path = current_path
46
+ path += "/#{current_dir}" unless current_dir.empty?
47
+ full_current_path = "#{path}/#{current_fn}"
48
+
49
+ puts "processing #{full_current_path}"
50
+ assign_id_to_section! s, full_current_path
51
+
52
+ # Parse the sourced file
53
+ level_file = File.read("#@path#{full_current_path}")
54
+ level_file = BookChef.protect_special_chars(level_file)
55
+ sourced_document = Nokogiri::XML.parse(level_file)
56
+ convert_references_and_footnotes! sourced_document, full_current_path
57
+
58
+ # Now process it too, replacing all src-s with xml from sourced files
59
+ sourced_document = process_level(sourced_document, path)
60
+
61
+ # replace the contents of the tag with the actual sourced file
62
+ s.children = sourced_document.root.children
63
+ end unless sourced_sections.empty?
64
+
65
+ @documents.delete(level_document) and return level_document
66
+ end
67
+
68
+ # Converts links 'href' attr from relative to absolute path
69
+ # according to the sections 'name' attrs.
70
+ def convert_links!(document, current_path)
71
+ document.search("a").each do |link|
72
+
73
+ next if link[:href].nil? || link[:href] =~ /\Ahttps?:\/\//
74
+ link_path_arr = link[:href].split("/")
75
+ current_path_arr = current_path.split("/").reject { |i| i.empty? }
76
+ uplevels_count = (link_path_arr.map { |i| i if i == ".." }).compact.size
77
+ link_path_arr.delete_if { |i| i == ".." }
78
+
79
+ if uplevels_count > current_path_arr.size
80
+ raise LinkLevelOutOfReach, "for link #{link.to_s} in \"#{current_path}\""
81
+ end
82
+
83
+ current_path_arr = current_path_arr.reverse.drop(uplevels_count).reverse
84
+ link[:href] = "#/#{current_path_arr.join("/")}/#{path_with_filename(link_path_arr.join("/"))}".gsub(/\/+/, '/')
85
+ end
86
+ end
87
+
88
+ def convert_references_and_footnotes!(document, current_path)
89
+
90
+ # Search the document tree and find the closest file
91
+ # that contains <footnotes> and <references>
92
+ container_paths = { footnote: current_path.split("/"), reference: current_path.split("/") }
93
+ [:footnote, :reference].each do |container|
94
+ catch :break_inner_loop do
95
+ (@documents + [document]).reverse.each_with_index do |d,i|
96
+ throw :break_inner_loop if d.search("//#{container}s").size > 0
97
+ container_paths[container].pop if container_paths[container].pop == "index.xml"
98
+ container_paths["#{container}_file"] = "/index.xml"
99
+ end
100
+ end
101
+ end
102
+
103
+ document.search("//footnote|//reference").each do |node|
104
+ node[:id] = current_path + "/#{node.name}_#{node[:id]}"
105
+ end
106
+
107
+ document.search("//@footnote|//@reference").each do |node|
108
+ node.parent["number"] = node.value
109
+ node.parent[node.name] = container_paths[node.name.to_sym].join("/") +
110
+ (container_paths["#{node.name}_file"] || '') +
111
+ "/#{node.name}_#{node.value}"
112
+ end
113
+ end
114
+
115
+ def make_image_paths_absolute!(document, current_path)
116
+ document.search("img").each do |img|
117
+ img[:src] = "file://" + @path + current_path + "/#{img[:src]}"
118
+ end
119
+ end
120
+
121
+ # Sets section id like this: <section id="/section1/subsection_a/intro.xml">
122
+ def assign_id_to_section!(node, full_current_path)
123
+ node[:id] = full_current_path
124
+ node.remove_attribute("src")
125
+ end
126
+
127
+ # returns just the filename out of the full path
128
+ # For example, if the path is "/section1/subsection"
129
+ # it returns "index.xml"
130
+ def filename_or_index(path)
131
+ (path.match(/[^\/]*\.xml\Z/) || ["index.xml"])[0]
132
+ end
133
+
134
+ # returns full path attaching "index.xml" to the end of no file specified
135
+ # For example, if the path is "/section/subsection"
136
+ # it returns "/section/subsection/index.xml"
137
+ def path_with_filename(path)
138
+ if path.match(/[^\/]*\.xml\Z/)
139
+ path
140
+ else
141
+ path + "/index.xml"
142
+ end
143
+ end
144
+
145
+ def normalize_code!(document)
146
+ document.search("code").each do |c|
147
+ minimum_whitespace = nil
148
+ lines_arr = c.children.to_s.split("\n")
149
+ new_lines_arr = []
150
+ lines_arr.each_with_index do |l,i|
151
+ next if l =~ /\A\s*\Z/
152
+ match_size = l.match(/\A( )*/).to_s.size
153
+ minimum_whitespace = match_size if !minimum_whitespace || match_size < minimum_whitespace
154
+ end
155
+ lines_arr.each_with_index do |l,i|
156
+ next if l =~ /\A\s*\Z/ && (i == 0 || i == lines_arr.size-1)
157
+ l.sub!("\n", '')
158
+ l.sub!(/\A( ){#{minimum_whitespace}}/, '')
159
+ new_lines_arr << split_long_line(l, l.match(/\A( )*/).to_s.size)
160
+ end
161
+ c.content = new_lines_arr.compact.join("\n")
162
+ end
163
+ end
164
+
165
+ def split_long_line(line, leading_spaces_number=0)
166
+ if line.length > 70
167
+ lines = line[61..line.length-1].split(/[ .]/, 2)
168
+ line1 = line[0..60] + lines[0]
169
+ leading_spaces = (0..leading_spaces_number-1).map { ' ' }.join
170
+ line2 = leading_spaces.to_s + lines[1].to_s
171
+ line2 = split_long_line(line2, leading_spaces_number)
172
+ return "#{line1}\n#{line2}"
173
+ else
174
+ line
175
+ end
176
+ end
177
+
178
+ def insert_version_from_git_tag!
179
+
180
+ `cd #{@path} && git tag`.split("\n").sort.reverse.each do |t|
181
+ if t =~ /\Av[0-9]/
182
+ @version = t.sub(/\Av/,'').rstrip
183
+ @document.root["version"] = t.sub(/\Av/,'').rstrip
184
+ return
185
+ end
186
+ end
187
+
188
+ end
189
+
190
+ end
191
+
192
+ end