From e47a6a39d9c7f4d817e17119200e9269f13576b0 Mon Sep 17 00:00:00 2001 From: Enrico Spinielli Date: Sun, 1 Dec 2024 14:12:56 +0100 Subject: [PATCH] introduced acronyms management --- _acronyms.yml | 108 +++++++ _extensions/leovan/pseudocode/_extension.yml | 7 + _extensions/leovan/pseudocode/pseudocode.lua | 289 ++++++++++++++++++ .../leovan/pseudocode/pseudocode.min.css | 1 + .../leovan/pseudocode/pseudocode.min.js | 1 + _extensions/rchaput/acronyms/_extension.yml | 9 + _extensions/rchaput/acronyms/acronyms.lua | 236 ++++++++++++++ .../rchaput/acronyms/acronyms_helpers.lua | 102 +++++++ .../rchaput/acronyms/acronyms_options.lua | 146 +++++++++ .../rchaput/acronyms/acronyms_pandoc.lua | 250 +++++++++++++++ .../rchaput/acronyms/acronyms_styles.lua | 134 ++++++++ .../acronyms/acronyms_translations.lua | 75 +++++ .../rchaput/acronyms/parse-acronyms.lua | 152 +++++++++ .../rchaput/acronyms/shortcodes_acronyms.lua | 144 +++++++++ .../rchaput/acronyms/sort_acronyms.lua | 87 ++++++ _quarto.yml | 10 + chapters/background/aerodrome.qmd | 12 +- chapters/background/aircraft.qmd | 22 +- chapters/background/airspace.qmd | 6 +- chapters/background/atmosphere.qmd | 12 +- chapters/background/cns.qmd | 7 +- chapters/background/earth.qmd | 42 +-- chapters/background/history.qmd | 9 +- chapters/index.qmd | 2 + index.qmd | 2 + preface.qmd | 16 +- 26 files changed, 1838 insertions(+), 43 deletions(-) create mode 100644 _acronyms.yml create mode 100644 _extensions/leovan/pseudocode/_extension.yml create mode 100644 _extensions/leovan/pseudocode/pseudocode.lua create mode 100644 _extensions/leovan/pseudocode/pseudocode.min.css create mode 100644 _extensions/leovan/pseudocode/pseudocode.min.js create mode 100644 _extensions/rchaput/acronyms/_extension.yml create mode 100644 _extensions/rchaput/acronyms/acronyms.lua create mode 100644 _extensions/rchaput/acronyms/acronyms_helpers.lua create mode 100644 _extensions/rchaput/acronyms/acronyms_options.lua create mode 100644 _extensions/rchaput/acronyms/acronyms_pandoc.lua create mode 100644 _extensions/rchaput/acronyms/acronyms_styles.lua create mode 100644 _extensions/rchaput/acronyms/acronyms_translations.lua create mode 100644 _extensions/rchaput/acronyms/parse-acronyms.lua create mode 100644 _extensions/rchaput/acronyms/shortcodes_acronyms.lua create mode 100644 _extensions/rchaput/acronyms/sort_acronyms.lua diff --git a/_acronyms.yml b/_acronyms.yml new file mode 100644 index 0000000..8cc7662 --- /dev/null +++ b/_acronyms.yml @@ -0,0 +1,108 @@ +--- +acronyms: + keys: + - shortname: ACAS + longname: Airborne Collision Avoidance System + - shortname: ACC + longname: Area Control Centre + - shortname: ADEP + longname: Aerodrome of DEParture + - shortname: ADES + longname: Aerodrome of DEStination + - shortname: ADS-B + longname: Automatic Dependent Surveillance–Broadcast + - shortname: AIP + longname: Aeronautical Information Publication + - shortname: AO + longname: Aircraft Operator + - shortname: AOBT + longname: Actual Off-Block Time + - shortname: ANS + longname: Air Navigation Service + - shortname: ANSP + longname: Air Navigation Service Provider + - shortname: ARVT + longname: ARriVal Time + - shortname: ATC + longname: Air Traffic Control + - shortname: ATOW + longname: Actual TakeOff Weight + - shortname: BADA + longname: Base of Aircraft Data + - shortname: BR + longname: Basic Regulation + - shortname: CPR + longname: Correlated Position Report + - shortname: CTFM + longname: Current Tactical Flight Model + - shortname: CTR + longname: Control Zone + - shortname: DDR2 + longname: Demand Data Repository version 2 + - shortname: EASA + longname: European Union Aviation Safety Agency + - shortname: ECEF + longname: Earth-Centered, Earth-Fixed + - shortname: ECTL + longname: EUROCONTROL + - shortname: EC + longname: European Commission + - shortname: ECMWF + longname: European Centre for Medium-Range Weather Forecasts + - shortname: ERA5 + longname: ECMWF's fifth generation of Atmospheric Reanalysis + - shortname: ETOW + longname: Estimated TakeOff Weight + - shortname: EU + longname: European Union + - shortname: FIR + longname: Flight Information Region + - shortname: FP + longname: Flight Plan + - shortname: FTFM + longname: Filed Tactical Flight Model + - shortname: GPS + longname: Global Positioning System + - shortname: ICAO + longname: International Civil Aviation Organization + - shortname: IFR + longname: Instrument Flight Rules + - shortname: IR + longname: Implementing Rule + - shortname: ISO + longname: International Organization for Standardization + - shortname: ML + longname: Machine Learning + - shortname: MTOW + longname: Maximum TakeOff Weight + - shortname: NAVAID + longname: NAVigational AID + - shortname: NED + longname: North-East-Down + - shortname: NM + longname: Network Manager + - shortname: OSN + longname: OpenSky Network + - shortname: PRC + longname: Performance Review Commission + - shortname: RA + longname: Resolution Advisory + - shortname: RAD + longname: Route Availability Document + - shortname: RMSE + longname: Root Mean Square Error + - shortname: TA + longname: Traffic Advisory + - shortname: TACAN + longname: TactiCal Air Navigation system + - shortname: TCAS + longname: Traffic alert and Collision Avoidance System + - shortname: TMA + longname: Terminal Manoeuvring Area + - shortname: TOW + longname: TakeOff Weight + - shortname: VOR + longname: Very High Frequency Omnidirectional Range Station + - shortname: WTC + longname: Wake Turbulence Category +--- diff --git a/_extensions/leovan/pseudocode/_extension.yml b/_extensions/leovan/pseudocode/_extension.yml new file mode 100644 index 0000000..df7b2c2 --- /dev/null +++ b/_extensions/leovan/pseudocode/_extension.yml @@ -0,0 +1,7 @@ +title: Pseudocode +author: 范叶亮 | Leo Van +version: 1.1.1 +quarto-required: ">=1.4.0" +contributes: + filters: + - pseudocode.lua diff --git a/_extensions/leovan/pseudocode/pseudocode.lua b/_extensions/leovan/pseudocode/pseudocode.lua new file mode 100644 index 0000000..73ad1be --- /dev/null +++ b/_extensions/leovan/pseudocode/pseudocode.lua @@ -0,0 +1,289 @@ +local function ensure_html_deps() + quarto.doc.add_html_dependency({ + name = "pseudocode", + version = "2.4.1", + scripts = { "pseudocode.min.js" }, + stylesheets = { "pseudocode.min.css" } + }) + quarto.doc.include_text("after-body", [[ + + ]]) +end + +local function ensure_latex_deps() + quarto.doc.use_latex_package("algorithm") + quarto.doc.use_latex_package("algpseudocode") + quarto.doc.use_latex_package("caption") +end + +local function extract_source_code_options(source_code, render_type) + local options = {} + local source_codes = {} + local found_source_code = false + + for str in string.gmatch(source_code, "([^\n]*)\n?") do + if (string.match(str, "^%s*#|.*") or string.gsub(str, "%s", "") == "") and not found_source_code then + if string.match(str, "^%s*#|%s+[" .. render_type .. "|label].*") then + str = string.gsub(str, "^%s*#|%s+", "") + local idx_start, idx_end = string.find(str, ":%s*") + + if idx_start and idx_end and idx_end + 1 < #str then + k = string.sub(str, 1, idx_start - 1) + v = string.sub(str, idx_end + 1) + v = string.gsub(v, "^\"%s*", "") + v = string.gsub(v, "%s*\"$", "") + + options[k] = v + else + quarto.log.warning("Invalid pseducode option: " .. str) + end + end + else + found_source_code = true + table.insert(source_codes, str) + end + end + + return options, table.concat(source_codes, "\n") +end + +local function render_pseudocode_block_html(global_options) + ensure_html_deps() + + local filter = { + CodeBlock = function(el) + if not el.attr.classes:includes("pseudocode") then + return el + end + + local options, source_code = extract_source_code_options(el.text, "html") + + source_code = string.gsub(source_code, "%s*\\begin{algorithm}[^\n]+", "\\begin{algorithm}") + source_code = string.gsub(source_code, "%s*\\begin{algorithmic}[^\n]+", "\\begin{algorithmic}") + + local alg_id = options["label"] + options["label"] = nil + options["html-caption-prefix"] = global_options.caption_prefix + + if global_options.number_with_in_chapter and global_options.html_chapter_level then + options["html-chapter-level"] = global_options.html_chapter_level + end + + if global_options.caption_number then + options["html-pseudocode-number"] = global_options.html_current_number + end + + local data_options = {} + for k, v in pairs(options) do + if string.match(k, "^html-") then + data_k = string.gsub(k, "^html", "data") + data_options[data_k] = v + end + end + + local inner_el = pandoc.Div(source_code) + inner_el.attr.classes = pandoc.List() + inner_el.attr.classes:insert("pseudocode") + + local outer_el = pandoc.Div(inner_el) + outer_el.attr.classes = pandoc.List() + outer_el.attr.classes:insert("pseudocode-container") + outer_el.attr.classes:insert("quarto-float") + outer_el.attr.attributes = data_options + + if alg_id then + outer_el.attr.identifier = alg_id + global_options.html_identifier_number_mapping[alg_id] = global_options.html_current_number + global_options.html_current_number = global_options.html_current_number + 1 + end + + return outer_el + end + } + + return filter +end + +local function render_pseudocode_block_latex(global_options) + ensure_latex_deps() + + if global_options.caption_number then + quarto.doc.include_text("before-body", "\\floatname{algorithm}{" .. global_options.caption_prefix .. "}") + else + quarto.doc.include_text("in-header", "\\DeclareCaptionLabelFormat{algnonumber}{" .. global_options.caption_prefix .. "}") + quarto.doc.include_text("before-body", "\\captionsetup[algorithm]{labelformat=algnonumber}") + end + + if global_options.number_with_in_chapter then + quarto.doc.include_text("before-body", "\\numberwithin{algorithm}{chapter}") + end + + local filter = { + CodeBlock = function(el) + if not el.attr.classes:includes("pseudocode") then + return el + end + + local options, source_code = extract_source_code_options(el.text, "pdf") + + local pdf_placement = "H" + if options["pdf-placement"] then + pdf_placement = options["pdf-placement"] + end + source_code = string.gsub(source_code, "\\begin{algorithm}%s*\n", "\\begin{algorithm}[" .. pdf_placement .. "]\n") + + if not options["pdf-line-number"] or options["pdf-line-number"] == "true" then + source_code = string.gsub(source_code, "\\begin{algorithmic}%s*\n", "\\begin{algorithmic}[1]\n") + end + + if options["label"] then + source_code = string.gsub(source_code, "\\begin{algorithmic}", "\\label{" .. options["label"] .. "}\n\\begin{algorithmic}") + end + + return pandoc.RawInline("latex", source_code) + end + } + + return filter +end + +local function render_pseudocode_block(global_options) + local filter = { + CodeBlock = function(el) + return el + end + } + + if quarto.doc.is_format("html") then + filter = render_pseudocode_block_html(global_options) + elseif quarto.doc.is_format("latex") then + filter = render_pseudocode_block_latex(global_options) + end + + return filter +end + +local function render_pseudocode_ref_html(global_options) + local filter = { + Cite = function(el) + local cite_text = pandoc.utils.stringify(el.content) + + for k, v in pairs(global_options.html_identifier_number_mapping) do + if cite_text == "@" .. k then + local link_src = "#" .. k + local alg_id = v + + if global_options.html_chapter_level then + alg_id = global_options.html_chapter_level .. "." .. alg_id + end + + local link_text = global_options.reference_prefix .. " " .. alg_id + local link = pandoc.Link(link_text, link_src) + link.attr.classes = pandoc.List() + link.attr.classes:insert("quarto-xref") + + return link + end + end + end + } + + return filter +end + +local function render_pseudocode_ref_latex(global_options) + local filter = { + Cite = function(el) + local cite_text = pandoc.utils.stringify(el.content) + + if string.match(cite_text, "^@alg-") then + return pandoc.RawInline("latex", global_options.reference_prefix .. "~\\ref{" .. string.gsub(cite_text, "^@", "") .. "}" ) + end + end + } + + return filter +end + +local function render_pseudocode_ref(global_options) + local filter = { + Cite = function(el) + return el + end + } + + if quarto.doc.is_format("html") then + filter = render_pseudocode_ref_html(global_options) + elseif quarto.doc.is_format("latex") then + filter = render_pseudocode_ref_latex(global_options) + end + + return filter +end + +function Pandoc(doc) + local global_options = { + caption_prefix = "Algorithm", + reference_prefix = "Algorithm", + caption_number = true, + number_with_in_chapter = false, + html_chapter_level = nil, + html_current_number = 1, + html_identifier_number_mapping = {} + } + + if doc.meta["pseudocode"] then + global_options.caption_prefix = pandoc.utils.stringify(doc.meta["pseudocode"]["caption-prefix"]) or global_options.caption_prefix + global_options.reference_prefix = pandoc.utils.stringify(doc.meta["pseudocode"]["reference-prefix"]) or global_options.reference_prefix + global_options.caption_number = doc.meta["pseudocode"]["caption-number"] or global_options.caption_number + end + + if doc.meta["book"] then + global_options.number_with_in_chapter = true + + if quarto.doc.is_format("html") then + local _, input_qmd_filename = string.match(quarto.doc["input_file"], "^(.-)([^\\/]-%.([^\\/%.]-))$") + local renders = doc.meta["book"]["render"] + + for _, render in pairs(renders) do + if render["file"] and render["number"] and pandoc.utils.stringify(render["file"]) == input_qmd_filename then + global_options.html_chapter_level = pandoc.utils.stringify(render["number"]) + end + end + end + end + + doc = doc:walk(render_pseudocode_block(global_options)) + + return doc:walk(render_pseudocode_ref(global_options)) +end diff --git a/_extensions/leovan/pseudocode/pseudocode.min.css b/_extensions/leovan/pseudocode/pseudocode.min.css new file mode 100644 index 0000000..c28ebfe --- /dev/null +++ b/_extensions/leovan/pseudocode/pseudocode.min.css @@ -0,0 +1 @@ +@import url(https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.css);.ps-root{font-family:KaTeX_Main,'Times New Roman',Times,serif;font-size:1em;font-weight:100;-webkit-font-smoothing:antialiased!important}.ps-root .ps-algorithm{margin:.8em 0;border-top:3px solid #000;border-bottom:2px solid #000}.ps-root .ps-algorithm.with-caption>.ps-line:first-child{border-bottom:2px solid #000}.ps-root .katex{text-indent:0;font-size:1em}.ps-root .MathJax,.ps-root .MathJax_CHTML{text-indent:0;font-size:1em!important}.ps-root .ps-line{margin:0;padding:0;line-height:1.2}.ps-root .ps-funcname{font-family:KaTeX_Main,'Times New Roman',Times,serif;font-weight:400;font-variant:small-caps;font-style:normal;text-transform:none}.ps-root .ps-keyword{font-family:KaTeX_Main,'Times New Roman',Times,serif;font-weight:700;font-variant:normal;font-style:normal;text-transform:none}.ps-root .ps-comment{font-family:KaTeX_Main,'Times New Roman',Times,serif;font-weight:400;font-variant:normal;font-style:normal;text-transform:none}.ps-root .ps-linenum{font-size:.8em;line-height:1em;width:1.6em;text-align:right;display:inline-block;position:relative;padding-right:.3em}.ps-root .ps-algorithmic.with-linenum .ps-line.ps-code{text-indent:-1.6em}.ps-root .ps-algorithmic.with-linenum .ps-line.ps-code>span{text-indent:0}.ps-root .ps-algorithmic.with-scopelines div.ps-block{border-left-style:solid;border-left-width:.1em;padding-left:.6em}.ps-root .ps-algorithmic.with-scopelines>div.ps-block{border-left:none} \ No newline at end of file diff --git a/_extensions/leovan/pseudocode/pseudocode.min.js b/_extensions/leovan/pseudocode/pseudocode.min.js new file mode 100644 index 0000000..e19fe9e --- /dev/null +++ b/_extensions/leovan/pseudocode/pseudocode.min.js @@ -0,0 +1 @@ +(function(e){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=e()}else if(typeof define==="function"&&define.amd){define([],e)}else{var t;if(typeof window!=="undefined"){t=window}else if(typeof global!=="undefined"){t=global}else if(typeof self!=="undefined"){t=self}else{t=this}t.pseudocode=e()}})(function(){var e,t,n;return function(){function p(o,s,a){function l(n,e){if(!s[n]){if(!o[n]){var t="function"==typeof require&&require;if(!e&&t)return t(n,!0);if(h)return h(n,!0);var i=new Error("Cannot find module '"+n+"'");throw i.code="MODULE_NOT_FOUND",i}var r=s[n]={exports:{}};o[n][0].call(r.exports,function(e){var t=o[n][1][e];return l(t||e)},r,r.exports,p,o,s,a)}return s[n].exports}for(var h="function"==typeof require&&require,e=0;ethis.renderElement(e,t))}}},{"./src/Lexer":2,"./src/ParseError":3,"./src/Parser":4,"./src/Renderer":5}],2:[function(e,t,n){var i=e("./utils");var u=e("./ParseError");var r=function(e){this._input=e;this._remain=e;this._pos=0;this._nextAtom=this._currentAtom=null;this._next()};r.prototype.accept=function(e,t){if(this._nextAtom.type===e&&this._matchText(t)){this._next();return this._currentAtom.text}return null};r.prototype.expect=function(e,t){var n=this._nextAtom;if(n.type!==e){throw new u(`Expected an atom of ${e} but received ${n.type}`,this._pos,this._input)}if(!this._matchText(t)){throw new u(`Expected \`${t}\` but received \`${n.text}\``,this._pos,this._input)}this._next();return this._currentAtom.text};r.prototype.get=function(){return this._currentAtom};var o={exec:function(e){var t=[{start:"$",end:"$"},{start:"\\(",end:"\\)"}];var n=e.length;for(var i=0;i0&&a[l-1]==="\\"){var h=l+o.length;a=a.slice(h);s+=h;continue}var p=[e.slice(0,s+l+o.length),e.slice(r.length,s+l)];return p}}return null}};var p={special:/^(\\\\|\\{|\\}|\\\$|\\&|\\#|\\%|\\_)/,math:o,func:/^\\([a-zA-Z]+)/,open:/^\{/,close:/^\}/,quote:/^(`|``|'|'')/,ordinary:/^[^\\{}$&#%_\s]+/};var c=/^%.*/;var f=/^\s+/;r.prototype._skip=function(e){this._pos+=e;this._remain=this._remain.slice(e)};r.prototype._next=function(){var e=false;while(1){var t=f.exec(this._remain);if(t){e=true;var n=t[0].length;this._skip(n)}var i=c.exec(this._remain);if(!i)break;var r=i[0].length;this._skip(r)}this._currentAtom=this._nextAtom;if(this._remain===""){this._nextAtom={type:"EOF",text:null,whitespace:false};return false}for(var o in p){var s=p[o];var a=s.exec(this._remain);if(!a)continue;var l=a[0];var h=a[1]?a[1]:l;this._nextAtom={type:o,text:h,whitespace:e};this._pos+=l.length;this._remain=this._remain.slice(a[0].length);return true}throw new u("Unrecoganizable atom",this._pos,this._input)};r.prototype._matchText=function(e){if(e===null||e===undefined)return true;if(i.isString(e))return e.toLowerCase()===this._nextAtom.text.toLowerCase();else return e.some(e=>e.toLowerCase()===this._nextAtom.text.toLowerCase())};t.exports=r},{"./ParseError":3,"./utils":6}],3:[function(e,t,n){function i(e,t,n){var i=`Error: ${e}`;if(t!==undefined&&n!==undefined){i+=` at position ${t}: \``;n=`${n.slice(0,t)}\u21B1${n.slice(t)}`;var r=Math.max(0,t-15);var o=t+15;i+=`${n.slice(r,o)}\``}this.message=i}i.prototype=Object.create(Error.prototype);i.prototype.constructor=i;t.exports=i},{}],4:[function(e,t,n){var s=e("./utils");var r=e("./ParseError");var a=function(e,t){this.type=e;this.value=t;this.children=[]};a.prototype.toString=function(e){if(!e)e=0;var t="";for(var n=0;n`;if(this.value)i+=` (${s.toString(this.value)})`;i+="\n";if(this.children){for(var r=0;r0){e.addChild(t);continue}break}return e};i.prototype._parseCaption=function(){var e=this._lexer;if(!e.accept("func","caption"))return null;var t=new a("caption");e.expect("open");t.addChild(this._parseCloseText());e.expect("close");return t};i.prototype._parseBlock=function(){var e=new a("block");while(true){var t=this._parseControl();if(t){e.addChild(t);continue}var n=this._parseFunction();if(n){e.addChild(n);continue}var i=this._parseStatement(h);if(i){e.addChild(i);continue}var r=this._parseCommand(p);if(r){e.addChild(r);continue}var o=this._parseComment();if(o){e.addChild(o);continue}break}return e};i.prototype._parseControl=function(){var e;if(e=this._parseIf())return e;if(e=this._parseLoop())return e;if(e=this._parseRepeat())return e;if(e=this._parseUpon())return e};i.prototype._parseFunction=function(){var e=this._lexer;if(!e.accept("func",["function","procedure"]))return null;var t=this._lexer.get().text;e.expect("open");var n=e.expect("ordinary");e.expect("close");e.expect("open");var i=this._parseCloseText();e.expect("close");var r=this._parseBlock();e.expect("func",`end${t}`);var o=new a("function",{type:t,name:n});o.addChild(i);o.addChild(r);return o};i.prototype._parseIf=function(){if(!this._lexer.accept("func","if"))return null;var e=new a("if");this._lexer.expect("open");e.addChild(this._parseCond());this._lexer.expect("close");e.addChild(this._parseBlock());var t=0;while(this._lexer.accept("func",["elif","elsif","elseif"])){this._lexer.expect("open");e.addChild(this._parseCond());this._lexer.expect("close");e.addChild(this._parseBlock());t++}var n=false;if(this._lexer.accept("func","else")){n=true;e.addChild(this._parseBlock())}this._lexer.expect("func","endif");e.value={numElif:t,hasElse:n};return e};i.prototype._parseLoop=function(){if(!this._lexer.accept("func",["FOR","FORALL","WHILE"]))return null;var e=this._lexer.get().text.toLowerCase();var t=new a("loop",e);this._lexer.expect("open");t.addChild(this._parseCond());this._lexer.expect("close");t.addChild(this._parseBlock());var n=e!=="forall"?`end${e}`:"endfor";this._lexer.expect("func",n);return t};i.prototype._parseRepeat=function(){if(!this._lexer.accept("func",["REPEAT"]))return null;var e=this._lexer.get().text.toLowerCase();var t=new a("repeat",e);t.addChild(this._parseBlock());this._lexer.expect("func","until");this._lexer.expect("open");t.addChild(this._parseCond());this._lexer.expect("close");return t};i.prototype._parseUpon=function(){if(!this._lexer.accept("func","upon"))return null;var e=new a("upon");this._lexer.expect("open");e.addChild(this._parseCond());this._lexer.expect("close");e.addChild(this._parseBlock());this._lexer.expect("func","endupon");return e};var l=["ensure","require","input","output"];var h=["state","print","return"];i.prototype._parseStatement=function(e){if(!this._lexer.accept("func",e))return null;var t=this._lexer.get().text.toLowerCase();var n=new a("statement",t);n.addChild(this._parseOpenText());return n};var p=["break","continue"];i.prototype._parseCommand=function(e){if(!this._lexer.accept("func",e))return null;var t=this._lexer.get().text.toLowerCase();var n=new a("command",t);return n};i.prototype._parseComment=function(){if(!this._lexer.accept("func","comment"))return null;var e=new a("comment");this._lexer.expect("open");e.addChild(this._parseCloseText());this._lexer.expect("close");return e};i.prototype._parseCall=function(){var e=this._lexer;if(!e.accept("func","call"))return null;var t=e.get().whitespace;e.expect("open");var n=e.expect("ordinary");e.expect("close");var i=new a("call");i.whitespace=t;i.value=n;e.expect("open");var r=this._parseCloseText();i.addChild(r);e.expect("close");return i};i.prototype._parseCond=i.prototype._parseCloseText=function(){return this._parseText("close")};i.prototype._parseOpenText=function(){return this._parseText("open")};i.prototype._parseText=function(e){var t=new a(`${e}-text`);var n=false;var i;while(true){i=this._parseAtom()||this._parseCall();if(i){if(n)i.whitespace|=n;t.addChild(i);continue}if(this._lexer.accept("open")){i=this._parseCloseText();n=this._lexer.get().whitespace;i.whitespace=n;t.addChild(i);this._lexer.expect("close");n=this._lexer.get().whitespace;continue}break}return t};var u={ordinary:{tokenType:"ordinary"},math:{tokenType:"math"},special:{tokenType:"special"},"cond-symbol":{tokenType:"func",tokenValues:["and","or","not","true","false","to","downto"]},"quote-symbol":{tokenType:"quote"},"sizing-dclr":{tokenType:"func",tokenValues:["tiny","scriptsize","footnotesize","small","normalsize","large","Large","LARGE","huge","Huge"]},"font-dclr":{tokenType:"func",tokenValues:["normalfont","rmfamily","sffamily","ttfamily","upshape","itshape","slshape","scshape","bfseries","mdseries","lfseries"]},"font-cmd":{tokenType:"func",tokenValues:["textnormal","textrm","textsf","texttt","textup","textit","textsl","textsc","uppercase","lowercase","textbf","textmd","textlf"]},"text-symbol":{tokenType:"func",tokenValues:["textbackslash"]}};i.prototype._parseAtom=function(){for(var e in u){var t=u[e];var n=this._lexer.accept(t.tokenType,t.tokenValues);if(n===null)continue;var i=this._lexer.get().whitespace;if(e!=="ordinary"&&e!=="math")n=n.toLowerCase();return new o(e,n,i)}return null};t.exports=i},{"./ParseError":3,"./utils":6}],5:[function(n,e,t){var a=n("./utils");function A(e){this._css={};this._fontSize=this._outerFontSize=e!==undefined?e:1}A.prototype.outerFontSize=function(e){if(e!==undefined)this._outerFontSize=e;return this._outerFontSize};A.prototype.fontSize=function(){return this._fontSize};A.prototype._fontCommandTable={normalfont:{"font-family":"KaTeX_Main"},rmfamily:{"font-family":"KaTeX_Main"},sffamily:{"font-family":"KaTeX_SansSerif"},ttfamily:{"font-family":"KaTeX_Typewriter"},bfseries:{"font-weight":"bold"},mdseries:{"font-weight":"medium"},lfseries:{"font-weight":"lighter"},upshape:{"font-style":"normal","font-variant":"normal"},itshape:{"font-style":"italic","font-variant":"normal"},scshape:{"font-style":"normal","font-variant":"small-caps"},slshape:{"font-style":"oblique","font-variant":"normal"},textnormal:{"font-family":"KaTeX_Main"},textrm:{"font-family":"KaTeX_Main"},textsf:{"font-family":"KaTeX_SansSerif"},texttt:{"font-family":"KaTeX_Typewriter"},textbf:{"font-weight":"bold"},textmd:{"font-weight":"medium"},textlf:{"font-weight":"lighter"},textup:{"font-style":"normal","font-variant":"normal"},textit:{"font-style":"italic","font-variant":"normal"},textsc:{"font-style":"normal","font-variant":"small-caps"},textsl:{"font-style":"oblique","font-variant":"normal"},uppercase:{"text-transform":"uppercase"},lowercase:{"text-transform":"lowercase"}};A.prototype._sizingScalesTable={tiny:.68,scriptsize:.8,footnotesize:.85,small:.92,normalsize:1,large:1.17,Large:1.41,LARGE:1.58,huge:1.9,Huge:2.28};A.prototype.updateByCommand=function(e){var t=this._fontCommandTable[e];if(t!==undefined){for(var n in t)this._css[n]=t[n];return}var i=this._sizingScalesTable[e];if(i!==undefined){this._outerFontSize=this._fontSize;this._fontSize=i;return}throw new ParserError("Unrecognized `text-style` command")};A.prototype.toCSS=function(){var e="";for(var t in this._css){var n=this._css[t];if(n===undefined)continue;e+=`${t}:${n};`}if(this._fontSize!==this._outerFontSize)e+=`font-size:${this._fontSize/this._outerFontSize}em;`;return e};function B(e,t){this._nodes=e;this._textStyle=t}B.prototype._renderCloseText=function(e,t){var n=new A(this._textStyle.fontSize());var i=new B(e.children,n);if(e.whitespace)this._html.putText(" ");this._html.putHTML(i.renderToHTML(t))};B.prototype.renderToHTML=function(e){this._html=new _;var t;while((t=this._nodes.shift())!==undefined){var n=t.type;var i=t.value;if(t.whitespace)this._html.putText(" ");switch(n){case"ordinary":this._html.putText(i);break;case"math":if(typeof e==="undefined"){throw EvalError("No math backend found. Please setup KaTeX or MathJax.")}else if(e.name==="katex"){this._html.putHTML(e.driver.renderToString(i))}else if(e.name==="mathjax"){if(e.version<3){this._html.putText(`$${i}$`)}else{this._html.putHTML(e.driver.tex2chtml(i,{display:false}).outerHTML)}}else{throw new EvalError(`Unknown math backend ${e}`)}break;case"cond-symbol":this._html.beginSpan("ps-keyword").putText(i.toLowerCase()).endSpan();break;case"special":if(i==="\\\\"){this._html.putHTML("
");break}var r={"\\{":"{","\\}":"}","\\$":"$","\\&":"&","\\#":"#","\\%":"%","\\_":"_"};var o=r[i];this._html.putText(o);break;case"text-symbol":var s={textbackslash:"\\"};var a=s[i];this._html.putText(a);break;case"quote-symbol":var l={"`":"\u2018","``":"\u201c","'":"\u2019","''":"\u201d"};var h=l[i];this._html.putText(h);break;case"call":this._html.beginSpan("ps-funcname").putText(i).endSpan();this._html.write("(");var p=t.children[0];this._renderCloseText(p,e);this._html.write(")");break;case"close-text":this._renderCloseText(t,e);break;case"font-dclr":case"sizing-dclr":this._textStyle.updateByCommand(i);this._html.beginSpan(null,this._textStyle.toCSS());var u=new B(this._nodes,this._textStyle);this._html.putHTML(u.renderToHTML(e));this._html.endSpan();break;case"font-cmd":var c=this._nodes[0];if(c.type!=="close-text")continue;var f=new A(this._textStyle.fontSize());f.updateByCommand(i);this._html.beginSpan(null,f.toCSS());var d=new B(c.children,f);this._html.putHTML(d.renderToHTML(e));this._html.endSpan();break;default:throw new ParseError(`Unexpected ParseNode of type ${t.type}`)}}return this._html.toMarkup()};function _(){this._body=[];this._textBuf=[]}_.prototype.beginDiv=function(e,t,n){this._beginTag("div",e,t,n);this._body.push("\n");return this};_.prototype.endDiv=function(){this._endTag("div");this._body.push("\n");return this};_.prototype.beginP=function(e,t,n){this._beginTag("p",e,t,n);this._body.push("\n");return this};_.prototype.endP=function(){this._flushText();this._endTag("p");this._body.push("\n");return this};_.prototype.beginSpan=function(e,t,n){this._flushText();return this._beginTag("span",e,t,n)};_.prototype.endSpan=function(){this._flushText();return this._endTag("span")};_.prototype.putHTML=function(e){this._flushText();this._body.push(e);return this};_.prototype.putText=function(e){this._textBuf.push(e);return this};_.prototype.write=function(e){this._body.push(e)};_.prototype.toMarkup=function(){this._flushText();var e=this._body.join("");return e.trim()};_.prototype.toDOM=function(){var e=this.toMarkup();var t=document.createElement("div");t.innerHTML=e;return t.firstChild};_.prototype._flushText=function(){if(this._textBuf.length===0)return;var e=this._textBuf.join("");this._body.push(this._escapeHtml(e));this._textBuf=[]};_.prototype._beginTag=function(e,t,n,i){var r=`<${e}`;if(t)r+=` class="${t}"`;if(n){var o;if(a.isString(n)){o=n}else{o="";for(var s in n){attrVal=n[s];o+=`${s}:${attrVal};`}}if(i)o+=i;r+=` style="${o}"`}r+=">";this._body.push(r);return this};_.prototype._endTag=function(e){this._body.push(``);return this};var i={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};_.prototype._escapeHtml=function(e){return String(e).replace(/[&<>"'/]/g,e=>i[e])};function r(e){e=e||{};this.indentSize=e.indentSize?this._parseEmVal(e.indentSize):1.2;this.commentDelimiter=e.commentDelimiter!==undefined?e.commentDelimiter:" // ";this.lineNumberPunc=e.lineNumberPunc!==undefined?e.lineNumberPunc:":";this.lineNumber=e.lineNumber!==undefined?e.lineNumber:false;this.noEnd=e.noEnd!==undefined?e.noEnd:false;this.scopeLines=e.scopeLines!==undefined?e.scopeLines:false;if(e.captionCount!==undefined)F.captionCount=e.captionCount;this.titlePrefix=e.titlePrefix!==undefined?e.titlePrefix:"Algorithm"}r.prototype._parseEmVal=function(e){e=e.trim();if(e.indexOf("em")!==e.length-2)throw new TypeError("Unit error; expected `em` suffix");return Number(e.substring(0,e.length-2))};function F(e,t){this._root=e.parse();this._options=new r(t);this._openLine=false;this._blockLevel=0;this._textLevel=-1;this._globalTextStyle=new A;this.backend=undefined;try{if(typeof katex==="undefined")katex=n("katex")}catch(e){}try{if(typeof MathJax==="undefined")MathJax=n("mathjax")}catch(e){}if(typeof katex!=="undefined"){this.backend={name:"katex",driver:katex}}else if(typeof MathJax!=="undefined"){this.backend={name:"mathjax",version:parseInt(MathJax.version.split(".")[0]),driver:MathJax}}}F.captionCount=0;F.prototype.toMarkup=function(){var e=this._html=new _;this._buildTree(this._root);delete this._html;return e.toMarkup()};F.prototype.toDOM=function(){var e=this.toMarkup();var t=document.createElement("div");t.innerHTML=e;return t.firstChild};F.prototype._beginGroup=function(e,t,n){this._closeLineIfAny();this._html.beginDiv(`ps-${e}${t?` ${t}`:""}`,n)};F.prototype._endGroup=function(e){this._closeLineIfAny();this._html.endDiv()};F.prototype._beginBlock=function(){var e=this._options.lineNumber&&this._blockLevel===0?.6:0;var t=this._options.indentSize+e;if(this._options.scopeLines)t/=2;this._beginGroup("block",null,{"margin-left":`${t}em`});this._blockLevel++};F.prototype._endBlock=function(){this._closeLineIfAny();this._endGroup();this._blockLevel--};F.prototype._newLine=function(){this._closeLineIfAny();this._openLine=true;this._globalTextStyle.outerFontSize(1);var e=this._options.indentSize;if(this._blockLevel>0){this._numLOC++;this._html.beginP("ps-line ps-code",this._globalTextStyle.toCSS());var t=this._options.lineNumber?e*1.25:0;t+=this._options.scopeLines?e*.1:0;if(this._options.lineNumber){this._html.beginSpan("ps-linenum",{left:`${-((this._blockLevel-1)*t)}em`}).putText(this._numLOC+this._options.lineNumberPunc).endSpan()}}else{this._html.beginP("ps-line",{"text-indent":`${-e}em`,"padding-left":`${e}em`},this._globalTextStyle.toCSS())}};F.prototype._closeLineIfAny=function(){if(!this._openLine)return;this._html.endP();this._openLine=false};F.prototype._typeKeyword=function(e){this._html.beginSpan("ps-keyword").putText(e).endSpan()};F.prototype._typeFuncName=function(e){this._html.beginSpan("ps-funcname").putText(e).endSpan()};F.prototype._typeText=function(e){this._html.write(e)};F.prototype._buildTreeForAllChildren=function(e){var t=e.children;for(var n=0;n0&&t[0].type==="comment"){var n=t.shift();this._buildTree(n)}};F.prototype._buildTree=function(e){var t;var n;var i;switch(e.type){case"root":this._beginGroup("root");this._buildTreeForAllChildren(e);this._endGroup();break;case"algorithm":var r;for(t=0;t=1.4.0" +contributes: + filters: + - parse-acronyms.lua + shortcodes: + - shortcodes_acronyms.lua diff --git a/_extensions/rchaput/acronyms/acronyms.lua b/_extensions/rchaput/acronyms/acronyms.lua new file mode 100644 index 0000000..271d209 --- /dev/null +++ b/_extensions/rchaput/acronyms/acronyms.lua @@ -0,0 +1,236 @@ +--[[ + + This file defines an Acronym and the Acronyms table. + +--]] + + +local Helpers = require("acronyms_helpers") + + +-- Define an Acronym with some default values +Acronym = { + -- The acronym's key, or label. Used to identify it. Must be unique. + key = nil, + -- The acronym's short form (i.e., the acronym itself). + shortname = nil, + -- The acronym's definition, or description. + longname = nil, + -- The number of times this acronym was used. + occurrences = 0, + -- The order in which acronyms are defined. 1=first, 2=second, etc. + definition_order = nil, + -- The order in which acronyms appear in the document. 1=first, nil=never. + usage_order = nil, +} + + +-- Helper method to generate a precise error message describing the user the +-- problem when creating an acronym (e.g., if the shortname is missing). +local function raiseAcronymCreationError(object) + local msg = lunacolors.red("Error when creating an acronym:\n") + msg = msg .. "! Both `shortname`` and `longname` must be specified:\n" + if object.shortname == nil then + msg = msg .. "x `shortname` was nil\n" + end + if object.longname == nil then + msg = msg .. "x `longname` was nil\n" + end + local unexpected_keys = {} + for k, _ in pairs(object.original_metadata) do + if k ~= "shortname" and k ~= "longname" and k ~= "key" then + table.insert(unexpected_keys, k) + end + end + msg = msg .. "i Found unexpected keys: " .. table.concat(unexpected_keys, ",") .. ".\n" + -- This str here represents the original metadata, not the formatted + -- Acronym (which could be obtained with `tostring(object)`). + local acronym_str = Helpers.metadata_to_str(object.original_metadata) + msg = msg .. "i The acronym was defined as: " .. acronym_str .. "\n" + quarto.log.error("[acronyms]", msg, "\n") + assert(false) +end + + +-- Create a new Acronym +function Acronym:new(object) + setmetatable(object, self) + self.__index = self + + -- Check that important attributes are non-nil + if object.shortname == nil or object.longname == nil then + raiseAcronymCreationError(object) + end + + -- If the key is not set, we want to use the shortname instead. + -- (Most of the time, the key is the shortname in lower case anyway...) + object.key = object.key or object.shortname + + return object +end + + +-- Debug (helper) function +function Acronym.__tostring(acronym) + local str = "Acronym{" + str = str .. "key=" .. acronym.key .. ";" + str = str .. "short=" .. acronym.shortname .. ";" + str = str .. "long=" .. acronym.longname .. ";" + str = str .. "occurrences=" .. acronym.occurrences .. ";" + str = str .. "definition_order=" .. tostring(acronym.definition_order) .. ";" + str = str .. "usage_order=" .. tostring(acronym.usage_order) + str = str .. "}" + return str +end + + +-- Increment the count of occurrences +function Acronym:incrementOccurrences() + self.occurrences = self.occurrences + 1 +end + + +-- Is this the acronym's first occurrence? +function Acronym:isFirstUse() + return self.occurrences <= 1 +end + + +-- The Acronyms database. +Acronyms = { + -- The table that contains all acronyms, indexed by their key. + acronyms = {}, + + -- The current "definition_order" value. + -- Each time a new acronym is defined, we increment this value to keep + -- count of the order in which acronyms are defined. + current_definition_order = 0, + + -- The current "usage order" value. + -- We increment this value each time an acronym is used for the first time, + -- to keep count of their order of appearance. This can be necessary for + -- generating the List of Acronyms, depending on the desired order. + current_usage_order = 0, + + -- Access to the `Acronym` class, if necessary. + Acronym = Acronym, +} + + +-- Get the Acronym with the given key, or nil if not found. +function Acronyms:get(key) + return self.acronyms[key] +end + + +-- Does the table contains the given key? +function Acronyms:contains(key) + return self:get(key) ~= nil +end + + +-- Add a new acronym to the table. Also handles duplicates. +function Acronyms:add(acronym, on_duplicate) + quarto.log.debug("[acronyms] Trying to add a new acronym...", acronym) + assert(acronym ~= nil, + "[acronyms] The acronym should not be nil in Acronyms:add!") + assert(acronym.key ~= nil, + "[acronyms] The acronym key should not be nil in Acronyms:add!") + assert(on_duplicate ~= nil, + "[acronyms] The parameter on_duplicate should not be nil in Acronyms:add!") + + -- Handling duplicate keys + if self:contains(acronym.key) then + quarto.log.debug("[acronyms] Found an acronym with a duplicate key: ", acronym.key) + if on_duplicate == "replace" then + -- Do nothing, let us replace the previous acronym. + elseif on_duplicate == "keep" then + -- Do nothing, but do not replace: we return here. + return + elseif on_duplicate == "warn" then + -- Warn, and do not replace. + quarto.log.warning("[acronyms] Found an acronym with a duplicate key: ", acronym.key) + return + elseif on_duplicate == "error" then + -- Stop execution. + quarto.log.error("[acronyms] Found an acronym with a duplicate key: ", acronym.key) + assert(false) + else + quarto.log.error("[acronyms] Unrecognized option `on_duplicate`=", tostring(on_duplicate), " in Acronyms:add.") + assert(false) + end + end + + self.current_definition_order = self.current_definition_order + 1 + acronym.definition_order = self.current_definition_order + self.acronyms[acronym.key] = acronym +end + + +function Acronyms:setAcronymUsageOrder(acronym) + assert(acronym ~=nil, + "[acronyms] The acronym should not be nil in Acronyms:setAcronymUsageOrder!") + self.current_usage_order = self.current_usage_order + 1 + acronym.usage_order = self.current_usage_order +end + + +-- Populate the Acronyms database from a YAML metadata +function Acronyms:parseFromMetadata(metadata, on_duplicate) + quarto.log.debug("[acronyms] Parsing acronyms from metadata...", metadata.acronyms) + -- We expect the acronyms to be in the `metadata.acronyms.keys` field. + if not (metadata and metadata.acronyms and metadata.acronyms.keys) then + return + end + -- This field should be a Pandoc "MetaList" (so we can iter over it). + if not Helpers.isMetaList(metadata.acronyms.keys) then + quarto.log.error("[acronyms] The `acronyms.keys` metadata should be a list!") + assert(false) + end + + -- Iterate over the defined acronyms. We use `ipairs` since we want to + -- keep their original order (useful for the `definition_order`!). + for _, v in ipairs(metadata.acronyms.keys) do + -- Remember that each of these values can be nil! + -- By using `and`, we make sure that `stringify` is applied on non-nil. + local key = v.key and pandoc.utils.stringify(v.key) + local shortname = v.shortname and pandoc.utils.stringify(v.shortname) + local longname = v.longname and pandoc.utils.stringify(v.longname) + local acronym = Acronym:new{ + key = key, + shortname = shortname, + longname = longname, + original_metadata = v, + } + Acronyms:add(acronym, on_duplicate) + end +end + + +-- Populate the Acronyms database from a YAML file +-- Inspired from https://github.com/dsanson/pandoc-abbreviations.lua/ +function Acronyms:parseFromYamlFile(filepath, on_duplicate) + quarto.log.debug("[acronyms] Trying to parse acronyms from file: ", filepath) + assert(filepath ~= nil, + "[acronyms] filepath must not be nil when parsing from external file!") + + -- First, read the file's content. + local file = io.open(filepath, "r") + if file == nil then + quarto.log.warning("[acronyms] File ", filepath, " could not be read! (does not exist?)") + return + end + local content = file:read("*a") + file:close() + + -- Secondly, use Pandoc's read ability to parse the content. + -- Pandoc does not know how to read YAML, so we'll trick it by + -- asking to parse Markdown instead (since the Markdown's metadata + -- is YAML anyway). + local metadata = pandoc.read(content, "markdown").meta + + -- Finally, read the metadata as usual. + self:parseFromMetadata(metadata, on_duplicate) +end + +return Acronyms diff --git a/_extensions/rchaput/acronyms/acronyms_helpers.lua b/_extensions/rchaput/acronyms/acronyms_helpers.lua new file mode 100644 index 0000000..450202f --- /dev/null +++ b/_extensions/rchaput/acronyms/acronyms_helpers.lua @@ -0,0 +1,102 @@ +--[[ + +This file defines a few helper functions, in particular with respect to pandoc. + +--]] + +local Options = require("acronyms_options") + + +local Helpers = {} + + +-- Helper function to determine pandoc's version. +-- `version` must be a table of numbers, e.g., `{2, 17, 0, 1}` +function Helpers.isAtLeastVersion(version) + -- `PANDOC_VERSION` exists since 2.1, but we never know... + if PANDOC_VERSION == nil then + return false + end + -- Loop up over the components + -- e.g., `2.17.0.1` => [0]=2, [1]=17, [2]=0, [3]=1 + for k, v in ipairs(version) do + if PANDOC_VERSION[k] == nil or PANDOC_VERSION[k] < version[k] then + -- Examples: 2.17 < 2.17.0.1, or 2.16 < 2.17 + return false + elseif PANDOC_VERSION[k] > version[k] then + -- Example: 2.17 > 2.16.2 (we do not need to check the next!) + return true + end + end + -- At this point, all components are equal + return true +end + + +-- Helper function to determine whether a metadata field is a list. +function Helpers.isMetaList(field) + -- We want to know whether we have multiple values (MetaList). + -- Pandoc 2.17 introduced a compatibility-breaking change for this: + -- the `.tag` is no longer present in >= 2.17 ; + -- the `pandoc.utils.type` function is only available in >= 2.17 + if Helpers.isAtLeastVersion({2, 17}) then + -- Use the new `pandoc.utils.type` function + return pandoc.utils.type(field) == "List" + else + -- Use the (old) `.tag` type attribute + return field.t == "MetaList" + end +end + + +-- Helper function to generate the ID (identifier) from an acronym key. +-- The ID can be used for, e.g., links. +function Helpers.key_to_id(key) + return Options["id_prefix"] .. key +end + + +-- Similar helper but for the link itself (based on the ID). +function Helpers.key_to_link(key) + return "#" .. Helpers.key_to_id(key) +end + + +-- Helper to print a Pandoc Metadata. +-- From a metadata (e.g., a YAML map), it returns a table-like string: +-- `{ key1: value1 ; key2: value2 ; ... }`. +function Helpers.metadata_to_str(metadata) + -- We need to reformat a bit the table + local t = {} + for k, v in pairs(metadata) do + table.insert(t, k .. ": " .. pandoc.utils.stringify(v)) + end + return "{ " .. table.concat(t, " ; ") .. " }" +end + + +-- Helper to convert a (case-insensitive) string to a boolean +-- Recognized values: `true`, `false`, `yes`, `no`, `y`, `n` +function Helpers.str_to_boolean(value) + local converts = { + ["true"] = true, + ["false"] = false, + ["yes"] = true, + ["no"] = false, + ["y"] = true, + ["n"] = false, + } + local result = converts[string.lower(value)] + if result == nil then + quarto.log.warning( + "[acronyms] Could not convert string to boolean, unrecognized value:", + value, + " ! Assuming `false`." + ) + result = false + end + return result +end + + +return Helpers diff --git a/_extensions/rchaput/acronyms/acronyms_options.lua b/_extensions/rchaput/acronyms/acronyms_options.lua new file mode 100644 index 0000000..36393b9 --- /dev/null +++ b/_extensions/rchaput/acronyms/acronyms_options.lua @@ -0,0 +1,146 @@ +--[[ + +This file defines the Options table. + +--]] + + +-- The table that holds all options, with their default values. +-- We will also add a few methods to this table, to handle these options +-- (parse them from configuration files, get them, ...). +local Options = { + + -- The language used in this document (Quarto standard option), following + -- the IETF BCP47 standard, i.e., a tag composed of subtags, separated by + -- hyphens (`-`). For example: `en-US`. The first subtag represent the + -- language itself (`en`, `fr`, `zh`, ...), other subtags represent + -- more precise information (region, script, ...). + lang = "", + + -- The prefix to prepend to all acronym's ID (to ensure their uniqueness). + -- IDs are especially used to link an acronym to its definition in the List + -- of Acronyms. + id_prefix = "acronyms_", + + -- How to sort acronyms in the List of Acronyms. + -- Please refer to the `sort_acronyms.lua` file for allowed values. + sorting = "alphabetical", + + -- The title (header) that precedes the List of Acronyms (LoA). + loa_title = nil, + + -- Whether to include in the LoA acronyms that have not been used. + include_unused = true, + + -- Whether to insert the LoA, and where. + insert_loa = "beginning", + + -- How to deal with non-existing acronyms. + non_existing = "key", + + -- How to deal with duplicate definitions of acronyms. + on_duplicate = "warn", + + -- The style to use when replacing an acronym. + -- Please refer to the `acronyms_styles.lua` for allowed values. + style = "long-short", + + -- Whether to insert a link to the acronym's definition in the LoA when + -- replacing (rendering) an acronym. + insert_links = true, + + -- Additional classes to add to the List of Acronyms header. + loa_header_classes = {}, + + -- Custom format for the List of Acronyms, as a Markdown template in which + -- the `{shortname}` and `{longname}` placeholders will be replaced. + loa_format = nil, + +} + + +--[[ +Parse the options from the Metadata (i.e., the YAML fields). +--]] +function Options:parseOptionsFromMetadata(m) + quarto.log.debug("[acronyms] Parsing options from metadata...", m.acronyms) + -- Load the lang (can be `nil`); this is the only option outside `acronyms`. + if m.lang ~= nil then + self.lang = pandoc.utils.stringify(m.lang) + end + + -- The options that we are interested in are all grouped under `acronyms`. + -- If it does not exist, use an empty table. + options = m.acronyms or {} + + if options["id_prefix"] ~= nil then + self.id_prefix = pandoc.utils.stringify(options["id_prefix"]) + end + + if options["sorting"] ~= nil then + self.sorting = pandoc.utils.stringify(options["sorting"]) + end + + if options["loa_title"] ~= nil then + if pandoc.utils.stringify(options["loa_title"]) == "" then + -- Writing `loa_title: ""` in the YAML returns `{}` (an empty table). + -- `pandoc.utils.stringify({})` returns `""` as well. + -- This value indicates that the user does not want a Header. + self.loa_title = "" + else + -- For any other case, we want to use the exact same value, + -- (not stringified!), i.e., a Pandoc object. + self.loa_title = options["loa_title"] + end + end + + if options["include_unused"] ~= nil then + -- This value should be a boolean here, we do not need to stringify it. + self.include_unused = options["include_unused"] + end + + if options["insert_loa"] ~= nil then + if options["insert_loa"] == false then + -- Special value: keep it exactly as-is. + self.insert_loa = false + else + -- Default case: it should be "beginning", or "end", we want it + -- as a string. + self.insert_loa = pandoc.utils.stringify(options["insert_loa"]) + end + end + + if options["non_existing"] ~= nil then + self.non_existing = pandoc.utils.stringify(options["non_existing"]) + end + + if options["on_duplicate"] ~= nil then + self.on_duplicate = pandoc.utils.stringify(options["on_duplicate"]) + end + + if options["style"] ~= nil then + self.style = pandoc.utils.stringify(options["style"]) + end + + if options["insert_links"] ~= nil then + -- This value should be a boolean here, we do not need to stringify it. + self.insert_links = options["insert_links"] + end + + if options["loa_header_classes"] ~= nil then + for _, v in ipairs(options["loa_header_classes"]) do + table.insert(self.loa_header_classes, pandoc.utils.stringify(v)) + end + end + + if options["loa_format"] ~= nil then + if pandoc.utils.type(options["loa_format"]) == "Inlines" then + self.loa_format = options["loa_format"][1].text + else + self.loa_format = pandoc.utils.stringify(options["loa_format"]) + end + end +end + + +return Options diff --git a/_extensions/rchaput/acronyms/acronyms_pandoc.lua b/_extensions/rchaput/acronyms/acronyms_pandoc.lua new file mode 100644 index 0000000..c07f6f3 --- /dev/null +++ b/_extensions/rchaput/acronyms/acronyms_pandoc.lua @@ -0,0 +1,250 @@ +--[[ + Functions to generate Pandoc elements. + + These functions are independent of the "entrypoint" script: they work + with both: + - filters (i.e., scripts that parse the Pandoc content and react to various + elements); + - shortcodes (i.e., scripts that are triggered by the new Quarto syntax, + `{{< shortcode-name args >}}`). + + This allows **acronyms** to work with its legacy filter mode, by reacting + to `\acr{key}` and `\printacronyms` elements, and with the new shortcode + syntax. However, due to the way it was coded, the legacy filter mode will + not support arguments and keyworded arguments. The shortcode mode is + particularly suited for these new features, as it is handled by Quarto + itself directly. It will be easier for users to customize a specific + acronym or list of acronym, rather than specifying options in the Metadata. +]] + +-- The Acronyms database +local Acronyms = require("acronyms") + +-- Some helper functions +local Helpers = require("acronyms_helpers") + +-- Sorting function +local sortAcronyms = require("sort_acronyms") + +-- Replacement function (handling styles) +local replaceExistingAcronymWithStyle = require("acronyms_styles") + +-- The options for the List Of Acronyms, as defined in the document's metadata. +local Options = require("acronyms_options") + +-- The translations for some hardcoded sentences (such as the LoA Title) +local Translations = require("acronyms_translations") + +-- The functions that we define here, and which will generate Pandoc elements +local AcronymsPandoc = {} + + +--[[ + Replace an acronym whose key is not found in the `acronyms` table with the + corresponding Pandoc elements. + + Params: + - acr_key: the acronym's key (identifier). + - non_existing: controls which behaviour to use: + - `key` => warn, and simply return the key as a Pandoc text + - `??` => warn, and return `"??"` as a pandoc Text (similar to bibtex's + behaviour) + - `error` => raise an error, and stop execution +--]] +function AcronymsPandoc.replaceNonExistingAcronym(acr_key, non_existing) + -- TODO: adding the source line to warnings would be useful. + -- But maybe not doable in Pandoc? + + -- Use default option if not set + non_existing = non_existing or Options["non_existing"] + + if non_existing == "key" then + quarto.log.warning("[acronyms] Acronym key", acr_key, "not recognized") + return pandoc.Str(acr_key) + elseif non_existing == "??" then + quarto.log.warning("[acronyms] Acronym key", acr_key, "not recognized") + return pandoc.Str("??") + elseif non_existing == "error" then + quarto.log.error( + "[acronyms] Acronym key", + tostring(acr_key), + "not recognized, stopping!" + ) + assert(false) + else + quarto.log.error( + "[acronyms] Unrecognized option `non_existing`=`", + tostring(non_existing), + "` when replacing acronyms." + ) + assert(false) + end +end + + +--[[ + Replace an acronym identified by its key, which exists in the `acronyms` + table, and generate the corresponding Pandoc elements. + + Params: + - key: identifies the acronym to replace. + - style: the style to use when replacing (see the `acronyms_styles.lua` file). + - first_use: whether we want to print this acronym as its first use, or + a "next use". Set to `nil` to use the acronym's own counter, or override + it directly by setting to `true` or `false`. + - insert_links: whether to insert a link to this acronym's definition in + the List of Acronyms. +--]] +function AcronymsPandoc.replaceExistingAcronym(acr_key, style, first_use, insert_links) + quarto.log.debug("[acronyms] Replacing acronym", acr_key) + local acronym = Acronyms:get(acr_key) + acronym:incrementOccurrences() + if acronym:isFirstUse() then + -- This acronym never appeared! We first set its usage order. + Acronyms:setAcronymUsageOrder(acronym) + end + + -- Use default values from Options if not specified + style = style or Options["style"] + if insert_links == nil then insert_links = Options["insert_links"] end + + -- Replace the acronym with the desired style + return replaceExistingAcronymWithStyle( + acronym, + style, + insert_links, + first_use + ) +end + + +--[[ + Generate the List Of Acronyms, as a Definition List. + + This method is used internally by `generateLoA`. +-- ]] +function AcronymsPandoc.generateDefinitionList(sorted_acronyms) + local definition_list = {} + for _, acronym in ipairs(sorted_acronyms) do + -- The definition's name. A Span with an ID so we can create a link. + local name = pandoc.Span( + acronym.shortname, + pandoc.Attr(Helpers.key_to_id(acronym.key), {}, {}) + ) + -- The definition's value. + local definition = pandoc.Plain(acronym.longname) + table.insert(definition_list, { name, definition }) + end + return pandoc.DefinitionList(definition_list) +end + + +--[[ + Generate the List Of Acronyms with a custom (user-supplied) format. + + This method is used internally by `generateLoA`. +--]] +function AcronymsPandoc.generateCustomFormat(sorted_acronyms, loa_format) + -- Most people will want a list; if we render each item independently, + -- it will not look correctly in the final document. We thus concatenate + -- all acronyms in a temporary Markdown document before rendering it. + local document_markup = "" + for _, acronym in ipairs(sorted_acronyms) do + quarto.log.debug( + "[acronyms] Generating definition for acronym", acronym.key + ) + local id = Helpers.key_to_id(acronym.key) + -- The acronym's name. We want it to be rendered with an ID attribute. + local name = "[" .. acronym.shortname .. "]{#" .. id .. "}" + -- The `loa_format` should be a Markdown template, with `{shortname}` + -- and `{longname}` as placeholder values that we must replace. + local acronym_markup = loa_format:gsub("{shortname}", name) + acronym_markup = acronym_markup:gsub("{longname}", acronym.longname) + quarto.log.debug( + "[acronyms] Template markup processed as", acronym_markup + ) + document_markup = document_markup .. acronym_markup .. "\n\n" + end + quarto.log.debug("[acronyms] Rendering Markdown markup:\n", document_markup) + local document = pandoc.read(document_markup) + -- We want to return all rendered blocks (potentially multiline content, + -- such as bullet lists, divs, paragraphs, ...); but we cannot use `blocks` + -- directly. Quarto expects a single Block, not Blocks. + -- The `pandoc.util.blocks_to_inlines` function should be useful, but + -- adds incorrect trailing `\` to some templates... + -- Wrapping the blocks in a Div seems the safest option, although it + -- adds an unnecessary (but still working) `
...
` markup. + return pandoc.Div(document.blocks) +end + + +--[[ + Generate the List Of Acronyms. + + Returns 2 values: the Header, and the list of acronyms. By default, it is + rendered as a DefinitionList, but a custom format can be used, in which + case it is rendered as Markdown directly. + + Params: + - sorting: the sorting method to use. + - include_unused: whether to include unused acronyms. + - title: the header title ; use `''` (the empty string) to avoid generating + a header (the user wants to create the header manually). + - header_classes: the table of extra classes to put to the header. +--]] +function AcronymsPandoc.generateLoA(sorting, include_unused, title, header_classes) + -- Original idea from https://gist.github.com/RLesur/e81358c11031d06e40b8fef9fdfb2682 + + -- Use default options if not specified + sorting = sorting or Options["sorting"] + include_unused = include_unused or Options["include_unused"] + if title == nil then + -- No shortcode-specific value given + if Options["loa_title"] ~= nil then + -- A value given in the metadata (options) + title = Options["loa_title"] + else + -- If neither the metadata nor the shortcode option are specified, + -- by default we use the translation for the user's language + title = pandoc.MetaInlines(pandoc.Str(Translations:get_loa_title(Options["lang"]))) + end + end + header_classes = header_classes or Options["loa_header_classes"] + local loa_format = Options["loa_format"] + + -- We first get the list of sorted acronyms, according to the defined criteria. + local sorted = sortAcronyms( + Acronyms.acronyms, + sorting, + include_unused + ) + + -- Create the actual List of Acronyms + local list_acronyms + if loa_format == nil then + -- Default format: create a DefinitionList + list_acronyms = AcronymsPandoc.generateDefinitionList(sorted) + else + -- Custom format, render acronyms based on the requested format. + list_acronyms = AcronymsPandoc.generateCustomFormat(sorted, loa_format) + end + + -- Create the Header (only if the title is not empty) + local header = nil + if title ~= "" then + local extra_classes = header_classes + -- Create a table specifically for this LoA, and copy all "extra classes" + -- (from the Options) to this table. The table will also contain `"loa"`. + local loa_classes = table.move(extra_classes, 1, #extra_classes, 2, {"loa"}) + header = pandoc.Header( + 1, + { table.unpack(title) }, + pandoc.Attr(Helpers.key_to_id("HEADER_LOA"), loa_classes, {}) + ) + end + + return header, list_acronyms +end + + +return AcronymsPandoc diff --git a/_extensions/rchaput/acronyms/acronyms_styles.lua b/_extensions/rchaput/acronyms/acronyms_styles.lua new file mode 100644 index 0000000..a843f28 --- /dev/null +++ b/_extensions/rchaput/acronyms/acronyms_styles.lua @@ -0,0 +1,134 @@ +--[[ + + This file defines the "styles" to replace acronyms. + + Such styles control how to use the acronym's short name, + long name, whether one should be between parentheses, etc. + + Styles are largely inspired from the LaTeX package "glossaries" + (and "glossaries-extra"). + A gallery of the their styles can be found at: + https://www.dickimaw-books.com/gallery/index.php?label=sample-abbr-styles + A more complete document (rather long) can be found at: + https://mirrors.chevalier.io/CTAN/macros/latex/contrib/glossaries-extra/samples/sample-abbr-styles.pdf + + More specifically, this file defines a table of functions. + Each function takes an acronym, and return one or several Pandoc elements. + These elements will replace the original acronym call in the Markdown + document. + + Most styles will depend on whether this is the acronym's first occurrence, + ("first use") or not ("next use"), similarly to the LaTeX "glossaries". + + For example, a simple (default) style can be to return the acronym's + long name, followed by the short name between parentheses. + When the parser encounters `\acr{RL}`, assuming that `RL` is correctly + defined in the acronyms database, the corresponding function would + return a Pandoc Link, where the text is "Reinforcement Learning (RL)", + and pointing to the definition of "RL" in the List of Acronyms. + + Note: the acronym's key MUST exist in the acronyms database. + Functions to replace a non-existing key must be handled elsewhere. + +--]] + +local Helpers = require("acronyms_helpers") + + +-- The table containing all styles, indexed by the style's name. +local styles = {} + + +-- Local helper function to create either a Str or a Link, +-- depending on whether we want to insert links. +local function create_element(content, key, insert_links) + if insert_links then + return pandoc.Link(content, Helpers.key_to_link(key)) + else + return pandoc.Str(content) + end +end + + +-- First use: long name (short name) +-- Next use: short name +styles["long-short"] = function(acronym, insert_links, is_first_use) + local text + if is_first_use then + text = acronym.longname .. " (" .. acronym.shortname .. ")" + else + text = acronym.shortname + end + + return create_element(text, acronym.key, insert_links) +end + + +-- First use: short name (long name) +-- Next use: short name +styles["short-long"] = function(acronym, insert_links, is_first_use) + local text + if is_first_use then + text = acronym.shortname .. " (" .. acronym.longname .. ")" + else + text = acronym.shortname + end + + return create_element(text, acronym.key, insert_links) +end + +-- First use: long name +-- Next use: long name +styles["long-long"] = function(acronym, insert_links) + local text + text = acronym.longname + + return create_element(text, acronym.key, insert_links) +end + +-- First use: short name [^1] +-- [^1]: short name: long name +-- Next use: short name +styles["short-footnote"] = function(acronym, insert_links, is_first_use) + if is_first_use then + -- The inline text (before the footnote) + local text = pandoc.Str(acronym.shortname) + -- We create a footnote, which must contain a Block with a Link and + -- the longname (as a simple text). + -- So we create a Pandoc Plain object to hold the link and text. + -- Directly using a list inside the Note seems not to work. + local note = pandoc.Note( + pandoc.Plain({ + create_element(acronym.shortname, acronym.key, insert_links), + pandoc.Str(": " .. acronym.longname) + }) + ) + -- We want to insert both the text and the footnote + return { text, note } + else + -- Simply return the shortname + return create_element(acronym.shortname, acronym.key, insert_links) + end +end + + +-- The "public" API of this module, the function which is returned by +-- require. +return function(acronym, style_name, insert_links, is_first_use) + -- Check that the requested strategy exists + assert(style_name ~= nil, + "[acronyms] The parameter style_name must not be nil!") + assert(styles[style_name] ~= nil, + "[acronyms] Style " .. tostring(style_name) .. " does not exist!") + + -- Check that the acronym exists + assert(acronym ~= nil, + "[acronyms] The acronym must not be nil!") + + -- Determine if it is the first use (if left unspecified) + if is_first_use == nil then + is_first_use = acronym:isFirstUse() + end + -- Call the style on this acronym + return styles[style_name](acronym, insert_links, is_first_use) +end diff --git a/_extensions/rchaput/acronyms/acronyms_translations.lua b/_extensions/rchaput/acronyms/acronyms_translations.lua new file mode 100644 index 0000000..77f8679 --- /dev/null +++ b/_extensions/rchaput/acronyms/acronyms_translations.lua @@ -0,0 +1,75 @@ +--[[ + Functions to translate some (hardcoded) sentences to the language + that is both available and closest to the user's preference. + + Matching algorithm in this file are based on RFC4647; languages are + based on RFC5646 (see Quarto's documentation about the `lang` option). +--]] + + +local Translations = { + -- Add custom translations here. + -- Each table refers to one given "sentence" to translate. + -- The table's contents must be the translations, indexed by a language. + -- Languages can be specified using subtags, for example, `en-GB` or `en-US`. + loa_title = { + [""] = "List Of Acronyms", -- Default value + ["en"] = "List Of Acronyms", + ["fr"] = "Liste des Acronymes", + } +} + + +-- Simplified Lookup, based on RFC4647; does not explicitly support private +-- subtags (`x-...`) but they should not be used in Quarto anyway. +-- `lang` must be the user's preferred language range (e.g., `zh-Hant-CN`). +-- `translations` must be a table, indexed by language tags. +-- Returns a table { lang, translation }, where `lang` is the closest language +-- found, and `translation` is the desired string in the found language. +function Translations:find_best(lang, translations) + quarto.log.debug("[acronyms] Request translation for ", lang) + + -- We will need to iterate over the subtags; for example, for `zh-Hant-CN`, + -- it should yield `zh-Hant-CN`, then `zh-Hant`, then `zh` (and finally ``). + -- This loop populates the table with `{ "", "zh", "zh-Hant", "zh-Hant-CN" }`. + local lang_components = {""} + local previous = "" + for component in string.gmatch(lang, "[^-]+") do + if previous == "" then + previous = component + else + previous = previous .. "-" .. component + end + table.insert(lang_components, previous) + end + + -- Now, we iterate over the (reversed) table, because we want to start with + -- the most specific lang. If this lang is found, we return it immediately. + for i = #lang_components, 1, -1 do + if translations[lang_components[i]] ~= nil then + local found_lang = lang_components[i] + local found_translation = translations[lang_components[i]] + quarto.log.debug("[acronyms] Found translation ", found_translation, + " for lang ", found_lang) + return { + ["lang"] = found_lang, + ["translation"] = found_translation + } + end + end + return nil +end + +function Translations:get_loa_title(lang) + local found = self:find_best(lang, self.loa_title) + if found == nil then + quarto.log.error( + "[acronyms] Could not find a suitable translation for ", lang, "!", + "Please ensure that a default translation is available for loa_title" + ) + assert(false) + end + return found["translation"] +end + +return Translations diff --git a/_extensions/rchaput/acronyms/parse-acronyms.lua b/_extensions/rchaput/acronyms/parse-acronyms.lua new file mode 100644 index 0000000..38ddda2 --- /dev/null +++ b/_extensions/rchaput/acronyms/parse-acronyms.lua @@ -0,0 +1,152 @@ +--[[ + Lua Filter to parse acronyms in a Markdown document. + + Acronyms must be in the form `\acr{key}` where key is the acronym key. + The first occurrence of an acronym is replaced by its long name, as + defined by a list of acronyms in the document's metadata. + Other occurrences are simply replaced by the acronym's short name. + + A List of Acronym is also generated (similar to a Glossary in LaTeX), + and all occurrences contain a link to the acronym's definition in this + List. +]] + +-- Some helper functions +local Helpers = require("acronyms_helpers") + +-- The Acronyms database +local Acronyms = require("acronyms") + +-- Sorting function +local sortAcronyms = require("sort_acronyms") + +-- Replacement function +local AcronymsPandoc = require("acronyms_pandoc") + +-- The options for the List Of Acronyms, as defined in the document's metadata. +local Options = require("acronyms_options") + + +--[[ + Parse the document's metadata, including options, and acronyms' definitions. + It does not change the metadata. +--]] +function Meta(m) + Options:parseOptionsFromMetadata(m) + + -- Parse acronyms directly from the metadata (`acronyms.keys`) + Acronyms:parseFromMetadata(m, Options["on_duplicate"]) + + -- Parse acronyms from external files + if (m and m.acronyms and m.acronyms.fromfile) then + if Helpers.isMetaList(m.acronyms.fromfile) then + -- We have several files to read + for _, filepath in ipairs(m.acronyms.fromfile) do + filepath = pandoc.utils.stringify(filepath) + Acronyms:parseFromYamlFile(filepath, Options["on_duplicate"]) + end + else + -- We have a single file + local filepath = pandoc.utils.stringify(m.acronyms.fromfile) + Acronyms:parseFromYamlFile(filepath, Options["on_duplicate"]) + end + end + + return nil +end + + +--[[ + Append the List Of Acronyms to the document (at the beginning). +--]] +function appendLoA(doc) + local pos + if not Options["insert_loa"] then + -- If disabled, do nothing + return nil + elseif Options["insert_loa"] == "beginning" then + -- Insert at the first block in the document + pos = 1 + elseif Options["insert_loa"] == "end" then + -- Insert at the last block in the document + pos = #doc.blocks + 1 + else + quarto.log.error( + "[acronyms] Unrecognized option `insert_loa`=`", + tostring(Options["insert_loa"]), + "` in `appendLoA`." + ) + assert(false) + end + + local header, definition_list = AcronymsPandoc.generateLoA() + + -- Insert the DefinitionList + table.insert(doc.blocks, pos, definition_list) + + -- Insert the Header + if header ~= nil then + table.insert(doc.blocks, pos, header) + end + + return pandoc.Pandoc(doc.blocks, doc.meta) +end + + +--[[ + Place the List of Acronyms in the document, in place of a `\printacronysm`. + + This is used when the user wants to generate the LoA at a very specific + place (instead of "simply" at the beginning or end). + Because the Header (title) and DefinitionList (LoA itself) are Blocks, + we must replace a Block as well (Pandoc does not allow to create Blocks + from Inlines). Thus, `\printacronyms` needs to be in its own Block (no + other text!). +--]] +function RawBlock(el) + -- The block's content must be exactly "\printacronyms" + if not (el and el.text == "\\printacronyms") then + return nil + end + + local header, definition_list = AcronymsPandoc.generateLoA() + + if header ~= nil then + return { header, definition_list } + else + return definition_list + end +end + + +--[[ +Replace each `\acr{KEY}` with the correct text and link to the list of acronyms. +--]] +function replaceAcronym(el) + local acr_key = string.match(el.text, "\\acr{(.+)}") + if acr_key then + -- This is an acronym, we need to parse it. + if Acronyms:contains(acr_key) then + -- The acronym exists (and is recognized) + return AcronymsPandoc.replaceExistingAcronym(acr_key) + else + -- The acronym does not exists + return AcronymsPandoc.replaceNonExistingAcronym(acr_key) + end + else + -- This is not an acronym, return nil to leave it unchanged. + return nil + end +end + + +-- Force the execution of the Meta filter before the RawInline +-- (we need to load the acronyms first!) +-- RawBlock and Doc happen after RawInline so that the actual usage order +-- of acronyms is known (and we can sort the List of Acronyms accordingly) +return { + { Meta = Meta }, + { RawInline = replaceAcronym }, + { RawBlock = RawBlock }, + { Pandoc = appendLoA }, +} diff --git a/_extensions/rchaput/acronyms/shortcodes_acronyms.lua b/_extensions/rchaput/acronyms/shortcodes_acronyms.lua new file mode 100644 index 0000000..02a4f08 --- /dev/null +++ b/_extensions/rchaput/acronyms/shortcodes_acronyms.lua @@ -0,0 +1,144 @@ +--[[ + Quarto Shortcodes to replace acronyms in a Quarto document. + + Acronyms must be in the form `{{< acr key >}}` where key is the acronym key. + The shortcode format (contrary to filters) allows specifying keyworded + arguments to better control the output (e.g., specific style, forcing first + or next occurrence, etc.). + + The List of Acronyms can also be generated with the `{{< printacronyms >}}` + shortcode. + + The filter (`parse-acronyms.lua`) remains useful even if only shortcodes + are used (i.e., no `\acr{key}`): it is responsible for parsing the metadata + once, and automatically appending the LoA (if the user sets the Options + accordingly). +]] + +-- Some helper functions +local Helpers = require("acronyms_helpers") + +-- The Acronyms database +local Acronyms = require("acronyms") + +-- Replacement functions +local AcronymsPandoc = require("acronyms_pandoc") + +-- The options for the List Of Acronyms, as defined in the document's metadata. +local Options = require("acronyms_options") + + +--[[ + Parse the value and return its string representation if it is not `""` + (the empty string), or `nil` otherwise. + + Keyworded arguments in Quarto shortcodes return an empty Inlines (`{}`) + if they are not specified, which makes it a bit harder to know whether + the argument was really supplied, or was just empty. +--]] +local function getOrNil (value) + -- Alternatively, we could check for `pandoc.utils.stringify(value) == ""` + -- But that does not seem very resilient for other corner cases. + -- For example, what if we want to specify the empty string as a value? + if value == pandoc.Inlines{} then + return nil + else + return pandoc.utils.stringify(value) + end +end + + +--[[ + Parse an optional value and convert it to a boolean if it was not `nil`. +--]] +local function getBooleanOrNil (value) + local value = getOrNil(value) + if value ~= nil then + value = Helpers.str_to_boolean(value) + end + return value +end + + +--[[ + Define the "main" shortcode behaviour: replacing an acronym. + + This function is associated to shortcodes `acronym` and `acr` so that it is + invoked through `{{< acr KEY >}}` or `{{< acronym KEY >}}`. +--]] +function replaceAcronym (args, kwargs, meta) + -- We want exactly 1 (unnamed) argument for the shortcode. + if #args == 0 or #args > 1 then + quarto.log.error( + "[acronyms] Incorrect number of arguments in shortcode `acronym`!\n", + "! Expected exactly 1 argument (the acronym key).\n", + "x Found ", tostring(#args), ".\n", + "i The arguments were: `", pandoc.utils.stringify(args), "`.\n" + ) + assert(false) + end + + local acronym_key = pandoc.utils.stringify(args[1]) + if Acronyms:contains(acronym_key) then + -- The acronym exists (and is recognized) + local style = getOrNil(kwargs["style"]) + local first_use = getBooleanOrNil(kwargs["first_use"]) + local insert_links = getBooleanOrNil(kwargs["insert_links"]) + return AcronymsPandoc.replaceExistingAcronym( + acronym_key, + style, + first_use, + insert_links + ) + else + -- The acronym does not exists + local non_existing = getOrNil(kwargs["non_existing"]) + return AcronymsPandoc.replaceNonExistingAcronym( + acronym_key, + non_existing + ) + end +end + + +--[[ + Generate the List of Acronyms in the document. +--]] +function generateListOfAcronyms (args, kwargs, meta) + if #args ~= 0 then + quarto.log.warning( + "[acronyms] Unused arguments passed to shortcode `printacronyms`:", + "expected 0, found", tostring(#args), "." + ) + end + + -- Parse (optional) keyworded-arguments + local sorting = getOrNil(kwargs["sorting"]) + local include_unused = getBooleanOrNil(kwargs["include_unused"]) + local title = getOrNil(kwargs["title"]) + local header_classes = getOrNil(kwargs["header_classes"]) + + local header, definition_list = AcronymsPandoc.generateLoA( + sorting, + include_unused, + title, + header_classes + ) + + if header ~= nil then + return { header, definition_list } + else + return definition_list + end +end + + +--[[ + Define the possible shortcodes for Quarto. +--]] +return { + ["acronym"] = replaceAcronym, + -- Same function but with a shorter name. + ["acr"] = replaceAcronym, + ["print-acronyms"] = generateListOfAcronyms, +} diff --git a/_extensions/rchaput/acronyms/sort_acronyms.lua b/_extensions/rchaput/acronyms/sort_acronyms.lua new file mode 100644 index 0000000..27c8a0a --- /dev/null +++ b/_extensions/rchaput/acronyms/sort_acronyms.lua @@ -0,0 +1,87 @@ +--[[ + + This file defines the sorting strategies. + + A sorting strategy (or comparator) is a function that receives 2 + acronyms, and returns a boolean which indicates whether the first + one should be before the second one (according to its criterion). + + Sorting strategies are leveraged using `table.sort(table, comp)` + where `comp` is one of these strategies. + +--]] + + +-- The table containing all the sorting strategies. +local sorting_strategies = {} + + +-- Sort acronyms by their shortname, in case-sensitive alphabetical order. +sorting_strategies["alphabetical"] = function(acronym1, acronym2) + -- TODO: check that this works with UTF-8 characters + return acronym1.shortname < acronym2.shortname +end + + +-- Sort acronyms by their shortname, in case-insensitive alphabetical order. +sorting_strategies["alphabetical-case-insensitive"] = function(acronym1, acronym2) + return acronym1.shortname:upper() < acronym2.shortname:upper() +end + + +-- Sort acronyms by their definition order (first to last). +sorting_strategies["initial"] = function(acronym1, acronym2) + return acronym1.definition_order < acronym2.definition_order +end + + +-- Sort acronyms by their usage order. +-- Unused acronyms must NOT be included! (Their order is `nil`) +sorting_strategies["usage"] = function(acronym1, acronym2) + return acronym1.usage_order < acronym2.usage_order +end + + +-- The "public" API, i.e., the function returned by `require`. +function sort_acronyms(acronyms, criterion, include_unused) + assert(acronyms ~= nil, + "[acronyms] The acronyms table must not be nil in sort_acronyms!") + assert(criterion ~= nil, + "[acronyms] The criterion must be not nil in sort_acronyms!") + local comparator = sorting_strategies[criterion] + if (comparator == nil) then + local msg = "[acronyms] Error when sorting acronyms:\n" + msg = msg .. "! Sorting criterion unrecognized: " .. tostring(criterion) .. "\n" + msg = msg .. "i Please check the `acronyms.sorting` metadata option.\n" + quarto.log.error(msg) + assert(false) + end + + -- Special rule: cannot use `usage` criterion if `include_unused` is true. + -- Otherwise, comparison of potentially nil values will crash. + if criterion == "usage" and include_unused then + local msg = "[acronyms] Error when sorting acronyms:\n" + msg = msg .. "! Cannot sort by `usage` when `include_unused` is true\n" + msg = msg .. "i Please set another sorting or set `include_unused` to `false`." + quarto.log.error(msg) + assert(false) + end + + -- The acronyms table is indexed by keys, not by ints. Thus, + -- we cannot use `ipairs` to walk over it in a specific order. + -- To sort the table, we need to create a second table first, + -- indexed by ints (i.e., a sequence). + local sequence_acronyms = {} + for _, acronym in pairs(acronyms) do + if include_unused or acronym.usage_order ~= nil then + table.insert(sequence_acronyms, acronym) + end + end + + -- Sort the keys according to the criterion + table.sort(sequence_acronyms, comparator) + + return sequence_acronyms +end + +return sort_acronyms diff --git a/_quarto.yml b/_quarto.yml index cdc348f..9de2d42 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -182,3 +182,13 @@ format: pdf: documentclass: scrbook papersize: a4 + +filters: + - acronyms + +acronyms: + include_unused: false + insert_links: false + fromfile: _acronyms.yml + insert_loa: "end" + loa_title: "" diff --git a/chapters/background/aerodrome.qmd b/chapters/background/aerodrome.qmd index f802ef2..a4e8a9f 100644 --- a/chapters/background/aerodrome.qmd +++ b/chapters/background/aerodrome.qmd @@ -4,7 +4,11 @@ output-file: aerodrome author: Manuel Waltert --- -In ICAO Annex 14, the term **aerodrome** is defined as *"a defined area on land or water (including any buildings, installations and equipment) intended to be used either wholly or in part for the arrival, departure and surface movement of aircraft."* The reference to a *defined area* suggests that aerodromes are systems, designed and operated for the purpose of both facilitating the efficient handling of departing and arrving aircraft as well as providing all the processes required by aircraft between take-off and landing. As such, aerodromes do not only have to be land-based. Rather, water-based as well as floating structures, such as oil rigs or ships, can be considered as aerodromes. Consequently, aerodromes are not solely designed for fixed-wing aircraft, but also for rotary-wing vehicles. +In \acr{ICAO} Annex 14, the term **aerodrome** is described as + +> a defined area on land or water (including any buildings, installations and equipment) intended to be used either wholly or in part for the arrival, departure and surface movement of aircraft. + +The reference to a *defined area* suggests that aerodromes are systems, designed and operated for the purpose of both facilitating the efficient handling of departing and arrving aircraft as well as providing all the processes required by aircraft between take-off and landing. As such, aerodromes do not only have to be land-based. Rather, water-based as well as floating structures, such as oil rigs or ships, can be considered as aerodromes. Consequently, aerodromes are not solely designed for fixed-wing aircraft, but also for rotary-wing vehicles. Since aerodromes have such a diverse range of applications, a distinction is made between different types of aerodromes. This typification often varies from country to country or region to region and (unfortunately) is not internationally standardised. For instance, an aerodrome designed for use of rotary-wing aircraft, i.e., helicopters, is called a **heliport**. A facility specifically designed for seaplanes and/or amphibian vehicles, which are aircraft able to land both on land and on water, is referred to as a **water aerodrome**. An aerodrome designed for the usage of small, often propeller-driven, general aviation aircraft, is called an **airstrip**, an **airfield**, or **small aerodrome**. Quite frequently, these type of aerodromes are equipped with a grass runway. An aerodrome used exclusively for military air operations is called a **military air base**. Finally, **civil airports** refer to facilities which are mainly available to commercial air transport. This refers to flight movements in which passengers (or cargo) are transported by an airline for a fee. For the remainder of this section, the focus is primarily on land-based **civil airports** designed for use by fixed-wing aircraft. @@ -15,7 +19,7 @@ i. are open to public use; ii. serve commercial air transport; and iii. have a paved instrument runway of 800 metres or more, or exclusively serve helicopters using instrument approach or departure procedures; -fall under the scope of the *Basic Regulation* (BR) of the **European Union Aviation Safety Agency** and its *Implementing Rules* (IR). These aerodromes are therefore subject to legally binding requirements that define how they must be designed, maintained, and operated. Aerodromes which handle no more than 10'000 commercial air transport passengers per year and no more than 850 movements related to cargo operations per year can be excempted from an applicablility of Regulation (EU) 2018/1139. For this reason, EASA publishes on its website [a list of aerodromes](https://www.easa.europa.eu/en/datasets/aerodromes-falling-scope-regulation-eu-20181139) specifying for which Regulation (EU) 2018/1139 is applicable and which aerodromes are exempted. +fall under the scope of the \acr{BR} of the \acr{EASA} and its \acr{IR}. These aerodromes are therefore subject to legally binding requirements that define how they must be designed, maintained, and operated. Aerodromes which handle no more than 10'000 commercial air transport passengers per year and no more than 850 movements related to cargo operations per year can be excempted from an applicablility of Regulation (EU) 2018/1139. For this reason, EASA publishes on its website [a list of aerodromes](https://www.easa.europa.eu/en/datasets/aerodromes-falling-scope-regulation-eu-20181139) specifying for which Regulation (EU) 2018/1139 is applicable and which aerodromes are exempted. Aerodromes falling under the scope of the *basic regulation* must be certified. In simple terms, in this certification procedure an aerodrome must show how and in what way the *certification specifications* of EASA are complied with or, if they cannot be complied with, what measures are/were taken to ensure an equivalent level of safety. The demonstration of an equivalent level of safety is particularly important for aerodromes that have grown over many decades and can only make certain changes to the facilities to ensure specifications under great financial constraints or not at all, e.g., due to topographical reasons. @@ -170,3 +174,7 @@ Types of taxiways: ### Landside components of an aerodrome + + + +## List of Acronyms {.unnumbered} diff --git a/chapters/background/aircraft.qmd b/chapters/background/aircraft.qmd index da230fc..7355085 100644 --- a/chapters/background/aircraft.qmd +++ b/chapters/background/aircraft.qmd @@ -6,7 +6,7 @@ output-file: aircraft ## Aircraft categorisation There exists a wide variety of air vehicles that can be grouped in broad categories. -ICAO refers to aircraft category as "a classification of aircraft according to specified characteristics". +\acr{ICAO} refers to aircraft category as "a classification of aircraft according to specified characteristics". The aircraft categorisation has relevance for the flight training and associated licensing, but also for the operation of the aircraft in terms of equipment, operating capabilities and limits, and access to airspace. Thus, aircraft categorisation comprises also the intended use and operating environment. @@ -94,7 +94,7 @@ Unfortunately, for the uninitiated, these terms are often used interchangeably, * track is the projected path of the aircraft on the surface of earth/map. * bearing is simply the direction between two points. A bearing can thus be expressed as to-a-point or from-a-point. For navigation purposes bearings can be expressed in relation to North. Sometimes bearings are expressed in relation to the heading, i.e., relative bearing. -It is important to note that heading, track, and bearing are referenced to North. Thus, based on the underlying reference, this could be magnetic, true, or based on the chart grid. In various parts of the world there is a substantial offset between the magnetic and true North dircetion and will need to be considered/corrected for the respective navigational task at hand. +It is important to note that heading, track, and bearing are referenced to North. Thus, based on the underlying reference, this could be magnetic, true, or based on the chart grid. In various parts of the world there is a substantial offset between the magnetic and true North direction that will need to be considered/corrected for the respective navigational task at hand. Since a magnetic compass is still an ultimate fallback instrument in aircraft, aircraft heading is predominantly used as magnetic heading. And accordingly, charts showing true North grids show also isogonic lines with the difference between true and magnetic North (i.e., variation) to account for the variation when determining positions or planning a flight. @@ -108,9 +108,9 @@ To support the flying task, navigational aids help pilots/aircrew to determine t For this purpose two additional terms are used to express the 'direction' of flight. * course is the desired dirction of flight. For a flight segment the course presents the bearing to the next way point. -* radial refers to the magnetic bearing from a navigation aid (e.g. VOR, TACAN), i.e., it reflects the magnetic bearing from the NAVAID to the aircraft. +* radial refers to the magnetic bearing from a navigation aid (e.g. \acr{VOR}, \acr{TACAN}), i.e., it reflects the magnetic bearing from the \acr{NAVAID} to the aircraft. -The latter can be confusing as a the direction of flight and its position in relation from (or to) a NAVAID may not coincide. +The latter can be confusing as a the direction of flight and its position in relation from (or to) a \acr{NAVAID} may not coincide. Even if an aircraft tracks on the radial of a NAVAID it may be flying to (inbound) or from (outbound) the station (on this radial). Absent of any wind influence, the (magnetic) heading would be the reverse course of a radial. If an aircraft flies towards a VOR on its (magnetic) radial 140, the (magnetic) compass heading reads 320. @@ -132,11 +132,15 @@ This section comprises an overview of a series of aircraft systems. ### Collision Avoidance ACAS/TCAS -In order to ensure the safe travel of aircraft a series of safety systems were developed. **ACAS** (airborne collision avoidance system) is a generic term to describe systems that track the surrounding air traffic on the basis of their tansponders. Dependent on the determined distance and relative range rate and approach speed, a potential risk of collision can be detected. +In order to ensure the safe travel of aircraft a series of safety systems were developed. \acr{ACAS} is a generic term to describe systems that track the surrounding air traffic on the basis of their tansponders. Dependent on the determined distance and relative range rate and approach speed, a potential risk of collision can be detected. -TCAS (traffic alert and collision avoidance system) is an implementation of the ICAO ACAS standard. To our knowledge, there are no other implementation of ACAS. TCAS II is mandated in Europe since the year 2000 and addresses changes to the logic of the detection and resolution logic. We consider therefore both terms as interchangable. +\acr{TCAS} is an implementation of the \acr{ICAO} \acr{ACAS} standard. To our knowledge, there are no other implementation of \acr{ACAS}. \acr{TCAS} II is mandated in Europe since the year 2000 and addresses changes to the logic of the detection and resolution logic. We consider therefore both terms as interchangable. -ACAS/TCAS issue two types of alerts: -* traffic advisory (TA) to support the pilot/aircrew to visually identify a potential conflicting flight -* resolution advisory (RA) is an avoidance maneuver. If both aircraft are equipped with an ACAS, the avoidance maneuvers are coordinated between the ACAS units via datalink. +\acr{ACAS}/\acr{TCAS} issue two types of alerts: + +* \acr{TA} to support the pilot/aircrew to visually identify a potential conflicting flight +* \acr{RA} is an avoidance maneuver. If both aircraft are equipped with an \acr{ACAS}, the avoidance maneuvers are coordinated between the \acr{ACAS} units via datalink. + + +## List of Acronyms {.unnumbered} diff --git a/chapters/background/airspace.qmd b/chapters/background/airspace.qmd index dfd1895..9a3f217 100644 --- a/chapters/background/airspace.qmd +++ b/chapters/background/airspace.qmd @@ -5,5 +5,9 @@ output-file: airspace * Definition of the term 'airspace' * Airspace classes (A, B, C, D, E, F, G), controlled vs. uncontrolled airspace -* Airspace around airports (TMAs, CTRs) +* Airspace around airports (\acr{TMA}, \acr{CTR}) + + + +## List of Acronyms {.unnumbered} diff --git a/chapters/background/atmosphere.qmd b/chapters/background/atmosphere.qmd index 5f76ffb..76927a7 100644 --- a/chapters/background/atmosphere.qmd +++ b/chapters/background/atmosphere.qmd @@ -4,10 +4,10 @@ author: David Gianazza --- -The elements presented in this section are mainly taken from the ICAO -(International Civil Aviation Organization) Standard Atmosphere Manual, -and the revised atmosphere model [@ATMORev] used in the Eurocontrol BADA -(Base of Aircraft Data) performance model. +The elements presented in this section are mainly taken from the \acr{ICAO} + Standard Atmosphere Manual, +and the revised atmosphere model [@ATMORev] used in the Eurocontrol \acr{BADA} + performance model. ## The International Standard Atmosphere (ISA) {#sect:ISA} @@ -368,3 +368,7 @@ by @eq-TEMP_HP. [^1]: Note that the pressure altitude and geopotential altitude are the same, when the atmosphere is standard + + +## List of Acronyms {.unnumbered} + diff --git a/chapters/background/cns.qmd b/chapters/background/cns.qmd index 72d927c..5b93b63 100644 --- a/chapters/background/cns.qmd +++ b/chapters/background/cns.qmd @@ -7,11 +7,11 @@ Merriam-Webster defines the term **airspace** as "the space lying above the eart According to the [Chicago Convention on International Civil Aviation](https://www.icao.int/publications/Documents/7300_cons.pdf) of 1944, every state "has complete and exclusive sovereignty over the airspace above its territory". Horizontally, a state's airspace extends over its entire territoriy, which also include territorial waters. According to the [United Nations Convention on the Law of the Sea](https://www.un.org/Depts/los/convention_agreements/texts/unclos/unclos_e.pdf), territorial waters extend up to 12 nautical miles (22.2 km) from a state's coastline. Vertically, a state's airspace begins at the earth's surface and ends at an altitude of 100 km above mean sea level, which is also known as the [Kármán-Line](https://en.wikipedia.org/wiki/K%C3%A1rm%C3%A1n_line). The Kármán-Line - as a legal differentiation - separates the Earth's atmosphere from space. -According to the Chicago Convention and its subsidary framework, next to the soverignty clause, States have the responsibility to establish air navigation services (ANS) under Article 28(a) of the convention. +According to the Chicago Convention and its subsidary framework, next to the soverignty clause, each State has the responsibility to establish an \acr{ANS} under Article 28(a) of the convention. For this purpose, States will introduce an airspace organisation , associated rules of the air, and assign the provision of these services to appropriate entities. -In order to manage an airspace, a state usually commissions a so-called **air navigation service provider** (ANSP), which is a public or private entity that offers so-called **air navigation services**. +In order to manage an airspace, a state usually commissions a so-called \acr{ANSP}, which is a public or private entity that offers so-called **air navigation services**. **Air navigation services** facilitate the 'safe, efficient, and orderly flow of air traffic' and comprise of the following five groups of services: @@ -268,3 +268,6 @@ However, not until the Second World War, was the concept of RAdio Detection And [Annex_15]: https://www.bazl.admin.ch/dam/bazl/en/dokumente/Fachleute/Regulationen_und_Grundlagen/icao-annex/icao_annex_15_aeronauticalinformationservices.pdf.download.pdf/an15_cons.pdf + + +## List of Acronyms {.unnumbered} diff --git a/chapters/background/earth.qmd b/chapters/background/earth.qmd index ebdae88..06d75eb 100644 --- a/chapters/background/earth.qmd +++ b/chapters/background/earth.qmd @@ -28,7 +28,7 @@ simplicity's sake. ## The Spherical Earth Model -### The Earth-Centered, Earth-Fixed (ECEF) Coordinate System +### The \acr{ECEF} Coordinate System The Earth is modeled here as a sphere of radius $R_T$. Let us define a reference frame fixed to the Earth and for which the axis $z_e$ passes @@ -41,7 +41,7 @@ center. In the following, we shall denote $(\overrightarrow{\imath}_e, \overrightarrow{\jmath}_e, \overrightarrow{k}_e)$ -the orthonormal vectors of the ECEF reference frame $Ox_ey_ez_e$. +the orthonormal vectors of the \acr{ECEF} reference frame $Ox_ey_ez_e$. ![Spherical Earth model](../../images/earth_model/Terre_spherique.jpg){#fig-spheric_earth width="8cm"} @@ -53,12 +53,12 @@ plane $x_eOy_e$ and $\overrightarrow{OP}$. The longitude, denoted $\lambda$, is the angle between the Greenwich meridian plane $x_eOz_e$ and the meridian plane containing $\overrightarrow{OP}$. -### The North-East-Down (NED) reference frame +### The \acr{NED} reference frame For given point $P$, located on or at proximity of the Earth's surface, -let us define another system of axes, called the NED (North, East, Down) +let us define another system of axes, called the \acr{NED} system, or the local horizontal reference frame centered on point $P$. -In this NED frame, the $x_h$ axis is in the local horizontal plane and +In this \acr{NED} frame, the $x_h$ axis is in the local horizontal plane and passes through $P$, pointing to the North. The $y_h$ axis is also in the horizotal plane and passes through $P$, but it points to the East. Finally, the vertical axis $z_h$ passes through $P$ and points downward, @@ -69,13 +69,13 @@ An orthonormal basis of vectors $(\overrightarrow{\imath}_h, \overrightarrow{\jmath}_h, \overrightarrow{k}_h$) is associated to this system. -![North, East, Down (NED) reference frame, on a spherical earth](../../images/earth_model/horizontal_frame.jpg){#fig-NED width="8cm"} +![\acr{NED} reference frame, on a spherical earth](../../images/earth_model/horizontal_frame.jpg){#fig-NED width="8cm"} ### Coordinates of a Point on the Sphere -The Cartesian coordinates -- in the ECEF (Earth-Centered, Earth-Fixed) +The Cartesian coordinates -- in the \acr{ECEF} system -- of a point $P$ at altitude $h$ are given by @eq-coor_sphere: $$ @@ -87,7 +87,7 @@ z_e= (R_T+h) \sin \mu \end{array} $$ {#eq-coor_sphere} -In the NED (North, East, Down) system where the unit vector +In the \acr{NED} system where the unit vector $\overrightarrow{k}_h$ points downward from $P$, the position vector can be expressed very simply by @eq-position_NED_sphere, and the coordinates by @eq-coor_NED_sphere. @@ -104,18 +104,18 @@ z_h= - (R_T+h) \end{array} $$ {#eq-coor_NED_sphere} -### Velocity of a Point with Respect to the ECEF Frame +### Velocity of a Point with Respect to the \acr{ECEF} Frame Let $Ox_my_mz_e$ be the orthonormed direct system such that $x_mOz_e$ is the meridian plane containing $P$, the position of the mobile agent. -This referential is obtained simply by rotating the ECEF axes +This referential is obtained simply by rotating the \acr{ECEF} axes $Ox_ey_ez_e$ of an angle $\lambda$ around the axis of the poles $z_e$. Let $(\overrightarrow{\imath}_m, \overrightarrow{\jmath}_m, \overrightarrow{k}_e)$ be the orthonormal basis associated with the reference frame fixed too the meridian plane passing through $P$. -The velocity with respect to the ECEF reference frame, considered as +The velocity with respect to the \acr{ECEF} reference frame, considered as fixed, is given in @eq-speed_sphere, taking into account the angular speed $\overrightarrow{\Omega}_{Ox_my_mz_e/\mathit{ECEF}} = \dot{\lambda} \overrightarrow{k}_e$ @@ -138,7 +138,7 @@ $$ \frac{d\overrightarrow{OP}}{dt}_{/Ox_my_mz_e} = -(R_T+h)\frac{d\mu}{dt}\frac{d\overrightarrow{k}_h}{d\mu} - \dot{h} \overrightarrow{k}_h = \dot{\mu}(R_T+h) \overrightarrow{\imath}_h - \dot{h} \overrightarrow{k}_h $$ {#eq-deriv_position_NED_sphere} -By combining @eq-deriv_position_NED_sphere and @eq-speed_sphere, the velocity with respect to the ECEF reference frame is then expressed (see @eq-deriv_vect_op) in a simple form in the basis of vectors $(\overrightarrow{\imath}_h, \overrightarrow{\jmath}_h, \overrightarrow{k}_h)$ associated with the local horizontal frame NED. +By combining @eq-deriv_position_NED_sphere and @eq-speed_sphere, the velocity with respect to the \acr{ECEF} reference frame is then expressed (see @eq-deriv_vect_op) in a simple form in the basis of vectors $(\overrightarrow{\imath}_h, \overrightarrow{\jmath}_h, \overrightarrow{k}_h)$ associated with the local horizontal frame \acr{NED}. $$ \begin{aligned} @@ -149,8 +149,7 @@ $$ {#eq-deriv_vect_op} Adopting the slightly unwieldy but explicit notation convention where "$/\mathit{ECEF}$" means *"with respect to the Earth's reference -frame"* and $\{.\}_{\mathit{NED}}$ means *"in the NED (North, East, -Down) coordinate system"*, the coordinates of the velocity with respect +frame"* and $\{.\}_{\mathit{NED}}$ means *"in the \acr{NED} coordinate system"*, the coordinates of the velocity with respect to the Earth are given by @eq-coor_deriv_vect_op. $$ @@ -342,12 +341,12 @@ $$ %)^{\frac{1}{2}}} $$ {#eq-ellipsoid_normal_distance} -### The ECEF and Other Reference Frames for the Ellipsoid of Revolution +### The \acr{ECEF} and Other Reference Frames for the Ellipsoid of Revolution -![Earth-Centered, Earth-Fixed (ECEF) and mobile reference frames, on the ellipsoid of revolution](../../images/earth_model/Reperes_ellipsoide.jpg){#fig-ellipsoid_ref_frames width="8cm"} +![\acr{ECEF} and mobile reference frames, on the ellipsoid of revolution](../../images/earth_model/Reperes_ellipsoide.jpg){#fig-ellipsoid_ref_frames width="8cm"} -In the case of the ellipsoid of revolution, we use an ECEF -(Earth-Centered, Earth-Fixed) reference frame similar to the spherical +In the case of the ellipsoid of revolution, we use an \acr{ECEF} + reference frame similar to the spherical model. Its system of axes is noted here $Ox_ey_ez_e$, with a base of orthonormal vectors $(\overrightarrow{\imath}_e, \overrightarrow{\jmath}_e, \overrightarrow{k}_e)$. @@ -359,7 +358,7 @@ The vectors $\overrightarrow{\imath}_m$ and $\overrightarrow{k}_e$ are thus in the meridian plane passing through $N$ (and $P$). We will denote $Ox_my_mz_e$ the associated axis system. -The local horizontal reference frame NED (North, East, Down), with +The local horizontal reference frame \acr{NED}, with orthonormal base $(\overrightarrow{\imath}_h, \overrightarrow{\jmath}_h, \overrightarrow{k}_h)$, is similar to the one of the spherical model, except that the vertical @@ -421,7 +420,7 @@ z_e= \left[\mathcal{N} (1-e^2)+h\right]\sin \mu \end{array} $$ {#eq-coor_ellipsoid_ECEF_P} -### Velocity of a Point with Respect to the ECEF Frame +### Velocity of a Point with Respect to the \acr{ECEF} Frame $$ \frac{d\overrightarrow{ON}}{dt}_{/Ox_my_mz_e} @@ -518,3 +517,6 @@ $$ \boxed{R_P = \mathcal{N}\cos\mu = \frac{a\cos\mu}{\sqrt{1-e^2 \sin ^2 \mu}}} %)^{\frac{1}{2}}} $$ {#eq-parallel_radius} + + +## List of Acronyms {.unnumbered} diff --git a/chapters/background/history.qmd b/chapters/background/history.qmd index 4cd5f45..992e4fb 100644 --- a/chapters/background/history.qmd +++ b/chapters/background/history.qmd @@ -61,7 +61,7 @@ In the United States, the Air Mail Act of 1925 and the Air Commerce Act of 1926 The first successful non-stop flight over the Atlantic Ocean took place in May 1927. American aviator Charles Lindbergh accomplished this feat by flying solo in a custom-built, single-engine monoplane named the _Spirit of St. Louis_. Lindbergh departed from Roosevelt Field in New York and landed in Le Bourget Field near Paris, France. In June 1928, Amelia Earhart made history by becoming the first woman to fly across the Atlantic, albeit as a passenger rather than as the pilot. The flight took place from Newfoundland, Canada, to Wales, and Earhart gained significant recognition for her participation in this groundbreaking journey. In May 1932, she became the first woman to fly solo non-stop across the Atlantic Ocean. Amelia Earhart disappeared in 1937, during an attempt to circumnavigate the globe. She and her navigator were en route over the Pacific Ocean when communication with them was lost. Despite extensive search efforts, they were never found, and their fate remains a mystery. -Technological advancements in aviation were profound during this time. The development of jet engines by Hans von Ohain and Sir Frank Whittle revolutionized aviation, leading to faster and more efficient aircraft. In the 1920s, Jimmy Doolittle became interested in instrument flying, which involved using cockpit instruments to navigate and control an aircraft instead of relying solely on visual cues. One of his notable accomplishments was the invention of the artificial horizon and directional gyroscope, which provided pilots with crucial information about the aircraft's attitude and heading. Doolittle advocated for the integration of instrument flying into pilot training and aviation practices. His efforts led to the establishment of instrument flight rules (IFR) and the implementation of instrument training programs in aviation. +Technological advancements in aviation were profound during this time. The development of jet engines by Hans von Ohain and Sir Frank Whittle revolutionized aviation, leading to faster and more efficient aircraft. In the 1920s, Jimmy Doolittle became interested in instrument flying, which involved using cockpit instruments to navigate and control an aircraft instead of relying solely on visual cues. One of his notable accomplishments was the invention of the artificial horizon and directional gyroscope, which provided pilots with crucial information about the aircraft's attitude and heading. Doolittle advocated for the integration of instrument flying into pilot training and aviation practices. His efforts led to the establishment of \acr{IFR} and the implementation of instrument training programs in aviation. As the world moved closer to World War II, rapid advancements in aircraft design, such as the introduction of monoplane fighters and strategic bombers, transformed aerial warfare. Notable aircraft of the era included the Supermarine Spitfire and the Messerschmitt Bf 109, which would become iconic symbols of aerial combat during the war. @@ -93,8 +93,13 @@ The last quarter of the 20th century saw a change of emphasis, with no significa One notable development during this period was the widespread adoption of digital flight management systems (FMS) in commercial aircraft. The Boeing 767 was the first to introduce these computer-based systems, which replaced traditional analog instruments. FMS allowed for more precise navigation, automated flight planning, and improved aircraft performance. The integration of FMS into cockpits paved the way for increased safety, efficiency, and reliability in aviation operations. -The use of GPS (*Global Positioning System*) also played a crucial role in transforming aviation. Following the incident of Korean Air Flight 007 in 1983, where the aircraft from New York to Seoul via Anchorage was shot down after deviating from its intended route, there was a renewed emphasis on enhancing navigation and surveillance capabilities. GPS technology provided a highly accurate and reliable means of determining an aircraft's position, velocity, and time. Its widespread adoption in the 1980s and 1990s greatly improved aircraft navigation, enabling precise route planning, automated guidance systems, and enhanced situational awareness for pilots. +The use of \acr{GPS} also played a crucial role in transforming aviation. Following the incident of Korean Air Flight 007 in 1983, where the aircraft from New York to Seoul via Anchorage was shot down after deviating from its intended route, there was a renewed emphasis on enhancing navigation and surveillance capabilities. GPS technology provided a highly accurate and reliable means of determining an aircraft's position, velocity, and time. Its widespread adoption in the 1980s and 1990s greatly improved aircraft navigation, enabling precise route planning, automated guidance systems, and enhanced situational awareness for pilots. Additionally, this period saw the emergence of more advanced air traffic management systems. New technologies were implemented, allowing for better monitoring and management of air traffic based on radar and data processing capabilities. These systems facilitated more efficient routing, reduced congestion, and enhanced safety in busy airspace. The digitalization of air traffic management systems played a key role in accommodating the increasing volume of air traffic and ensuring smooth operations in the rapidly evolving aviation landscape. This book delves into the details of these technologies and explains how to make the most of collected data in aviation and air traffic management. + + + +## List of Acronyms {.unnumbered} + diff --git a/chapters/index.qmd b/chapters/index.qmd index b22b529..05298aa 100644 --- a/chapters/index.qmd +++ b/chapters/index.qmd @@ -7,6 +7,8 @@ listing: id: reference-links template: ../ejs/link.ejs contents: index.yml +acronyms: + insert_loa: false --- # Table of contents {-} diff --git a/index.qmd b/index.qmd index b0d7623..5cad51e 100644 --- a/index.qmd +++ b/index.qmd @@ -1,4 +1,6 @@ --- +acronyms: + insert_loa: false resources: /images/aircraft_vintage.webp css: media/css/index.css --- diff --git a/preface.qmd b/preface.qmd index 19b1d51..2f7b509 100644 --- a/preface.qmd +++ b/preface.qmd @@ -1,15 +1,22 @@ --- output-file: preface +acronyms: + include_unused: false + insert_links: false + insert_loa: "end" + loa_title: "" + fromfile: + - _acronyms.yml --- -# Preface {-} +# Preface {.unnumbered} About a century after the invention of powered flight, aviation has slowly become a vital element of everyday life. While pioneers and flying aces build the collective imaginary around the early days of aviation, technical advances around surveillance systems, the use of -radar in civil aviation in the 1950s, the generalisation of GPS for -civil applications in the 1980s and the ADS-B mandate emerging in the +radar in civil aviation in the 1950s, the generalisation of \acr{GPS} for +civil applications in the 1980s and the \acr{ADS-B} mandate emerging in the 2000s make the use of data in aviation an interesting field of research for many disciplines. In particular, such effort has been justified by the historic growth of traffic from the early 2000s, and new challenges @@ -120,3 +127,6 @@ sharing. Data curation is often a very time consuming process and enriching data by labelling specific tags or merging several sources of information brings additional value to a dataset. This part deals with the data sharing and publication process. (paper reproducibility?) + + +## List of Acronyms {.unnumbered}