1
0
mirror of https://github.com/jqlang/jq.git synced 2025-11-16 18:22:22 +03:00

Redesign website (#2628)

* Bump up Bootstrap to v5.3.1, Bootstrap Icon to v1.10.5.
* Use autoComplete.js to drop dependency on jQuery and typeahead.js.
* Support dark mode.
* New svg logo and icon with responsive color mode support.
* Normalize section ids to lower kebab-case for easiness of linking.
* Use relative paths for links for local development (--root /output).
* Various markup cleanups and accessibility improvements.
This commit is contained in:
itchyny
2023-07-31 09:52:52 +09:00
committed by GitHub
parent 4af3f99728
commit c8e28da129
26 changed files with 402 additions and 656 deletions

View File

@@ -20,7 +20,7 @@ jobs:
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.10' python-version: '3.11'
cache: pipenv cache: pipenv
- name: Install pipenv - name: Install pipenv
run: pip install pipenv run: pip install pipenv

View File

@@ -197,14 +197,10 @@ endif
### Packaging ### Packaging
docs/site.yml: configure.ac
sed 's/^jq_version: .*/jq_version: "$(VERSION)"/' $@ > $@.new
mv $@.new $@
install-binaries: $(BUILT_SOURCES) install-binaries: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) install-exec $(MAKE) $(AM_MAKEFLAGS) install-exec
DOC_FILES = docs/content docs/public docs/templates docs/site.yml \ DOC_FILES = docs/content docs/public docs/templates \
docs/Pipfile docs/Pipfile.lock docs/build_manpage.py \ docs/Pipfile docs/Pipfile.lock docs/build_manpage.py \
docs/build_mantests.py docs/build_website.py docs/README.md \ docs/build_mantests.py docs/build_website.py docs/README.md \
docs/validate_manual_schema.py docs/manual_schema.yml docs/validate_manual_schema.py docs/manual_schema.yml

View File

