--- /dev/null
+/build
+/public
--- /dev/null
+[submodule "书社"]
+ path = .⛩️📰
+ url = https://git.ladys.computer/Shushe.git
--- /dev/null
+Subproject commit 2a4b2ff1cb88305fb662deda701ee23afbf56498
--- /dev/null
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
--- /dev/null
+SHELL = /bin/sh
+
+# ━ § BASIC INFORMATION ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+override define makefileinfo
+ ╭──────────────────────────────╮
+╔╡ ⁌ 📰 Caudex ∷ GNUmakefile ╞═══════════════════════════════╗
+║╰──────────────────────────────╯ ║
+╟┬ ¶ Prerequisites ───────────────────────────────────────────┬╢
+║│ │║
+║│ Requires G·N·U Make, at least version 3.81, and all of the │║
+║│ requirements of ⛩️📰 书社, which is included as a git │║
+║│ submodule. Run `make help-shushe´ for more information. │║
+║╰────────────────────────────────────────────────────────────╯║
+╟┬ ¶ Usage ───────────────────────────────────────────────────┬╢
+║│ │║
+║│ • `make all´ (default): Compile, but do not install, all │║
+║│ files. │║
+║│ │║
+║│ • `make clean´: Remove `BUILDDIR´. │║
+║│ │║
+║│ • `make clean´: Remove `BUILDDIR´. │║
+║│ │║
+║│ • `make gone´: Remove `BUILDDIR´ and installed files. │║
+║│ │║
+║│ • `make help´: Print this message. │║
+║│ │║
+║│ • `make install´: Compile all files and install in │║
+║│ `DESTDIR´. │║
+║│ │║
+║│ • `make list´: List all recognized source files and their │║
+║│ classification (including media type and dependencies). │║
+║│ │║
+║│ • `make uninstall´: Remove installed files, but not │║
+║│ `BUILDDIR´. │║
+║│ │║
+║│ • `make +⟨CATEGORY⟩´: Create a new entry in ⟨CATEGORY⟩ and │║
+║│ output its path. │║
+║│ │║
+║│ Set `VERBOSE=1´ to see the text of commands as they are │║
+║│ executed. │║
+║│ │║
+║│ See `README.markdown´ for a more involved description of │║
+║│ the capabilities and configuration of this program. │║
+║╰────────────────────────────────────────────────────────────╯║
+╟┬ ¶ Copyright & License ─────────────────────────────────────┬╢
+║│ │║
+║│ Copyright © 2024 Lady [@ Lady’s Computer]. │║
+║│ │║
+║│ This Source Code Form is subject to the terms of the │║
+║│ Mozilla Public License, v 2.0. If a copy of the M·P·L was │║
+║│ not distributed with this file, You can obtain one at │║
+║│ <http://mozilla.org/MPL/2.0/>. │║
+║╰────────────────────────────────────────────────────────────╯║
+╚══════════════════════════════════════════════════════════════╝
+endef
+
+# ━ § MAKE·FILE SETUP ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+# Programs needed to run this make·file.
+#
+# If these are not installed on your computer, or you need to use a different implementation, you can override the appropriate variable.
+#
+# See also the documentation for ⛩️📰 书社, which specifies additional programs.
+AWK := awk
+CAT := cat
+ECHO := echo
+GIT := git
+MKDIR := mkdir
+OD := od
+PRINTF := printf
+RM := rm
+SED := sed
+TEST := test
+TR := tr
+XARGS := xargs
+XMLCATALOG := xmlcatalog
+
+# The directory which contains the codex entries and related metadata.
+SRCDIR := entries
+
+# The directory which contains additional assets for the codex generator.
+ASSETDIR := assets
+
+# The directory which contains includes for additional assets for the codex generator.
+ASSETINCLUDEDIR := $(ASSETDIR)/includes
+
+# The directory in which to generate temporary buildfiles.
+BUILDDIR := build
+
+# The directory into which to output files on `make´.
+DESTDIR := public
+
+# The location of this Makefile (and related ⛩️📰 书社 files), relative to the current working directory.
+#
+# By default, this is inferred from the variable `MAKEFILE_LIST´.
+THISDIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
+
+# Extra magic files to forward to ⛩️📰 书社.
+#
+# Your computer probably has several already installed at `/usr/share/file/magic´.
+EXTRAMAGIC :=
+
+# Extra parsers to forward to ⛩️📰 书社.
+#
+# Which parsers are provided will influence which kinds of files are recognized as plaintext.
+EXTRAPARSERS :=
+
+# Extra transforms to forward to ⛩️📰 书社.
+EXTRATRANSFORMS :=
+
+# Variables to communicate to further calls to Make.
+#
+# By default, `MAKEOVERRIDES´ contains all variable overrides specified on the commandline.
+# However, some values are specific to 📰 Caudex and may have different meaning in other make·files; filter these out.
+MAKEOVERRIDES := $(filter-out SRCDIR=% ASSETDIR=% BUILDDIR=% DESTDIR=% THISDIR=% EXTRAMAGIC=% EXTRAPARSERS=% EXTRATRANSFORMS=%,$(MAKEOVERRIDES))
+
+# The location of ⛩️📰 书社.
+SHUSHE := $(THISDIR)/.⛩️📰
+
+# The name of the generator program.
+GENERATOR := 📰 Caudex
+
+# A description of the current git revision of 📰 Caudex.
+VERSION := $(shell cd $(THISDIR); $(GIT) describe 2> /dev/null || $(GIT) rev-parse HEAD 2> /dev/null || true)
+
+# Set to a non·empty value to print all commands as they run.
+VERBOSE :=
+
+# The default target for this makefile.
+.DEFAULT_GOAL := all
+
+# ━ § BEGIN MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+# ─ ¶ Non‐Recipe Variable Definitions ─────────────────────────────────
+
+# A variable which contains a newline, to allow the generation of multiline strings in function calls.
+override define newline
+
+
+endef
+
+# (callable) Quote the given string for use within shell calls.
+override quote = '$(subst ','"'"',$1)'
+
+# Outputs an `@´ to silence rules, unless `VERBOSE´ is nonempty.
+override silent := $(if $(VERBOSE),,@)
+
+# The list of categories, determined by the set of directories in `SRCDIR´ with corresponding metadata files.
+override categories := $(sort $(patsubst $(SRCDIR)/%/@,%,$(wildcard $(SRCDIR)/*/@)))
+
+# (callable) The set of file locations for the entries of the provided categories.
+override entriesforcategory = $(sort $(foreach category,$1,$(wildcard $(SRCDIR)/$(category)/???-????) $(wildcard $(SRCDIR)/$(category)/???-????,*)))
+
+# A random W·R·M·G(·like) base⹀32 identifier.
+#
+# This variable returns a different value each time it is referenced.
+# Use `foreach´ to save the value for repeated usage.
+#
+# This variable recursively calls itself to ensure there is no existing entry with the resulting identifier.
+override wrmg = $(foreach attempt,$(shell $(OD) -t u4 -N 4 /dev/random | $(SED) 's/^[0-9]* *//' | $(TR) -d ' \n' | $(AWK) 'BEGIN{chars="0123456789ABCDEFGHJKMNPQRSTVWXYZ&~@=U"}{d=32^5;r=$$0%(32^6);printf("%s",substr(chars,r%37,1));while(d>0){printf("%s", substr(chars,int(r/d),1));if(d==32^4)printf("%s", "-");r%=d;d=int(d/32)}}'),$(if $(wildcard $(SRCDIR)/*/($(attempt))*),$(call wrmg),$(attempt)))
+
+# (callable) Returns the identifiers of the provided entries.
+override identifier = $(shell $(TR) ' ' '\n' <<< $(call quote,$1) | $(SED) 's/^\(.*[/]\)\{0,1\}\(.\{3\}-.\{4\}\)\(,[^/]*\)\{0,1\}$$/\2/')
+
+# Output locations for compiled catalog files for categories.
+override categorycatalogs := $(patsubst %,$(BUILDDIR)/catalogs/categories/%/index,$(categories))
+
+# Output locations for compiled catalog files for entries.
+override entrycatalogs := $(foreach entry,$(call entriesforcategory,$(categories)),$(patsubst $(SRCDIR)/%,$(BUILDDIR)/catalogs/entries/%,$(entry)))
+
+# Output locations for all compiled catalog files.
+override allcatalogs := $(BUILDDIR)/catalogs/indices/index $(BUILDDIR)/catalogs/indices/fullindex $(categorycatalogs) $(entrycatalogs)
+
+# (overridable) Options to pass to ⛩️📰 书社.
+override shusheopts := SRCDIR=$(call quote,$(if $(wildcard $(ASSETDIR)),$(ASSETDIR),) $(if $(entrycatalogs),$(BUILDDIR)/catalogs/entries,) $(BUILDDIR)/catalogs/indices) BUILDDIR=$(call quote,$(BUILDDIR)/⛩️📰) INCLUDEDIR=$(call quote,$(SRCDIR) $(if $(wildcard $(ASSETINCLUDEDIR)),$(ASSETINCLUDEDIR),) $(if $(categorycatalogs),$(BUILDDIR)/catalogs/categories,)) DESTDIR=$(call quote,$(DESTDIR)) EXTRAMAGIC=$(call quote,$(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/magic/*)) $(EXTRAMAGIC))) EXTRAPARSERS=$(call quote,$(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/parsers/*.xslt)) $(EXTRAPARSERS))) EXTRATRANSFORMS=$(call quote,$(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/transforms/*.xslt)) $(EXTRATRANSFORMS))) GENERATOR=$(call quote,$(GENERATOR)) VERSION=$(call quote,$(VERSION))
+
+# ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
+
+# (callable) Check to see if the given directory exists and create it if not.
+override ensuredirectory = if $(TEST) ! -d $(call quote,$1); then $(MKDIR) -p $(call quote,$1); fi
+
+# (callable) Test to see if the dependencies provided by the second argument matches the value in the file corresponding to the first argument in `$(BUILDDIR)/lastprereqs´.
+# If not, return the third argument, plus an additional rule to save the new prerequisites.
+override makeandsavedeps = $(if $(subst $(shell $(CAT) $(call quote,$(BUILDDIR)/lastdeps/$1) 2> /dev/null || true),,$2),$3$(newline)$(silent)$(call ensuredirectory,$(BUILDDIR)/lastdeps)$(newline)$(silent)$(PRINTF) '%s\n' $(call quote,$2) > $(BUILDDIR)/lastdeps/$1,)
+
+# (callable) Make the catalog for the provided category.
+override define makecategorycatalog
+@$(PRINTF) '%s\n' $(call quote,Generating catalog for category `$1´…)
+$(silent)$(call ensuredirectory,$(BUILDDIR)/catalogs/categories/$1)
+$(silent)$(XMLCATALOG) --create --noout $(call quote,$(BUILDDIR)/catalogs/categories/$1/index)
+$(silent)$(XMLCATALOG) --add uri '?' 'about:category' --noout $(call quote,$(BUILDDIR)/catalogs/categories/$1/index)
+$(silent)$(XMLCATALOG) --add uri '@' $(call quote,$1/@) --noout $(call quote,$(BUILDDIR)/catalogs/categories/$1/index)
+$(foreach entry,$(call entriesforcategory,$1),$(silent)$(XMLCATALOG) --add uri $(call quote,$(call identifier,$(entry))) $(call quote,$(patsubst $(SRCDIR)/%,%,$(entry))) --noout $(call quote,$(BUILDDIR)/catalogs/categories/$1/index)$(newline))
+endef
+
+# (callable) Make the catalog for the dynamic or static index.
+override define makeindexcatalog
+@$(PRINTF) '%s\n' 'Generating $(if $1,static,dynamic) catalog index…'
+$(silent)$(call ensuredirectory,$(BUILDDIR)/catalogs/indices)
+$(silent)$(XMLCATALOG) --create --noout $(call quote,$(BUILDDIR)/catalogs/indices/$(if $1,full,)index)
+$(silent)$(XMLCATALOG) --add uri '.' '$(if $1,standalone,index).xhtml' --noout $(call quote,$(BUILDDIR)/catalogs/indices/$(if $1,full,)index)
+$(silent)$(XMLCATALOG) --add uri '?' 'about:$(if $1,full,)index' --noout $(call quote,$(BUILDDIR)/catalogs/indices/$(if $1,full,)index)
+$(silent)$(XMLCATALOG) --add uri '@' '@' --noout $(call quote,$(BUILDDIR)/catalogs/indices/$(if $1,full,)index)
+$(foreach category,$(categories),$(silent)$(XMLCATALOG) --add uri $(call quote,$(category)) $(call quote,$(category)/index) --noout $(call quote,$(BUILDDIR)/catalogs/indices/$(if $1,full,)index)$(newline))
+endef
+
+# ─ ¶ ⛩️📰 书社 Targets ──────────────────────────────────────────────
+
+# Targets to forward to ⛩️📰 书社.
+all install list uninstall : $(SHUSHE)/GNUmakefile catalogs
+ $(silent)$(MAKE) -f $(call quote,$<) $(call quote,$@) $(shusheopts)
+%-shushe : $(SHUSHE)/GNUmakefile FORCE
+ $(silent)$(MAKE) -f $(call quote,$<) $(call quote,$*) $(shusheopts)
+build/⛩️📰/% : $(SHUSHE)/GNUmakefile catalogs FORCE
+ $(SILENT)$(MAKE) -f $(call quote,$<) $(call quote,$@) $(shusheopts)
+public/% : $(SHUSHE)/GNUmakefile catalogs FORCE
+ $(SILENT)$(MAKE) -f $(call quote,$<) $(call quote,$@) $(shusheopts)
+
+# ─ ¶ Phony Targets ───────────────────────────────────────────────────
+
+# Generate all catalogs, then remove any which no longer have corresponding source files.
+catalogs : $(allcatalogs)
+ @$(PRINTF) '%s\n' 'Removing any outdated catalogs…'
+ $(foreach deletedcategory,$(filter-out $(categorycatalogs),$(wildcard $(BUILDDIR)/catalogs/categories/*/index)),@$(PRINTF) '%s\n' $(call quote,Removing `$(patsubst $(BUILDDIR)/catalogs/categories/%/index,%,$(deletedcategory))´…)$(newline)$(silent)$(RM) -rf $(call quote,./$(dir $(deletedcategory)))$(newline)$(silent)$(RM) -rf $(call quote,./$(patsubst $(BUILDDIR)/catalogs/categories/%,$(BUILDDIR)/catalogs/entries/%,$(dir $(deletedcategory))))$(newline))
+ $(foreach deletedentry,$(filter-out $(entrycatalogs),$(wildcard $(BUILDDIR)/catalogs/entries/*/*)),@$(PRINTF) '%s\n' $(call quote,Removing `$(patsubst $(BUILDDIR)/catalogs/entries/%,%,$(deletedentry))´…)$(newline)$(silent)$(RM) $(call quote,./$(deletedentry))$(newline))
+
+# Destroy buildfiles.
+clean:
+ $(if $(BUILDDIR),$(silent)$(RM) -rf $(call quote,$(BUILDDIR)/),)
+
+# Provide help.
+help :
+ @$(PRINTF) '%b' '$(subst $(newline),\n,$(makefileinfo))'
+
+# Destroy build directory and installed files.
+gone : clean uninstall ;
+
+# List the i·d’s of all recognized entries.
+listids :
+ @$(TR) '| ' ' \n' <<< '$(strip $(foreach category,$(categories),\0033[1m$(category)\0033[22m: $(patsubst %,•|%,$(call identifier,$(call entriesforcategory,$(category)))) ))' | $(XARGS) -0 $(PRINTF) '%b'
+
+# Output a random unused identifier.
+wrmg :
+ @$(ECHO) $(wrmg)
+
+# Create a new entry in the provided category and output its path.
++% : FORCE
+ $(silent)$(call ensuredirectory,$(SRCDIR)/$*)
+ $(if $(wildcard $(SRCDIR)/$*/@),,$(silent)$(PRINTF) '%b' '%%\nCATEGORY : $*\n%%\n' > '$(SRCDIR)/$*/@')
+ $(foreach identifier,$(wrmg),@$(PRINTF) '%b' '%%\nENTRY : $(identifier)\n%%\n\n' > '$(SRCDIR)/$*/$(identifier)'$(newline)@$(ECHO) '$(SRCDIR)/$*/$(identifier)')
+
+# ─ ¶ Special Targets ─────────────────────────────────────────────────
+
+# Perform secondary expansion; this enables pattern rules to determine their prerequisites based on the matched pattern.
+.SECONDEXPANSION : ;
+
+# Don’t use any implicit rules.
+.SUFFIXES : ;
+
+# Treat targets with this as a prerequisite as though they were phony.
+#
+# Mainly useful in pattern rules.
+FORCE : ;
+
+# Phony rules; always consider these out·of·date.
+.PHONY : FORCE all build catalogs clean gone help install list listids uninstall wrmg ;
+
+# ─ ¶ Build Targets ───────────────────────────────────────────────────
+
+# Initialize the ⛩️📰 书社 repository if it is empty.
+$(SHUSHE)/GNUmakefile : %/GNUmakefile :
+ @$(PRINTF) '%s\n' $(call quote,Initializing git submodule at `$*´)
+ $(silent)$(GIT) submodule update --init $(call quote,$*)
+
+# Touch parsers and transforms if the files in `lib/´ have changed.
+$(PARSERS) $(TRANSFORMS) : $(wildcard lib/*.xslt)
+ $(silent)$(TOUCH) $(call quote,$@)
+
+# Create an empty codex metadata file if none exists.
+$(SRCDIR)/@ :
+ $(silent)$(PRINTF) '%b' '%%\nCODEX :\n%%\n' > $(call quote,$@)
+
+# Create category catalogs.
+$(categorycatalogs) : $(BUILDDIR)/catalogs/categories/%/index : FORCE
+ $(call makeandsavedeps,$*,$(call entriesforcategory,$*),$(call makecategorycatalog,$*))
+
+# Create entry catalogs.
+$(entrycatalogs) : $(BUILDDIR)/catalogs/entries/% :
+ @$(PRINTF) '%s\n' $(call quote,Generating catalog for document `$(call identifier,$*)´…)
+ $(silent)$(call ensuredirectory,$(dir $@))
+ $(silent)$(XMLCATALOG) --create --noout $(call quote,$@)
+ $(silent)$(XMLCATALOG) --add uri '.' '$(call identifier,$*).xhtml' --noout $(call quote,$@)
+ $(silent)$(XMLCATALOG) --add uri '?' 'about:entry' --noout $(call quote,$@)
+ $(silent)$(XMLCATALOG) --add uri '@' $(call quote,$*) --noout $(call quote,$@)
+
+$(BUILDDIR)/catalogs/indices/index : FORCE | $(SRCDIR)/@
+ $(call makeandsavedeps,index,$(categories),$(call makeindexcatalog,))
+
+$(BUILDDIR)/catalogs/indices/fullindex : FORCE | $(SRCDIR)/@
+ $(call makeandsavedeps,fullindex,$(categories),$(call makeindexcatalog,1))
--- /dev/null
+# 📰 Caudex
+
+<b>A simple codex generator.</b>
+
+<dfn>📰 Caudex</dfn> is a static website generator aimed at
+ generating simple, category⹀based lists of documents akin to the
+ “Codex” feature of games like <cite>Dragon Age</cite>.
+It is built on top of [⛩️📰 书社][Shushe] and consequently inherits
+ all of the (few) dependencies and prerequisites of the latter.
+
+Using 📰 Caudex is fairly straightforward, but customizing its
+ output requires some familiarity with X·S·L·T and, probably, a
+ minimal level of comfort using G·N·U Make.
+
+## Nomenclature
+
+<i lang="la">Caudex</i> is a Latin word meaning “tree trunk, bollard,
+ book”.
+It is the historical antecedent of Latin (and English) <i
+ lang="la">cōdex</i>.
+
+## Basic Usage
+
+For an easy quickstart, you can simply clone the 📰 Caudex
+ repository and work directly in that folder.
+
+```sh
+git clone https://git.ladys.computer/Caudex
+
+# Create a new entry in `my_category´ :—
+make +my_category | xargs -o nano
+
+# Build the website
+make install
+```
+
+However, the most flexible way to get started with 📰 Caudex is to
+ include it in a project as a Git submodule :—
+
+```sh
+git submodule add https://git.ladys.computer/Caudex
+```
+
+It can then be conigured, and activated, through recursive invocation
+ of G·N·U Make :—
+
+```make
+SHELL = /bin/sh
+
+# Location of the 📰 Caudex submodule.
+CAUDEX := Caudex
+
+# Additional options to pass to 📰 Caudex.
+#
+# Any variable overrides you explicitly give on the command line will
+# also automatically be forwarded.
+CAUDEXOPTS :=
+
+# Build the website by running 📰 Caudex `make install´.
+build: $(CAUDEX)/GNUmakefile
+ $(MAKE) -f $(CAUDEX)/GNUmakefile install $(CAUDEXOPTS)
+
+# Forward targets which begin with a plus; these have special meaning
+# in 📰 Caudex.
++%: $(CAUDEX)/GNUmakefile
+ $(MAKE) -f $(CAUDEX)/GNUmakefile $@ $(CAUDEXOPTS)
+
+# If the make·file of 📰 Caudex doesn’t exist, the submodule needs
+# to be initialized.
+$(CAUDEX)/GNUmakefile:
+ git submodule update --init --recursive '$(CAUDEX)/GNUmakefile'
+```
+
+## Setup and Configuration
+
+📰 Caudex inherits all of the dependencies of ⛩️📰 书社 and enables
+ you to override them in the same way.
+
+In addition to the configuration variables for ⛩️📰 书社, the
+ following variables are recognized and treated specially by
+ 📰 Caudex :—
+
+- **`SRCDIR`:**
+ The location of the codex entries and related metadata (default:
+ `entries`).
+ Only one directory is supported.
+
+- **`ASSETDIR`:**
+ The location of additional source files (default: `assets`).
+ Multiple asset directories can be provided, so long as the same file
+ subpath doesn’t exist in more than one of them.
+
+- **`ASSETINCLUDEDIR`:**
+ The location of includes to be used by additional source files
+ (default: `assets/includes`).
+ Multiple asset include directories can be provided, so long as the
+ same file subpath doesn’t exist in more than one of them.
+
+- **`BUILDDIR`:**
+ The location of the (temporary) build directory (default: `build`).
+ `make clean` will delete this, and it is recommended that it not be
+ used for programs aside from 📰 Caudex.
+
+- **`DESTDIR`:**
+ The location of directory to output files to (default: `public`).
+ `make install` will overwrite files in this directory which
+ correspond to those in `SRCDIR` and `ASSETDIR`.
+ It *will not* touch other files, including those generated from files
+ in `SRCDIR` which have since been deleted.
+
+ Files are first compiled to `$(BUILDDIR)/⛩️📰/public` before they
+ are copied to `DESTDIR`, so this folder is relatively quick and
+ inexpensive to re·create.
+ It’s reasonable to simply delete it before every `make install` to
+ ensure stale content is removed.
+
+- **`THISDIR`:**
+ The location of the 📰 Caudex `GNUmakefile`.
+ This should be set automatically when calling Make and shouldn’t ever
+ need to be set manually.
+
+- **`EXTRAMAGIC`:**
+ Additional magic files for ⛩️📰 书社.
+
+- **`EXTRAPARSERS`:**
+ Additional parsers for ⛩️📰 书社.
+
+- **`EXTRATRANSFORMS`:**
+ Additional transforms for ⛩️📰 书社.
+
+- **`GENERATOR`:**
+ The name of the generator program (default: `📰 Caudex`).
+
+- **`VERSION`:**
+ The current version of `GENERATOR` (default: derived from the current
+ git tag/branch/commit).
+
+- **`SRCREV`:**
+ The current version of the source files (default: derived from the
+ current git tag/branch/commit).
+
+- **`VERBOSE`:**
+ If this variable has a value, every recipe instruction will be
+ printed when it runs (default: empty).
+ This is helpful for debugging, but typically too noisy for general
+ usage.
+
+## Source Files
+
+Codex entries should be placed in subdirectories of `SRCDIR` and have
+ a file·name of the form `???-????` or `???-????,*`, where `???-????`
+ is a unique identifier for the entry within the codex.
+The command `make +⟨category⟩` is provided as a convenience to create
+ a new entry with a random identifier within the subdirectory
+ `⟨category⟩/`.
+If the identifier is followed by a comma, the remainder of the
+ file·name may be used to provide a human⹀friendly description of the
+ file’s contents.
+Remember: The file·name still needs to be compatible with ⛩️📰 书社
+ and Make (it must not contain spaces or other fraught Ascii
+ characters).
+
+Entries should be of the `text/x.codex-entry` format, which is defined
+ as follows :—
+
+1. The string `%%`, followed by a newline and
+
+2. A [Record Jar][draft-phillips-record-jar-01] record starting with
+ `ENTRY :`, followed by
+
+3. Any number of additional records, followed by
+
+4. Zero or more lines of (mostly⹀)plain text.
+
+For example :—
+
+```txt
+%%
+ENTRY : 30W-5M41
+TITLE : My Amazing Entry
+%%
+
+This is the text of my amazing entry.
+```
+
+The text of the entry is processed minimally:
+It is broken into paragraphs on blank lines, and paragraphs for which
+ each line begins with white·space are considered block quotations.
+All other whitespace is trimmed and collapsed.
+
+## Metadata
+
+Codices, categories, and entries should all have metadata.
+For entries, the metadata is provided by the record which begins the
+ `text/x.codex-entry` format.
+For codicies and categories, the metadata is provided by a special file
+ named `@` in `SRCDIR` or the category directory, respectively.
+In all cases, metadata is kept in the Record Jar format.
+
+The following metadata fields are recognized by default :—
+
+- **`CATEGORY`:**
+ Indicates that the current record is a category record.
+ The value of this field gives the identifier for the category.
+
+- **`CODEX`:**
+ Indicates that the current record is a codex record.
+ The value of this field gives the identifier for the codex.
+
+- **`ENTRY`:**
+ Indicates that the current record is an entry record.
+ The value of this field gives the identifier for the entry.
+
+- **`TITLE`:**
+ For all record types, gives the title of the thing being described.
+
+If the `@` file is missing from a category directory, none of the
+ entries in the category will be recognized.
+However, a minimal `@` file will be created for you when you create an
+ entry in a new category using `make +⟨category⟩`.
+
+## Output Files
+
+Assets are installed to their corresponding location in `DESTDIR`
+ (default: `public/`).
+An entry with the identifier `%` will be installed to `%.xhtml`.
+
+Two index files are created :— `index.xhtml` loads entries
+ dynamically upon request, and `standalone.xhtml` contains all of the
+ entries and does not require a network connection.
+
+Output can be customized by supplying additional transforms, ⅌ normal
+ ⛩️📰 书社 conventions.
+The easiest way to customize the transform is to introduce new
+ templates which operate in the `书社:header`, `书社:footer`, or
+ `书社:metadata` modes.
+
+If you want to customize the actual main body output of 📰 Caudex,
+ you will need a pattern like this :—
+
+```xml
+<?xml version="1.0"?>
+<transform
+ xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:exsl="http://exslt.org/common"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
+ exclude-result-prefixes="exsl"
+ version="1.0"
+>
+ <书社:id>example:modify-caudex-index</书社:id>
+ <!-- Create a template with increased priority, excluding elements with a `@data-caudex-parse´ attribute. -->
+ <template match="/html:div[@书社:parsed-by='urn:fdc:ladys.computer:20240204:Caudex:catalog.xslt'][@class='index' or @class='fullindex'][not(@data-caudex-parse)]" priority="1">
+ <!-- Add the attribute to the element in a variable. -->
+ <variable name="toparse">
+ <copy>
+ <attribute name="data-caudex-parse"/>
+ <copy-of select="@*|node()"/>
+ </copy>
+ </variable>
+ <!-- Apply templates to the contents of the variable, saving the result. -->
+ <variable name="parsed">
+ <apply-templates select="exsl:node-set($toparse)/*"/>
+ </variable>
+ <!-- Do something with the result. -->
+ <for-each select="exsl:node-set($parsed)/*">
+ <!-- … -->
+ </for-each>
+ </template>
+</transform>
+```
+
+[Shushe]: <https://git.ladys.computer/Shushe>
+[draft-phillips-record-jar-01]: <https://datatracker.ietf.org/doc/html/draft-phillips-record-jar-01>
--- /dev/null
+<?xml version="1.0"?>
+<!--
+⁌ 📰 Caudex ∷ lib/split.xslt
+
+© 2024 Lady [@ Lady’s Computer]
+
+This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
+If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+-->
+<transform
+ xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:Caudex="urn:fdc:ladys.computer:20240204:Caudex"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ exclude-result-prefixes="Caudex"
+ version="1.0"
+>
+ <template name="Caudex:split">
+ <param name="source"/>
+ <param name="separator" select="'
'"/>
+ <choose>
+ <when test="contains($source, $separator)">
+ <html:span>
+ <value-of select="substring-before($source, $separator)"/>
+ </html:span>
+ <call-template name="Caudex:split">
+ <with-param name="source" select="substring-after($source, $separator)"/>
+ <with-param name="separator" select="$separator"/>
+ </call-template>
+ </when>
+ <otherwise>
+ <html:span>
+ <value-of select="$source"/>
+ </html:span>
+ </otherwise>
+ </choose>
+ </template>
+</transform>
--- /dev/null
+0 string %%\nENTRY\ : codex entry text
+!:mime text/x.codex-entry
+!:strength + 100
--- /dev/null
+<?xml version="1.0"?>
+<!--
+⁌ 📰 Caudex ∷ parsers/catalog.xslt
+
+© 2024 Lady [@ Lady’s Computer]
+
+This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
+If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+-->
+<transform
+ xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:Caudex="urn:fdc:ladys.computer:20240204:Caudex"
+ xmlns:catalog="urn:oasis:names:tc:entity:xmlns:xml:catalog"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
+ exclude-result-prefixes="catalog"
+ version="1.0"
+>
+ <书社:id>urn:fdc:ladys.computer:20240204:Caudex:catalog.xslt</书社:id>
+ <template match="/catalog:catalog">
+ <html:div class="{substring-after(catalog:uri[@name='?']/@uri, 'about:')}" 书社:parsed-by="urn:fdc:ladys.computer:20240204:Caudex:catalog.xslt">
+ <if test="catalog:uri[@name='.']">
+ <attribute name="书社:destination">
+ <value-of select="catalog:uri[@name='.']/@uri"/>
+ </attribute>
+ </if>
+ <apply-templates select="catalog:uri[not(@name='?' or @name='.')]"/>
+ </html:div>
+ </template>
+ <template match="catalog:uri[not(@name='?' or @name='.')]">
+ <html:div id="{@name}">
+ <if test="contains(@uri, '?cksum=')">
+ <attribute name="Caudex:cksum">
+ <value-of select="substring-after(@uri, '?cksum=')"/>
+ </attribute>
+ </if>
+ <书社:link xlink:show="embed">
+ <attribute name="xlink:href">
+ <text>about:shushe?include=</text>
+ <choose>
+ <when test="contains(@uri, '?')">
+ <value-of select="substring-before(@uri, '?')"/>
+ </when>
+ <otherwise>
+ <value-of select="@uri"/>
+ </otherwise>
+ </choose>
+ </attribute>
+ </书社:link>
+ </html:div>
+ </template>
+</transform>
--- /dev/null
+<?xml version="1.0"?>
+<!--
+⁌ 📰 Caudex ∷ parsers/codex-entry.xslt
+
+© 2024 Lady [@ Lady’s Computer]
+
+This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
+If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+-->
+<transform
+ xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:Caudex="urn:fdc:ladys.computer:20240204:Caudex"
+ xmlns:exsl="http://exslt.org/common"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
+ exclude-result-prefixes="exsl Caudex"
+ version="1.0"
+>
+ <import href="../lib/split.xslt"/>
+ <书社:id>urn:fdc:ladys.computer:20240204:Caudex:codex-entry.xslt</书社:id>
+ <template match="html:script[@type='text/x.codex-entry']">
+ <variable name="lines">
+ <call-template name="Caudex:split">
+ <with-param name="source" select="string()"/>
+ </call-template>
+ </variable>
+ <variable name="linespans" select="exsl:node-set($lines)/*"/>
+ <variable name="record-end" select="$linespans[starts-with(., '%%')][last()]"/>
+ <variable name="text-start" select="$record-end/following-sibling::*[string()!=''][1]"/>
+ <html:div>
+ <html:script type="text/record-jar">
+ <for-each select="$record-end/preceding-sibling::*|$record-end">
+ <value-of select="."/>
+ <text>
</text>
+ </for-each>
+ </html:script>
+ <html:script type="text/plain">
+ <for-each select="$text-start|$text-start/following-sibling::*">
+ <value-of select="."/>
+ <text>
</text>
+ </for-each>
+ </html:script>
+ </html:div>
+ </template>
+</transform>
--- /dev/null
+<?xml version="1.0"?>
+<!--
+⁌ 📰 Caudex ∷ transforms/entry.xslt
+
+© 2024 Lady [@ Lady’s Computer]
+
+This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
+If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+-->
+<!DOCTYPE transform [
+ <!ENTITY Caudex "urn:fdc:ladys.computer:20240204:Caudex:">
+ <!ENTITY 书社 "urn:fdc:ladys.computer:20231231:Shu1She4:">
+]>
+<transform
+ xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:Caudex="urn:fdc:ladys.computer:20240204:Caudex"
+ xmlns:exsl="http://exslt.org/common"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
+ exclude-result-prefixes="exsl Caudex"
+ version="1.0"
+>
+ <import href="../lib/split.xslt"/>
+ <书社:id>&Caudex;entry.xslt</书社:id>
+ <template match="/html:div[@书社:parsed-by='&Caudex;catalog.xslt'][@class='entry']">
+ <html:body>
+ <for-each select="@*[namespace-uri()!='urn:fdc:ladys.computer:20231231:Shu1She4']">
+ <copy/>
+ </for-each>
+ <for-each select="html:div[@id='@']/html:div[@书社:parsed-by='&Caudex;codex-entry.xslt']">
+ <variable name="metadata" select="html:div[@书社:parsed-by='&书社;record-jar.xslt']/html:dl[1]"/>
+ <html:meta itemprop="&书社;title">
+ <attribute name="content">
+ <value-of select="$metadata//html:dt[string()='TITLE']/following-sibling::html:dd"/>
+ <if test="$metadata//html:dt[string()='TITLE'] and $metadata//html:dt[string()='ENTRY']">
+ <text> </text>
+ </if>
+ <if test="$metadata//html:dt[string()='ENTRY']">
+ <text>(</text>
+ <value-of select="$metadata//html:dt[string()='ENTRY']/following-sibling::html:dd"/>
+ <text>)</text>
+ </if>
+ </attribute>
+ </html:meta>
+ <html:article>
+ <if test="$metadata//html:dt[string()='TITLE']">
+ <html:h1>
+ <value-of select="$metadata//html:dt[string()='TITLE']/following-sibling::html:dd"/>
+ </html:h1>
+ </if>
+ <variable name="paragraphs">
+ <call-template name="Caudex:split">
+ <with-param name="source" select="string(html:pre[@书社:parsed-by='&书社;plain.xslt'])"/>
+ <with-param name="separator" select="'

'"/>
+ </call-template>
+ </variable>
+ <for-each select="exsl:node-set($paragraphs)/*">
+ <variable name="lines">
+ <call-template name="Caudex:split">
+ <with-param name="source" select="string()"/>
+ </call-template>
+ </variable>
+ <variable name="linespans" select="exsl:node-set($lines)/*"/>
+ <variable name="partype">
+ <choose>
+ <when test="not($linespans[not(starts-with(., ' ') or starts-with(., '	'))])">
+ <text>blockquote</text>
+ </when>
+ <otherwise>
+ <text>p</text>
+ </otherwise>
+ </choose>
+ </variable>
+ <if test="$linespans[normalize-space()!='']">
+ <element name="html:{$partype}">
+ <for-each select="$linespans">
+ <if test="normalize-space()!=''">
+ <value-of select="normalize-space()"/>
+ <if test="following-sibling::*[normalize-space()!='']">
+ <text> </text>
+ </if>
+ </if>
+ </for-each>
+ </element>
+ </if>
+ </for-each>
+ </html:article>
+ </for-each>
+ </html:body>
+ </template>
+</transform>
--- /dev/null
+<?xml version="1.0"?>
+<!--
+⁌ 📰 Caudex ∷ transforms/entry.xslt
+
+© 2024 Lady [@ Lady’s Computer]
+
+This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
+If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+-->
+<!DOCTYPE transform [
+ <!ENTITY Caudex "urn:fdc:ladys.computer:20240204:Caudex:">
+ <!ENTITY 书社 "urn:fdc:ladys.computer:20231231:Shu1She4:">
+]>
+<transform
+ xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:Caudex="urn:fdc:ladys.computer:20240204:Caudex"
+ xmlns:exsl="http://exslt.org/common"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
+ exclude-result-prefixes="exsl"
+ version="1.0"
+>
+ <import href="../lib/split.xslt"/>
+ <书社:id>&Caudex;index.xslt</书社:id>
+ <template match="/html:div[@书社:parsed-by='&Caudex;catalog.xslt'][@class='index' or @class='fullindex']">
+ <html:body>
+ <for-each select="@*[namespace-uri()!='urn:fdc:ladys.computer:20231231:Shu1She4']">
+ <copy/>
+ </for-each>
+ <variable name="metadata" select="html:div[@书社:parsed-by='&书社;record-jar.xslt']"/>
+ <html:meta itemprop="&书社;title" content="{$metadata//html:dt[string()='TITLE']/following-sibling::html:dd}"/>
+ <html:main>
+ <html:nav>
+ <html:ul>
+ <for-each select=".//html:div[@书社:parsed-by='&Caudex;catalog.xslt'][@class='category']">
+ <variable name="category-metadata" select="html:div[@id='@']/html:div[@书社:parsed-by='&书社;record-jar.xslt']"/>
+ <html:li>
+ <html:strong>
+ <choose>
+ <when test="$category-metadata//html:dt[string()='TITLE']">
+ <value-of select="$category-metadata//html:dt[string()='TITLE']/following-sibling::html:dd"/>
+ </when>
+ <otherwise>
+ <value-of select="../@id"/>
+ </otherwise>
+ </choose>
+ </html:strong>
+ <html:ul>
+ <for-each select="html:div[@id!='@']/html:div[@书社:parsed-by='&Caudex;codex-entry.xslt']">
+ <sort select="html:div[@书社:parsed-by='&书社;record-jar.xslt']/html:dl[1]//html:dt[string()='TITLE']/following-sibling::html:dd"/>
+ <sort select="../@id"/>
+ <variable name="entry-metadata" select="html:div[@书社:parsed-by='&书社;record-jar.xslt']/html:dl[1]"/>
+ <html:li>
+ <html:a href="{../@id}.xhtml" data-identifier="{../@id}" data-cksum="{@书社:cksum}">
+ <value-of select="$entry-metadata//html:dt[string()='TITLE']/following-sibling::html:dd"/>
+ <text> </text>
+ <html:small>
+ <text>(</text>
+ <value-of select="../@id"/>
+ <text>)</text>
+ </html:small>
+ </html:a>
+ </html:li>
+ </for-each>
+ </html:ul>
+ </html:li>
+ </for-each>
+ </html:ul>
+ </html:nav>
+ <html:div id="pane">
+ <if test="@class='fullindex'">
+ <for-each select=".//html:div[@书社:parsed-by='&Caudex;catalog.xslt'][@class='category']/html:div[@id!='@']/html:div[@书社:parsed-by='&Caudex;codex-entry.xslt']">
+ <variable name="context" select="."/>
+ <variable name="entry">
+ <html:div 书社:parsed-by="&Caudex;catalog.xslt" class="entry">
+ <html:div id="@">
+ <copy-of select="."/>
+ </html:div>
+ </html:div>
+ </variable>
+ <variable name="transformed-entry">
+ <apply-templates select="exsl:node-set($entry)/*"/>
+ </variable>
+ <for-each select="exsl:node-set($transformed-entry)//html:article">
+ <html:article id="{$context/../@id}" hidden="hidden">
+ <copy-of select="@*|node()"/>
+ </html:article>
+ </for-each>
+ </for-each>
+ </if>
+ </html:div>
+ </html:main>
+ <html:script type="text/javascript">
+ <choose>
+ <when test="@class='fullindex'">
+ <text><![CDATA[#!javascript
+~function(links){
+ var i=0
+ for(;i<links.length;++i){
+ ~function(link){
+ var identifier=link.getAttribute("data-identifier")
+ var cksum=link.getAttribute("data-cksum")
+ if(!(identifier&&cksum))return
+ link.addEventListener("click",function(event){
+ var j=0
+ var k=0
+ var pane=document.getElementById("pane")
+ var articles=pane.getElementsByTagNameNS("http://www.w3.org/1999/xhtml","article")
+ for(;j<articles.length;++j){
+ ~function(article){
+ if(article.getAttribute("id")==identifier){
+ article.removeAttribute("hidden")
+ link.setAttribute("class","cached read")
+ try{
+ localStorage.setItem(identifier,cksum)
+ }catch(e){}
+ }
+ else article.setAttribute("hidden","hidden")
+ }(articles.item(j))
+ }
+ for(;k<links.length;++k){
+ ~function(linkk){
+ var parent=linkk.parentNode
+ if(linkk==link)parent.setAttribute("class","selected")
+ else parent.setAttribute("class","")
+ }(links.item(k))
+ }
+ event.preventDefault()
+ pane.focus()
+ },false)
+ try{
+ var lastReadCksum=localStorage.getItem(identifier)
+ if(lastReadCksum==cksum)link.setAttribute("class","cached read")
+ else if(lastReadCksum)link.setAttribute("class","cached updated")
+ else link.setAttribute("class","cached new")
+ }catch(e){}
+ }(links.item(i))
+ }
+}(document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml","a"))
+]]></text>
+ </when>
+ <otherwise>
+ <text><![CDATA[#!javascript
+~function(links){
+ var i=0
+ var cache={}
+ for(;i<links.length;++i){
+ ~function(link){
+ var identifier=link.getAttribute("data-identifier")
+ var cksum=link.getAttribute("data-cksum")
+ if(!(identifier&&cksum))return
+ link.addEventListener("click",function(event){
+ var pane=document.getElementById("pane")
+ var request
+ if(identifier in cache){
+ pane.textContent=""
+ pane.appendChild(document.importNode(cache[identifier],true))
+ }else{
+ pane.textContent="Loading…"
+ request=new XMLHttpRequest
+ request.open("GET",link.href)
+ request.addEventListener("load",function(){
+ var k=0
+ var article=request.responseXML.getElementsByTagNameNS("http://www.w3.org/1999/xhtml","article").item(0)
+ cache[identifier]=article
+ pane.textContent=""
+ pane.appendChild(document.importNode(article,true))
+ for(;k<links.length;++k){
+ ~function(linkk){
+ var parent=linkk.parentNode
+ if(linkk==link)parent.setAttribute("class","selected")
+ else parent.setAttribute("class","")
+ }(links.item(k))
+ }
+ link.setAttribute("class","cached read")
+ try{
+ localStorage.setItem(identifier,cksum)
+ }catch(e){}
+ },false)
+ request.responseType="document"
+ request.send()
+ }
+ event.preventDefault()
+ pane.focus()
+ },false)
+ try{
+ var lastReadCksum=localStorage.getItem(identifier)
+ if(lastReadCksum==cksum)link.setAttribute("class","read")
+ else if(lastReadCksum)link.setAttribute("class","updated")
+ else link.setAttribute("class","new")
+ }catch(e){}
+ }(links[i])
+ }
+}(document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml","a"))
+]]></text>
+ </otherwise>
+ </choose>
+ </html:script>
+ </html:body>
+ </template>
+</transform>
--- /dev/null
+<?xml version="1.0"?>
+<!--
+⁌ 📰 Caudex ∷ transforms/metadata.xslt
+
+© 2024 Lady [@ Lady’s Computer]
+
+This Source Code Form is subject to the terms of the Mozilla Public License, v 2.0.
+If a copy of the M·P·L was not distributed with this file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+-->
+<transform
+ xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xslt="http://www.w3.org/1999/XSL/Transform"
+ xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
+ version="1.0"
+>
+ <书社:id>urn:fdc:ladys.computer:20240204:Caudex:metadata.xslt</书社:id>
+ <template match="xslt:include[@书社:id='urn:fdc:ladys.computer:20240204:Caudex:metadata.xslt']" mode="书社:metadata">
+ <html:style type="text/css">
+ <text><![CDATA[@charset "UTF-8";
+@namespace html "http://www.w3.org/1999/xhtml";
+@namespace 书社 "urn:fdc:ladys.computer:20231231:Shu1She4";
+html*[hidden]{Display:None}
+]]></text>
+ <if test="//html:body[@class='index' or @class='fullindex']">
+ <text><![CDATA[/*Index styling*/
+html|html{Margin:0;Padding:0;Height:100%}
+html|body{Box-Sizing:Border-Box;Margin:0;Height:100%;Padding:1EM}
+html|main{Display:Block;Width:100%;Height:100%}
+html|main>html|*{Display:Inline-Block;Box-Sizing:Border-Box;Border:.5EM Transparent None;Height:100%;Width:50%;Vertical-Align:Top;Overflow:Auto}
+html|main>html|nav{Border-Right-Style:Solid;Overflow:Scroll}
+html|main>html|div{Border-Left-Style:Solid}
+]]></text>
+ </if>
+ </html:style>
+ </template>
+</transform>