@wdprlib/parser 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/index.cjs +172 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +172 -0
- package/package.json +2 -2
- package/src/index.ts +6 -0
- package/src/parser/rules/block/gallery/index.ts +215 -0
- package/src/parser/rules/block/gallery/items.ts +62 -0
- package/src/parser/rules/block/index.ts +3 -0
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ const resolved = await resolveModules(ast, {
|
|
|
43
43
|
// value — look it up with a parameterized query and return the normalized name
|
|
44
44
|
const found = category ? await findCategory(category) : null // your lookup
|
|
45
45
|
if (category && !found) return { status: 'category-not-found', category }
|
|
46
|
-
return { status: 'ok', tags: [{ tag: '
|
|
46
|
+
return { status: 'ok', tags: [{ tag: 'apple', weight: 42 }], category: found?.unixName ?? null }
|
|
47
47
|
},
|
|
48
48
|
getPageTags: () => ['tag1', 'tag2'],
|
|
49
49
|
}, {
|
|
@@ -58,6 +58,8 @@ const resolved = await resolveModules(ast, {
|
|
|
58
58
|
- Wikidot markup parsing (bold, italic, links, images, tables, etc.)
|
|
59
59
|
- Include resolution (`[[include page]]`)
|
|
60
60
|
- Module support (ListPages, ListUsers, TagCloud, IfTags, etc.)
|
|
61
|
+
- `[[gallery]]` (auto-collection uses the page's attachments passed to `@wdprlib/render` as `page.files`; lightbox via `@wdprlib/runtime`)
|
|
62
|
+
|
|
61
63
|
- Data extraction for server-side rendering
|
|
62
64
|
|
|
63
65
|
## Related Packages
|
package/dist/index.cjs
CHANGED
|
@@ -9318,6 +9318,177 @@ var bibliographyRule = {
|
|
|
9318
9318
|
}
|
|
9319
9319
|
};
|
|
9320
9320
|
|
|
9321
|
+
// packages/parser/src/parser/rules/block/gallery/items.ts
|
|
9322
|
+
function parseGalleryItemLine(content) {
|
|
9323
|
+
const spacePos = content.indexOf(" ");
|
|
9324
|
+
let source = spacePos < 0 ? content : content.slice(0, spacePos);
|
|
9325
|
+
const attrText = spacePos < 0 ? "" : content.slice(spacePos + 1);
|
|
9326
|
+
let newWindow = false;
|
|
9327
|
+
if (source.startsWith("*")) {
|
|
9328
|
+
source = source.slice(1);
|
|
9329
|
+
newWindow = true;
|
|
9330
|
+
}
|
|
9331
|
+
const attrs = parseItemAttrs(attrText);
|
|
9332
|
+
let link = attrs.get("link") ?? null;
|
|
9333
|
+
if (link !== null && link.startsWith("*")) {
|
|
9334
|
+
newWindow = true;
|
|
9335
|
+
link = link.slice(1);
|
|
9336
|
+
}
|
|
9337
|
+
const alt = attrs.get("alt") ?? null;
|
|
9338
|
+
return { source, link, alt, newWindow };
|
|
9339
|
+
}
|
|
9340
|
+
function parseItemAttrs(text) {
|
|
9341
|
+
const attrs = new Map;
|
|
9342
|
+
const parts = text.trim().split('="');
|
|
9343
|
+
let key = parts[0]?.trim() ?? "";
|
|
9344
|
+
for (let i = 1;i < parts.length; i++) {
|
|
9345
|
+
const val = parts[i] ?? "";
|
|
9346
|
+
const quotePos = val.lastIndexOf('"');
|
|
9347
|
+
if (quotePos < 0) {
|
|
9348
|
+
attrs.set(key, "");
|
|
9349
|
+
key = val.slice(1).trim();
|
|
9350
|
+
} else {
|
|
9351
|
+
attrs.set(key, stripslashes(val.slice(0, quotePos)));
|
|
9352
|
+
key = val.slice(quotePos + 1).trim();
|
|
9353
|
+
}
|
|
9354
|
+
}
|
|
9355
|
+
return attrs;
|
|
9356
|
+
}
|
|
9357
|
+
function stripslashes(value) {
|
|
9358
|
+
return value.replace(/\\(.)/gs, "$1").replace(/\\$/, "");
|
|
9359
|
+
}
|
|
9360
|
+
|
|
9361
|
+
// packages/parser/src/parser/rules/block/gallery/index.ts
|
|
9362
|
+
var GALLERY_SIZES = ["small", "medium", "thumbnail", "square", "original"];
|
|
9363
|
+
function normalizeSize(value) {
|
|
9364
|
+
return value !== undefined && GALLERY_SIZES.includes(value) ? value : "thumbnail";
|
|
9365
|
+
}
|
|
9366
|
+
function normalizeViewer(value) {
|
|
9367
|
+
return value !== "no" && value !== "false";
|
|
9368
|
+
}
|
|
9369
|
+
function normalizeOrder(value) {
|
|
9370
|
+
switch (value) {
|
|
9371
|
+
case "name":
|
|
9372
|
+
case "name desc":
|
|
9373
|
+
case "created_at":
|
|
9374
|
+
case "created_at desc":
|
|
9375
|
+
return value;
|
|
9376
|
+
case "nameDesc":
|
|
9377
|
+
return "name desc";
|
|
9378
|
+
case "dateAdded":
|
|
9379
|
+
return "created_at";
|
|
9380
|
+
case "dateAddedDesc":
|
|
9381
|
+
return "created_at desc";
|
|
9382
|
+
case "name desc desc":
|
|
9383
|
+
return "name";
|
|
9384
|
+
case "created_at desc desc":
|
|
9385
|
+
return "created_at";
|
|
9386
|
+
default:
|
|
9387
|
+
return "name";
|
|
9388
|
+
}
|
|
9389
|
+
}
|
|
9390
|
+
function tryParseGalleryContent(ctx, pos) {
|
|
9391
|
+
const start = pos;
|
|
9392
|
+
if (ctx.tokens[pos]?.type !== "NEWLINE") {
|
|
9393
|
+
return null;
|
|
9394
|
+
}
|
|
9395
|
+
const lines = [];
|
|
9396
|
+
let p = pos + 1;
|
|
9397
|
+
for (;; ) {
|
|
9398
|
+
const colon = ctx.tokens[p];
|
|
9399
|
+
if (colon?.type !== "COLON" || !colon.lineStart) {
|
|
9400
|
+
break;
|
|
9401
|
+
}
|
|
9402
|
+
const space = ctx.tokens[p + 1];
|
|
9403
|
+
if (space?.type !== "WHITESPACE" || !space.value.startsWith(" ")) {
|
|
9404
|
+
break;
|
|
9405
|
+
}
|
|
9406
|
+
let content = "";
|
|
9407
|
+
let q = p + 1;
|
|
9408
|
+
while (q < ctx.tokens.length) {
|
|
9409
|
+
const token5 = ctx.tokens[q];
|
|
9410
|
+
if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
|
|
9411
|
+
break;
|
|
9412
|
+
}
|
|
9413
|
+
content += token5.value;
|
|
9414
|
+
q++;
|
|
9415
|
+
}
|
|
9416
|
+
if (content === " ") {
|
|
9417
|
+
return null;
|
|
9418
|
+
}
|
|
9419
|
+
if (ctx.tokens[q]?.type !== "NEWLINE") {
|
|
9420
|
+
return null;
|
|
9421
|
+
}
|
|
9422
|
+
lines.push(content.trim());
|
|
9423
|
+
p = q + 1;
|
|
9424
|
+
}
|
|
9425
|
+
if (lines.length === 0) {
|
|
9426
|
+
return null;
|
|
9427
|
+
}
|
|
9428
|
+
if (ctx.tokens[p]?.type !== "BLOCK_END_OPEN") {
|
|
9429
|
+
return null;
|
|
9430
|
+
}
|
|
9431
|
+
const nameResult = parseBlockName(ctx, p + 1);
|
|
9432
|
+
if (!nameResult || nameResult.name !== "gallery") {
|
|
9433
|
+
return null;
|
|
9434
|
+
}
|
|
9435
|
+
const closePos = p + 1 + nameResult.consumed;
|
|
9436
|
+
if (ctx.tokens[closePos]?.type !== "BLOCK_CLOSE") {
|
|
9437
|
+
return null;
|
|
9438
|
+
}
|
|
9439
|
+
return { lines, consumed: closePos + 1 - start };
|
|
9440
|
+
}
|
|
9441
|
+
var galleryRule = {
|
|
9442
|
+
name: "gallery",
|
|
9443
|
+
startTokens: ["BLOCK_OPEN"],
|
|
9444
|
+
requiresLineStart: true,
|
|
9445
|
+
parse(ctx) {
|
|
9446
|
+
if (ctx.tokens[ctx.pos]?.type !== "BLOCK_OPEN") {
|
|
9447
|
+
return { success: false };
|
|
9448
|
+
}
|
|
9449
|
+
let pos = ctx.pos + 1;
|
|
9450
|
+
const nameResult = parseBlockName(ctx, pos);
|
|
9451
|
+
if (!nameResult || nameResult.name !== "gallery") {
|
|
9452
|
+
return { success: false };
|
|
9453
|
+
}
|
|
9454
|
+
pos += nameResult.consumed;
|
|
9455
|
+
const attrResult = parseAttributesRaw(ctx, pos);
|
|
9456
|
+
pos += attrResult.consumed;
|
|
9457
|
+
if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
|
|
9458
|
+
return { success: false };
|
|
9459
|
+
}
|
|
9460
|
+
pos++;
|
|
9461
|
+
const size = normalizeSize(attrResult.attrs.size);
|
|
9462
|
+
const viewer = normalizeViewer(attrResult.attrs.viewer);
|
|
9463
|
+
const order = normalizeOrder(attrResult.attrs.order);
|
|
9464
|
+
const openConsumed = pos - ctx.pos;
|
|
9465
|
+
const content = tryParseGalleryContent(ctx, pos);
|
|
9466
|
+
if (!content) {
|
|
9467
|
+
return {
|
|
9468
|
+
success: true,
|
|
9469
|
+
elements: [
|
|
9470
|
+
{
|
|
9471
|
+
element: "gallery",
|
|
9472
|
+
data: { size, order, viewer, content: { type: "auto", files: null } }
|
|
9473
|
+
}
|
|
9474
|
+
],
|
|
9475
|
+
consumed: openConsumed
|
|
9476
|
+
};
|
|
9477
|
+
}
|
|
9478
|
+
const items = content.lines.map(parseGalleryItemLine);
|
|
9479
|
+
return {
|
|
9480
|
+
success: true,
|
|
9481
|
+
elements: [
|
|
9482
|
+
{
|
|
9483
|
+
element: "gallery",
|
|
9484
|
+
data: { size, order, viewer, content: { type: "items", items } }
|
|
9485
|
+
}
|
|
9486
|
+
],
|
|
9487
|
+
consumed: openConsumed + content.consumed
|
|
9488
|
+
};
|
|
9489
|
+
}
|
|
9490
|
+
};
|
|
9491
|
+
|
|
9321
9492
|
// packages/parser/src/parser/rules/block/index.ts
|
|
9322
9493
|
var blockRules = [
|
|
9323
9494
|
blockCommentRule,
|
|
@@ -9347,6 +9518,7 @@ var blockRules = [
|
|
|
9347
9518
|
iframeRule,
|
|
9348
9519
|
iftagsRule,
|
|
9349
9520
|
bibliographyRule,
|
|
9521
|
+
galleryRule,
|
|
9350
9522
|
divRule
|
|
9351
9523
|
];
|
|
9352
9524
|
// packages/parser/src/parser/rules/inline/formatting/container.ts
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Position as Position2, Point, Version as Version2, Element as Element8, SyntaxTree as SyntaxTree3, ContainerType, ContainerData, AttributeMap, VariableMap, Alignment, LinkType, LinkLocation, LinkLabel, PageRef as PageRef3, ImageSource, FloatAlignment, ListType, ListItem, ListData, CodeBlockData as CodeBlockData2, TabData, TableCell, TableRow, TableData, DefinitionListItem, Module as Module5, CollapsibleData, ClearFloat, AnchorTarget, HeaderType, AlignType, HeadingLevel, Heading, DateItem, Embed, TocEntry as TocEntry2, Diagnostic as Diagnostic2, DiagnosticSeverity, ParseResult as ParseResult3 } from "@wdprlib/ast";
|
|
1
|
+
import { Position as Position2, Point, Version as Version2, Element as Element8, SyntaxTree as SyntaxTree3, ContainerType, ContainerData, AttributeMap, VariableMap, Alignment, LinkType, LinkLocation, LinkLabel, PageRef as PageRef3, ImageSource, FloatAlignment, ListType, ListItem, ListData, CodeBlockData as CodeBlockData2, TabData, TableCell, TableRow, TableData, DefinitionListItem, Module as Module5, CollapsibleData, ClearFloat, AnchorTarget, HeaderType, AlignType, HeadingLevel, Heading, DateItem, Embed, TocEntry as TocEntry2, GallerySize, GalleryOrder, GalleryItem, GalleryContent, GalleryData, Diagnostic as Diagnostic2, DiagnosticSeverity, ParseResult as ParseResult3 } from "@wdprlib/ast";
|
|
2
2
|
import { createPoint, createPosition, text, container, paragraph, bold, italics, heading, lineBreak, horizontalRule, link, list, listItemElements, listItemSubList } from "@wdprlib/ast";
|
|
3
3
|
import { WikitextMode, WikitextSettings as WikitextSettings4 } from "@wdprlib/ast";
|
|
4
4
|
import { createSettings, DEFAULT_SETTINGS } from "@wdprlib/ast";
|
|
@@ -1155,4 +1155,4 @@ interface ResolveOptions {
|
|
|
1155
1155
|
*/
|
|
1156
1156
|
declare function resolveModules(ast: SyntaxTree2, dataProvider: DataProvider, options: ResolveOptions): Promise<SyntaxTree2>;
|
|
1157
1157
|
import { STYLE_SLOT_PREFIX } from "@wdprlib/ast";
|
|
1158
|
-
export { tokenize, text, resolveTagCloud, resolveModules, resolveListUsers, resolveIncludesWithTrace, resolveIncludesAsync, resolveIncludes, preprocessIftags, parseTags, parseParent, parseOrder, parseNumericSelector, parseDateSelector, parseCategory, parse, paragraph, normalizeQuery, listItemSubList, listItemElements, list, link, lineBreak, italics, isTagCloudModule, isListUsersModule, horizontalRule, heading, extractListUsersVariables, extractIncludeReferences, extractDataRequirements, createToken, createSettings, createPosition, createPoint, container, compileTemplate, compileListUsersTemplate, bold, WikitextSettings4 as WikitextSettings, WikitextMode, Version2 as Version, VariableMap, VariableContext, UserInfo, TokenType, Token, TocEntry2 as TocEntry, TagCloudTagData, TagCloudModuleData, TagCloudExternalData, TagCloudDataRequirement, TagCloudDataFetcher, TableRow, TableData, TableCell, TabData, SyntaxTree3 as SyntaxTree, SiteContext, STYLE_SLOT_PREFIX, ResolveOptions, ResolveIncludesTraceResult, ResolveIncludesOptions, Position2 as Position, Point, ParserOptions, Parser, ParseResult3 as ParseResult, ParseFunction, PageRef3 as PageRef, PageData, NormalizedTags, NormalizedParent, NormalizedOrder, NormalizedNumericSelector, NormalizedListPagesQuery, NormalizedDateSelector, NormalizedCategory, ModuleSourceTransform, Module5 as Module, ListUsersVariableContext, ListUsersVariable, ListUsersUserData, ListUsersExternalData, ListUsersDataRequirement, ListUsersDataFetcher, ListUsersCompiledTemplate, ListType, ListPagesVariable, ListPagesQuery, ListPagesExternalData, ListPagesDataRequirement, ListPagesDataFetcher, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LexerOptions, Lexer, IncludeReference, IncludeIterationTrace, IncludeFetcher, IncludeDependency, ImageSource, HeadingLevel, Heading, HeaderType, FloatAlignment, ExtractionResult, Embed, Element8 as Element, DiagnosticSeverity, Diagnostic2 as Diagnostic, DefinitionListItem, DateItem, DataRequirements, DataProvider, DEFAULT_SETTINGS, ContainerType, ContainerData, CompiledTemplate, CollapsibleData, CodeBlockData2 as CodeBlockData, ClearFloat, AttributeMap, AsyncIncludeFetcher, AnchorTarget, Alignment, AlignType };
|
|
1158
|
+
export { tokenize, text, resolveTagCloud, resolveModules, resolveListUsers, resolveIncludesWithTrace, resolveIncludesAsync, resolveIncludes, preprocessIftags, parseTags, parseParent, parseOrder, parseNumericSelector, parseDateSelector, parseCategory, parse, paragraph, normalizeQuery, listItemSubList, listItemElements, list, link, lineBreak, italics, isTagCloudModule, isListUsersModule, horizontalRule, heading, extractListUsersVariables, extractIncludeReferences, extractDataRequirements, createToken, createSettings, createPosition, createPoint, container, compileTemplate, compileListUsersTemplate, bold, WikitextSettings4 as WikitextSettings, WikitextMode, Version2 as Version, VariableMap, VariableContext, UserInfo, TokenType, Token, TocEntry2 as TocEntry, TagCloudTagData, TagCloudModuleData, TagCloudExternalData, TagCloudDataRequirement, TagCloudDataFetcher, TableRow, TableData, TableCell, TabData, SyntaxTree3 as SyntaxTree, SiteContext, STYLE_SLOT_PREFIX, ResolveOptions, ResolveIncludesTraceResult, ResolveIncludesOptions, Position2 as Position, Point, ParserOptions, Parser, ParseResult3 as ParseResult, ParseFunction, PageRef3 as PageRef, PageData, NormalizedTags, NormalizedParent, NormalizedOrder, NormalizedNumericSelector, NormalizedListPagesQuery, NormalizedDateSelector, NormalizedCategory, ModuleSourceTransform, Module5 as Module, ListUsersVariableContext, ListUsersVariable, ListUsersUserData, ListUsersExternalData, ListUsersDataRequirement, ListUsersDataFetcher, ListUsersCompiledTemplate, ListType, ListPagesVariable, ListPagesQuery, ListPagesExternalData, ListPagesDataRequirement, ListPagesDataFetcher, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LexerOptions, Lexer, IncludeReference, IncludeIterationTrace, IncludeFetcher, IncludeDependency, ImageSource, HeadingLevel, Heading, HeaderType, GallerySize, GalleryOrder, GalleryItem, GalleryData, GalleryContent, FloatAlignment, ExtractionResult, Embed, Element8 as Element, DiagnosticSeverity, Diagnostic2 as Diagnostic, DefinitionListItem, DateItem, DataRequirements, DataProvider, DEFAULT_SETTINGS, ContainerType, ContainerData, CompiledTemplate, CollapsibleData, CodeBlockData2 as CodeBlockData, ClearFloat, AttributeMap, AsyncIncludeFetcher, AnchorTarget, Alignment, AlignType };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Position as Position2, Point, Version as Version2, Element as Element8, SyntaxTree as SyntaxTree3, ContainerType, ContainerData, AttributeMap, VariableMap, Alignment, LinkType, LinkLocation, LinkLabel, PageRef as PageRef3, ImageSource, FloatAlignment, ListType, ListItem, ListData, CodeBlockData as CodeBlockData2, TabData, TableCell, TableRow, TableData, DefinitionListItem, Module as Module5, CollapsibleData, ClearFloat, AnchorTarget, HeaderType, AlignType, HeadingLevel, Heading, DateItem, Embed, TocEntry as TocEntry2, Diagnostic as Diagnostic2, DiagnosticSeverity, ParseResult as ParseResult3 } from "@wdprlib/ast";
|
|
1
|
+
import { Position as Position2, Point, Version as Version2, Element as Element8, SyntaxTree as SyntaxTree3, ContainerType, ContainerData, AttributeMap, VariableMap, Alignment, LinkType, LinkLocation, LinkLabel, PageRef as PageRef3, ImageSource, FloatAlignment, ListType, ListItem, ListData, CodeBlockData as CodeBlockData2, TabData, TableCell, TableRow, TableData, DefinitionListItem, Module as Module5, CollapsibleData, ClearFloat, AnchorTarget, HeaderType, AlignType, HeadingLevel, Heading, DateItem, Embed, TocEntry as TocEntry2, GallerySize, GalleryOrder, GalleryItem, GalleryContent, GalleryData, Diagnostic as Diagnostic2, DiagnosticSeverity, ParseResult as ParseResult3 } from "@wdprlib/ast";
|
|
2
2
|
import { createPoint, createPosition, text, container, paragraph, bold, italics, heading, lineBreak, horizontalRule, link, list, listItemElements, listItemSubList } from "@wdprlib/ast";
|
|
3
3
|
import { WikitextMode, WikitextSettings as WikitextSettings4 } from "@wdprlib/ast";
|
|
4
4
|
import { createSettings, DEFAULT_SETTINGS } from "@wdprlib/ast";
|
|
@@ -1155,4 +1155,4 @@ interface ResolveOptions {
|
|
|
1155
1155
|
*/
|
|
1156
1156
|
declare function resolveModules(ast: SyntaxTree2, dataProvider: DataProvider, options: ResolveOptions): Promise<SyntaxTree2>;
|
|
1157
1157
|
import { STYLE_SLOT_PREFIX } from "@wdprlib/ast";
|
|
1158
|
-
export { tokenize, text, resolveTagCloud, resolveModules, resolveListUsers, resolveIncludesWithTrace, resolveIncludesAsync, resolveIncludes, preprocessIftags, parseTags, parseParent, parseOrder, parseNumericSelector, parseDateSelector, parseCategory, parse, paragraph, normalizeQuery, listItemSubList, listItemElements, list, link, lineBreak, italics, isTagCloudModule, isListUsersModule, horizontalRule, heading, extractListUsersVariables, extractIncludeReferences, extractDataRequirements, createToken, createSettings, createPosition, createPoint, container, compileTemplate, compileListUsersTemplate, bold, WikitextSettings4 as WikitextSettings, WikitextMode, Version2 as Version, VariableMap, VariableContext, UserInfo, TokenType, Token, TocEntry2 as TocEntry, TagCloudTagData, TagCloudModuleData, TagCloudExternalData, TagCloudDataRequirement, TagCloudDataFetcher, TableRow, TableData, TableCell, TabData, SyntaxTree3 as SyntaxTree, SiteContext, STYLE_SLOT_PREFIX, ResolveOptions, ResolveIncludesTraceResult, ResolveIncludesOptions, Position2 as Position, Point, ParserOptions, Parser, ParseResult3 as ParseResult, ParseFunction, PageRef3 as PageRef, PageData, NormalizedTags, NormalizedParent, NormalizedOrder, NormalizedNumericSelector, NormalizedListPagesQuery, NormalizedDateSelector, NormalizedCategory, ModuleSourceTransform, Module5 as Module, ListUsersVariableContext, ListUsersVariable, ListUsersUserData, ListUsersExternalData, ListUsersDataRequirement, ListUsersDataFetcher, ListUsersCompiledTemplate, ListType, ListPagesVariable, ListPagesQuery, ListPagesExternalData, ListPagesDataRequirement, ListPagesDataFetcher, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LexerOptions, Lexer, IncludeReference, IncludeIterationTrace, IncludeFetcher, IncludeDependency, ImageSource, HeadingLevel, Heading, HeaderType, FloatAlignment, ExtractionResult, Embed, Element8 as Element, DiagnosticSeverity, Diagnostic2 as Diagnostic, DefinitionListItem, DateItem, DataRequirements, DataProvider, DEFAULT_SETTINGS, ContainerType, ContainerData, CompiledTemplate, CollapsibleData, CodeBlockData2 as CodeBlockData, ClearFloat, AttributeMap, AsyncIncludeFetcher, AnchorTarget, Alignment, AlignType };
|
|
1158
|
+
export { tokenize, text, resolveTagCloud, resolveModules, resolveListUsers, resolveIncludesWithTrace, resolveIncludesAsync, resolveIncludes, preprocessIftags, parseTags, parseParent, parseOrder, parseNumericSelector, parseDateSelector, parseCategory, parse, paragraph, normalizeQuery, listItemSubList, listItemElements, list, link, lineBreak, italics, isTagCloudModule, isListUsersModule, horizontalRule, heading, extractListUsersVariables, extractIncludeReferences, extractDataRequirements, createToken, createSettings, createPosition, createPoint, container, compileTemplate, compileListUsersTemplate, bold, WikitextSettings4 as WikitextSettings, WikitextMode, Version2 as Version, VariableMap, VariableContext, UserInfo, TokenType, Token, TocEntry2 as TocEntry, TagCloudTagData, TagCloudModuleData, TagCloudExternalData, TagCloudDataRequirement, TagCloudDataFetcher, TableRow, TableData, TableCell, TabData, SyntaxTree3 as SyntaxTree, SiteContext, STYLE_SLOT_PREFIX, ResolveOptions, ResolveIncludesTraceResult, ResolveIncludesOptions, Position2 as Position, Point, ParserOptions, Parser, ParseResult3 as ParseResult, ParseFunction, PageRef3 as PageRef, PageData, NormalizedTags, NormalizedParent, NormalizedOrder, NormalizedNumericSelector, NormalizedListPagesQuery, NormalizedDateSelector, NormalizedCategory, ModuleSourceTransform, Module5 as Module, ListUsersVariableContext, ListUsersVariable, ListUsersUserData, ListUsersExternalData, ListUsersDataRequirement, ListUsersDataFetcher, ListUsersCompiledTemplate, ListType, ListPagesVariable, ListPagesQuery, ListPagesExternalData, ListPagesDataRequirement, ListPagesDataFetcher, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LexerOptions, Lexer, IncludeReference, IncludeIterationTrace, IncludeFetcher, IncludeDependency, ImageSource, HeadingLevel, Heading, HeaderType, GallerySize, GalleryOrder, GalleryItem, GalleryData, GalleryContent, FloatAlignment, ExtractionResult, Embed, Element8 as Element, DiagnosticSeverity, Diagnostic2 as Diagnostic, DefinitionListItem, DateItem, DataRequirements, DataProvider, DEFAULT_SETTINGS, ContainerType, ContainerData, CompiledTemplate, CollapsibleData, CodeBlockData2 as CodeBlockData, ClearFloat, AttributeMap, AsyncIncludeFetcher, AnchorTarget, Alignment, AlignType };
|
package/dist/index.js
CHANGED
|
@@ -9257,6 +9257,177 @@ var bibliographyRule = {
|
|
|
9257
9257
|
}
|
|
9258
9258
|
};
|
|
9259
9259
|
|
|
9260
|
+
// packages/parser/src/parser/rules/block/gallery/items.ts
|
|
9261
|
+
function parseGalleryItemLine(content) {
|
|
9262
|
+
const spacePos = content.indexOf(" ");
|
|
9263
|
+
let source = spacePos < 0 ? content : content.slice(0, spacePos);
|
|
9264
|
+
const attrText = spacePos < 0 ? "" : content.slice(spacePos + 1);
|
|
9265
|
+
let newWindow = false;
|
|
9266
|
+
if (source.startsWith("*")) {
|
|
9267
|
+
source = source.slice(1);
|
|
9268
|
+
newWindow = true;
|
|
9269
|
+
}
|
|
9270
|
+
const attrs = parseItemAttrs(attrText);
|
|
9271
|
+
let link = attrs.get("link") ?? null;
|
|
9272
|
+
if (link !== null && link.startsWith("*")) {
|
|
9273
|
+
newWindow = true;
|
|
9274
|
+
link = link.slice(1);
|
|
9275
|
+
}
|
|
9276
|
+
const alt = attrs.get("alt") ?? null;
|
|
9277
|
+
return { source, link, alt, newWindow };
|
|
9278
|
+
}
|
|
9279
|
+
function parseItemAttrs(text) {
|
|
9280
|
+
const attrs = new Map;
|
|
9281
|
+
const parts = text.trim().split('="');
|
|
9282
|
+
let key = parts[0]?.trim() ?? "";
|
|
9283
|
+
for (let i = 1;i < parts.length; i++) {
|
|
9284
|
+
const val = parts[i] ?? "";
|
|
9285
|
+
const quotePos = val.lastIndexOf('"');
|
|
9286
|
+
if (quotePos < 0) {
|
|
9287
|
+
attrs.set(key, "");
|
|
9288
|
+
key = val.slice(1).trim();
|
|
9289
|
+
} else {
|
|
9290
|
+
attrs.set(key, stripslashes(val.slice(0, quotePos)));
|
|
9291
|
+
key = val.slice(quotePos + 1).trim();
|
|
9292
|
+
}
|
|
9293
|
+
}
|
|
9294
|
+
return attrs;
|
|
9295
|
+
}
|
|
9296
|
+
function stripslashes(value) {
|
|
9297
|
+
return value.replace(/\\(.)/gs, "$1").replace(/\\$/, "");
|
|
9298
|
+
}
|
|
9299
|
+
|
|
9300
|
+
// packages/parser/src/parser/rules/block/gallery/index.ts
|
|
9301
|
+
var GALLERY_SIZES = ["small", "medium", "thumbnail", "square", "original"];
|
|
9302
|
+
function normalizeSize(value) {
|
|
9303
|
+
return value !== undefined && GALLERY_SIZES.includes(value) ? value : "thumbnail";
|
|
9304
|
+
}
|
|
9305
|
+
function normalizeViewer(value) {
|
|
9306
|
+
return value !== "no" && value !== "false";
|
|
9307
|
+
}
|
|
9308
|
+
function normalizeOrder(value) {
|
|
9309
|
+
switch (value) {
|
|
9310
|
+
case "name":
|
|
9311
|
+
case "name desc":
|
|
9312
|
+
case "created_at":
|
|
9313
|
+
case "created_at desc":
|
|
9314
|
+
return value;
|
|
9315
|
+
case "nameDesc":
|
|
9316
|
+
return "name desc";
|
|
9317
|
+
case "dateAdded":
|
|
9318
|
+
return "created_at";
|
|
9319
|
+
case "dateAddedDesc":
|
|
9320
|
+
return "created_at desc";
|
|
9321
|
+
case "name desc desc":
|
|
9322
|
+
return "name";
|
|
9323
|
+
case "created_at desc desc":
|
|
9324
|
+
return "created_at";
|
|
9325
|
+
default:
|
|
9326
|
+
return "name";
|
|
9327
|
+
}
|
|
9328
|
+
}
|
|
9329
|
+
function tryParseGalleryContent(ctx, pos) {
|
|
9330
|
+
const start = pos;
|
|
9331
|
+
if (ctx.tokens[pos]?.type !== "NEWLINE") {
|
|
9332
|
+
return null;
|
|
9333
|
+
}
|
|
9334
|
+
const lines = [];
|
|
9335
|
+
let p = pos + 1;
|
|
9336
|
+
for (;; ) {
|
|
9337
|
+
const colon = ctx.tokens[p];
|
|
9338
|
+
if (colon?.type !== "COLON" || !colon.lineStart) {
|
|
9339
|
+
break;
|
|
9340
|
+
}
|
|
9341
|
+
const space = ctx.tokens[p + 1];
|
|
9342
|
+
if (space?.type !== "WHITESPACE" || !space.value.startsWith(" ")) {
|
|
9343
|
+
break;
|
|
9344
|
+
}
|
|
9345
|
+
let content = "";
|
|
9346
|
+
let q = p + 1;
|
|
9347
|
+
while (q < ctx.tokens.length) {
|
|
9348
|
+
const token5 = ctx.tokens[q];
|
|
9349
|
+
if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
|
|
9350
|
+
break;
|
|
9351
|
+
}
|
|
9352
|
+
content += token5.value;
|
|
9353
|
+
q++;
|
|
9354
|
+
}
|
|
9355
|
+
if (content === " ") {
|
|
9356
|
+
return null;
|
|
9357
|
+
}
|
|
9358
|
+
if (ctx.tokens[q]?.type !== "NEWLINE") {
|
|
9359
|
+
return null;
|
|
9360
|
+
}
|
|
9361
|
+
lines.push(content.trim());
|
|
9362
|
+
p = q + 1;
|
|
9363
|
+
}
|
|
9364
|
+
if (lines.length === 0) {
|
|
9365
|
+
return null;
|
|
9366
|
+
}
|
|
9367
|
+
if (ctx.tokens[p]?.type !== "BLOCK_END_OPEN") {
|
|
9368
|
+
return null;
|
|
9369
|
+
}
|
|
9370
|
+
const nameResult = parseBlockName(ctx, p + 1);
|
|
9371
|
+
if (!nameResult || nameResult.name !== "gallery") {
|
|
9372
|
+
return null;
|
|
9373
|
+
}
|
|
9374
|
+
const closePos = p + 1 + nameResult.consumed;
|
|
9375
|
+
if (ctx.tokens[closePos]?.type !== "BLOCK_CLOSE") {
|
|
9376
|
+
return null;
|
|
9377
|
+
}
|
|
9378
|
+
return { lines, consumed: closePos + 1 - start };
|
|
9379
|
+
}
|
|
9380
|
+
var galleryRule = {
|
|
9381
|
+
name: "gallery",
|
|
9382
|
+
startTokens: ["BLOCK_OPEN"],
|
|
9383
|
+
requiresLineStart: true,
|
|
9384
|
+
parse(ctx) {
|
|
9385
|
+
if (ctx.tokens[ctx.pos]?.type !== "BLOCK_OPEN") {
|
|
9386
|
+
return { success: false };
|
|
9387
|
+
}
|
|
9388
|
+
let pos = ctx.pos + 1;
|
|
9389
|
+
const nameResult = parseBlockName(ctx, pos);
|
|
9390
|
+
if (!nameResult || nameResult.name !== "gallery") {
|
|
9391
|
+
return { success: false };
|
|
9392
|
+
}
|
|
9393
|
+
pos += nameResult.consumed;
|
|
9394
|
+
const attrResult = parseAttributesRaw(ctx, pos);
|
|
9395
|
+
pos += attrResult.consumed;
|
|
9396
|
+
if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
|
|
9397
|
+
return { success: false };
|
|
9398
|
+
}
|
|
9399
|
+
pos++;
|
|
9400
|
+
const size = normalizeSize(attrResult.attrs.size);
|
|
9401
|
+
const viewer = normalizeViewer(attrResult.attrs.viewer);
|
|
9402
|
+
const order = normalizeOrder(attrResult.attrs.order);
|
|
9403
|
+
const openConsumed = pos - ctx.pos;
|
|
9404
|
+
const content = tryParseGalleryContent(ctx, pos);
|
|
9405
|
+
if (!content) {
|
|
9406
|
+
return {
|
|
9407
|
+
success: true,
|
|
9408
|
+
elements: [
|
|
9409
|
+
{
|
|
9410
|
+
element: "gallery",
|
|
9411
|
+
data: { size, order, viewer, content: { type: "auto", files: null } }
|
|
9412
|
+
}
|
|
9413
|
+
],
|
|
9414
|
+
consumed: openConsumed
|
|
9415
|
+
};
|
|
9416
|
+
}
|
|
9417
|
+
const items = content.lines.map(parseGalleryItemLine);
|
|
9418
|
+
return {
|
|
9419
|
+
success: true,
|
|
9420
|
+
elements: [
|
|
9421
|
+
{
|
|
9422
|
+
element: "gallery",
|
|
9423
|
+
data: { size, order, viewer, content: { type: "items", items } }
|
|
9424
|
+
}
|
|
9425
|
+
],
|
|
9426
|
+
consumed: openConsumed + content.consumed
|
|
9427
|
+
};
|
|
9428
|
+
}
|
|
9429
|
+
};
|
|
9430
|
+
|
|
9260
9431
|
// packages/parser/src/parser/rules/block/index.ts
|
|
9261
9432
|
var blockRules = [
|
|
9262
9433
|
blockCommentRule,
|
|
@@ -9286,6 +9457,7 @@ var blockRules = [
|
|
|
9286
9457
|
iframeRule,
|
|
9287
9458
|
iftagsRule,
|
|
9288
9459
|
bibliographyRule,
|
|
9460
|
+
galleryRule,
|
|
9289
9461
|
divRule
|
|
9290
9462
|
];
|
|
9291
9463
|
// packages/parser/src/parser/rules/inline/formatting/container.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wdprlib/parser",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "Parser for Wikidot markup",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ast",
|
|
@@ -41,6 +41,6 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@braintree/sanitize-url": "^7.1.1",
|
|
44
|
-
"@wdprlib/ast": "2.
|
|
44
|
+
"@wdprlib/ast": "2.3.0"
|
|
45
45
|
}
|
|
46
46
|
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Block rule for the Wikidot `[[gallery]]` image gallery.
|
|
4
|
+
*
|
|
5
|
+
* ```
|
|
6
|
+
* [[gallery size="thumbnail" order="name" viewer="no"]]
|
|
7
|
+
* : first-image.jpg
|
|
8
|
+
* : *page/other-image.jpg link="some-page" alt="Alt text"
|
|
9
|
+
* [[/gallery]]
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* The content form requires the `]]` to be followed immediately by one or
|
|
13
|
+
* more `: source` lines and a `[[/gallery]]` close tag (Wikidot regex
|
|
14
|
+
* `\[\[gallery(\s[^\]]*?)?\]\](?:((?:\n: [^\n]+)+)\n\[\[\/gallery\]\])?`).
|
|
15
|
+
* Anything else falls back to the standalone content-less form, which shows
|
|
16
|
+
* the current page's image attachments after data resolution
|
|
17
|
+
* (`content: { type: "auto", files: null }`); the following text then
|
|
18
|
+
* parses normally.
|
|
19
|
+
*
|
|
20
|
+
* Honored opening-tag attributes are `size` (small / medium / thumbnail /
|
|
21
|
+
* square / original, fallback thumbnail), `viewer` (`"no"`/`"false"`
|
|
22
|
+
* disable the lightbox) and `order` (auto-collection sort order, with
|
|
23
|
+
* Wikidot's deprecated aliases normalized).
|
|
24
|
+
*
|
|
25
|
+
* Deliberate differences from the Wikidot parser: the opening tag must
|
|
26
|
+
* fit on one line and attribute names are lowercased with unquoted values
|
|
27
|
+
* accepted (both shared with wdpr's other block rules), and `flickr:`
|
|
28
|
+
* sources get no special treatment — they resolve like any other
|
|
29
|
+
* filename (wdpr does not call the Flickr API).
|
|
30
|
+
*
|
|
31
|
+
* @module
|
|
32
|
+
*/
|
|
33
|
+
import type { Element, GalleryItem, GalleryOrder, GallerySize } from "@wdprlib/ast";
|
|
34
|
+
import type { BlockRule, ParseContext, RuleResult } from "../../types";
|
|
35
|
+
import { parseAttributesRaw, parseBlockName } from "../utils";
|
|
36
|
+
import { parseGalleryItemLine } from "./items";
|
|
37
|
+
|
|
38
|
+
export { parseGalleryItemLine } from "./items";
|
|
39
|
+
|
|
40
|
+
const GALLERY_SIZES: readonly string[] = ["small", "medium", "thumbnail", "square", "original"];
|
|
41
|
+
|
|
42
|
+
/** Validate a size keyword, falling back to thumbnail like Wikidot. */
|
|
43
|
+
function normalizeSize(value: string | undefined): GallerySize {
|
|
44
|
+
return value !== undefined && GALLERY_SIZES.includes(value)
|
|
45
|
+
? (value as GallerySize)
|
|
46
|
+
: "thumbnail";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** `viewer="no"` / `viewer="false"` disable the lightbox; anything else enables it. */
|
|
50
|
+
function normalizeViewer(value: string | undefined): boolean {
|
|
51
|
+
return value !== "no" && value !== "false";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Normalize the `order` attribute, folding Wikidot's deprecated aliases
|
|
56
|
+
* (`nameDesc`, `dateAdded`, `dateAddedDesc`) and the documented
|
|
57
|
+
* ListPages-compatibility forms (`"name desc desc"` / `"created_at desc
|
|
58
|
+
* desc"`, which mean the same as without the `desc desc`).
|
|
59
|
+
*/
|
|
60
|
+
function normalizeOrder(value: string | undefined): GalleryOrder {
|
|
61
|
+
switch (value) {
|
|
62
|
+
case "name":
|
|
63
|
+
case "name desc":
|
|
64
|
+
case "created_at":
|
|
65
|
+
case "created_at desc":
|
|
66
|
+
return value;
|
|
67
|
+
case "nameDesc":
|
|
68
|
+
return "name desc";
|
|
69
|
+
case "dateAdded":
|
|
70
|
+
return "created_at";
|
|
71
|
+
case "dateAddedDesc":
|
|
72
|
+
return "created_at desc";
|
|
73
|
+
case "name desc desc":
|
|
74
|
+
return "name";
|
|
75
|
+
case "created_at desc desc":
|
|
76
|
+
return "created_at";
|
|
77
|
+
default:
|
|
78
|
+
return "name";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface GalleryContentResult {
|
|
83
|
+
/** Trimmed line contents (after the leading `: `) */
|
|
84
|
+
lines: string[];
|
|
85
|
+
/** Token count from the content start (the NEWLINE after `]]`) through `[[/gallery]]` */
|
|
86
|
+
consumed: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Try to match the content form starting at `pos` (the token right after the
|
|
91
|
+
* opening tag's `]]`): one NEWLINE, then consecutive `: source` lines, then a
|
|
92
|
+
* NEWLINE directly followed by `[[/gallery]]`. Returns null when the token
|
|
93
|
+
* stream deviates from the Wikidot regex, which makes the gallery standalone.
|
|
94
|
+
*/
|
|
95
|
+
function tryParseGalleryContent(ctx: ParseContext, pos: number): GalleryContentResult | null {
|
|
96
|
+
const start = pos;
|
|
97
|
+
if (ctx.tokens[pos]?.type !== "NEWLINE") {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const lines: string[] = [];
|
|
102
|
+
let p = pos + 1;
|
|
103
|
+
|
|
104
|
+
for (;;) {
|
|
105
|
+
const colon = ctx.tokens[p];
|
|
106
|
+
if (colon?.type !== "COLON" || !colon.lineStart) {
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
// Wikidot requires a literal space after the colon (`\n: `)
|
|
110
|
+
const space = ctx.tokens[p + 1];
|
|
111
|
+
if (space?.type !== "WHITESPACE" || !space.value.startsWith(" ")) {
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let content = "";
|
|
116
|
+
let q = p + 1;
|
|
117
|
+
while (q < ctx.tokens.length) {
|
|
118
|
+
const token = ctx.tokens[q];
|
|
119
|
+
if (!token || token.type === "NEWLINE" || token.type === "EOF") {
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
content += token.value;
|
|
123
|
+
q++;
|
|
124
|
+
}
|
|
125
|
+
// `: [^\n]+` needs at least one character after the space
|
|
126
|
+
if (content === " ") {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
if (ctx.tokens[q]?.type !== "NEWLINE") {
|
|
130
|
+
// line hit EOF: the close tag can no longer follow on its own line
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
lines.push(content.trim());
|
|
135
|
+
p = q + 1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (lines.length === 0) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// The last consumed NEWLINE must be directly followed by [[/gallery]]
|
|
143
|
+
if (ctx.tokens[p]?.type !== "BLOCK_END_OPEN") {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const nameResult = parseBlockName(ctx, p + 1);
|
|
147
|
+
if (!nameResult || nameResult.name !== "gallery") {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const closePos = p + 1 + nameResult.consumed;
|
|
151
|
+
if (ctx.tokens[closePos]?.type !== "BLOCK_CLOSE") {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { lines, consumed: closePos + 1 - start };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export const galleryRule: BlockRule = {
|
|
159
|
+
name: "gallery",
|
|
160
|
+
startTokens: ["BLOCK_OPEN"],
|
|
161
|
+
requiresLineStart: true,
|
|
162
|
+
|
|
163
|
+
parse(ctx: ParseContext): RuleResult<Element> {
|
|
164
|
+
if (ctx.tokens[ctx.pos]?.type !== "BLOCK_OPEN") {
|
|
165
|
+
return { success: false };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let pos = ctx.pos + 1;
|
|
169
|
+
const nameResult = parseBlockName(ctx, pos);
|
|
170
|
+
if (!nameResult || nameResult.name !== "gallery") {
|
|
171
|
+
return { success: false };
|
|
172
|
+
}
|
|
173
|
+
pos += nameResult.consumed;
|
|
174
|
+
|
|
175
|
+
const attrResult = parseAttributesRaw(ctx, pos);
|
|
176
|
+
pos += attrResult.consumed;
|
|
177
|
+
|
|
178
|
+
if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
|
|
179
|
+
return { success: false };
|
|
180
|
+
}
|
|
181
|
+
pos++;
|
|
182
|
+
|
|
183
|
+
const size = normalizeSize(attrResult.attrs.size);
|
|
184
|
+
const viewer = normalizeViewer(attrResult.attrs.viewer);
|
|
185
|
+
const order = normalizeOrder(attrResult.attrs.order);
|
|
186
|
+
const openConsumed = pos - ctx.pos;
|
|
187
|
+
|
|
188
|
+
const content = tryParseGalleryContent(ctx, pos);
|
|
189
|
+
if (!content) {
|
|
190
|
+
return {
|
|
191
|
+
success: true,
|
|
192
|
+
elements: [
|
|
193
|
+
{
|
|
194
|
+
element: "gallery",
|
|
195
|
+
data: { size, order, viewer, content: { type: "auto", files: null } },
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
consumed: openConsumed,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const items: GalleryItem[] = content.lines.map(parseGalleryItemLine);
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
success: true,
|
|
206
|
+
elements: [
|
|
207
|
+
{
|
|
208
|
+
element: "gallery",
|
|
209
|
+
data: { size, order, viewer, content: { type: "items", items } },
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
consumed: openConsumed + content.consumed,
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { GalleryItem } from "@wdprlib/ast";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse one gallery content line (the text after the leading `: `,
|
|
5
|
+
* already trimmed) into a {@link GalleryItem}.
|
|
6
|
+
*
|
|
7
|
+
* The text before the first space is the source; the rest is parsed as
|
|
8
|
+
* `key="value"` attributes of which `link` and `alt` are honored. A
|
|
9
|
+
* leading `*` on the source or on the link value requests a new window.
|
|
10
|
+
*/
|
|
11
|
+
export function parseGalleryItemLine(content: string): GalleryItem {
|
|
12
|
+
const spacePos = content.indexOf(" ");
|
|
13
|
+
let source = spacePos < 0 ? content : content.slice(0, spacePos);
|
|
14
|
+
const attrText = spacePos < 0 ? "" : content.slice(spacePos + 1);
|
|
15
|
+
|
|
16
|
+
let newWindow = false;
|
|
17
|
+
if (source.startsWith("*")) {
|
|
18
|
+
source = source.slice(1);
|
|
19
|
+
newWindow = true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const attrs = parseItemAttrs(attrText);
|
|
23
|
+
let link = attrs.get("link") ?? null;
|
|
24
|
+
if (link !== null && link.startsWith("*")) {
|
|
25
|
+
newWindow = true;
|
|
26
|
+
link = link.slice(1);
|
|
27
|
+
}
|
|
28
|
+
const alt = attrs.get("alt") ?? null;
|
|
29
|
+
|
|
30
|
+
return { source, link, alt, newWindow };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parse `key="value"` attribute pairs from a gallery item line, following
|
|
35
|
+
* the splitting behavior of Wikidot's gallery `getAttrs()`: fragments are
|
|
36
|
+
* separated by `="`, values run to the last `"` in each fragment and are
|
|
37
|
+
* backslash-unescaped, and keys are not lowercased.
|
|
38
|
+
*/
|
|
39
|
+
function parseItemAttrs(text: string): Map<string, string> {
|
|
40
|
+
const attrs = new Map<string, string>();
|
|
41
|
+
const parts = text.trim().split('="');
|
|
42
|
+
let key = parts[0]?.trim() ?? "";
|
|
43
|
+
|
|
44
|
+
for (let i = 1; i < parts.length; i++) {
|
|
45
|
+
const val = parts[i] ?? "";
|
|
46
|
+
const quotePos = val.lastIndexOf('"');
|
|
47
|
+
if (quotePos < 0) {
|
|
48
|
+
attrs.set(key, "");
|
|
49
|
+
key = val.slice(1).trim();
|
|
50
|
+
} else {
|
|
51
|
+
attrs.set(key, stripslashes(val.slice(0, quotePos)));
|
|
52
|
+
key = val.slice(quotePos + 1).trim();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return attrs;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** PHP-style `stripslashes()`: drop each escaping backslash, and a trailing lone one. */
|
|
60
|
+
function stripslashes(value: string): string {
|
|
61
|
+
return value.replace(/\\(.)/gs, "$1").replace(/\\$/, "");
|
|
62
|
+
}
|
|
@@ -49,6 +49,7 @@ import { iftagsRule } from "./iftags";
|
|
|
49
49
|
import { tocRule } from "./toc";
|
|
50
50
|
import { orphanLiRule } from "./orphan-li";
|
|
51
51
|
import { bibliographyRule } from "./bibliography";
|
|
52
|
+
import { galleryRule } from "./gallery";
|
|
52
53
|
|
|
53
54
|
export { headingRule } from "./heading";
|
|
54
55
|
export { horizontalRuleRule } from "./horizontal-rule";
|
|
@@ -79,6 +80,7 @@ export { iftagsRule } from "./iftags";
|
|
|
79
80
|
export { tocRule } from "./toc";
|
|
80
81
|
export { orphanLiRule } from "./orphan-li";
|
|
81
82
|
export { bibliographyRule } from "./bibliography";
|
|
83
|
+
export { galleryRule } from "./gallery";
|
|
82
84
|
|
|
83
85
|
/**
|
|
84
86
|
* All block rules in priority order.
|
|
@@ -120,6 +122,7 @@ export const blockRules: BlockRule[] = [
|
|
|
120
122
|
iframeRule,
|
|
121
123
|
iftagsRule,
|
|
122
124
|
bibliographyRule,
|
|
125
|
+
galleryRule,
|
|
123
126
|
divRule,
|
|
124
127
|
// paragraphRule is not included - used as fallback
|
|
125
128
|
];
|