blob: f2702313dbfb11bf4149f36f56916607bc00e756 [file] [log] [blame]
David Ostrovsky2b5fe092021-03-03 11:52:30 +01001#!/usr/bin/env python3
Dave Borowitzbface272016-05-19 16:20:00 -04002# coding=utf-8
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -07003# Copyright (C) 2013 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Paladox nonef262d682020-02-02 01:40:48 +000017import argparse
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -070018import re
19import sys
20
David Pursehouse6c25ea82013-09-26 16:48:27 +090021PAT_GERRIT = re.compile(r'^GERRIT')
22PAT_INCLUDE = re.compile(r'^(include::.*)(\[\])$')
23PAT_GET = re.compile(r'^get::([^ \t\n]*)')
Yuxuan 'fishy' Wang71acd112013-10-02 12:37:53 -070024PAT_TITLE = re.compile(r'^\.(.*)')
25PAT_STARS = re.compile(r'^\*\*\*\*')
Yuxuan 'fishy' Wang99cb68d2013-10-31 17:26:00 -070026PAT_SEARCHBOX = re.compile(r'^SEARCHBOX')
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -070027
28GERRIT_UPLINK = """
29
30++++
31<hr style=\"
32 height: 2px;
33 color: silver;
34 margin-top: 1.2em;
35 margin-bottom: 0.5em;
36\">
37++++
Yuxuan 'fishy' Wang71acd112013-10-02 12:37:53 -070038
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -070039"""
40
Yuxuan 'fishy' Wang71acd112013-10-02 12:37:53 -070041GET_TITLE = '<div class="title">%s</div>'
42
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -070043GET_MACRO = """
44
45++++
Yuxuan 'fishy' Wang0fed98e2013-10-04 10:08:55 -070046<div class="listingblock">
Yuxuan 'fishy' Wang71acd112013-10-02 12:37:53 -070047%s
Yuxuan 'fishy' Wang0fed98e2013-10-04 10:08:55 -070048<div class="content">
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -070049<a id=\"{0}\" onmousedown="javascript:
50 var i = document.URL.lastIndexOf(\'/Documentation/\');
51 var url = document.URL.substring(0, i) + \'{0}\';
52 document.getElementById(\'{0}\').href = url;">
53 GET {0} HTTP/1.0
54</a>
Yuxuan 'fishy' Wang71acd112013-10-02 12:37:53 -070055</div>
Yuxuan 'fishy' Wang0fed98e2013-10-04 10:08:55 -070056</div>
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -070057++++
Yuxuan 'fishy' Wang71acd112013-10-02 12:37:53 -070058
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -070059"""
60
Yuxuan 'fishy' Wang99cb68d2013-10-31 17:26:00 -070061SEARCH_BOX = """
62
63++++
Saša Živkov0b6f8fe2016-04-05 10:45:50 +020064<div style="
65 position:fixed;
66 top:0px;
67 right:0px;
68 text-align:
69 right;
70 padding-top:2px;
71 padding-right:0.5em;
72 padding-bottom:2px;">
73<input size="40"
74 style="line-height: 0.75em;font-size: 0.75em;"
75 id="docSearch"
76 type="text">
77<button style="
78 background:none!important;
79 border:none;
80 padding:0!important;
81 vertical-align:bottom;
82 font-family:'Open Sans','DejaVu Sans',sans-serif;
83 font-size:0.8em;
84 color:#1d4b8f;
85 text-decoration:none;"
86 type="button"
87 id="searchBox">
88 Search
89</button>
Han-Wen Nienhuys0096cc32019-03-25 14:46:40 +010090 %s
91</div>
92++++
93"""
94
95BUILTIN_SEARCH = """
Yuxuan 'fishy' Wang99cb68d2013-10-31 17:26:00 -070096<script type="text/javascript">
97var f = function() {
Paladox none3750a922018-11-03 17:43:38 +000098 window.location = '../#/Documentation/q/' +
Yuxuan 'fishy' Wang99cb68d2013-10-31 17:26:00 -070099 encodeURIComponent(document.getElementById("docSearch").value);
100}
101document.getElementById("searchBox").onclick = f;
102document.getElementById("docSearch").onkeypress = function(e) {
103 if (13 == (e.keyCode ? e.keyCode : e.which)) {
104 f();
105 }
106}
107</script>
Yuxuan 'fishy' Wang99cb68d2013-10-31 17:26:00 -0700108"""
109
Han-Wen Nienhuys0096cc32019-03-25 14:46:40 +0100110GOOGLE_SITE_SEARCH = """
111<script type="text/javascript">
112var f = function() {
113 window.location = 'https://www.google.com/search?q=' +
114 encodeURIComponent(document.getElementById("docSearch").value +
115 ' site:@SITE@');
116}
117document.getElementById("searchBox").onclick = f;
118document.getElementById("docSearch").onkeypress = function(e) {
119 if (13 == (e.keyCode ? e.keyCode : e.which)) {
120 f();
121 }
122}
123</script>
124"""
125
126
Edwin Kempina92861f2014-11-04 17:48:10 +0100127LINK_SCRIPT = """
128
129++++
130<script type="text/javascript">
131 decorate(document.getElementsByTagName('h1'));
132 decorate(document.getElementsByTagName('h2'));
133 decorate(document.getElementsByTagName('h3'));
Edwin Kempine55cf282014-12-11 17:09:45 +0100134 decorate(document.getElementsByTagName('h4'));
Edwin Kempina92861f2014-11-04 17:48:10 +0100135
136 var divs = document.getElementsByTagName('div');
137 var arr = new Array();
138 var excluded = getExcludedIds();
139 for(var i = 0; i < divs.length; i++) {
140 var d = divs[i];
141 var id = d.getAttribute('id');
142 if (id != null && !(id in excluded)) {
143 arr[arr.length] = d;
144 }
145 }
146 decorate(arr);
147
148 var anchors = document.getElementsByTagName('a');
149 arr = new Array();
150 for(var i = 0; i < anchors.length; i++) {
151 var a = anchors[i];
152 // if the anchor has no id there is no target to
153 // which we can link
154 if (a.getAttribute('id') != null) {
155 // if the anchor is empty there is no content which
156 // can receive the mouseover event, an empty anchor
157 // applies to the element that follows, move the
158 // element that follows into the anchor so that there
159 // is content which can receive the mouseover event
160 if (a.firstChild == null) {
161 var next = a.nextSibling;
162 if (next != null) {
163 next.parentNode.removeChild(next);
164 a.appendChild(next);
165 }
166 }
167 arr[arr.length] = a;
168 }
169 }
170 decorate(arr);
171
172 function decorate(e) {
173 for(var i = 0; i < e.length; i++) {
174 e[i].onmouseover = function (evt) {
175 var element = this;
176 // do nothing if the link icon is currently showing
177 var a = element.firstChild;
178 if (a != null && a instanceof Element
179 && a.getAttribute('id') == 'LINK') {
180 return;
181 }
182
183 // if there is no id there is no target to link to
184 var id = element.getAttribute('id');
185 if (id == null) {
186 return;
187 }
188
189 // create and show a link icon that links to this element
190 a = document.createElement('a');
191 a.setAttribute('id', 'LINK');
192 a.setAttribute('href', '#' + id);
193 a.setAttribute('style', 'position: absolute;'
194 + ' left: ' + (element.offsetLeft - 16 - 2 * 4) + 'px;'
Yuxuan 'fishy' Wang4f5ad9d2016-05-03 16:18:58 -0700195 + ' padding-left: 4px; padding-right: 4px;');
196 var span = document.createElement('span');
197 span.setAttribute('style', 'height: ' + element.offsetHeight + 'px;'
198 + ' display: inline-block; vertical-align: baseline;'
199 + ' font-size: 16px; text-decoration: none; color: grey;');
200 a.appendChild(span);
201 var link = document.createTextNode('🔗');
202 span.appendChild(link);
Edwin Kempina92861f2014-11-04 17:48:10 +0100203 element.insertBefore(a, element.firstChild);
204
205 // remove the link icon when the mouse is moved away,
Chad Horohoe69142322018-05-17 10:19:22 -0700206 // but keep it shown if the mouse is over the element, the link or
207 // the icon
Edwin Kempina92861f2014-11-04 17:48:10 +0100208 hide = function(evt) {
209 if (document.elementFromPoint(evt.clientX, evt.clientY) != element
210 && document.elementFromPoint(evt.clientX, evt.clientY) != a
Yuxuan 'fishy' Wang4f5ad9d2016-05-03 16:18:58 -0700211 && document.elementFromPoint(evt.clientX, evt.clientY) != span
212 && document.elementFromPoint(evt.clientX, evt.clientY) != link
Edwin Kempina92861f2014-11-04 17:48:10 +0100213 && element.contains(a)) {
214 element.removeChild(a);
215 }
216 }
217 element.onmouseout = hide;
218 a.onmouseout = hide;
Yuxuan 'fishy' Wang4f5ad9d2016-05-03 16:18:58 -0700219 span.onmouseout = hide;
220 link.onmouseout = hide;
Edwin Kempina92861f2014-11-04 17:48:10 +0100221 }
222 }
223 }
224
225 function getExcludedIds() {
226 var excluded = {};
227 excluded['header'] = true;
228 excluded['toc'] = true;
229 excluded['toctitle'] = true;
230 excluded['content'] = true;
231 excluded['preamble'] = true;
232 excluded['footer'] = true;
233 excluded['footer-text'] = true;
234 return excluded;
235 }
236</script>
237
238++++
239
240"""
241
Paladox nonef262d682020-02-02 01:40:48 +0000242parser = argparse.ArgumentParser()
243parser.add_argument('-o', '--out', help='output file')
244parser.add_argument('-s', '--src', help='source file')
245parser.add_argument('-x', '--suffix', help='suffix for included filenames')
246parser.add_argument('-b', '--searchbox', action="store_true", default=True,
Yuxuan 'fishy' Wang537664a2014-09-03 10:06:16 -0700247 help="generate the search boxes")
Paladox nonef262d682020-02-02 01:40:48 +0000248parser.add_argument('--no-searchbox', action="store_false", dest='searchbox',
Yuxuan 'fishy' Wang537664a2014-09-03 10:06:16 -0700249 help="don't generate the search boxes")
Marco Miller6213e7c2020-02-26 16:15:31 -0500250parser.add_argument('--site-search', action="store", metavar="SITE",
Han-Wen Nienhuys0096cc32019-03-25 14:46:40 +0100251 help=("generate the search box using google. SITE should " +
252 "point to the domain/path of the site, eg. " +
253 "gerrit-review.googlesource.com/Documentation"))
Paladox nonef262d682020-02-02 01:40:48 +0000254args = parser.parse_args()
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -0700255
Marco Miller6213e7c2020-02-26 16:15:31 -0500256if args.site_search:
Han-Wen Nienhuys0096cc32019-03-25 14:46:40 +0100257 SEARCH_BOX = (SEARCH_BOX %
Marco Miller6213e7c2020-02-26 16:15:31 -0500258 GOOGLE_SITE_SEARCH.replace("@SITE@", args.site_search))
Han-Wen Nienhuys0096cc32019-03-25 14:46:40 +0100259else:
260 SEARCH_BOX = SEARCH_BOX % BUILTIN_SEARCH
261
262
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -0700263try:
Chad Horohoedd224702018-05-16 22:33:06 -0400264 try:
Paladox nonef262d682020-02-02 01:40:48 +0000265 out_file = open(args.out, 'w', errors='ignore')
266 src_file = open(args.src, 'r', errors='ignore')
Chad Horohoedd224702018-05-16 22:33:06 -0400267 except TypeError:
Paladox nonef262d682020-02-02 01:40:48 +0000268 out_file = open(args.out, 'w')
269 src_file = open(args.src, 'r')
Chad Horohoedd224702018-05-16 22:33:06 -0400270 last_line = ''
271 ignore_next_line = False
272 last_title = ''
273 for line in src_file:
274 if PAT_GERRIT.match(last_line):
275 # Case of "GERRIT\n------" at the footer
276 out_file.write(GERRIT_UPLINK)
277 last_line = ''
278 elif PAT_SEARCHBOX.match(last_line):
279 # Case of 'SEARCHBOX\n---------'
Paladox nonef262d682020-02-02 01:40:48 +0000280 if args.searchbox:
Chad Horohoedd224702018-05-16 22:33:06 -0400281 out_file.write(SEARCH_BOX)
282 last_line = ''
283 elif PAT_INCLUDE.match(line):
284 # Case of 'include::<filename>'
285 match = PAT_INCLUDE.match(line)
286 out_file.write(last_line)
Paladox nonef262d682020-02-02 01:40:48 +0000287 last_line = match.group(1) + args.suffix + match.group(2) + '\n'
Chad Horohoedd224702018-05-16 22:33:06 -0400288 elif PAT_STARS.match(line):
289 if PAT_TITLE.match(last_line):
290 # Case of the title in '.<title>\n****\nget::<url>\n****'
291 match = PAT_TITLE.match(last_line)
292 last_title = GET_TITLE % match.group(1)
293 else:
294 out_file.write(last_line)
295 last_title = ''
296 elif PAT_GET.match(line):
297 # Case of '****\nget::<url>\n****' in rest api
298 url = PAT_GET.match(line).group(1)
299 out_file.write(GET_MACRO.format(url) % last_title)
300 ignore_next_line = True
301 elif ignore_next_line:
302 # Handle the trailing '****' of the 'get::' case
303 last_line = ''
304 ignore_next_line = False
305 else:
306 out_file.write(last_line)
307 last_line = line
308 out_file.write(last_line)
309 out_file.write(LINK_SCRIPT)
310 out_file.close()
Yuxuan 'fishy' Wange6cbae92013-09-03 18:26:54 -0700311except IOError as err:
Chad Horohoedd224702018-05-16 22:33:06 -0400312 sys.stderr.write(
Paladox nonef262d682020-02-02 01:40:48 +0000313 "error while expanding %s to %s: %s" % (args.src, args.out, err))
Chad Horohoedd224702018-05-16 22:33:06 -0400314 exit(1)