]>
Lady’s Gitweb - Lemon/blob - mod.js
e6913e30539aff7a0f1ec87db1bc26901288331e
2 // ====================================================================
4 // Copyright © 2022 Lady [@ Lady’s Computer].
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
10 import { xhtmlNamespace
} from "./names.js";
14 * Create a D·O·M Element from a tagged template.
19 * Lemon("elementName", "namespace")`content`;
21 * Lemon("elementName", "namespace")({ attr: "value" })`content`;
24 * Content may, via substitutions, include additional nodes. As a
25 * convenience, if the namespace is not defined, it is the X·H·T·M·L
26 * namespace. As a convenience, `Lemon.elementName` is a shorthand
27 * for `Lemon("element-name").` As a convenience, substitutions may
28 * take the form of an array of nodes.
30 * By default, `globalThis.document` is used as the owner document
31 * for new nodes. To pick a different document, supply a different
32 * `document` on the `this` value when calling. You can use `bind` to
33 * simplify this in some cases :—
36 * const MyLemon = Lemon.bind({ document });
37 * MyLemon.p`words`; // same as Lemon.call({ document }, "p")`words`
42 const lemonProxyHandler
= Object
.assign(
45 /** If `P` doesn’t exist on `O`, calls `O` with `P` instead. */
47 if (typeof P
!= "string" || Reflect
.has(O
, P
)) {
48 return Reflect
.get(O
, P
, Receiver
);
49 } else if (typeof O
== "function") {
50 return O([...function* () {
51 for (const character
of P
) {
52 yield /[A-Z]/u.test(character
)
53 ? `-${character.toLowerCase()}`
65 Object
.defineProperties(
66 Object
.setPrototypeOf(
67 function (name
, namespace = xhtmlNamespace
) {
68 return Object
.setPrototypeOf(
70 context
: this ?? globalThis
,
72 namespace: `${namespace}`,
80 name
: { value
: "Lemon" },
81 prototype: { value
: Object
.create(Function
.prototype) },
83 value
: Object
.defineProperties(
84 function (thisArg
, ...args
) {
86 Function
.prototype.bind
.call(
94 { name
: { value
: "bind" } },
106 export default Lemon
;
109 * Creates an Element with `name`, `namespace`, and `attributes` drawn
110 * from this object and content determined by the provided strings and
113 const createNode = function (strings
, ...expressions
) {
114 const { raw
} = strings
;
115 const { context
, name
, namespace, attributes
} = this;
116 const document
= context
.document
?? globalThis
.document
;
117 const result
= document
.createElementNS(namespace, name
);
118 for (const [attName
, attValue
] of Object
.entries(attributes
)) {
119 result
.setAttribute(attName
, `${attValue}`);
121 const maxIndex
= Math
.max(raw
.length
, expressions
.length
);
122 for (let index
= 0; index
< maxIndex
; ++index
) {
124 ...nodesFromExpression
.call(context
, raw
[index
]),
125 ...nodesFromExpression
.call(context
, expressions
[index
]),
133 * Creates a new template tag builder with the provided attributes for
134 * for making Element’s with the `name` specified on this.
136 * As a shorthand, you can also use this function as a template tag
137 * itself, in which case it is treated as though its attributes were
140 const nodeTagBuilder = function (attrsOrStrings
, ...expressions
) {
141 const { context
, name
, namespace } = this;
143 Array
.isArray(attrsOrStrings
) && "raw" in attrsOrStrings
&&
144 Array
.isArray(attrsOrStrings
.raw
)
146 // The first argument is usable as a template string.
147 return createNode
.call(
148 { context
, name
, namespace, attributes
: {} },
153 // The first argument is not a template string.
154 return createNode
.bind({
158 attributes
: { ...Object(attrsOrStrings
) },
163 /** Processes the provided expression and yields Nodes. */
164 const nodesFromExpression
= function* (expression
) {
165 const document
= this.document
?? globalThis
.document
;
166 const nodePrototype
= Object
.getPrototypeOf(
167 Object
.getPrototypeOf(document
.createDocumentFragment()),
169 if (expression
== null) {
170 // The expression is nullish.
173 // The expression is not nullish.
174 const expressionIsNode
= (() => {
175 // Due to how D·O·M‐to‐Ecmascript bindings work, attempting to
176 // call a D·O·M method or accessor on a value which is not
177 // actually of the appropriate type will throw.
179 // This I·I·F·E uses that fact to test whether the provided
180 // expression is already a Node.
181 try { // throws unless this is a Node
182 return !!Reflect
.get(nodePrototype
, "nodeType", expression
);
187 if (expressionIsNode
) {
188 // The expression is already a `Node`.
190 } else if (Array
.isArray(expression
)) {
191 // The expression is an array of expressions.
192 for (const subexpression
of expression
) {
193 yield* nodesFromExpression
.call(this, subexpression
);
196 // The expression is something else.
197 yield document
.createTextNode(`${expression}`);
This page took 0.060474 seconds and 3 git commands to generate.