@@ -11,16 +11,16 @@ need `python3` and `pipenv`. You can install `pipenv` like so:
Though, you may need to say `pip3` instead, depending on your system. Once Though, you may need to say `pip3` instead, depending on your system. Once
you have `pipenv` installed, you can install the dependencies by running you have `pipenv` installed, you can install the dependencies by running
`pipenv sync` from the `docs` directory. `pipenv sync` from the `docs/` directory.
Also, you may need to run `virtualenv -p /usr/bin/python3 venv/` and Also, you may need to run `virtualenv -p /usr/bin/python3 venv/` and
then `source venv/bin/activate`, and only then `pipenv sync`. then `source venv/bin/activate`, and only then `pipenv sync`.
Once this is done, rerun `./configure` in the jq root directory and then Once this is done, rerun `./configure` in the jq root directory and then
the Makefile will be able to generate the jq manpage. You can also just the `Makefile` will be able to generate the jq manpage. You can just run
run `pipenv run build_manpage.py` in the `docs` directory to build the `make jq.1` to build the manpage manually, and `make tests/man.test` to
`jq.1` page manually, and `pipenv run build_mantests.py` to build the update the manual tests.
contents of `tests/man.test` and `tests/manonig.test`.
To build the website, run `pipenv run ./build_website.py` from inside To build the website, run `pipenv run python3 build_website.py --root /output`
the `docs` directory. in the `docs/` directory. To serve them locally, you can run
`python3 -m http.server`.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse
import glob import glob
import itertools import itertools
from jinja2 import Environment, FileSystemLoader, select_autoescape, pass_context from jinja2 import Environment, FileSystemLoader, select_autoescape, pass_context
@@ -10,6 +11,10 @@ import re
import shutil import shutil
import yaml import yaml
parser = argparse.ArgumentParser()
parser.add_argument('--root', default='/jq')
args = parser.parse_args()
env = Environment( env = Environment(
loader=FileSystemLoader('templates'), loader=FileSystemLoader('templates'),
autoescape=select_autoescape(['html.j2']), autoescape=select_autoescape(['html.j2']),
@@ -21,40 +26,51 @@ def load_yml_file(fn):
return yaml.safe_load(f) return yaml.safe_load(f)
env.globals['url'] = 'https://jqlang.github.io/jq'
env.globals['root'] = args.root
env.filters['search_id'] = lambda input: input.replace(r'`', '') env.filters['search_id'] = lambda input: input.replace(r'`', '')
env.filters['section_id'] = lambda input: re.sub(r"[^a-zA-Z0-9_]", '', input) env.filters['section_id'] = lambda input: re.sub(
env.filters['entry_id'] = lambda input: re.sub(r"[ `]", '', input) r'[^-a-zA-Z0-9_]', '', input.replace(' ', '-')).lower()
env.filters['entry_id'] = lambda input: re.sub(
r'^(split|first-last-nth)$',
r'\1' + ('-1' if ';' not in input else '-2'), # avoid id conflict
re.sub(
r'\b([^-]+)(?:-\1)+\b',
r'\1', # e.g. range-range-range -> range
re.sub(r' ?/ ?|,? ', '-',
re.sub(r'[`;]|: .*|\(.*?\)| \[.+\]', '', input)))).lower()
env.filters['markdownify'] = lambda input: Markup(markdown(input)) env.filters['markdownify'] = lambda input: Markup(markdown(input))
env.filters['no_paragraph'] = lambda input: Markup(re.sub(r"</?p>", '', input)) env.filters['no_paragraph'] = lambda input: Markup(re.sub(r'</?p>', '', input))
env.globals['unique_id'] = pass_context( env.globals['unique_id'] = pass_context(
lambda ctx: str(next(ctx['unique_ctr']))) lambda ctx: str(next(ctx['unique_ctr'])))
env.globals.update(load_yml_file('site.yml'))
env.globals['navigation'] = ['tutorial', 'download', 'manual'] def raise_handler(message):
raise Exception(message)
def generate_file(env, fname='content/1.tutorial/default.yml'): env.globals['raise'] = raise_handler
def generate_file(env, fname):
path, base = os.path.split(fname) path, base = os.path.split(fname)
path = os.path.relpath(path, 'content') path = os.path.relpath(path, 'content')
if path == '.': if path == '.':
path = '' path = ''
slug = 'index'
permalink = '' permalink = ''
else: else:
slug = os.path.basename(path)
permalink = path + '/' permalink = path + '/'
output_dir = os.path.join('output', path) output_dir = os.path.join('output', path)
output_path = os.path.join(output_dir, 'index.html') output_path = os.path.join(output_dir, 'index.html')
template_name = re.sub(r".yml$", '.html.j2', base) template_name = re.sub(r'.yml$', '.html.j2', base)
ctx = load_yml_file(fname) ctx = load_yml_file(fname)
ctx.update(unique_ctr=itertools.count(1), ctx.update(unique_ctr=itertools.count(1),
permalink=permalink, permalink=permalink,
slug=slug,
navitem=path) navitem=path)
os.makedirs(output_dir, exist_ok=True) os.makedirs(output_dir, exist_ok=True)
env.get_template(template_name).stream(ctx).dump(output_path, env.get_template(template_name).stream(ctx).dump(output_path,
@@ -72,6 +88,7 @@ def copy_public_files(root=''):
shutil.copyfile(f.path, dst) shutil.copyfile(f.path, dst)
os.makedirs('output', exist_ok=True)
copy_public_files() copy_public_files()
for fn in glob.glob('content/**/*.yml', recursive=True): for fn in glob.glob('content/**/*.yml', recursive=True):

View File

@@ -196,9 +196,9 @@ body:
#### Building the documentation #### Building the documentation
jq's documentation is compiled into static HTML using Python. jq's documentation is compiled into static HTML using Python.
To build the docs, run `pipenv run python3 build_website.py` from To build the docs, run `pipenv run python3 build_website.py --root /output`
the docs/ subdirectory. To serve them locally, you can run in the `docs/` directory. To serve them locally, you can run
`python3 -m SimpleHTTPServer`. You'll need a few Python dependencies, `python3 -m http.server`. You'll need a few Python dependencies,
which can be installed by following the instructions in `docs/README.md`. which can be installed by following the instructions in `docs/README.md`.
The man page is built by `make jq.1`, or just `make`, also from The man page is built by `make jq.1`, or just `make`, also from

View File

@@ -24,7 +24,7 @@ body3: |
tail: | tail: |
Go read the [tutorial](/jq/tutorial/) for more, or the [manual](/jq/manual/) Go read the [tutorial](./tutorial/) for more, or the [manual](./manual/)
for *way* more. for *way* more.
Have a question related to jq? You can seek answers on [Stack Overflow](https://stackoverflow.com/) Have a question related to jq? You can seek answers on [Stack Overflow](https://stackoverflow.com/)
@@ -34,7 +34,7 @@ tail: |
news: news:
- date: 1 November 2018 - date: 1 November 2018
body: | body: |
jq 1.6 released. See installation options on the [download](/jq/download/) jq 1.6 released. See installation options on the [download](./download/)
page, and the [release notes](https://github.com/jqlang/jq/releases/tag/jq-1.6) page, and the [release notes](https://github.com/jqlang/jq/releases/tag/jq-1.6)
for details. for details.
@@ -44,7 +44,7 @@ news:
jq 1.5 released, including new datetime, math, and regexp functions, jq 1.5 released, including new datetime, math, and regexp functions,
try/catch syntax, array and object destructuring, a streaming parser, try/catch syntax, array and object destructuring, a streaming parser,
and a module system. See installation options on the and a module system. See installation options on the
[download](/jq/download/) page, and the [download](./download/) page, and the
[release notes](https://github.com/jqlang/jq/releases/tag/jq-1.5) [release notes](https://github.com/jqlang/jq/releases/tag/jq-1.5)
for details. for details.
@@ -63,7 +63,7 @@ news:
- date: 09 June 2014 - date: 09 June 2014
body: | body: |
jq 1.4 (finally) released! Get it on the [download](/jq/download/) page. jq 1.4 (finally) released! Get it on the [download](./download/) page.
- date: 19 May 2013 - date: 19 May 2013
body: | body: |

View File

@@ -3,9 +3,8 @@ headline: jq Manual (development version)
history: | history: |
*For released versions, see [jq 1.6](/jq/manual/v1.6), *For released versions, see [jq 1.6](./v1.6/), [jq 1.5](./v1.5/),
[jq 1.5](/jq/manual/v1.5), [jq 1.4](/jq/manual/v1.4) [jq 1.4](./v1.4/) or [jq 1.3](./v1.3/).*
or [jq 1.3](/jq/manual/v1.3).*
body: | body: |

View File

@@ -4,7 +4,7 @@ headline: jq 1.3 Manual
history: | history: |
*The manual for the development version of jq can be found *The manual for the development version of jq can be found
[here](/jq/manual).* [here](../).*
body: | body: |

View File

@@ -4,7 +4,7 @@ headline: jq 1.4 Manual
history: | history: |
*The manual for the development version of jq can be found *The manual for the development version of jq can be found
[here](/jq/manual).* [here](../).*
body: | body: |

View File

@@ -4,7 +4,7 @@ headline: jq 1.5 Manual
history: | history: |
*The manual for the development version of jq can be found *The manual for the development version of jq can be found
[here](/jq/manual).* [here](../).*
body: | body: |

View File

@@ -4,7 +4,7 @@ headline: jq 1.6 Manual
history: | history: |
*The manual for the development version of jq can be found *The manual for the development version of jq can be found
[here](/jq/manual).* [here](../).*
body: | body: |

View File

@@ -1,173 +0,0 @@
body {
padding-top: 80px;
}
.container {
max-width: 970px;
}
/* index.liquid *******************************************/
#blurb {
padding-top: 40px;
}
#blurb p {
font-size: 1.9em;
}
#blurb .btn-group {
margin: 4px;
}
#multiblurb {
line-height: 1.7;
text-align: center;
font-size: 12pt;
}
#multiblurb code {
border: 0;
font-size: 12pt;
}
#news {
font-size: 12pt;
}
#news .date {
font-style: italic;
}
/* default.liquid *****************************************/
.tutorial-example {
position: relative;
margin-bottom: 10px;
}
.tutorial-example pre {
margin-bottom: 0px;
}
.tutorial-example a {
position: absolute;
top: 0px;
right: 0px;
padding: 15px 8px;
color: #777777;
font-weight: bold;
line-height: 10px;
font-size: 12px;
border-left: 1px solid #DDDDDD;
display: block;
}
.tutorial-example .accordion-body pre {
margin: 0 4px;
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
@media print {
.tutorial-example a {
display: none;
}
}
/* manual.liquid ******************************************/
section {
padding-top: 24px;
}
h3 code {
border: 0;
font-size: 20px;
}
@media (max-width: 991px) {
#navcolumn {
/* Put nav column above manual content */
position: relative !important;
margin-bottom: 60px;
}
}
@media (min-width: 992px) {
#manualcontent {
/* Put nav column left of manual content */
padding-left: 280px;
}
}
.nav-pills {
margin-bottom: 20px;
}
.nav-pills li a {
padding: 8px 12px;
}
.manual-example table {
border-top: 1px solid #E5E5E5;
}
.manual-example table td {
white-space: pre-wrap;
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
}
.manual-example table td.jqprogram {
font-weight: bold;
}
.manual-example table th {
text-align: right;
padding-right: 10px;
}
@media print {
#navcolumn {
display: none !important;
}
.manual-example {
display: block !important;
height: auto !important;
}
.jqplay-btn {
display: none !important;
}
}
/* shared/_footer.liquid **********************************/
footer {
background-color: #F5F5F5;
padding: 20px 0;
margin-top: 40px;
color: #999999;
text-align: center;
}
footer p {
margin: 8px 0;
}
/* typeahead **********************************************/
.twitter-typeahead {
width: 100%;
}
.tt-menu {
width: 100%;
background-color: #fff;
padding: 8px 0;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
}
.tt-suggestion {
padding: 3px 20px;
}
.tt-suggestion:hover {
cursor: pointer;
color: #fff;
background-color: #446e9b;
}
.tt-suggestion.tt-cursor {
color: #fff;
background-color: #446e9b;
}
.tt-suggestion p {
margin: 0;
}

