From: Thomas Walker Lynch Date: Wed, 1 Jul 2026 10:08:12 +0000 (+0000) Subject: fixes tests after a key change, adds "Note" cross references X-Git-Url: https://git.reasoningtechnology.com/machine%20fig.png?a=commitdiff_plain;h=49e5ce66bea8baf69c4965f2814461a08cf4d4e9;p=RT-Style fixes tests after a key change, adds "Note" cross references --- diff --git a/developer/authored/ExampleGreet.make/Greeter.lib.c b/developer/authored/ExampleGreet.make/Greeter.lib.c index 1d23879..4f284a4 100644 --- a/developer/authored/ExampleGreet.make/Greeter.lib.c +++ b/developer/authored/ExampleGreet.make/Greeter.lib.c @@ -9,8 +9,8 @@ void ExampleGreet·Greeter·hello_loop(int count); #include void ExampleGreet·Greeter·hello_loop(int count){ - for(int TM = 0; TM < count; ++TM){ - int current_count = ExampleGreet·Math·add(TM ,1); + for(int i = 0; i < count; ++i){ + int current_count = ExampleGreet·Math·add(i ,1); printf("Hello iteration: %d\n" ,current_count); } } diff --git a/developer/authored/Manuscript.copy/Core/RT-Manuscript_make.js b/developer/authored/Manuscript.copy/Core/RT-Manuscript_make.js index fb3f58e..fae3826 100644 --- a/developer/authored/Manuscript.copy/Core/RT-Manuscript_make.js +++ b/developer/authored/Manuscript.copy/Core/RT-Manuscript_make.js @@ -202,3 +202,5 @@ window.RT.load = function(module_path) { window.RT.load('Core/stage_manager'); window.RT.load('Core/theme_make'); window.RT.load('Theme/manifest.js') +window.RT.load('Layout/counter'); +window.RT.load('Layout/note'); diff --git a/developer/authored/Manuscript.copy/Core/stage_manager.js b/developer/authored/Manuscript.copy/Core/stage_manager.js index 4c41214..87f4550 100644 --- a/developer/authored/Manuscript.copy/Core/stage_manager.js +++ b/developer/authored/Manuscript.copy/Core/stage_manager.js @@ -4,25 +4,46 @@ manages MathJax async typesetting, and handles scroll restoration. */ -(function() { +(function(){ - if (!window.RT) { + if(!window.RT){ console.error("RT not defined - was RT-Manuscript_make run?"); return; } // Prevent duplicate initialization - if (window.RT.Element instanceof Set) { + if(window.RT.Element instanceof Set){ console.warn("RT stage_manager already initialized. Aborting duplicate run."); return; } - // Phase queues/pointers - window.RT.Element = new Set(); - window.RT.PageStyle = new Set(); - window.RT.paginate = null; + /* Phase task queues/functions in order the phases are processed. - const debug = window.RT.Debug || { log: function(){}, warn: function(){}, error: function(){} }; + Generators and element styling must run before pagination. Even styling changes the size of the document. + + Page styling can only happen after the pages are added. Pages are + element pairs, the content is what is on the page. + + The document can only be walked for counters after the pages are added, because pages have page numbers. (Even if the document is not paginated, the counters can not be processed until after the endnote generators, or any other elements that have counters, run.) + + The pageinate_0 breaks the document into ... elements. It will break some elements, but not others. As examples, it breaks lists and tables, but does not break paragraphs. It has a target length, but will lengthen or shorten a page so that the content fits. + + Not breaking paragraphs simplifies pagination, especially in light of the possible embedding of other elements. It is also nice to read a paragraph without page breaks in them. Footnotes can change page length a small amount due to being formatted. This is handled during pagination_0. + + Pages have page numbers, which are counters. So counters come after pagination. + + Cross reference targets can have counters in them, so they are handled after counters. + + Adding counter values and cross references can cause the content of a page to lengthen. Rather than having that cascade, which could change page number cross reference text, We merely lengthen pages as requierd. + */ + window.RT.Element = new Set(); // expand generators, style elements + window.RT.paginate_0 = null; // add the ... pairs + window.RT.PageStyle = new Set(); // apply style to pages + window.RT.counter = null; // walk doc for counters, then read snapshots + window.RT.note = null; // mark notes, read them back, any order + window.RT.paginate_1 = null; // bump individual pages lengths up as needed + + const debug = window.RT.Debug || { log: function(){} ,warn: function(){} ,error: function(){} }; let target_y = 0; let is_reload = false; @@ -33,72 +54,73 @@ // SCROLL & LAYOUT LOCK UTILITIES // ========================================================= - function lock_layout() { + function lock_layout(){ is_layout_locked = true; document.documentElement.style.visibility = "hidden"; } - function unlock_layout() { - if (!is_layout_locked) return; + function unlock_layout(){ + if(!is_layout_locked) return; is_layout_locked = false; document.documentElement.style.visibility = ""; - window.removeEventListener("load", unlock_layout); + window.removeEventListener("load" ,unlock_layout); document.dispatchEvent(new Event("RT_layout_complete")); } function configure_history(){ - if ('scrollRestoration' in history) { - history.scrollRestoration = 'manual'; - } + if('scrollRestoration' in history) history.scrollRestoration = 'manual'; } function capture_scroll_target(){ const raw_target = sessionStorage.getItem('RT_saved_y'); target_y = raw_target !== null ? parseInt(raw_target ,10) : 0; - if (window.performance) { + if(window.performance){ const nav_entries = performance.getEntriesByType("navigation"); - if (nav_entries.length > 0) { + if(nav_entries.length > 0){ is_reload = (nav_entries[0].type === "reload"); - } else if (performance.navigation) { + } + else if(performance.navigation){ is_reload = (performance.navigation.type === 1); } } } function enforce_scroll(target ,use_hash ,attempts){ - if (attempts > 15) { + if(attempts > 15){ unlock_layout(); return; } - if (use_hash) { + if(use_hash){ const hash_target = document.getElementById(window.location.hash.substring(1)); - if (hash_target) { - hash_target.scrollIntoView(); - } - } else { + if(hash_target) hash_target.scrollIntoView(); + } + else { window.scrollTo(0 ,target); } let is_successful = use_hash ? true : (Math.abs(window.scrollY - target) < 5 || target === 0); - if (is_successful && document.body.scrollHeight > 1000) { + + if( is_successful && (document.body.scrollHeight > 1000) ){ setTimeout(() => { - if (!use_hash && Math.abs(window.scrollY - target) >= 5) { + if( !use_hash && (Math.abs(window.scrollY - target) >= 5) ){ enforce_scroll(target ,use_hash ,attempts + 1); - } else { + } + else { unlock_layout(); } }, 100); - } else { + } + else { setTimeout(() => enforce_scroll(target ,use_hash ,attempts + 1) ,50); } } function bind_window_events(){ window.addEventListener('scroll' ,() => { - if (is_layout_locked) return; + if(is_layout_locked) return; clearTimeout(scroll_timer); scroll_timer = setTimeout(() => { sessionStorage.setItem('RT_saved_y' ,window.scrollY); @@ -114,70 +136,96 @@ // MASTER PIPELINE EXECUTION // ========================================================= - function run_pipeline() { + function run_pipeline(){ // Phase 1: Base Elements - debug.log('stage_manager', 'Phase 1: Executing Element tasks'); - if (window.RT.Element.size > 0) { - for (const element_fn of window.RT.Element) { - if (typeof element_fn === 'function') { - try { element_fn(); } - catch (e) { debug.error('stage_manager', "Element task failed: " + e); } - } else { - debug.warn('stage_manager', 'Invalid element in RT.Element Set: ' + element_fn); + debug.log('stage_manager' ,'Phase 1: Executing Element tasks'); + if(window.RT.Element.size > 0){ + for(const element_fn of window.RT.Element){ + if(typeof element_fn === 'function'){ + try{ element_fn(); } + catch(e){ debug.error('stage_manager' ,"Element task failed: " + e); } + } + else { + debug.warn('stage_manager' ,'Invalid element in RT.Element Set: ' + element_fn); } } } - // Define the continuation (Phases 2 & 3 + Scroll) to run after MathJax resolves + // Define the continuation (Phases 2 through 7 + Scroll) to run after MathJax resolves const run_post_math_phases = () => { - // Phase 2: Pagination - debug.log('stage_manager', 'Phase 2: Executing Pagination'); - if (typeof window.RT.paginate === 'function') { - try { window.RT.paginate(); } - catch (e) { debug.error('stage_manager', "Pagination failed: " + e); } - } else { - debug.log('stage_manager', 'No pagination function registered. Skipping.'); + + // Phase 2: Pagination Part 0 + debug.log('stage_manager' ,'Phase 2: Executing paginate_0'); + if(typeof window.RT.paginate_0 === 'function'){ + try{ window.RT.paginate_0(); } + catch(e){ debug.error('stage_manager' ,"paginate_0 failed: " + e); } + } + else { + debug.log('stage_manager' ,'No paginate_0 function registered. Skipping.'); } // Phase 3: Page Styling - debug.log('stage_manager', 'Phase 3: Executing PageStyle tasks'); - if (window.RT.PageStyle.size > 0) { - for (const style_fn of window.RT.PageStyle) { - if (typeof style_fn === 'function') { - try { style_fn(); } - catch (e) { debug.error('stage_manager', "PageStyle task failed: " + e); } + debug.log('stage_manager' ,'Phase 3: Executing PageStyle tasks'); + if(window.RT.PageStyle.size > 0){ + for(const style_fn of window.RT.PageStyle){ + if(typeof style_fn === 'function'){ + try{ style_fn(); } + catch(e){ debug.error('stage_manager' ,"PageStyle task failed: " + e); } } } } + // Phase 4: Counters + debug.log('stage_manager' ,'Phase 4: Executing counter processing'); + if(typeof window.RT.counter === 'function'){ + try{ window.RT.counter(); } + catch(e){ debug.error('stage_manager' ,"Counter processing failed: " + e); } + } + + // Phase 5: Cross Reference + debug.log('stage_manager' ,'Phase 5: Executing note processing'); + if(typeof window.RT.note === 'function'){ + try{ window.RT.note(); } + catch(e){ debug.error('stage_manager' ,"Cross reference processing failed: " + e); } + } + + // Phase 6: Pagination Part 1 + debug.log('stage_manager' ,'Phase 6: Executing paginate_1'); + if(typeof window.RT.paginate_1 === 'function'){ + try{ window.RT.paginate_1(); } + catch(e){ debug.error('stage_manager' ,"paginate_1 failed: " + e); } + } + // Final Step: Resolve Scroll Target - debug.log('scroll' ,`Pagination layout complete. Enforcing scroll target.`); + debug.log('scroll' ,`Pipeline execution complete. Enforcing scroll target.`); let final_target = target_y; let use_hash = false; - if (window.location.hash && !is_reload) { + + if(window.location.hash && !is_reload){ const hash_target = document.getElementById(window.location.hash.substring(1)); - if (hash_target) { - use_hash = true; - } + if(hash_target) use_hash = true; } enforce_scroll(final_target ,use_hash ,0); }; // MathJax Intercept: Await typeset before paginating - if (window.MathJax) { - if (typeof window.MathJax.typesetPromise === 'function') { + if(window.MathJax){ + if(typeof window.MathJax.typesetPromise === 'function'){ window.MathJax.typesetPromise().then(run_post_math_phases).catch((err) => { - debug.error('stage_manager', 'MathJax typeset failed: ' + err); + debug.error('stage_manager' ,'MathJax typeset failed: ' + err); run_post_math_phases(); }); - } else if (window.MathJax.Hub && window.MathJax.Hub.Queue) { - window.MathJax.Hub.Queue(["Typeset", window.MathJax.Hub], run_post_math_phases); - } else { + } + else if( window.MathJax.Hub && window.MathJax.Hub.Queue ){ + window.MathJax.Hub.Queue(["Typeset" ,window.MathJax.Hub] ,run_post_math_phases); + } + else { run_post_math_phases(); } - } else { + } + else { run_post_math_phases(); } } @@ -191,9 +239,9 @@ capture_scroll_target(); bind_window_events(); - document.addEventListener('DOMContentLoaded', run_pipeline); + document.addEventListener('DOMContentLoaded' ,run_pipeline); // Safety Net: restore visibility on load if the async layout engine hangs - window.addEventListener("load", unlock_layout); + window.addEventListener("load" ,unlock_layout); })(); diff --git a/developer/authored/Manuscript.copy/Document/manual.html b/developer/authored/Manuscript.copy/Document/manual.html index 7c86051..d5ff9f2 100644 --- a/developer/authored/Manuscript.copy/Document/manual.html +++ b/developer/authored/Manuscript.copy/Document/manual.html @@ -27,8 +27,7 @@

