diff --git a/.pnp.cjs b/.pnp.cjs index 375e2a21e..4db9233f5 100644 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -42,6 +42,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@fullcalendar/luxon2", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.11.3"],\ ["@fullcalendar/timegrid", "npm:5.11.3"],\ ["@fullcalendar/vue3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.11.3"],\ + ["@parcel/optimizer-data-url", "npm:2.8.0"],\ + ["@parcel/transformer-inline-string", "npm:2.8.0"],\ ["@parcel/transformer-sass", "npm:2.8.0"],\ ["@popperjs/core", "npm:2.11.6"],\ ["@rollup/pluginutils", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.0.2"],\ @@ -52,7 +54,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["browser-fs-access", "npm:0.31.1"],\ ["browserlist", "npm:1.0.1"],\ ["c8", "npm:7.12.0"],\ - ["caniuse-lite", "npm:1.0.30001434"],\ + ["caniuse-lite", "npm:1.0.30001435"],\ ["d3", "npm:7.6.1"],\ ["eslint", "npm:8.28.0"],\ ["eslint-config-standard", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:17.0.0"],\ @@ -63,7 +65,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["eslint-plugin-promise", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.1"],\ ["eslint-plugin-vue", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:9.8.0"],\ ["file-saver", "npm:2.0.5"],\ - ["highcharts", "npm:10.3.1"],\ + ["highcharts", "npm:10.3.2"],\ ["html-validate", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:7.10.0"],\ ["jquery", "npm:3.6.1"],\ ["jquery-migrate", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:3.4.0"],\ @@ -72,14 +74,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["list.js", "npm:2.3.1"],\ ["lodash", "npm:4.17.21"],\ ["lodash-es", "npm:4.17.21"],\ - ["luxon", "npm:3.1.0"],\ + ["luxon", "npm:3.1.1"],\ ["moment", "npm:2.29.4"],\ ["moment-timezone", "npm:0.5.39"],\ ["ms", "npm:2.1.3"],\ ["murmurhash-js", "npm:1.0.0"],\ ["naive-ui", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.34.2"],\ ["parcel", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.8.0"],\ - ["pinia", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.0.26"],\ + ["pinia", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.0.27"],\ ["pinia-plugin-persist", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:1.0.0"],\ ["pug", "npm:3.0.2"],\ ["sass", "npm:1.56.1"],\ @@ -337,7 +339,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@fullcalendar/luxon2", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.11.3"],\ ["@fullcalendar/common", "npm:5.11.3"],\ ["@types/luxon", null],\ - ["luxon", "npm:3.1.0"],\ + ["luxon", "npm:3.1.1"],\ ["tslib", "npm:2.4.0"]\ ],\ "packagePeers": [\ @@ -1136,6 +1138,19 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@parcel/optimizer-data-url", [\ + ["npm:2.8.0", {\ + "packageLocation": "./.yarn/cache/@parcel-optimizer-data-url-npm-2.8.0-89a39d906e-998fb94cee.zip/node_modules/@parcel/optimizer-data-url/",\ + "packageDependencies": [\ + ["@parcel/optimizer-data-url", "npm:2.8.0"],\ + ["@parcel/plugin", "npm:2.8.0"],\ + ["@parcel/utils", "npm:2.8.0"],\ + ["isbinaryfile", "npm:4.0.10"],\ + ["mime", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@parcel/optimizer-htmlnano", [\ ["npm:2.8.0", {\ "packageLocation": "./.yarn/cache/@parcel-optimizer-htmlnano-npm-2.8.0-d2ead43d0c-68010e586b.zip/node_modules/@parcel/optimizer-htmlnano/",\ @@ -1529,6 +1544,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@parcel/transformer-inline-string", [\ + ["npm:2.8.0", {\ + "packageLocation": "./.yarn/cache/@parcel-transformer-inline-string-npm-2.8.0-5fce2c90b8-e40616c55b.zip/node_modules/@parcel/transformer-inline-string/",\ + "packageDependencies": [\ + ["@parcel/transformer-inline-string", "npm:2.8.0"],\ + ["@parcel/plugin", "npm:2.8.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@parcel/transformer-js", [\ ["npm:2.8.0", {\ "packageLocation": "./.yarn/unplugged/@parcel-transformer-js-virtual-0a5c0b53bd/node_modules/@parcel/transformer-js/",\ @@ -2690,10 +2715,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ],\ "linkType": "HARD"\ }],\ - ["npm:1.0.30001434", {\ - "packageLocation": "./.yarn/cache/caniuse-lite-npm-1.0.30001434-9c6ea57daf-7c9d2641e8.zip/node_modules/caniuse-lite/",\ + ["npm:1.0.30001435", {\ + "packageLocation": "./.yarn/cache/caniuse-lite-npm-1.0.30001435-7cebb35f0a-ec88b9c37f.zip/node_modules/caniuse-lite/",\ "packageDependencies": [\ - ["caniuse-lite", "npm:1.0.30001434"]\ + ["caniuse-lite", "npm:1.0.30001435"]\ ],\ "linkType": "HARD"\ }]\ @@ -4910,10 +4935,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }]\ ]],\ ["highcharts", [\ - ["npm:10.3.1", {\ - "packageLocation": "./.yarn/cache/highcharts-npm-10.3.1-e67a887ff6-8a1cf9a363.zip/node_modules/highcharts/",\ + ["npm:10.3.2", {\ + "packageLocation": "./.yarn/cache/highcharts-npm-10.3.2-1672942f09-43cb42b24c.zip/node_modules/highcharts/",\ "packageDependencies": [\ - ["highcharts", "npm:10.3.1"]\ + ["highcharts", "npm:10.3.2"]\ ],\ "linkType": "HARD"\ }]\ @@ -5456,6 +5481,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["isbinaryfile", [\ + ["npm:4.0.10", {\ + "packageLocation": "./.yarn/cache/isbinaryfile-npm-4.0.10-91d1251522-a6b28db7e2.zip/node_modules/isbinaryfile/",\ + "packageDependencies": [\ + ["isbinaryfile", "npm:4.0.10"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["isexe", [\ ["npm:2.0.0", {\ "packageLocation": "./.yarn/cache/isexe-npm-2.0.0-b58870bd2e-26bf6c5480.zip/node_modules/isexe/",\ @@ -5918,10 +5952,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }]\ ]],\ ["luxon", [\ - ["npm:3.1.0", {\ - "packageLocation": "./.yarn/cache/luxon-npm-3.1.0-16e2508500-f8a850b759.zip/node_modules/luxon/",\ + ["npm:3.1.1", {\ + "packageLocation": "./.yarn/cache/luxon-npm-3.1.1-64fe977c1d-388fb35d3c.zip/node_modules/luxon/",\ "packageDependencies": [\ - ["luxon", "npm:3.1.0"]\ + ["luxon", "npm:3.1.1"]\ ],\ "linkType": "HARD"\ }]\ @@ -5987,6 +6021,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["mime", "npm:1.6.0"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:2.6.0", {\ + "packageLocation": "./.yarn/cache/mime-npm-2.6.0-88b89d8de0-1497ba7b9f.zip/node_modules/mime/",\ + "packageDependencies": [\ + ["mime", "npm:2.6.0"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["minimatch", [\ @@ -6659,17 +6700,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }]\ ]],\ ["pinia", [\ - ["npm:2.0.26", {\ - "packageLocation": "./.yarn/cache/pinia-npm-2.0.26-0d96417fac-0d38cc0efc.zip/node_modules/pinia/",\ + ["npm:2.0.27", {\ + "packageLocation": "./.yarn/cache/pinia-npm-2.0.27-3e0154e702-29c862ea43.zip/node_modules/pinia/",\ "packageDependencies": [\ - ["pinia", "npm:2.0.26"]\ + ["pinia", "npm:2.0.27"]\ ],\ "linkType": "SOFT"\ }],\ - ["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.0.26", {\ - "packageLocation": "./.yarn/__virtual__/pinia-virtual-3c74e5a139/0/cache/pinia-npm-2.0.26-0d96417fac-0d38cc0efc.zip/node_modules/pinia/",\ + ["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.0.27", {\ + "packageLocation": "./.yarn/__virtual__/pinia-virtual-0b7bfddb52/0/cache/pinia-npm-2.0.27-3e0154e702-29c862ea43.zip/node_modules/pinia/",\ "packageDependencies": [\ - ["pinia", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.0.26"],\ + ["pinia", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.0.27"],\ ["@types/typescript", null],\ ["@types/vue", null],\ ["@types/vue__composition-api", null],\ @@ -6677,7 +6718,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@vue/devtools-api", "npm:6.4.5"],\ ["typescript", null],\ ["vue", "npm:3.2.45"],\ - ["vue-demi", "virtual:3c74e5a1392a9d26efc27d5867a5220b1ab24b8bfb7c76fe2dac826f7e9d478b9c8eb69cc87331bb2ba20521466999017a02e9dca572946a787e2b4314602fca#npm:0.13.1"]\ + ["vue-demi", "virtual:0b7bfddb52b3cb488814806546397e52c62caef1815758033c8eac7ce386779ac52132e251ad567a19dde858cd2ed318ab2b52e9e258efd261b951e0d2160c16#npm:0.13.1"]\ ],\ "packagePeers": [\ "@types/typescript",\ @@ -6706,7 +6747,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/vue", null],\ ["@types/vue__composition-api", null],\ ["@vue/composition-api", null],\ - ["pinia", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.0.26"],\ + ["pinia", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.0.27"],\ ["vue", "npm:3.2.45"],\ ["vue-demi", "virtual:f56fcf19bbebc2ada1b28955da8cc216b1e9a569a1a7337d2d1926c1ebd1bc7a5bd91aedae1d05c15c8562f33caf7c59bd3020a667340f6bdc6a7b13fc2ba847#npm:0.12.5"]\ ],\ @@ -7232,6 +7273,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@fullcalendar/luxon2", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.11.3"],\ ["@fullcalendar/timegrid", "npm:5.11.3"],\ ["@fullcalendar/vue3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.11.3"],\ + ["@parcel/optimizer-data-url", "npm:2.8.0"],\ + ["@parcel/transformer-inline-string", "npm:2.8.0"],\ ["@parcel/transformer-sass", "npm:2.8.0"],\ ["@popperjs/core", "npm:2.11.6"],\ ["@rollup/pluginutils", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.0.2"],\ @@ -7242,7 +7285,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["browser-fs-access", "npm:0.31.1"],\ ["browserlist", "npm:1.0.1"],\ ["c8", "npm:7.12.0"],\ - ["caniuse-lite", "npm:1.0.30001434"],\ + ["caniuse-lite", "npm:1.0.30001435"],\ ["d3", "npm:7.6.1"],\ ["eslint", "npm:8.28.0"],\ ["eslint-config-standard", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:17.0.0"],\ @@ -7253,7 +7296,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["eslint-plugin-promise", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.1"],\ ["eslint-plugin-vue", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:9.8.0"],\ ["file-saver", "npm:2.0.5"],\ - ["highcharts", "npm:10.3.1"],\ + ["highcharts", "npm:10.3.2"],\ ["html-validate", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:7.10.0"],\ ["jquery", "npm:3.6.1"],\ ["jquery-migrate", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:3.4.0"],\ @@ -7262,14 +7305,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["list.js", "npm:2.3.1"],\ ["lodash", "npm:4.17.21"],\ ["lodash-es", "npm:4.17.21"],\ - ["luxon", "npm:3.1.0"],\ + ["luxon", "npm:3.1.1"],\ ["moment", "npm:2.29.4"],\ ["moment-timezone", "npm:0.5.39"],\ ["ms", "npm:2.1.3"],\ ["murmurhash-js", "npm:1.0.0"],\ ["naive-ui", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.34.2"],\ ["parcel", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.8.0"],\ - ["pinia", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.0.26"],\ + ["pinia", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.0.27"],\ ["pinia-plugin-persist", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:1.0.0"],\ ["pug", "npm:3.0.2"],\ ["sass", "npm:1.56.1"],\ @@ -8159,16 +8202,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "SOFT"\ }],\ ["npm:0.13.1", {\ - "packageLocation": "./.yarn/unplugged/vue-demi-virtual-cfef5ecd67/node_modules/vue-demi/",\ + "packageLocation": "./.yarn/unplugged/vue-demi-virtual-615072ef29/node_modules/vue-demi/",\ "packageDependencies": [\ ["vue-demi", "npm:0.13.1"]\ ],\ "linkType": "SOFT"\ }],\ - ["virtual:3c74e5a1392a9d26efc27d5867a5220b1ab24b8bfb7c76fe2dac826f7e9d478b9c8eb69cc87331bb2ba20521466999017a02e9dca572946a787e2b4314602fca#npm:0.13.1", {\ - "packageLocation": "./.yarn/unplugged/vue-demi-virtual-cfef5ecd67/node_modules/vue-demi/",\ + ["virtual:0b7bfddb52b3cb488814806546397e52c62caef1815758033c8eac7ce386779ac52132e251ad567a19dde858cd2ed318ab2b52e9e258efd261b951e0d2160c16#npm:0.13.1", {\ + "packageLocation": "./.yarn/unplugged/vue-demi-virtual-615072ef29/node_modules/vue-demi/",\ "packageDependencies": [\ - ["vue-demi", "virtual:3c74e5a1392a9d26efc27d5867a5220b1ab24b8bfb7c76fe2dac826f7e9d478b9c8eb69cc87331bb2ba20521466999017a02e9dca572946a787e2b4314602fca#npm:0.13.1"],\ + ["vue-demi", "virtual:0b7bfddb52b3cb488814806546397e52c62caef1815758033c8eac7ce386779ac52132e251ad567a19dde858cd2ed318ab2b52e9e258efd261b951e0d2160c16#npm:0.13.1"],\ ["@types/vue", null],\ ["@types/vue__composition-api", null],\ ["@vue/composition-api", null],\ diff --git a/.yarn/cache/@parcel-optimizer-data-url-npm-2.8.0-89a39d906e-998fb94cee.zip b/.yarn/cache/@parcel-optimizer-data-url-npm-2.8.0-89a39d906e-998fb94cee.zip new file mode 100644 index 000000000..a0ad46637 Binary files /dev/null and b/.yarn/cache/@parcel-optimizer-data-url-npm-2.8.0-89a39d906e-998fb94cee.zip differ diff --git a/.yarn/cache/@parcel-transformer-inline-string-npm-2.8.0-5fce2c90b8-e40616c55b.zip b/.yarn/cache/@parcel-transformer-inline-string-npm-2.8.0-5fce2c90b8-e40616c55b.zip new file mode 100644 index 000000000..5598830cd Binary files /dev/null and b/.yarn/cache/@parcel-transformer-inline-string-npm-2.8.0-5fce2c90b8-e40616c55b.zip differ diff --git a/.yarn/cache/caniuse-lite-npm-1.0.30001434-9c6ea57daf-7c9d2641e8.zip b/.yarn/cache/caniuse-lite-npm-1.0.30001434-9c6ea57daf-7c9d2641e8.zip deleted file mode 100644 index daa4e7488..000000000 Binary files a/.yarn/cache/caniuse-lite-npm-1.0.30001434-9c6ea57daf-7c9d2641e8.zip and /dev/null differ diff --git a/.yarn/cache/caniuse-lite-npm-1.0.30001435-7cebb35f0a-ec88b9c37f.zip b/.yarn/cache/caniuse-lite-npm-1.0.30001435-7cebb35f0a-ec88b9c37f.zip new file mode 100644 index 000000000..9851a7ce7 Binary files /dev/null and b/.yarn/cache/caniuse-lite-npm-1.0.30001435-7cebb35f0a-ec88b9c37f.zip differ diff --git a/.yarn/cache/highcharts-npm-10.3.1-e67a887ff6-8a1cf9a363.zip b/.yarn/cache/highcharts-npm-10.3.2-1672942f09-43cb42b24c.zip similarity index 63% rename from .yarn/cache/highcharts-npm-10.3.1-e67a887ff6-8a1cf9a363.zip rename to .yarn/cache/highcharts-npm-10.3.2-1672942f09-43cb42b24c.zip index 4713b4599..410fae01e 100644 Binary files a/.yarn/cache/highcharts-npm-10.3.1-e67a887ff6-8a1cf9a363.zip and b/.yarn/cache/highcharts-npm-10.3.2-1672942f09-43cb42b24c.zip differ diff --git a/.yarn/cache/isbinaryfile-npm-4.0.10-91d1251522-a6b28db7e2.zip b/.yarn/cache/isbinaryfile-npm-4.0.10-91d1251522-a6b28db7e2.zip new file mode 100644 index 000000000..33eb2b844 Binary files /dev/null and b/.yarn/cache/isbinaryfile-npm-4.0.10-91d1251522-a6b28db7e2.zip differ diff --git a/.yarn/cache/luxon-npm-3.1.0-16e2508500-f8a850b759.zip b/.yarn/cache/luxon-npm-3.1.0-16e2508500-f8a850b759.zip deleted file mode 100644 index 6a145dd08..000000000 Binary files a/.yarn/cache/luxon-npm-3.1.0-16e2508500-f8a850b759.zip and /dev/null differ diff --git a/.yarn/cache/luxon-npm-3.1.1-64fe977c1d-388fb35d3c.zip b/.yarn/cache/luxon-npm-3.1.1-64fe977c1d-388fb35d3c.zip new file mode 100644 index 000000000..1e0c7262f Binary files /dev/null and b/.yarn/cache/luxon-npm-3.1.1-64fe977c1d-388fb35d3c.zip differ diff --git a/.yarn/cache/mime-npm-2.6.0-88b89d8de0-1497ba7b9f.zip b/.yarn/cache/mime-npm-2.6.0-88b89d8de0-1497ba7b9f.zip new file mode 100644 index 000000000..644ef2b53 Binary files /dev/null and b/.yarn/cache/mime-npm-2.6.0-88b89d8de0-1497ba7b9f.zip differ diff --git a/.yarn/cache/pinia-npm-2.0.26-0d96417fac-0d38cc0efc.zip b/.yarn/cache/pinia-npm-2.0.26-0d96417fac-0d38cc0efc.zip deleted file mode 100644 index c7cad55cd..000000000 Binary files a/.yarn/cache/pinia-npm-2.0.26-0d96417fac-0d38cc0efc.zip and /dev/null differ diff --git a/.yarn/cache/pinia-npm-2.0.27-3e0154e702-29c862ea43.zip b/.yarn/cache/pinia-npm-2.0.27-3e0154e702-29c862ea43.zip new file mode 100644 index 000000000..c0477d355 Binary files /dev/null and b/.yarn/cache/pinia-npm-2.0.27-3e0154e702-29c862ea43.zip differ diff --git a/dev/tests/debug.sh b/dev/tests/debug.sh new file mode 100644 index 000000000..37e7bc3ab --- /dev/null +++ b/dev/tests/debug.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# This script recreate the same environment used during tests on GitHub Actions +# and drops you into a terminal at the point where the actual tests would be run. +# +# Refer to https://github.com/ietf-tools/datatracker/blob/main/.github/workflows/build.yml#L141-L155 +# for the commands to run next. +# +# Simply type "exit" + ENTER to exit and shutdown this test environment. + +echo "Fetching latest images..." +docker pull ghcr.io/ietf-tools/datatracker-app-base:latest +docker pull ghcr.io/ietf-tools/datatracker-db:latest +echo "Starting containers..." +docker compose -f docker-compose.debug.yml -p dtdebug up -d +echo "Copying working directory into container..." +docker compose -p dtdebug cp ../../. app:/__w/datatracker/datatracker/ +echo "Run prepare script..." +docker compose -p dtdebug exec app chmod +x ./dev/tests/prepare.sh +docker compose -p dtdebug exec app sh ./dev/tests/prepare.sh +docker compose -p dtdebug exec app /usr/local/bin/wait-for db:3306 -- echo "DB ready" +echo "=================================================================" +echo "Launching zsh terminal:" +docker compose -p dtdebug exec app /bin/zsh +echo "Shutting down containers..." +docker compose -p dtdebug down -v diff --git a/dev/tests/docker-compose.debug.yml b/dev/tests/docker-compose.debug.yml new file mode 100644 index 000000000..44649c117 --- /dev/null +++ b/dev/tests/docker-compose.debug.yml @@ -0,0 +1,33 @@ +# This docker-compose replicates the test workflow happening on GitHub during a PR / build check. +# To be used from the debug.sh script. + +version: '3.8' + +services: + app: + image: ghcr.io/ietf-tools/datatracker-app-base:latest + command: -f /dev/null + working_dir: /__w/datatracker/datatracker + entrypoint: tail + hostname: app + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + CI: 'true' + GITHUB_ACTIONS: 'true' + HOME: /github/home + db: + image: ghcr.io/ietf-tools/datatracker-db:latest + restart: unless-stopped + volumes: + - mariadb-data:/var/lib/mysql + environment: + MYSQL_ROOT_PASSWORD: RkTkDPFnKpko + MYSQL_DATABASE: ietf_utf8 + MYSQL_USER: django + MYSQL_PASSWORD: RkTkDPFnKpko + CI: 'true' + GITHUB_ACTIONS: 'true' + +volumes: + mariadb-data: diff --git a/ietf/doc/models.py b/ietf/doc/models.py index bb2175355..cc01f9756 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -8,6 +8,8 @@ import io import os import rfc2html +from pathlib import Path +from lxml import etree from typing import Optional, TYPE_CHECKING from weasyprint import HTML as wpHTML @@ -21,6 +23,7 @@ from django.conf import settings from django.utils import timezone from django.utils.encoding import force_text from django.utils.html import mark_safe # type:ignore +from django.contrib.staticfiles import finders import debug # pyflakes:ignore @@ -541,6 +544,46 @@ class DocumentInfo(models.Model): def text_or_error(self): return self.text() or "Error; cannot read '%s'"%self.get_base_name() + def html_body(self, classes=""): + if self.get_state_slug() == "rfc": + try: + html = Path( + os.path.join(settings.RFC_PATH, self.canonical_name() + ".html") + ).read_text() + except IOError: + return None + else: + try: + html = Path( + os.path.join( + settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR, + self.name + "-" + self.rev + ".html", + ) + ).read_text() + except IOError: + return None + + # If HTML was generated by rfc2html, do not return it. Caller + # will use htmlize() to use a more current rfc2html to + # generate an HTMLized version. TODO: There should be a + # better way to determine how an HTML format was generated. + if html.startswith("
"):
+            return None
+
+        # get body
+        body = etree.HTML(html).xpath("//body")[0]
+        body.tag = "div"
+        if classes:
+            body.attrib["class"] = classes
+
+        # remove things
+        for tag in ["script"]:
+            for t in body.xpath(f"//{tag}"):
+                t.getparent().remove(t)
+        html = etree.tostring(body, encoding=str, method="html")
+
+        return html
+
     def htmlized(self):
         name = self.get_base_name()
         text = self.text()
@@ -566,17 +609,29 @@ class DocumentInfo(models.Model):
 
     def pdfized(self):
         name = self.get_base_name()
-        text = self.text()
-        cache = caches['pdfized']
-        cache_key = name.split('.')[0]
+        text = self.html_body(classes="rfchtml")
+        stylesheets = [finders.find("ietf/css/document_html_referenced.css")]
+        if text:
+            stylesheets.append(finders.find("ietf/css/document_html_txt.css"))
+        else:
+            text = self.htmlized()
+            stylesheets.append(io.BytesIO(b"body { font-size: 9.2pt; }"))
+
+        cache = caches["pdfized"]
+        cache_key = name.split(".")[0]
         try:
             pdf = cache.get(cache_key)
         except EOFError:
             pdf = None
         if not pdf:
-            html = rfc2html.markup(text, path=settings.PDFIZER_URL_PREFIX)
             try:
-                pdf = wpHTML(string=html.replace('\xad','')).write_pdf(stylesheets=[io.BytesIO(b'html { font-size: 94%;}')])
+                pdf = wpHTML(
+                    string=text, base_url=settings.IDTRACKER_BASE_URL
+                ).write_pdf(
+                    stylesheets=stylesheets,
+                    presentational_hints=True,
+                    optimize_size=("fonts", "images"),
+                )
             except AssertionError:
                 pdf = None
             if pdf:
diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py
index e5353d1de..1137cf636 100644
--- a/ietf/doc/templatetags/ietf_filters.py
+++ b/ietf/doc/templatetags/ietf_filters.py
@@ -260,8 +260,8 @@ def urlize_ietf_docs(string, autoescape=None):
 
 urlize_ietf_docs = stringfilter(urlize_ietf_docs)
 
-@register.filter(name='urlize_related_source_list', is_safe=True, needs_autoescape=True)
-def urlize_related_source_list(related, autoescape=None):
+@register.filter(name='urlize_related_source_list', is_safe=True, document_html=False)
+def urlize_related_source_list(related, document_html=False):
     """Convert a list of RelatedDocuments into list of links using the source document's canonical name"""
     links = []
     names = set()
@@ -273,10 +273,9 @@ def urlize_related_source_list(related, autoescape=None):
             continue
         names.add(name)
         titles.add(title)
-        url = urlreverse('ietf.doc.views_doc.document_main', kwargs=dict(name=name))
-        if autoescape:
-            name = escape(name)
-            title = escape(title)
+        url = urlreverse('ietf.doc.views_doc.document_main' if document_html is False else 'ietf.doc.views_doc.document_html', kwargs=dict(name=name))
+        name = escape(name)
+        title = escape(title)
         links.append(mark_safe(
             '%(name)s' % dict(name=prettify_std_name(name),
                                                                       title=title,
@@ -284,17 +283,16 @@ def urlize_related_source_list(related, autoescape=None):
         ))
     return links
         
-@register.filter(name='urlize_related_target_list', is_safe=True, needs_autoescape=True)
-def urlize_related_target_list(related, autoescape=None):
+@register.filter(name='urlize_related_target_list', is_safe=True, document_html=False)
+def urlize_related_target_list(related, document_html=False):
     """Convert a list of RelatedDocuments into list of links using the target document's canonical name"""
     links = []
     for rel in related:
         name=rel.target.document.canonical_name()
         title = rel.target.document.title
-        url = urlreverse('ietf.doc.views_doc.document_main', kwargs=dict(name=name))
-        if autoescape:
-            name = escape(name)
-            title = escape(title)
+        url = urlreverse('ietf.doc.views_doc.document_main' if document_html is False else 'ietf.doc.views_doc.document_html', kwargs=dict(name=name))
+        name = escape(name)
+        title = escape(title)
         links.append(mark_safe(
             '%(name)s' % dict(name=prettify_std_name(name),
                                                                       title=title,
@@ -554,6 +552,19 @@ def consensus(doc):
     else:
         return "Unknown"
 
+
+@register.filter
+def std_level_to_label_format(doc):
+    """Returns valid Bootstrap classes to label a status level badge."""
+    if doc.is_rfc():
+        if doc.related_that("obs"):
+            return "obs"
+        else:
+            return doc.std_level_id
+    else:
+        return "draft"
+
+
 @register.filter
 def pos_to_label_format(text):
     """Returns valid Bootstrap classes to label a ballot position."""
diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py
index 533c61fd5..751625e59 100644
--- a/ietf/doc/tests.py
+++ b/ietf/doc/tests.py
@@ -730,13 +730,13 @@ Man                    Expires September 22, 2015               [Page 3]
 
         r = self.client.get(urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=draft.name)))
         self.assertEqual(r.status_code, 200)
-        self.assertContains(r, "Versions:")
+        self.assertContains(r, "Select version")
         self.assertContains(r, "Deimos street")
         q = PyQuery(r.content)
         self.assertEqual(q('title').text(), 'draft-ietf-mars-test-01')
-        self.assertEqual(len(q('.rfcmarkup pre')), 4)
-        self.assertEqual(len(q('.rfcmarkup span.h1')), 2)
-        self.assertEqual(len(q('.rfcmarkup a[href]')), 41)
+        self.assertEqual(len(q('.rfcmarkup pre')), 3)
+        self.assertEqual(len(q('.rfcmarkup span.h1, .rfcmarkup h1')), 2)
+        self.assertEqual(len(q('.rfcmarkup a[href]')), 28)
 
         r = self.client.get(urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=draft.name, rev=draft.rev)))
         self.assertEqual(r.status_code, 200)
diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py
index 27d36284a..1824123ca 100644
--- a/ietf/doc/utils.py
+++ b/ietf/doc/utils.py
@@ -995,41 +995,6 @@ def get_search_cache_key(params):
     key = "doc:document:search:" + hashlib.sha512(json.dumps(kwargs, sort_keys=True).encode('utf-8')).hexdigest()
     return key
     
-def label_wrap(label, items, joiner=',', max=50):
-    lines = []
-    if not items:
-        return lines
-    line = '%s: %s' % (label, items[0])
-    for item in items[1:]:
-        if len(line)+len(joiner+' ')+len(item) > max:
-            lines.append(line+joiner)
-            line = ' '*(len(label)+len(': ')) + item
-        else:
-            line += joiner+' '+item
-    if line:
-        lines.append(line)
-    return lines
-
-def join_justified(left, right, width=72):
-    count = max(len(left), len(right))
-    left = left + ['']*(count-len(left))
-    right = right + ['']*(count-len(right))
-    lines = []
-    i = 0
-    while True:
-        l = left[i]
-        r = right[i]
-        if len(l)+1+len(r) > width:
-            left = left + ['']
-            right = right[:i] + [''] + right[i:]
-            r = right[i]
-            count += 1
-        lines.append( l + ' ' + r.rjust(width-len(l)-1) )
-        i += 1
-        if i >= count:
-            break
-    return lines
-
 def build_file_urls(doc):
     if isinstance(doc,Document) and doc.get_state_slug() == "rfc":
         name = doc.canonical_name()
@@ -1069,135 +1034,6 @@ def build_file_urls(doc):
 
     return file_urls, found_types
 
-def build_doc_supermeta_block(doc):
-    items = []
-    items.append(f'[Search]')
-
-    file_urls, found_types = build_file_urls(doc)
-    file_urls = [('txt',url) if label=='plain text' else (label,url) for label,url in file_urls]
-
-    if file_urls:
-        file_labels = {
-            'txt' : 'Plaintext version of this document',
-            'xml' : 'XML source for this document',
-            'pdf' : 'PDF version of this document',
-            'html' : 'HTML version of this document, from XML2RFC',
-            'bibtex' : 'BibTex entry for this document',
-        }
-        parts=[]
-        for label,url in file_urls:
-            if 'htmlized' not in label:
-                file_label=file_labels.get(label,'')
-                title_attribute = f' title="{file_label}"' if file_label else ''
-                partstring = f'{label}' 
-                parts.append(partstring)
-        items.append('[' + '|'.join(parts) + ']')
-
-    items.append(f'[Tracker]')
-    if doc.group.acronym != 'none':
-        items.append(f'[WG]')
-    items.append(f'[Email]')
-    if doc.rev != "00":
-        items.append(f'[Diff1]')
-        items.append(f'[Diff2]')
-    items.append(f'[Nits]')
-
-    return ' '.join(items)
-
-def build_doc_meta_block(doc, path):
-    def add_markup(path, doc, lines):
-        is_hst = doc.is_dochistory()
-        rev = doc.rev
-        if is_hst:
-            doc = doc.doc
-        name = doc.name
-        rfcnum = doc.rfc_number()
-        errata_url = settings.RFC_EDITOR_ERRATA_URL.format(rfc_number=rfcnum) if not is_hst else ""
-        ipr_url = "%s?submit=draft&id=%s" % (urlreverse('ietf.ipr.views.search'), name)
-        for i, line in enumerate(lines):
-            # add draft links
-            line = re.sub(r'\b(draft-[-a-z0-9]+)\b', r'\g<1>'%(path, ), line)
-            # add rfcXXXX to RFC links
-            line = re.sub(r' (rfc[0-9]+)\b', r' \g<1>'%(path, ), line)
-            # add XXXX to RFC links
-            line = re.sub(r' ([0-9]{3,5})\b', r' \g<1>'%(path, ), line)
-            # add draft revision links
-            line = re.sub(r' ([0-9]{2})\b', r' \g<1>'%(path, name, ), line)
-            if rfcnum:
-                # add errata link
-                line = re.sub(r'Errata exist', r'Errata exist'%(errata_url, ), line)
-            if is_hst or not rfcnum:
-                # make current draft rev bold
-                line = re.sub(r'>(%s)<'%rev, r'>\g<1><', line)
-            line = re.sub(r'IPR declarations', r'IPR declarations'%(ipr_url, ), line)
-            line = line.replace(r'[txt]', r'[txt]' % doc.get_href())
-            lines[i] = line
-        return lines
-    #
-    now = timezone.now()
-    draft_state = doc.get_state('draft')
-    block = ''
-    meta = {}
-    if doc.type_id == 'draft':
-        revisions = []
-        ipr = doc.related_ipr()
-        if ipr:
-            meta['ipr'] = [ "IPR declarations" ]
-        if doc.is_rfc() and not doc.is_dochistory():
-            if not doc.name.startswith('rfc'):
-                meta['from'] = [ "%s-%s"%(doc.name, doc.rev) ]
-            meta['errata'] = [ "Errata exist" ] if doc.tags.filter(slug='errata').exists() else []
-            
-            meta['obsoletedby'] = [ document.rfc_number() for alias in doc.related_that('obs') for document in alias.docs.all() ]
-            meta['obsoletedby'].sort()
-            meta['updatedby'] = [ document.rfc_number() for alias in doc.related_that('updates') for document in alias.docs.all() ]
-            meta['updatedby'].sort()
-            meta['stdstatus'] = [ doc.std_level.name ]
-        else:
-            dd = doc.doc if doc.is_dochistory() else doc
-            revisions += [ '(%s)%s'%(d.name, ' '*(2-((len(d.name)-1)%3))) for d in dd.replaces() ]
-            revisions += doc.revisions()
-            if doc.is_dochistory() and doc.doc.is_rfc():
-                revisions += [ doc.doc.canonical_name() ]
-            else:
-                revisions += [ d.name for d in doc.replaced_by() ]
-            meta['versions'] = revisions
-            if not doc.is_dochistory and draft_state.slug == 'active' and now > doc.expires:
-                # Active past expiration date
-                meta['active'] = [ 'Document is active' ]
-                meta['state' ] = [ doc.friendly_state() ]
-            intended_std = doc.intended_std_level if doc.intended_std_level else None
-            if intended_std:
-                if intended_std.slug in ['ps', 'ds', 'std']:
-                    meta['stdstatus'] = [ "Standards Track" ]
-                else:
-                    meta['stdstatus'] = [ intended_std.name ]
-    elif doc.type_id == 'charter':
-        meta['versions'] = doc.revisions()
-    #
-    # Add markup to items that needs it.
-    if 'versions' in meta:
-        meta['versions'] = label_wrap('Versions', meta['versions'], joiner="")
-    for label in ['Obsoleted by', 'Updated by', 'From' ]:
-        item = label.replace(' ','').lower()
-        if item in meta and meta[item]:
-            meta[item] = label_wrap(label, meta[item])
-    #
-    left = []
-    right = []
-    #right = [ '[txt]']
-    for item in [ 'from', 'versions', 'obsoletedby', 'updatedby', ]:
-        if item in meta and meta[item]:
-            left += meta[item]
-    for item in ['stdstatus', 'active', 'state', 'ipr', 'errata', ]:
-        if item in meta and meta[item]:
-            right += meta[item]
-    lines = join_justified(left, right)
-    block = '\n'.join(add_markup(path, doc, lines))
-    #
-    return block
-
-
 def augment_docs_and_user_with_user_info(docs, user):
     """Add attribute to each document with whether the document is tracked
     or has a review wish by the user or not, and the review teams the user is on."""
diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py
index bdf3b6c1a..4c7d21645 100644
--- a/ietf/doc/views_doc.py
+++ b/ietf/doc/views_doc.py
@@ -49,6 +49,7 @@ from django.template.loader import render_to_string
 from django.urls import reverse as urlreverse
 from django.conf import settings
 from django import forms
+from django.contrib.staticfiles import finders
 
 
 import debug                            # pyflakes:ignore
@@ -61,9 +62,9 @@ from ietf.doc.utils import (add_links_in_new_revision_events, augment_events_wit
     can_adopt_draft, can_unadopt_draft, get_chartering_type, get_tags_for_stream_id,
     needed_ballot_positions, nice_consensus, prettify_std_name, update_telechat, has_same_ballot,
     get_initial_notify, make_notify_changed_event, make_rev_history, default_consensus,
-    add_events_message_info, get_unicode_document_content, build_doc_meta_block,
+    add_events_message_info, get_unicode_document_content,
     augment_docs_and_user_with_user_info, irsg_needed_ballot_positions, add_action_holder_change_event,
-    build_doc_supermeta_block, build_file_urls, update_documentauthors, fuzzy_find_documents,
+    build_file_urls, update_documentauthors, fuzzy_find_documents,
     bibxml_for_draft)
 from ietf.doc.utils_bofreq import bofreq_editors, bofreq_responsible
 from ietf.group.models import Role, Group
@@ -140,12 +141,12 @@ def interesting_doc_relations(doc):
 
     return interesting_relations_that, interesting_relations_that_doc
 
-def document_main(request, name, rev=None):
+def document_main(request, name, rev=None, document_html=False):
     doc = get_object_or_404(Document.objects.select_related(), docalias__name=name)
 
     # take care of possible redirections
     aliases = DocAlias.objects.filter(docs=doc).values_list("name", flat=True)
-    if rev==None and doc.type_id == "draft" and not name.startswith("rfc"):
+    if document_html is False and rev==None and doc.type_id == "draft" and not name.startswith("rfc"):
         for a in aliases:
             if a.startswith("rfc"):
                 return redirect("ietf.doc.views_doc.document_main", name=a)
@@ -169,7 +170,7 @@ def document_main(request, name, rev=None):
                 doc = h
                 break
 
-        if not snapshot:
+        if not snapshot and document_html is False:
             return redirect('ietf.doc.views_doc.document_main', name=name)
 
         if doc.type_id == "charter":
@@ -184,7 +185,6 @@ def document_main(request, name, rev=None):
 
     top = render_document_top(request, doc, "status", name)
 
-
     telechat = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
     if telechat and (not telechat.telechat_date or telechat.telechat_date < date_today(settings.TIME_ZONE)):
        telechat = None
@@ -446,8 +446,22 @@ def document_main(request, name, rev=None):
         else:
             stream_desc = "(None)"
 
-        return render(request, "doc/document_draft.html",
+        html = None
+        js = None
+        css = None
+        if document_html:
+            html = doc.html_body()
+            if request.COOKIES.get("pagedeps") == "inline":
+                js = Path(finders.find("ietf/js/document_html.js")).read_text()
+                css = Path(finders.find("ietf/css/document_html_inline.css")).read_text()
+                if html:
+                    css += Path(finders.find("ietf/css/document_html_txt.css")).read_text()
+        return render(request, "doc/document_draft.html" if document_html is False else "doc/document_html.html",
                                   dict(doc=doc,
+                                       document_html=document_html,
+                                       css=css,
+                                       js=js,
+                                       html=html,
                                        group=group,
                                        top=top,
                                        name=name,
@@ -787,7 +801,6 @@ def document_raw_id(request, name, rev=None, ext=None):
     except:
         raise Http404
 
-
 def document_html(request, name, rev=None):
     found = fuzzy_find_documents(name, rev)
     num_found = found.documents.count()
@@ -811,30 +824,7 @@ def document_html(request, name, rev=None):
     if not os.path.exists(doc.get_file_name()):
         raise Http404("File not found: %s" % doc.get_file_name())
 
-    if doc.type_id in ['draft',]:
-        doc.supermeta = build_doc_supermeta_block(doc)
-        doc.meta = build_doc_meta_block(doc, settings.HTMLIZER_URL_PREFIX)
-
-    doccolor = 'bgwhite' # Unknown
-    if doc.type_id=='draft':
-        if doc.is_rfc():
-            if doc.related_that('obs'):
-                doccolor = 'bgbrown'
-            else:
-                doccolor = {
-                    'ps'   : 'bgblue',
-                    'exp'  : 'bgyellow',
-                    'inf'  : 'bgorange',
-                    'ds'   : 'bgcyan',
-                    'hist' : 'bggrey',
-                    'std'  : 'bggreen',
-                    'bcp'  : 'bgmagenta',
-                    'unkn' : 'bgwhite',
-                }.get(doc.std_level_id, 'bgwhite')
-        else:
-            doccolor = 'bgred' # Draft
-
-    return render(request, "doc/document_html.html", {"doc":doc, "doccolor":doccolor })
+    return document_main(request, name, rev=rev, document_html=True)
 
 def document_pdfized(request, name, rev=None, ext=None):
 
diff --git a/ietf/static/css/document_html.scss b/ietf/static/css/document_html.scss
new file mode 100644
index 000000000..f23f466b3
--- /dev/null
+++ b/ietf/static/css/document_html.scss
@@ -0,0 +1,316 @@
+@use "sass:map";
+
+// FIXME: It's not clear why these three variables remain unset by bs5, but just
+// set them to placeholder values so the CSS embedded in the HTML validates.
+$btn-font-family: inherit !default;
+$nav-link-font-weight: inherit !default;
+$tooltip-margin: inherit !default;
+
+@import "bootstrap/scss/functions";
+@import "bootstrap/scss/variables";
+@import "bootstrap/scss/maps";
+@import "bootstrap/scss/mixins";
+@import "bootstrap/scss/utilities";
+@import "bootstrap/scss/root";
+
+// Layout & components
+@import "bootstrap/scss/reboot";
+@import "bootstrap/scss/type";
+// @import "bootstrap/scss/images";
+@import "bootstrap/scss/containers";
+@import "bootstrap/scss/grid";
+// @import "bootstrap/scss/tables";
+@import "bootstrap/scss/forms";
+@import "bootstrap/scss/buttons";
+@import "bootstrap/scss/transitions";
+// @import "bootstrap/scss/dropdown";
+@import "bootstrap/scss/button-group";
+@import "bootstrap/scss/nav";
+@import "bootstrap/scss/navbar";
+// @import "bootstrap/scss/card";
+// @import "bootstrap/scss/accordion";
+// @import "bootstrap/scss/breadcrumb";
+@import "bootstrap/scss/pagination";
+@import "bootstrap/scss/badge";
+@import "bootstrap/scss/alert";
+// @import "bootstrap/scss/progress";
+// @import "bootstrap/scss/list-group";
+// @import "bootstrap/scss/close";
+// @import "bootstrap/scss/toasts";
+// @import "bootstrap/scss/modal";
+@import "bootstrap/scss/tooltip";
+// @import "bootstrap/scss/popover";
+// @import "bootstrap/scss/carousel";
+// @import "bootstrap/scss/spinners";
+// @import "bootstrap/scss/offcanvas";
+// @import "bootstrap/scss/placeholders";
+
+// Helpers
+@import "bootstrap/scss/helpers";
+
+// Utilities
+@import "bootstrap/scss/utilities/api";
+
+:root {
+    --doc-ptsize-max: 16pt;
+}
+
+.overscroll-none {
+    overscroll-behavior: none;
+}
+
+.no-scrollbar {
+    scrollbar-width: none;
+}
+
+.sidebar-toggle[aria-expanded="true"] {
+    display: none;
+}
+
+.sidebar-toggle[aria-expanded="false"] {
+    display: inherit;
+}
+
+@media screen {
+    @include media-breakpoint-down(md) {
+        body {
+            padding-top: 60px;
+        }
+
+        html {
+            scroll-padding-top: 60px;
+        }
+    }
+
+    @include media-breakpoint-down(sm) {
+        body {
+            padding-top: 70px;
+        }
+
+        html {
+            scroll-padding-top: 70px;
+        }
+    }
+}
+
+.rfcmarkup,
+.rfchtml {
+    font-family: var(--bs-font-monospace);
+
+    caption {
+        padding: 0;
+        color: var(--bs-body-color);
+    }
+
+    code {
+        font-size: 1em;
+        color: var(--bs-body-color);
+    }
+
+    @media screen {
+        @include media-breakpoint-only(xs) {
+            font-size: min(7pt, var(--doc-ptsize-max));
+        }
+
+        @include media-breakpoint-up(sm) {
+            font-size: min(9.5pt, var(--doc-ptsize-max));
+        }
+
+        @include media-breakpoint-up(md) {
+            font-size: min(9.5pt, var(--doc-ptsize-max));
+        }
+
+        @include media-breakpoint-up(lg) {
+            font-size: min(11pt, var(--doc-ptsize-max));
+        }
+
+        @include media-breakpoint-up(xl) {
+            font-size: min(13pt, var(--doc-ptsize-max));
+        }
+
+        @include media-breakpoint-up(xxl) {
+            font-size: min(16pt, var(--doc-ptsize-max));
+        }
+
+        .grey,
+        hr {
+            opacity: $hr-opacity;
+        }
+    }
+
+    h1,
+    h2,
+    h3,
+    h4,
+    h5,
+    h6 {
+        font-weight: bold;
+        font-size: 1em;
+    }
+
+    pre,
+    code {
+        font-size: 1em;
+    }
+
+    pre {
+        width: 72ch;
+        margin: 0;
+        padding: 0;
+    }
+
+    .bcp14 {
+        font-weight: bold;
+        // color: $gray-700;
+    }
+}
+
+.rfcmarkup {
+
+    h1,
+    h2,
+    h3,
+    h4,
+    h5,
+    h6 {
+        white-space: pre;
+        display: inline;
+    }
+}
+
+tbody.meta tr {
+
+    td:first-child,
+    th:first-child,
+    td.edit {
+        display: none;
+    }
+}
+
+.sidebar {
+    height: 100vh;
+
+    .toplink,
+    #name-table-of-contents {
+        display: none;
+    }
+
+    th,
+    td {
+        display: block;
+        padding: 0;
+    }
+
+    td {
+        margin-bottom: map.get($spacers, 3);
+    }
+}
+
+// Add some padding when there are multiple buttons in a line that can wrap
+.buttonlist .btn {
+    margin-bottom: map.get($spacers, 1);
+}
+
+// Make revision numbers pagination items fixed-width
+.revision-list {
+    .page-item {
+        width: 2.2rem;
+    }
+
+    .page-item.rfc {
+        width: 6.6rem;
+    }
+}
+
+#docinfo {
+    max-height: 70vh;
+    z-index: -1;
+}
+
+
+.badge-obs {
+    color: white;
+    background-color: $orange-800;
+}
+
+.badge-ps {
+    color: black;
+    background-color: $blue-300;
+}
+
+.badge-exp {
+    color: black;
+    background-color: $yellow-200;
+}
+
+.badge-inf {
+    color: white;
+    background-color: $orange;
+}
+
+.badge-ds {
+    color: black;
+    background-color: $cyan-200;
+}
+
+.badge-hist {
+    color: white;
+    background-color: $gray-700;
+}
+
+.badge-std {
+    color: black;
+    background-color: $teal-200;
+}
+
+.badge-bcp {
+    color: white;
+    background-color: $pink-500;
+}
+
+.badge-unkn {
+    color: black;
+    background-color: $gray-300;
+}
+
+.badge-draft {
+    color: white;
+    background-color: $danger;
+}
+
+#toc-nav {
+    width: inherit;
+    overscroll-behavior-y: none; // Prevent overscrolling from scrolling the main content
+}
+
+
+@media print {
+    @page {
+        size: letter;
+        margin: .75in;
+    }
+
+    body {
+        margin: 0;
+        padding: 0;
+        font-size: 10pt;
+    }
+
+    pre {
+        page-break-inside: avoid;
+    }
+
+    a:link,
+    a:visited {
+        // color: inherit;
+        // text-decoration: none;
+    }
+
+    .newpage {
+        page-break-before: always !important;
+    }
+
+    .noprint {
+        display: none;
+    }
+}
diff --git a/ietf/static/css/document_html_inline.scss b/ietf/static/css/document_html_inline.scss
new file mode 100644
index 000000000..a75753438
--- /dev/null
+++ b/ietf/static/css/document_html_inline.scss
@@ -0,0 +1,6 @@
+@import "document_html";
+
+// Make the bootstrap icons available via data-url.
+$bootstrap-icons-font-src: url(data-url:npm:bootstrap-icons/font/fonts/bootstrap-icons.woff2) format("woff2"),
+url(data-url:npm:bootstrap-icons/font/fonts/bootstrap-icons.woff) format("woff");
+@import "bootstrap-icons/font/bootstrap-icons";
diff --git a/ietf/static/css/document_html_referenced.scss b/ietf/static/css/document_html_referenced.scss
new file mode 100644
index 000000000..534298940
--- /dev/null
+++ b/ietf/static/css/document_html_referenced.scss
@@ -0,0 +1,6 @@
+@import "document_html";
+
+// Make the bootstrap icons available.
+$bootstrap-icons-font-src: url("npm:bootstrap-icons/font/fonts/bootstrap-icons.woff2") format("woff2"),
+url("npm:bootstrap-icons/font/fonts/bootstrap-icons.woff") format("woff");
+@import "bootstrap-icons/font/bootstrap-icons";
diff --git a/ietf/static/css/document_html_txt.scss b/ietf/static/css/document_html_txt.scss
new file mode 100644
index 000000000..1f9ef0ad9
--- /dev/null
+++ b/ietf/static/css/document_html_txt.scss
@@ -0,0 +1,376 @@
+:root {
+  --line: 1.2em;
+  --block: 0 0 0 3ch;
+  --paragraph: var(--line) 0 var(--line) 3ch;
+}
+
+// WeasyPrint can't handle CSS variables in multi-attribute properties, so work
+// around that
+// https://github.com/Kozea/WeasyPrint/issues/1219
+
+@mixin margin-paragraph {
+  margin-top: var(--line);
+  margin-right: 0;
+  margin-bottom: var(--line);
+  margin-left: 3ch;
+}
+
+@mixin margin-line {
+  margin-top: var(--line);
+  margin-right: 0;
+  margin-bottom: var(--line);
+  margin-left: 0;
+}
+
+@mixin margin-block {
+  margin-top: 0;
+  margin-right: 0;
+  margin-bottom: 0;
+  margin-left: 3ch;
+}
+
+.rfchtml {
+// body {
+//   color: black;
+//   font-family: monospace;
+//   font-size: 1em;
+  line-height: var(--line);
+  width: 72ch;
+
+  // margin: var(--line) 3ch;
+  margin-top: var(--line);
+  margin-right: 3ch;
+  margin-bottom: var(--line);
+  margin-left: 3ch;
+
+h1, h2, h3, h4, h5 {
+  font-weight: bold;
+  font-size: inherit;
+  line-height: inherit;
+  // margin: var(--line) 0;
+  @include margin-line;
+}
+.section-number {
+  margin-right: 1ch;
+}
+p {
+  // margin: var(--paragraph);
+  @include margin-paragraph;
+}
+aside, ol, dl {
+  // margin: var(--block);
+  @include margin-block;
+}
+figure {
+  margin: 0;
+}
+blockquote {
+  // margin: var(--paragraph);
+  @include margin-paragraph;
+  border-left: 2px solid darkgrey;
+}
+
+/* Header junk */
+#external-metadata {
+  display: none !important; /* metadata.min.js is evil because it produces unstyleable goop */
+}
+#identifiers dt:is(.label-stream, .label-rfc, .label-std, .label-bcp, .label-internet-draft,
+                   .label-workgroup, .label-intended-status, .label-obsoletes, .label-updates,
+                   .label-published, .label-expires, .label-category, .label-issn,
+                   .label-authors), .ears {
+  display: none;
+}
+#identifiers {
+  margin: 0;
+  display: grid;
+  grid-template-columns: 47ch 24ch;
+  grid-auto-rows: auto;
+  gap: 0 1ch;
+}
+#identifiers dt {
+  margin: 0 1ch 0 0;
+  float: revert;
+  display: inline-block;
+}
+#identifiers dd {
+  grid-column: 1;
+  margin: 0;
+  width: 47ch;
+  /* HAXX: this gets around the lack of text-content-trim support */
+  display: flex;
+  flex-wrap: wrap;
+}
+#identifiers dd::before {
+  margin: 0 1ch 0 0;
+}
+#identifiers dd.rfc::before {
+  content: "Request for Comments:";
+}
+#identifiers dd.std::before {
+  content: "STD:";
+}
+#identifiers dd.bcp::before {
+  content: "BCP:";
+}
+#identifiers dd.internet-draft::before {
+  content: "Internet-Draft:";
+}
+#identifiers dd.workgroup::before {
+  content: "Workgroup:";
+}
+#identifiers dd.intended-status::before {
+  content: "Intended Status:";
+}
+#identifiers dd.obsoletes::before {
+  content: "Obsoletes:";
+  margin: 0 0 0 -10ch;
+}
+#identifiers dd.obsoletes {
+  padding-left: 10ch;
+  width: 37ch;
+}
+#identifiers dd.updates::before {
+  content: "Updates:";
+  margin: 0 0 0 -8ch;
+}
+#identifiers dd.updates {
+  padding-left: 8ch;
+  width: 39ch;
+}
+#identifiers dd:is(.updates, .obsoletes) a {
+  margin: 0 0 0 1ch;
+}
+#identifiers dd:is(.updates, .obsoletes) a:last-of-type {
+  margin: 0 1ch;
+}
+#identifiers dd.published::before {
+  content: "Published:";
+}
+#identifiers dd.expires::before {
+  content: "Expires:";
+}
+#identifiers dd.category::before {
+  content: "Category:";
+}
+#identifiers dd.issn::before {
+  content: "ISSN:";
+}
+#identifiers dd.authors {
+  grid-area: 1 / 2 / 100 / 3;
+  width: 24ch;
+  text-align: right;
+  display: block;
+}
+
+#title {
+  clear: left;
+  text-align: center;
+  margin-top: 2em;
+}
+#rfcnum {
+  display: none;
+}
+.toplink {
+  display: none;
+}
+nav.toc ul {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+nav.toc ul > li {
+  padding-left: 2ch;
+}
+nav.toc > ul > li {
+  padding-left: 3ch;
+}
+nav.toc ul > li > p {
+  margin: 0;
+}
+
+/* Lists */
+ol, ul {
+  padding: 0;
+}
+ol {
+  margin: 0 0 0 6ch; /* todo: deal with lists that have >= 10 items */
+}
+ol ol, ul ol {
+  margin: 0 0 0 3ch;
+}
+ul {
+  margin: 0 0 0 4ch;
+  list-style-type: '*';
+}
+ul ul {
+  list-style-type: '-';
+}
+ul ul, ol ul {
+  margin-left: 1ch;
+}
+ul ul ul {
+  list-style-type: 'o';
+}
+li {
+  // margin: var(--line) 0;
+  @include margin-line;
+  padding: 0 0 0 2ch;
+}
+.compact li {
+  margin: 0;
+}
+li p:first-child, dd p:first-child {
+  margin: 0;
+}
+dt {
+  float: left;
+  clear: left;
+  margin: 0 2ch 0 0;
+  break-after: avoid;
+}
+dd {
+  // margin: var(--paragraph);
+  @include margin-paragraph;
+  break-before: avoid;
+}
+dl.compact dt, dl.compact dd {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+dl.references dt {
+  margin-right: 1ch;
+}
+dl.references dd {
+  margin-left: 11ch;
+}
+dd.break {
+  display: none;
+}
+
+/* Figures, tables */
+pre {
+  // margin: var(--line) 0;
+  @include margin-line;
+}
+div.artwork, div.sourcecode {
+  display: flex;
+  flex-wrap: nowrap;
+  align-items: end;
+}
+div.artwork.alignCenter, div.sourcecode.alignCenter {
+  justify-content: center;
+}
+div.artwork.alignRight, div.sourcecode.alignRight {
+  justify-content: end;
+}
+div.artwork::before, div.sourcecode::before {
+  flex: 0 1 3ch;
+  content: "";
+}
+div.artwork.alignRight::before, div.sourcecode.alignRight::before {
+  flex-grow: 1;
+}
+div.artwork pre, div.sourcecode pre {
+  flex: 0 0 content;
+  margin: 0;
+  max-width: 72ch;
+  overflow: auto;
+}
+div.artwork .pilcrow, div.sourcecode .pilcrow {
+  flex: 0 0 1ch;
+}
+figcaption, table caption {
+  text-align: center;
+  margin-top: var(--line);
+}
+table {
+  --half-line: calc(var(--line) / 2 - 1px);
+  caption-side: bottom;
+  // margin: var(--line) 0 var(--half-line) 3ch;
+  margin-top: var(--line);
+  margin-right: 0;
+  margin-bottom: var(--half-line);
+  margin-left: 3ch;
+  border-collapse: collapse;
+}
+table.center {
+  margin-left: auto; /* todo: add 3ch */
+  margin-right: auto;
+}
+table caption {
+  margin-top: calc(var(--half-line) + var(--line));
+}
+thead, tfoot {
+  border-top-style: double;
+  border-bottom-style: double;
+}
+td, th {
+  border: 1px solid black;
+  // padding: var(--half-line) 1ch;
+  padding-top: var(--half-line);
+  padding-right: 1ch;
+  padding-bottom: var(--half-line);
+  padding-left: 1ch;
+}
+.text-left {
+  text-align: left;
+}
+.text-center {
+  text-align: center;
+}
+.text-right {
+  text-align: right;
+}
+
+/* Links */
+a.selfRef, a.pilcrow {
+  color: black;
+  text-decoration: none;
+}
+a.relref, a.xref {
+  hyphens: none;
+}
+a.relref, a.xref.cite {
+  white-space: nowrap;
+}
+.pilcrow {
+  display: inline-block;
+  margin-right: -1ch;
+  opacity: 0.01;
+  text-decoration: none;
+}
+:hover > .pilcrow {
+  opacity: 0.2;
+}
+* > .pilcrow[href]:hover {
+  opacity: 0.6;
+}
+
+/* sup, sub */
+sup, sub {
+  line-height: 1.1;
+}
+
+/* Authors */
+address {
+  font-style: normal;
+  // margin: 2em 0 var(--line) 3ch;
+  margin-top: 2em;
+  margin-right: 0;
+  margin-bottom: var(--line);
+  margin-left: 3ch;
+}
+h2 + address {
+  margin-top: 1em;
+}
+address .tel, address .email {
+  // margin: var(--line) 0 0;
+  margin-top: var(--line);
+  margin-right: 0;
+  margin-bottom: 0;
+  margin-left: 0;
+}
+address .tel + .email {
+  margin: 0;
+}
+}
diff --git a/ietf/static/css/ietf.scss b/ietf/static/css/ietf.scss
index 86b71f154..9cfb610b7 100644
--- a/ietf/static/css/ietf.scss
+++ b/ietf/static/css/ietf.scss
@@ -168,8 +168,9 @@ table tbody.meta {
     }
 }
 