View File

@@ -1,181 +0,0 @@
@charset "utf-8";
body {
padding-top: 80px;
}
.container {
max-width: 970px;
}
/* index.liquid *******************************************/
#blurb {
padding-top: 40px;
p {
font-size: 1.9em;
}
.btn-group {
margin: 4px;
}
}
#multiblurb {
line-height: 1.7;
text-align: center;
font-size: 12pt;
code {
border: 0;
font-size: 12pt;
}
}
#news {
font-size: 12pt;
.date {
font-style: italic;
}
}
/* default.liquid *****************************************/
.tutorial-example {
position: relative;
margin-bottom: 10px;
pre {
margin-bottom: 0px;
}
a {
position: absolute;
top: 0px;
right: 0px;
padding: 15px 8px;
color: #777777;
font-weight: bold;
line-height: 10px;
font-size: 12px;
border-left: 1px solid #DDDDDD;
display: block;
}
.accordion-body pre {
margin: 0 4px;
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
}
@media print {
.tutorial-example a {
display: none;
}
}
/* manual.liquid ******************************************/
section {
padding-top: 24px;
}
h3 code {
border: 0;
font-size: 20px;
}
@media(max-width: 991px){
#navcolumn {
/* Put nav column above manual content */
position: relative !important;
margin-bottom: 60px;
}
}
@media(min-width: 992px) {
#manualcontent {
/* Put nav column left of manual content */
padding-left: 280px;
}
}
.nav-pills {
li a {
padding: 8px 12px;
}
margin-bottom: 20px;
}
.manual-example table {
border-top: 1px solid #E5E5E5;
td {
white-space: pre-wrap;
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
}
td.jqprogram {
font-weight: bold;
}
th {
text-align: right;
padding-right: 10px;
}
}
@media print {
#navcolumn {
display: none !important;
}
.manual-example {
display: block !important;
height: auto !important;
}
.jqplay-btn {
display: none !important;
}
}
/* shared/_footer.liquid **********************************/
footer {
background-color: #F5F5F5;
padding: 20px 0;
margin-top: 40px;
color: #999999;
text-align: center;
p {
margin: 8px 0;
}
}
/* typeahead **********************************************/
.twitter-typeahead {
width: 100%;
}
.tt-menu {
width: 100%;
background-color: #fff;
padding: 8px 0;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
}
.tt-suggestion {
padding: 3px 20px;
&:hover {
cursor: pointer;
color: #fff;
background-color: #446e9b;
}
&.tt-cursor {
color: #fff;
background-color: #446e9b;
}
p {
margin: 0;
}
}

99
docs/public/css/style.css Normal file
View File

