Philipp Wollermann | cb4b1e3 | 2018-03-23 10:24:37 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python |
Dave Borowitz | bface27 | 2016-05-19 16:20:00 -0400 | [diff] [blame] | 2 | # coding=utf-8 |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 3 | # 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 | |
| 17 | from optparse import OptionParser |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 18 | import re |
| 19 | import sys |
| 20 | |
David Pursehouse | 6c25ea8 | 2013-09-26 16:48:27 +0900 | [diff] [blame] | 21 | PAT_GERRIT = re.compile(r'^GERRIT') |
| 22 | PAT_INCLUDE = re.compile(r'^(include::.*)(\[\])$') |
| 23 | PAT_GET = re.compile(r'^get::([^ \t\n]*)') |
Yuxuan 'fishy' Wang | 71acd11 | 2013-10-02 12:37:53 -0700 | [diff] [blame] | 24 | PAT_TITLE = re.compile(r'^\.(.*)') |
| 25 | PAT_STARS = re.compile(r'^\*\*\*\*') |
Yuxuan 'fishy' Wang | 99cb68d | 2013-10-31 17:26:00 -0700 | [diff] [blame] | 26 | PAT_SEARCHBOX = re.compile(r'^SEARCHBOX') |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 27 | |
| 28 | GERRIT_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' Wang | 71acd11 | 2013-10-02 12:37:53 -0700 | [diff] [blame] | 38 | |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 39 | """ |
| 40 | |
Yuxuan 'fishy' Wang | 71acd11 | 2013-10-02 12:37:53 -0700 | [diff] [blame] | 41 | GET_TITLE = '<div class="title">%s</div>' |
| 42 | |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 43 | GET_MACRO = """ |
| 44 | |
| 45 | ++++ |
Yuxuan 'fishy' Wang | 0fed98e | 2013-10-04 10:08:55 -0700 | [diff] [blame] | 46 | <div class="listingblock"> |
Yuxuan 'fishy' Wang | 71acd11 | 2013-10-02 12:37:53 -0700 | [diff] [blame] | 47 | %s |
Yuxuan 'fishy' Wang | 0fed98e | 2013-10-04 10:08:55 -0700 | [diff] [blame] | 48 | <div class="content"> |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 49 | <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' Wang | 71acd11 | 2013-10-02 12:37:53 -0700 | [diff] [blame] | 55 | </div> |
Yuxuan 'fishy' Wang | 0fed98e | 2013-10-04 10:08:55 -0700 | [diff] [blame] | 56 | </div> |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 57 | ++++ |
Yuxuan 'fishy' Wang | 71acd11 | 2013-10-02 12:37:53 -0700 | [diff] [blame] | 58 | |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 59 | """ |
| 60 | |
Yuxuan 'fishy' Wang | 99cb68d | 2013-10-31 17:26:00 -0700 | [diff] [blame] | 61 | SEARCH_BOX = """ |
| 62 | |
| 63 | ++++ |
Saša Živkov | 0b6f8fe | 2016-04-05 10:45:50 +0200 | [diff] [blame] | 64 | <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> |
Yuxuan 'fishy' Wang | 99cb68d | 2013-10-31 17:26:00 -0700 | [diff] [blame] | 90 | <script type="text/javascript"> |
| 91 | var f = function() { |
| 92 | window.location = '../#/Documentation/' + |
| 93 | encodeURIComponent(document.getElementById("docSearch").value); |
| 94 | } |
| 95 | document.getElementById("searchBox").onclick = f; |
| 96 | document.getElementById("docSearch").onkeypress = function(e) { |
| 97 | if (13 == (e.keyCode ? e.keyCode : e.which)) { |
| 98 | f(); |
| 99 | } |
| 100 | } |
| 101 | </script> |
| 102 | </div> |
| 103 | ++++ |
| 104 | |
| 105 | """ |
| 106 | |
Edwin Kempin | a92861f | 2014-11-04 17:48:10 +0100 | [diff] [blame] | 107 | LINK_SCRIPT = """ |
| 108 | |
| 109 | ++++ |
| 110 | <script type="text/javascript"> |
| 111 | decorate(document.getElementsByTagName('h1')); |
| 112 | decorate(document.getElementsByTagName('h2')); |
| 113 | decorate(document.getElementsByTagName('h3')); |
Edwin Kempin | e55cf28 | 2014-12-11 17:09:45 +0100 | [diff] [blame] | 114 | decorate(document.getElementsByTagName('h4')); |
Edwin Kempin | a92861f | 2014-11-04 17:48:10 +0100 | [diff] [blame] | 115 | |
| 116 | var divs = document.getElementsByTagName('div'); |
| 117 | var arr = new Array(); |
| 118 | var excluded = getExcludedIds(); |
| 119 | for(var i = 0; i < divs.length; i++) { |
| 120 | var d = divs[i]; |
| 121 | var id = d.getAttribute('id'); |
| 122 | if (id != null && !(id in excluded)) { |
| 123 | arr[arr.length] = d; |
| 124 | } |
| 125 | } |
| 126 | decorate(arr); |
| 127 | |
| 128 | var anchors = document.getElementsByTagName('a'); |
| 129 | arr = new Array(); |
| 130 | for(var i = 0; i < anchors.length; i++) { |
| 131 | var a = anchors[i]; |
| 132 | // if the anchor has no id there is no target to |
| 133 | // which we can link |
| 134 | if (a.getAttribute('id') != null) { |
| 135 | // if the anchor is empty there is no content which |
| 136 | // can receive the mouseover event, an empty anchor |
| 137 | // applies to the element that follows, move the |
| 138 | // element that follows into the anchor so that there |
| 139 | // is content which can receive the mouseover event |
| 140 | if (a.firstChild == null) { |
| 141 | var next = a.nextSibling; |
| 142 | if (next != null) { |
| 143 | next.parentNode.removeChild(next); |
| 144 | a.appendChild(next); |
| 145 | } |
| 146 | } |
| 147 | arr[arr.length] = a; |
| 148 | } |
| 149 | } |
| 150 | decorate(arr); |
| 151 | |
| 152 | function decorate(e) { |
| 153 | for(var i = 0; i < e.length; i++) { |
| 154 | e[i].onmouseover = function (evt) { |
| 155 | var element = this; |
| 156 | // do nothing if the link icon is currently showing |
| 157 | var a = element.firstChild; |
| 158 | if (a != null && a instanceof Element |
| 159 | && a.getAttribute('id') == 'LINK') { |
| 160 | return; |
| 161 | } |
| 162 | |
| 163 | // if there is no id there is no target to link to |
| 164 | var id = element.getAttribute('id'); |
| 165 | if (id == null) { |
| 166 | return; |
| 167 | } |
| 168 | |
| 169 | // create and show a link icon that links to this element |
| 170 | a = document.createElement('a'); |
| 171 | a.setAttribute('id', 'LINK'); |
| 172 | a.setAttribute('href', '#' + id); |
| 173 | a.setAttribute('style', 'position: absolute;' |
| 174 | + ' left: ' + (element.offsetLeft - 16 - 2 * 4) + 'px;' |
Yuxuan 'fishy' Wang | 4f5ad9d | 2016-05-03 16:18:58 -0700 | [diff] [blame] | 175 | + ' padding-left: 4px; padding-right: 4px;'); |
| 176 | var span = document.createElement('span'); |
| 177 | span.setAttribute('style', 'height: ' + element.offsetHeight + 'px;' |
| 178 | + ' display: inline-block; vertical-align: baseline;' |
| 179 | + ' font-size: 16px; text-decoration: none; color: grey;'); |
| 180 | a.appendChild(span); |
| 181 | var link = document.createTextNode('🔗'); |
| 182 | span.appendChild(link); |
Edwin Kempin | a92861f | 2014-11-04 17:48:10 +0100 | [diff] [blame] | 183 | element.insertBefore(a, element.firstChild); |
| 184 | |
| 185 | // remove the link icon when the mouse is moved away, |
Chad Horohoe | 6914232 | 2018-05-17 10:19:22 -0700 | [diff] [blame] | 186 | // but keep it shown if the mouse is over the element, the link or |
| 187 | // the icon |
Edwin Kempin | a92861f | 2014-11-04 17:48:10 +0100 | [diff] [blame] | 188 | hide = function(evt) { |
| 189 | if (document.elementFromPoint(evt.clientX, evt.clientY) != element |
| 190 | && document.elementFromPoint(evt.clientX, evt.clientY) != a |
Yuxuan 'fishy' Wang | 4f5ad9d | 2016-05-03 16:18:58 -0700 | [diff] [blame] | 191 | && document.elementFromPoint(evt.clientX, evt.clientY) != span |
| 192 | && document.elementFromPoint(evt.clientX, evt.clientY) != link |
Edwin Kempin | a92861f | 2014-11-04 17:48:10 +0100 | [diff] [blame] | 193 | && element.contains(a)) { |
| 194 | element.removeChild(a); |
| 195 | } |
| 196 | } |
| 197 | element.onmouseout = hide; |
| 198 | a.onmouseout = hide; |
Yuxuan 'fishy' Wang | 4f5ad9d | 2016-05-03 16:18:58 -0700 | [diff] [blame] | 199 | span.onmouseout = hide; |
| 200 | link.onmouseout = hide; |
Edwin Kempin | a92861f | 2014-11-04 17:48:10 +0100 | [diff] [blame] | 201 | } |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | function getExcludedIds() { |
| 206 | var excluded = {}; |
| 207 | excluded['header'] = true; |
| 208 | excluded['toc'] = true; |
| 209 | excluded['toctitle'] = true; |
| 210 | excluded['content'] = true; |
| 211 | excluded['preamble'] = true; |
| 212 | excluded['footer'] = true; |
| 213 | excluded['footer-text'] = true; |
| 214 | return excluded; |
| 215 | } |
| 216 | </script> |
| 217 | |
| 218 | ++++ |
| 219 | |
| 220 | """ |
| 221 | |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 222 | opts = OptionParser() |
| 223 | opts.add_option('-o', '--out', help='output file') |
| 224 | opts.add_option('-s', '--src', help='source file') |
| 225 | opts.add_option('-x', '--suffix', help='suffix for included filenames') |
Yuxuan 'fishy' Wang | 537664a | 2014-09-03 10:06:16 -0700 | [diff] [blame] | 226 | opts.add_option('-b', '--searchbox', action="store_true", default=True, |
| 227 | help="generate the search boxes") |
| 228 | opts.add_option('--no-searchbox', action="store_false", dest='searchbox', |
| 229 | help="don't generate the search boxes") |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 230 | options, _ = opts.parse_args() |
| 231 | |
| 232 | try: |
Chad Horohoe | dd22470 | 2018-05-16 22:33:06 -0400 | [diff] [blame] | 233 | try: |
| 234 | out_file = open(options.out, 'w', errors='ignore') |
| 235 | src_file = open(options.src, 'r', errors='ignore') |
| 236 | except TypeError: |
| 237 | out_file = open(options.out, 'w') |
| 238 | src_file = open(options.src, 'r') |
| 239 | last_line = '' |
| 240 | ignore_next_line = False |
| 241 | last_title = '' |
| 242 | for line in src_file: |
| 243 | if PAT_GERRIT.match(last_line): |
| 244 | # Case of "GERRIT\n------" at the footer |
| 245 | out_file.write(GERRIT_UPLINK) |
| 246 | last_line = '' |
| 247 | elif PAT_SEARCHBOX.match(last_line): |
| 248 | # Case of 'SEARCHBOX\n---------' |
| 249 | if options.searchbox: |
| 250 | out_file.write(SEARCH_BOX) |
| 251 | last_line = '' |
| 252 | elif PAT_INCLUDE.match(line): |
| 253 | # Case of 'include::<filename>' |
| 254 | match = PAT_INCLUDE.match(line) |
| 255 | out_file.write(last_line) |
| 256 | last_line = match.group(1) + options.suffix + match.group(2) + '\n' |
| 257 | elif PAT_STARS.match(line): |
| 258 | if PAT_TITLE.match(last_line): |
| 259 | # Case of the title in '.<title>\n****\nget::<url>\n****' |
| 260 | match = PAT_TITLE.match(last_line) |
| 261 | last_title = GET_TITLE % match.group(1) |
| 262 | else: |
| 263 | out_file.write(last_line) |
| 264 | last_title = '' |
| 265 | elif PAT_GET.match(line): |
| 266 | # Case of '****\nget::<url>\n****' in rest api |
| 267 | url = PAT_GET.match(line).group(1) |
| 268 | out_file.write(GET_MACRO.format(url) % last_title) |
| 269 | ignore_next_line = True |
| 270 | elif ignore_next_line: |
| 271 | # Handle the trailing '****' of the 'get::' case |
| 272 | last_line = '' |
| 273 | ignore_next_line = False |
| 274 | else: |
| 275 | out_file.write(last_line) |
| 276 | last_line = line |
| 277 | out_file.write(last_line) |
| 278 | out_file.write(LINK_SCRIPT) |
| 279 | out_file.close() |
Yuxuan 'fishy' Wang | e6cbae9 | 2013-09-03 18:26:54 -0700 | [diff] [blame] | 280 | except IOError as err: |
Chad Horohoe | dd22470 | 2018-05-16 22:33:06 -0400 | [diff] [blame] | 281 | sys.stderr.write( |
| 282 | "error while expanding %s to %s: %s" % (options.src, options.out, err)) |
| 283 | exit(1) |