PageSourceSearch

https://akingdom.github.io/js/pykelet.js

js akingdom.github.io collected 2026-10-03 08:35:19 UTC 10,112 bytes, 200 lines download raw bytes

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.