@@ -0,0 +1,99 @@
main {
padding: 1rem;
& * {
scroll-margin-top: 4rem;
}
@media print {
width: 100%!important;
--bs-code-color: --bs-body-color;
}
}
header {
z-index: 1050!important; /* higher than #contents */
}
section[id] {
display: flow-root;
> :first-child {
.icon-link {
opacity: 0;
&:focus {
opacity: .8;
}
}
&:hover .icon-link {
opacity: 1;
}
}
}
.offcanvas[aria-modal=true] .nav-link {
padding: .7rem;
}
.offcanvas-md {
--bs-offcanvas-width: auto;
}
ul {
list-style: none;
padding-left: 1rem;
}
pre {
margin: 0 .5rem 1rem;
padding: .5rem 1rem;
background-color: var(--bs-secondary-bg-subtle);
border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color);
}
button{
&[aria-expanded=false] .bi-chevron-down {
display: none;
}
&[aria-expanded=true] .bi-chevron-right {
display: none;
}
}
mark {
padding: 0;
}
.container-searchbox {
position: relative;
& input:focus ~ kbd {
display: none;
}
& ul {
position: absolute;
width: 100%;
top: 100%;
padding: 0;
background-color: var(--bs-body-bg);
border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color);
}
& li {
padding: .3em .6em;
white-space: nowrap;
overflow-x: hidden;
&[aria-selected=true] {
background-color: var(--bs-secondary-bg);
}
&:hover {
cursor: pointer;
background-color: var(--bs-secondary-bg-subtle);
}
}
}

BIN
docs/public/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

1
docs/public/icon.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="48" height="48" version="1.0" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"><style>@media(prefers-color-scheme:dark){g[fill]{filter:invert(1)}}</style><g transform="matrix(.011 0 0 .011 1.94 12)"><g fill="#444"><circle cx="220" cy="1570" r="190"/><path d="m1330 312c-30 41-698 1492-710 1528s25 66 100 101c77 36 102 34 124 3s701-1477 716-1525c15-47-20-73-84-104s-116-44-146-3z"/></g><g fill="#111"><circle cx="2410" cy="200" r="190"/><path d="m1830 655c0 129 0 130 70 130h360v423c0 246-5 474-10 506-19 112-88 176-190 176-59 0-120-27-147-66-51-73-65-84-99-84-31 0-40 8-104 93-84 111-86 129-32 189 227 252 692 187 836-118 53-112 56-153 56-795v-514c0-64-30-100-100-100h-490c-149 0-150 0-150 160zM2867 1233c30 302 250 499 438 527 144 19 244 0 375-90v410c0 81 0 100 150 100 136 0 150-20 150-150v-1420c0-99-19-100-150-100s-139 15-139 82c-103-89-351-149-523-49s-342 278-301 690zm803-107c0 292-105 356-237 356-178 0-249-147-273-296-27-168 25-420 262-420 171 0 248 89 248 360z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

1
docs/public/jq.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="400" height="220" version="1.0" viewBox="0 0 400 220" xmlns="http://www.w3.org/2000/svg"><style>@media (prefers-color-scheme:dark){g[fill]{filter:invert(1);stroke:none}}</style><g transform="scale(.1)" stroke="#fff" stroke-width="10"><g fill="#444"><circle cx="220" cy="1570" r="190"/><path d="m1330 312c-30 41-698 1492-710 1528s25 66 100 101c77 36 102 34 124 3s701-1477 716-1525c15-47-20-73-84-104s-116-44-146-3z"/></g><g fill="#111"><circle cx="2410" cy="200" r="190"/><path d="m1830 655c0 129 0 130 70 130h360v423c0 246-5 474-10 506-19 112-88 176-190 176-59 0-120-27-147-66-51-73-65-84-99-84-31 0-40 8-104 93-84 111-86 129-32 189 227 252 692 187 836-118 53-112 56-153 56-795v-514c0-64-30-100-100-100h-490c-149 0-150 0-150 160zM2867 1233c30 302 250 499 438 527 144 19 244 0 375-90v410c0 81 0 100 150 100 136 0 150-20 150-150v-1420c0-99-19-100-150-100s-139 15-139 82c-103-89-351-149-523-49s-342 278-301 690zm803-107c0 292-105 356-237 356-178 0-249-147-273-296-27-168 25-420 262-420 171 0 248 89 248 360z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,52 +1,34 @@
var section_names = function(q) { (() => {
if (!q) { const searchInput = document.querySelector('input#searchbox');
return []; const sectionIDs = JSON.parse(document.querySelector('#section-ids').innerText);
const sanitize = (string) => string.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
new autoComplete({
selector: `#${searchInput.id}`,
wrapper: false,
data: {
src: Object.keys(sectionIDs),
filter: (list) => list.sort((x, y) =>
x.match.indexOf('<') - y.match.indexOf('<') || x.value.length - y.value.length),
},
searchEngine: (query, value) => {
const index = value.toLowerCase().indexOf(query.toLowerCase());
if (index >= 0) {
return sanitize(value.substring(0, index)) +
`<mark>${sanitize(value.substring(index, index + query.length))}</mark>` +
sanitize(value.substring(index + query.length));
} }
var matches = []; },
q = q.toLowerCase(); });
$.each(section_map, function(k, v) { searchInput.addEventListener('selection', (event) => {
if (k.toLowerCase().indexOf(q) != -1) { event.target.value = event.detail.selection.value;
matches.push(k); location.hash = `#${sectionIDs[event.detail.selection.value]}`;
});
document.addEventListener('keydown', (event) => {
if (event.code === 'Slash' && !event.altKey && !event.ctrlKey && !event.metaKey
&& !event.shiftKey && !/^(INPUT|TEXTAREA)$/.test(event.target.nodeName)) {
searchInput.focus();
searchInput.select();
event.preventDefault();
} }
}); });
matches.sort(function(a, b) { })();
// shortest to longest
return a.length - b.length;
});
return matches;
}
var section_names_cb = function(q, cb) {
cb(section_names(q));
}
var go_to_section = function() {
query = $('#searchbox').val();
results = section_names(query);
if (results.length == 0) {
return;
}
result = results[0];
location.hash = '#' + section_map[result];
if (result != query) {
$('#searchbox').val(result);
}
}
$(function(){
$('#searchbox').typeahead(
{hint: false, highlight: true, minLength: 1},
{name: "contents", source: section_names_cb, limit: 6}
).on('typeahead:selected', function(e, data) {
go_to_section();
});
$('#searchbox').change(go_to_section);
});
// add "Run" button to execute examples on jqplay.org
$(function() {
$.each($('.manual-example table'), function(index, value) {
$value = $(value)
var j = $value.find('tr:nth-child(2) td:first').text();
var q = $value.find('.jqprogram').text().replace(/^jq /, '').replace(/(\r\n|\n|\r)/gm," ").replace(/^'(.+)'$/, '$1');
var url = 'https://jqplay.org/jq?q=' + encodeURIComponent(q) +'&j=' + encodeURIComponent(j)
var $last_tr = $value.find('tr:last');
$last_tr.after('<tr class="jqplay-btn"><th><a href="' + url + '" class="btn btn-primary btn-sm">Run</a></th><th></th></tr><tr><th></th><th></th></tr>');
});
});

