|  | #!/usr/bin/env python | 
|  | # coding=utf-8 | 
|  | # Copyright (C) 2013 The Android Open Source Project | 
|  | # | 
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | # you may not use this file except in compliance with the License. | 
|  | # You may obtain a copy of the License at | 
|  | # | 
|  | # http://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | # Unless required by applicable law or agreed to in writing, software | 
|  | # distributed under the License is distributed on an "AS IS" BASIS, | 
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | # See the License for the specific language governing permissions and | 
|  | # limitations under the License. | 
|  |  | 
|  | from optparse import OptionParser | 
|  | import re | 
|  | import sys | 
|  |  | 
|  | PAT_GERRIT = re.compile(r'^GERRIT') | 
|  | PAT_INCLUDE = re.compile(r'^(include::.*)(\[\])$') | 
|  | PAT_GET = re.compile(r'^get::([^ \t\n]*)') | 
|  | PAT_TITLE = re.compile(r'^\.(.*)') | 
|  | PAT_STARS = re.compile(r'^\*\*\*\*') | 
|  | PAT_SEARCHBOX = re.compile(r'^SEARCHBOX') | 
|  |  | 
|  | GERRIT_UPLINK = """ | 
|  |  | 
|  | ++++ | 
|  | <hr style=\" | 
|  | height: 2px; | 
|  | color: silver; | 
|  | margin-top: 1.2em; | 
|  | margin-bottom: 0.5em; | 
|  | \"> | 
|  | ++++ | 
|  |  | 
|  | """ | 
|  |  | 
|  | GET_TITLE = '<div class="title">%s</div>' | 
|  |  | 
|  | GET_MACRO = """ | 
|  |  | 
|  | ++++ | 
|  | <div class="listingblock"> | 
|  | %s | 
|  | <div class="content"> | 
|  | <a id=\"{0}\" onmousedown="javascript: | 
|  | var i = document.URL.lastIndexOf(\'/Documentation/\'); | 
|  | var url = document.URL.substring(0, i) + \'{0}\'; | 
|  | document.getElementById(\'{0}\').href = url;"> | 
|  | GET {0} HTTP/1.0 | 
|  | </a> | 
|  | </div> | 
|  | </div> | 
|  | ++++ | 
|  |  | 
|  | """ | 
|  |  | 
|  | SEARCH_BOX = """ | 
|  |  | 
|  | ++++ | 
|  | <div style=" | 
|  | position:fixed; | 
|  | top:0px; | 
|  | right:0px; | 
|  | text-align: | 
|  | right; | 
|  | padding-top:2px; | 
|  | padding-right:0.5em; | 
|  | padding-bottom:2px;"> | 
|  | <input size="40" | 
|  | style="line-height: 0.75em;font-size: 0.75em;" | 
|  | id="docSearch" | 
|  | type="text"> | 
|  | <button style=" | 
|  | background:none!important; | 
|  | border:none; | 
|  | padding:0!important; | 
|  | vertical-align:bottom; | 
|  | font-family:'Open Sans','DejaVu Sans',sans-serif; | 
|  | font-size:0.8em; | 
|  | color:#1d4b8f; | 
|  | text-decoration:none;" | 
|  | type="button" | 
|  | id="searchBox"> | 
|  | Search | 
|  | </button> | 
|  | <script type="text/javascript"> | 
|  | var f = function() { | 
|  | window.location = '../#/Documentation/q/' + | 
|  | encodeURIComponent(document.getElementById("docSearch").value); | 
|  | } | 
|  | document.getElementById("searchBox").onclick = f; | 
|  | document.getElementById("docSearch").onkeypress = function(e) { | 
|  | if (13 == (e.keyCode ? e.keyCode : e.which)) { | 
|  | f(); | 
|  | } | 
|  | } | 
|  | </script> | 
|  | </div> | 
|  | ++++ | 
|  |  | 
|  | """ | 
|  |  | 
|  | LINK_SCRIPT = """ | 
|  |  | 
|  | ++++ | 
|  | <script type="text/javascript"> | 
|  | decorate(document.getElementsByTagName('h1')); | 
|  | decorate(document.getElementsByTagName('h2')); | 
|  | decorate(document.getElementsByTagName('h3')); | 
|  | decorate(document.getElementsByTagName('h4')); | 
|  |  | 
|  | var divs = document.getElementsByTagName('div'); | 
|  | var arr = new Array(); | 
|  | var excluded = getExcludedIds(); | 
|  | for(var i = 0; i < divs.length; i++) { | 
|  | var d = divs[i]; | 
|  | var id = d.getAttribute('id'); | 
|  | if (id != null && !(id in excluded)) { | 
|  | arr[arr.length] = d; | 
|  | } | 
|  | } | 
|  | decorate(arr); | 
|  |  | 
|  | var anchors = document.getElementsByTagName('a'); | 
|  | arr = new Array(); | 
|  | for(var i = 0; i < anchors.length; i++) { | 
|  | var a = anchors[i]; | 
|  | // if the anchor has no id there is no target to | 
|  | // which we can link | 
|  | if (a.getAttribute('id') != null) { | 
|  | // if the anchor is empty there is no content which | 
|  | // can receive the mouseover event, an empty anchor | 
|  | // applies to the element that follows, move the | 
|  | // element that follows into the anchor so that there | 
|  | // is content which can receive the mouseover event | 
|  | if (a.firstChild == null) { | 
|  | var next = a.nextSibling; | 
|  | if (next != null) { | 
|  | next.parentNode.removeChild(next); | 
|  | a.appendChild(next); | 
|  | } | 
|  | } | 
|  | arr[arr.length] = a; | 
|  | } | 
|  | } | 
|  | decorate(arr); | 
|  |  | 
|  | function decorate(e) { | 
|  | for(var i = 0; i < e.length; i++) { | 
|  | e[i].onmouseover = function (evt) { | 
|  | var element = this; | 
|  | // do nothing if the link icon is currently showing | 
|  | var a = element.firstChild; | 
|  | if (a != null && a instanceof Element | 
|  | && a.getAttribute('id') == 'LINK') { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // if there is no id there is no target to link to | 
|  | var id = element.getAttribute('id'); | 
|  | if (id == null) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // create and show a link icon that links to this element | 
|  | a = document.createElement('a'); | 
|  | a.setAttribute('id', 'LINK'); | 
|  | a.setAttribute('href', '#' + id); | 
|  | a.setAttribute('style', 'position: absolute;' | 
|  | + ' left: ' + (element.offsetLeft - 16 - 2 * 4) + 'px;' | 
|  | + ' padding-left: 4px; padding-right: 4px;'); | 
|  | var span = document.createElement('span'); | 
|  | span.setAttribute('style', 'height: ' + element.offsetHeight + 'px;' | 
|  | + ' display: inline-block; vertical-align: baseline;' | 
|  | + ' font-size: 16px; text-decoration: none; color: grey;'); | 
|  | a.appendChild(span); | 
|  | var link = document.createTextNode('🔗'); | 
|  | span.appendChild(link); | 
|  | element.insertBefore(a, element.firstChild); | 
|  |  | 
|  | // remove the link icon when the mouse is moved away, | 
|  | // but keep it shown if the mouse is over the element, the link or | 
|  | // the icon | 
|  | hide = function(evt) { | 
|  | if (document.elementFromPoint(evt.clientX, evt.clientY) != element | 
|  | && document.elementFromPoint(evt.clientX, evt.clientY) != a | 
|  | && document.elementFromPoint(evt.clientX, evt.clientY) != span | 
|  | && document.elementFromPoint(evt.clientX, evt.clientY) != link | 
|  | && element.contains(a)) { | 
|  | element.removeChild(a); | 
|  | } | 
|  | } | 
|  | element.onmouseout = hide; | 
|  | a.onmouseout = hide; | 
|  | span.onmouseout = hide; | 
|  | link.onmouseout = hide; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | function getExcludedIds() { | 
|  | var excluded = {}; | 
|  | excluded['header'] = true; | 
|  | excluded['toc'] = true; | 
|  | excluded['toctitle'] = true; | 
|  | excluded['content'] = true; | 
|  | excluded['preamble'] = true; | 
|  | excluded['footer'] = true; | 
|  | excluded['footer-text'] = true; | 
|  | return excluded; | 
|  | } | 
|  | </script> | 
|  |  | 
|  | ++++ | 
|  |  | 
|  | """ | 
|  |  | 
|  | opts = OptionParser() | 
|  | opts.add_option('-o', '--out', help='output file') | 
|  | opts.add_option('-s', '--src', help='source file') | 
|  | opts.add_option('-x', '--suffix', help='suffix for included filenames') | 
|  | opts.add_option('-b', '--searchbox', action="store_true", default=True, | 
|  | help="generate the search boxes") | 
|  | opts.add_option('--no-searchbox', action="store_false", dest='searchbox', | 
|  | help="don't generate the search boxes") | 
|  | options, _ = opts.parse_args() | 
|  |  | 
|  | try: | 
|  | try: | 
|  | out_file = open(options.out, 'w', errors='ignore') | 
|  | src_file = open(options.src, 'r', errors='ignore') | 
|  | except TypeError: | 
|  | out_file = open(options.out, 'w') | 
|  | src_file = open(options.src, 'r') | 
|  | last_line = '' | 
|  | ignore_next_line = False | 
|  | last_title = '' | 
|  | for line in src_file: | 
|  | if PAT_GERRIT.match(last_line): | 
|  | # Case of "GERRIT\n------" at the footer | 
|  | out_file.write(GERRIT_UPLINK) | 
|  | last_line = '' | 
|  | elif PAT_SEARCHBOX.match(last_line): | 
|  | # Case of 'SEARCHBOX\n---------' | 
|  | if options.searchbox: | 
|  | out_file.write(SEARCH_BOX) | 
|  | last_line = '' | 
|  | elif PAT_INCLUDE.match(line): | 
|  | # Case of 'include::<filename>' | 
|  | match = PAT_INCLUDE.match(line) | 
|  | out_file.write(last_line) | 
|  | last_line = match.group(1) + options.suffix + match.group(2) + '\n' | 
|  | elif PAT_STARS.match(line): | 
|  | if PAT_TITLE.match(last_line): | 
|  | # Case of the title in '.<title>\n****\nget::<url>\n****' | 
|  | match = PAT_TITLE.match(last_line) | 
|  | last_title = GET_TITLE % match.group(1) | 
|  | else: | 
|  | out_file.write(last_line) | 
|  | last_title = '' | 
|  | elif PAT_GET.match(line): | 
|  | # Case of '****\nget::<url>\n****' in rest api | 
|  | url = PAT_GET.match(line).group(1) | 
|  | out_file.write(GET_MACRO.format(url) % last_title) | 
|  | ignore_next_line = True | 
|  | elif ignore_next_line: | 
|  | # Handle the trailing '****' of the 'get::' case | 
|  | last_line = '' | 
|  | ignore_next_line = False | 
|  | else: | 
|  | out_file.write(last_line) | 
|  | last_line = line | 
|  | out_file.write(last_line) | 
|  | out_file.write(LINK_SCRIPT) | 
|  | out_file.close() | 
|  | except IOError as err: | 
|  | sys.stderr.write( | 
|  | "error while expanding %s to %s: %s" % (options.src, options.out, err)) | 
|  | exit(1) |