1// pykelet.js 2/** 3 * @file pykelet.js 4 * @fileoverview This script reads a special **'Pykelet comment block'** from the HTML. 5 * * --- **JSDoc and File Structure Requirements** --- 6 * This file uses a specific header structure for documentation and version control: 7 * 1. Must begin with the **JSDoc block** ('/ ** ... * /'). 8 * 2. Mandatory tags are typically **@file** (first) and **@fileoverview**. 9 * 3. Optional tags like **@license** and **@author** should follow. 10 * 4. The **mandatory `window.versions` update code** must immediately follow the JSDoc block. 11 * * --- **Pykelet Comment Block (HTML Metadata)** --- 12 * * **1. Pykelet Comment Block:** 13 * The block must be an **HTML comment** (<!-- ... -->) placed **before the `<html>` tag** (after `<!DOCTYPE html>`). 14 * THE HTML data must start with the marker `PYKELET` on the first line, followed by metadata in YAML Jekyll/Hugo `KEY: VALUE` format. 15 * * **Example HTML Structure:** 16 * <!DOCTYPE html> 17 * <!--PYKELET 18 * TITLE: My Document Title 19 * AUTHOR: John Doe 20 * --> 21 * <html> 22 * <head> 23 * <script src="js/pykelet.js"></script> 24 * ... 25 * The `pykelet.js` reference is recommended to be placed immediately after the `<head>` tag so that any subsequent scripting can reference the named key-value pairs as set in the comment block. 26 * 27 * * **2. Core Functionality:** 28 * The script copies all key-value pairs found within this block into `document.pykelet.comment`. 29 * Additionally, it automatically populates HTML elements whose `id` attribute matches a 30 * key from the Pykelet block (e.g., an element with id="TITLE" will get the content 31 * from the 'TITLE' key). 32 * * **3. IMPORTANT:** * Include this script as early as possible in the document (ideally as the first item in the `<head>`), 33 * and ensure the **Pykelet comment block** is placed **immediately after `<!DOCTYPE html>`** for reliable access. 34 * * **4. Disable Auto-Fill:** 35 * Auto-filling can be disabled by defining `const disabled_fillDocumentFromPykeletComments = true;` 36 * in a script *before* this file is loaded. 37 * * @author Andrew Kingdom 38 * @license MIT (3-clause BSD) 39 * @copyright You MAY freely use this file but NO ONE SHALL REMOVE the author credentials. 40 */ 41window.versions={...(window.versions||{}), pykelet:'2.0.6'}; // Update versions object with our version single source of truth. NB: @version is not used in the JSDOC above. 42 43 44/** 45 * Initializes the document.pykelet object by immediately reading the Pykelet block from the HTML. 46 * This should be called early to make the metadata available as soon as possible. 47 */ 48function initPykeletFromComment() { 49 document.pykelet = { 50Â Â Â Â comment: getPykeletFromComment() // Grab details from the Pykelet block comment immediately. 51 }; 52} 53 54/** 55 * Populates HTML elements with content from the parsed Pykelet block. 56 * An HTML element's 'id' must match a key (case-insensitive) from the Pykelet block for it to be filled. 57 * For example, if the Pykelet block contains 'TITLE: My Page', an element <span id="TITLE"></span> 58 * will have its textContent set to "My Page". 59 * @param {boolean} [force=false] - If true, forces the document filling even if 60 * 'disabled_fillDocumentFromPykeletComments' is defined. 61 */ 62function fillDocumentFromPykeletComments(force = false) { 63Â Â if (!force && typeof(disabled_fillDocumentFromPykeletComments) !== 'undefined') { 64Â Â Â Â console.log('Pykelet document filling skipped as disabled_fillDocumentFromPykeletComments is true.'); 65Â Â Â Â return; // Don't fill the document if disabled_fillDocumentFromPykeletComments is defined, unless forced. 66Â Â } 67 68 const comments = document.pykelet.comment; 69Â Â if (comments) { 70Â Â Â Â for (let key in comments) { 71Â Â Â Â Â Â if (comments.hasOwnProperty(key)) { 72Â Â Â Â Â Â Â Â // Keys from the Pykelet block are automatically converted to uppercase by getPykeletFromComment. 73Â Â Â Â Â Â Â Â // Ensure we match element IDs in a case-insensitive way if needed, 74Â Â Â Â Â Â Â Â // but direct match is simpler if IDs are expected to be uppercase. 75Â Â Â Â Â Â Â Â const element = document.getElementById(key); // Assuming HTML IDs will match the uppercase keys 76Â Â Â Â Â Â Â Â if (element) { 77Â Â Â Â Â Â Â Â Â Â element.textContent = comments[key]; 78//Â Â Â Â Â Â Â Â Â Â Â console.debug(`Pykelet: Populated element #${key} with '${comments[key]}'`); 79Â Â Â Â Â Â Â Â } else { 80//Â Â Â Â Â Â Â Â Â Â Â console.debug(`Pykelet: No element found with ID '${key}' to populate.`); 81Â Â Â Â Â Â Â Â } 82Â Â Â Â Â Â } 83Â Â Â Â } 84Â Â } else { 85Â Â Â Â console.warn('Pykelet: No metadata found in Pykelet block comment to fill document elements.'); 86Â Â } 87} 88 89/** 90 * Searches the HTML document for the first 'Pykelet block' comment and parses its contents. 91 * A 'Pykelet block' is an HTML comment starting with 'PYKELET'. 92 * Inside, it contains key:value pairs, one per line. 93 * Example 94 * * Â Â <!--PYKELET 95 * * Â Â DESCRIPTION: This tool helps identify the most important parts in a text. 96 * * Â Â TITLE: Identify key phrases in a text. 97 * * Â Â FILENAME:Â Â text_tool.html 98 * * Â Â AUTHOR:Â Â Â Andrew Kingdom 99 * * Â Â --> 100 * The extracted key-value pairs are returned as an object (dictionary). 101 * This function replicates the functionality of a YAML front matter block, 102 * adapted for HTML comments. 103 * @param {Comment|undefined} [pykeletComment=undefined] - Optional. A specific comment Node to parse. 104 * If undefined, the function will search the document. 105 * @returns {Object|null}
105 An object containing the extracted key-value pairs, or null if no Pykelet block is found. 106 */ 107function getPykeletFromComment(pykeletComment = undefined) { 108Â Â /** 109Â Â Â * Helper function to get all comment nodes from a given DOM element. 110Â Â Â * @param {Element} elem - The DOM element to search within. 111Â Â Â * @returns {Comment[]} An array of comment nodes. 112Â Â Â */ 113 function getComments(elem) { 114 Â return Array.from(elem.childNodes).filter(node => node.nodeType === Node.COMMENT_NODE); 115 } 116Â Â /** 117Â Â Â * Helper function to get all comment nodes from a given DOM element. 118Â Â Â * @param {Element} elem - The DOM element to search within. 119Â Â Â * @returns {Comment[]} An array of comment nodes. 120Â Â Â */ 121 function isValidPykeletNode(node){ 122Â Â return node && node.nodeType === Node.COMMENT_NODE && 123Â Â Â Â Â Â typeof node.nodeValue === "string" && 124Â Â Â Â Â Â node.nodeValue.trim().startsWith("PYKELET"); 125Â } 126 127 if(pykeletComment === undefined) { 128Â Â Â Â // If no specific comment is provided, search the document for the first PYKELET comment. 129Â Â Â Â // It first checks for a 'markdown-body' class (common in GitHub-rendered HTML) 130Â Â Â Â // as a potential container, otherwise, it searches the entire document body. 131Â Â Â Â const githubBody = document.getElementsByClassName('markdown-body')[0]; 132 const comments = getComments(githubBody ? githubBody : document); 133 134 pykeletComment = comments.find(comment => comment.nodeValue.trim().startsWith('PYKELET')); 135Â Â } 136Â Â if (!pykeletComment) { 137Â Â Â Â console.info('Pykelet: No comment block found'); 138Â Â Â Â return null; // No PYKELET comment found (redundant if previous check is thorough, but as a safeguard) 139Â Â } 140Â Â if (!isValidPykeletNode(pykeletComment)) { 141Â Â Â return null; // Not valid 142Â Â } 143Â Â let metadata = {} 144Â Â // Trim leading/trailing whitespace and remove the '<!--' and '-->' markers. 145Â Â const lines = pykeletComment.nodeValue 146Â Â Â Â Â Â Â Â Â Â .trim()Â // if Cannot read properties error, you're probably supplying an invalid comment (and probably shouldn't supply anything). 147Â Â Â Â Â Â Â Â Â Â .replace(/<!--\s*PYKELET\s*/g, '') 148Â Â Â Â Â Â Â Â Â Â .replace(/\s*-->/g, '') 149Â Â Â Â Â Â Â Â Â Â .split('\n'); 150Â Â Â 151Â Â lines.forEach(line => { 152Â Â Â Â const [key, ...valueParts] = line.trim().split(':'); 153Â Â Â Â const fullKey = `${key.trim()}`.toUpperCase(); 154Â Â Â Â if (fullKey) { 155Â Â Â Â Â Â metadata[fullKey] = valueParts.join(':').trim(); 156Â Â Â Â } 157Â Â }); 158 159 return metadata; 160} 161 162/** 163 * Returns an array of all 'Pykelet block' comments found within a given DOM element. 164 * This is a special utility function primarily intended for 'index' or 'listing' files 165 * that might embed multiple Pykelet blocks (e.g., in <data> elements) to collect metadata 166 * about other files. For most single HTML files, 'getPykeletFromComment' is sufficient. 167 * @param {Element} elem - The DOM element to search within (e.g., 'document' or a specific container). 168 * @returns {Comment[]} An array of comment nodes that are Pykelet blocks. 169 */ 170function getPykeletsFromComments(elem) { 171Â Â /** 172Â Â Â * Helper function to get all comment nodes from a given DOM element. 173Â Â Â * @param {Element} elem - The DOM element to search within. 174Â Â Â * @returns {Comment[]} An array of comment nodes. 175Â Â Â */ 176 function getComments(elem) { 177 Â return Array.from(elem.childNodes).filter(node => node.nodeType === Node.COMMENT_NODE); 178 } 179Â Â Â 180Â Â // Retrieve all comment nodes from the provided element and filter for PYKELET comments. 181Â Â const comments = getComments(elem)?.filter(comment => comment.nodeValue.trim().startsWith('PYKELET')); 182 183Â Â return comments || []; // Return an empty array if no comments or elem is null/undefined 184} 185 186 187// --- Script Execution Flow --- 188 189// 1. Immediately initialize 'document.pykelet.comment'. 190// This makes the Pykelet metadata available as soon as this script executes, 191// before the entire DOM content is loaded. 192initPykeletFromComment(); 193 194// 2. Defer the auto-population of HTML elements until the DOM is fully loaded. 195// This ensures that all target HTML elements (with matching IDs) exist in the document 196// before the script attempts to fill them. 197window.addEventListener('DOMContentLoaded', fillDocumentFromPykeletComments); 198 199// Note: For debugging, you can access the parsed metadata via 'document.pykelet.comment' 200// in your browser's developer console after the page loads.
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.