230731.1 · c8e28da12973f8844ac0457e2db6ccd56286b34a
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.
reading declarations
@@ -20,7 +20,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with:- python-version: '3.10'+ python-version: '3.11' cache: pipenv - name: Install pipenv run: pip install pipenv
@@ -197,14 +197,10 @@ endif ### Packaging -docs/site.yml: configure.ac- sed 's/^jq_version: .*/jq_version: "$(VERSION)"/' $@ > $@.new- mv $@.new $@- install-binaries: $(BUILT_SOURCES) $(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/build_mantests.py docs/build_website.py docs/README.md \ docs/validate_manual_schema.py docs/manual_schema.yml
@@ -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 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 then `source venv/bin/activate`, and only then `pipenv sync`. 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-run `pipenv run build_manpage.py` in the `docs` directory to build the-`jq.1` page manually, and `pipenv run build_mantests.py` to build the-contents of `tests/man.test` and `tests/manonig.test`.+the `Makefile` will be able to generate the jq manpage. You can just run+`make jq.1` to build the manpage manually, and `make tests/man.test` to+update the manual tests. -To build the website, run `pipenv run ./build_website.py` from inside-the `docs` directory.+To build the website, run `pipenv run python3 build_website.py --root /output`+in the `docs/` directory. To serve them locally, you can run+`python3 -m http.server`.
@@ -1,4 +1,5 @@ #!/usr/bin/env python3+import argparse import glob import itertools from jinja2 import Environment, FileSystemLoader, select_autoescape, pass_context@@ -10,6 +11,10 @@ import re import shutil import yaml +parser = argparse.ArgumentParser()+parser.add_argument('--root', default='/jq')+args = parser.parse_args()+ env = Environment( loader=FileSystemLoader('templates'), autoescape=select_autoescape(['html.j2']),@@ -21,40 +26,51 @@ def load_yml_file(fn): 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['section_id'] = lambda input: re.sub(r"[^a-zA-Z0-9_]", '', input)-env.filters['entry_id'] = lambda input: re.sub(r"[ `]", '', input)+env.filters['section_id'] = lambda input: re.sub(+ 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['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( 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)+++env.globals['raise'] = raise_handler -def generate_file(env, fname='content/1.tutorial/default.yml'):+def generate_file(env, fname): path, base = os.path.split(fname) path = os.path.relpath(path, 'content') if path == '.': path = ''- slug = 'index' permalink = '' else:- slug = os.path.basename(path) permalink = path + '/' output_dir = os.path.join('output', path) 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.update(unique_ctr=itertools.count(1), permalink=permalink,- slug=slug, navitem=path) os.makedirs(output_dir, exist_ok=True) env.get_template(template_name).stream(ctx).dump(output_path,@@ -72,6 +88,7 @@ def copy_public_files(root=''): shutil.copyfile(f.path, dst) +os.makedirs('output', exist_ok=True) copy_public_files() for fn in glob.glob('content/**/*.yml', recursive=True):
docs/content/download/default.yml
@@ -196,9 +196,9 @@ body: #### Building the documentation jq's documentation is compiled into static HTML using Python.- To build the docs, run `pipenv run python3 build_website.py` from- the docs/ subdirectory. To serve them locally, you can run- `python3 -m SimpleHTTPServer`. You'll need a few Python dependencies,+ To build the docs, run `pipenv run python3 build_website.py --root /output`+ in the `docs/` directory. To serve them locally, you can run+ `python3 -m http.server`. You'll need a few Python dependencies, 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
@@ -24,7 +24,7 @@ body3: | 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. Have a question related to jq? You can seek answers on [Stack Overflow](https://stackoverflow.com/)@@ -34,7 +34,7 @@ tail: | news: - date: 1 November 2018 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) for details. @@ -44,7 +44,7 @@ news: jq 1.5 released, including new datetime, math, and regexp functions, try/catch syntax, array and object destructuring, a streaming parser, 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) for details. @@ -63,7 +63,7 @@ news: - date: 09 June 2014 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 body: |
docs/content/manual/manual.yml
@@ -3,9 +3,8 @@ headline: jq Manual (development version) history: | - *For released versions, see [jq 1.6](/jq/manual/v1.6),- [jq 1.5](/jq/manual/v1.5), [jq 1.4](/jq/manual/v1.4)- or [jq 1.3](/jq/manual/v1.3).*+ *For released versions, see [jq 1.6](./v1.6/), [jq 1.5](./v1.5/),+ [jq 1.4](./v1.4/) or [jq 1.3](./v1.3/).* body: |
docs/content/manual/v1.3/manual.yml
@@ -4,7 +4,7 @@ headline: jq 1.3 Manual history: | *The manual for the development version of jq can be found- [here](/jq/manual).*+ [here](../).* body: |
docs/content/manual/v1.4/manual.yml
@@ -4,7 +4,7 @@ headline: jq 1.4 Manual history: | *The manual for the development version of jq can be found- [here](/jq/manual).*+ [here](../).* body: |
docs/content/manual/v1.5/manual.yml
@@ -4,7 +4,7 @@ headline: jq 1.5 Manual history: | *The manual for the development version of jq can be found- [here](/jq/manual).*+ [here](../).* body: |
docs/content/manual/v1.6/manual.yml
@@ -4,7 +4,7 @@ headline: jq 1.6 Manual history: | *The manual for the development version of jq can be found- [here](/jq/manual).*+ [here](../).* body: |
docs/public/css/base.css
@@ -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;-}
docs/public/css/base.scss
@@ -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;- }-}
@@ -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);+ }+ }+}
@@ -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>
docs/public/jq.png
@@ -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>
docs/public/js/manual-search.js
@@ -1,52 +1,34 @@-var section_names = function(q) {- if (!q) {- return [];- }- var matches = [];- q = q.toLowerCase();- $.each(section_map, function(k, v) {- if (k.toLowerCase().indexOf(q) != -1) {- matches.push(k);- }- });- matches.sort(function(a, b) {- // shortest to longest- return a.length - b.length;+(() => {+ const searchInput = document.querySelector('input#searchbox');+ const sectionIDs = JSON.parse(document.querySelector('#section-ids').innerText);+ const sanitize = (string) => string.replaceAll('<', '<').replaceAll('>', '>');+ 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));+ }+ }, });- 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();+ searchInput.addEventListener('selection', (event) => {+ event.target.value = event.detail.selection.value;+ location.hash = `#${sectionIDs[event.detail.selection.value]}`; });- $('#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>');+ 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();+ } });-});+})();
docs/site.yml
@@ -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'
docs/templates/default.html.j2
@@ -2,32 +2,29 @@ <html lang="en"> {% include "shared/_head.html.j2" %} - <body id="{{slug}}">+ <body> {% include "shared/_navbar.html.j2" %} - <div class="container">- <div class="row">- <h1>{{headline}}</h1>- {% for item in body %}- {% if item.text %}- {{ item.text | replace('$JQ_VERSION', jq_version) | markdownify }}- {% endif %}+ <main id="main" class="container-lg">+ <h1>{{ headline }}</h1>+ {%- for item in body %}+ {%- if item.text %}+ {{ item.text | markdownify }}+ {%- endif %} - {% if item.command %}- {% set resultID = unique_id() %}- <div class="tutorial-example">- <div class="accordion-heading">- <pre>{{item.command}}</pre>- <a data-toggle="collapse" href="#result{{resultID}}">Show result</a>- </div>- <div id="result{{resultID}}" class="accordion-body collapse">- <pre>{{item.result}}</pre>- </div>- </div>- {% endif %}- {% endfor %}+ {%- if item.command %}+ {%- set resultID = unique_id() %}+ <div class="tutorial-example mb-3">+ <div class="d-flex accordion-heading me-2">+ <pre class="flex-grow-1 me-0 mb-0" tabindex="0">{{ item.command }}</pre>+ <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"+ data-bs-toggle="collapse" data-bs-target="#result{{ resultID }}" aria-expanded="false" aria-controls="result{{ resultID }}">Show result</button>+ </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" %} </body>
@@ -2,71 +2,54 @@ <html lang="en"> {% include "shared/_head.html.j2" %} - <body id="{{ slug }}">+ <body> {% include "shared/_navbar.html.j2" %} - <div class="container">+ <main id="main" class="container-lg"> <div class="row">- <div class="jumbotron">- <div class="row">- <div class="col-sm-6">- <img src="{{root}}/jq.png" class="img-responsive" alt="jq logo"- width="400" height="220">- </div>- <div class="col-sm-6">- <div id="blurb">- {{blurb | markdownify}}-- <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- <span class="caret"></span>- </a>- <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 href="https://github.com/jqlang/jq/releases/download/jq-1.6/jq-osx-amd64">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 href="{{root}}/download/">Other platforms, older versions, and source</a></li>- </ul>- </div>-- <div class="btn-group">- <a class="btn btn-primary" href="https://jqplay.org">- Try online at jqplay.org!- </a>- </div>- </div>- </div>- </div>+ <div class="col-md-6 text-center p-3">+ <h1><img src="{{ root }}/jq.svg" class="img-fluid" alt="jq" width="400" height="220"></h1>+ </div>+ <div class="col-md-6 d-flex flex-column justify-content-center text-center align-items-center">+ <h2 class="px-1" style="width:16em">{{ blurb }}</h2>+ <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">+ Download jq 1.6+ <span class="caret"></span>+ </button>+ <ul class="dropdown-menu">+ <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 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 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 class="dropdown-item" href="{{ root }}/download/">Other platforms, older versions, and source</a></li>+ </ul>+ <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>+ </a> </div> </div> </div> - <div class="row" id="multiblurb">- <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 class="row my-3">+ <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 class="row" style="text-align:center; margin-top: 30px">- {{tail | markdownify}}+ <div class="text-center my-3">+ {{ tail | markdownify }} </div> - <div class="row">- <h2>News</h2>- <div id="news">- <ul>- {% for item in news %}- <li>- <span class="date">{{item.date}}</span>- {{item.body | markdownify}}- </li>- {% endfor %}- </ul>- </div>- </div>- </div>+ <h2>News</h2>+ <ul>+ {%- for item in news %}+ <li>+ <span class="fst-italic">{{ item.date }}</span>+ {{ item.body | markdownify }}+ </li>+ {%- endfor %}+ </ul>+ </main> {% include "shared/_footer.html.j2" %} </body>
@@ -2,98 +2,109 @@ <html lang="en"> {% include "shared/_head.html.j2" %} - <body id="{{slug}}" data-spy="scroll" data-target="#navcolumn" data-offset="100">+ <body> {% include "shared/_navbar.html.j2" %} - <div class="container">- <div class="row">- <div class="affix" id="navcolumn">+ <div class="container-lg row align-items-start mx-auto p-3">++ <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>- <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>+ <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>+ {{ history | markdownify }}+ {{ body | markdownify }}+ {%- for section in sections %}+ <section id="{{ section.title | section_id }}">{{ check_section_id(section.title | section_id) }}+ <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 }}+ {%- for entry in section.entries %}+ <section id="{{ entry.title | entry_id }}">{{ check_section_id(entry.title | entry_id) }}+ <h3>+ {{ entry.title | markdownify | no_paragraph }}+ <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>+ {{ entry.body | markdownify }}+ {%- if entry.examples %}+ <div class="pb-3">+ {%- 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 }}">+ <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 %}+ </button>+ <div id="example{{ exampleID }}" class="collapse mx-3 small d-print-block">+ {%- for example in entry.examples %}+ <table class="table table-borderless table-sm w-auto">+ <tr>+ <th class="pe-3">Command</th>+ <td class="font-monospace">jq '{{ example.program }}'</td>+ </tr>+ <tr>+ <th>Input</th>+ <td class="font-monospace">{{ example.input }}</td>+ </tr>+ {%- if not example.output[0] %}+ <tr>+ <th>Output</th>+ <td class="fst-italic">none</td>+ </tr>+ {%- endif %}+ {%- for output in example.output %}+ <tr>+ <th>{% if loop.first %}Output{% endif %}</th>+ <td class="font-monospace">{{ output }}</td>+ </tr>+ {%- endfor %}+ <tr class="d-print-none">+ <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>+ </table>+ {%- endfor %}+ </div>+ </div>+ {%- endif %}+ </section>+ {%- endfor %}+ </section>+ {%- endfor %}+ </main> - <div id="manualcontent">- <h1>{{headline}}</h1>- {{ history | markdownify }}- {{ body | markdownify }}- {% for section in sections %}- <section id="{{section.title | section_id}}">- <h2>{{section.title}}</h2>- {{ (section.body | markdownify) if section.body }}- {% for entry in section.entries %}- <section id="{{entry.title | entry_id}}">- <h3>- {{entry.title | markdownify | no_paragraph }}- {% if entry.subtitle %}<small>{{entry.subtitle}}</small>{% endif %}- </h3>- {{entry.body | markdownify}}-- {% if entry.examples %}- <div>- {% set exampleID = unique_id() %}- <a data-toggle="collapse" href="#example{{exampleID}}">- <i class="glyphicon glyphicon-chevron-right"></i>- {% if entry.examples | length > 1 %}Examples{%else%}Example{%endif%}- </a>- <div id="example{{exampleID}}" class="manual-example collapse">- {% for example in entry.examples %}- <table>- <tr><th></th><td class="jqprogram">jq '{{ example.program }}'</td></tr>- <tr><th>Input</th><td>{{ example.input }}</td></tr>- {% if not example.output[0] %}- <tr>- <th>Output</th>- <td><i>none</i></td>- </tr>- {% endif %}- {% for output in example.output %}- <tr>- {% if loop.first %}- <th>Output</th>- {% else %}- <th></th>- {% endif %}- <td>{{ output }}</td>- </tr>- {% endfor %}- </table>- {% endfor %}- </div>- </div>- {% endif %}- </section>- {% endfor %}- </section>- {% endfor %}- </div>- </div> </div> {% include "shared/_footer.html.j2" %}- <script>- var section_map = {- {% for section in sections %}- {% for entry in section.entries %}- {{entry.title | search_id | tojson }} : {{entry.title | entry_id | tojson }},- {% endfor %}- {{section.title | search_id | tojson }} : {{section.title | section_id | tojson }}- {% if not loop.last %},{% endif %}- {% endfor %}- };+ <script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/autoComplete.min.js"+ integrity="sha384-xbzjoN6H5XHmAqoSSR42hZVRninPGx85y+XZQGlWxVu2y91lTmr/oD80i5cjeUBv" crossorigin="anonymous"></script>+ <script id="section-ids" type="application/json">+ {+ {%- for section in sections %}+ {%- for entry in section.entries %}+ {{ entry.title | search_id | tojson }}: {{ entry.title | entry_id | tojson }},+ {%- endfor %}+ {{ section.title | search_id | tojson }}: {{ section.title | section_id | tojson }}{{ "," if not loop.last }}+ {%- endfor %}+ } </script>- <script src="{{root}}/js/manual-search.js"></script>+ <script src="{{ root }}/js/manual-search.js"></script> </body> </html>
docs/templates/shared/_footer.html.j2
@@ -1,10 +1,14 @@- <footer>- <div class="container">- <p>This website is made with <a href="http://getbootstrap.com">Bootstrap</a>, themed with <a href="https://bootswatch.com">Bootswatch</a>.</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>+ <footer class="bd-footer bg-body-tertiary">+ <div class="py-3 text-center">+ <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> </footer>-- <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></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>+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"+ integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script>
docs/templates/shared/_head.html.j2
@@ -1,17 +1,23 @@ <head>- <script>- if ((window.location.host == "jqlang.github.io") && (window.location.protocol != "https:"))- window.location.protocol = "https";- </script> <meta charset="utf-8">- <title>{{headline}}</title>+ <title>{{ headline }}</title> <meta name="viewport" content="width=device-width, initial-scale=1">- <meta http-equiv="X-UA-Compatible" content="IE=edge" />- <link rel="canonical" href="{{ url }}/{{ permalink }}" />- <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="stylesheet" href="{{ root }}/css/base.css" type="text/css">- <!--[if lt IE 9]>- <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>- <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>- <![endif]-->+ <link rel="icon" href="{{ root }}/icon.svg" type="image/svg+xml">+ <link rel="apple-touch-icon" href="{{ root }}/icon.png" type="image/png">+ <link rel="canonical" href="{{ url }}/{{ permalink }}">+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.1/dist/cosmo/bootstrap.min.css"+ integrity="sha384-dulfW0vmzZ638jigSgZXvDxMmd70GCnIv6oa+riKq6Kk4E0MKf7qmBfwP02wltv5" crossorigin="anonymous">+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css"+ integrity="sha384-Ay26V7L8bsJTsX9Sxclnvsn+hkdiwRnrjZJXqKmkIDobPgIIWBOVguEcQQLDuhfN" crossorigin="anonymous">+ <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>
docs/templates/shared/_navbar.html.j2
@@ -1,27 +1,40 @@- <div class="navbar navbar-default navbar-fixed-top" role="navigation">- <div class="container">- <div class="navbar-header">- <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#nav-collapse">- <span class="sr-only">Toggle navigation</span>- <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 class="navbar-collapse collapse" id="nav-collapse">- <ul class="nav navbar-nav">- {% for item in navigation %}- <li {% if item == navitem %} class="active" {% endif %}>- <a href="{{root}}/{{item}}/">{{item | capitalize}}</a>- </li>- {% endfor %}- <li><a href="https://github.com/jqlang/jq/issues">Issues</a></li>- <li><a href="https://github.com/jqlang/jq">Source</a></li>- <li><a href="https://jqplay.org">Try online!</a></li>- <li><a href="https://raw.githubusercontent.com/jqlang/jq/master/NEWS.md">News</a></li>+ <div class="container visually-hidden-focusable">+ <a class="d-inline-flex p-2" href="#main">Skip to main content</a>+ {%- if navitem.startswith('manual') %}+ <a class="d-inline-flex p-2 m-1" href="#contents">Skip to table of contents</a>+ {%- endif %}+ </div>+ <header class="navbar navbar-expand-md sticky-top bg-body-tertiary d-print-none">+ <nav class="container-lg" aria-label="Page navigation">+ <button type="button" class="navbar-toggler" data-bs-toggle="offcanvas" data-bs-target="#navbar-collapse"+ aria-controls="navbar-collapse" aria-expanded="false" aria-label="Toggle page navigation">+ <span class="navbar-toggler-icon"></span>+ </button>+ <a class="navbar-brand" href="{{ root }}/" aria-label="Top page"><img src="{{ root }}/jq.svg" alt="jq logo" style="height:1.5rem"></a>+ <div id="navbar-collapse" class="offcanvas offcanvas-start navbar-collapse w-auto" aria-labelledby="navbar-title">+ <div class="offcanvas-header">+ <h3 id="navbar-title" class="me-3">jq</h3>+ <button type="button" class="d-md-none btn-close" data-bs-dismiss="offcanvas" aria-label="Close page navigation"></button>+ </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> </div>- </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>+ {%- 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>