View File

@@ -1,9 +0,0 @@
# The key value pairs found below are available within the templates.
url: https://jqlang.github.io/jq
# This line is modified by the Makefile. To change the version number,
# edit the Autoconf version number at the top of configure.ac
jq_version: "1.6-159-gcff5336-dirty"
root: '/jq'

View File

@@ -2,32 +2,29 @@
<html lang="en"> <html lang="en">
{% include "shared/_head.html.j2" %} {% include "shared/_head.html.j2" %}
<body id="{{slug}}"> <body>
{% include "shared/_navbar.html.j2" %} {% include "shared/_navbar.html.j2" %}
<div class="container"> <main id="main" class="container-lg">
<div class="row">
<h1>{{ headline }}</h1> <h1>{{ headline }}</h1>
{% for item in body %} {%- for item in body %}
{% if item.text %} {%- if item.text %}
{{ item.text | replace('$JQ_VERSION', jq_version) | markdownify }} {{ item.text | markdownify }}
{% endif %} {%- endif %}
{% if item.command %} {%- if item.command %}
{% set resultID = unique_id() %} {%- set resultID = unique_id() %}
<div class="tutorial-example"> <div class="tutorial-example mb-3">
<div class="accordion-heading"> <div class="d-flex accordion-heading me-2">
<pre>{{item.command}}</pre> <pre class="flex-grow-1 me-0 mb-0" tabindex="0">{{ item.command }}</pre>
<a data-toggle="collapse" href="#result{{resultID}}">Show result</a> <button type="button" class="btn btn-sm btn-secondary text-body-secondary bg-secondary-subtle link-body-emphasis flex-shrink-0 d-flex align-items-center border border-start-0 d-print-none"
</div> data-bs-toggle="collapse" data-bs-target="#result{{ resultID }}" aria-expanded="false" aria-controls="result{{ resultID }}">Show result</button>
<div id="result{{resultID}}" class="accordion-body collapse">
<pre>{{item.result}}</pre>
</div>
</div>
{% endif %}
{% endfor %}
</div> </div>
<pre id="result{{ resultID }}" class="accordion-body collapse p-3 border-top-0 d-print-block" tabindex="0">{{ item.result }}</pre>
</div> </div>
{%- endif %}
{%- endfor %}
</main>
{% include "shared/_footer.html.j2" %} {% include "shared/_footer.html.j2" %}
</body> </body>

View File

@@ -2,71 +2,54 @@
<html lang="en"> <html lang="en">
{% include "shared/_head.html.j2" %} {% include "shared/_head.html.j2" %}
<body id="{{ slug }}"> <body>
{% include "shared/_navbar.html.j2" %} {% include "shared/_navbar.html.j2" %}
<div class="container"> <main id="main" class="container-lg">
<div class="row"> <div class="row">
<div class="jumbotron"> <div class="col-md-6 text-center p-3">
<div class="row"> <h1><img src="{{ root }}/jq.svg" class="img-fluid" alt="jq" width="400" height="220"></h1>
<div class="col-sm-6">
<img src="{{root}}/jq.png" class="img-responsive" alt="jq logo"
width="400" height="220">
</div> </div>
<div class="col-sm-6"> <div class="col-md-6 d-flex flex-column justify-content-center text-center align-items-center">
<div id="blurb"> <h2 class="px-1" style="width:16em">{{ blurb }}</h2>
{{blurb | markdownify}} <div class="btn-group d-print-none" role="group">
<button type="button" class="btn btn-primary dropdown-toggle text-nowrap" data-bs-toggle="dropdown" aria-expanded="false">
<div class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group">
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
Download jq 1.6 Download jq 1.6
<span class="caret"></span> <span class="caret"></span>
</a> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-linux64">Linux (64-bit)</a></li> <li><a class="dropdown-item" href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-linux64" aria-label="Link to download executable: Linux (64-bit)"><span class="bi bi-download me-2" aria-hidden="true"></span>Linux (64-bit)</a></li>
<li><a href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-osx-amd64">macOS (64-bit)</a></li> <li><a class="dropdown-item" href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-osx-amd64" aria-label="Link to download executable: macOS (64-bit)"><span class="bi bi-download me-2" aria-hidden="true"></span>macOS (64-bit)</a></li>
<li><a href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-win64.exe">Windows (64-bit)</a></li> <li><a class="dropdown-item" href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-win64.exe" aria-label="Link to download executable: Windows (64-bit)"><span class="bi bi-download me-2" aria-hidden="true"></span>Windows (64-bit)</a></li>
<li><a href="{{root}}/download/">Other platforms, older versions, and source</a></li> <li><a class="dropdown-item" href="{{ root }}/download/">Other platforms, older versions, and source</a></li>
</ul> </ul>
</div> <a class="btn btn-primary text-nowrap" href="https://jqplay.org" target="_blank" rel="noopener">
Try online at jqplay.org!<span class="bi bi-box-arrow-up-right ms-1" aria-hidden="true"></span>
<div class="btn-group">
<a class="btn btn-primary" href="https://jqplay.org">
Try online at jqplay.org!
</a> </a>
</div> </div>
</div> </div>
</div> </div>
</div>
</div> <div class="row my-3">
</div> <div class="col-md-4">{{ body1 | markdownify }}</div>
<div class="col-md-4">{{ body2 | markdownify }}</div>
<div class="col-md-4">{{ body3 | markdownify }}</div>
</div> </div>
<div class="row" id="multiblurb"> <div class="text-center my-3">
<div class="col-sm-4">{{body1 | markdownify}}</div>
<div class="col-sm-4">{{body2 | markdownify}}</div>
<div class="col-sm-4">{{body3 | markdownify}}</div>
</div>
<div class="row" style="text-align:center; margin-top: 30px">
{{ tail | markdownify }} {{ tail | markdownify }}
</div> </div>
<div class="row">
<h2>News</h2> <h2>News</h2>
<div id="news">
<ul> <ul>
{% for item in news %} {%- for item in news %}
<li> <li>
<span class="date">{{item.date}}</span> <span class="fst-italic">{{ item.date }}</span>
{{ item.body | markdownify }} {{ item.body | markdownify }}
</li> </li>
{% endfor %} {%- endfor %}
</ul> </ul>
</div> </main>
</div>
</div>
{% include "shared/_footer.html.j2" %} {% include "shared/_footer.html.j2" %}
</body> </body>