Leonardo·da·Vinci and other notes

- The middle dot used as a namespace separator is the middle dot of typography. On macOS: Option + Shift + 9. On Windows: Hold Alt and type 0183 on the numeric keypadLinux: Compose Key followed by . and -. In Emacs it can be defined on a key, I have it on C-x g cdot, along with many other C-x g characters. It can also be - copied then pasted from the heading above. + The middle dot used as a namespace separator is the middle dot of typography. On macOS: Option + Shift + 9. On Windows: Hold Alt and type 0183 on the numeric keypadLinux: Compose Key followed by . and -. It can be added to editor settings. If necessary, it can also be copied then pasted from the heading above.

diff --git a/developer/authored/Manuscript.copy/Element/counter.js b/developer/authored/Manuscript.copy/Element/counter.js deleted file mode 100644 index 94d355e..0000000 --- a/developer/authored/Manuscript.copy/Element/counter.js +++ /dev/null @@ -1,437 +0,0 @@ -/* - Processes tags. - Calculates numbering, maintains the explicit status machine, manages snapshots, and outputs values for read tags. - Supports two modes: 'scoped' (default) and 'milestone'. -*/ - -(function() { - - if (!RT) { - console.error("RT not defined - was RT-Manuscript_make run?"); - return; - } - if (!RT.Element) { - console.error("RT.Element not defined - was the state_manager run?"); - return; - } - - RT.Counter = RT.Counter || {}; - RT.dict_instance = RT.dict_instance || {}; - RT.dict_snapshot = RT.dict_snapshot || {}; - - - class Count { - constructor() { - this.status = 'empty'; - this.list = null; - } - - reset() { - this.status = 'preamble'; - this.list = [0]; - } - - reset(list) { - if (Array.isArray(list)) { - const is_natural = list.every(val => Number.isInteger(val) && val >= 0); - if (is_natural) { - this.list = list.length > 0 ? [...list] : null; - } else { - console.error("RT-Manuscript Layout Error: Counter list must contain only natural numbers."); - this.list = null; - } - } else { - this.list = null; - } - } - - - read(...path) { - if (path.length === 0) return undefined; - const key = path[0]; - - if (key === 'list') { - if (this.status === 'empty') { - console.error("RT-Manuscript Layout Error: Attempted to read 'list' from an empty Count object."); - return null; - } - if (!this.list) return null; - - if (path[1] === 'short') { - return this.list.slice(0, -1); - } - return [...this.list]; - } - - if (key === 'status') { - return this.status; - } - - return undefined; - } - - write(key, value) { - if (key === 'status') { - this.status = value; - } else if (key === 'list') { - if (Array.isArray(value)) { - const is_natural = value.every(val => Number.isInteger(val) && val >= 0); - if (is_natural) { - this.list = value.length > 0 ? [...value] : null; - } else { - console.error("RT-Manuscript Layout Error: Counter list must contain only natural numbers."); - this.list = null; - } - } else { - this.list = null; - } - } else if (key === 'Count' || key === 'count') { - const source_count = value instanceof Count ? value : (value && value.count instanceof Count ? value.count : null); - if (source_count) { - this.status = source_count.status; - this.list = source_count.list ? [...source_count.list] : null; - } else { - console.error("RT-Manuscript Layout Error: Invalid object provided to write('Count')."); - } - } - } - - increment() { - if (this.status === 'empty') { - console.error("RT-Manuscript Layout Error: Attempted to increment an empty Count object."); - return; - } - if (this.list && this.list.length > 0) { - this.list[this.list.length - 1] += 1; - } - } - - push(val) { - if (this.status === 'empty') { - console.error("RT-Manuscript Layout Error: Attempted to push to an empty Count object."); - return; - } - if (!this.list) { - this.list = []; - } - this.list.push(val); - } - - pop() { - if (this.status === 'empty') { - console.error("RT-Manuscript Layout Error: Attempted to pop from an empty Count object."); - return undefined; - } - if (this.list && this.list.length > 0) { - const val = this.list.pop(); - if (this.list.length === 0) { - this.list = null; // Revert to strict null state if emptied - } - return val; - } - return undefined; - } - - clone() { - const c = new Count(); - c.status = this.status; - c.list = this.list ? [...this.list] : null; - return c; - } - - } - - class CounterMachine { - constructor(config) { - this.count = new Count(); - this.style = ['NaturalNumber']; - this.separator = '.'; - this.separator_placement = 'embedded'; - this.mode = 'scoped'; // 'scoped', 'milestone' - - if (config) { - this.write(config); - } - } - - read(...path) { - if (path.length === 0) return undefined; - - if (path[0] === 'count') { - if (path.length === 1) { - return this.count.clone(); - } - return this.count.read(...path.slice(1)); - } - - return path.reduce((acc, key) => (acc && acc[key] !== undefined) ? acc[key] : undefined, this); - } - - write(dict) { - for (const [key, value] of Object.entries(dict)) { - if (key === 'style') { - let parsed = Array.isArray(value) ? value : [value]; - if (parsed.length === 1 && parsed[0] === 'outline') { - parsed = ['Roman', 'Alpha', 'roman', 'alpha', 'CountingNumber']; - } - this.style = parsed; - } else if (key === 'Count' || key === 'count') { - // Support copying from another CounterMachine or Count object - const source_count = value instanceof CounterMachine ? value.count : (value instanceof Count ? value : null); - if (source_count) { - this.count = source_count.clone(); - } else { - console.error("RT-Manuscript Layout Error: Invalid object provided to write('Count')."); - } - } else { - this[key] = value; - } - } - } - - enter(first_step_val) { - const current_status = this.count.read('status'); - - if (current_status === 'empty') { - // Transition state strictly before mutating the list - this.count.write('status', 'preamble'); - this.count.push(first_step_val !== undefined ? first_step_val : 0); - } else if (current_status === 'preamble') { - this.count.push(0); // indent appends 0 - this.count.write('status', 'preamble'); - } else if (current_status === 'between') { - this.count.increment(); - this.count.write('status', 'preamble'); - } - } - - exit() { - const current_status = this.count.read('status'); - - if (current_status === 'empty') { - console.error("RT-Manuscript Layout Error: Attempted to exit an empty counter scope."); - } else if (current_status === 'preamble') { - this.count.write('status', 'between'); - } else if (current_status === 'between') { - this.count.pop(); - this.count.write('status', 'between'); - } - } - - to_string(count_obj) { - if (!count_obj) return ''; - - const status = count_obj.read('status'); - if (status === 'empty') return ''; - - let active_list; - if (this.mode === 'scoped' && status === 'between') { - active_list = count_obj.read('list', 'short'); - } else { - active_list = count_obj.read('list'); - } - - // Safety check catches null or empty arrays - if (!active_list || active_list.length === 0) { - return ''; - } - - const formatted_list = active_list.map((val, depth) => { - const current_style = depth < this.style.length ? this.style[depth] : this.style[this.style.length - 1]; - const method_name = `to_${current_style}`; - return typeof this[method_name] === 'function' ? this[method_name](val) : this.to_NaturalNumber(val); - }); - - let count_str = formatted_list.join(this.separator); - if (this.separator_placement === 'embedded-after') { - count_str += this.separator; - } - return count_str; - } - - // --- Atomic Type Conversions --- - - to_NaturalNumber(num) { return num.toString(); } - from_NaturalNumber(val) { - const n = parseInt(val, 10); - return isNaN(n) ? 0 : n; - } - - to_CountingNumber(num) { return (num + 1).toString(); } - from_CountingNumber(val) { - const n = parseInt(val, 10); - return isNaN(n) ? 0 : Math.max(0, n - 1); - } - - to_roman(num) { return this.to_Roman(num).toLowerCase(); } - from_roman(val) { return this.from_Roman(val.toUpperCase()); } - - to_Roman(num) { - let n = num + 1; // 0-indexed to 1-indexed - if (n < 1) return n.toString(); - const lookup = {M:1000, CM:900, D:500, CD:400, C:100, XC:90, L:50, XL:40, X:10, IX:9, V:5, IV:4, I:1}; - let roman = ''; - for (let i in lookup) { - while (n >= lookup[i]) { - roman += i; - n -= lookup[i]; - } - } - return roman; - } - from_Roman(val) { - if (!/^M*(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/i.test(val)) return 0; - const lookup = {M:1000, CM:900, D:500, CD:400, C:100, XC:90, L:50, XL:40, X:10, IX:9, V:5, IV:4, I:1}; - let temp = val.toUpperCase(); - let num = 0; - let i = 0; - while (i < temp.length) { - if (i + 1 < temp.length && lookup[temp.substring(i, i + 2)]) { - num += lookup[temp.substring(i, i + 2)]; - i += 2; - } else { - num += lookup[temp[i]]; - i++; - } - } - return Math.max(0, num - 1); // 1-indexed to 0-indexed - } - - to_Alpha(num) { return String.fromCharCode(65 + num); } - from_Alpha(val) { - if (/^[A-Z]$/.test(val)) return val.charCodeAt(0) - 65; - return 0; - } - - to_alpha(num) { return String.fromCharCode(97 + num); } - from_alpha(val) { - if (/^[a-z]$/.test(val)) return val.charCodeAt(0) - 97; - return 0; - } - - clone() { - const copy = new CounterMachine(); - copy.count = this.count.clone(); - copy.style = [...this.style]; - copy.separator = this.separator; - copy.separator_placement = this.separator_placement; - copy.mode = this.mode; - return copy; - } - } - - RT.Element.add( function() { - const debug = RT.Debug || { log: function(){} }; - if (debug.log) debug.log('counter', 'Processing counters'); - - const root_node = document.documentElement; - - function walk(node) { - if (node.nodeType !== Node.ELEMENT_NODE) return; - - const tag = node.tagName.toLowerCase(); - let machine_to_exit = null; - - if (tag === 'rt·counter·make') { - const name = node.getAttribute('counter'); - if (name) { - const style_attr = node.getAttribute('style'); - const parsed_style = style_attr ? style_attr.split(',').map(s => s.trim()) : ['NaturalNumber']; - - RT.dict_instance[name] = new CounterMachine({ - style: parsed_style, - separator: node.getAttribute('separator') || '.', - separator_placement: node.getAttribute('separator-placement') || 'embedded', - mode: node.getAttribute('mode') || 'scoped' - }); - - const on_first_step_str = node.getAttribute('on-first-step'); - if (on_first_step_str) { - const top_style = RT.dict_instance[name].style[0]; - const method_name = `from_${top_style}`; - const active_machine = RT.dict_instance[name]; - const initial_val = typeof active_machine[method_name] === 'function' - ? active_machine[method_name](on_first_step_str) - : active_machine.from_NaturalNumber(on_first_step_str); - - active_machine.first_step_val = initial_val; - } - } - } else if (tag === 'rt·counter·step') { - const name = node.getAttribute('counter'); - if (name && RT.dict_instance[name]) { - const active_machine = RT.dict_instance[name]; - active_machine.enter(active_machine.first_step_val); - active_machine.first_step_val = undefined; // consume it - machine_to_exit = active_machine; - } - } else if (tag === 'rt·counter·snapshot') { - const counter_name = node.getAttribute('counter'); - const snapshot_name = node.getAttribute('snapshot'); - - if (counter_name && snapshot_name && RT.dict_instance[counter_name]) { - const active_machine = RT.dict_instance[counter_name]; - - if (active_machine.read('count', 'status') === 'empty') { - console.error(`RT-Manuscript Layout Error: Attempted to snapshot an empty counter '${counter_name}' at snapshot '${snapshot_name}'. A step is required first.`); - } else { - RT.dict_snapshot[snapshot_name] = active_machine.clone(); - } - } - } - - let child = node.firstElementChild; - while (child) { - walk(child); - child = child.nextElementSibling; - } - - if (machine_to_exit) { - machine_to_exit.exit(); - } - } - - walk(root_node); - - const reads = root_node.querySelectorAll('RT·counter·read, rt·counter·read'); - - if (typeof TM !== 'undefined' && TM.loop) { - TM.loop(reads, function(node) { - process_read_node(node); - }); - } else { - for (let i = 0; i < reads.length; i++) { - process_read_node(reads[i]); - } - } - - function process_read_node(node) { - const snapshot_name = node.getAttribute('snapshot'); - const key = node.getAttribute('key') || 'count'; - - if (snapshot_name && RT.dict_snapshot[snapshot_name]) { - const snapshot_machine = RT.dict_snapshot[snapshot_name]; - - if (key === 'count') { - const raw_state = snapshot_machine.read('count'); - node.innerHTML = snapshot_machine.to_string(raw_state); - } else { - const keys = key.split('.'); - const value = snapshot_machine.read(...keys); - - if (value === null) { - node.innerHTML = 'null'; - } else if (value !== undefined) { - node.innerHTML = Array.isArray(value) ? value.join(',') : value; - } else { - node.innerHTML = `[Missing key: ${key}]`; - } - } - } else { - node.innerHTML = `[Unknown snapshot: ${snapshot_name}]`; - console.error(`RT-Manuscript Layout Error: failed. No snapshot named '${snapshot_name}' found.`); - } - } - }); - -})(); diff --git a/developer/authored/Manuscript.copy/Element/endnote.js b/developer/authored/Manuscript.copy/Element/endnote.js index 8b7b446..0cb72e8 100644 --- a/developer/authored/Manuscript.copy/Element/endnote.js +++ b/developer/authored/Manuscript.copy/Element/endnote.js @@ -3,72 +3,112 @@ Creates bidirectional links between inline citations and the generated endnotes list. */ -(function() { +(function(){ - if (!window.RT) { - console.error("RT not defined - was RT-Manuscript_make run?"); + if(!window.RT){ + console.error("RT not defined"); return; } - if (!window.RT.Element) { - console.error("RT.Element not defined - was the state_manager run?"); + if(!window.RT.Element){ + console.error("RT.Element not defined"); return; } - RT.Element.add( function() { + window.RT.Element.add(function(){ const debug = window.RT.Debug || { log: function(){} }; - if (debug.log) debug.log('endnote', 'Processing endnotes'); + if(debug.log){ + debug.log('endnote' ,'Processing endnotes'); + } - const citations = document.querySelectorAll('rt·cite'); - if(citations.length === 0) return; + const endnotes = document.querySelectorAll('RT·endnote'); + if(endnotes.length === 0){ + return; + } - const article = document.querySelector('rt·article'); - if(!article) return; + const article = document.querySelector('RT·article'); + if(!article){ + return; + } - // 1. Ensure the H1 is a direct child of the article so the TOC can see it - let endnotesHeader = document.getElementById('endnotes-header'); - if (!endnotesHeader) { - endnotesHeader = document.createElement('h1'); - endnotesHeader.id = 'endnotes-header'; - endnotesHeader.innerText = 'Endnotes'; - article.appendChild(endnotesHeader); + let endnotes_header = document.getElementById('endnotes_header'); + if(!endnotes_header){ + endnotes_header = document.createElement('h1'); + endnotes_header.id = 'endnotes_header'; + endnotes_header.innerText = 'Endnotes'; + article.appendChild(endnotes_header); } - // 2. Locate or generate the endnotes list container - let endnoteContainer = document.querySelector('rt·endnotes'); - if(!endnoteContainer) { - endnoteContainer = document.createElement('rt·endnotes'); - article.appendChild(endnoteContainer); + let endnote_container = document.querySelector('RT·endnotes'); + if(!endnote_container){ + endnote_container = document.createElement('RT·endnotes'); + article.appendChild(endnote_container); } - - // 3. Ensure the list structure exists - if(!endnoteContainer.querySelector('ol')) { - endnoteContainer.innerHTML = '

    '; + + if(!endnote_container.querySelector('.RT_endnote_list')){ + const list_container = document.createElement('div'); + list_container.className = 'RT_endnote_list'; + endnote_container.appendChild(list_container); + + const counter_make = document.createElement('RT·counter·make'); + counter_make.setAttribute('counter' ,'endnote'); + counter_make.setAttribute('style' ,'CountingNumber'); + article.insertBefore(counter_make ,article.firstChild); } - const list = endnoteContainer.querySelector('ol'); + const list = endnote_container.querySelector('.RT_endnote_list'); - // Process each inline citation - citations.forEach((cite, index) => { - const refNum = index + 1; - const refText = cite.getAttribute('ref') || cite.innerHTML; + function process_endnote(node ,index){ + const snapshot_name = 'endnote_cite_' + index; + const ref_text = node.innerHTML; + + const step = document.createElement('RT·counter·step'); + step.setAttribute('counter' ,'endnote'); + + const snapshot = document.createElement('RT·counter·snapshot'); + snapshot.setAttribute('counter' ,'endnote'); + snapshot.setAttribute('snapshot' ,snapshot_name); - cite.innerHTML = `[${refNum}]`; - cite.style.cursor = 'pointer'; - cite.style.color = window.RT.theme('read', 'brand', 'link'); - cite.style.textDecoration = 'none'; + node.parentNode.insertBefore(step ,node); + step.appendChild(snapshot); + step.appendChild(node); - // Append the corresponding entry into the endnotes list - const li = document.createElement('li'); - li.id = `note-${refNum}`; - li.innerHTML = `${refText} `; + node.innerHTML = '[]'; + node.style.cursor = 'pointer'; + node.style.color = window.RT.theme('read' ,'brand' ,'link'); + node.style.textDecoration = 'none'; + + const li = document.createElement('div'); + li.id = 'note_' + index; + li.style.display = 'flex'; + li.style.marginBottom = '0.5rem'; + + const left_div = document.createElement('div'); + left_div.style.marginRight = '0.5rem'; + left_div.innerHTML = '[]'; + + const right_div = document.createElement('div'); + const return_link = document.createElement('a'); + return_link.href = '#cite_' + index; + return_link.style.textDecoration = 'none'; + return_link.innerHTML = '↩'; + + right_div.innerHTML = ref_text + ' '; + right_div.appendChild(return_link); + + li.appendChild(left_div); + li.appendChild(right_div); + list.appendChild(li); - }); - - // Style the container - endnoteContainer.style.display = 'block'; - endnoteContainer.style.marginTop = '1rem'; - endnoteContainer.style.borderTop = `1px solid ${window.RT.theme('read', 'surface', '3')}`; - endnoteContainer.style.paddingTop = '1rem'; + } + + for(let i = 0; i < endnotes.length; i++){ + process_endnote(endnotes[i] ,i + 1); + } + + endnote_container.style.display = 'block'; + endnote_container.style.marginTop = '1rem'; + endnote_container.style.borderTop = '1px solid ' + window.RT.theme('read' ,'surface' ,'3'); + endnote_container.style.paddingTop = '1rem'; }); })(); diff --git a/developer/authored/Manuscript.copy/Layout/article_tech_ref.js b/developer/authored/Manuscript.copy/Layout/article_tech_ref.js index 42d5c7f..ba12d63 100644 --- a/developer/authored/Manuscript.copy/Layout/article_tech_ref.js +++ b/developer/authored/Manuscript.copy/Layout/article_tech_ref.js @@ -15,10 +15,8 @@ const required_elements = [ 'chapter' ,'code' - ,'counter' ,'endnote' ,'math' - ,'symbol' ,'term' ,'title' ,'TOC' @@ -171,7 +169,7 @@ } //---------------------------------------- - // Execution & Registration + // Registration upon load // // Load the element files diff --git a/developer/authored/Manuscript.copy/Layout/counter.js b/developer/authored/Manuscript.copy/Layout/counter.js new file mode 100644 index 0000000..7c9562d --- /dev/null +++ b/developer/authored/Manuscript.copy/Layout/counter.js @@ -0,0 +1,437 @@ +/* + Processes tags. + Calculates numbering, maintains the explicit status machine, manages snapshots, and outputs values for read tags. + Supports two modes: 'scoped' (default) and 'milestone'. +*/ + +(function() { + + if (!RT) { + console.error("RT not defined - was RT-Manuscript_make run?"); + return; + } + if (!RT.Element) { + console.error("RT.Element not defined - was the state_manager run?"); + return; + } + + RT.Counter = RT.Counter || {}; + RT.dict_instance = RT.dict_instance || {}; + RT.dict_snapshot = RT.dict_snapshot || {}; + + + class Count { + constructor() { + this.status = 'empty'; + this.list = null; + } + + reset() { + this.status = 'preamble'; + this.list = [0]; + } + + reset(list) { + if (Array.isArray(list)) { + const is_natural = list.every(val => Number.isInteger(val) && val >= 0); + if (is_natural) { + this.list = list.length > 0 ? [...list] : null; + } else { + console.error("RT-Manuscript Layout Error: Counter list must contain only natural numbers."); + this.list = null; + } + } else { + this.list = null; + } + } + + + read(...path) { + if (path.length === 0) return undefined; + const key = path[0]; + + if (key === 'list') { + if (this.status === 'empty') { + console.error("RT-Manuscript Layout Error: Attempted to read 'list' from an empty Count object."); + return null; + } + if (!this.list) return null; + + if (path[1] === 'short') { + return this.list.slice(0, -1); + } + return [...this.list]; + } + + if (key === 'status') { + return this.status; + } + + return undefined; + } + + write(key, value) { + if (key === 'status') { + this.status = value; + } else if (key === 'list') { + if (Array.isArray(value)) { + const is_natural = value.every(val => Number.isInteger(val) && val >= 0); + if (is_natural) { + this.list = value.length > 0 ? [...value] : null; + } else { + console.error("RT-Manuscript Layout Error: Counter list must contain only natural numbers."); + this.list = null; + } + } else { + this.list = null; + } + } else if (key === 'Count' || key === 'count') { + const source_count = value instanceof Count ? value : (value && value.count instanceof Count ? value.count : null); + if (source_count) { + this.status = source_count.status; + this.list = source_count.list ? [...source_count.list] : null; + } else { + console.error("RT-Manuscript Layout Error: Invalid object provided to write('Count')."); + } + } + } + + increment() { + if (this.status === 'empty') { + console.error("RT-Manuscript Layout Error: Attempted to increment an empty Count object."); + return; + } + if (this.list && this.list.length > 0) { + this.list[this.list.length - 1] += 1; + } + } + + push(val) { + if (this.status === 'empty') { + console.error("RT-Manuscript Layout Error: Attempted to push to an empty Count object."); + return; + } + if (!this.list) { + this.list = []; + } + this.list.push(val); + } + + pop() { + if (this.status === 'empty') { + console.error("RT-Manuscript Layout Error: Attempted to pop from an empty Count object."); + return undefined; + } + if (this.list && this.list.length > 0) { + const val = this.list.pop(); + if (this.list.length === 0) { + this.list = null; // Revert to strict null state if emptied + } + return val; + } + return undefined; + } + + clone() { + const c = new Count(); + c.status = this.status; + c.list = this.list ? [...this.list] : null; + return c; + } + + } + + class CounterMachine { + constructor(config) { + this.count = new Count(); + this.style = ['NaturalNumber']; + this.separator = '.'; + this.separator_placement = 'embedded'; + this.mode = 'scoped'; // 'scoped', 'milestone' + + if (config) { + this.write(config); + } + } + + read(...path) { + if (path.length === 0) return undefined; + + if (path[0] === 'count') { + if (path.length === 1) { + return this.count.clone(); + } + return this.count.read(...path.slice(1)); + } + + return path.reduce((acc, key) => (acc && acc[key] !== undefined) ? acc[key] : undefined, this); + } + + write(dict) { + for (const [key, value] of Object.entries(dict)) { + if (key === 'style') { + let parsed = Array.isArray(value) ? value : [value]; + if (parsed.length === 1 && parsed[0] === 'outline') { + parsed = ['Roman', 'Alpha', 'roman', 'alpha', 'CountingNumber']; + } + this.style = parsed; + } else if (key === 'Count' || key === 'count') { + // Support copying from another CounterMachine or Count object + const source_count = value instanceof CounterMachine ? value.count : (value instanceof Count ? value : null); + if (source_count) { + this.count = source_count.clone(); + } else { + console.error("RT-Manuscript Layout Error: Invalid object provided to write('Count')."); + } + } else { + this[key] = value; + } + } + } + + enter(first_step_val) { + const current_status = this.count.read('status'); + + if (current_status === 'empty') { + // Transition state strictly before mutating the list + this.count.write('status', 'preamble'); + this.count.push(first_step_val !== undefined ? first_step_val : 0); + } else if (current_status === 'preamble') { + this.count.push(0); // indent appends 0 + this.count.write('status', 'preamble'); + } else if (current_status === 'between') { + this.count.increment(); + this.count.write('status', 'preamble'); + } + } + + exit() { + const current_status = this.count.read('status'); + + if (current_status === 'empty') { + console.error("RT-Manuscript Layout Error: Attempted to exit an empty counter scope."); + } else if (current_status === 'preamble') { + this.count.write('status', 'between'); + } else if (current_status === 'between') { + this.count.pop(); + this.count.write('status', 'between'); + } + } + + to_string(count_obj) { + if (!count_obj) return ''; + + const status = count_obj.read('status'); + if (status === 'empty') return ''; + + let active_list; + if (this.mode === 'scoped' && status === 'between') { + active_list = count_obj.read('list', 'short'); + } else { + active_list = count_obj.read('list'); + } + + // Safety check catches null or empty arrays + if (!active_list || active_list.length === 0) { + return ''; + } + + const formatted_list = active_list.map((val, depth) => { + const current_style = depth < this.style.length ? this.style[depth] : this.style[this.style.length - 1]; + const method_name = `to_${current_style}`; + return typeof this[method_name] === 'function' ? this[method_name](val) : this.to_NaturalNumber(val); + }); + + let count_str = formatted_list.join(this.separator); + if (this.separator_placement === 'embedded-after') { + count_str += this.separator; + } + return count_str; + } + + // --- Atomic Type Conversions --- + + to_NaturalNumber(num) { return num.toString(); } + from_NaturalNumber(val) { + const n = parseInt(val, 10); + return isNaN(n) ? 0 : n; + } + + to_CountingNumber(num) { return (num + 1).toString(); } + from_CountingNumber(val) { + const n = parseInt(val, 10); + return isNaN(n) ? 0 : Math.max(0, n - 1); + } + + to_roman(num) { return this.to_Roman(num).toLowerCase(); } + from_roman(val) { return this.from_Roman(val.toUpperCase()); } + + to_Roman(num) { + let n = num + 1; // 0-indexed to 1-indexed + if (n < 1) return n.toString(); + const lookup = {M:1000, CM:900, D:500, CD:400, C:100, XC:90, L:50, XL:40, X:10, IX:9, V:5, IV:4, I:1}; + let roman = ''; + for (let i in lookup) { + while (n >= lookup[i]) { + roman += i; + n -= lookup[i]; + } + } + return roman; + } + from_Roman(val) { + if (!/^M*(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/i.test(val)) return 0; + const lookup = {M:1000, CM:900, D:500, CD:400, C:100, XC:90, L:50, XL:40, X:10, IX:9, V:5, IV:4, I:1}; + let temp = val.toUpperCase(); + let num = 0; + let i = 0; + while (i < temp.length) { + if (i + 1 < temp.length && lookup[temp.substring(i, i + 2)]) { + num += lookup[temp.substring(i, i + 2)]; + i += 2; + } else { + num += lookup[temp[i]]; + i++; + } + } + return Math.max(0, num - 1); // 1-indexed to 0-indexed + } + + to_Alpha(num) { return String.fromCharCode(65 + num); } + from_Alpha(val) { + if (/^[A-Z]$/.test(val)) return val.charCodeAt(0) - 65; + return 0; + } + + to_alpha(num) { return String.fromCharCode(97 + num); } + from_alpha(val) { + if (/^[a-z]$/.test(val)) return val.charCodeAt(0) - 97; + return 0; + } + + clone() { + const copy = new CounterMachine(); + copy.count = this.count.clone(); + copy.style = [...this.style]; + copy.separator = this.separator; + copy.separator_placement = this.separator_placement; + copy.mode = this.mode; + return copy; + } + } + + counter = function(){ + const debug = RT.Debug || { log: function(){} }; + if (debug.log) debug.log('counter', 'Processing counters'); + + const root_node = document.documentElement; + + function walk(node) { + if (node.nodeType !== Node.ELEMENT_NODE) return; + + const tag = node.tagName.toLowerCase(); + let machine_to_exit = null; + + if (tag === 'rt·counter·make') { + const name = node.getAttribute('counter'); + if (name) { + const style_attr = node.getAttribute('style'); + const parsed_style = style_attr ? style_attr.split(',').map(s => s.trim()) : ['NaturalNumber']; + + RT.dict_instance[name] = new CounterMachine({ + style: parsed_style, + separator: node.getAttribute('separator') || '.', + separator_placement: node.getAttribute('separator-placement') || 'embedded', + mode: node.getAttribute('mode') || 'scoped' + }); + + const on_first_step_str = node.getAttribute('on-first-step'); + if (on_first_step_str) { + const top_style = RT.dict_instance[name].style[0]; + const method_name = `from_${top_style}`; + const active_machine = RT.dict_instance[name]; + const initial_val = typeof active_machine[method_name] === 'function' + ? active_machine[method_name](on_first_step_str) + : active_machine.from_NaturalNumber(on_first_step_str); + + active_machine.first_step_val = initial_val; + } + } + } else if (tag === 'rt·counter·step') { + const name = node.getAttribute('counter'); + if (name && RT.dict_instance[name]) { + const active_machine = RT.dict_instance[name]; + active_machine.enter(active_machine.first_step_val); + active_machine.first_step_val = undefined; // consume it + machine_to_exit = active_machine; + } + } else if (tag === 'rt·counter·snapshot') { + const counter_name = node.getAttribute('counter'); + const snapshot_name = node.getAttribute('snapshot'); + + if (counter_name && snapshot_name && RT.dict_instance[counter_name]) { + const active_machine = RT.dict_instance[counter_name]; + + if (active_machine.read('count', 'status') === 'empty') { + console.error(`RT-Manuscript Layout Error: Attempted to snapshot an empty counter '${counter_name}' at snapshot '${snapshot_name}'. A step is required first.`); + } else { + RT.dict_snapshot[snapshot_name] = active_machine.clone(); + } + } + } + + let child = node.firstElementChild; + while (child) { + walk(child); + child = child.nextElementSibling; + } + + if (machine_to_exit) { + machine_to_exit.exit(); + } + } + + walk(root_node); + + const reads = root_node.querySelectorAll('RT·counter·read, rt·counter·read'); + + for (let i = 0; i < reads.length; i++) { + process_read_node(reads[i]); + } + + function process_read_node(node) { + const snapshot_name = node.getAttribute('snapshot'); + const key = node.getAttribute('key') || 'count'; + + if (snapshot_name && RT.dict_snapshot[snapshot_name]) { + const snapshot_machine = RT.dict_snapshot[snapshot_name]; + + if (key === 'count') { + const raw_state = snapshot_machine.read('count'); + node.innerHTML = snapshot_machine.to_string(raw_state); + } else { + const keys = key.split('.'); + const value = snapshot_machine.read(...keys); + + if (value === null) { + node.innerHTML = 'null'; + } else if (value !== undefined) { + node.innerHTML = Array.isArray(value) ? value.join(',') : value; + } else { + node.innerHTML = `[Missing key: ${key}]`; + } + } + } else { + node.innerHTML = `[Unknown snapshot: ${snapshot_name}]`; + console.error(`RT-Manuscript Layout Error: failed. No snapshot named '${snapshot_name}' found.`); + } + } + }; + + //------------------------------------------ + // on module load + // + + window.RT.counter = counter; + +})(); diff --git a/developer/authored/Manuscript.copy/Layout/note.js b/developer/authored/Manuscript.copy/Layout/note.js new file mode 100644 index 0000000..cc437ff --- /dev/null +++ b/developer/authored/Manuscript.copy/Layout/note.js @@ -0,0 +1,76 @@ +/* + Element/note.js + Processes RT·Note·write and RT·Note·read tags for document cross referencing. +*/ + +(function(){ + + if(!window.RT){ + console.error("RT not defined. Was RT_Manuscript_make run?"); + return; + } + + function note(){ + const debug = window.RT.Debug || { log: function(){} ,error: function(){} }; + if(debug.log) debug.log('note' ,'Running note resolution'); + + const root_node = document.documentElement; + const ref_dictionary = {}; + + // Pass 1: Gather writes + const writes = root_node.querySelectorAll('RT·Note·write, rt·note·write'); + for(let i = 0; i < writes.length; i++){ + const node = writes[i]; + const key = node.getAttribute('key'); + + if(key){ + const text_content = node.innerHTML; + let page_num = "unknown"; + + // Traverse up to find the page counter + let parent = node.parentElement; + while(parent){ + if( parent.tagName && (parent.tagName.toLowerCase() === 'rt·page') ){ + const page_read = parent.querySelector('rt·counter·read[snapshot^="page_snap_"]'); + if(page_read){ + page_num = page_read.innerHTML; + } + break; + } + parent = parent.parentElement; + } + + ref_dictionary[key] = { + content: text_content + ,page: page_num + }; + } + } + + // Pass 2: Resolve reads + const reads = root_node.querySelectorAll('RT·Note·read, rt·note·read'); + for(let i = 0; i < reads.length; i++){ + const node = reads[i]; + const key = node.getAttribute('key'); + const field = node.getAttribute('field') || 'content'; + + if(key && ref_dictionary[key]){ + const value = ref_dictionary[key][field]; + if(value !== undefined){ + node.innerHTML = value; + } else { + node.innerHTML = `[Invalid field: ${field}]`; + } + } else { + node.innerHTML = `[Unknown note: ${key}]`; + } + } + } + + //---------------------------------------- + // Registration upon load + // + + window.RT.note = note; + +})(); diff --git a/developer/authored/Manuscript.copy/Layout/paginate.js b/developer/authored/Manuscript.copy/Layout/paginate.js index 0a9a26a..bff6dce 100644 --- a/developer/authored/Manuscript.copy/Layout/paginate.js +++ b/developer/authored/Manuscript.copy/Layout/paginate.js @@ -1,291 +1,291 @@ /* + Layout/paginate.js Processes tags and paginates their contents. - Handles inline footnotes and element splitting to enforce page height limits. + Handles inline footnotes, element splitting, and page height limits. */ -(function() { +(function(){ - if (!window.RT) { + if(!window.RT){ console.error("RT not defined - was RT-Style_make run?"); return; } - window.RT.paginate = function() { - const RT = window.RT; - const debug = RT.Debug || { log: function(){}, error: function(){} }; - const page_conf = (RT.config && RT.config.page) ? RT.config.page : {}; - const page_height_limit = page_conf.height_limit || 1000; - - if (debug.log) debug.log('paginate', 'Running document pagination'); - - let measureContainer = null; - - // ========================================================= - // 1. DOM Measurement Utilities - // ========================================================= - function getElHeight(el) { - const wasInDOM = el.parentNode !== null; - if (!wasInDOM) document.body.appendChild(el); - const rect = el.getBoundingClientRect(); - const style = window.getComputedStyle(el); - const margin = parseFloat(style.marginTop) + parseFloat(style.marginBottom); - if (!wasInDOM) el.remove(); - return (rect.height || 0) + (margin || 0); - } + const RT = window.RT; + const debug = RT.Debug || { log: function(){} ,error: function(){} }; + const page_conf = (RT.config && RT.config.page) ? RT.config.page : {}; + const page_height_limit = page_conf.height_limit || 1000; + + let measureContainer = null; + + // ========================================================= + // 1. DOM Measurement Utilities + // ========================================================= + function getElHeight(el){ + const wasInDOM = el.parentNode !== null; + if(!wasInDOM) document.body.appendChild(el); + const rect = el.getBoundingClientRect(); + const style = window.getComputedStyle(el); + const margin = parseFloat(style.marginTop) + parseFloat(style.marginBottom); + if(!wasInDOM) el.remove(); + return (rect.height || 0) + (margin || 0); + } - function getMeasureContainer() { - if (measureContainer && measureContainer.parentNode) return measureContainer; - const article = document.querySelector('RT·article'); - if (!article) { - const temp = document.createElement('div'); - temp.style.visibility = 'hidden'; - temp.style.position = 'absolute'; - temp.style.width = '100%'; - document.body.appendChild(temp); - measureContainer = temp; - return temp; - } - const container = document.createElement('div'); - const articleStyle = window.getComputedStyle(article); - container.style.visibility = 'hidden'; - container.style.position = 'absolute'; - container.style.width = articleStyle.width; - container.style.fontFamily = articleStyle.fontFamily; - container.style.fontSize = articleStyle.fontSize; - container.style.lineHeight = articleStyle.lineHeight; - container.style.fontWeight = articleStyle.fontWeight; - document.body.appendChild(container); - measureContainer = container; - return container; + function getMeasureContainer(){ + if(measureContainer && measureContainer.parentNode) return measureContainer; + const article = document.querySelector('RT·article'); + if(!article){ + const temp = document.createElement('div'); + temp.style.visibility = 'hidden'; + temp.style.position = 'absolute'; + temp.style.width = '100%'; + document.body.appendChild(temp); + measureContainer = temp; + return temp; } + const container = document.createElement('div'); + const articleStyle = window.getComputedStyle(article); + container.style.visibility = 'hidden'; + container.style.position = 'absolute'; + container.style.width = articleStyle.width; + container.style.fontFamily = articleStyle.fontFamily; + container.style.fontSize = articleStyle.fontSize; + container.style.lineHeight = articleStyle.lineHeight; + container.style.fontWeight = articleStyle.fontWeight; + document.body.appendChild(container); + measureContainer = container; + return container; + } + + function measureFragment(frag){ + const container = getMeasureContainer(); + container.appendChild(frag); + const h = getElHeight(frag); + container.removeChild(frag); + return h; + } - function measureFragment(frag) { - const container = getMeasureContainer(); - container.appendChild(frag); - const h = getElHeight(frag); - container.removeChild(frag); - return h; + // ========================================================= + // Splitting Logic + // ========================================================= + function isSplittable(el){ + const tag = el.tagName; + if(tag === 'UL' || tag === 'OL'){ + const items = Array.from(el.children).filter(c => c.tagName === 'LI'); + if(items.length === 0) return null; + + const itemHeights = items.map(li => getElHeight(li)); + const emptyClone = el.cloneNode(false); + const overhead = getElHeight(emptyClone); + + el._splitInfo = { type: 'list' ,itemHeights ,overhead ,offset: 0 }; + return makeListSplitter(el ,el._splitInfo); } - // ========================================================= - // STEP 1: PREPARE FOOTNOTES (Strip and tag) - // ========================================================= - const article_seq = document.querySelectorAll('RT·article'); - if (article_seq.length === 0) { - debug.error('pagination', 'No elements found. Pagination aborted.'); - return; + if(tag === 'TABLE'){ + const thead = el.querySelector('thead'); + const tbody = el.querySelector('tbody'); + const rows = tbody ? Array.from(tbody.rows) : Array.from(el.rows); + if(rows.length === 0) return null; + + const theadHeight = thead ? getElHeight(thead) : 0; + const rowHeights = rows.map(row => getElHeight(row)); + + const emptyClone = el.cloneNode(false); + if(thead) emptyClone.appendChild(thead.cloneNode(true)); + emptyClone.appendChild(document.createElement('tbody')); + const overhead = getElHeight(emptyClone) - theadHeight; + + el._splitInfo = { type: 'table' ,rowHeights ,overhead ,theadHeight ,offset: 0 }; + return makeTableSplitter(el ,el._splitInfo); } + return null; + } - const footnote_registry = {}; - let footnote_counter = 1; + function makeListSplitter(el ,info){ + return (remaining) => { + const children = Array.from(el.children).filter(c => c.tagName === 'LI'); + const start = info.offset; - Array.from(article_seq).forEach(article => { - const all_nodes = Array.from(article.querySelectorAll('*')); - const raw_footnotes = all_nodes.filter(node => node.tagName.toLowerCase() === 'rt·footnote'); + let bestCount = 0; + let bestHeight = 0; + const tempList = el.cloneNode(false); - raw_footnotes.forEach(fn => { - const id = footnote_counter++; - footnote_registry[id] = fn.innerHTML; - - const prev = fn.previousSibling; - if (prev && prev.nodeType === Node.TEXT_NODE) { - prev.textContent = prev.textContent.replace(/\s+$/, ''); + for(let i = 0; i < children.length; i++){ + const itemClone = children[i].cloneNode(true); + tempList.appendChild(itemClone); + const fragHeight = measureFragment(tempList); + if(fragHeight <= remaining){ + bestCount = i + 1; + bestHeight = fragHeight; + } else { + tempList.removeChild(itemClone); + break; } + } - const marker = document.createElement('RT·fn-marker'); - marker.setAttribute('data-id', id); - - if (fn.parentNode) { - fn.parentNode.replaceChild(marker, fn); - } - }); - }); + if(bestCount === 0) return { first: null ,rest: el ,firstHeight: 0 }; - // ========================================================= - // Splitting Logic - // ========================================================= - function isSplittable(el) { - const tag = el.tagName; - if (tag === 'UL' || tag === 'OL') { - const items = Array.from(el.children).filter(c => c.tagName === 'LI'); - if (items.length === 0) return null; - - const itemHeights = items.map(li => getElHeight(li)); - const emptyClone = el.cloneNode(false); - const overhead = getElHeight(emptyClone); - - el._splitInfo = { type: 'list', itemHeights, overhead, offset: 0 }; - return makeListSplitter(el, el._splitInfo); + const first = el.cloneNode(false); + for(let i = 0; i < bestCount; i++){ + first.appendChild(children[i].cloneNode(true)); } - if (tag === 'TABLE') { - const thead = el.querySelector('thead'); - const tbody = el.querySelector('tbody'); - const rows = tbody ? Array.from(tbody.rows) : Array.from(el.rows); - if (rows.length === 0) return null; - - const theadHeight = thead ? getElHeight(thead) : 0; - const rowHeights = rows.map(row => getElHeight(row)); + let rest = null; + if(bestCount < children.length){ + rest = el.cloneNode(false); + for(let i = bestCount; i < children.length; i++){ + rest.appendChild(children[i].cloneNode(true)); + } - const emptyClone = el.cloneNode(false); - if (thead) emptyClone.appendChild(thead.cloneNode(true)); - emptyClone.appendChild(document.createElement('tbody')); - const overhead = getElHeight(emptyClone) - theadHeight; + if(el.tagName === 'OL'){ + const currentStart = parseInt(el.getAttribute('start') ,10) || 1; + rest.setAttribute('start' ,currentStart + bestCount); + } - el._splitInfo = { type: 'table', rowHeights, overhead, theadHeight, offset: 0 }; - return makeTableSplitter(el, el._splitInfo); + rest._splitInfo = { + type: 'list' + ,itemHeights: info.itemHeights + ,overhead: info.overhead + ,offset: start + bestCount + }; } - return null; - } - function makeListSplitter(el, info) { - return (remaining) => { - const children = Array.from(el.children).filter(c => c.tagName === 'LI'); - const start = info.offset; + return { first ,rest ,firstHeight: bestHeight }; + }; + } - let bestCount = 0; - let bestHeight = 0; - const tempList = el.cloneNode(false); - - for (let i = 0; i < children.length; i++) { - const itemClone = children[i].cloneNode(true); - tempList.appendChild(itemClone); - const fragHeight = measureFragment(tempList); - if (fragHeight <= remaining) { - bestCount = i + 1; - bestHeight = fragHeight; - } else { - tempList.removeChild(itemClone); - break; - } + function makeTableSplitter(el ,info){ + const thead = el.querySelector('thead'); + const createShell = () => { + const shell = el.cloneNode(false); + if(thead) shell.appendChild(thead.cloneNode(true)); + const newTbody = document.createElement('tbody'); + shell.appendChild(newTbody); + return shell; + }; + + return (remaining) => { + const tbody = el.querySelector('tbody'); + const rows = tbody ? Array.from(tbody.rows) : Array.from(el.rows); + const start = info.offset; + + let bestCount = 0; + let bestHeight = 0; + const tempTable = createShell(); + const tempBody = tempTable.querySelector('tbody'); + + for(let i = 0; i < rows.length; i++){ + tempBody.appendChild(rows[i].cloneNode(true)); + const h = measureFragment(tempTable); + if(h <= remaining){ + bestCount = i + 1; + bestHeight = h; + } else { + tempBody.removeChild(tempBody.lastChild); + break; } + } - if (bestCount === 0) return { first: null, rest: el, firstHeight: 0 }; + if(bestCount === 0) return { first: null ,rest: el ,firstHeight: 0 }; - const first = el.cloneNode(false); - for (let i = 0; i < bestCount; i++) { - first.appendChild(children[i].cloneNode(true)); + const first = createShell(); + const firstBody = first.querySelector('tbody'); + for(let i = 0; i < bestCount; i++){ + firstBody.appendChild(rows[i].cloneNode(true)); + } + + let rest = null; + if(bestCount < rows.length){ + rest = createShell(); + const restBody = rest.querySelector('tbody'); + for(let i = bestCount; i < rows.length; i++){ + restBody.appendChild(rows[i].cloneNode(true)); } - let rest = null; - if (bestCount < children.length) { - rest = el.cloneNode(false); - for (let i = bestCount; i < children.length; i++) { - rest.appendChild(children[i].cloneNode(true)); - } + rest._splitInfo = { + type: 'table' + ,rowHeights: info.rowHeights + ,overhead: info.overhead + ,theadHeight: info.theadHeight + ,offset: start + bestCount + }; + } - if (el.tagName === 'OL') { - const currentStart = parseInt(el.getAttribute('start'), 10) || 1; - rest.setAttribute('start', currentStart + bestCount); - } + return { first ,rest ,firstHeight: bestHeight }; + }; + } - rest._splitInfo = { - type: 'list', - itemHeights: info.itemHeights, - overhead: info.overhead, - offset: start + bestCount - }; - } - return { first, rest, firstHeight: bestHeight }; - }; - } + // ========================================================= + // PAGINATE 0: CHUNKING & INJECTING STRUCTURE + // ========================================================= + paginate_0 = function(){ + if(debug.log) debug.log('paginate_0' ,'Running initial document chunking'); - function makeTableSplitter(el, info) { - const thead = el.querySelector('thead'); - const createShell = () => { - const shell = el.cloneNode(false); - if (thead) shell.appendChild(thead.cloneNode(true)); - const newTbody = document.createElement('tbody'); - shell.appendChild(newTbody); - return shell; - }; - - return (remaining) => { - const tbody = el.querySelector('tbody'); - const rows = tbody ? Array.from(tbody.rows) : Array.from(el.rows); - const start = info.offset; - - let bestCount = 0; - let bestHeight = 0; - const tempTable = createShell(); - const tempBody = tempTable.querySelector('tbody'); - - for (let i = 0; i < rows.length; i++) { - tempBody.appendChild(rows[i].cloneNode(true)); - const h = measureFragment(tempTable); - if (h <= remaining) { - bestCount = i + 1; - bestHeight = h; - } else { - tempBody.removeChild(tempBody.lastChild); - break; - } - } + const article_seq = document.querySelectorAll('RT·article'); + if(article_seq.length === 0){ + debug.error('pagination' ,'No elements found. Pagination aborted.'); + return; + } - if (bestCount === 0) return { first: null, rest: el, firstHeight: 0 }; + const footnote_registry = {}; + let footnote_counter = 1; - const first = createShell(); - const firstBody = first.querySelector('tbody'); - for (let i = 0; i < bestCount; i++) { - firstBody.appendChild(rows[i].cloneNode(true)); + // Strip and tag footnotes + Array.from(article_seq).forEach(article => { + const all_nodes = Array.from(article.querySelectorAll('*')); + const raw_footnotes = all_nodes.filter(node => node.tagName.toLowerCase() === 'rt·footnote'); + + raw_footnotes.forEach(fn => { + const id = footnote_counter++; + footnote_registry[id] = fn.innerHTML; + + const prev = fn.previousSibling; + if(prev && prev.nodeType === Node.TEXT_NODE){ + prev.textContent = prev.textContent.replace(/\s+$/ ,''); } - let rest = null; - if (bestCount < rows.length) { - rest = createShell(); - const restBody = rest.querySelector('tbody'); - for (let i = bestCount; i < rows.length; i++) { - restBody.appendChild(rows[i].cloneNode(true)); - } - - rest._splitInfo = { - type: 'table', - rowHeights: info.rowHeights, - overhead: info.overhead, - theadHeight: info.theadHeight, - offset: start + bestCount - }; + const marker = document.createElement('RT·fn-marker'); + marker.setAttribute('data-id' ,id); + + if(fn.parentNode){ + fn.parentNode.replaceChild(marker ,fn); } + }); + }); - return { first, rest, firstHeight: bestHeight }; - }; - } - - // ========================================================= - // STEP 2: NORMAL PAGINATOR - // ========================================================= - function paginateArticle(article) { + function paginateArticle(article){ const raw_element_seq = Array.from(article.children).filter(el => - !['SCRIPT', 'STYLE', 'RT·PAGE'].includes(el.tagName) + !['SCRIPT' ,'STYLE' ,'RT·PAGE'].includes(el.tagName) ); - if (raw_element_seq.length === 0) return; + if(raw_element_seq.length === 0) return; const page_seq = []; let current_batch_seq = []; let current_h = 0; let i = 0; - while (i < raw_element_seq.length) { + while(i < raw_element_seq.length){ const el = raw_element_seq[i]; const splitter = isSplittable(el); - if (splitter) { + if(splitter){ const remaining = page_height_limit - current_h; - const { first, rest, firstHeight } = splitter(remaining); + const { first ,rest ,firstHeight } = splitter(remaining); - if (first) { + if(first){ current_batch_seq.push(first); current_h += firstHeight; - if (rest) { - raw_element_seq.splice(i, 1, rest); + if(rest){ + raw_element_seq.splice(i ,1 ,rest); } else { - raw_element_seq.splice(i, 1); + raw_element_seq.splice(i ,1); } } else { - if (current_batch_seq.length === 0) { + if(current_batch_seq.length === 0){ const frame = document.createElement('RT·scroll-frame'); frame.style.display = 'block'; frame.style.overflowY = 'auto'; @@ -310,15 +310,15 @@ let backtrack_seq = []; let backtrack_h = 0; - while (current_batch_seq.length > 0) { + while(current_batch_seq.length > 0){ const last = current_batch_seq[current_batch_seq.length - 1]; - if (!/^H[1-6]/.test(last.tagName)) break; + if(!/^H[1-6]/.test(last.tagName)) break; const popped = current_batch_seq.pop(); backtrack_seq.unshift(popped); backtrack_h += getElHeight(popped); } - if (current_batch_seq.length > 0) { + if(current_batch_seq.length > 0){ page_seq.push(current_batch_seq); current_batch_seq = backtrack_seq; current_h = backtrack_h; @@ -334,17 +334,46 @@ i++; } - if (current_batch_seq.length > 0) { + if(current_batch_seq.length > 0){ page_seq.push(current_batch_seq); } article.innerHTML = ''; + + // Inject global page counter initialization + const page_counter_make = document.createElement('rt·counter·make'); + page_counter_make.setAttribute('counter' ,'RT_page_number'); + page_counter_make.setAttribute('style' ,'NaturalNumber'); + page_counter_make.setAttribute('on-first-step' ,'1'); + page_counter_make.setAttribute('mode' ,'milestone'); + article.appendChild(page_counter_make); + let p = 0; - while (p < page_seq.length) { + while(p < page_seq.length){ const batch = page_seq[p]; const page_el = document.createElement('RT·page'); - page_el.id = `page-${p + 1}`; + batch.forEach(item => page_el.appendChild(item)); + + // Inject step and snapshot tags into the page + const page_step = document.createElement('rt·counter·step'); + page_step.setAttribute('counter' ,'RT_page_number'); + page_el.appendChild(page_step); + + const snapshot_name = `page_snap_${p + 1}`; + const page_snap = document.createElement('rt·counter·snapshot'); + page_snap.setAttribute('counter' ,'RT_page_number'); + page_snap.setAttribute('snapshot' ,snapshot_name); + page_el.appendChild(page_snap); + + // Optional footer text injection reading the counter snapshot + const page_footer = document.createElement('div'); + page_footer.className = 'RT·page-footer'; + const page_read = document.createElement('rt·counter·read'); + page_read.setAttribute('snapshot' ,snapshot_name); + page_footer.appendChild(page_read); + page_el.appendChild(page_footer); + article.appendChild(page_el); p++; } @@ -352,9 +381,7 @@ Array.from(article_seq).forEach(article => paginateArticle(article)); - // ========================================================= - // STEP 3: RESOLVE FOOTNOTES - // ========================================================= + // Resolve footnotes inside the generated pages Array.from(article_seq).forEach(article => { const rendered_pages = article.querySelectorAll('RT·page'); @@ -362,7 +389,7 @@ const all_page_nodes = Array.from(page.querySelectorAll('*')); const markers = all_page_nodes.filter(node => node.tagName.toLowerCase() === 'rt·fn-marker'); - if (markers.length === 0) return; + if(markers.length === 0) return; const fn_container = document.createElement('div'); fn_container.className = 'RT·footnote-container'; @@ -378,8 +405,8 @@ const sup = document.createElement('sup'); sup.innerHTML = `${id}`; - if (marker.parentNode) { - marker.parentNode.replaceChild(sup, marker); + if(marker.parentNode){ + marker.parentNode.replaceChild(sup ,marker); } const fn_line = document.createElement('div'); @@ -393,9 +420,33 @@ }); }); - if (measureContainer && measureContainer.parentNode) { + if(measureContainer && measureContainer.parentNode){ measureContainer.remove(); measureContainer = null; } }; + + // ========================================================= + // PAGINATE 1: ABSORB DIMENSIONAL DELTA + // ========================================================= + paginate_1 = function(){ + if(debug.log) debug.log('paginate_1' ,'Adjusting final page heights after component injections'); + + const rendered_pages = document.querySelectorAll('RT·page'); + Array.from(rendered_pages).forEach(page => { + const actual_height = page.scrollHeight; + if(actual_height > page_height_limit){ + page.style.minHeight = actual_height + 'px'; + } + }); + }; + + //---------------------------------------- + // Registration upon load + // + + window.RT.paginate_0 = paginate_0; + window.RT.paginate_1 = paginate_1; + + })(); diff --git a/developer/document/RT-code-format.html b/developer/document/RT-code-format.html index 2122e67..af29ba0 100644 --- a/developer/document/RT-code-format.html +++ b/developer/document/RT-code-format.html @@ -71,7 +71,7 @@

    Ad hoc namespacing

    - Most parsers now a allow a center dot (·) in identifiers. In languages were namespaces are not explicitly available, we use the cdot to represent a namespace. Namespaces are closely related to module names, class names, interface names, and to type names,so all are written in PascalCase. So for example, Math·rounded__x_coordinate, might be variable in the Math namespace, a function on the Math interface, etc. + Most parsers now a allow a typographical middle dot (·) in identifiers. In languages were namespaces are not explicitly available, we use the cdot to represent a namespace. Namespaces are closely related to module names, class names, interface names, and to type names,so all are written in PascalCase. So for example, Math·rounded__x_coordinate, might be variable in the Math namespace, a function on the Math interface, etc.

    Component order

    @@ -191,6 +191,25 @@

    Python enforces indentation syntactically. Use two-space indentation for all Python code, even though four is common in the wider ecosystem.

    +

    Short stuff

    + +

    Generally if a compound statement fits on a single line in less 40 to 60 characters, put it on the single line. If dropping the containing braces can be done, do that also. As examples, the following forms are preferred over their extended box counterparts:

    + + + if(x == 0) return 0; + + + + if(x == 0) return 0; + else if(x == 1) return 10; + else if(x == 2) return 20; + + + + while(i<=10){i++; pt++;} + + +

    The CLI vs. work function pattern

    To avoid the "String Trap" (where logic is tightly coupled to the terminal and requires string serialization to reuse) executable modules must separate the Command Line Interface from the core logic. @@ -212,29 +231,6 @@

  1. Prefer explicit *_count parameters over sentinel values when passing arrays.
  2. -

    Automated formatting tools

    -

    - To ensure consistency without manual drudgery, the Harmony skeleton provides a dedicated code formatter called RTfmt. A person can find this tool in the shared/tool/ directory. -

    - -

    The RTfmt CLI

    -

    - RTfmt is a shallow-tokenizing formatter written in Python. It parses source code into structural blocks (strings, comments, commas, and enclosures) without needing a full Abstract Syntax Tree. This architecture allows it to safely enforce RT formatting rules across multiple languages while preserving indentation and protecting native operators. -

    -
      -
    • RTfmt write <file...> : Formats files in place. It performs a content comparison before writing, leaving the file modification timestamp untouched if the file is already compliant.
    • -
    • RT·formatter pipe : Reads from standard input and writes to standard output, making it ideal for editor integration.
    • -
    • --lisp : A flag that instructs the formatter to skip the outermost enclosure padding rule, honoring the Lisp function call exception.
    • -
    - -

    Emacs integration

    -

    - For Emacs users, the RT·formatter.el file provides the RT·formatter-buffer interactive command. -

    -

    - This wrapper passes the current buffer through the RT·formatter pipe command. It automatically detects Lisp-derived modes to append the --lisp flag. Furthermore, it utilizes a non-destructive buffer replacement strategy. This ensures that a programmer's cursor position, selection marks, and window scroll state remain anchored exactly where they were before the formatting occurred. -

    -

    Exercises

    Exercise 1: Comma and function call formatting

    Reformat the following C code snippet to strictly adhere to the RT code format rules.

    diff --git a/developer/document/todo.txt b/developer/document/todo.txt index ae46dd9..f29774c 100644 --- a/developer/document/todo.txt +++ b/developer/document/todo.txt @@ -1,6 +1,12 @@ 2026-06-28 17:14:07 Z -The theme meta name field, must match the key the manifest uses to write the theme into the library. There is a third name, the file name. We should simplify this. If the names don't match then the theme selector, which accesses the 'read meta name' will fail to color in the selected theme. + The theme meta name field, must match the key the manifest uses to write the theme into the library. There is a third name, the file name. We should simplify this. If the names don't match then the theme selector, which accesses the 'read meta name' will fail to color in the selected theme. -The RT = RT || {}, etc, should be replaced with checks and error messes, already done in some places. Things being missing is a problem. + The RT = RT || {}, etc, should be replaced with checks and error messes, already done in some places. Things being missing is a problem. + +2026-07-01 08:06:54 Z + + Also paginate uses ad hoc page numbering, so it must be updated to make use of the counters. I see a kink in the plan, as pages are generated it is not possible for a user to add cross reference that has a page number counter as its content. I will add this to the 'todo.txt' notes, and we will punt this issue to a later versioin. + + The Note tag should also do the duty of footnote and endnote. If cited in a list at the bottom of the page, it is a footnote. If listed at the end of the document, it is an end note. Notes should then have path keys, so that footnotes and endnotes etc. can have order. diff --git a/tester/authored/Counter/test_0.html b/tester/authored/Counter/test_0.html index 16e962a..9ec2a6d 100644 --- a/tester/authored/Counter/test_0.html +++ b/tester/authored/Counter/test_0.html @@ -79,7 +79,7 @@ snapshot() == (between,[1])
    snapshot() -> ( - , + , [] , "" ) @@ -91,7 +91,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) @@ -104,7 +104,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) @@ -117,7 +117,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) @@ -130,7 +130,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) @@ -143,7 +143,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) @@ -156,7 +156,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) // Expected: ( between ,[0] , "" ) @@ -168,7 +168,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) // Expected: ( preamble ,[1] , "2" ) @@ -180,7 +180,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) // Expected: ( preamble ,[1,0] , "2.1" ) @@ -192,7 +192,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) // Expected: ( between ,[1,0] , "2" ) @@ -204,7 +204,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) // Expected: ( preamble ,[1,1] , "2.2" ) @@ -219,7 +219,7 @@ snapshot() == (between,[1])
    snapshot() == ( - , + , [] , "" ) // Expected: ( between ,[1] , "" ) diff --git a/tester/authored/Counter/test_1.html b/tester/authored/Counter/test_1.html index e8eb51f..88956ac 100644 --- a/tester/authored/Counter/test_1.html +++ b/tester/authored/Counter/test_1.html @@ -33,7 +33,7 @@
    snapshot() -> ( - , + , [] , "" ) @@ -45,7 +45,7 @@
    snapshot() == ( - , + , [] , "" ) @@ -58,7 +58,7 @@
    snapshot() == ( - , + , [] , "" ) @@ -71,7 +71,7 @@
    snapshot() == ( - , + , [] , "" ) @@ -84,7 +84,7 @@
    snapshot() == ( - , + , [] , "" ) @@ -97,7 +97,7 @@
    snapshot() == ( - , + , [] , "" ) @@ -110,7 +110,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( between ,[0] , "" ) @@ -122,7 +122,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( preamble ,[1] , "I" ) @@ -134,7 +134,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( preamble ,[1,0] , "I.A" ) @@ -146,7 +146,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( between ,[1,0] , "I" ) @@ -158,7 +158,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( preamble ,[1,1] , "I.B" ) @@ -173,7 +173,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( between ,[1] , "" ) diff --git a/tester/authored/Counter/test_2.html b/tester/authored/Counter/test_2.html index 8056556..7b6b3db 100644 --- a/tester/authored/Counter/test_2.html +++ b/tester/authored/Counter/test_2.html @@ -33,7 +33,7 @@
    snapshot() -> ( - , + , [] , "" ) @@ -45,7 +45,7 @@
    snapshot() == ( - , + , [] , "" ) @@ -58,7 +58,7 @@
    snapshot() == ( - , + , [] , "" ) @@ -71,7 +71,7 @@
    snapshot() == ( - , + , [] , "" ) @@ -84,7 +84,7 @@
    snapshot() == ( - , + , [] , "" ) @@ -97,7 +97,7 @@
    snapshot() == ( - , + , [] , "" ) @@ -110,7 +110,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( between ,[0] , "I" ) @@ -122,7 +122,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( preamble ,[1] , "II" ) @@ -134,7 +134,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( preamble ,[1,0] , "II.A" ) @@ -146,7 +146,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( between ,[1,0] , "II.A" ) @@ -158,7 +158,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( preamble ,[1,1] , "II.B" ) @@ -173,7 +173,7 @@
    snapshot() == ( - , + , [] , "" ) // Expected: ( between ,[1] , "II" ) diff --git a/tester/authored/Endnote/RT-Manuscript_locator.js b/tester/authored/Endnote/RT-Manuscript_locator.js new file mode 100644 index 0000000..4a1b936 --- /dev/null +++ b/tester/authored/Endnote/RT-Manuscript_locator.js @@ -0,0 +1,37 @@ +/* + direct.js + + + We have four scenarios + + immediate - used in the RT-style distribution itself (authored, consummer, staged) + direct - used in the RT-style project itself, but not in the distribution + indirect - the version all Harmony projects use + URL_only - always pulls style through a URL, a webserver must be present + +*/ + +window.RT = window.RT || {}; + +(function() { + const project_name = "RT-Style"; + const path = window.location.pathname; + const project_root_index = path.indexOf('/' + project_name + '/'); + + if (project_root_index !== -1) { + // substring(0, x) excludes the trailing slash. We must prepend it to the payload. + const absolute_project_root = path.substring(0, project_root_index + project_name.length + 1); + window.RT.dirpr_library = absolute_project_root + "/consumer/Manuscript"; + } else { + // Fallback for when served via local Python HTTP daemon from the project root + window.RT.dirpr_library = "../consumer/made/Manuscript"; + } + + document.write( + ' + + + + + + + + + +

    + This document verifies the behavior of the refactored endnote module. The endnote system now relies on the underlying counter infrastructure. We can test this by creating an endnote here This is the first reference text, detailing a specific technical point. and then inspecting the generated snapshots. +

    + +
    +
    // Diagnostic for Endnote 1
    +
    // The endnote script should have created snapshot: 'endnote_cite_1'
    +
    + snapshot("endnote_cite_1") == ( + , + [] , + "" + ) +
    +
    // Expected: ( preamble, [1], "1" )
    +
    + +

    + When a second endnote is encountered This is the second reference text, perhaps a book citation., the counter should increment accordingly, maintaining a distinct state for the new snapshot. +

    + +
    +
    // Diagnostic for Endnote 2
    +
    // The endnote script should have created snapshot: 'endnote_cite_2'
    +
    + snapshot("endnote_cite_2") == ( + , + [] , + "" + ) +
    +
    // Expected: ( preamble, [2], "2" )
    +
    + +

    + The endnote module will automatically generate the endnotes section at the bottom of the article, compiling these references and their bidirectional links using the exact same counter snapshot data read above. +

    + +
    + + diff --git a/tester/authored/Note/RT-Manuscript_locator.js b/tester/authored/Note/RT-Manuscript_locator.js new file mode 100644 index 0000000..4a1b936 --- /dev/null +++ b/tester/authored/Note/RT-Manuscript_locator.js @@ -0,0 +1,37 @@ +/* + direct.js + + + We have four scenarios + + immediate - used in the RT-style distribution itself (authored, consummer, staged) + direct - used in the RT-style project itself, but not in the distribution + indirect - the version all Harmony projects use + URL_only - always pulls style through a URL, a webserver must be present + +*/ + +window.RT = window.RT || {}; + +(function() { + const project_name = "RT-Style"; + const path = window.location.pathname; + const project_root_index = path.indexOf('/' + project_name + '/'); + + if (project_root_index !== -1) { + // substring(0, x) excludes the trailing slash. We must prepend it to the payload. + const absolute_project_root = path.substring(0, project_root_index + project_name.length + 1); + window.RT.dirpr_library = absolute_project_root + "/consumer/Manuscript"; + } else { + // Fallback for when served via local Python HTTP daemon from the project root + window.RT.dirpr_library = "../consumer/made/Manuscript"; + } + + document.write( + ' + + + + + + + + + +

    + This document verifies the behavior of the new cross reference module. We establish a note here Target Alpha Data and then verify the read mechanism. +

    + +
    +
    // Diagnostic 1: Default Content Read
    +
    // The script should render the inner HTML of the target.
    +
    + Read default: +
    +
    // Expected: Target Alpha Data
    +
    + +

    + We can verify the explicit field attribute. +

    + +
    +
    // Diagnostic 2: Explicit Content Read
    +
    + Read explicit: +
    +
    // Expected: Target Alpha Data
    +
    + +

    + To verify page numbering metadata, we insert a manual page break tag to force a new layout container. +

    + + + +

    + Now on a new layout boundary, we establish a second note Target Beta Data. +

    + +
    +
    // Diagnostic 3: Page Number Read
    +
    // The script must traverse the DOM, locate the page snapshot, and read the counter.
    +
    + Target Alpha Page: +
    +
    // Expected: 1
    +
    + Target Beta Page: +
    +
    // Expected: 2
    +
    + +

    + Finally we verify error handling for incorrect parameters. +

    + +
    +
    // Diagnostic 4: Error Handling
    +
    + Unknown Note: +
    +
    // Expected: [Unknown note: target_gamma]
    +
    + Invalid Field: +
    +
    // Expected: [Invalid field: bogus_field]
    +
    + +
    + + +