-// Try and hyphenate table headings
-th {
+// Try and hyphenate table headings and other things
+th,
+.hyphenate {
     hyphens: auto;
 }
 
@@ -208,6 +209,17 @@ th {
     max-width: 300px;
 }
 
+// Make revision numbers pagination items fixed-width
+.revision-list {
+    .page-item {
+        width: 2.2rem;
+    }
+
+    .page-item.rfc {
+        width: 6.6rem;
+    }
+}
+
 // Style the photo cards
 .photo {
     width: 12em;
diff --git a/ietf/static/images/irtf-logo.svg b/ietf/static/images/irtf-logo.svg
index ad339fe2b..be64890b2 100644
--- a/ietf/static/images/irtf-logo.svg
+++ b/ietf/static/images/irtf-logo.svg
@@ -1 +1,76 @@
-R
\ No newline at end of file
+
+
+  
+  
+  
+    R
+    
+    
+    
+    
+    
+  
+
diff --git a/ietf/static/js/document_html.js b/ietf/static/js/document_html.js
new file mode 100644
index 000000000..2d0882df5
--- /dev/null
+++ b/ietf/static/js/document_html.js
@@ -0,0 +1,86 @@
+import {
+    Tooltip as Tooltip,
+    // Button as Button,
+    // Collapse as Collapse,
+    // ScrollSpy as ScrollSpy,
+    Tab as Tab
+} from "bootstrap";
+
+import Cookies from "js-cookie";
+import { populate_nav } from "./nav.js";
+
+const cookies = Cookies.withAttributes({ sameSite: "strict" });
+
+document.addEventListener("DOMContentLoaded", function (event) {
+    // handle point size slider
+    const cookie = "doc-ptsize-max";
+
+    function change_ptsize(ptsize) {
+        document.documentElement.style.setProperty(`--${cookie}`,
+            `${ptsize}pt`);
+        cookies.set(cookie, ptsize);
+    }
+
+    document.getElementById("ptsize")
+        .oninput = function () { change_ptsize(this.value) };
+
+    const ptsize = cookies.get(cookie);
+    change_ptsize(ptsize ? Math.min(Math.max(7, ptsize), 16) : 12);
+
+    // Use the Bootstrap tooltip plugin for all elements with a title attribute
+    const tt_triggers = document.querySelectorAll(
+        "[title]:not([title=''])");
+    [...tt_triggers].map(tt_el => {
+        const tooltip = Tooltip.getOrCreateInstance(tt_el);
+        tt_el.addEventListener("click", el => {
+            tooltip.hide();
+            tt_el.blur();
+        });
+    });
+
+    // Rewrite ids and hrefs to not contains dots (bug in bs5.2 scrollspy)
+    // See https://github.com/twbs/bootstrap/issues/34381
+    // TODO: check if this can be removed when bs5 is updated
+    const ids = document.querySelectorAll(
+        "#content [id^=section-], #content [id^=appendix-]");
+    [...ids].map(id_el => id_el.id = id_el.id.replaceAll(/\./g, "-"));
+    const hrefs = document.querySelectorAll(
+        "#content [href*='#section-'], #content [href*='#appendix-']"
+    );
+    [...hrefs].map(id_el => {
+        const href = new URL(id_el.href);
+        href.hash = href.hash.replaceAll(/\./g, "-");
+        id_el.href = href.href;
+    });
+
+    // Set up a nav pane
+    const toc_pane = document.getElementById("toc-nav");
+    populate_nav(toc_pane,
+        `#content h2, #content h3, #content h4, #content h5, #content h6
+         #content .h1, #content .h2, #content .h3, #content .h4, #content .h5, #content .h6`,
+        ["py-0"]);
+
+    // activate pref buttons selected by pref cookies
+    document.querySelectorAll(".btn-check")
+        .forEach(btn => {
+            const id = btn.id.replace("-radio", "");
+            if (cookies.get(btn.name) == id) {
+                btn.checked = true;
+            }
+            btn.addEventListener("click", el => {
+                cookies.set(btn.name, id);
+                window.location.reload();
+            });
+        });
+
+    // activate tab selected in prefs
+    let defpane;
+    try {
+        defpane = Tab.getOrCreateInstance(
+            `#${cookies.get("deftab")}-tab`);
+    } catch (err) {
+        defpane = Tab.getOrCreateInstance("#docinfo-tab");
+    };
+    defpane.show();
+    document.activeElement.blur();
+});
diff --git a/ietf/static/js/ietf.js b/ietf/static/js/ietf.js
index c30fe6fcb..17165bbfb 100644
--- a/ietf/static/js/ietf.js
+++ b/ietf/static/js/ietf.js
@@ -24,7 +24,7 @@ if (!process.env.BUILD_DEPLOY) {
 
 import Cookies from "js-cookie";
 
-import debounce from "lodash/debounce";
+import { populate_nav } from "./nav.js";
 
 // setup CSRF protection using jQuery
 function csrfSafeMethod(method) {
@@ -154,9 +154,9 @@ $(document)
 $(function () {
     const contentElement = $('#content.ietf-auto-nav');
     if (contentElement.length > 0) {
+        const heading_selector = "h2:not([style='display:none']):not(.navskip), h3:not([style='display:none']):not(.navskip), h4:not([style='display:none']):not(.navskip), h5:not([style='display:none']):not(.navskip), h6:not([style='display:none']):not(.navskip), .nav-heading:not([style='display:none']):not(.navskip)";
         const headings = contentElement
-            .find("h1:visible, h2:visible, h3:visible, h4:visible, h5:visible, h6:visible, .nav-heading:visible")
-            .not(".navskip");
+            .find(heading_selector);
 
         const contents = (headings.length > 0) &&
             ($(headings)
@@ -178,8 +178,6 @@ $(function () {
 
         if (pageTooTall || haveExtraNav) {
             // console.log("Enabling nav.");
-            let n = 0;
-            let last_level;
 
             contentElement
                 .attr("data-bs-offset", 0)
@@ -197,48 +195,7 @@ $(function () {
                 .children()
                 .last();
 
-            contentElement
-                .find("h1:visible, h2:visible, h3:visible, h4:visible, h5:visible, h6:visible, .nav-heading:visible")
-                .not(".navskip")
-                .each(function () {
-                    // Some headings have line breaks in them - only use first line in that case.
-                    const frag = $(this)
-                        .html()
-                        .split(" $(x)
-                            .text())
-                        .join(" ");
-
-                    if (text === undefined || text === "") {
-                        // Nothing to do for empty headings.
-                        return;
-                    }
-                    let id = $(this)
-                        .attr("id");
-
-                    if (id === undefined) {
-                        id = `autoid-${++n}`;
-                        $(this)
-                            .attr("id", id);
-                    }
-
-                    const level = parseInt(this.nodeName.substring(1)) - 1;
-                    if (!last_level) {
-                        last_level = level;
-                    }
-
-                    if (level > last_level) {
-                        last_level = level;
-                    } else
-                        while (level < last_level) {
-                            last_level--;
-                        }
-
-                    $(nav)
-                        .append(`${text}`);
-                });
+            populate_nav(nav[0], heading_selector);
 
             if (haveExtraNav) {
                 $('#righthand-panel').append('
'); @@ -246,18 +203,6 @@ $(function () { extraNav.remove(); } - $(document) - // Chrome apparently wants this debounced to something >10ms, - // otherwise the main view doesn't scroll? - .on("scroll", debounce(function () { - const item = $('#righthand-nav') - .find(".active") - .last(); - if (item.length) { - item[0].scrollIntoView({ block: "center", behavior: "smooth" }); - } - }, 100)); - // offset the scrollspy to account for the menu bar const contentOffset = contentElement ? contentElement.offset().top : 0; diff --git a/ietf/static/js/nav.js b/ietf/static/js/nav.js new file mode 100644 index 000000000..06b82f80d --- /dev/null +++ b/ietf/static/js/nav.js @@ -0,0 +1,100 @@ +import debounce from "lodash/debounce"; + +function make_nav() { + const nav = document.createElement("nav"); + nav.classList.add("nav-pills", "ps-3", "flex-column"); + return nav; +} + +function get_level(el) { + let h; + if (el.tagName.match(/^h\d/i)) { + h = el.tagName + } else { + el.classList.forEach(cl => { + if (cl.match(/^h\d/i)) { + h = cl; + return; + } + }); + } + return h.charAt(h.length - 1); +} + +export function populate_nav(nav, heading_selector, classes) { + // Extract section headings from document + const headings = document.querySelectorAll(heading_selector); + const min_level = Math.min(...Array.from(headings) + .map(get_level)); + + let nav_stack = [nav]; + let cur_level = 0; + let n = 0; + + headings.forEach(el => { + const level = get_level(el) - min_level; + + if (level < cur_level) { + while (level < cur_level) { + let nav = nav_stack.pop(); + cur_level--; + nav_stack[level].appendChild(nav); + } + } else { + while (level > cur_level) { + nav_stack.push(make_nav()); + cur_level++; + } + } + + const link = document.createElement("a"); + link.classList.add("nav-link", "ps-1", "d-flex", "hyphenate", + classes); + + if (!el.id) { + el.id = `autoid-${++n}`; + } + link.href = `#${el.id}`; + + const words = el.innerText.split(/\s+/); + let nr = ""; + if (words[0].includes(".")) { + nr = words.shift(); + } else if (words.length > 1 && words[1].includes(".")) { + nr = words.shift() + " " + words.shift(); + nr = nr.replace(/\s*Appendix\s*/, ""); + } + + if (nr) { + const number = document.createElement("div"); + number.classList.add("pe-1"); + number.textContent = nr; + link.appendChild(number); + } + + const text = document.createElement("div"); + text.classList.add("text-break"); + text.textContent = words.join(" "); + link.appendChild(text); + + nav_stack[level].appendChild(link); + }); + + for (var i = nav_stack.length - 1; i > 0; i--) { + nav_stack[i - 1].appendChild(nav_stack[i]); + } + + // Chrome apparently wants this debounced to something >10ms, + // otherwise the main view doesn't scroll? + document.addEventListener("scroll", debounce(function () { + const items = nav.querySelectorAll(".active"); + const item = [...items].pop(); + console.log(item); + if (item) { + item.scrollIntoView({ + block: "center", + behavior: "smooth" + }); + } + }, 100)); +} diff --git a/ietf/templates/doc/document_draft.html b/ietf/templates/doc/document_draft.html index 0bb597b60..f763159fc 100644 --- a/ietf/templates/doc/document_draft.html +++ b/ietf/templates/doc/document_draft.html @@ -26,7 +26,7 @@ {% block content %} {% origin %} {{ top|safe }} - {% include "doc/revisions_list.html" %} + {% include "doc/revisions_list.html" with document_html=document_html %}
{% if doc.rev != latest_rev %}
The information below is for an old version of the document.
@@ -38,363 +38,7 @@ {% endif %} {% endif %} - - - - - - - - - - - - - - - - - - - - {% if replaces or can_edit_stream_info %} - - - - - - - {% endif %} - {% if replaced_by %} - - - - - - - {% endif %} - {% if can_view_possibly_replaces %} - {% if possibly_replaces %} - - - - - - - {% endif %} - {% if possibly_replaced_by %} - - - - - - - {% endif %} - {% endif %} - - - - - - - {% if doc.get_state_slug != "rfc" and not snapshot %} - - - - - - - {% endif %} - - - - - - - {% for check in doc.submission.latest_checks %} - {% if check.passed != None and check.symbol.strip %} - - - - - - - {% endif %} - {% endfor %} - {% if review_assignments or can_request_review %} - - - - - - - {% endif %} - {% if conflict_reviews %} - - - - - - - {% endif %} - {% with doc.docextresource_set.all as resources %} - {% if resources or can_edit_stream_info or can_edit_individual %} - - - - - - - {% endif %} - {% endwith %} - + {% include "doc/document_info.html" %} + + + + + + + {% if document_html %} + + + + + + + {% if doc.rev != "00" %} + + + + + + + {% endif %} + {% endif %} + + + + + + + {% if not document_html %} + {# FIXME: This shows the date of the last history event, which is not what participants necessarily expect here. #} + + + + + + + {% endif %} + {% if replaces or not document_html and can_edit_stream_info %} + + + + + + + {% endif %} + {% if replaced_by %} + + + + + + + {% endif %} + {% if can_view_possibly_replaces %} + {% if possibly_replaces %} + + + + + + + {% endif %} + {% if possibly_replaced_by %} + + + + + + + {% endif %} + {% endif %} + + + + + + + {% if doc.get_state_slug != "rfc" and not snapshot %} + + + + + + + {% endif %} + + + + + + + {% for check in doc.submission.latest_checks %} + {% if check.passed != None and check.symbol.strip %} + + + + + + + {% endif %} + {% endfor %} + {% if not document_html %} + {% if review_assignments or can_request_review %} + + + + + + + {% endif %} + {% if conflict_reviews %} + + + + + + + {% endif %} + {% endif %} + {% with doc.docextresource_set.all as resources %} + {% if document_html and resources or document_html and doc.group and doc.group.list_archive %} + {% if resources or doc.group and doc.group.list_archive or can_edit_stream_info or can_edit_individual %} + + + + + + + {% endif %} + {% endif %} + {% endwith %} + \ No newline at end of file diff --git a/ietf/templates/doc/htmlized_base.html b/ietf/templates/doc/htmlized_base.html deleted file mode 100644 index e3624c0df..000000000 --- a/ietf/templates/doc/htmlized_base.html +++ /dev/null @@ -1,139 +0,0 @@ - -{% load ietf_filters static %} -{# Copyright The IETF Trust 2021, All Rights Reserved #} -{% load origin %} -{% origin %} - - - - - - {% block title %}No title{% endblock %} - - - - {% block pagehead %}{% endblock %} - {% include "base/icons.html" %} - - -
- {% block content %}{{ content|safe }}{% endblock %} - {% block content_end %}{% endblock %} -
- {% block footer %}{% endblock %} - {% block js %}{% endblock %} - - \ No newline at end of file diff --git a/ietf/templates/doc/revisions_list.html b/ietf/templates/doc/revisions_list.html index 04b12ed09..578263447 100644 --- a/ietf/templates/doc/revisions_list.html +++ b/ietf/templates/doc/revisions_list.html @@ -1,21 +1,38 @@ {# Copyright The IETF Trust 2015, All Rights Reserved #} {% load origin %} {% origin %} - - +{% endif %} \ No newline at end of file diff --git a/ietf/utils/test_runner.py b/ietf/utils/test_runner.py index 3e53808af..9bbc32764 100644 --- a/ietf/utils/test_runner.py +++ b/ietf/utils/test_runner.py @@ -174,14 +174,21 @@ def vnu_fmt_message(file, msg, content): def vnu_filter_message(msg, filter_db_issues, filter_test_issues): "True if the vnu message is a known false positive" - if filter_db_issues and re.search( - r"""^Forbidden\ code\ point\ U\+| - Illegal\ character\ in\ query:\ '\['| - 'href'\ on\ element\ 'a':\ Percentage\ \("%"\)\ is\ not\ followed| - ^Saw\ U\+\d+\ in\ stream| - ^Document\ uses\ the\ Unicode\ Private\ Use\ Area""", + if re.search( + r"""^Document\ uses\ the\ Unicode\ Private\ Use\ Area| + ^Element\ 'h.'\ not\ allowed\ as\ child\ of\ element\ 'pre'""", msg["message"], flags=re.VERBOSE, + ) or ( + filter_db_issues + and re.search( + r"""^Forbidden\ code\ point\ U\+| + Illegal\ character\ in\ query:\ '\['| + 'href'\ on\ element\ 'a':\ Percentage\ \("%"\)\ is\ not| + ^Saw\ U\+\d+\ in\ stream""", + msg["message"], + flags=re.VERBOSE, + ) ): return True diff --git a/package.json b/package.json index a5a960c9a..81879c64b 100644 --- a/package.json +++ b/package.json @@ -20,23 +20,23 @@ "bootstrap": "5.2.3", "bootstrap-icons": "1.10.2", "browser-fs-access": "0.31.1", - "caniuse-lite": "1.0.30001434", + "caniuse-lite": "1.0.30001435", "d3": "7.6.1", "file-saver": "2.0.5", - "highcharts": "10.3.1", + "highcharts": "10.3.2", "jquery": "3.6.1", "jquery-ui-dist": "1.13.2", "js-cookie": "3.0.1", "list.js": "2.3.1", "lodash": "4.17.21", "lodash-es": "4.17.21", - "luxon": "3.1.0", + "luxon": "3.1.1", "moment": "2.29.4", "moment-timezone": "0.5.39", "ms": "2.1.3", "murmurhash-js": "1.0.0", "naive-ui": "2.34.2", - "pinia": "2.0.26", + "pinia": "2.0.27", "pinia-plugin-persist": "1.0.0", "select2": "4.1.0-rc.0", "select2-bootstrap-5-theme": "1.3.0", @@ -50,6 +50,8 @@ }, "devDependencies": { "@faker-js/faker": "7.6.0", + "@parcel/optimizer-data-url": "2.8.0", + "@parcel/transformer-inline-string": "2.8.0", "@parcel/transformer-sass": "2.8.0", "@rollup/pluginutils": "5.0.2", "@vitejs/plugin-vue": "3.2.0", @@ -76,6 +78,9 @@ "distDir": "ietf/static/dist/ietf", "source": [ "ietf/static/css/datepicker.scss", + "ietf/static/css/document_html_inline.scss", + "ietf/static/css/document_html_referenced.scss", + "ietf/static/css/document_html_txt.scss", "ietf/static/css/ietf.scss", "ietf/static/css/jquery-ui.scss", "ietf/static/css/liaisons.css", @@ -83,6 +88,7 @@ "ietf/static/css/select2.scss", "ietf/static/images/arrow-ani.webp", "ietf/static/images/iab-logo-card.png", + "ietf/static/images/iab-logo.svg", "ietf/static/images/iesg-draft-state-diagram.png", "ietf/static/images/ietf-logo-card.png", "ietf/static/images/ietf-logo-nor-16-dev.png", @@ -100,6 +106,7 @@ "ietf/static/images/ietf-logo-nor.svg", "ietf/static/images/ietf-logo.svg", "ietf/static/images/irtf-logo-card.png", + "ietf/static/images/irtf-logo.svg", "ietf/static/js/agenda_filter.js", "ietf/static/js/agenda_materials.js", "ietf/static/js/agenda_personalize.js", @@ -110,6 +117,7 @@ "ietf/static/js/d3.js", "ietf/static/js/datepicker.js", "ietf/static/js/doc-search.js", + "ietf/static/js/document_html.js", "ietf/static/js/document_relations.js", "ietf/static/js/document_timeline.js", "ietf/static/js/draft-submit.js", diff --git a/requirements.txt b/requirements.txt index 155e5c676..a0e7953d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -63,6 +63,6 @@ tblib>=1.7.0 # So that the django test runner provides tracebacks tlds>=2022042700 # Used to teach bleach about which TLDs currently exist tqdm>=4.64.0 Unidecode>=1.3.4 -weasyprint>=52.5,<53 # Datatracker tests past on 54, but xml2rfc tests do not. +weasyprint>=53 xml2rfc>=3.12.4 xym>=0.6,<1.0 diff --git a/yarn.lock b/yarn.lock index 341d7cfc1..158abc9b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -824,6 +824,18 @@ __metadata: languageName: node linkType: hard +"@parcel/optimizer-data-url@npm:2.8.0": + version: 2.8.0 + resolution: "@parcel/optimizer-data-url@npm:2.8.0" + dependencies: + "@parcel/plugin": 2.8.0 + "@parcel/utils": 2.8.0 + isbinaryfile: ^4.0.2 + mime: ^2.4.4 + checksum: 998fb94ceea6c385c47a2337e6c911884f6580720609fd8e95b8036b8ce5860733a3028e3b7cff62e5f1b0aed2d86fda587cce8232d5e54514898fcb8e9e76ce + languageName: node + linkType: hard + "@parcel/optimizer-htmlnano@npm:2.8.0": version: 2.8.0 resolution: "@parcel/optimizer-htmlnano@npm:2.8.0" @@ -1145,6 +1157,15 @@ __metadata: languageName: node linkType: hard +"@parcel/transformer-inline-string@npm:2.8.0": + version: 2.8.0 + resolution: "@parcel/transformer-inline-string@npm:2.8.0" + dependencies: + "@parcel/plugin": 2.8.0 + checksum: e40616c55bbebfacc38a572d44028f79dab95abbe57dd27175bdce267894c7885fe1aab38012b1ce2c652f03d0d91ce5dbdc64c2625a12ae83cfff603558c259 + languageName: node + linkType: hard + "@parcel/transformer-js@npm:2.8.0": version: 2.8.0 resolution: "@parcel/transformer-js@npm:2.8.0" @@ -2075,10 +2096,10 @@ browserlist@latest: languageName: node linkType: hard -"caniuse-lite@npm:1.0.30001434": - version: 1.0.30001434 - resolution: "caniuse-lite@npm:1.0.30001434" - checksum: 7c9d2641e8e8f3ddf9af14c4ce47266a9d8fd1fc0243626049ff1b2eca4bf02938ff440813cc3feae3fa8d851ec8d1b9718044340c8d09bb4372d92d4f6b519c +"caniuse-lite@npm:1.0.30001435": + version: 1.0.30001435 + resolution: "caniuse-lite@npm:1.0.30001435" + checksum: ec88b9c37f66095e26ddb8b43110e9564ebccb6de77e495b8e8b9d64fdbfe37f7762be8fd2578c3ecc181a183a159578c9bd8e9b90eb15b44b78e8a6d0e92530 languageName: node linkType: hard @@ -3944,10 +3965,10 @@ browserlist@latest: languageName: node linkType: hard -"highcharts@npm:10.3.1": - version: 10.3.1 - resolution: "highcharts@npm:10.3.1" - checksum: 8a1cf9a363cfcd30b7ea64f6baba5c23c06998367b36eb9bee8b3903d49820df9de8251e3a6434f88a153d537243c2b41a55dc44e841c486f49e20bfe41a42d2 +"highcharts@npm:10.3.2": + version: 10.3.2 + resolution: "highcharts@npm:10.3.2" + checksum: 43cb42b24c3d6effc03d021f410a760322092ebd7012b60c2c475aa48a426af6c2a0a2937707bfc1c7aab089f0cc968c9aec3a9a1147e87ffc463b0adfb61ad4 languageName: node linkType: hard @@ -4402,6 +4423,13 @@ browserlist@latest: languageName: node linkType: hard +"isbinaryfile@npm:^4.0.2": + version: 4.0.10 + resolution: "isbinaryfile@npm:4.0.10" + checksum: a6b28db7e23ac7a77d3707567cac81356ea18bd602a4f21f424f862a31d0e7ab4f250759c98a559ece35ffe4d99f0d339f1ab884ffa9795172f632ab8f88e686 + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -4823,10 +4851,10 @@ browserlist@latest: languageName: node linkType: hard -"luxon@npm:3.1.0": - version: 3.1.0 - resolution: "luxon@npm:3.1.0" - checksum: f8a850b759ba7a2e009d904c522ed7bc264bf4add57578f8948e52a0ed96b627b025b5aad8032295b570ae19fac41f0ffab91bdb128715fb0cc020798a7ba886 +"luxon@npm:3.1.1": + version: 3.1.1 + resolution: "luxon@npm:3.1.1" + checksum: 388fb35d3c51a19d8b305a3338e7e74634b08562e7d2f9ed5c05a7f4bc9ee1c1ab6a2546b6d9c4c104516b24043757d65f5f3fe3d78b206fbf55a9586ab62230 languageName: node linkType: hard @@ -4888,6 +4916,15 @@ browserlist@latest: languageName: node linkType: hard +"mime@npm:^2.4.4": + version: 2.6.0 + resolution: "mime@npm:2.6.0" + bin: + mime: cli.js + checksum: 1497ba7b9f6960694268a557eae24b743fd2923da46ec392b042469f4b901721ba0adcf8b0d3c2677839d0e243b209d76e5edcbd09cfdeffa2dfb6bb4df4b862 + languageName: node + linkType: hard + "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -5520,9 +5557,9 @@ browserlist@latest: languageName: node linkType: hard -"pinia@npm:2.0.26": - version: 2.0.26 - resolution: "pinia@npm:2.0.26" +"pinia@npm:2.0.27": + version: 2.0.27 + resolution: "pinia@npm:2.0.27" dependencies: "@vue/devtools-api": ^6.4.5 vue-demi: "*" @@ -5535,7 +5572,7 @@ browserlist@latest: optional: true typescript: optional: true - checksum: 0d38cc0efc6572436c439491491df0e989182bad342fc3828d8865a42db6c6580c8f86663fcc47955bfeca5baf6e7f95e61b9b3f795ba161c19ef86fdc9b2841 + checksum: 29c862ea4304cdfeae385cbd2ab08100809aa9a79c147c4cb4085215601c0323e0ca664cea6abcaa0298e7ea53865117b6950ebb59224347fbc1683dbe956ac8 languageName: node linkType: hard @@ -6011,6 +6048,8 @@ browserlist@latest: "@fullcalendar/luxon2": 5.11.3 "@fullcalendar/timegrid": 5.11.3 "@fullcalendar/vue3": 5.11.3 + "@parcel/optimizer-data-url": 2.8.0 + "@parcel/transformer-inline-string": 2.8.0 "@parcel/transformer-sass": 2.8.0 "@popperjs/core": 2.11.6 "@rollup/pluginutils": 5.0.2 @@ -6021,7 +6060,7 @@ browserlist@latest: browser-fs-access: 0.31.1 browserlist: latest c8: 7.12.0 - caniuse-lite: 1.0.30001434 + caniuse-lite: 1.0.30001435 d3: 7.6.1 eslint: 8.28.0 eslint-config-standard: 17.0.0 @@ -6032,7 +6071,7 @@ browserlist@latest: eslint-plugin-promise: 6.1.1 eslint-plugin-vue: 9.8.0 file-saver: 2.0.5 - highcharts: 10.3.1 + highcharts: 10.3.2 html-validate: 7.10.0 jquery: 3.6.1 jquery-migrate: 3.4.0 @@ -6041,14 +6080,14 @@ browserlist@latest: list.js: 2.3.1 lodash: 4.17.21 lodash-es: 4.17.21 - luxon: 3.1.0 + luxon: 3.1.1 moment: 2.29.4 moment-timezone: 0.5.39 ms: 2.1.3 murmurhash-js: 1.0.0 naive-ui: 2.34.2 parcel: 2.8.0 - pinia: 2.0.26 + pinia: 2.0.27 pinia-plugin-persist: 1.0.0 pug: 3.0.2 sass: 1.56.1
DocumentType - {% if doc.get_state_slug == "rfc" and not snapshot %} - RFC - {{ doc.std_level }} - {% if published %} - ({{ doc.pub_date|date:"F Y" }}) - {% else %} - (Publication date unknown) - {% endif %} - {% if has_verified_errata %} - - Errata - - {% elif has_errata %} - - Errata - - {% endif %} - {% if obsoleted_by %}
Obsoleted by {{ obsoleted_by|urlize_related_source_list|join:", " }}
{% endif %} - {% if updated_by %}
Updated by {{ updated_by|urlize_related_source_list|join:", " }}
{% endif %} - {% if obsoletes %}
Obsoletes {{ obsoletes|urlize_related_target_list|join:", " }}
{% endif %} - {% if updates %}
Updates {{ updates|urlize_related_target_list|join:", " }}
{% endif %} - {% if status_changes %} -
Status changed by {{ status_changes|urlize_related_source_list|join:", " }}
- {% endif %} - {% if proposed_status_changes %} -
Proposed status changed by {{ proposed_status_changes|urlize_related_source_list|join:", " }}
- {% endif %} - {% if rfc_aliases %}
Also known as {{ rfc_aliases|join:", "|urlize_ietf_docs }}
{% endif %} - {% if draft_name %} -
- Was - {{ draft_name }} - {% if submission %}({{ submission|safe }}){% endif %} -
- {% endif %} - {% else %} - {% if snapshot and doc.doc.get_state_slug == 'rfc' %} - This is an older version of an Internet-Draft that was ultimately published as an RFC. - {% elif snapshot and doc.rev != latest_rev %} - This is an older version of an Internet-Draft whose latest revision is {{ doc.doc.get_state }} - {% else %} - {% if snapshot and doc.rev == latest_rev %}{{ doc.doc.get_state }}{% else %}{{ doc.get_state }}{% endif %} Internet-Draft - {% if submission %}({{ submission|safe }}){% endif %} - {% if resurrected_by %}- resurrect requested by {{ resurrected_by }}{% endif %} - {% endif %} - {% endif %} -
Author{{ doc.authors|pluralize }} - {% if can_edit_authors %} - Edit - {% endif %} - - {# Implementation that uses the current primary email for each author #} - {% for author in doc.authors %} - {% person_link author %}{% if not forloop.last %},{% endif %} - {% endfor %} -
Last updated - {{ doc.time|date:"Y-m-d" }} - {% if latest_revision and latest_revision.time|date:"Y-m-d" != doc.time|date:"Y-m-d" %} - (Latest revision {{ latest_revision.time|date:"Y-m-d" }}) - {% endif %} -
Replaces - {% if can_edit_stream_info and not snapshot %} - Edit - {% endif %} - - {% if replaces %} - {{ replaces|urlize_related_target_list|join:", " }} - {% else %} - (None) - {% endif %} -
- Replaced by - - - {{ replaced_by|urlize_related_source_list|join:", " }} -
- Possibly Replaces - - {% if can_edit_replaces and not snapshot %} - - Edit - - {% endif %} - - {{ possibly_replaces|urlize_related_target_list|join:", " }} -
- Possibly Replaced By - - {% if can_edit_replaces and not snapshot %} - {% comment %}Edit{% endcomment %} - {% endif %} - - {{ possibly_replaced_by|urlize_related_source_list|join:", " }} -
- Stream - - {% if can_change_stream and not snapshot %} - - Edit - - {% endif %} - - {{ stream_desc }} -
- Intended RFC status - - {% if can_edit_stream_info and not snapshot %} - - Edit - - {% endif %} - - {% if doc.intended_std_level %} - {{ doc.intended_std_level }} - {% else %} - - (None) - - {% endif %} -
- Formats - - - {% if doc.get_state_slug != "active" and doc.get_state_slug != "rfc" %} -
- Expired & archived -
- {% endif %} - {% include "doc/document_format_buttons.html" %} -
- {{ check.checker|title }} - - - {% if check.errors or check.warnings %} - - {{ check.symbol|safe }} - - {% else %} - - {{ check.symbol|safe }} - - {% endif %} - - {{ check.errors }} errors, {{ check.warnings }} warnings - - {% include "doc/yang-check-modal-overlay.html" %} -
- Reviews - - - {% for review_assignment in review_assignments %} - {% include "doc/review_assignment_summary.html" with current_doc_name=doc.name current_rev=doc.rev %} - {% endfor %} - {% if no_review_from_teams %} - {% for team in no_review_from_teams %} - {{ team.acronym.upper }}{% if not forloop.last %},{% endif %} - {% endfor %} - will not review this version - {% endif %} - {% if can_request_review or can_submit_unsolicited_review_for_teams %} -
- {% if can_request_review %} - - - - Request review - - {% endif %} - {% if can_submit_unsolicited_review_for_teams|length == 1 %} - - - - Submit unsolicited review - - {% elif can_submit_unsolicited_review_for_teams %} - - - - Submit unsolicited review - - {% endif %} -
- {% endif %} -
- IETF conflict review - - - {{ conflict_reviews|join:", "|urlize_ietf_docs }} -
- - Additional resources - - {% if can_edit_stream_info or can_edit_individual %} - - Edit - - {% endif %} - - {% if resources or doc.group and doc.group.list_archive %} - {% for resource in resources|dictsort:"display_name" %} - {% if resource.name.type.slug == 'url' or resource.name.type.slug == 'email' %} - - {% firstof resource.display_name resource.name.name %} - -
- {# Maybe make how a resource displays itself a method on the class so templates aren't doing this switching #} - {% else %} - - {% firstof resource.display_name resource.name.name %}: {{ resource.value|escape }} - -
- {% endif %} - {% endfor %} - {% if doc.group and doc.group.list_archive %} - {% if doc.group.list_archive|startswith:settings.MAILING_LIST_ARCHIVE_URL %} - - Mailing list discussion - - {% elif doc.group.list_archive|is_valid_url %} - - Mailing list discussion - - {% else %} - {{ doc.group.list_archive|urlencode }} - {% endif %} - {% endif %} - {% endif %} -
diff --git a/ietf/templates/doc/document_format_buttons.html b/ietf/templates/doc/document_format_buttons.html index 1dcf3008c..efd125766 100644 --- a/ietf/templates/doc/document_format_buttons.html +++ b/ietf/templates/doc/document_format_buttons.html @@ -1,7 +1,18 @@ {% if file_urls %} {% else %} diff --git a/ietf/templates/doc/document_html.html b/ietf/templates/doc/document_html.html index 1f0ed51fc..2811a19c2 100644 --- a/ietf/templates/doc/document_html.html +++ b/ietf/templates/doc/document_html.html @@ -1,92 +1,276 @@ -{% extends "doc/htmlized_base.html" %} {# Copyright The IETF Trust 2016, All Rights Reserved #} + +{% load analytical %} {% load origin %} {% load static %} -{% load ietf_filters %} -{% block pagehead %} - - -{% endblock %} -{% block morecss %} - .bgwhite { background-color: white; } - .bgred { background-color: #F44; } - .bggrey { background-color: #666; } - .bgbrown { background-color: #840; } - .bgorange { background-color: #FA0; } - .bgyellow { background-color: #EE0; } - .bgmagenta{ background-color: #F4F; } - .bgblue { background-color: #66F; } - .bgcyan { background-color: #4DD; } - .bggreen { background-color: #4F4; } - .draftcontent { margin-top:1em;} -{% endblock %} -{% block title %} - {% if doc.get_state_slug == "rfc" %} - RFC {{ doc.rfc_number }} - {{ doc.title }} - {% else %} - {{ doc.name }}-{{ doc.rev }} - {% endif %} -{% endblock %} -{% block content %} - - {% origin %} -
-
-
+{% load ietf_filters textfilters %} +{% origin %} + + + {% analytical_head_top %} + + + + {% if not snapshot and doc.get_state_slug == "rfc" %} + RFC {{ doc.rfc_number }} - {{ doc.title }} + {% else %} + {{ doc.name }}-{{ doc.rev }} + {% endif %} + + + {% if request.COOKIES.pagedeps == 'inline' %} + + + {% else %} + + {% if html %} + + {% endif %} + + {% endif %} + + + {% include "base/icons.html" %} + {% include "doc/opengraph.html" %} + {% analytical_head_bottom %} + + + {% analytical_body_top %} + + +
+
+ {% if html and request.COOKIES.htmlconf != 'txt' %} +
+
+ {{ html|safe }} +
+ {% else %} +
+
+ + {{ doc.htmlized|default:"Generation of htmlized text failed"|linkify|safe }} +
+ {% endif %} +
+
- {% if doc.meta %} -
-
{{ doc.supermeta|safe }}
-{{ doc.meta|safe }}
-
- {% endif %} -
{{ doc.htmlized|default:"Generation of htmlized text failed"|safe }}
-
-{% endblock %} -{% block js %} - -{% endblock %} \ No newline at end of file + {% analytical_body_bottom %} + + \ No newline at end of file diff --git a/ietf/templates/doc/document_info.html b/ietf/templates/doc/document_info.html new file mode 100644 index 000000000..0548f350b --- /dev/null +++ b/ietf/templates/doc/document_info.html @@ -0,0 +1,449 @@ +{# Copyright The IETF Trust 2016-2020, All Rights Reserved #} +{% load origin %} +{% load static %} +{% load ietf_filters %} +{% load person_filters %} +{% origin %} + +
Document{% if document_html %}Document type{% else %}Type{% endif %} + {% if doc.get_state_slug == "rfc" and not snapshot %} + RFC + {% if not document_html %} + - {{ doc.std_level }} + {% else %} + {{ doc.std_level }} + {% endif %} + + {% if published %} + {% if document_html %}
{% else %}({% endif %}{{ doc.pub_date|date:"F Y" }}{% if not document_html %}){% endif %} + {% else %} + (Publication date unknown) + {% endif %} + {% if document_html %}
{% endif %} + {% if has_verified_errata or has_errata %} + + Errata + + {% endif %} + {% if doc.related_ipr %} + IPR + {% endif %} + {% if obsoleted_by %}
Obsoleted by {{ obsoleted_by|urlize_related_source_list:document_html|join:", " }}
{% endif %} + {% if updated_by %}
Updated by {{ updated_by|urlize_related_source_list:document_html|join:", " }}
{% endif %} + {% if obsoletes %}
Obsoletes {{ obsoletes|urlize_related_target_list:document_html|join:", " }}
{% endif %} + {% if updates %}
Updates {{ updates|urlize_related_target_list:document_html|join:", " }}
{% endif %} + {% if status_changes %} +
Status changed by {{ status_changes|urlize_related_source_list|join:", " }}
+ {% endif %} + {% if proposed_status_changes %} +
Proposed status changed by {{ proposed_status_changes|urlize_related_source_list|join:", " }}
+ {% endif %} + {% if rfc_aliases %}
Also known as {{ rfc_aliases|join:", "|urlize_ietf_docs }}
{% endif %} + {% if draft_name %} +
+ Was + {{ draft_name }} + {% if submission %}({{ submission|safe }}){% endif %} +
+ {% endif %} + {% else %} + {% if snapshot and doc.doc.get_state_slug == 'rfc' %} + This is an older version of an Internet-Draft that was ultimately published as {{doc.doc.canonical_name|prettystdname}}. + {% elif snapshot and doc.rev != latest_rev %} + This is an older version of an Internet-Draft whose latest revision state is "{{ doc.doc.get_state }}". + {% else %} + {% if snapshot and doc.rev == latest_rev %}{{ doc.doc.get_state }}{% else %}{{ doc.get_state }}{% endif %} Internet-Draft + {% if submission %}({{ submission|safe }}){% endif %} + {% if resurrected_by %}- resurrect requested by {{ resurrected_by }}{% endif %} + {% endif %} + {% endif %} + {% if doc.get_state_slug != "active" and doc.get_state_slug != "rfc" %} +
+ Expired & archived +
+ {% endif %} +
Select version + {% include "doc/revisions_list.html" with document_html=document_html %} +
Compare versions + Inline + Side-by-side +
Author{{ doc.authors|pluralize }} + {% if can_edit_authors %} + Edit + {% endif %} + + {# Implementation that uses the current primary email for each author #} + {% for author in doc.authors %} + {% person_link author %}{% if not forloop.last %},{% endif %} + {% endfor %} + {% if document_html and not snapshot or document_html and doc.rev == latest_rev%} +
+ Email authors + {% endif %} +
Last updated + {{ doc.time|date:"Y-m-d" }} + {% if latest_revision and latest_revision.time|date:"Y-m-d" != doc.time|date:"Y-m-d" %} + (Latest revision {{ latest_revision.time|date:"Y-m-d" }}) + {% endif %} +
Replaces + {% if can_edit_stream_info and not snapshot %} + Edit + {% endif %} + + {% if replaces %} + {% if document_html %} + {{ replaces|urlize_related_target_list:document_html|join:"
" }} + {% else %} + {{ replaces|urlize_related_target_list:document_html|join:", " }} + {% endif %} + {% else %} + (None) + {% endif %} +
+ Replaced by + + + {% if document_html %} + {{ replaced_by|urlize_related_source_list:document_html|join:"
" }} + {% else %} + {{ replaced_by|urlize_related_source_list:document_html|join:", " }} + {% endif %} +
+ Possibly Replaces + + {% if can_edit_replaces and not snapshot %} + + Edit + + {% endif %} + + {% if document_html %} + {{ possibly_replaces|urlize_related_target_list:document_html|join:"
" }} + {% else %} + {{ possibly_replaces|urlize_related_target_list:document_html|join:", " }} + {% endif %} +
+ Possibly Replaced By + + {% if can_edit_replaces and not snapshot %} + {% comment %}Edit{% endcomment %} + {% endif %} + + {% if document_html %} + {{ possibly_replaced_by|urlize_related_source_list:document_html|join:"
" }} + {% else %} + {{ possibly_replaced_by|urlize_related_source_list:document_html|join:", " }} + {% endif %} +
+ RFC stream + + {% if can_change_stream and not snapshot %} + + Edit + + {% endif %} + + {% if stream_desc != "(None)" %} + {% if doc.stream.name|lower in 'iab,irtf,ise,editorial' %} + + {% endif %} + {% if document_html %} + {% if doc.stream.name|lower in 'iab,ietf,irtf' %} + {{ doc.stream.name|upper }} Logo + {% else %} + {{ stream_desc }} + {% endif %} + {% else %} + {{ stream_desc }} + {% endif %} + {% if doc.stream.name|lower in 'iab,irtf,ise,editorial' %} + + {% endif %} + {% else %} + {{ stream_desc }} + {% endif %} +
+ Intended RFC status + + {% if can_edit_stream_info and not snapshot %} + + Edit + + {% endif %} + + {% if doc.intended_std_level %} + {{ doc.intended_std_level }} + {% else %} + + (None) + + {% endif %} +
+ {% if document_html %}Other formats{% else %}Formats{% endif %} + + + {% if document_html %} + {% include "doc/document_format_buttons.html" with skip_format="htmlized" %} + {% else %} + {% include "doc/document_format_buttons.html" %} + {% endif %} +
+ {{ check.checker|title }} + + + {% if check.errors or check.warnings %} + + {{ check.symbol|safe }} + + {% else %} + + {{ check.symbol|safe }} + + {% endif %} + + {{ check.errors }} errors, {{ check.warnings }} warnings + + {% include "doc/yang-check-modal-overlay.html" %} +
+ Reviews + + + {% for review_assignment in review_assignments %} + {% include "doc/review_assignment_summary.html" with current_doc_name=doc.name current_rev=doc.rev %} + {% endfor %} + {% if no_review_from_teams %} + {% for team in no_review_from_teams %} + {{ team.acronym.upper }}{% if not forloop.last %},{% endif %} + {% endfor %} + will not review this version + {% endif %} + {% if can_request_review or can_submit_unsolicited_review_for_teams %} +
+ {% if can_request_review %} + + + + Request review + + {% endif %} + {% if can_submit_unsolicited_review_for_teams|length == 1 %} + + + + Submit unsolicited review + + {% elif can_submit_unsolicited_review_for_teams %} + + + + Submit unsolicited review + + {% endif %} +
+ {% endif %} +
+ IETF conflict review + + + {{ conflict_reviews|join:", "|urlize_ietf_docs }} +
+ + Additional resources + + {% if can_edit_stream_info or can_edit_individual %} + + Edit + + {% endif %} + + {% if resources or doc.group and doc.group.list_archive %} + {% for resource in resources|dictsort:"display_name" %} + {% if resource.name.type.slug == 'url' or resource.name.type.slug == 'email' %} + + {% firstof resource.display_name resource.name.name %} + +
+ {# Maybe make how a resource displays itself a method on the class so templates aren't doing this switching #} + {% else %} + + {% firstof resource.display_name resource.name.name %}: {{ resource.value|escape }} + +
+ {% endif %} + {% endfor %} + {% if doc.group and doc.group.list_archive %} + {% if doc.group.list_archive|startswith:settings.MAILING_LIST_ARCHIVE_URL %} + + Mailing list discussion + + {% elif doc.group.list_archive|is_valid_url %} + + Mailing list discussion + + {% else %} + {{ doc.group.list_archive|urlencode }} + {% endif %} + {% endif %} + {% endif %} +