View File

@@ -2,97 +2,108 @@
<html lang="en"> <html lang="en">
{% include "shared/_head.html.j2" %} {% include "shared/_head.html.j2" %}
<body id="{{slug}}" data-spy="scroll" data-target="#navcolumn" data-offset="100"> <body>
{% include "shared/_navbar.html.j2" %} {% include "shared/_navbar.html.j2" %}
<div class="container"> <div class="container-lg row align-items-start mx-auto p-3">
<div class="row">
<div class="affix" id="navcolumn">
<h4>Contents</h4>
<ul class="nav nav-pills nav-stacked">
{% for section in sections %}
<li>
<a href="#{{section.title | section_id}}">{{section.title}}</a>
</li>
{% endfor %}
</ul>
<form class="form-group" onsubmit="go_to_section(); return false;">
<input type="text"
class="form-control"
placeholder="Search"
autocomplete="off"
id="searchbox">
</form>
</div>
<div id="manualcontent"> <button type="button" class="d-md-none w-auto position-fixed bottom-0 end-0 p-2 m-3 bg-body-secondary border-0 text-body d-print-none"
data-bs-toggle="offcanvas" data-bs-target="#contents" aria-controls="contents" aria-expanded="false" aria-label="Toggle table of contents">
<span class="bi bi-list" aria-hidden="true"></span>
</button>
<nav id="contents" class="col-md-3 sticky-md-top p-3 overflow-y-auto offcanvas-md offcanvas-end d-print-none" style="top:4.5rem; height:calc(100dvh - 5.5rem);" aria-label="Table of contents">
<div class="d-flex justify-content-between">
<h4>Contents</h4>
<button type="button" class="btn-close d-md-none" data-bs-dismiss="offcanvas" data-bs-target="#contents" aria-label="Close table of contents"></button>
</div>
<ul class="offcanvas-md-body nav nav-pills flex-column">
{%- for section in sections %}
<li class="nav-item" data-bs-dismiss="offcanvas" data-bs-target="#contents">
<a class="nav-link" href="#{{ section.title | section_id }}">{{ section.title }}</a>
</li>
{%- endfor %}
</ul>
</nav>
{%- set section_ids = {} %}
{%- macro check_section_id(id) -%}
{%- if section_ids.__contains__(id) %}
{{- raise('Duplicate section id: ' ~ id) }}
{%- endif %}
{%- set _ = section_ids.__setitem__(id, true) %}
{%- endmacro %}
<main id="main" class="col-md-9" data-bs-spy="scroll" data-bs-target="#contents" data-bs-threshold="0,1" data-bs-root-margin="-30% 0% -70%">
<h1>{{ headline }}</h1> <h1>{{ headline }}</h1>
{{ history | markdownify }} {{ history | markdownify }}
{{ body | markdownify }} {{ body | markdownify }}
{% for section in sections %} {%- for section in sections %}
<section id="{{section.title | section_id}}"> <section id="{{ section.title | section_id }}">{{ check_section_id(section.title | section_id) }}
<h2>{{section.title}}</h2> <h2>{{ section.title }} <a href="#{{ section.title | section_id }}" class="icon-link" aria-label="Link to this section: {{ section.title }}"><span class="bi bi-link-45deg" aria-hidden="true"></span></a></h2>
{{ (section.body | markdownify) if section.body }} {{ section.body | markdownify if section.body }}
{% for entry in section.entries %} {%- for entry in section.entries %}
<section id="{{entry.title | entry_id}}"> <section id="{{ entry.title | entry_id }}">{{ check_section_id(entry.title | entry_id) }}
<h3> <h3>
{{ entry.title | markdownify | no_paragraph }} {{ entry.title | markdownify | no_paragraph }}
{% if entry.subtitle %}<small>{{entry.subtitle}}</small>{% endif %} <a href="#{{ entry.title | entry_id }}" class="icon-link" aria-label="Link to this section: {{ entry.title }}"><span class="bi bi-link-45deg" aria-hidden="true"></span></a>
</h3> </h3>
{{ entry.body | markdownify }} {{ entry.body | markdownify }}
{%- if entry.examples %}
{% if entry.examples %} <div class="pb-3">
<div> {%- set exampleID = unique_id() %}
{% set exampleID = unique_id() %} <button type="button" class="btn btn-sm btn-secondary text-body-secondary bg-transparent link-body-emphasis border-0" data-bs-toggle="collapse" data-bs-target="#example{{ exampleID }}" aria-expanded="false" aria-controls="example{{ exampleID }}">
<a data-toggle="collapse" href="#example{{exampleID}}"> <span class="me-1 d-print-none" aria-hidden="true"><span class="bi bi-chevron-right"></span><span class="bi bi-chevron-down"></span></span>Example{% if entry.examples | length > 1 %}s{% endif %}
<i class="glyphicon glyphicon-chevron-right"></i> </button>
{% if entry.examples | length > 1 %}Examples{%else%}Example{%endif%} <div id="example{{ exampleID }}" class="collapse mx-3 small d-print-block">
</a> {%- for example in entry.examples %}
<div id="example{{exampleID}}" class="manual-example collapse"> <table class="table table-borderless table-sm w-auto">
{% for example in entry.examples %} <tr>
<table> <th class="pe-3">Command</th>
<tr><th></th><td class="jqprogram">jq '{{ example.program }}'</td></tr> <td class="font-monospace">jq '{{ example.program }}'</td>
<tr><th>Input</th><td>{{ example.input }}</td></tr> </tr>
{% if not example.output[0] %} <tr>
<th>Input</th>
<td class="font-monospace">{{ example.input }}</td>
</tr>
{%- if not example.output[0] %}
<tr> <tr>
<th>Output</th> <th>Output</th>
<td><i>none</i></td> <td class="fst-italic">none</td>
</tr> </tr>
{% endif %} {%- endif %}
{% for output in example.output %} {%- for output in example.output %}
<tr> <tr>
{% if loop.first %} <th>{% if loop.first %}Output{% endif %}</th>
<th>Output</th> <td class="font-monospace">{{ output }}</td>
{% else %} </tr>
<th></th> {%- endfor %}
{% endif %} <tr class="d-print-none">
<td>{{ output }}</td> <th><a href="https://jqplay.org/jq?q={{ example.program | urlencode }}&j={{ example.input | urlencode }}" class="btn btn-outline-primary btn-sm" target="_blank" rel="noopener">Run<span class="bi bi-box-arrow-up-right ms-2" aria-hidden="true"></span></a></th>
<td></td>
</tr> </tr>
{% endfor %}
</table> </table>
{% endfor %} {%- endfor %}
</div> </div>
</div> </div>
{% endif %} {%- endif %}
</section> </section>
{% endfor %} {%- endfor %}
</section> </section>
{% endfor %} {%- endfor %}
</div> </main>
</div>
</div> </div>
{% include "shared/_footer.html.j2" %} {% include "shared/_footer.html.j2" %}
<script> <script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/autoComplete.min.js"
var section_map = { integrity="sha384-xbzjoN6H5XHmAqoSSR42hZVRninPGx85y+XZQGlWxVu2y91lTmr/oD80i5cjeUBv" crossorigin="anonymous"></script>
{% for section in sections %} <script id="section-ids" type="application/json">
{% for entry in section.entries %} {
{%- for section in sections %}
{%- for entry in section.entries %}
{{ entry.title | search_id | tojson }}: {{ entry.title | entry_id | tojson }}, {{ entry.title | search_id | tojson }}: {{ entry.title | entry_id | tojson }},
{% endfor %} {%- endfor %}
{{section.title | search_id | tojson }} : {{section.title | section_id | tojson }} {{ section.title | search_id | tojson }}: {{ section.title | section_id | tojson }}{{ "," if not loop.last }}
{% if not loop.last %},{% endif %} {%- endfor %}
{% endfor %} }
};
</script> </script>
<script src="{{ root }}/js/manual-search.js"></script> <script src="{{ root }}/js/manual-search.js"></script>
</body> </body>

