From 7dc90564012a055cd0f12e340828763406b9c316 Mon Sep 17 00:00:00 2001 From: Paul Hibbitts Date: Fri, 21 Jun 2024 11:05:51 -0700 Subject: [PATCH] Improved Search plugin results list presentation, including source page title when appropriate. --- docs/assets/js/search.js | 126 +++++++++++++++++++++++++++++------ docs/assets/js/search.min.js | 2 +- 2 files changed, 108 insertions(+), 20 deletions(-) diff --git a/docs/assets/js/search.js b/docs/assets/js/search.js index 64df3bf..594c4d5 100644 --- a/docs/assets/js/search.js +++ b/docs/assets/js/search.js @@ -198,6 +198,65 @@ return keyword; } + // Function to convert string to title case and replace hyphens with spaces + // This code was developed with the assistance of ChatGPT, an AI language model by OpenAI + function convertToTitle(str) { + return str + .split('-') // Split the string by hyphens + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) // Capitalize the first letter and lower case the rest + .join(' '); // Join the words with spaces + } + + // Function to strip common Markdown markup + // This code was developed with the assistance of ChatGPT, an AI language model by OpenAI + function stripCommonMarkdown(markdown) { + // Regular expressions for common Markdown elements + const regexes = [ + { pattern: /(\*\*|__)(.*?)\1/g, replacement: '$2' }, // Bold: **text** or __text__ + { pattern: /(\*|_)(.*?)\1/g, replacement: '$2' }, // Italic: *text* or _text_ + { pattern: /[-+*]\s+(.*?)/g, replacement: '$1' }, // Unordered lists: - item, + item, * item + { pattern: /\d+\.\s+(.*?)/g, replacement: '$1' }, // Ordered lists: 1. item + { pattern: /^#{1,6}\s+(.*)/gm, replacement: '$1' } // Headers: # Header, ## Header, etc. + ]; + + // Apply all regular expressions to the input text + let plainText = markdown; + regexes.forEach(({ pattern, replacement }) => { + plainText = plainText.replace(pattern, replacement); + }); + + // Trim leading/trailing whitespace and return + return plainText.trim(); + } + + // Function to replace Markdown links with their titles and remove Markdown images + // This code was developed with the assistance of ChatGPT, an AI language model by OpenAI + function replaceMarkdownLinksWithTitlesandRemoveImages(content) { + // Regular expression to match both Markdown images and links + // Capture images separately to identify and remove them + const markdownLinkRegex = /(!?\[([^\]]+)\]\(([^)]+)\))/g; + + return content.replace(markdownLinkRegex, (match, fullMatch, title, url) => { + // Check if it's an image link by the presence of '!' at the start + if (fullMatch.startsWith('!')) { + // Return an empty string to remove the image + return ''; + } + // Otherwise, replace the link with its title + return title; + }); + } + + // Function to strip all HTML tags from a string + // This code was developed with the assistance of ChatGPT, an AI language model by OpenAI + function stripHtmlTags(content) { + // Regular expression to match HTML tags + const htmlTagRegex = /<[^>]*>/g; + + // Replace all HTML tags with an empty string + return content.replace(htmlTagRegex, ''); + } + /** * @param {String} query Search query * @returns {Array} Array of results @@ -222,14 +281,16 @@ var handlePostTitle = ''; var handlePostContent = ''; var postTitle = post.title && post.title.trim(); - var postContent = post.body && post.body.trim(); + const postContent = stripHtmlTags(stripCommonMarkdown(replaceMarkdownLinksWithTitlesandRemoveImages(post.body && post.body.trim()))); var postUrl = post.slug || ''; + const postPageSlug = postUrl.split('/')[1].split('?')[0].replace('0', ''); + const postPageTitle = convertToTitle(postPageSlug); - // Skip posts that contain iframes, Font Awesome icons, embedly cards, or Markdown images + // Skip posts that contain iframes, Font Awesome icons, or embedly cards // console.log(postContent); - var isImage = /!\[[^\]]*\]\([^)]*\)/g.test(postContent); // Check if it's a Markdown image + // const isImage = /!\[[^\]]*\]\([^)]*\)/g.test(postContent); // Check if it's a Markdown image - if (postContent.includes('iframe') || postContent.includes(':fas') || postContent.includes(':fab') || postContent.includes('embedly-card') || isImage) { + if (postContent.includes('iframe') || postContent.includes(':fas') || postContent.includes(':fab') || postContent.includes('embedly-card')) { return; } @@ -270,26 +331,53 @@ if (postContent && end > postContent.length) { end = postContent.length; } - - var matchContent = - handlePostContent && - '...' + - handlePostContent - .substring(start, end) - .replace( - regEx, - function (word) { return ("" + word + ""); } - ) + - '...'; - - resultStr += matchContent; + + // This code was developed with the assistance of ChatGPT, an AI language model by OpenAI + const matchContent = handlePostContent && (() => { + // Extract the substring where the match will be applied + const contentSegment = handlePostContent.substring(start, end); + + // Find the first occurrence of the word using the regular expression + const match = contentSegment.match(regEx); + + if (match) { + // Get the position of the first match + const matchIndex = contentSegment.indexOf(match[0]); + + // Split the content segment into before, match, and after parts + const beforeMatch = contentSegment.substring(0, matchIndex); + const firstMatch = contentSegment.substring(matchIndex, matchIndex + match[0].length); + const afterMatch = contentSegment.substring(matchIndex + match[0].length); + + // Return the reassembled string with the first match wrapped in tags + return '...' + + beforeMatch + + `${firstMatch}` + + afterMatch + + '...'; + } + + // If no match is found, return the original segment surrounded by ellipses + return '...' + contentSegment + '...'; + })(); + + resultStr += matchContent; } }); + // This code was developed with the assistance of ChatGPT, an AI language model by OpenAI + // Only prepend postPageTitle when it is not empty and not equal to handlePostTitle (case insensitive) if (matchesScore > 0) { - var matchingPost = { + const matchingPost = { title: handlePostTitle, - content: postContent ? resultStr : '', + content: ( + // Convert both postPageTitle and handlePostTitle to lowercase for case-insensitive comparison + postPageTitle && + postPageTitle.toLowerCase() !== handlePostTitle.toLowerCase() && + postPageTitle.toLowerCase() !== 'readme' // Exclude 'ReadMe' from being prepended + ? `${postPageTitle}
` + : '' + ) + (postContent ? resultStr : ''), url: postUrl, score: matchesScore, }; diff --git a/docs/assets/js/search.min.js b/docs/assets/js/search.min.js index 8bcf8df..5672dc9 100644 --- a/docs/assets/js/search.min.js +++ b/docs/assets/js/search.min.js @@ -1 +1 @@ -!function(){function e(e){return e.replace(//,"").replace(/{docsify-ignore}/,"").replace(//,"").replace(/{docsify-ignore-all}/,"").trim()}var n,t={},a={EXPIRE_KEY:"docsify.search.expires",INDEX_KEY:"docsify.search.index"};function r(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,function(e){return n[e]})}function i(e){return e.text||"table"!==e.type||(e.cells.unshift(e.header),e.text=e.cells.map(function(e){return e.join(" | ")}).join(" |\n ")),e.text}function o(e){return e.text||"list"!==e.type||(e.text=e.raw),e.text}function s(e){return e&&e.normalize?e.normalize("NFD").replace(/[\u0300-\u036f]/g,""):e}function c(n,s){var c,d,l="auto"===n.paths,p=l?(c=s.router,d=[],Docsify.dom.findAll(".sidebar-nav a:not(.section-link):not([data-nosearch])").forEach(function(e){var n=e.href,t=e.getAttribute("href"),a=c.parse(n).path;a&&-1===d.indexOf(a)&&!Docsify.util.isAbsolutePath(t)&&d.push(a)}),d):n.paths,h="";if(p.length&&l&&n.pathNamespaces){var u=p[0];if(Array.isArray(n.pathNamespaces))h=n.pathNamespaces.filter(function(e){return u.slice(0,e.length)===e})[0]||h;else if(n.pathNamespaces instanceof RegExp){var f=u.match(n.pathNamespaces);f&&(h=f[0])}var m=-1===p.indexOf(h+"/"),g=-1===p.indexOf(h+"/README");m&&g&&p.unshift(h+"/")}else -1===p.indexOf("/")&&-1===p.indexOf("/README")&&p.unshift("/");var v,b,$,x=((v=n.namespace)?a.EXPIRE_KEY+"/"+v:a.EXPIRE_KEY)+h,y=((b=n.namespace)?a.INDEX_KEY+"/"+b:a.INDEX_KEY)+h;if(t={},localStorage.getItem(x)=0||i>=0){t+=a>=0?3:i>=0?2:0,i<0&&(i=0);var o=0,u=0;u=0==(o=i<11?0:i-10)?70:i+e.length+60,h&&u>h.length&&(u=h.length),c+=l&&"..."+l.substring(o,u).replace(n,function(e){return''+e+""})+"..."}}),t>0)){var m={title:d,content:h?c:"",url:u,score:t};a.push(m)}}(c);return a.sort(function(e,n){return n.score-e.score})}(e),h="";p.forEach(function(e){h+='
\n\n

'+e.title+"

\n

"+e.content+"

\n
\n
"}),i.classList.add("show"),o.classList.add("show"),i.innerHTML=h||'

'+d+"

",n.hideOtherSidebarContent&&(c&&c.classList.add("hide"),l&&l.classList.add("hide"))}function p(e){n=e}var h={placeholder:"Type to search",noData:"No matches found.",paths:"auto",depth:2,maxAge:864e5,hideOtherSidebarContent:!1,namespace:void 0,pathNamespaces:void 0},u=function(e,t){var a=Docsify.util,r=t.config.search||h;Array.isArray(r)?h.paths=r:"object"==typeof r&&(h.paths=Array.isArray(r.paths)?r.paths:"auto",h.maxAge=a.isPrimitive(r.maxAge)?r.maxAge:h.maxAge,h.placeholder=r.placeholder||h.placeholder,h.noData=r.noData||h.noData,h.depth=r.depth||h.depth,h.hideOtherSidebarContent=r.hideOtherSidebarContent||h.hideOtherSidebarContent,h.namespace=r.namespace||h.namespace,h.pathNamespaces=r.pathNamespaces||h.pathNamespaces);var i="auto"===h.paths;e.mounted(function(e){var a,r,o,s,d,p,u,f,m,g,v,b;a=h,o=(r=t).router.parse().query.s,n=s=a,Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0.6em 7px;\n font-size: inherit;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search input::-ms-clear {\n display: none;\n height: 0;\n width: 0;\n}\n\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),void 0===(d=o)&&(d=""),p='
\n \n
\n \n \n \n \n \n
\n
\n
\n ',u=Docsify.dom.create("div",p),f=Docsify.dom.find("aside"),Docsify.dom.toggleClass(u,"search"),Docsify.dom.before(f,u),g=Docsify.dom.find("div.search"),v=Docsify.dom.find(g,"input"),b=Docsify.dom.find(g,".input-wrap"),Docsify.dom.on(g,"click",function(e){return -1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()}),Docsify.dom.on(v,"input",function(e){clearTimeout(m),m=setTimeout(function(n){return l(e.target.value.trim())},100)}),Docsify.dom.on(b,"click",function(e){"INPUT"!==e.target.tagName&&(v.value="",l())}),o&&setTimeout(function(e){return l(o)},500),i||c(h,t)}),e.doneEach(function(e){var a,r,o,s,l;a=h,r=t,n=o=a,function e(n,t){var a=Docsify.dom.getNode('.search input[type="search"]');if(a){if("string"==typeof n)a.placeholder=n;else{var r=Object.keys(n).filter(function(e){return t.indexOf(e)>-1})[0];a.placeholder=n[r]}}}(a.placeholder,r.route.path),s=a.noData,l=r.route.path,d="string"==typeof s?s:s[Object.keys(s).filter(function(e){return l.indexOf(e)>-1})[0]],i&&c(h,t)})};$docsify.plugins=[].concat(u,$docsify.plugins)}(); \ No newline at end of file +!function(){function e(e){return e.replace(//,"").replace(/{docsify-ignore}/,"").replace(//,"").replace(/{docsify-ignore-all}/,"").trim()}var n={},t="docsify.search.expires",a="docsify.search.index";function r(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,(function(e){return n[e]}))}function i(e){return e.text||"table"!==e.type||(e.cells.unshift(e.header),e.text=e.cells.map((function(e){return e.join(" | ")})).join(" |\n ")),e.text}function o(e){return e.text||"list"!==e.type||(e.text=e.raw),e.text}function s(e){return e&&e.normalize?e.normalize("NFD").replace(/[\u0300-\u036f]/g,""):e}function c(s,c){var l="auto"===s.paths,d=l?function(e){var n=[];return Docsify.dom.findAll(".sidebar-nav a:not(.section-link):not([data-nosearch])").forEach((function(t){var a=t.href,r=t.getAttribute("href"),i=e.parse(a).path;i&&-1===n.indexOf(i)&&!Docsify.util.isAbsolutePath(r)&&n.push(i)})),n}(c.router):s.paths,p="";if(d.length&&l&&s.pathNamespaces){var h=d[0];if(Array.isArray(s.pathNamespaces))p=s.pathNamespaces.filter((function(e){return h.slice(0,e.length)===e}))[0]||p;else if(s.pathNamespaces instanceof RegExp){var f=h.match(s.pathNamespaces);f&&(p=f[0])}var u=-1===d.indexOf(p+"/"),m=-1===d.indexOf(p+"/README");u&&m&&d.unshift(p+"/")}else-1===d.indexOf("/")&&-1===d.indexOf("/README")&&d.unshift("/");var g,y=((g=s.namespace)?t+"/"+g:t)+p,v=function(e){return e?a+"/"+e:a}(s.namespace)+p,b=localStorage.getItem(y){n=n.replace(e,t)})),n.trim()}((n.body&&n.body.trim()).replace(/(!?\[([^\]]+)\]\(([^)]+)\))/g,((e,n,t,a)=>n.startsWith("!")?"":t))).replace(/<[^>]*>/g,"");var f=n.slug||"";const u=f.split("/")[1].split("?")[0].replace("0","").split("-").map((e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase())).join(" ");if(!(h.includes("iframe")||h.includes(":fas")||h.includes(":fab")||h.includes("embedly-card"))&&p&&(i.forEach((function(e){var n,t=new RegExp(r(s(e)).replace(/[|\\{}()[\]^$+*?.]/g,"\\$&"),"gi"),a=-1;if(l=p?r(s(p)):p,d=h?r(s(h)):h,n=p?l.search(t):-1,a=h?d.search(t):-1,n>=0||a>=0){o+=n>=0?3:a>=0?2:0,a<0&&(a=0);var i,f=0;f=0==(i=a<11?0:a-10)?70:a+e.length+60,h&&f>h.length&&(f=h.length);const r=d&&(()=>{const e=d.substring(i,f),n=e.match(t);if(n){const t=e.indexOf(n[0]);return"..."+e.substring(0,t)+`${e.substring(t,t+n[0].length)}`+e.substring(t+n[0].length)+"..."}return"..."+e+"..."})();c+=r}})),o>0)){const e={title:l,content:(u&&u.toLowerCase()!==l.toLowerCase()&&"readme"!==u.toLowerCase()?`${u}
`:"")+(h?c:""),url:f,score:o};t.push(e)}},c=0;c\n

'+e.title+"

\n

"+e.content+"

\n\n"})),a.classList.add("show"),i.classList.add("show"),a.innerHTML=h||'

'+d+"

",l.hideOtherSidebarContent&&(o&&o.classList.add("hide"),c&&c.classList.add("hide"))}function h(e){l=e}function f(e,n){var t,a,r,i,o=n.router.parse().query.s;h(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0.6em 7px;\n font-size: inherit;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search input::-ms-clear {\n display: none;\n height: 0;\n width: 0;\n}\n\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n='
\n \n
\n \n \n \n \n \n
\n
\n
\n ',t=Docsify.dom.create("div",n),a=Docsify.dom.find("aside");Docsify.dom.toggleClass(t,"search"),Docsify.dom.before(a,t)}(o),a=Docsify.dom.find("div.search"),r=Docsify.dom.find(a,"input"),i=Docsify.dom.find(a,".input-wrap"),Docsify.dom.on(a,"click",(function(e){return-1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()})),Docsify.dom.on(r,"input",(function(e){clearTimeout(t),t=setTimeout((function(n){return p(e.target.value.trim())}),100)})),Docsify.dom.on(i,"click",(function(e){"INPUT"!==e.target.tagName&&(r.value="",p())})),o&&setTimeout((function(e){return p(o)}),500)}function u(e,n){h(e),function(e,n){var t=Docsify.dom.getNode('.search input[type="search"]');if(t)if("string"==typeof e)t.placeholder=e;else{var a=Object.keys(e).filter((function(e){return n.indexOf(e)>-1}))[0];t.placeholder=e[a]}}(e.placeholder,n.route.path),function(e,n){if("string"==typeof e)d=e;else{var t=Object.keys(e).filter((function(e){return n.indexOf(e)>-1}))[0];d=e[t]}}(e.noData,n.route.path)}var m={placeholder:"Type to search",noData:"No matches found.",paths:"auto",depth:2,maxAge:864e5,hideOtherSidebarContent:!1,namespace:void 0,pathNamespaces:void 0};$docsify.plugins=[].concat((function(e,n){var t=Docsify.util,a=n.config.search||m;Array.isArray(a)?m.paths=a:"object"==typeof a&&(m.paths=Array.isArray(a.paths)?a.paths:"auto",m.maxAge=t.isPrimitive(a.maxAge)?a.maxAge:m.maxAge,m.placeholder=a.placeholder||m.placeholder,m.noData=a.noData||m.noData,m.depth=a.depth||m.depth,m.hideOtherSidebarContent=a.hideOtherSidebarContent||m.hideOtherSidebarContent,m.namespace=a.namespace||m.namespace,m.pathNamespaces=a.pathNamespaces||m.pathNamespaces);var r="auto"===m.paths;e.mounted((function(e){f(m,n),!r&&c(m,n)})),e.doneEach((function(e){u(m,n),r&&c(m,n)}))}),$docsify.plugins)}(); \ No newline at end of file