| <!doctype html> | |
| <html> | |
| <head> | |
| <meta name="viewport" content="width=device-width"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/3.0.1/github-markdown.css" /> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script> | |
| <style> | |
| @media (max-width: 767px) { | |
| .markdown-body { | |
| padding: 15px; | |
| } | |
| #search { | |
| max-width: 85%; | |
| } | |
| } | |
| body { | |
| overflow: scroll; | |
| } | |
| .markdown-body { | |
| box-sizing: border-box; | |
| min-width: 200px; | |
| max-width: 980px; | |
| margin: 0 auto; | |
| padding: 45px; | |
| } | |
| #search { | |
| border: 1px solid #d1d5da; | |
| padding-left: 30px; | |
| overflow: hidden; | |
| } | |
| .searchCondition { | |
| display: flex; | |
| flex-wrap: wrap; | |
| } | |
| .searchCondition > div { | |
| margin-right: 30px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="app"> | |
| <article class="markdown-body"> | |
| <div class="searchCondition"> | |
| <div> | |
| <form style="display:flex;"> | |
| <label for="search" style="margin-right: 3px;" >search:</label> | |
| <div style="position: relative;"> | |
| <input id="search" placeholder="Search all options" v-model="searchCondition"> | |
| <svg style="position: absolute; left: 8px; top: 7px;" class="octicon octicon-search subnav-search-icon" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"> | |
| <path fill-rule="evenodd" d="M15.7 13.3l-3.81-3.83A5.93 5.93 0 0 0 13 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 0 0 0-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z"></path> | |
| </svg> | |
| </div> | |
| </form> | |
| </div> | |
| <div> | |
| <label for="stable">stable: </label> | |
| <input type="checkbox" id="stable" v-model="shouldStable"> | |
| </div> | |
| </div> | |
| <div v-html="aboutHtml"></div> | |
| <div v-html="configurationAboutHtml"></div> | |
| <div v-html="outputHtml"></div> | |
| </article> | |
| </div> | |
| <script> | |
| const ConfigurationMdUrl = 'https://raw.githubusercontent.com/rust-lang/rustfmt/master/Configurations.md'; | |
| const UrlHash = window.location.hash.replace(/^#/, ''); | |
| new Vue({ | |
| el: '#app', | |
| data() { | |
| const configurationDescriptions = []; | |
| configurationDescriptions.links = {}; | |
| return { | |
| aboutHtml: '', | |
| configurationAboutHtml: '', | |
| searchCondition: UrlHash, | |
| configurationDescriptions, | |
| shouldStable: false | |
| } | |
| }, | |
| computed: { | |
| outputHtml() { | |
| const ast = this.configurationDescriptions | |
| .filter(({ head, text, stable }) => { | |
| if ( | |
| text.includes(this.searchCondition) === false && | |
| head.includes(this.searchCondition) === false | |
| ) { | |
| return false; | |
| } | |
| return (this.shouldStable) | |
| ? stable === true | |
| : true; | |
| }) | |
| .reduce((stack, { value }) => { | |
| return stack.concat(value); | |
| }, []); | |
| ast.links = {}; | |
| return marked.parser(ast); | |
| } | |
| }, | |
| created: async function() { | |
| const res = await axios.get(ConfigurationMdUrl); | |
| const { | |
| about, | |
| configurationAbout, | |
| configurationDescriptions | |
| } = parseMarkdownAst(res.data); | |
| this.aboutHtml = marked.parser(about); | |
| this.configurationAboutHtml = marked.parser(configurationAbout); | |
| this.configurationDescriptions = configurationDescriptions; | |
| }, | |
| mounted() { | |
| if (UrlHash === '') return; | |
| const interval = setInterval(() => { | |
| const target = document.querySelector(`#${UrlHash}`); | |
| if (target != null) { | |
| target.scrollIntoView(true); | |
| clearInterval(interval); | |
| } | |
| }, 100); | |
| } | |
| }); | |
| const extractDepthOnes = (ast) => { | |
| return ast.reduce((stack, next) => { | |
| if (next.depth === 1) { | |
| stack.push([]); | |
| } | |
| const lastIndex = stack.length - 1; | |
| stack[lastIndex].push(next); | |
| return stack; | |
| }, []); | |
| } | |
| const extractDepthTwos = (ast) => { | |
| return ast.map((elem) => { | |
| return elem.reduce((stack, next) => { | |
| if (next.depth === 2) { | |
| stack.push([]); | |
| } | |
| const lastIndex = stack.length - 1; | |
| stack[lastIndex].push(next); | |
| return stack; | |
| }, | |
| [[]]); | |
| }); | |
| } | |
| const createHeadAndValue = (ast) => { | |
| return ast.map((elem) => { | |
| return elem.map((val) => { | |
| return { | |
| head: val[0].text, | |
| value: val, | |
| stable: val.some((elem) => { | |
| return !!elem.text && elem.text.includes("**Stable**: Yes") | |
| }), | |
| text: val.reduce((result, next) => { | |
| return next.text != null | |
| ? `${result} ${next.text}` | |
| : result; | |
| }, '') | |
| } | |
| }); | |
| }) | |
| } | |
| const parseMarkdownAst = (rawMarkdown) => { | |
| const ast = marked.lexer(rawMarkdown); | |
| const depthOnes = extractDepthOnes(ast); | |
| const depthTwos = extractDepthTwos(depthOnes); | |
| const [ | |
| abouts, configurations | |
| ] = createHeadAndValue(depthTwos); | |
| const about = abouts[0].value; | |
| about.links = {}; | |
| const [ | |
| configurationAbout, ...configurationDescriptions | |
| ] = configurations; | |
| configurationAbout.value.links = {}; | |
| return { | |
| about, | |
| configurationAbout: configurationAbout.value, | |
| configurationDescriptions | |
| }; | |
| } | |
| </script> | |
| </body> | |
| </html> |