View File

@@ -1,10 +1,14 @@
<footer> <footer class="bd-footer bg-body-tertiary">
<div class="container"> <div class="py-3 text-center">
<p>This website is made with <a href="http://getbootstrap.com">Bootstrap</a>, themed with <a href="https://bootswatch.com">Bootswatch</a>.</p> <p>
<p>jq is licensed under the MIT license (code) and the <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY-3.0</a> license (docs).</p> This website is made with <a href="https://getbootstrap.com" target="_blank" rel="noopener">Bootstrap</a>,
themed with <a href="https://bootswatch.com" target="_blank" rel="noopener">Bootswatch</a>.
</p>
<p>
jq is licensed under the MIT license (code) and the
<a href="https://creativecommons.org/licenses/by/3.0/" target="_blank" rel="noopener">CC-BY-3.0</a> license (docs).
</p>
</div> </div>
</footer> </footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js" integrity="sha256-Sk3nkD6mLTMOF0EOpNtsIry+s1CsaqQC1rVLTAy+0yc= sha512-K1qjQ+NcF2TYO/eI3M6v8EiNYZfA95pQumfvcVrTHtwQVDG+aHRqLi/ETn2uB+1JqwYqVG3LIvdm9lj6imS/pQ==" crossorigin="anonymous"></script>
<script src="https://twitter.github.io/typeahead.js/releases/0.11.1/typeahead.bundle.js"></script>

View File

