-// Core/loader.js
+/*
+ Core/RT-Manuscript_make.js
-window.RT = window.RT || {};
-window.RT.Module = window.RT.Module || new Set();
-
-// 2. Establish the Debug System
-window.RT.Debug = {
- active_tokens: new Set([
- 'scroll'
- ]),
-
- log: function(token, message) {
- if (this.active_tokens.has(token)) {
- console.log(`[RT:${token}]`, message);
- }
- },
-
- warn: function(token, message) {
- if (this.active_tokens.has(token)) {
- console.warn(`[RT:${token}]`, message);
- }
- },
-
- error: function(token, message) {
- console.error(`[RT:${token}] CRITICAL:`, message);
- },
-
- enable: function(token) { this.active_tokens.add(token); console.log(`Enabled: ${token}`); },
- disable: function(token) { this.active_tokens.delete(token); console.log(`Disabled: ${token}`); }
-};
-
-// 3. Establish the Utilities
-window.RT.Utility = {
-
- String: {
- to_roman: function(num) {
- if (num < 1) return num.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 (num >= lookup[i]) {
- roman += i;
- num -= lookup[i];
- }
- }
- return roman;
- },
-
- strip_common_indent: function(text, tag_indent = '') {
- const raw_lines = text.split('\n');
- const content_lines = raw_lines.filter(line => line.trim().length > 0);
- let common_indent = '';
-
- if (content_lines.length > 0) {
- const first_match = content_lines[0].match(/^\s*/);
- common_indent = first_match ? first_match[0] : '';
-
- for (let i = 1; i < content_lines.length; i++) {
- const line = content_lines[i];
- let j = 0;
- while (j < common_indent.length && j < line.length && common_indent[j] === line[j]) {
- j++;
- }
- common_indent = common_indent.substring(0, j);
- if (common_indent.length === 0) break;
- }
- }
-
- let final_string = '';
- if (common_indent.length > 0 && common_indent.startsWith(tag_indent)) {
- const cleaned_lines = raw_lines.map(line => {
- return line.startsWith(common_indent) ? line.replace(common_indent, '') : line;
- });
-
- if (cleaned_lines.length > 0 && cleaned_lines[0].length === 0) {
- cleaned_lines.shift();
- }
- if (cleaned_lines.length > 0 && cleaned_lines[cleaned_lines.length - 1].trim().length === 0) {
- cleaned_lines.pop();
- }
- final_string = cleaned_lines.join('\n');
- } else {
- final_string = text.trim();
- }
-
- return final_string;
- }
- },
-
- Dom: {
- measure_outer_height: function(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);
- },
-
- is_block_content: function(element) {
- return element.textContent.trim().includes('\n');
- }
- },
+ Bootstrap. Establishes the module registry and the loader ,then requests the
+ rest of the system.
- Font: {
- measure_ink_ratio: function(target_font, ref_font = null) {
- const debug = window.RT.Debug;
- debug.log('layout', `Measuring ink ratio for ${target_font}`);
+ RT.load emits a script tag with document.write ,so a requested file does not
+ run until the current file has finished. Everything here must therefore be
+ self contained: a service this file needs cannot be one this file loads.
+ Shared services accordingly live in Core/utility ,which is requested first so
+ that every file after it may rely on RT.Debug existing.
- const canvas = document.createElement('canvas');
- const ctx = canvas.getContext('2d');
+ RT.load works only while the document is parsing. Called after load it writes
+ into a closed stream and destroys the document.
+*/
- if (!ref_font) {
- const bodyStyle = window.getComputedStyle(document.body);
- ref_font = bodyStyle.fontFamily;
- }
-
- const get_metrics = (font) => {
- ctx.font = '100px ' + font;
- const metrics = ctx.measureText('M');
- return {
- ascent: metrics.actualBoundingBoxAscent,
- descent: metrics.actualBoundingBoxDescent
- };
- };
-
- const ref_m = get_metrics(ref_font);
- const target_m = get_metrics(target_font);
-
- const ratio = ref_m.ascent / target_m.ascent;
-
- return {
- ratio: ratio,
- baseline_diff: ref_m.descent - target_m.descent
- };
- }
- },
-
- Color: {
- extract_l: function(color_string) {
- const str = String(color_string).trim();
-
- if (!str.startsWith('oklch')) {
- console.error(`[RT:color] Invalid format: Expected oklch color string, received '${str}'`);
- return 0;
- }
-
- const match = str.match(/oklch\(\s*([\d.]+%?)/);
- if (!match) {
- console.error(`[RT:color] Parsing error: Could not extract lightness from '${str}'`);
- return 0;
- }
-
- const l_value = match[1];
- return l_value.includes('%') ? parseFloat(l_value) / 100 : parseFloat(l_value);
- },
-
- is_high_contrast: function(bg_color, text_color) {
- const bg_l = this.extract_l(bg_color);
- const text_l = this.extract_l(text_color);
- return Math.abs(text_l - bg_l) >= 0.7;
- },
-
- is_readable: function(bg_color, text_color) {
- const bg_l = this.extract_l(bg_color);
- const text_l = this.extract_l(text_color);
- return Math.abs(text_l - bg_l) >= 0.5;
- },
-
- is_light: function(color_string) {
- return this.extract_l(color_string) > 0.75;
- },
-
- is_gray: function(color_string) {
- const l = this.extract_l(color_string);
- return l >= 0.25 && l <= 0.75;
- },
-
- is_dark: function(color_string) {
- return this.extract_l(color_string) < 0.25;
- }
- }
-
-};
+window.RT = window.RT || {};
+window.RT.Module = window.RT.Module || new Set();
window.RT.load = function(module_path){
const key = module_path.endsWith('.js') ? module_path : module_path + '.js';
document.write('<script src="' + window.RT.dirpr_library + '/' + key + '"></script>');
};
+// Shared services first ,so RT.Debug exists for everything below.
+window.RT.load('Core/utility');
+
window.RT.load('Core/stage_manager');
window.RT.load('Core/theme_make');
-window.RT.load('Theme/manifest.js')
+window.RT.load('Theme/manifest');
window.RT.load('Layout/counter');
window.RT.load('Layout/note');
/*
Core/stage_manager.js
- Orchestrates the execution pipeline to resolve layout dependencies
- and handles scroll restoration.
+
+ Establishes the element registry and the schedule ,then orchestrates the
+ execution pipeline. Also handles layout locking and scroll restoration.
+
+ Two structures ,two purposes:
+
+ RT.Element a dictionary of element namespaces ,keyed by element name.
+ Pure data. Presence means an element has plugged in. Helper
+ functions do not live here ,or an element named for a helper
+ would collide with it.
+
+ RT.Phase an ordered list of phase names ,the schedule ,stated in one
+ place rather than inferred from dictionary key order.
+ RT.Task phase name -> ordered list of functions.
+
+ Tasks within a phase are intended to be mutually independent; all real
+ ordering is expressed by the phases. The structure does not enforce this ,so
+ enable the 'shuffle' debug token to randomize task order and surface any
+ accidental dependency immediately.
+
+ RT.Debug is looked up at call time ,never captured into a local here. RT.load
+ is deferred ,so a capture in this file body could bind a service that does not
+ yet exist.
*/
(function(){
return;
}
- // Inject Utilities prior to execution
- window.RT.load('Core/utility');
-
// Prevent duplicate initialization
- if(window.RT.Element instanceof Set){
- console.warn("RT stage_manager already initialized. Aborting duplicate run.");
+ if(window.RT.Element){
+ if(window.RT.Debug) window.RT.Debug.warn('stage' ,'stage_manager already initialized. Aborting duplicate run.');
return;
}
-
- /* Phase task queues/functions in order the phases are processed.
- Generators and element styling must run before pagination. Even styling changes the size of the document.
+ // Element namespaces. An element creates its own key ,in its own file body ,
+ // and nothing else may create it. That invariant is what allows presence to
+ // serve as the load guard.
+ window.RT.Element = {};
+
+ // Cross element tables that belong to no single element.
+ window.RT.Registry = {};
- Page styling can only happen after the pages are added. Pages are
- element pairs, the content is what is on the page.
+ /* The schedule.
- 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.)
+ configure compile the layout configuration from the selected theme.
+ Separated from the element phase so later tasks may read it
+ without an implicit ordering assumption.
- The paginate_0 breaks the document into <page> ... </page> 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.
+ element expand generators ,style elements. Changes document height ,
+ so it must precede pagination.
- 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.
+ paginate_0 slice the continuous DOM into <RT·page> pairs.
- Pages have page numbers, which are counters. So counters come after pagination.
+ page_style apply geometry to the generated pages.
- Cross reference targets can have counters in them, so they are handled after counters.
+ counter walk for counters ,then resolve read tags. After pagination ,
+ because a page number is itself a counter.
- 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 required.
+ note resolve cross references. After counters ,because a reference
+ target may contain a counter value.
+
+ paginate_1 absorb dimensional deltas by growing pages. Last ,and it only
+ ever grows: relocating content would change page numbers ,
+ which would change cross reference lengths ,which would
+ relocate more content. Growth is local and terminal.
+ */
+ window.RT.Phase = [
+ 'configure'
+ ,'element'
+ ,'paginate_0'
+ ,'page_style'
+ ,'counter'
+ ,'note'
+ ,'paginate_1'
+ ];
+
+ window.RT.Task = {};
+ window.RT.Phase.forEach(phase_name => { window.RT.Task[phase_name] = []; });
+
+ /* Register a task against a phase.
+
+ The phase name is validated. Without the check a misspelling either throws
+ or ,worse ,silently creates a queue nothing runs; the element would then do
+ nothing and report nothing.
+
+ Task lists are lists ,not sets. The module guard and the namespace guard
+ already prevent a file registering twice ,and a task may legitimately be
+ queued more than once when that is genuinely wanted.
*/
- window.RT.Element = new Set(); // expand generators, style elements
- window.RT.paginate_0 = null; // add the <page> ... </page> 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(){} };
-
+ window.RT.task_add = function(phase_name ,task_fn){
+ if(!window.RT.Task[phase_name]){
+ window.RT.Debug.error('stage' ,'unknown phase: ' + phase_name);
+ return;
+ }
+ if(typeof task_fn !== 'function'){
+ window.RT.Debug.error('stage' ,'task is not a function ,phase: ' + phase_name);
+ return;
+ }
+ window.RT.Task[phase_name].push(task_fn);
+ };
+
let target_y = 0;
let is_reload = false;
let is_layout_locked = false;
}
// =========================================================
- // MASTER PIPELINE EXECUTION
+ // PIPELINE EXECUTION
// =========================================================
- 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);
- }
- }
- }
-
- // 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.');
+ function shuffled(task_seq){
+ const out = task_seq.slice();
+ for(let i = out.length - 1; i > 0; i--){
+ const j = Math.floor(Math.random() * (i + 1));
+ [out[i] ,out[j]] = [out[j] ,out[i]];
}
+ return out;
+ }
- // 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); }
- }
- }
- }
+ function run_phase(phase_name){
+ const debug = window.RT.Debug;
+ let task_seq = window.RT.Task[phase_name];
- // 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); }
+ if(debug.active_tokens.has('shuffle')){
+ task_seq = shuffled(task_seq);
+ debug.log('stage' ,'phase ' + phase_name + ': task order shuffled');
}
- // 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); }
- }
+ task_seq.forEach(task_fn => {
+ try{ task_fn(); }
+ catch(e){ debug.error('stage' ,phase_name + ' task 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); }
- }
+ function resolve_scroll_target(){
+ window.RT.Debug.log('scroll' ,'Pipeline execution complete. Enforcing scroll target.');
- // Final Step: Resolve 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){
- const hash_target = document.getElementById(window.location.hash.substring(1));
- if(hash_target) use_hash = true;
+ const hash_target = document.getElementById(window.location.hash.substring(1));
+ if(hash_target) use_hash = true;
}
- enforce_scroll(final_target ,use_hash ,0);
+ enforce_scroll(target_y ,use_hash ,0);
+ }
+
+ function run_pipeline(){
+ window.RT.Phase.forEach(phase_name => {
+ window.RT.Debug.log('stage' ,'phase: ' + phase_name);
+ run_phase(phase_name);
+ });
+ resolve_scroll_target();
}
// =========================================================
// INITIALIZATION
// =========================================================
-
+
lock_layout();
configure_history();
capture_scroll_target();
bind_window_events();
-
+
document.addEventListener('DOMContentLoaded' ,run_pipeline);
- // Safety Net: restore visibility on load if the async layout engine hangs
+ // Safety net: restore visibility on load if the layout engine hangs
window.addEventListener("load" ,unlock_layout);
-
+
})();
/*
Core/utility.js
- Centralized utility functions for global registry queries and repetitive DOM operations.
+ Shared services: token filtered debug logging ,string ,DOM ,font ,and colour
+ helpers ,registry management ,and structural queries.
+
+ Loaded first by Core/RT-Manuscript_make.js ,ahead of the stage manager ,so that
+ every later file may rely on RT.Debug existing. Nothing here depends on the
+ document ,so it is safe to establish at parse time.
+
+ Note: consumers must look RT.Debug up at call time rather than capturing it
+ into a local in a file body. RT.load is deferred ,so a file body may run
+ before the service it wants exists.
*/
-(function(){
+window.RT = window.RT || {};
+
+window.RT.Debug = {
+ active_tokens: new Set([
+ 'scroll'
+ ]),
+
+ log: function(token, message) {
+ if (this.active_tokens.has(token)) {
+ console.log(`[RT:${token}]`, message);
+ }
+ },
+
+ warn: function(token, message) {
+ if (this.active_tokens.has(token)) {
+ console.warn(`[RT:${token}]`, message);
+ }
+ },
+
+ error: function(token, message) {
+ console.error(`[RT:${token}] CRITICAL:`, message);
+ },
+
+ enable: function(token) { this.active_tokens.add(token); console.log(`Enabled: ${token}`); },
+ disable: function(token) { this.active_tokens.delete(token); console.log(`Disabled: ${token}`); }
+};
+
+window.RT.Utility = {
+
+ String: {
+ to_roman: function(num) {
+ if (num < 1) return num.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 (num >= lookup[i]) {
+ roman += i;
+ num -= lookup[i];
+ }
+ }
+ return roman;
+ },
+
+ strip_common_indent: function(text, tag_indent = '') {
+ const raw_lines = text.split('\n');
+ const content_lines = raw_lines.filter(line => line.trim().length > 0);
+ let common_indent = '';
+
+ if (content_lines.length > 0) {
+ const first_match = content_lines[0].match(/^\s*/);
+ common_indent = first_match ? first_match[0] : '';
+
+ for (let i = 1; i < content_lines.length; i++) {
+ const line = content_lines[i];
+ let j = 0;
+ while (j < common_indent.length && j < line.length && common_indent[j] === line[j]) {
+ j++;
+ }
+ common_indent = common_indent.substring(0, j);
+ if (common_indent.length === 0) break;
+ }
+ }
+
+ let final_string = '';
+ if (common_indent.length > 0 && common_indent.startsWith(tag_indent)) {
+ const cleaned_lines = raw_lines.map(line => {
+ return line.startsWith(common_indent) ? line.replace(common_indent, '') : line;
+ });
+
+ if (cleaned_lines.length > 0 && cleaned_lines[0].length === 0) {
+ cleaned_lines.shift();
+ }
+ if (cleaned_lines.length > 0 && cleaned_lines[cleaned_lines.length - 1].trim().length === 0) {
+ cleaned_lines.pop();
+ }
+ final_string = cleaned_lines.join('\n');
+ } else {
+ final_string = text.trim();
+ }
+
+ return final_string;
+ }
+ },
+
+ Dom: {
+ measure_outer_height: function(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);
+ },
+
+ is_block_content: function(element) {
+ return element.textContent.trim().includes('\n');
+ }
+ },
- window.RT = window.RT || {};
- window.RT.Utility = window.RT.Utility || {};
+ Font: {
+ measure_ink_ratio: function(target_font, ref_font = null) {
+ const debug = window.RT.Debug;
+ debug.log('layout', `Measuring ink ratio for ${target_font}`);
+
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+
+ if (!ref_font) {
+ const bodyStyle = window.getComputedStyle(document.body);
+ ref_font = bodyStyle.fontFamily;
+ }
+
+ const get_metrics = (font) => {
+ ctx.font = '100px ' + font;
+ const metrics = ctx.measureText('M');
+ return {
+ ascent: metrics.actualBoundingBoxAscent,
+ descent: metrics.actualBoundingBoxDescent
+ };
+ };
+
+ const ref_m = get_metrics(ref_font);
+ const target_m = get_metrics(target_font);
+
+ const ratio = ref_m.ascent / target_m.ascent;
+
+ return {
+ ratio: ratio,
+ baseline_diff: ref_m.descent - target_m.descent
+ };
+ }
+ },
+
+ Color: {
+ extract_l: function(color_string) {
+ const str = String(color_string).trim();
+
+ if (!str.startsWith('oklch')) {
+ console.error(`[RT:color] Invalid format: Expected oklch color string, received '${str}'`);
+ return 0;
+ }
+
+ const match = str.match(/oklch\(\s*([\d.]+%?)/);
+ if (!match) {
+ console.error(`[RT:color] Parsing error: Could not extract lightness from '${str}'`);
+ return 0;
+ }
+
+ const l_value = match[1];
+ return l_value.includes('%') ? parseFloat(l_value) / 100 : parseFloat(l_value);
+ },
+
+ is_high_contrast: function(bg_color, text_color) {
+ const bg_l = this.extract_l(bg_color);
+ const text_l = this.extract_l(text_color);
+ return Math.abs(text_l - bg_l) >= 0.7;
+ },
+
+ is_readable: function(bg_color, text_color) {
+ const bg_l = this.extract_l(bg_color);
+ const text_l = this.extract_l(text_color);
+ return Math.abs(text_l - bg_l) >= 0.5;
+ },
+
+ is_light: function(color_string) {
+ return this.extract_l(color_string) > 0.75;
+ },
+
+ is_gray: function(color_string) {
+ const l = this.extract_l(color_string);
+ return l >= 0.25 && l <= 0.75;
+ },
+
+ is_dark: function(color_string) {
+ return this.extract_l(color_string) < 0.25;
+ }
+ }
+
+};
+
+(function(){
// Registry Management
window.RT.Utility.Registry = {
if(!window.RT) return;
+ if(RT.Element.TOC) return; // already plugged in
+ const ns = RT.Element.TOC = {};
+
const apply_style = function(a ,config){
a.style.textDecoration = 'none';
a.style.color = 'inherit';
a.onmouseout = () => a.style.color = 'inherit';
};
- RT.Element.add(function(){
+ RT.task_add('element' ,function(){
const debug = window.RT.Debug || { log: function(){} };
if(debug.log) debug.log('TOC' ,'Generating Table of Contents from expanded section steps');
const config = window.RT.layout_config || {};
- const TOC_seq = document.querySelectorAll('RT·TOC, rt·toc');
+ const TOC_seq = document.querySelectorAll('RT·TOC');
TOC_seq.forEach((container ,TOC_index) => {
container.style.display = 'block';
if(!window.RT) return;
+ if(RT.Element.Code) return; // already plugged in
+ const ns = RT.Element.Code = {};
+
const apply_style = function(el ,is_block ,exact_px ,offset_px ,text_color ,overlay_color ,config){
el.style.fontFamily = "'Courier New', Courier, monospace";
el.style.backgroundColor = overlay_color;
}
};
- RT.Element.add(function(){
+ RT.task_add('element' ,function(){
const U = window.RT.Utility;
const config = window.RT.layout_config || {};
const metrics = U.Font.measure_ink_ratio('monospace');
- const nodes = document.querySelectorAll('rt·code, RT·code');
+ const nodes = document.querySelectorAll('rt·code');
for(let i = 0; i < nodes.length; i++){
const el = nodes[i];
if(!window.RT) return;
+ if(RT.Element.Endnote) return; // already plugged in
+ const ns = RT.Element.Endnote = {};
+
const apply_style = function(link, config) {
link.style.cursor = 'pointer';
link.style.color = config.brand_link || '#0056b3';
function process_endnotes(){
const config = window.RT.layout_config || {};
- const article = document.querySelector('RT·article, rt·article, RT·memo, rt·memo');
+ const article = document.querySelector('RT·article, RT·memo');
if(!article) return;
initial_make.setAttribute('on-first-step', '0');
article.insertBefore(initial_make, article.firstChild);
- const nodes = document.querySelectorAll('RT·endnote, rt·endnote, RT-endnote, rt-endnote, RT·endnotes, rt·endnotes, RT-endnotes, rt-endnotes');
+ const nodes = document.querySelectorAll('RT·endnote, RT-endnote, RT·endnotes, RT-endnotes');
let endnote_buffer = [];
let anchor_id_counter = 1;
}
}
- window.RT.Element.add(process_endnotes);
+ RT.task_add('element' ,process_endnotes);
})();
*/
(function(){
if(!window.RT) return;
+
+ if(RT.Element.Footnote) return; // already plugged in
+ const ns = RT.Element.Footnote = {};
const apply_style = function(el, config) {};
// Footnote processing is structurally evaluated by the paginator.
// This closure exists strictly to validate the semantic footprint.
- RT.Element.add(function() {
+ RT.task_add('element' ,function() {
const config = window.RT.layout_config || {};
- document.querySelectorAll('rt·footnote, RT·footnote').forEach(el => apply_style(el, config));
+ document.querySelectorAll('rt·footnote').forEach(el => apply_style(el, config));
});
})();
(function() {
if (!window.RT) return;
+ if(RT.Element.Grid) return; // already plugged in
+ const ns = RT.Element.Grid = {};
+
const debug = window.RT.Debug || { log: function(){}, warn: function(){}, error: function(){} };
class GridState {
return { start, extent };
}
- RT.Element.add(function process_grids() {
+ RT.task_add('element' ,function process_grids() {
if(debug.log) debug.log('grid', 'Processing grid structures');
// 1. Native Grid
console.error("RT not defined. Was RT Manuscript make run?");
return;
}
+
+ if(RT.Element.Math) return; // already plugged in
+ const ns = RT.Element.Math = {};
if(!window.RT.Element){
console.error("RT.Element not defined. Was the stage manager run?");
return;
const debug = window.RT.Debug || { log: function(){} };
if(debug.log) debug.log('math' ,'Processing math tags directly');
- const math_elements = Array.from(document.querySelectorAll('RT·math, rt·math'));
+ const math_elements = Array.from(document.querySelectorAll('RT·math'));
if(math_elements.length === 0) return;
};
RT.load('Math/mathjax_svg');
- RT.Element.add(scan_tags);
+ RT.task_add('element' ,scan_tags);
})();
/*
Element/section.js
Expands <RT·section> macros into <RT·counter·step> primitives.
- Utilizes the global RT.Section namespace for state tracking and execution guards.
+ Utilizes the RT.Element.Section namespace for state tracking and execution guards.
*/
(function(){
if(!window.RT) return;
- window.RT.Section = window.RT.Section || {};
-
- // Guard against multiple script inclusions
- if(window.RT.Section.is_loaded) return;
- window.RT.Section.is_loaded = true;
+ if(RT.Element.Section) return; // already plugged in
+ const ns = RT.Element.Section = {};
+
+ ns.tags = ['RT·section'];
const apply_style = function(title_node ,depth ,config){
const base_size = 2.25;
title_node.style.lineHeight = '1.2';
};
- RT.Element.add(function(){
+ RT.task_add('element' ,function(){
const debug = window.RT.Debug || { log: function(){} };
if(debug.log) debug.log('section' ,'Expanding section macros');
const U = window.RT.Utility;
const config = window.RT.layout_config || {};
- const section_seq = document.querySelectorAll('RT·section, rt·section');
+ const section_seq = document.querySelectorAll('RT·section');
if(section_seq.length === 0) return;
- const article = document.querySelector('RT·article, rt·article, RT·memo, rt·memo');
+ const article = document.querySelector('RT·article, RT·memo');
const counter_name = 'RT·Section·counter';
// Check the global dictionary for existence rather than traversing the DOM
- if(article && !U.Registry.has(window.RT.Section, counter_name)){
+ if(article && !U.Registry.has(ns, counter_name)){
const make = document.createElement('RT·counter·make');
make.setAttribute('counter' ,counter_name);
make.setAttribute('style' ,'CountingNumber');
article.insertBefore(make ,article.firstChild);
// Register the physical node and its attributes into the global namespace
- U.Registry.register_make(window.RT.Section, counter_name, make, ['splitable']);
+ U.Registry.register_make(ns, counter_name, make, ['splitable']);
}
let section_idx = 0;
step.setAttribute('counter' ,counter_name);
// Query the global dictionary for the splitable flag
- if(U.Registry.has(window.RT.Section[counter_name], 'splitable')) {
+ if(U.Registry.has(ns[counter_name], 'splitable')) {
step.setAttribute('splitable', 'true');
}
if(!window.RT) return;
+ if(RT.Element.Term) return; // already plugged in
+ const ns = RT.Element.Term = {};
+
const apply_style = function(el ,is_neologism ,is_first ,config){
if(is_first){
el.style.fontStyle = 'italic';
}
};
- RT.Element.add(function(){
+ RT.task_add('element' ,function(){
const config = window.RT.layout_config || {};
const seen_terms_dpa = new Set();
const selector_s = 'rt·term, rt·term-em, rt·neologism, rt·neologism-em';
console.error("RT not defined - was RT-Manuscript_make run?");
return;
}
+
+ if(RT.Element.ThemeSelector) return; // already plugged in
+ const ns = RT.Element.ThemeSelector = {};
if (!window.RT.Element) {
console.error("RT.Element not defined - was the state_manager run?");
return;
}
- RT.Element.add( function() {
+ RT.task_add('element' , function() {
const debug = window.RT.Debug || { log: function(){} };
if (debug.log) debug.log('theme_selector', 'Building theme selectors');
if(!window.RT) return;
+ if(RT.Element.Title) return; // already plugged in
+ const ns = RT.Element.Title = {};
+
const apply_style = function(container ,h1 ,meta ,copy_div ,config){
container.style.textAlign = 'center';
container.style.marginBottom = '3rem';
}
};
- RT.Element.add(function(){
+ RT.task_add('element' ,function(){
const config = window.RT.layout_config || {};
- const nodes = document.querySelectorAll('rt·title, RT·title');
+ const nodes = document.querySelectorAll('rt·title');
for(let i = 0; i < nodes.length; i++){
const el = nodes[i];
required_elements.forEach(name => RT.load('Element/' + name));
- if(RT.Element && RT.PageStyle){
- RT.Element.add(compile_configuration);
- RT.Element.add(apply_macro_boundaries);
- RT.PageStyle.add(apply_macro_boundaries);
- }
+ RT.task_add('configure' ,compile_configuration);
+ RT.task_add('element' ,apply_macro_boundaries);
+ RT.task_add('page_style' ,apply_macro_boundaries);
})();
return;
}
- RT.Counter = RT.Counter || {};
- RT.dict_instance = RT.dict_instance || {};
- RT.dict_snapshot = RT.dict_snapshot || {};
- RT.dict_serial = RT.dict_serial || {};
- RT.serial_id_allocator = RT.serial_id_allocator || 1;
+ if(RT.Element.Counter) return; // already plugged in
+ const ns = RT.Element.Counter = {};
+
+ ns.tags = ['RT·Counter·make' ,'RT·Counter·step' ,'RT·Counter·snapshot' ,'RT·Counter·read'];
+
+ ns.dict_instance = {}; // counter name -> live machine
+ ns.dict_snapshot = {}; // snapshot name -> cloned machine
+ ns.dict_serial = {}; // serial / split id -> cloned machine (suspension store)
+ ns.serial_id_allocator = 1;
class Count{
constructor(){
if(name){
const continues_id = node.getAttribute('continues');
- if(continues_id && RT.dict_serial[continues_id]){
- RT.dict_instance[name] = RT.dict_serial[continues_id].clone();
+ if(continues_id && ns.dict_serial[continues_id]){
+ ns.dict_instance[name] = ns.dict_serial[continues_id].clone();
}else{
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({
+ ns.dict_instance[name] = new CounterMachine({
style: parsed_style
,separator: node.getAttribute('separator') || '.'
,separator_placement: node.getAttribute('separator-placement') || 'embedded'
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 top_style = ns.dict_instance[name].style[0];
const method_name = `from_${top_style}`;
- const active_machine = RT.dict_instance[name];
+ const active_machine = ns.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);
}
}
- const serial = node.getAttribute('serial') || String(RT.serial_id_allocator++);
+ const serial = node.getAttribute('serial') || String(ns.serial_id_allocator++);
node.setAttribute('serial' ,serial);
- RT.dict_serial[serial] = RT.dict_instance[name];
+ ns.dict_serial[serial] = ns.dict_instance[name];
}
}else if(tag === 'rt·counter·step'){
const name = node.getAttribute('counter');
const is_continuation = node.getAttribute('continuation') === 'true';
- if(name && RT.dict_instance[name]){
- const active_machine = RT.dict_instance[name];
+ if(name && ns.dict_instance[name]){
+ const active_machine = ns.dict_instance[name];
if(!is_continuation){
active_machine.enter(active_machine.first_step_val);
active_machine.first_step_val = undefined;
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(counter_name && snapshot_name && ns.dict_instance[counter_name]){
+ const active_machine = ns.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();
+ ns.dict_snapshot[snapshot_name] = active_machine.clone();
}
}
}
}else{
const split_id = node.getAttribute('split-id');
if(split_id){
- RT.dict_serial[split_id] = machine_to_exit.clone();
+ ns.dict_serial[split_id] = machine_to_exit.clone();
}
}
}
walk(root_node);
- const reads = root_node.querySelectorAll('RT·counter·read, rt·counter·read');
+ const reads = root_node.querySelectorAll('RT·counter·read');
for(let i = 0; i < reads.length; i++){
process_read_node(reads[i]);
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(snapshot_name && ns.dict_snapshot[snapshot_name]){
+ const snapshot_machine = ns.dict_snapshot[snapshot_name];
if(key === 'count'){
const raw_state = snapshot_machine.read('count');
}
};
- window.RT.counter = counter;
+ RT.task_add('counter' ,counter);
})();
const ref_dictionary = {};
// Pass 1: Gather writes
- const writes = root_node.querySelectorAll('RT·Note·write, rt·note·write');
+ const writes = root_node.querySelectorAll('RT·Note·write');
for(let i = 0; i < writes.length; i++){
const node = writes[i];
const key = node.getAttribute('key');
}
// Pass 2: Resolve reads
- const reads = root_node.querySelectorAll('RT·Note·read, rt·note·read');
+ const reads = root_node.querySelectorAll('RT·Note·read');
for(let i = 0; i < reads.length; i++){
const node = reads[i];
const key = node.getAttribute('key');
// Registration upon load
//
- window.RT.note = note;
+ RT.task_add('note' ,note);
})();
function get_measure_container(){
if(measure_container && measure_container.parentNode) return measure_container;
- const article = document.querySelector('RT·article, rt·article');
+ const article = document.querySelector('RT·article');
if(!article){
const temp = document.createElement('div');
temp.style.visibility = 'hidden';
function paginate_0(){
if(debug.log) debug.log('paginate_0' ,'Running initial document chunking');
- const article_seq = document.querySelectorAll('RT·article, rt·article, RT·memo, rt·memo');
+ const article_seq = document.querySelectorAll('RT·article, RT·memo');
if(article_seq.length === 0){
debug.error('pagination' ,'No <RT·article> elements found. Pagination aborted.');
return;
Array.from(article_seq).forEach(article => paginate_article(article));
Array.from(article_seq).forEach(article => {
- const rendered_pages = article.querySelectorAll('RT·page, rt·page');
+ const rendered_pages = article.querySelectorAll('RT·page');
Array.from(rendered_pages).forEach(page => {
const all_page_nodes = Array.from(page.querySelectorAll('*'));
function paginate_1(){
if(debug.log) debug.log('paginate_1' ,'Adjusting final page heights after component injections');
- const rendered_pages = document.querySelectorAll('RT·page, rt·page');
+ const rendered_pages = document.querySelectorAll('RT·page');
Array.from(rendered_pages).forEach(page => {
const actual_height = page.scrollHeight;
if(actual_height > page_height_limit){
});
}
- window.RT.paginate_0 = paginate_0;
- window.RT.paginate_1 = paginate_1;
+ RT.task_add('paginate_0' ,paginate_0);
+ RT.task_add('paginate_1' ,paginate_1);
})();