@wdprlib/parser 4.1.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/dist/index.cjs CHANGED
@@ -31,7 +31,8 @@ var __export = (target, all) => {
31
31
  var exports_src = {};
32
32
  __export(exports_src, {
33
33
  tokenize: () => tokenize,
34
- text: () => import_ast5.text,
34
+ text: () => import_ast6.text,
35
+ resolveTagCloud: () => resolveTagCloud,
35
36
  resolveModules: () => resolveModules,
36
37
  resolveListUsers: () => resolveListUsers,
37
38
  resolveIncludesWithTrace: () => resolveIncludesWithTrace,
@@ -45,36 +46,37 @@ __export(exports_src, {
45
46
  parseDateSelector: () => parseDateSelector,
46
47
  parseCategory: () => parseCategory,
47
48
  parse: () => parse,
48
- paragraph: () => import_ast5.paragraph,
49
+ paragraph: () => import_ast6.paragraph,
49
50
  normalizeQuery: () => normalizeQuery,
50
- listItemSubList: () => import_ast5.listItemSubList,
51
- listItemElements: () => import_ast5.listItemElements,
52
- list: () => import_ast5.list,
53
- link: () => import_ast5.link,
54
- lineBreak: () => import_ast5.lineBreak,
55
- italics: () => import_ast5.italics,
51
+ listItemSubList: () => import_ast6.listItemSubList,
52
+ listItemElements: () => import_ast6.listItemElements,
53
+ list: () => import_ast6.list,
54
+ link: () => import_ast6.link,
55
+ lineBreak: () => import_ast6.lineBreak,
56
+ italics: () => import_ast6.italics,
57
+ isTagCloudModule: () => isTagCloudModule,
56
58
  isListUsersModule: () => isListUsersModule2,
57
- horizontalRule: () => import_ast5.horizontalRule,
58
- heading: () => import_ast5.heading,
59
+ horizontalRule: () => import_ast6.horizontalRule,
60
+ heading: () => import_ast6.heading,
59
61
  extractListUsersVariables: () => extractListUsersVariables,
60
62
  extractIncludeReferences: () => extractIncludeReferences,
61
63
  extractDataRequirements: () => extractDataRequirements,
62
64
  createToken: () => createToken,
63
- createSettings: () => import_ast6.createSettings,
64
- createPosition: () => import_ast5.createPosition,
65
- createPoint: () => import_ast5.createPoint,
66
- container: () => import_ast5.container,
65
+ createSettings: () => import_ast7.createSettings,
66
+ createPosition: () => import_ast6.createPosition,
67
+ createPoint: () => import_ast6.createPoint,
68
+ container: () => import_ast6.container,
67
69
  compileTemplate: () => compileTemplate,
68
70
  compileListUsersTemplate: () => compileListUsersTemplate,
69
- bold: () => import_ast5.bold,
70
- STYLE_SLOT_PREFIX: () => import_ast2.STYLE_SLOT_PREFIX,
71
+ bold: () => import_ast6.bold,
72
+ STYLE_SLOT_PREFIX: () => import_ast3.STYLE_SLOT_PREFIX,
71
73
  Parser: () => Parser,
72
74
  Lexer: () => Lexer,
73
- DEFAULT_SETTINGS: () => import_ast6.DEFAULT_SETTINGS
75
+ DEFAULT_SETTINGS: () => import_ast7.DEFAULT_SETTINGS
74
76
  });
75
77
  module.exports = __toCommonJS(exports_src);
76
- var import_ast5 = require("@wdprlib/ast");
77
78
  var import_ast6 = require("@wdprlib/ast");
79
+ var import_ast7 = require("@wdprlib/ast");
78
80
 
79
81
  // packages/parser/src/lexer/tokens.ts
80
82
  function createToken(type, value, position, lineStart = false) {
@@ -1441,7 +1443,7 @@ function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
1441
1443
  }
1442
1444
 
1443
1445
  // packages/parser/src/parser/parse/context.ts
1444
- var import_ast3 = require("@wdprlib/ast");
1446
+ var import_ast4 = require("@wdprlib/ast");
1445
1447
 
1446
1448
  // packages/parser/src/parser/rules/tokens.ts
1447
1449
  function currentToken(ctx) {
@@ -4527,6 +4529,92 @@ var listUsersModuleRule = {
4527
4529
  }
4528
4530
  };
4529
4531
 
4532
+ // packages/parser/src/parser/rules/block/module/tagcloud/parser.ts
4533
+ var import_ast = require("@wdprlib/ast");
4534
+ var FONT_SIZE_PATTERN = new RegExp(`^([0-9]+)(${import_ast.CSS_LENGTH_UNITS.join("|")})$`, "i");
4535
+ var COLOR_PATTERN = /^[0-9]+,[0-9]+,[0-9]+$/;
4536
+ var POSITIVE_INT_PATTERN = /^[0-9]+$/;
4537
+ var ERROR_FONT_FORMAT = "Unsupported format for font size. Use a number followed by a CSS length unit such as px, em or %.";
4538
+ var ERROR_FONT_MISMATCH = "Format for minFontSize and maxFontSize must use the same unit.";
4539
+ var ERROR_COLOR_FORMAT = 'Unsupported color format. Use "RRR,GGG,BBB" for Red,Green,Blue each within 0-255 range.';
4540
+ function errorBlock(message) {
4541
+ return {
4542
+ element: "container",
4543
+ data: {
4544
+ type: "div",
4545
+ attributes: { class: "error-block" },
4546
+ elements: [{ element: "text", data: message }]
4547
+ }
4548
+ };
4549
+ }
4550
+ function parseLimit(value) {
4551
+ if (!value || !POSITIVE_INT_PATTERN.test(value))
4552
+ return 50;
4553
+ const num = Number.parseInt(value, 10);
4554
+ return Number.isSafeInteger(num) && num > 0 ? num : 50;
4555
+ }
4556
+ function normalizeTarget(value) {
4557
+ if (!value)
4558
+ return "/system:page-tags/tag/";
4559
+ let target = value;
4560
+ if (!target.startsWith("/"))
4561
+ target = `/${target}`;
4562
+ if (!target.endsWith("/"))
4563
+ target = `${target}/`;
4564
+ return `${target}tag/`;
4565
+ }
4566
+ function parseColor(value) {
4567
+ const [r = 0, g = 0, b = 0] = value.split(",").map((part) => Number.parseInt(part, 10));
4568
+ return [r, g, b];
4569
+ }
4570
+ var tagCloudModuleRule = {
4571
+ name: "module-tagcloud",
4572
+ acceptsNames: ["tagcloud"],
4573
+ hasBody: false,
4574
+ parse(_ctx, _pos, args) {
4575
+ const maxFontSize = args.maxfontsize;
4576
+ const minFontSize = args.minfontsize;
4577
+ const maxColor = args.maxcolor;
4578
+ const minColor = args.mincolor;
4579
+ let sizeSmall = 100;
4580
+ let sizeBig = 300;
4581
+ let fontSizeUnit = "%";
4582
+ if (maxFontSize && minFontSize) {
4583
+ const maxMatch = FONT_SIZE_PATTERN.exec(maxFontSize);
4584
+ if (!maxMatch)
4585
+ return errorBlock(ERROR_FONT_FORMAT);
4586
+ const maxUnit = maxMatch[2].toLowerCase();
4587
+ const minMatch = FONT_SIZE_PATTERN.exec(minFontSize);
4588
+ if (!minMatch || minMatch[2].toLowerCase() !== maxUnit) {
4589
+ return errorBlock(ERROR_FONT_MISMATCH);
4590
+ }
4591
+ sizeBig = Number.parseInt(maxMatch[1], 10);
4592
+ sizeSmall = Number.parseInt(minMatch[1], 10);
4593
+ fontSizeUnit = maxUnit;
4594
+ }
4595
+ let colorSmall = [128, 128, 192];
4596
+ let colorBig = [64, 64, 128];
4597
+ if (maxColor && minColor) {
4598
+ if (!COLOR_PATTERN.test(maxColor) || !COLOR_PATTERN.test(minColor)) {
4599
+ return errorBlock(ERROR_COLOR_FORMAT);
4600
+ }
4601
+ colorSmall = parseColor(minColor);
4602
+ colorBig = parseColor(maxColor);
4603
+ }
4604
+ return {
4605
+ module: "tag-cloud",
4606
+ "min-font-size": sizeSmall,
4607
+ "max-font-size": sizeBig,
4608
+ "font-size-unit": fontSizeUnit,
4609
+ "min-color": colorSmall,
4610
+ "max-color": colorBig,
4611
+ target: normalizeTarget(args.target),
4612
+ limit: parseLimit(args.limit),
4613
+ category: args.category || null
4614
+ };
4615
+ }
4616
+ };
4617
+
4530
4618
  // packages/parser/src/parser/rules/block/module/mapping.ts
4531
4619
  var MODULE_RULES = [
4532
4620
  rateModuleRule,
@@ -4536,7 +4624,8 @@ var MODULE_RULES = [
4536
4624
  joinModuleRule,
4537
4625
  pageTreeModuleRule,
4538
4626
  listPagesModuleRule,
4539
- listUsersModuleRule
4627
+ listUsersModuleRule,
4628
+ tagCloudModuleRule
4540
4629
  ];
4541
4630
  var moduleRuleMap = new Map;
4542
4631
  for (const rule of MODULE_RULES) {
@@ -5617,18 +5706,130 @@ function extractListUsersModule(listUsers, state, result) {
5617
5706
  result.compiledListUsersTemplates.set(id, compileListUsersTemplate(body));
5618
5707
  }
5619
5708
 
5709
+ // packages/parser/src/parser/rules/block/module/tagcloud/resolve.ts
5710
+ function isTagCloudModule(module2) {
5711
+ return module2.module === "tag-cloud";
5712
+ }
5713
+ var NO_TAGS_MESSAGE_PREFIX = "It seems you have no tags attached to pages. To attach a tag simply click on the ";
5714
+ var NO_TAGS_MESSAGE_SUFFIX = " button at the bottom of any page.";
5715
+ function rawUrlEncode(value) {
5716
+ return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
5717
+ }
5718
+ function selectTags(tags, limit) {
5719
+ return tags.toSorted((a, b) => b.weight - a.weight || compareTags(a.tag, b.tag)).slice(0, limit).toSorted((a, b) => compareTags(a.tag, b.tag));
5720
+ }
5721
+ function compareTags(a, b) {
5722
+ return a < b ? -1 : a > b ? 1 : 0;
5723
+ }
5724
+ function resolveTagCloud(module2, data) {
5725
+ if (data.status === "category-not-found") {
5726
+ return [
5727
+ {
5728
+ element: "container",
5729
+ data: {
5730
+ type: "div",
5731
+ attributes: { class: "error-block" },
5732
+ elements: [{ element: "text", data: `Category "${data.category}" can not be found.` }]
5733
+ }
5734
+ }
5735
+ ];
5736
+ }
5737
+ const tags = selectTags(data.tags, module2.limit);
5738
+ if (tags.length === 0) {
5739
+ return [noTagsMessage()];
5740
+ }
5741
+ let minWeight = Number.POSITIVE_INFINITY;
5742
+ let maxWeight = Number.NEGATIVE_INFINITY;
5743
+ for (const tag of tags) {
5744
+ if (tag.weight < minWeight)
5745
+ minWeight = tag.weight;
5746
+ if (tag.weight > maxWeight)
5747
+ maxWeight = tag.weight;
5748
+ }
5749
+ const weightRange = maxWeight - minWeight;
5750
+ const categorySuffix = data.category !== null ? `/category/${rawUrlEncode(data.category)}` : "";
5751
+ const anchors = [];
5752
+ for (const tag of tags) {
5753
+ const a = weightRange === 0 ? 0 : (tag.weight - minWeight) / weightRange;
5754
+ const fontSize = interpolate(module2["min-font-size"], module2["max-font-size"], a);
5755
+ const [r, g, b] = [0, 1, 2].map((i) => interpolate(module2["min-color"][i], module2["max-color"][i], a));
5756
+ anchors.push({ element: "text", data: `
5757
+ ` });
5758
+ anchors.push({
5759
+ element: "anchor",
5760
+ data: {
5761
+ target: null,
5762
+ attributes: {
5763
+ class: "tag",
5764
+ href: `${module2.target}${rawUrlEncode(tag.tag)}${categorySuffix}`,
5765
+ style: `font-size: ${fontSize}${module2["font-size-unit"]}; color: rgb(${r}, ${g}, ${b});`
5766
+ },
5767
+ elements: [{ element: "text", data: tag.tag }]
5768
+ }
5769
+ });
5770
+ }
5771
+ anchors.push({ element: "text", data: `
5772
+ ` });
5773
+ return [
5774
+ {
5775
+ element: "container",
5776
+ data: {
5777
+ type: "div",
5778
+ attributes: { class: "pages-tag-cloud-box" },
5779
+ elements: anchors
5780
+ }
5781
+ }
5782
+ ];
5783
+ }
5784
+ function interpolate(min, max, a) {
5785
+ return Math.round(min + (max - min) * a);
5786
+ }
5787
+ function noTagsMessage() {
5788
+ return {
5789
+ element: "container",
5790
+ data: {
5791
+ type: "paragraph",
5792
+ attributes: {},
5793
+ elements: [
5794
+ { element: "text", data: NO_TAGS_MESSAGE_PREFIX },
5795
+ {
5796
+ element: "container",
5797
+ data: {
5798
+ type: "italics",
5799
+ attributes: {},
5800
+ elements: [{ element: "text", data: "tags" }]
5801
+ }
5802
+ },
5803
+ { element: "text", data: NO_TAGS_MESSAGE_SUFFIX }
5804
+ ]
5805
+ }
5806
+ };
5807
+ }
5808
+
5809
+ // packages/parser/src/parser/rules/block/module/listpages/extraction/tagcloud.ts
5810
+ function extractTagCloudModule(tagCloud, state, result) {
5811
+ const id = state.nextId++;
5812
+ result.requirements.tagCloud.push({
5813
+ id,
5814
+ category: tagCloud.category,
5815
+ limit: tagCloud.limit
5816
+ });
5817
+ }
5818
+
5620
5819
  // packages/parser/src/parser/rules/block/module/listpages/extract.ts
5621
5820
  function extractDataRequirements(ast) {
5622
5821
  const result = {
5623
5822
  requirements: {
5624
5823
  listPages: [],
5625
- listUsers: []
5824
+ listUsers: [],
5825
+ tagCloud: []
5626
5826
  },
5627
5827
  compiledListPagesTemplates: new Map,
5628
5828
  compiledListUsersTemplates: new Map
5629
5829
  };
5630
5830
  const listPagesState = { nextId: 0 };
5631
5831
  const listUsersState = { nextId: 0 };
5832
+ const tagCloudState = { nextId: 0 };
5632
5833
  walkElements(ast.elements, (element) => {
5633
5834
  if (element.element !== "module")
5634
5835
  return;
@@ -5636,6 +5837,8 @@ function extractDataRequirements(ast) {
5636
5837
  extractListPagesModule(element.data, listPagesState, result);
5637
5838
  } else if (isListUsersModule(element.data)) {
5638
5839
  extractListUsersModule(element.data, listUsersState, result);
5840
+ } else if (isTagCloudModule(element.data)) {
5841
+ extractTagCloudModule(element.data, tagCloudState, result);
5639
5842
  }
5640
5843
  });
5641
5844
  return result;
@@ -6751,6 +6954,16 @@ async function buildListUsersDataMap(dataProvider, requirements) {
6751
6954
  }
6752
6955
  return dataMap;
6753
6956
  }
6957
+ async function buildTagCloudDataMap(dataProvider, requirements) {
6958
+ const dataMap = new Map;
6959
+ for (const req of requirements) {
6960
+ const data = await dataProvider.fetchTagCloud?.(req);
6961
+ if (data) {
6962
+ dataMap.set(req.id, data);
6963
+ }
6964
+ }
6965
+ return dataMap;
6966
+ }
6754
6967
 
6755
6968
  // packages/parser/src/parser/rules/block/module/resolution/contexts.ts
6756
6969
  async function buildListPagesContext(dataProvider, requirements, compiledTemplates, parse, urlPath) {
@@ -6767,6 +6980,16 @@ async function buildListPagesContext(dataProvider, requirements, compiledTemplat
6767
6980
  parse
6768
6981
  };
6769
6982
  }
6983
+ async function buildTagCloudContext(dataProvider, requirements) {
6984
+ if (requirements.length === 0 || !dataProvider.fetchTagCloud) {
6985
+ return null;
6986
+ }
6987
+ const dataMap = await buildTagCloudDataMap(dataProvider, requirements);
6988
+ if (dataMap.size === 0) {
6989
+ return null;
6990
+ }
6991
+ return { dataMap };
6992
+ }
6770
6993
  async function buildListUsersContext(dataProvider, requirements, compiledTemplates, parse) {
6771
6994
  if (requirements.length === 0 || !dataProvider.fetchListUsers) {
6772
6995
  return null;
@@ -6823,10 +7046,27 @@ function resolveDynamicModuleElement(element, ctx, ids) {
6823
7046
  ids: { ...ids, listUsersId: listUsersId + 1 }
6824
7047
  };
6825
7048
  }
7049
+ if (isTagCloudModule(element.data)) {
7050
+ const elements = [];
7051
+ const tagCloudId = ids.tagCloudId;
7052
+ if (ctx.tagCloud) {
7053
+ const moduleData = ctx.tagCloud.dataMap.get(tagCloudId);
7054
+ if (moduleData) {
7055
+ elements.push(...resolveTagCloud(element.data, moduleData));
7056
+ }
7057
+ } else if (!ctx.fetchTagCloudProvided) {
7058
+ elements.push(element);
7059
+ }
7060
+ return {
7061
+ handled: true,
7062
+ elements,
7063
+ ids: { ...ids, tagCloudId: tagCloudId + 1 }
7064
+ };
7065
+ }
6826
7066
  return { handled: false, elements: [element], ids };
6827
7067
  }
6828
7068
  function countDynamicModules(elements) {
6829
- const counts = { listPagesId: 0, listUsersId: 0 };
7069
+ const counts = { listPagesId: 0, listUsersId: 0, tagCloudId: 0 };
6830
7070
  walkElements(elements, (element) => {
6831
7071
  if (element.element !== "module")
6832
7072
  return;
@@ -6834,6 +7074,8 @@ function countDynamicModules(elements) {
6834
7074
  counts.listPagesId++;
6835
7075
  } else if (isListUsersModule2(element.data)) {
6836
7076
  counts.listUsersId++;
7077
+ } else if (isTagCloudModule(element.data)) {
7078
+ counts.tagCloudId++;
6837
7079
  }
6838
7080
  });
6839
7081
  return counts;
@@ -6844,12 +7086,18 @@ function walkAndResolve(elements, ctx) {
6844
7086
  const result = [];
6845
7087
  let listPagesId = ctx.listPagesIdCounter;
6846
7088
  let listUsersId = ctx.listUsersIdCounter;
7089
+ let tagCloudId = ctx.tagCloudIdCounter;
6847
7090
  for (const element of elements) {
6848
- const dynamicModule = resolveDynamicModuleElement(element, ctx, { listPagesId, listUsersId });
7091
+ const dynamicModule = resolveDynamicModuleElement(element, ctx, {
7092
+ listPagesId,
7093
+ listUsersId,
7094
+ tagCloudId
7095
+ });
6849
7096
  if (dynamicModule.handled) {
6850
7097
  result.push(...dynamicModule.elements);
6851
7098
  listPagesId = dynamicModule.ids.listPagesId;
6852
7099
  listUsersId = dynamicModule.ids.listUsersId;
7100
+ tagCloudId = dynamicModule.ids.tagCloudId;
6853
7101
  continue;
6854
7102
  }
6855
7103
  if (isIfTagsElement(element)) {
@@ -6860,21 +7108,25 @@ function walkAndResolve(elements, ctx) {
6860
7108
  const childResult = walkAndResolve(ifTagsData.elements, {
6861
7109
  ...ctx,
6862
7110
  listPagesIdCounter: listPagesId,
6863
- listUsersIdCounter: listUsersId
7111
+ listUsersIdCounter: listUsersId,
7112
+ tagCloudIdCounter: tagCloudId
6864
7113
  });
6865
7114
  result.push(...childResult.elements);
6866
7115
  listPagesId = childResult.nextListPagesId;
6867
7116
  listUsersId = childResult.nextListUsersId;
7117
+ tagCloudId = childResult.nextTagCloudId;
6868
7118
  } else {
6869
7119
  const counts = countDynamicModules(ifTagsData.elements);
6870
7120
  listPagesId += counts.listPagesId;
6871
7121
  listUsersId += counts.listUsersId;
7122
+ tagCloudId += counts.tagCloudId;
6872
7123
  }
6873
7124
  } else {
6874
7125
  const childResult = walkAndResolve(ifTagsData.elements, {
6875
7126
  ...ctx,
6876
7127
  listPagesIdCounter: listPagesId,
6877
- listUsersIdCounter: listUsersId
7128
+ listUsersIdCounter: listUsersId,
7129
+ tagCloudIdCounter: tagCloudId
6878
7130
  });
6879
7131
  result.push({
6880
7132
  element: "if-tags",
@@ -6885,32 +7137,41 @@ function walkAndResolve(elements, ctx) {
6885
7137
  });
6886
7138
  listPagesId = childResult.nextListPagesId;
6887
7139
  listUsersId = childResult.nextListUsersId;
7140
+ tagCloudId = childResult.nextTagCloudId;
6888
7141
  }
6889
7142
  continue;
6890
7143
  }
6891
- const mapped = mapElementChildrenWithState(element, { listPagesId, listUsersId }, (children, state) => {
7144
+ const mapped = mapElementChildrenWithState(element, { listPagesId, listUsersId, tagCloudId }, (children, state) => {
6892
7145
  const childResult = walkAndResolve(children, {
6893
7146
  ...ctx,
6894
7147
  listPagesIdCounter: state.listPagesId,
6895
- listUsersIdCounter: state.listUsersId
7148
+ listUsersIdCounter: state.listUsersId,
7149
+ tagCloudIdCounter: state.tagCloudId
6896
7150
  });
6897
7151
  return {
6898
7152
  elements: childResult.elements,
6899
7153
  state: {
6900
7154
  listPagesId: childResult.nextListPagesId,
6901
- listUsersId: childResult.nextListUsersId
7155
+ listUsersId: childResult.nextListUsersId,
7156
+ tagCloudId: childResult.nextTagCloudId
6902
7157
  }
6903
7158
  };
6904
7159
  });
6905
7160
  result.push(mapped.element);
6906
7161
  listPagesId = mapped.state.listPagesId;
6907
7162
  listUsersId = mapped.state.listUsersId;
7163
+ tagCloudId = mapped.state.tagCloudId;
6908
7164
  }
6909
- return { elements: result, nextListPagesId: listPagesId, nextListUsersId: listUsersId };
7165
+ return {
7166
+ elements: result,
7167
+ nextListPagesId: listPagesId,
7168
+ nextListUsersId: listUsersId,
7169
+ nextTagCloudId: tagCloudId
7170
+ };
6910
7171
  }
6911
7172
 
6912
7173
  // packages/parser/src/parser/rules/block/module/resolution/styles.ts
6913
- var import_ast = require("@wdprlib/ast");
7174
+ var import_ast2 = require("@wdprlib/ast");
6914
7175
  function collectStyles(elements) {
6915
7176
  const styles = [];
6916
7177
  const ctx = { nextSlotId: 0 };
@@ -6926,7 +7187,7 @@ function collectStylesFromElements(elements, styles, ctx) {
6926
7187
  }
6927
7188
  if (element.element === "if-tags") {
6928
7189
  const slotId = ctx.nextSlotId++;
6929
- styles.push(`${import_ast.STYLE_SLOT_PREFIX}${slotId}`);
7190
+ styles.push(`${import_ast2.STYLE_SLOT_PREFIX}${slotId}`);
6930
7191
  const data = { ...element.data, _styleSlot: slotId };
6931
7192
  result.push({
6932
7193
  element: "if-tags",
@@ -6945,15 +7206,19 @@ async function resolveModules(ast, dataProvider, options) {
6945
7206
  const parse = createModuleParseFunction(options, dataProvider);
6946
7207
  const listPagesCtx = await buildListPagesContext(dataProvider, options.requirements.listPages ?? [], options.compiledListPagesTemplates, parse, options.urlPath);
6947
7208
  const listUsersCtx = await buildListUsersContext(dataProvider, options.requirements.listUsers ?? [], options.compiledListUsersTemplates, parse);
7209
+ const tagCloudCtx = await buildTagCloudContext(dataProvider, options.requirements.tagCloud ?? []);
6948
7210
  const pageTags = dataProvider.getPageTags?.() ?? null;
6949
7211
  const resolvedElements = walkAndResolve(ast.elements, {
6950
7212
  listPages: listPagesCtx,
6951
7213
  listUsers: listUsersCtx,
7214
+ tagCloud: tagCloudCtx,
6952
7215
  fetchListPagesProvided: dataProvider.fetchListPages !== undefined,
6953
7216
  fetchListUsersProvided: dataProvider.fetchListUsers !== undefined,
7217
+ fetchTagCloudProvided: dataProvider.fetchTagCloud !== undefined,
6954
7218
  pageTags,
6955
7219
  listPagesIdCounter: 0,
6956
- listUsersIdCounter: 0
7220
+ listUsersIdCounter: 0,
7221
+ tagCloudIdCounter: 0
6957
7222
  });
6958
7223
  const { elements: finalElements, styles } = collectStyles(resolvedElements.elements);
6959
7224
  const result = {
@@ -6985,7 +7250,7 @@ function createModuleSourceTransform(options, dataProvider) {
6985
7250
  }
6986
7251
 
6987
7252
  // packages/parser/src/parser/rules/block/module/index.ts
6988
- var import_ast2 = require("@wdprlib/ast");
7253
+ var import_ast3 = require("@wdprlib/ast");
6989
7254
 
6990
7255
  // packages/parser/src/parser/rules/block/footnoteblock/attributes.ts
6991
7256
  function parseFootnoteBlockAttributes(ctx, startPos) {
@@ -9053,6 +9318,177 @@ var bibliographyRule = {
9053
9318
  }
9054
9319
  };
9055
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
+
9056
9492
  // packages/parser/src/parser/rules/block/index.ts
9057
9493
  var blockRules = [
9058
9494
  blockCommentRule,
@@ -9082,6 +9518,7 @@ var blockRules = [
9082
9518
  iframeRule,
9083
9519
  iftagsRule,
9084
9520
  bibliographyRule,
9521
+ galleryRule,
9085
9522
  divRule
9086
9523
  ];
9087
9524
  // packages/parser/src/parser/rules/inline/formatting/container.ts
@@ -11932,7 +12369,7 @@ function createParseContext(tokens, options = {}) {
11932
12369
  pos: 0,
11933
12370
  version: options.version ?? "wikidot",
11934
12371
  trackPositions: options.trackPositions ?? true,
11935
- settings: options.settings ?? import_ast3.DEFAULT_SETTINGS,
12372
+ settings: options.settings ?? import_ast4.DEFAULT_SETTINGS,
11936
12373
  footnotes: [],
11937
12374
  tocEntries: [],
11938
12375
  codeBlocks: [],
@@ -12897,12 +13334,12 @@ function preprocess(text) {
12897
13334
  }
12898
13335
 
12899
13336
  // packages/parser/src/parser/preprocess/expr/evaluate.ts
12900
- var import_ast4 = require("@wdprlib/ast");
13337
+ var import_ast5 = require("@wdprlib/ast");
12901
13338
  function evaluateDirective(kind, match) {
12902
13339
  if (kind === "expr") {
12903
- const result2 = import_ast4.evaluateExpression(match.head);
13340
+ const result2 = import_ast5.evaluateExpression(match.head);
12904
13341
  if (result2.success)
12905
- return import_ast4.formatExprValue(result2.value);
13342
+ return import_ast5.formatExprValue(result2.value);
12906
13343
  if (result2.error === "empty expression")
12907
13344
  return "";
12908
13345
  return "ERROR";
@@ -12910,11 +13347,11 @@ function evaluateDirective(kind, match) {
12910
13347
  if (kind === "if") {
12911
13348
  if (!match.hasPipe)
12912
13349
  return "";
12913
- return import_ast4.isTruthy(match.head) ? match.thenText : match.elseText;
13350
+ return import_ast5.isTruthy(match.head) ? match.thenText : match.elseText;
12914
13351
  }
12915
13352
  if (!match.hasPipe)
12916
13353
  return "";
12917
- const result = import_ast4.evaluateExpression(match.head);
13354
+ const result = import_ast5.evaluateExpression(match.head);
12918
13355
  if (!result.success)
12919
13356
  return "ERROR";
12920
13357
  return result.value !== 0 && !Number.isNaN(result.value) ? match.thenText : match.elseText;