@@ -1,17 +1,23 @@
<head> <head>
<script>
if ((window.location.host == "jqlang.github.io") && (window.location.protocol != "https:"))
window.location.protocol = "https";
</script>
<meta charset="utf-8"> <meta charset="utf-8">
<title>{{ headline }}</title> <title>{{ headline }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <link rel="icon" href="{{ root }}/icon.svg" type="image/svg+xml">
<link rel="canonical" href="{{ url }}/{{ permalink }}" /> <link rel="apple-touch-icon" href="{{ root }}/icon.png" type="image/png">
<link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.5/spacelab/bootstrap.min.css" rel="stylesheet" integrity="sha256-j7Dtnd7ZjexEiPNbscbopFn9+Cs0b3TLipKsWAPHZIM= sha512-RFhfi6P8zWMAJrEGU+CPjuxPh3r/UUBGqQ+/o6WKPIVZmQqeOipGotH2ihRULuQ8wsMBoK15TSZqc/7VYWyuIw==" crossorigin="anonymous"> <link rel="canonical" href="{{ url }}/{{ permalink }}">
<link rel="stylesheet" href="{{ root }}/css/base.css" type="text/css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.1/dist/cosmo/bootstrap.min.css"
<!--[if lt IE 9]> integrity="sha384-dulfW0vmzZ638jigSgZXvDxMmd70GCnIv6oa+riKq6Kk4E0MKf7qmBfwP02wltv5" crossorigin="anonymous">
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css"
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> integrity="sha384-Ay26V7L8bsJTsX9Sxclnvsn+hkdiwRnrjZJXqKmkIDobPgIIWBOVguEcQQLDuhfN" crossorigin="anonymous">
<![endif]--> <link rel="stylesheet" href="{{ root }}/css/style.css" type="text/css">
<script>
(function() {
function setTheme(mediaQuery) {
document.documentElement.setAttribute('data-bs-theme',
document.documentElement.style.colorScheme = mediaQuery.matches ? 'dark' : 'light');
}
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
setTheme(mediaQuery); mediaQuery.addEventListener('change', setTheme);
})();
</script>
</head> </head>

View File

@@ -1,27 +1,40 @@
<div class="navbar navbar-default navbar-fixed-top" role="navigation"> <div class="container visually-hidden-focusable">
<div class="container"> <a class="d-inline-flex p-2" href="#main">Skip to main content</a>
<div class="navbar-header"> {%- if navitem.startswith('manual') %}
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#nav-collapse"> <a class="d-inline-flex p-2 m-1" href="#contents">Skip to table of contents</a>
<span class="sr-only">Toggle navigation</span> {%- endif %}
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{root}}/">jq</a>
</div> </div>
<header class="navbar navbar-expand-md sticky-top bg-body-tertiary d-print-none">
<div class="navbar-collapse collapse" id="nav-collapse"> <nav class="container-lg" aria-label="Page navigation">
<ul class="nav navbar-nav"> <button type="button" class="navbar-toggler" data-bs-toggle="offcanvas" data-bs-target="#navbar-collapse"
{% for item in navigation %} aria-controls="navbar-collapse" aria-expanded="false" aria-label="Toggle page navigation">
<li {% if item == navitem %} class="active" {% endif %}> <span class="navbar-toggler-icon"></span>
<a href="{{root}}/{{item}}/">{{item | capitalize}}</a> </button>
</li> <a class="navbar-brand" href="{{ root }}/" aria-label="Top page"><img src="{{ root }}/jq.svg" alt="jq logo" style="height:1.5rem"></a>
{% endfor %} <div id="navbar-collapse" class="offcanvas offcanvas-start navbar-collapse w-auto" aria-labelledby="navbar-title">
<li><a href="https://github.com/jqlang/jq/issues">Issues</a></li> <div class="offcanvas-header">
<li><a href="https://github.com/jqlang/jq">Source</a></li> <h3 id="navbar-title" class="me-3">jq</h3>
<li><a href="https://jqplay.org">Try online!</a></li> <button type="button" class="d-md-none btn-close" data-bs-dismiss="offcanvas" aria-label="Close page navigation"></button>
<li><a href="https://raw.githubusercontent.com/jqlang/jq/master/NEWS.md">News</a></li> </div>
<ul class="offcanvas-body navbar-nav me-auto text-nowrap">
<li class="nav-item d-md-none"><a class="nav-link{% if not navitem %} active{% endif %}"{% if not navitem %} aria-current="page"{% endif %} href="{{ root }}/">Top page</a></li>
{%- for item in ['tutorial', 'download', 'manual'] %}
<li class="nav-item"><a class="nav-link{% if item == navitem %} active{% endif %}"{% if item == navitem %} aria-current="page"{% endif %} href="{{ root }}/{{ item }}/">{{ item | capitalize }}</a></li>
{%- endfor %}
<li class="nav-item"><a class="nav-link" href="https://github.com/jqlang/jq" target="_blank" rel="noopener">GitHub</a></li>
<li class="nav-item"><a class="nav-link" href="https://github.com/jqlang/jq/issues" target="_blank" rel="noopener">Issues</a></li>
<li class="nav-item"><a class="nav-link" href="https://jqplay.org" target="_blank" rel="noopener">Try online!</a></li>
<li class="nav-item"><a class="nav-link" href="https://raw.githubusercontent.com/jqlang/jq/master/NEWS.md" target="_blank" rel="noopener">News</a></li>
</ul> </ul>
</div> </div>
{%- if navitem.startswith('manual') %}
<div class="container-searchbox form-control p-0 d-none d-md-flex d-print-none border" style="width:16rem">
<span class="bi bi-search my-auto ms-2 me-1" aria-hidden="true"></span>
<input type="text" id="searchbox" class="form-control border-0 px-1" placeholder="Search manual" role="combobox"
aria-label="Search manual" aria-keyshortcuts="/" aria-expanded="false" autocomplete="off" spellcheck="false">
<kbd class="mx-1 my-auto text-body bg-body rounded border" style="padding:.1rem .2rem" aria-hidden="true">/</kbd>
</div> </div>
</div> {%- endif %}
<a class="d-md-none bi bi-github fs-4 text-body" href="https://github.com/jqlang/jq" target="_blank" rel="noopener" aria-label="GitHub"></a>
</nav>
</header>