]> Lady’s Gitweb - Caudex/commitdiff
Initial commit; minimal working implementation 0.1.0
authorLady <redacted>
Sun, 4 Feb 2024 23:28:32 +0000 (18:28 -0500)
committerLady <redacted>
Thu, 15 Aug 2024 00:34:43 +0000 (20:34 -0400)
There is a lot to be done here, both in terms of the output and its
styling and in terms of building out additional functionality, but this
could conceivably be used as⹀is by someone willing to put the work in
to customize it.

13 files changed:
.gitignore [new file with mode: 0644]
.gitmodules [new file with mode: 0644]
\9b©ï¸\8fð\9f\93° [new submodule]
COPYING [new file with mode: 0644]
GNUmakefile [new file with mode: 0644]
README.markdown [new file with mode: 0644]
lib/split.xslt [new file with mode: 0644]
magic/codex-entry [new file with mode: 0644]
parsers/catalog.xslt [new file with mode: 0644]
parsers/codex-entry.xslt [new file with mode: 0644]
transforms/entry.xslt [new file with mode: 0644]
transforms/index.xslt [new file with mode: 0644]
transforms/metadata.xslt [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..33687b1
--- /dev/null
@@ -0,0 +1,2 @@
+/build
+/public
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..51a8c8e
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "书社"]
+       path = .⛩️📰
+       url = https://git.ladys.computer/Shushe.git
diff --git a/.â\9b©ï¸\8fð\9f\93° b/.â\9b©ï¸\8fð\9f\93°
new file mode 160000 (submodule)
index 0000000..2a4b2ff
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 2a4b2ff1cb88305fb662deda701ee23afbf56498
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..a612ad9
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,373 @@
+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.
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644 (file)
index 0000000..5602ca0
--- /dev/null
@@ -0,0 +1,300 @@
+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))
diff --git a/README.markdown b/README.markdown
new file mode 100644 (file)
index 0000000..d0c0d0d
--- /dev/null
@@ -0,0 +1,274 @@
+# 🪾📰 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>
diff --git a/lib/split.xslt b/lib/split.xslt
new file mode 100644 (file)
index 0000000..bf05118
--- /dev/null
@@ -0,0 +1,37 @@
+<?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="'&#xA;'"/>
+               <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>
diff --git a/magic/codex-entry b/magic/codex-entry
new file mode 100644 (file)
index 0000000..2eca487
--- /dev/null
@@ -0,0 +1,3 @@
+0  string  %%\nENTRY\ :  codex entry text
+!:mime text/x.codex-entry
+!:strength + 100
diff --git a/parsers/catalog.xslt b/parsers/catalog.xslt
new file mode 100644 (file)
index 0000000..a6a560a
--- /dev/null
@@ -0,0 +1,53 @@
+<?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>
diff --git a/parsers/codex-entry.xslt b/parsers/codex-entry.xslt
new file mode 100644 (file)
index 0000000..d854ebe
--- /dev/null
@@ -0,0 +1,45 @@
+<?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>&#xA;</text>
+                               </for-each>
+                       </html:script>
+                       <html:script type="text/plain">
+                               <for-each select="$text-start|$text-start/following-sibling::*">
+                                       <value-of select="."/>
+                                       <text>&#xA;</text>
+                               </for-each>
+                       </html:script>
+               </html:div>
+       </template>
+</transform>
diff --git a/transforms/entry.xslt b/transforms/entry.xslt
new file mode 100644 (file)
index 0000000..96cd637
--- /dev/null
@@ -0,0 +1,91 @@
+<?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="'&#xA;&#xA;'"/>
+                                               </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(., '&#x9;'))])">
+                                                                       <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>
diff --git a/transforms/index.xslt b/transforms/index.xslt
new file mode 100644 (file)
index 0000000..7bb1ef8
--- /dev/null
@@ -0,0 +1,201 @@
+<?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>
diff --git a/transforms/metadata.xslt b/transforms/metadata.xslt
new file mode 100644 (file)
index 0000000..158b5d7
--- /dev/null
@@ -0,0 +1,37 @@
+<?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>
This page took 0.106711 seconds and 4 git commands to generate.