summaryrefslogtreecommitdiff
path: root/web/cgit-extra.html
diff options
context:
space:
mode:
Diffstat (limited to 'web/cgit-extra.html')
-rw-r--r--web/cgit-extra.html278
1 files changed, 278 insertions, 0 deletions
diff --git a/web/cgit-extra.html b/web/cgit-extra.html
new file mode 100644
index 0000000..cb30b54
--- /dev/null
+++ b/web/cgit-extra.html
@@ -0,0 +1,278 @@
+<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js" async></script>
+<script>
+const VROOT = "/";
+
+function repoPathFromLink( a ) {
+ const href = new URL( a.getAttribute( "href" ), location.href );
+ let p = href.pathname;
+ if( !p.startsWith( VROOT ) ) return null;
+ p = p.slice( VROOT.length );
+ p = p.replace( /\/+$/, "" );
+ if( !p ) return null;
+ return p;
+}
+
+function branchFromForm() {
+ const formWrap = document.querySelector( ".form" );
+ const form = formWrap.querySelector( "form" );
+
+ const branchEl = form.querySelector( "select" );
+ if( !branchEl )
+ return "";
+
+ const branch = branchEl.value;
+ if( !branch )
+ return "";
+
+ return branch;
+}
+
+async function getFile( repo, branch, file ) {
+ try {
+ const res = await fetch( `https://${location.host}/${repo}/plain/${file}` );
+ if( !res.ok )
+ return null;
+ return await res.text();
+ } catch( e ) {
+ return null;
+ }
+}
+
+function isWhiteChar( char ) {
+ return char === " " || char === "\t" || char === "\n" || char === "\r";
+}
+
+function removeTrailingWhiteSpace( text ) {
+ while( text.length > 1
+ && isWhiteChar( text[text.length - 1] )
+ && isWhiteChar( text[text.length - 2] )
+ ) {
+ text = text.slice( 0, -1 );
+ }
+ return text;
+}
+
+async function getReadmeFile( repo, branch ) {
+ let file = await getFile( repo, branch, "README.md" );
+ if( file )
+ return { file: removeTrailingWhiteSpace( file ), isMd: true };
+
+ file = await getFile( repo, branch, "README" );
+ if( file )
+ return { file: removeTrailingWhiteSpace( file ), isMd: false };
+
+ return null;
+}
+
+function isIndex( repo ) {
+ let path = location.pathname;
+ return path.replaceAll( "/", "" ) === repo;
+}
+function parseLink( text, start ) {
+ let isImg = 0;
+
+ const open = text.indexOf( "[", start );
+ if( open === -1 ) return null;
+ const close = text.indexOf( "]", open );
+ if( close === -1 ) return null;
+
+ const lopen = close + 1;
+ if( text[lopen] != '(' ) return { continue: true, end: lopen };
+ const lclose = text.indexOf( ")", lopen );
+ if( lclose === -1 ) return { continue: true, end: lopen };
+
+ const title = text.substring( open + 1, close );
+ const link = text.substring( lopen + 1, lclose );
+
+ if( open > 0 && text[open-1] == "!" ) isImg = 1;
+ return { title, link, start: open - (isImg?1:0), end: lclose + 1, img: isImg };
+}
+
+function parseLinks( text ) {
+ let start = 0;
+ const links = [];
+
+ while( true ) {
+ const res = parseLink( text, start );
+ if( !res )
+ break;
+
+ if( res.continue ) {
+ start = res.end + 1;
+ continue;
+ }
+
+ links.push( res );
+ start = res.end + 1;
+ }
+
+ return links;
+}
+
+function replaceLinks( text, links ) {
+ let offset = 0;
+ for( let link of links ) {
+ const start = link.start + offset;
+ const end = link.end + offset;
+
+ const textFirst = text.slice( 0, start );
+ const textSecond = text.slice( end );
+
+ let textMid = "";
+ if( link.img ) {
+ textMid = `<img src="${link.link}" alt="${link.title}" />`;
+ } else {
+ textMid = `<a href="${link.link}">${link.title}</a>`;
+ }
+
+ text = textFirst + textMid + textSecond;
+ offset += textMid.length - ( link.end - link.start );
+ }
+
+ return text;
+}
+
+function escapeHtml( html ) {
+ const entityMap = {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&quot;',
+ "'": '&#39;',
+ '/': '&#x2F;',
+ '`': '&#x60;',
+ '=': '&#x3D;'
+ };
+
+ return String( html ).replace( /[&<>"'`=\/]/g, ( s, i, str ) => {
+ if( str.slice( i, i + 6 ).toLowerCase() === "<code>" ) return "<";
+ if( s === '<' ) {
+ if( str.slice( i, i + 6 ).toLowerCase() === "<code>"
+ || str.slice( i, i + 7 ).toLowerCase() === "</code>" ) {
+ return "<";
+ }
+ }
+
+ if( s === '>' ) {
+ if( str.slice( i - 5, i + 1 ).toLowerCase() === "<code>"
+ || str.slice( i - 6, i + 1 ).toLowerCase() === "</code>" ) {
+ return ">";
+ }
+ }
+
+ if( s === '/' ) {
+ if( str.slice( i - 1, i + 6 ).toLowerCase() === "</code>" ) {
+ return "/";
+ }
+ }
+
+ return entityMap[s];
+ });
+}
+
+function replaceCodeTicks( text ) {
+ let inBlock = 0;
+ let tick = text.indexOf( "```" );
+ while( tick != -1 ) {
+ if( !inBlock ) {
+ let second = text.slice( tick + 3 );
+ while( isWhiteChar( second[0] ) )
+ second = second.slice( 1 );
+ text = text.slice( 0, tick ) + "<code>" + second;
+ tick = text.indexOf( "```", tick );
+ } else {
+ let second = text.slice( tick + 3 );
+ while( second.length > 1 && isWhiteChar( second[0] ) && isWhiteChar( second[1] ) )
+ second = second.slice( 1 );
+ text = text.slice( 0, tick ) + "</code>" + second;
+ tick = text.indexOf( "```", tick );
+ }
+ inBlock = !inBlock;
+ }
+
+ return escapeHtml( text );
+}
+
+function runHighlight() {
+ if( hljs === undefined || !hljs )
+ return setTimeout( runHighlight, 200 );
+
+ hljs.highlightAll();
+}
+
+
+function createReadme( text, isMd ) {
+ const tr = document.createElement( "tr" );
+ const td = document.createElement( "td" );
+ const pre = document.createElement( "pre" );
+ tr.appendChild( td );
+ td.appendChild( pre );
+ td.setAttribute( "colspan", "4" );
+ if( !isMd )
+ pre.innerHTML = text;
+ else {
+ const noTicks = replaceCodeTicks( text );
+ const links = parseLinks( noTicks );
+ pre.innerHTML = replaceLinks( noTicks, links );
+ }
+
+ pre.style.whiteSpace = "pre-wrap";
+ pre.style.fontFamily = "JPN12";
+ pre.style.padding = 0;
+ pre.style.marginTop = "0px";
+ pre.style.marginBottom = "0px";
+ tr.classList.add( "nohover" );
+ tr.style.backgroundColor = "#808080"
+ const list = document.querySelector( ".list" );
+ const tbody = list.querySelector( "tbody" );
+
+ let insertTarget = tbody.children[3];
+ for( let i = 2; i < tbody.children.length; ++i ) {
+ if( tbody.children[i-1].classList.contains( "nohover" ) ) {
+ insertTarget = tbody.children[i];
+ break;
+ }
+ }
+
+ tbody.insertBefore( tr, insertTarget );
+
+ const th = document.createElement( "th" );
+ th.classList.add( "left" );
+ th.setAttribute( "colspan", "4" );
+ th.innerHTML = "Readme";
+ tbody.insertBefore( th, tr );
+
+ //setTimeout( () => { setTimeout( runHighlight ) } );
+}
+
+function isCodeView( repo ) {
+ return location.pathname.includes( `/${repo}/tree` );
+}
+
+const main = async () => {
+ const mainEl = document.querySelector( ".main" );
+ if( !mainEl )
+ return;
+
+ const repoEl = mainEl.querySelectorAll( "a" )[1];
+ if( !repoEl )
+ return;
+
+ const repo = repoPathFromLink( repoEl );
+ if( !repo )
+ return;
+
+ if( isCodeView( repo ) )
+ return setTimeout( runHighlight );
+
+ if( !isIndex( repo ) )
+ return;
+
+ const branch = branchFromForm();
+ const { file, isMd } = await getReadmeFile( repo, branch );
+ createReadme( file, isMd );
+}
+
+setTimeout( main );
+</script>
+<link rel="stylesheet" href="https://networkheaven.net/static/highlight.css" type="text/css" />