@gmb/bitmark-parser-generator
Version:
A bitmark parser and generator using Peggy.js
47,212 lines • 1.57 MB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/config/Config.ts
import { Enum as Enum3 } from "@ncoderz/superenum";
// src/model/enum/BitType.ts
import "@ncoderz/superenum";
var BitType = {
_error: "_error",
// Used for error handling to indicate a bit type that is not supported or a bit parse error
_comment: "_comment",
// Used to indicate a bit is commented out
_standard: "_standard",
// Not to be used as a bit, but as a base for other bit types
advertising: "advertising",
advertisingAdvertorial: "advertising-advertorial",
advertisingAdvertorialPage: "advertising-advertorial-page",
advertisingCallToAction: "advertising-call-to-action",
advertisingCallToActionMeeting: "advertising-call-to-action-meeting",
advertisingBanner: "advertising-banner",
advertisingSkyscraper: "advertising-skyscraper",
advertisingRectangle: "advertising-rectangle",
advertisingFullPage: "advertising-full-page",
advertisingHalfPage: "advertising-half-page",
advertisingQuarterPage: "advertising-quarter-page",
advertisingClassifiedPage: "advertising-classified-page",
advertisingLandscape: "advertising-landscape",
advertisingPortrait: "advertising-portrait",
aiChat: "ai-chat",
aiEditor: "ai-editor",
aiPrompt: "ai-prompt",
aiTutor: "ai-tutor",
aiWbt: "ai-wbt",
anchor: "anchor",
appAiPrompt: "app-ai-prompt",
appBitmarkFromEditor: "app-bitmark-from-editor",
appBitmarkFromJavascript: "app-bitmark-from-javascript",
appCalculatorLatex: "app-calculator-latex",
appCodeCell: "app-code-cell",
appCodeEditor: "app-code-editor",
appCodeIde: "app-code-ide",
appCreateBitsFromImage: "app-create-bits-from-image",
appFlashcards: "app-flashcards",
appFlashcardsLearn: "app-flashcards-learn",
appFlashcardsQuiz: "app-flashcards-quiz",
appGetScreenshot: "app-get-screenshot",
appLink: "app-link",
appRag: "app-rag",
appSearch: "app-search",
article: "article",
articleAi: "article-ai",
articleAlt: "article-alt",
articleAttachment: "article-attachment",
articleAuthor: "article-author",
articleEmbed: "article-embed",
articleLink: "article-link",
articleResponsive: "article-responsive",
articleResponsiveAlt: "article-responsive-alt",
assignment: "assignment",
assignmentList: "assignment-list",
audio: "audio",
audioEmbed: "audio-embed",
audioLink: "audio-link",
audioTranscript: "audio-transcript",
author: "author",
authorContentBitGenerator: "author-content-bit-generator",
bitAlias: "bit-alias",
bitBookEnding: "bit-book-ending",
bitBookSummary: "bit-book-summary",
bitmarkExample: "bitmark-example",
blogArticle: "blog-article",
book: "book",
bookAcknowledgements: "book-acknowledgements",
bookAddendum: "book-addendum",
bookAfterword: "book-afterword",
bookAlias: "book-alias",
bookAppendix: "book-appendix",
bookArticle: "book-article",
bookAuthor: "book-author",
bookAutherBio: "book-author-bio",
bookBibliography: "book-bibliography",
bookClose: "book-close",
bookComingSoon: "book-coming-soon",
bookConclusion: "book-conclusion",
bookCopyright: "book-copyright",
bookCopyrightPermissions: "book-copyright-permissions",
bookCover: "book-cover",
bookDedication: "book-dedication",
bookDiff: "book-diff",
bookEnd: "book-end",
bookEndnotes: "book-endnotes",
bookEpigraph: "book-epigraph",
bookEpilogue: "book-epilogue",
bookForword: "book-foreword",
bookFrontispiece: "book-frontispiece",
bookImprint: "book-imprint",
bookIncitingIncident: "book-inciting-incident",
bookIntroduction: "book-introduction",
bookLink: "book-link",
bookLinkNext: "book-link-next",
bookLinkPrev: "book-link-prev",
bookListOfContributors: "book-list-of-contributors",
bookNotes: "book-notes",
bookPostscript: "book-postscript",
bookPreface: "book-preface",
bookPrologue: "book-prologue",
bookReadMore: "book-read-more",
bookReference: "book-reference",
bookReferenceList: "book-reference-list",
bookRequestForABookReview: "book-request-for-a-book-review",
bookSummary: "book-summary",
bookTeaser: "book-teaser",
bookTitle: "book-title",
botActionAnnounce: "bot-action-announce",
botActionRatingNumber: "bot-action-rating-number",
botActionRemind: "bot-action-remind",
botActionResponse: "bot-action-response",
botActionSave: "bot-action-save",
botActionSend: "bot-action-send",
botActionTrueFalse: "bot-action-true-false",
botInterview: "bot-interview",
brandColor: "brand-color",
browserImage: "browser-image",
bug: "bug",
bugAlt: "bug-alt",
bugCollapsible: "bug-collapsible",
buttonCopyText: "button-copy-text",
catalogItemBook: "catalog-item-book",
catalogItemExternalBook: "catalog-item-external-book",
catalogItemProduct: "catalog-item-product",
callToAction: "call-to-action",
callToActionBookNow: "call-to-action-book-now",
callToActionContact: "call-to-action-contact",
callToActionCreateAccount: "call-to-action-create-account",
callToActionDownload: "call-to-action-download",
callToActionGetNow: "call-to-action-get-now",
callToActionGetOffer: "call-to-action-get-offer",
callToActionJoin: "call-to-action-join",
callToActionLearnMore: "call-to-action-learn-more",
callToActionMail: "call-to-action-mail",
callToActionSeeMore: "call-to-action-see-more",
callToActionShopNow: "call-to-action-shop-now",
callToActionStartNow: "call-to-action-start-now",
callToActionSubscribe: "call-to-action-subscribe",
callToActionWatch: "call-to-action-watch",
card1: "card-1",
chapter: "chapter",
chapterSubjectMatter: "chapter-subject-matter",
chat: "chat",
checklist: "checklist",
cloze: "cloze",
clozeAndMultipleChoiceText: "cloze-and-multiple-choice-text",
clozeInstructionGrouped: "cloze-instruction-grouped",
clozeList: "cloze-list",
clozeSeveral: "cloze-several",
clozeSolutionGrouped: "cloze-solution-grouped",
coachAudioTranscript: "coach-audio-transcript",
coachCallToActionChecklist: "coach-call-to-action-checklist",
coachCallToActionCloze: "coach-call-to-action-cloze",
coachCallToActionClozeAndMultipleChoiceText: "coach-call-to-action-cloze-and-multiple-choice-text",
coachCallToActionEssay: "coach-call-to-action-essay",
coachCallToActionMultipleChoiceText: "coach-call-to-action-multiple-choice-text",
coachHomeRules: "coach-home-rules",
coachSelfReflectionCloze: "coach-self-reflection-cloze",
coachSelfReflectionEssay: "coach-self-reflection-essay",
coachSelfReflectionMultipleChoice: "coach-self-reflection-multiple-choice",
coachSelfReflectionMultipleChoice1: "coach-self-reflection-multiple-choice-1",
coachSelfReflectionMultipleChoiceText: "coach-self-reflection-multiple-choice-text",
coachSelfReflectionMultipleResponse: "coach-self-reflection-multiple-response",
coachSelfReflectionMultipleResponse1: "coach-self-reflection-multiple-response-1",
coachSelfReflectionRating: "coach-self-reflection-rating",
coachVideoTranscript: "coach-video-transcript",
code: "code",
codeRuntime: "code-runtime",
collapsible: "collapsible",
conclusion: "conclusion",
conclusionAlt: "conclusion-alt",
consoleLog: "console-log",
container: "container",
containerAccordionTabs: "container-accordion-tabs",
containerBits2: "container-bits-2",
containerCards: "container-cards",
containerCarousel: "container-carousel",
containerCookRecipe: "container-cook-recipe",
containerFolder: "container-folder",
containerFolderAll: "container-folder-all",
containerGallery: "container-gallery",
containerGrid: "container-grid",
containerGroup: "container-group",
containerNewsArticle: "container-news-article",
containerNowrap: "container-nowrap",
containerNowrapStretch: "container-nowrap-stretch",
containerPreview: "container-preview",
containerScroller: "container-scroller",
containerSlides: "container-slides",
containerStack: "container-stack",
containerTabs: "container-tabs",
containerWrap: "container-wrap",
conversationLeft1: "conversation-left-1",
conversationLeft1Scream: "conversation-left-1-scream",
conversationLeft1Thought: "conversation-left-1-thought",
conversationRight1: "conversation-right-1",
conversationRight1Scream: "conversation-right-1-scream",
conversationRight1Thought: "conversation-right-1-thought",
clozeAndMultipleChoiceTextConversationLeft1: "cloze-and-multiple-choice-text-conversation-left-1",
clozeAndMultipleChoiceTextConversationLeft1Scream: "cloze-and-multiple-choice-text-conversation-left-1-scream",
clozeAndMultipleChoiceTextConversationLeft1Thought: "cloze-and-multiple-choice-text-conversation-left-1-thought",
clozeAndMultipleChoiceTextConversationRight1: "cloze-and-multiple-choice-text-conversation-right-1",
clozeAndMultipleChoiceTextConversationRight1Scream: "cloze-and-multiple-choice-text-conversation-right-1-scream",
clozeAndMultipleChoiceTextConversationRight1Thought: "cloze-and-multiple-choice-text-conversation-right-1-thought",
cookArrangement: "cook-arrangement",
cookIngredients: "cook-ingredients",
cookInsert: "cook-insert",
cookNoteOnQuantity: "cook-note-on-quantity",
cookPersonalRecommendation: "cook-personal-recommendation",
cookPlate: "cook-plate",
cookPracticeAdvise: "cook-practice-advise",
cookPreparation: "cook-preparation",
cookRecommendation: "cook-recommendation",
cookRemark: "cook-remark",
cookSideDish: "cook-side-dish",
cookSideDrink: "cook-side-drink",
cookStep: "cook-step",
cookTimer: "cook-timer",
cookVariation: "cook-variation",
correction: "correction",
danger: "danger",
dangerAlt: "danger-alt",
dangerCollapsible: "danger-collapsible",
definitionList: "definition-list",
definitionListCollapsible: "definition-list-collapsible",
definitionTerm: "definition-term",
deleted: "deleted",
details: "details",
details1: "details-1",
detailsImage: "details-image",
diff: "diff",
document: "document",
documentDownload: "document-download",
documentEmbed: "document-embed",
documentLink: "document-link",
documentUpload: "document-upload",
editorial: "editorial",
editorNote: "editor-note",
essay: "essay",
example: "example",
exampleAlt: "example-alt",
exampleCollapsible: "example-collapsible",
exampleList: "example-list",
extractorAiChat: "extractor-ai-chat",
extractorBlock: "extractor-block",
extractorBlueprint: "extractor-blueprint",
extractorBlueprintConfiguration: "extractor-blueprint-configuration",
extractorConfiguration: "extractor-configuration",
extractorImage: "extractor-image",
extractorImageCollapsible: "extractor-image-collapsible",
extractorInformation: "extractor-information",
extractorPage: "extractor-page",
extractorPageCollapsible: "extractor-page-collapsible",
extractorPageFooter: "extractor-page-footer",
extractorPageFooterCollapsible: "extractor-page-footer-collapsible",
extractorPageHeader: "extractor-page-header",
extractorPageHeaderCollapsible: "extractor-page-header-collapsible",
extractorPageNumber: "extractor-page-number",
extractorPageNumberCollapsible: "extractor-page-number-collapsible",
extractorPageWithBlocks: "extractor-page-with-blocks",
extractorPageWithBlocksCollapsible: "extractor-page-with-blocks-collapsible",
extractorReconcile: "extractor-reconcile",
extractorRepeatedText: "extractor-repeated-text",
extractorRule: "extractor-rule",
extractorTheme: "extractor-theme",
featured: "featured",
feedback: "feedback",
figure: "figure",
flashcard: "flashcard",
flashcard1: "flashcard-1",
focusImage: "focus-image",
footNote: "foot-note",
formFreeText: "form-free-text",
formula: "formula",
formulaImage: "formula-image",
gapText: "gap-text",
gapTextInstructionGrouped: "gap-text-instruction-grouped",
glossaryTerm: "glossary-term",
groupBorn: "group-born",
groupDied: "group-died",
handInAudio: "hand-in-audio",
handInContact: "hand-in-contact",
handInDocument: "hand-in-document",
handInFeedbackExpert: "hand-in-feedback-expert",
handInFeedbackSelf: "hand-in-feedback-self",
handInFile: "hand-in-file",
handInFreeText: "hand-in-free-text",
handInFreeTextExpert: "hand-in-free-text-expert",
handInLocation: "hand-in-location",
handInPhoto: "hand-in-photo",
handInScan: "hand-in-scan",
handInSubmit: "hand-in-submit",
handInSystemMedia: "hand-in-system-media",
handInVideo: "hand-in-video",
handInVoice: "hand-in-voice",
help: "help",
helpAlt: "help-alt",
highlightText: "highlight-text",
hint: "hint",
hintAlt: "hint-alt",
hintCollapsible: "hint-collapsible",
image: "image",
imageBanner: "image-banner",
imageFigure: "image-figure",
imageFigureAlt: "image-figure-alt",
imageLandscape: "image-landscape",
imageLink: "image-link",
imageMood: "image-mood",
imageOnDevice: "image-on-device",
imagePortrait: "image-portrait",
imagePrototype: "image-prototype",
imageResponsive: "image-responsive",
imageScreenshot: "image-screenshot",
imageSeparator: "image-separator",
imageSeparatorAlt: "image-separator-alt",
imagesLogoGrave: "images-logo-grave",
imageStyled: "image-styled",
imageSuperWide: "image-super-wide",
imageZoom: "image-zoom",
indexTerm: "index-term",
info: "info",
infoAlt: "info-alt",
infoCollapsible: "info-collapsible",
internalLink: "internal-link",
interview: "interview",
interviewInstructionGrouped: "interview-instruction-grouped",
langAudioScript: "lang-audio-script",
langEnablingLanguageSkills: "lang-enabling-language-skills",
langEnglishAroundWorld: "lang-english-around-world",
langExtraActivity: "lang-extra-activity",
langGoodToKnow: "lang-good-to-know",
langHomework: "lang-homework",
langLearningGoal: "lang-learning-goal",
langLearningOutcomes: "lang-learning-outcomes",
langLearningStrategy: "lang-learning-strategy",
langLevelDown: "lang-level-down",
langLevelUp: "lang-level-up",
langLifeSkillIcon: "lang-life-skill-icon",
langLifeSkills: "lang-life-skills",
langLikeALocal: "lang-like-a-local",
langMaterial: "lang-material",
langTeacherNote: "lang-teacher-note",
langTeacherPronunciation: "lang-teacher-pronunciation",
langUsefulPhrases: "lang-useful-phrases",
langVideoScript: "lang-video-script",
langVocabulary: "lang-vocabulary",
learningDocumentationFeedback: "learning-documentation-feedback",
learningPathBook: "learning-path-book",
learningPathBotTraining: "learning-path-bot-training",
learningPathClassroomEvent: "learning-path-classroom-event",
learningPathClassroomTraining: "learning-path-classroom-training",
learningPathClosing: "learning-path-closing",
learningPathExternalLink: "learning-path-external-link",
learningPathFeedback: "learning-path-feedback",
learningPathLearningGoal: "learning-path-learning-goal",
learningPathLti: "learning-path-lti",
learningPathSign: "learning-path-sign",
learningPathStep: "learning-path-step",
learningPathVideoCall: "learning-path-video-call",
leAssignment: "le-assignment",
leClassroomEvent: "le-classroom-event",
leCompletion: "le-completion",
leExternalLink: "le-external-link",
leFinishingTask: "le-finishing-task",
leFollowUpTask: "le-follow-up-task",
leMultiDayEvent: "le-multi-day-event",
legend: "legend",
leDocumentDownload: "le-document-download",
leLearningObjectives: "le-learning-objectives",
leLearningStep: "le-learning-step",
leListenAudioEmbed: "le-listen-audio-embed",
lePreparationTask: "le-preparation-task",
leRead: "le-read",
leReadBook: "le-read-book",
leTask: "le-task",
leTodo: "le-todo",
leVideoCall: "le-video-call",
leWatchVideoEmbed: "le-watch-video-embed",
lifeSkillSticker: "life-skill-sticker",
list: "list",
listItem: "list-item",
mark: "mark",
match: "match",
matchAll: "match-all",
matchAllReverse: "match-all-reverse",
matchAudio: "match-audio",
matchMatrix: "match-matrix",
matchPicture: "match-picture",
matchReverse: "match-reverse",
matchSolutionGrouped: "match-solution-grouped",
message: "message",
metalevelExplanation: "metalevel-explanation",
metaSearchDefaultTerms: "meta-search-default-terms",
metaSearchDefaultTopics: "meta-search-default-topics",
milestone: "milestone",
module: "module",
moduleProduct: "module-product",
multipleChoice: "multiple-choice",
multipleChoice1: "multiple-choice-1",
multipleChoiceText: "multiple-choice-text",
multipleResponse: "multiple-response",
multipleResponse1: "multiple-response-1",
newspaperArticle: "newspaper-article",
nonProductionPrototypeIframe: "non-production-prototype-iframe",
note: "note",
noteAi: "note-ai",
noteAlt: "note-alt",
notebookArticle: "notebook-article",
noteCollapsible: "note-collapsible",
output: "output",
page: "page",
pageAcademy: "page-academy",
pageArticle: "page-article",
pageArticleAlt: "page-article-alt",
pageArticleResponsive: "page-article-responsive",
pageBanner: "page-banner",
pageBuyButton: "page-buy-button",
pageBuyButtonAlt: "page-buy-button-alt",
pageBuyButtonPromotion: "page-buy-button-promotion",
pageCategory: "page-category",
pageCollapsible: "page-collapsible",
pageContainer: "page-container",
pageContainerFolder: "page-container-folder",
pageContainerGroup: "page-container-group",
pageContainerNowrap: "page-container-nowrap",
pageContainerNowrapStretch: "page-container-nowrap-stretch",
pageContainerWrap: "page-container-wrap",
pageCoverImage: "page-cover-image",
pageFooter: "page-footer",
pageHero: "page-hero",
pageOpenBook: "page-open-book",
pageOpenBookList: "page-open-book-list",
printBook: "print-book",
openBookChapter: "open-book-chapter",
openBookChapterTeaser: "open-book-chapter-teaser",
pagePerson: "page-person",
pageProduct: "page-product",
pageProductList: "page-product-list",
pageProductVideo: "page-product-video",
pageProductVideoList: "page-product-video-list",
pagePromotion: "page-promotion",
pageSectionFolder: "page-section-folder",
pageShopInShop: "page-shop-in-shop",
pageSpecial: "page-special",
pageEnd: "page-end",
pageStart: "page-start",
pageSubpage: "page-subpage",
pageSubscribe: "page-subscribe",
parameters: "parameters",
photo: "photo",
platform: "platform",
platformBrandTarget: "platform-brand-target",
platformHeader: "platform-header",
platformMain: "platform-main",
platformMainButton: "platform-main-button",
platformMainInput: "platform-main-input",
platformPath: "platform-path",
platformSection: "platform-section",
platformSectionButton: "platform-section-button",
platformSectionChat: "platform-section-chat",
platformSectionHeader: "platform-section-header",
platformSectionInput: "platform-section-input",
platformSystemIcon: "platform-system-icon",
preparationNote: "preparation-note",
printThisBook: "print-this-book",
printThisChapter: "print-this-chapter",
printPageBreak: "print-page-break",
pronunciationTable: "pronunciation-table",
prototypeImages: "prototype-images",
qAndA: "q-and-a",
qAndACard: "q-and-a-card",
question1: "question-1",
quote: "quote",
rating: "rating",
recipe: "recipe",
recordAudio: "record-audio",
recordVideo: "record-video",
releaseNote: "release-note",
releaseNotesSummary: "release-notes-summary",
remark: "remark",
remarkAlt: "remark-alt",
remarkCollapsible: "remark-collapsible",
reviewApprovedNote: "review-approved-note",
reviewAuthorNote: "review-author-note",
reviewCustomerNote: "review-customer-note",
reviewError: "review-error",
reviewErrorAi: "review-error-ai",
reviewErrorTranslation: "review-error-translation",
reviewNote: "review-note",
reviewRequestForReviewNote: "review-request-for-review-note",
reviewReviewerNote: "review-reviewer-note",
sampleSolution: "sample-solution",
sandbox: "sandbox",
sandboxOutputJson: "sandbox-output-json",
sandboxOutputMarkup: "sandbox-output-markup",
sandboxOutputRender: "sandbox-output-render",
scorm: "scorm",
screenshot: "screenshot",
selfAssessment: "self-assessment",
separator: "separator",
separatorAlt: "separator-alt",
sequence: "sequence",
sideNote: "side-note",
sideNoteCollapsible: "side-note-collapsible",
smartStandardArticleNonNormative: "smart-standard-article-non-normative",
smartStandardArticleNonNormativeCollapsible: "smart-standard-article-non-normative-collapsible",
smartStandardArticleNormative: "smart-standard-article-normative",
smartStandardArticleNormativeCollapsible: "smart-standard-article-normative-collapsible",
smartStandardDefinitionListNonNormative: "smart-standard-definition-list-non-normative",
smartStandardDefinitionListNormative: "smart-standard-definition-list-normative",
smartStandardExampleNonNormative: "smart-standard-example-non-normative",
smartStandardExampleNonNormativeCollapsible: "smart-standard-example-non-normative-collapsible",
smartStandardExampleNormative: "smart-standard-example-normative",
smartStandardExampleNormativeCollapsible: "smart-standard-example-normative-collapsible",
smartStandardFormula: "smart-standard-formula",
smartStandardFormulaNonNormative: "smart-standard-formula-non-normative",
smartStandardFormulaNormative: "smart-standard-formula-normative",
smartStandardImageFigureNonNormative: "smart-standard-image-figure-non-normative",
smartStandardImageFigureNonNormativeCollapsible: "smart-standard-image-figure-non-normative-collapsible",
smartStandardImageFigureNormative: "smart-standard-image-figure-normative",
smartStandardImageFigureNormativeCollapsible: "smart-standard-image-figure-normative-collapsible",
smartStandardLegend: "smart-standard-legend",
smartStandardLegendNonNormative: "smart-standard-legend-non-normative",
smartStandardLegendNormative: "smart-standard-legend-normative",
smartStandardList: "smart-standard-list",
smartStandardListCollapsible: "smart-standard-list-collapsible",
smartStandardListItem: "smart-standard-list-item",
smartStandardListItemCollapsible: "smart-standard-list-item-collapsible",
smartStandardNoteNonNormative: "smart-standard-note-non-normative",
smartStandardNoteNonNormativeCollapsible: "smart-standard-note-non-normative-collapsible",
smartStandardNoteNormative: "smart-standard-note-normative",
smartStandardNoteNormativeCollapsible: "smart-standard-note-normative-collapsible",
smartStandardRemarkLegend: "smart-standard-remark-legend",
smartStandardRemarkLegendNonNormative: "smart-standard-remark-legend-non-normative",
smartStandardRemarkLegendNormative: "smart-standard-remark-legend-normative",
smartStandardRemarkFormula: "smart-standard-remark-formula",
smartStandardRemarkFormulaNonNormative: "smart-standard-remark-formula-non-normative",
smartStandardRemarkFormulaNormative: "smart-standard-remark-formula-normative",
smartStandardRemarkNonNormative: "smart-standard-remark-non-normative",
smartStandardRemarkNonNormativeCollapsible: "smart-standard-remark-non-normative-collapsible",
smartStandardRemarkNormative: "smart-standard-remark-normative",
smartStandardRemarkNormativeCollapsible: "smart-standard-remark-normative-collapsible",
smartStandardRemarkTableExtendedImageNonNormative: "smart-standard-remark-table-extended-image-non-normative",
smartStandardRemarkTableExtendedImageNonNormativeCollapsible: "smart-standard-remark-table-extended-image-non-normative-collapsible",
smartStandardRemarkTableExtendedImageNormative: "smart-standard-remark-table-extended-image-normative",
smartStandardRemarkTableExtendedImageNormativeCollapsible: "smart-standard-remark-table-extended-image-normative-collapsible",
smartStandardRemarkTableExtendedNonNormative: "smart-standard-remark-table-extended-non-normative",
smartStandardRemarkTableExtendedNonNormativeCollapsible: "smart-standard-remark-table-extended-non-normative-collapsible",
smartStandardRemarkTableExtendedNormative: "smart-standard-remark-table-extended-normative",
smartStandardRemarkTableExtendedNormativeCollapsible: "smart-standard-remark-table-extended-normative-collapsible",
smartStandardRemarkTableImageNonNormative: "smart-standard-remark-table-image-non-normative",
smartStandardRemarkTableImageNonNormativeCollapsible: "smart-standard-remark-table-image-non-normative-collapsible",
smartStandardRemarkTableImageNormative: "smart-standard-remark-table-image-normative",
smartStandardRemarkTableImageNormativeCollapsible: "smart-standard-remark-table-image-normative-collapsible",
smartStandardRemarkTableNonNormative: "smart-standard-remark-table-non-normative",
smartStandardRemarkTableNonNormativeCollapsible: "smart-standard-remark-table-non-normative-collapsible",
smartStandardRemarkTableNormative: "smart-standard-remark-table-normative",
smartStandardRemarkTableNormativeCollapsible: "smart-standard-remark-table-normative-collapsible",
smartStandardTableExtendedImageNonNormative: "smart-standard-table-extended-image-non-normative",
smartStandardTableExtendedImageNonNormativeCollapsible: "smart-standard-table-extended-image-non-normative-collapsible",
smartStandardTableExtendedImageNormative: "smart-standard-table-extended-image-normative",
smartStandardTableExtendedImageNormativeCollapsible: "smart-standard-table-extended-image-normative-collapsible",
smartStandardTableExtendedNonNormative: "smart-standard-table-extended-non-normative",
smartStandardTableExtendedNonNormativeCollapsible: "smart-standard-table-extended-non-normative-collapsible",
smartStandardTableExtendedNormative: "smart-standard-table-extended-normative",
smartStandardTableExtendedNormativeCollapsible: "smart-standard-table-extended-normative-collapsible",
smartStandardTableImageNonNormative: "smart-standard-table-image-non-normative",
smartStandardTableImageNonNormativeCollapsible: "smart-standard-table-image-non-normative-collapsible",
smartStandardTableImageNormative: "smart-standard-table-image-normative",
smartStandardTableImageNormativeCollapsible: "smart-standard-table-image-normative-collapsible",
smartStandardTableNonNormative: "smart-standard-table-non-normative",
smartStandardTableNonNormativeCollapsible: "smart-standard-table-non-normative-collapsible",
smartStandardTableNormative: "smart-standard-table-normative",
smartStandardTableNormativeCollapsible: "smart-standard-table-normative-collapsible",
standardArticleNonNormative: "standard-article-non-normative",
standardArticleNormative: "standard-article-normative",
standardDefinitionListNonNormative: "standard-definition-list-non-normative",
standardDefinitionListNormative: "standard-definition-list-normative",
standardExampleNonNormative: "standard-example-non-normative",
standardExampleNormative: "standard-example-normative",
standardImageFigureNonNormative: "standard-image-figure-non-normative",
standardImageFigureNormative: "standard-image-figure-normative",
standardList: "standard-list",
standardListItem: "standard-list-item",
standardNoteNonNormative: "standard-note-non-normative",
standardNoteNormative: "standard-note-normative",
standardRemarkNonNormative: "standard-remark-non-normative",
standardRemarkNormative: "standard-remark-normative",
standardRemarkTableExtendedImageNonNormative: "standard-remark-table-extended-image-non-normative",
standardRemarkTableExtendedImageNormative: "standard-remark-table-extended-image-normative",
standardRemarkTableExtendedNonNormative: "standard-remark-table-extended-non-normative",
standardRemarkTableExtendedNormative: "standard-remark-table-extended-normative",
standardRemarkTableImageNonNormative: "standard-remark-table-image-non-normative",
standardRemarkTableImageNormative: "standard-remark-table-image-normative",
standardRemarkTableNonNormative: "standard-remark-table-non-normative",
standardRemarkTableNormative: "standard-remark-table-normative",
standardTableExtendedImageNonNormative: "standard-table-extended-image-non-normative",
standardTableExtendedImageNormative: "standard-table-extended-image-normative",
standardTableExtendedNonNormative: "standard-table-extended-non-normative",
standardTableExtendedNormative: "standard-table-extended-normative",
standardTableImageNonNormative: "standard-table-image-non-normative",
standardTableImageNormative: "standard-table-image-normative",
standardTableNonNormative: "standard-table-non-normative",
standardTableNormative: "standard-table-normative",
statement: "statement",
stdout: "stdout",
step: "step",
stepImageScreenshot: "step-image-screenshot",
stepImageScreenshotWithPointer: "step-image-screenshot-with-pointer",
sticker: "sticker",
stickyNote: "sticky-note",
stillImageFilm: "still-image-film",
stillImageFilmEmbed: "still-image-film-embed",
stillImageFilmLink: "still-image-film-link",
summary: "summary",
summaryAi: "summary-ai",
survey: "survey",
survey1: "survey-1",
surveyAnonymous: "survey-anonymous",
surveyAnonymous1: "survey-anonymous-1",
surveyMatrix: "survey-matrix",
surveyMatrixMe: "survey-matrix-me",
surveyRating: "survey-rating",
surveyRatingDisplay: "survey-rating-display",
surveyRatingOnce: "survey-rating-once",
table: "table",
tableAlt: "table-alt",
tableExtended: "table-extended",
tableExtendedImage: "table-extended-image",
tableImage: "table-image",
tableImageAlt: "table-image-alt",
takePicture: "take-picture",
toc: "toc",
tocChapter: "toc-chapter",
tocInline: "toc-inline",
tocResource: "toc-resource",
tocContent: "toc-content",
trueFalse: "true-false",
trueFalse1: "true-false-1",
vendorAmcharts5Chart: "vendor-amcharts-5-chart",
vendorDatadogDashboard: "vendor-datadog-dashboard",
vendorDatadogDashboardEmbed: "vendor-datadog-dashboard-embed",
vendorFormbricksEmbed: "vendor-formbricks-embed",
vendorFormbricksLink: "vendor-formbricks-link",
vendorFormbricksEmbedAnonymous: "vendor-formbricks-embed-anonymous",
vendorFormbricksLinkAnonymous: "vendor-formbricks-link-anonymous",
vendorHighchartsChart: "vendor-highcharts-chart",
vendorIframelyCard: "vendor-iframely-card",
vendorIframelyEmbed: "vendor-iframely-embed",
vendorIframelyPreview: "vendor-iframely-preview",
vendorIframelyPreviewMini: "vendor-iframely-preview-mini",
vendorJupyterCellCode: "vendor-jupyter-cell-code",
vendorJupyterCellMarkdown: "vendor-jupyter-cell-markdown",
vendorJupyterCellRaw: "vendor-jupyter-cell-raw",
vendorJupyterIpynb: "vendor-jupyter-ipynb",
vendorJupyterOutput: "vendor-jupyter-output",
vendorLearndashEmbed: "vendor-learndash-embed",
vendorPadletEmbed: "vendor-padlet-embed",
vendorStripePricingTable: "vendor-stripe-pricing-table",
vendorStripePricingTableExternal: "vendor-stripe-pricing-table-external",
vendorStripePricingTablePrintOnRequest: "vendor-stripe-pricing-table-print-on-request",
video: "video",
videoEmbed: "video-embed",
videoEmbedLandscape: "video-embed-landscape",
videoEmbedPortrait: "video-embed-portrait",
videoLandscape: "video-landscape",
videoLink: "video-link",
videoLinkLandscape: "video-link-landscape",
videoLinkPortrait: "video-link-portrait",
videoPortrait: "video-portrait",
videoTranscript: "video-transcript",
warning: "warning",
warningAlt: "warning-alt",
warningCollapsible: "warning-collapsible",
websiteLink: "website-link",
workbookArticle: "workbook-article"
};
// src/model/enum/Count.ts
import "@ncoderz/superenum";
var Count = {
infinity: "infinity"
};
// src/model/enum/ExampleType.ts
import "@ncoderz/superenum";
var ExampleType = {
none: "none",
// Example is not allowed
string: "string",
// Example is a string value
boolean: "boolean"
// Example is a boolean value
};
// src/model/enum/Tag.ts
import "@ncoderz/superenum";
var tags = {
tag_title: "#",
tag_anchor: "\u25BC",
tag_reference: "\u25BA",
tag_property: "@",
tag_item: "%",
tag_instruction: "!",
tag_hint: "?",
tag_true: "+",
tag_false: "-",
tag_sampleSolution: "$",
tag_gap: "_",
tag_mark: "=",
tag_resource: "&"
};
var Tag = tags;
// src/model/enum/TagFormat.ts
import "@ncoderz/superenum";
var TagFormat = {
none: "none",
// The property does not have a value
plainText: "plainText",
// If the value of the property is treated as a trimmed plain text string
bitmarkText: "bitmarkText",
// If the value of the property is treated as a bitmark+
number: "number",
// If the value is treated as a number
boolean: "boolean",
// If the value is treated as a boolean
invertedBoolean: "invertedBoolean",
// If the value is treated as a boolean with the value inverted (e.g. isLongAnswer ==> isShortAnswer = false)
enumeration: "enumeration",
// If the value is treated as an enumeration (the value is a string that must be one of a predefined set of values)
coordinates: "coordinates"
// Comma-separated list of 4 numbers (e.g. bounding-box x, y, x1, y1)
};
// src/model/enum/TextFormat.ts
import "@ncoderz/superenum";
var TextFormat = {
// plain text
plainText: "text",
latex: "latex",
// LaTeX code, alias for text
json: "json",
// json, alias for text
xml: "xml",
// xml, alias for text
// bitmarkText
// bitmarkText in the body is bitmark++ and bitmarkText in a tag is bitmark+
bitmarkText: "bitmark++"
};
// src/model/config/enum/CardSetConfigKey.ts
import "@ncoderz/superenum";
var CardSetConfigKey = {
flashcard: "flashcard",
flashcard1: "flashcard1",
definitionList: "definitionList",
definitionListPlain: "definitionListPlain",
elements: "elements",
statements: "statements",
quiz: "quiz",
quizResponses: "quizResponses",
feedback: "feedback",
questions: "questions",
matchPairs: "matchPairs",
matchAudioPairs: "matchAudioPairs",
matchImagePairs: "matchImagePairs",
matchMatrix: "matchMatrix",
table: "table",
tableExtended: "tableExtended",
pronunciationTable: "pronunciationTable",
botActionResponses: "botActionResponses",
exampleBitList: "exampleBitList",
pageFooterSections: "pageFooterSections",
clozeList: "clozeList",
ingredients: "ingredients",
// DEPRECATED - TO BE REMOVED IN THE FUTURE
// captionDefinitionsList: 'captionDefinitionsList',
bookReferenceList: "bookReferenceList"
};
// src/model/config/enum/GroupConfigType.ts
import "@ncoderz/superenum";
var GroupConfigType = {
standard: "standard",
// Any ordinary group
resource: "resource",
// A resource group
comboResource: "comboResource"
// A combo resource group
};
// src/model/enum/BitTagConfigKeyType.ts
import "@ncoderz/superenum";
var BitTagConfigKeyType = {
unknown: "unknown",
// Unknown tag type, used for error handling
tag: "tag",
// A standard tag
property: "property",
// A property tag
resource: "resource",
// A resource tag
//
group: "group"
// A reusable group of tags
};
// src/model/config/AbstractTagConfig.ts
var AbstractTagConfig = class {
// Deprecated version
constructor(params) {
__publicField(this, "type");
__publicField(this, "configKey");
__publicField(this, "tag");
__publicField(this, "maxCount");
// Default: 1
__publicField(this, "minCount");
// Default: 0
__publicField(this, "chain");
__publicField(this, "jsonKey");
// Legacy string-form jsonKey
__publicField(this, "exportJsonKey");
// New JSON-pattern jsonKey (export-only)
__publicField(this, "hasExportJsonKey");
// True iff exportJsonKey was explicitly set
__publicField(this, "htmlKey");
// HTML key-pattern (export-only)
__publicField(this, "hasHtmlKey");
// True iff htmlKey was explicitly set
__publicField(this, "deprecated");
this.type = params.type;
this.configKey = params.configKey;
this.tag = params.tag;
this.maxCount = params.maxCount;
this.minCount = params.minCount;
this.chain = params.chain;
this.jsonKey = params.jsonKey;
this.exportJsonKey = params.exportJsonKey;
this.hasExportJsonKey = params.hasExportJsonKey;
this.htmlKey = params.htmlKey;
this.hasHtmlKey = params.hasHtmlKey;
this.deprecated = params.deprecated;
}
};
// src/model/config/MarkupTagConfig.ts
var MarkupTagConfig = class extends AbstractTagConfig {
constructor(params) {
super({
type: BitTagConfigKeyType.tag,
...params
});
__publicField(this, "type", BitTagConfigKeyType.tag);
}
toString(options) {
const opts = Object.assign({}, options);
let s = `TAG[${this.tag}`;
if (opts.includeConfigs) {
if (this.deprecated != null) s += `, deprecated=${this.deprecated}`;
if (this.maxCount != null) s += `, max=${this.maxCount}`;
if (this.minCount != null) s += `, min=${this.minCount}`;
}
s += "]";
if (this.chain && opts.includeChains) {
s += `
|__`;
let first = true;
for (const tag of Object.values(this.chain)) {
if (!first) s += ` `;
s += `${tag.toString(opts)}`;
first = false;
}
}
return s;
}
};
// src/model/config/PropertyTagConfig.ts
var PropertyTagConfig = class extends AbstractTagConfig {
// The default value of the property - this value can be omitted from the markup
constructor(params) {
super({
type: BitTagConfigKeyType.property,
...params
});
__publicField(this, "type", BitTagConfigKeyType.property);
__publicField(this, "array");
// If the property is treated as an array rather than a single value
__publicField(this, "format");
// How the property is formatted
__publicField(this, "values");
// If the format is an enumeration, the possible values of the property
__publicField(this, "defaultValue");
this.array = params.maxCount === Count.infinity || params.maxCount > 1;
this.format = params.format;
this.values = params.values;
this.defaultValue = params.defaultValue;
}
toString(options) {
const opts = Object.assign({}, options);
let s = `PTY[${this.tag}`;
if (opts.includeConfigs) {
if (this.deprecated != null) s += `, deprecated=${this.deprecated}`;
if (this.maxCount != null) s += `, max=${this.maxCount}`;
if (this.minCount != null) s += `, min=${this.minCount}`;
if (this.array != null) s += `, arr=${this.array}`;
if (this.format != null) s += `, fmt=${this.format}`;
if (this.values != null) s += `, vals=[${this.values.join(",")}]`;
if (this.defaultValue != null) s += `, def=${this.defaultValue}`;
if (this.jsonKey != null) s += `, json=${this.jsonKey}`;
}
s += "]";
if (this.chain && opts.includeChains) {
s += `
|__`;
let first = true;
for (const tag of Object.values(this.chain)) {
if (!first) s += ` `;
s += `${tag.toString(opts)}`;
first = false;
}
}
return s;
}
};
// src/model/config/ResourceTagConfig.ts
var ResourceTagConfig = class extends AbstractTagConfig {
constructor(params) {
super({
type: BitTagConfigKeyType.resource,
...params
});
__publicField(this, "type", BitTagConfigKeyType.resource);
}
toString(options) {
const opts = Object.assign({}, options);
let s = `RES[${this.tag}`;
if (opts.includeConfigs) {
if (this.deprecated != null) s += `, deprecated=${this.deprecated}`;
if (this.maxCount != null) s += `, max=${this.maxCount}`;
if (this.minCount != null) s += `, min=${this.minCount}`;
if (this.jsonKey != null) s += `, json=${this.jsonKey}`;
}
s += "]";
if (this.chain && opts.includeChains) {
s += `
|__`;
let first = true;
for (const tag of Object.values(this.chain)) {
if (!first) s += ` `;
s += `${tag.toString(opts)}`;
first = false;
}
}
return s;
}
};
// src/model/config/CardVariantConfig.ts
var CardVariantConfig = class {
// Body text format. Default: bitmark++
constructor(tags2, bodyAllowed, bodyRequired, repeatCount, jsonKey, exportJsonKey, hasExportJsonKey = false, format, htmlKey, hasHtmlKey = false) {
__publicField(this, "tags");
__publicField(this, "bodyAllowed");
// Default: true
__publicField(this, "bodyRequired");
// Default: false
__publicField(this, "repeatCount");
// Default: 1
// JSON mapping fields
__publicField(this, "jsonKey");
// JSON path for body text (legacy)
__publicField(this, "exportJsonKey");
// New JSON-pattern jsonKey
__publicField(this, "hasExportJsonKey");
__publicField(this, "htmlKey");
// HTML key-pattern
__publicField(this, "hasHtmlKey");
__publicField(this, "format");
this.tags = tags2;
this.bodyAllowed = bodyAllowed == null ? true : bodyAllowed;
this.bodyRequired = bodyRequired;
this.repeatCount = repeatCount;
this.jsonKey = jsonKey;
this.exportJsonKey = exportJsonKey;
this.hasExportJsonKey = hasExportJsonKey;
this.htmlKey = htmlKey;
this.hasHtmlKey = hasHtmlKey;
this.format = format;
}
toString(options) {
const opts = Object.assign({}, options);
let s = "";
const flags = [];
if (this.bodyAllowed) flags.push("bodyAllowed");
if (this.bodyRequired) flags.push("bodyRequired");
if (this.repeatCount != null) flags.push(`repeatCount=${this.repeatCount}`);
s += `[Flags]
${flags.join(", ")}`;
s += `
[Tags]`;
for (const tag of Object.values(this.tags)) {
s += `
${tag.toString(opts)}`;
}
return s;
}
};
// src/model/config/CardSideConfig.ts
var CardSideConfig = class {
constructor(name, repeat, variants, jsonKey, exportJsonKey, hasExportJsonKey = false, htmlKey, hasHtmlKey = false) {
__publicField(this, "name");
__publicField(this, "repeat");
__publicField(this, "jsonKey");
__publicField(this, "exportJsonKey");
__publicField(this, "hasExportJsonKey");
__publicField(this, "htmlKey");
__publicField(this, "hasHtmlKey");
__publicField(this, "variants");
this.name = name;
this.repeat = repeat;
this.jsonKey = jsonKey;
this.exportJsonKey = exportJsonKey;
this.hasExportJsonKey = hasExportJsonKey;
this.htmlKey = htmlKey;
this.hasHtmlKey = hasHtmlKey;
this.variants = variants;
}
toString(options) {
const opts = Object.assign({}, options);
let s = "";
s += `[Side: ${this.name}]`;
if (this.repeat) s += " (repeat)";
let variantNo = 0;
for (const variant of this.variants) {
s += `
[Variant ${variantNo}]`;
s += `
${variant.toString(opts)}`;
variantNo++;
}
return s;
}
};
// src/model/config/CardSetConfig.ts
var CardSetConfig = class {
constructor(configKey, jsonKey, exportJsonKey, hasExportJsonKey, sections, sides, htmlKey = void 0, hasHtmlKey = false) {
__publicField(this, "configKey");
__publicField(this, "jsonKey");
__publicField(this, "exportJsonKey");
__publicField(this, "hasExportJsonKey");
__publicField(this, "htmlKey");
__publicField(this, "hasHtmlKey");
__publicField(this, "sections");
__publicField(this, "sides");
this.configKey = configKey;
this.jsonKey = jsonKey;
this.exportJsonKey = exportJsonKey;
this.hasExportJsonKey = hasExportJsonKey;
this.htmlKey = htmlKey;
this.hasHtmlKey = hasHtmlKey;
this.sections = sections;
this.sides = sides;
}
// Legacy accessor — provides the same shape as the old `variants: CardVariantConfig[][]`
// so downstream consumers (Config.getCardSetVariantConfig, etc.) continue to work.
get variants() {
return this.sides.map((side) => side.variants);
}
toString(options) {
const opts = Object.assign({}, options);
let s = "";
s += `[CardSet: ${this.configKey}]`;
s += `
jsonKey: ${this.jsonKey}`;
for (const side of this.sides) {
s += `
${side.toString(opts)}`;
}
return s;
}
};
// src/model/config/BitConfig.ts
var BitConfig = class {
constructor(config) {
__publicField(this, "since");
// Supported since version
__publicField(this, "bitType");
__publicField(this, "inheritedBitTypes");
// Bit inheritance tree (array for order)
__publicField(this, "inheritedBitTypesSet");
// Bit inheritance tree (set for faster lookup)
__publicField(this, "textFormatDefault");
// Default text format
__publicField(this, "tags", {});
__publicField(this, "cardSet");
__publicField(this, "quizBit");
// True if the bit is a quiz bit
__publicField(this, "deprecated");
// Deprecated version
__publicField(this, "bodyAllowed");
// Default: true
__publicField(this, "bodyRequired");
// Default: false
__publicField(this, "footerAllowed");
// Default: true
__publicField(this, "footerRequired");
// Default: false
__publicField(this, "resourceAttachmentAllowed");
// Default: true
__publicField(this, "rootExampleType");
__publicField(this, "comboResourceConfigKey");
const {
since,
bitType,
inheritedBitTypes,
textFormatDefault,
tags: tags2,
cardSet,
quizBit,
deprecated,
bodyAllowed,
bodyRequired,
footerAllowed,
footerRequired,
resourceAttachmentAllowed,
rootExampleType,
comboResourceConfigKey
} = config;
this.since = since;
this.bitType = bitType;
this.inheritedBitTypes = inheritedBitTypes;
this.inheritedBitTypesSet = new Set(inheritedBitTypes);
this.textFormatDefault = textFormatDefault;
this.tags = tags2;
this.cardSet = cardSet;
this.quizBit = quizBit;
this.deprecated = deprecated;
this.bodyAllowed = bodyAllowed == null ? true : bodyAllowed;
this.bodyRequired = bodyRequired;
this.footerAllowed = footerAllowed == null ? true : footerAllowed;
this.footerRequired = footerRequired;
this.resourceAttachmentAllowed = resourceAttachmentAllowed == null ? true : resourceAttachmentAllowed;
this.rootExampleType = rootExampleType;
this.comboResourceConfigKey = comboResourceConfigKey;
}
toString(options) {
const opts = Object.assign({}, options);
let s = `[Bit]
${this.bitType}`;
if (this.inheritedBitTypes.length > 0) {
s += `
[Inheritance]`;
s += `
${this.inheritedBitTypes.join(" => ")}`;
}
s += `
[Default text format]
${this.textFormatDefault}`;
const flags = [];
if (this.since != null) flags.push(`since=${this.since}`);
if (this.deprecated != null) flags.push(`deprecated=${this.deprecated}`);
if (this.bodyAllowed) flags.push("bodyAllowed");
if (this.bodyRequired) flags.push("bodyRequired");
if (this.footerAllowed) flags.push("footerAllowed");
if (this.footerRequired) flags.push("footerRequired");
if (this.resourceAttachmentAllowed) flags.push("resourceAttachmentAllowed");
if (this.rootExampleType != null) flags.push(`rootExampleType=${this.rootExampleType}`);
if (this.comboResourceConfigKey != null)
flags.push(`comboResourceConfigKey=${this.comboResourceConfigKey}`);
s += `
[Flags]
${flags.join(", ")}`;
s += `
[Tags]`;
for (const tag of Object.values(this.tags)) {
if (tag.maxCount === Count.infinity || tag.maxCount > 0) {
s += `
${tag.toString(opts)}`;
}
}
if (this.cardSet) {
s += `
${this.cardSet.toString(opts)}`;
}
return s;
}
};
// src/model/config/enum/ConfigKey.ts
import { Enum } from "@ncoderz/superenum";
// src/model/enum/GroupKey.ts
import "@ncoderz/superenum";
var groupKeys = {
group_standardAllBits: "group_standardAllBits",
group_standardItemLead: "group_standardItemLead",
group_standardItemLeadInstructionHint: "group_standardItemLeadInstructionHint",
group_standardInstructionHint: "group_standardInstructionHint",
group_standardTags: "group_standardTags",
group_standardTagsNoItemLead: "group_standardTagsNoItemLead",
group_imageSource: "group_imageSource",
group_technicalTerm: "group_technicalTerm",
group_person: "group_person",
group_gap: "group_gap",
group_trueFalse: "group_trueFalse",
group_trueFalseInlineHighlight: "group_trueFalseInlineHighlight",
group_trueFalseInlineSelect: "group_trueFalseInlineSelect",
group_trueFalseResponses: "group_trueFalseResponses",
group_markConfig: "group_markConfig",
group_mark: "group_mark",
group_bookCommon: "group_bookCommon",
group_learningPathCommon: "group_learningPathCommon",
group_advertisingCommon: "group_advertisingCommon",
group_platformStylesCommon: "group_platformStylesCommon",
group_quizCommon: "group_quizCommon",
group_backgroundWallpaper: "group_backgroundWallpaper",
group_imageNoZoom: "group_imageNoZoom",
group_resourceBitTags: "group_resourceBitTags",
group_resourceCommon: "group_resourceCommon",
group_resourceImageCommon: "group_resourceImageCommon",
group_resourceImageCommonNoZoom: "group_resourceImageCommonNoZoom",
group_resourceAudioCommon: "group_resourceAudioCommon",
group_resourceVideoCommon: "group_resourceVideoCommon",
group_resourceIcon: "group_resourceIcon",
// Alias for image
group_resourceImage: "group_resourceImage",
group_resourceImageNoZoom: "group_resourceImageNoZoom",
group_resourceImageEmbed: "group_resourceImageEmbed",
group_resourceImageLink: "group_resourceImageLink",
group_resourceImageResponsive: "group_resourceImageResponsive",
// Combo resource
group_resourceImagePortrait: "group_resourceImagePortrait",
group_resourceImageLandscape: "group_resourceImageLandscape",
group_resourceAudio: "group_resourceAudio",
group_resourceAudioEmbed: "group_resourceAudioEmbed",
group_resourceAudioLink: "group_resourceAudioLink",
group_resourceVideo: "group_resourceVideo",
group_resourceVideoEmbed: "group_resourceVideoEmbed",
group_resourceVideoLink: "group_resourceVideoLink",
group_resourceStillImageFilm: "group_resourceStillImageFilm",
// Combo resource
group_resourceStillImageFilmEmbed: "group_resourceStillImageFilmEmbed",
group_resourceStillImageFilmLink: "group_resourceStillImageFilmLink",
group_resourceArticle: "group_resourceArticle",
group_resourceArticleEmbed: "group_resourceArticleEmbed",
group_resourceArticleLink: "group_resourceArticleLink",
group_resourceDocument: "group_resourceDocument",
group_resourceDocumentEmbed: "group_resourceDocumentEmbed",
group_resourceDocumentLink: "group_resourceDocumentLink",
group_resourceDocumentDownload: "group_resourceDocumentDownload",
group_resourceAppLink: "group_resourceAppLink",
group_resourceWebsiteLink: "group_resourceWebsiteLink",
group_previewImages: "group_previewImages",
group_previewVideos: "group_previewVideos"
};
// src/model/enum/PropertyKey.ts
import "@ncoderz/superenum";
var propertyKeys = {
property_action: "@action",
property_activityType: "@activityType",
property_additionalSolutions: "@additionalSolutions",
property_advertisingClickUrl: "@advertisingClickUrl",
property_ageRange: "@ageRange",
property_aiGenerated: "@aiGenerated",
property_allowedBit: "@allowedBit",
property_allowPrint: "@allowPrint",
property_allowSubtitles: "@allowSubtitles",
property_alt: "@alt",
property_altLangTag: "@altLangTag",
property_analyticsTag: "@analyticsTag",
property_author: "@author",
property_authorFullName: "@authorFullName",
property_authorJobTitle: "@authorJobTitle",
property_authorPseudonym: "@authorPseudonym",
property_authorTitle: "@authorTitle",
property_autoplay: "@autoplay",
property_availableClassifications: "@availableClassifications",
property_brandColor: "@brandColor",
property_brandColorName: "@brandColorName",
property_backgroundImage: "@backgroundImage",
property_blockId: "@blockId",
property_book: "@book",
property_bookAlias: "@bookAlias",
property_bookDiff: "@bookDiff",
property_bot: "@bot",
property_bubbleTag: "@bubbleTag",
property_extractorTag: "@extractorTag",
property_extractorInternal: "@extractorInternal",
property_bitFingerprint: "@bitFingerprint",
property_buttonCaption: "@buttonCaption",
property_callToActionUrl: "@callToActionUrl",
property_caption: "@caption",
property_categoryTag: "@categoryTag",
property_chatWithBook: "@chatWithBook",
property_chatWithBookBrainKey: "@chatWithBookBrainKey",
property_currency: "@currency",
property_bookIsbn: "@bookIsbn",
property_bookAuthor: "@bookAuthor",
property_bookType: "@bookType",
property_bookBindingType: "@bookBindingType",
property_bookNumberOfPages: "@bookNumberOfPages",
property_bookRating: "@bookRating",
property_bookSeriesTitle: "@bookSeriesTitle",
property_bookSeriesId: "@bookSeriesId",
property_bookSeriesNumber: "@bookSeriesNumber",
property_bookLanguage: "@bookLanguage",
property_bookPublishingDate: "@bookPublishingDate",
property_bookPublisher: "@bookPublisher",
property_bookTranslator: "@bookTranslator",
property_bookOriginalTitle: "@bookOriginalTitle",
property_gmbExternalShop: "@gmbExternalShop",
property_gmbExternalShopItems: "@gmbExternalShopItems",
property_citationStyle: "@citationStyle",
property_classification: "@classification",
property_codeLineNumbers: "@codeLineNumbers",
property_codeMinimap: "@codeMinimap",
property_color: "@color",
property_colorTag: "@colorTag",
property_computerLanguage: "@computerLanguage",
property_content2Buy: "@content2Buy",
property_listPrice: "@listPrice",
property_customerProductId: "@customerProductId",
property_customerProductShopLink: "@customerProductShopLink",
property_copyright: "@copyright",
property_coverColor: "@coverColor",
property_coverImage: "@coverImage",
property_customerExternalId: "@customerExternalId",
property_customerId: "@customerId",
property_date: "@date",
property_dateEnd: "@dateEnd",
property_decimalPlaces: "@decimalPlaces",
property_deeplink: "@deeplink",
property_displayName: "@displayName",
property_diffContext: "@diffContext",
property_diffOp: "@diffOp",
property_diffRef: "@diffRef",
property_diffTime: "@diffTime",
property_diffTo: "@diffTo",
property_disableCalculation: "@disableCalculation",
property_disableFeedback: "@disableFeedback",
property_duration: "@duration",
property_isEditable: "@isEditable",
property_needsValidation: "@needsValidation",
property_validationDate: "@validationDate",
property_emphasis: "@emphasis",
property_example: "@example",
property_externalId: "@externalId",
property_externalLink: "@externalLink",
property_externalLinkText: "@externalLinkText",
property_feedbackEngine: "@feedbackEngine",
property_feedbackType: "@feedbackType",
property_userFeedbackTranslationUrl: "@userFeedbackTranslationUrl",
property_userFeedbackContentUrl: "@userFeedbackContentUrl",
property_flashcardSet: "@flashcardSet",
property_focusX: "@focusX",
property_focusY: "@focusY",
property_format: "@format",
property_formula: "@formula",
property_fullName: "@fullName",
property_groupTag: "@groupTag",
property_gUri: "@gUri",
property_handInAcceptFileType: "@handInAcceptFileType",
property_handInInstruction: "@handInInstruction",
property_handInRequirement: "@handInRequirement",
property_hasBookNavigation: "@hasBookNavigation",
property_hasMarkAsDone: "@hasMarkAsDone",
property_height: "@height",
property_icon: "@icon",
property_iconTag: "@iconTag",
property_id: "@id",
property_iframeSrc: "@iframeSrc",
property_iframeName: "@iframeName",
property_imageFirst: "@imageFirst",
property_imageSource: "@imageSource",
property_index: "@index",
property_internalComment: "@internalComment",
property_internalPrintPdf: "@internalPrintPdf",
property_hasPrintRestriction: "@hasPrintRestriction",
property_enforceUpdateOverUserInput: "@enforceUpdateOverUserInput",
property_extractorExtractionTimestamp: "@extractorExtractionTimestamp",
property_isCaseSensitive: "@isCaseSensitive",
property_isCollapsible: "@isCollapsible",
property_isInfoOnly: "@isInfoOnly",
property_isPublic: "@isPublic",
property_isTemplate: "@isTemplate",
property_isTemplateStripTheme: "@isTemplateStripTheme",
property_isTracked: "@isTracked",
property_jobTitle: "@jobTitle",
property_jupyterExecutionCount: "@jupyterExecutionCount",
property_jupyterId: "@jupyterId",
property_kind: "@kind",
property_label: "@label",
property_labelFalse: "@labelFalse",
property_labelTrue: "@labelTrue",
property_lang: "@lang",
property_language: "@language",
property_layer: "@layer",
property_layerRole: "@layerRole",
property_levelACTFL: "@levelACTFL",
property_levelCEFR: "@levelCEFR",
property_levelCEFRp: "@levelCEFRp",
property_levelILR: "@levelILR",
property_license: "@license",
property_list: "@list",
property_listItemIndent: "@listItemIndent",
property_location: "@location",
property_machineTranslated: "@machineTranslated",
property_translationOf: "@translationOf",
property_translationOfBook: "@translationOfBook",
property_spansPageBreak: "@spansPageBreak",
property_mailingList: "@mailingList",
property_mark: "@mark",
property_maxCreatedBits: "@maxCreatedBits",
property_maxDisplayLevel: "@maxDisplayLevel",
property_maxTocChapterLevel: "@maxTocChapterLevel",
property_mockupId: "@mockupId",
property_mute: "@mute",
property_padletId: "@padletId",
property_page: "@page",
property_printParentChapterLevel: "@printParentChapterLevel",
property_printOnRequestCustomerId: "@printOnRequestCustomerId",
property_printOnRequestProductId: "@printOnRequestProductId",
property_pageNo: "@pageNo",
property_partialAnswer: "@partialAnswer",
property_partner: "@partner",
// Deprecated, replaced by person
property_path: "@path",
property_platformBackgroundColor: "@platformBackgroundColor",
property_platformBorderColor: "@platformBorderColor",
property_platformBrandTarget: "@platformBrandTarget",
property_platformButtonBackgroundColor: "@platformButtonBackgroundColor",
property_platformButtonPrimaryColor: "@platformButtonPrimaryColor",
property_platformMessageBackgroundColor: "@platformMessageBackgroundColor",
property_platformName: "@platformName",
property_platformPlaceholderColor: "@platformPlaceholderColor",
property_platformPrimaryColor: "@platformPrimaryColor",
property_platformScrollbarColor: "@platformScrollbarColor",
property_platformSecondaryColor: "@platformSecondaryColor",
property_platformSelectionColor: "@platformSelectionColor",
property_platformSelectionTextColor: "@platformSelectionTextColor",
property_platformBaseLayerColor: "@platformBaseLayerColor",
property_platformMargin: "@platformMargin",
property_platformBorderRadius: "@platformBorderRadius",
property_platformSelectionBorderRadius: "@platformSelectionBorderRadius",
property_platformLogoMaxHeight: "@platformLogoMaxHeight",
property_platformNeedsShadow: "@platformNeedsShadow",
property_platformSeparatorColor: "@platformSeparatorColor",
property_platformTextSelectionColor: "@platformTextSelectionColor",
property_person: "@person",
property_pointerLeft: "@pointerLeft",
property_pointerTop: "@pointerTop",
property_posterImage: "@posterImage",
property_processHandIn: "@processHandIn",
property_processHandInLocation: "@processHandInLocation",
property_product: "@product",
property_productFolder: "@productFolder",
property_productId: "@productId",
property_productVideo: "@productVideo",
property_progress: "@progress",
property_pseudonym: "@pseudonym",
property_publications: "@publications",
property_publisher: "@publisher",
property_publisherName: "@publisherName",
property_quizCountItems: "@quizCountItems",
property_quizStrikethroughSolutions: "@quizStrikethroughSolutions",
property_quotedPerson: "@quotedPerson",
property_ratingLevelEnd: "@ratingLevelEnd",
property_ratingLevelSelected: "@ratingLevelSelected",
property_ratingLevelStart: "@ratingLevelStart",
property_reaction: "@reaction",
property_reasonableNumOfChars: "@reasonableNumOfChars",
property_reductionTag: "@reductionTag",
property_refAuthor: "@refAuthor",
property_refBookTitle: "@refBookTitle",
property_reference: "@reference",
property_refPublicationYear: "@refPublicationYear",
property_refPublisher: "@refPublisher",
property_relatedBook: "@relatedBook",
property_releaseDate: "@releaseDate",
property_releaseKind: "@releaseKind",
property_releaseVersion: "@releaseVersion",
property_reportTag: "@reportTag",
property_resolved: "@resolved",
property_resolvedBy: "@resolvedBy",
property_resolvedDate: "@resolvedDate",
property_revealSolutions: "@revealSolutions",
property_reviewTag: "@reviewTag",
property_reviewerTag: "@reviewerTag",
property_rightsPolicy: "@rightsPolicy",
property_sampleSolution: "@sampleSolution",
property_scormSource: "@scormSource",
property_search: "@search",
property_searchIndex: "@searchIndex",
property_selected: "@selected",
property_servings: "@servings",
property_showInIndex: "@showInIndex",
property_showSubtitles: "@showSubtitles",
property_siteName: "@siteName",
property_size: "@size",
property_slug: "@slug",
property_sourceBB: "@sourceBB",
property_sourceDocument: "@sourceDocument",
property_sourceRL: "@sourceRL",
property_spaceId: "@spaceId",
property_src1x: "@src1x",
property_src2x: "@src2x",
property_src3x: "@src3x",
property_src4x: "@src4x",
property_srcAlt: "@srcAlt",
property_stripePricingTableId: "@stripePricingTableId",
property_stripePricingTableDescriptiveName: "@stripePricingTableDescriptiveName",
property_stripePublishableKey: "@stripePublishableKey",
property_subject: "@subject",
property_subtype: "@subtype",
property_tableAutoWidth: "@tableAutoWidth",
property_tableColWidth: "@tableColWidth",
property_tableColumnMinWidth: "@tableColumnMinWidth",
property_tableFixedHeader: "@tableFixedHeader",
property_tableHeaderWhitespaceNoWrap: "@tableHeaderWhitespaceNoWrap",
property_tableHeight: "@tableHeight",
property_tablePagination: "@tablePagination",
property_tablePaginationLimit: "@tablePaginationLimit",
property_tableResizableColumns: "@tableResizableColumns",
property_tableSearch: "@tableSearch",
property_tableSort: "@tableSort",
property_tableWhitespaceNoWrap: "@tableWhitespaceNoWrap",
property_tableCellType: "@tableCellType",
property_tableRowSpan: "@tableRowSpan",
property_tableColSpan: "@tableColSpan",
property_tableScope: "@tableScope",
property_tag: "@tag",
property_target: "@target",
property_technicalTerm: "@technicalTerm",
property_textReference: "@textReference",
property_theme: "@theme",
property_rtl: "@rtl",
property_thumbImage: "@thumbImage",
property_title: "@title",
property_toc: "@toc",
property_tocContent: "@tocContent",
property_tocResource: "@tocResource",
property_topicTag: "@topicTag",
property_trim: "@trim",
property_unit: "@unit",
property_unitAbbr: "@unitAbbr",
property_unitAbbrPlural: "@unitAbbrPlural",
property_unitPlural: "@unitPlural",
property_vendorDashboardId: "@vendorDashboardId",
property_vendorSurveyId: "@vendorSurveyId",
property_vendorUrl: "@vendorUrl",
property_videoCallLink: "@videoCallLink",
property_width: "@width",
property_x: "@x",
property_y: "@y",
property_zoomDisabled: "@zoomDisabled"
};
var PropertyKey = propertyKeys;
// src/model/enum/ResourceKey.ts
import "@ncoderz/superenum";
var resourceKeys = {
resource_image: "&image",
resource_imageResponsive: "&imageResponsive",
resource_imagePortrait: "&imagePortrait",
resource_imageLandscape: "&imageLandscape",
resource_imageEmbed: "&imageEmbed",
resource_imageLink: "&imageLink",
resource_audio: "&audio",
resource_audioEmbed: "&audioEmbed",
resource_audioLink: "&audioLink",
resource_video: "&video",
resource_videoEmbed: "&videoEmbed",
resource_videoLink: "&videoLink",
resource_stillImageFilm: "&stillImageFilm",
resource_stillImageFilmEmbed: "&stillImageFilmEmbed",
resource_stillImageFilmLink: "&stillImageFilmLink",
resource_article: "&article",
resource_articleEmbed: "&articleEmbed",
resource_articleLink: "&articleLink",
resource_document: "&document",
resource_documentEmbed: "&documentEmbed",
resource_documentLink: "&documentLink",
resource_documentDownload: "&documentDownload",
resource_appLink: "&appLink",
resource_websiteLink: "&websiteLink",
resource_icon: "&icon",
resource_platformBackgroundImage: "&platformBackgroundImage",
resource_platformIcon: "&platformIcon",
resource_platformLogo: "&platformLogo",
resource_backgroundWallpaper: "&backgroundWallpaper",
resource_imagePlaceholder: "&imagePlaceholder",
resource_previewImage: "&previewImage",
resource_previewVideo: "&previewVideo",
resource_coverImage: "&coverImage",
resource_backgroundImage: "&backgroundImage"
};
// src/model/enum/ResourceType.ts
import "@ncoderz/superenum";
// src/utils/StringUtils.ts
var StringUtils = class {
/**
* Check if an object is a string.
*
* @param obj - The object to check.
* @returns true if the object is a string, otherwise false.
*/
isString(obj) {
return typeof obj === "string" || obj instanceof String;
}
/**
* Pass any value and convert it to a string.
*
* @param str the value to convert to a string
* @returns the string or an empty string if the value is undefined
*/
string(str) {
return str ? `${str}` : "";
}
/**
* Pass any value and convert it to a trimmed string.
*
* @param str the value to convert to a trimmed string
* @returns the trimmed string or an empty string if the value is undefined
*/
trimmedString(str) {
return this.string(str).trim();
}
/**
* Count the occurrences of a substring in a string.
*
* @param str the string to search
* @param subStr the substring to search for
* @returns the number of occurrences of the substring in the string
*/
countOccurrences(str, subStr) {
return str.split(subStr).length - 1;
}
/**
* Count the occurrences of a substring at the start of a string.
*
* @param str the string to search
* @param subStr the substring to search for
* @returns the number of occurrences of the substring at the start of the string
*/
countOccurrencesAtStart(str, subStr) {
let count = 0;
while (str.startsWith(subStr)) {
count++;
str = str.substring(subStr.length);
}
return count;
}
/**
* Count the occurrences of a substring at the end of a string.
*
* @param str the string to search
* @param subStr the substring to search for
* @returns the number of occurrences of the substring at the end of the string
*/
countOccurrencesAtEnd(str, subStr) {
let count = 0;
while (str.endsWith(subStr)) {
count++;
str = str.substring(0, str.length - subStr.length);
}
return count;
}
/**
* Split a string using a set of placeholders, returning an array of strings and placeholders.
*
* e.g.
* str = "This {0} a {1} with lots {2} placeholders"
* placeholders = ["{0}", "{1}", "{2}"]
*
* return = ["This ", "{0}", " a ", "{1}", " with lots ", "{2}", " placeholders"]
*
* @param str
* @param placeholders
*/
splitPlaceholders(str, placeholders) {
let res = [str];
const splitTexts = (texts, key) => {
const newTexts = [];
for (const text of texts) {
const tParts = text.split(key);
for (let i = 0, len = tParts.length; i < len; i++) {
const tPart = tParts[i];
const lastIndex = i === len - 1;
newTexts.push(tPart);
if (!lastIndex) {
newTexts.push(key);
}
}
}
return newTexts;
};
for (const p of placeholders) {
res = splitTexts(res, p);
}
return res;
}
/**
* Return the first line of text to a specified maximum width.
* @argument str The string to be truncated.
* @argument width The maximum width of the wrapped text in characters.
* @returns A string containing the first line, possibly truncated.
*/
firstLine(str, width) {
const result = str;
if (width == null) width = str.length;
if (width < 1 || str == null || str.length <= width) return result;
const strs = str.split(`
`);
if (strs.length > 0) {
return strs[0].substring(0, width);
}
return result;
}
/**
* Wraps the provided string input to a specified maximum width.
* @argument str The string to be wrapped.
* @argument width The maximum width of the wrapped text in characters.
* @returns A string array containing all lines.
*/
wordWrap(str, width) {
const result = [];
if (width < 1 || str == null || str.length <= width) return result;
const len = str.length;
const rangeMax = len - width;
let rangeStart = 0;
let subString;
while (rangeStart < rangeMax) {
let subLength;
const rangeEnd = rangeStart + width;
const ixNewLine = str.indexOf("\n", rangeStart);
if (ixNewLine > -1 && ixNewLine > rangeStart && ixNewLine < rangeEnd) {
subLength = ixNewLine - rangeStart;
subString = str.substr(rangeStart, subLength);
rangeStart = rangeStart + subLength + 1;
result.push(subString);
continue;
}
const ix = str.lastIndexOf(" ", rangeEnd);
let rangeStartNext = 0;
if (ix > -1 && ix != rangeStart - 1) {
subLength = ix - rangeStart;
if (subLength > 0) {
rangeStartNext = rangeStart + subLength + 1;
}
} else {
subLength = width;
rangeStartNext = rangeStart + width;
}
subString = str.slice(rangeStart, rangeStart + subLength);
rangeStart = rangeStartNext;
result.push(subString.trim());
}
if (rangeStart < len) {
subString = str.slice(rangeStart);
result.push(subString);
}
return result;
}
/**
* Convert a kebab-case string to camelCase.
*
* @param str the string to convert
* @returns the camelCase version of the string
*/
kebabToCamel(str) {
if (!str) return str;
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
}
/**
* Convert a camelCase string to kebab-case.
*
* @param str the string to convert
* @returns the kebab-case version of the string
*/
camelToKebab(str) {
if (!str) return str;
return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
}
};
var stringUtils = new StringUtils();
// src/model/enum/ResourceType.ts
var ResourceType = {
unknown: "unknown",
image: "image",
imageResponsive: "image-responsive",
imagePortrait: "image-portrait",
imageLandscape: "image-landscape",
imageEmbed: "image-embed",
imageLink: "image-link",
audio: "audio",
audioEmbed: "audio-embed",
audioLink: "audio-link",
video: "video",
videoEmbed: "video-embed",
videoLink: "video-link",
stillImageFilm: "still-image-film",
stillImageFilmEmbed: "still-image-film-embed",
stillImageFilmLink: "still-image-film-link",
article: "article",
articleEmbed: "article-embed",
articleLink: "article-link",
document: "document",
documentEmbed: "document-embed",
documentLink: "document-link",
documentDownload: "document-download",
appLink: "app-link",
websiteLink: "website-link",
// Aliases for image
icon: "icon",
backgroundWallpaper: "backgroundWallpaper",
imagePlaceholder: "imagePlaceholder",
platformIcon: "platformIcon",
platformLogo: "platformLogo",
platformBackgroundImage: "platformBackgroundImage",
previewImage: "previewImage",
previewVideo: "previewVideo",
coverImage: "coverImage",
backgroundImage: "backgroundImage"
};
function resourceTypeToConfigKey(type) {
return `&${stringUtils.kebabToCamel(type)}`;
}
// src/model/config/enum/ConfigKey.ts
var ConfigKey = {
// Internal
_unknown: "_unknown",
// Tags
...tags,
// Resources
...resourceKeys,
// Properties
...propertyKeys,
// Groups
...groupKeys
};
function typeFromConfigKey(tagKey) {
if (!tagKey) return BitTagConfigKeyType.unknown;
if (tagKey.startsWith("@")) return BitTagConfigKeyType.property;
if (tagKey.startsWith("&")) return BitTagConfigKeyType.resource;
if (tagKey.startsWith("group_")) return BitTagConfigKeyType.group;
return BitTagConfigKeyType.tag;
}
function configKeyToPropertyType(configKey) {
return configKey.replace(/^@/, "");
}
function configKeyToResourceType(configKey) {
return Enum(ResourceType).fromKey(configKey.replace(/^&/, ""));
}
// src/model/config/ResourcesConfig.ts
var ResourcesConfig = class {
constructor(tags2, resourceAttachmentAllowed, resourceAttachmentConfigKey, comboResourceConfigKeysMap) {
__publicField(this, "tags");
__publicField(this, "resourceAttachmentAllowed");
__publicField(this, "resourceAttachmentConfigKey");
__publicField(this, "comboResourceConfigKeysMap");
this.tags = tags2;
this.resourceAttachmentAllowed = resourceAttachmentAllowed ?? false;
this.resourceAttachmentConfigKey = resourceAttachmentConfigKey;
this.comboResourceConfigKeysMap = comboResourceConfigKeysMap;
}
getCountsMin() {
const counts = /* @__PURE__ */ new Map();
for (const r of Object.values(this.tags)) {
if (r.minCount != null) {
counts.set(r.configKey, r.minCount);
}
}
return counts;
}
getCountsMax() {
const counts = /* @__PURE__ */ new Map();
for (const r of Object.values(this.tags)) {
if (r.maxCount != null) {
counts.set(r.configKey, r.maxCount);
}
}
return counts;
}
};
// src/utils/ObjectUtils.ts
var ObjectUtils = class {
/**
* Check if a variable is a function
*
* @param val
* @returns true if a function
*/
isFunction(val) {
return Object.prototype.toString.call(val) === "[object Function]";
}
/**
* Check if a variable is an array
*
* @param val
* @returns true if a array
*/
isArray(val) {
return Array.isArray(val);
}
/**
* Check if a variable is a date
*
* @param val
* @returns true if a date
*/
isDate(val) {
return Object.prototype.toString.call(val) === "[object Date]";
}
/**
* Check if a variable is a object
*
* @param val
* @returns true if an object
*/
isObject(val) {
return Object.prototype.toString.call(val) === "[object Object]";
}
/**
* Check if a variable is a value (i.e. a number)
*
* @param val
* @returns true if a value
*/
isValue(val) {
return !this.isObject(val) && !this.isArray(val);
}
/**
* Get the value of a property in a plain JS object
* using a path string or array.
*
* @param obj the object to get a property from
* @param path the path to the property to set as a dotted string or array or a mix of both
* @returns the value of the property, or undefined if the property does not exist
*/
getViaPath(obj, path5) {
if (!obj) return void 0;
if (!path5) return void 0;
if (!Array.isArray(path5)) path5 = [path5];
path5 = path5.flatMap((p) => p.split("."));
let node = obj;
for (const key of path5) {
if (node == null) return void 0;
node = node[key];
}
return node;
}
/**
* Set the value of a property in a plain JS object
* using a path string or array.
*
* @param obj the object to modify
* @param path the path to the property to set as a dotted string or array or a mix of both
* @param value the value to set
* @param create - if true, create plain objects in the path if they do not exist
* @returns true if the value was set, false if the value was not set
*/
setViaPath(obj, path5, value, create) {
if (!obj) return false;
if (!path5) return false;
if (!Array.isArray(path5)) path5 = [path5];
path5 = path5.flatMap((p) => p.split("."));
let node = obj;
for (let i = 0; i < path5.length - 1; i++) {
const key = path5[i];
if (node[key] == void 0) {
if (create) {
node[key] = {};
} else {
return false;
}
}
node = node[key];
}
node[path5[path5.length - 1]] = value;
return true;
}
/**
* Return an array of the objects at the end of the paths
* specified in the path array.
*
* e.g.
*
* const obj = obj = {
* a: {
* b: {
* c: 1,
* },
* },
* d: [{ e: 2 }, { f: 3 }],
* g: [{ h: [{ i: 4 }] }, { h: [{ i: 5 }, { i: 6 }] }, { h: [{ i: 7 }] }],
* }
*
* objectUtils.flatMapPath(obj, ['a']);
* [{ b: { c: 1 }]
*
* objectUtils.flatMapPath(obj, ['a', 'b']);
* [{ c: 1 }]
*
* objectUtils.flatMapPath(obj, ['a', 'b', 'c']);
* [1]
*
* objectUtils.flatMapPath(obj, ['d']);
* [{ e: 2 }, { f: 3 }]
*
* objectUtils.flatMapPath(obj, ['d', 'e']);
* [2]
*
* objectUtils.flatMapPath(obj, ['g']);
* [{ h: [{ i: 4 }] }, { h: [{ i: 5 }, { i: 6 }] }, { h: [{ i: 7 }] }]
*
* objectUtils.flatMapPath(obj, ['g', 'h']);
* [{ i: 4 }, { i: 5 }, { i: 6 }, { i: 7 }]
*
* objectUtils.flatMapPath(obj, ['g', 'h', 'i']);
* [4, 5, 6, 7]
*
* @param obj
* @param path
* @returns
*/
flatMapPath(obj, path5) {
if (!obj) return [];
if (!path5) return [];
if (!Array.isArray(path5)) path5 = [path5];
const node = obj;
const key = path5.shift();
if (key == null) return node;
const data = node[key];
if (!data) return [];
if (Array.isArray(data)) {
return data.flatMap((d) => this.flatMapPath(d, path5.slice()));
} else {
return [this.flatMapPath(data, path5)].flat();
}
}
// testFlatMapPath() {
// const obj = {
// a: {
// b: {
// c: 1,
// },
// },
// d: [{ e: 2 }, { f: 3 }],
// g: [{ h: [{ i: 4 }] }, { h: [{ i: 5 }, { i: 6 }] }, { h: [{ i: 4 }] }],
// };
// console.log(this.flatMapPath(obj, 'a')); // [{ b: { c: 1 }]
// console.log(this.flatMapPath(obj, ['a', 'b'])); // [{ c: 1 }]
// console.log(this.flatMapPath(obj, ['a', 'b', 'c'])); // [1]
// console.log(this.flatMapPath(obj, ['d'])); // [{ e: 2 }, { f: 3 }]
// console.log(this.flatMapPath(obj, ['d', 'e'])); // [2]
// console.log(this.flatMapPath(obj, ['g'])); // [{ h: [{ i: 4 }] }, { h: [{ i: 5 }, { i: 6 }] }, { h: [{ i: 4 }] }]
// console.log(this.flatMapPath(obj, ['g', 'h'])); // [{ i: 4 }, { i: 5 }, { i: 6 }, { i: 4 }]
// console.log(this.flatMapPath(obj, ['g', 'h', 'i'])); // [4, 5, 6, 4]
// }
/**
* Order the properties of a plain JS object.
* The original properties are removed, and re-added in the new order.
* Any non-specified properties are dropped.
*
* NOTE: The JS spec does not specifiy an order for property iteration, but all major JS engines including V8 which
* is used by NodeJS and Chrome do have a specific iteration order which is defined by the order properties are
* added to an object. Many applications rely on this iteration order.
*
* @param obj - object with properties to re-order
* @param order - array of property keys in the desired order.
*/
orderProperties(obj, order) {
const objAsAny = obj;
const props = Object.assign({}, objAsAny);
for (const k of Object.keys(objAsAny)) {
delete objAsAny[k];
}
for (const k of order) {
if (Object.prototype.hasOwnProperty.call(props, k)) {
objAsAny[k] = props[k];
}
}
}
/**
* Remove any keys whose value is 'undefined' from a plain JS object
*
* @param obj plain JS object
* @param excludeKeys keys to be excluded from removal
*/
removeUndefinedProperties(obj, excludeKeys) {
const objAsAny = obj;
for (const [k, v] of Object.entries(objAsAny)) {
if (excludeKeys && excludeKeys.indexOf(k) >= 0) continue;
if (v == void 0) {
delete objAsAny[k];
}
}
}
/**
* Remove any keys whose value is 'false' from a plain JS object
*
* @param obj plain JS object
* @param excludeKeys keys to be excluded from removal
*/
removeFalseProperties(obj, excludeKeys) {
const objAsAny = obj;
for (const [k, v] of Object.entries(objAsAny)) {
if (excludeKeys && excludeKeys.indexOf(k) >= 0) continue;
if (v === false) {
delete objAsAny[k];
}
}
}
/**
* Remove any keys whose value is '' from a plain JS object
*
* @param obj plain JS object
* @param excludeKeys keys to be excluded from removal
*/
removeEmptyStringProperties(obj, excludeKeys) {
const objAsAny = obj;
for (const [k, v] of Object.entries(objAsAny)) {
if (excludeKeys && excludeKeys.indexOf(k) >= 0) continue;
if (v === "") {
delete objAsAny[k];
}
}
}
/**
* Remove any keys whose value is '[]' from a plain JS object
*
* @param obj plain JS object
* @param excludeKeys keys to be excluded from removal
*/
removeEmptyArrayProperties(obj, excludeKeys) {
const objAsAny = obj;
for (const [k, v] of Object.entries(objAsAny)) {
if (excludeKeys && excludeKeys.indexOf(k) >= 0) continue;
if (Array.isArray(v) && v.length === 0) {
delete objAsAny[k];
}
}
}
/**
* Remove any keys whose value is '{}' from a plain JS object
*
* @param obj plain JS object
* @param excludeKeys keys to be excluded from removal
*/
removeEmptyObjectProperties(obj, excludeKeys) {
const objAsAny = obj;
for (const [k, v] of Object.entries(objAsAny)) {
if (excludeKeys && excludeKeys.indexOf(k) >= 0) continue;
if (this.isObject(v) && Object.keys(v).length === 0) {
delete objAsAny[k];
}
}
}
/**
* Remove any unwanted properties from a plain JS object
*
* @param obj plain JS object
* @param excludeKeys keys to be excluded from removal
*/
removeUnwantedProperties(obj, options) {
options = Object.assign({}, options);
if (!options.ignoreAllUndefined) this.removeUndefinedProperties(obj, options.ignoreUndefined);
if (!options.ignoreAllFalse) this.removeFalseProperties(obj, options.ignoreFalse);
if (!options.ignoreAllEmptyString)
this.removeEmptyStringProperties(obj, options.ignoreEmptyString);
if (!options.ignoreAllEmptyArrays)
this.removeEmptyArrayProperties(obj, options.ignoreEmptyArrays);
if (!options.ignoreAllEmptyObjects)
this.removeEmptyObjectProperties(obj, options.ignoreEmptyObjects);
}
/**
* Extract a single value from a plain JS object
*
* If the item at the key is not an array, the item is returned.
* If the object at the key is an array, the last item in the array is returned.
*
* @param obj
* @param key
* @returns a non-array value, or undefined if the key does not exist
*/
extractSingleValue(obj, key) {
if (!obj) return void 0;
const objAsAny = obj;
if (objAsAny[key] == void 0) return void 0;
let value = objAsAny[key];
if (Array.isArray(value)) {
if (value.length === 0) return void 0;
value = value[value.length - 1];
}
return value;
}
/**
* Deep merge two or more objects or arrays.
* (c) 2023 Chris Ferdinandi, MIT License, https://gomakethings.com
* TypeScript version (c) 2023 RA Sewell
*
* @param objs The arrays or objects to merge
* @returns The merged arrays or objects
*/
deepMerge(...objs) {
function _deepMerge(...objs2) {
function getType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}
function mergeObj(clone2, obj) {
for (const [key, value] of Object.entries(obj)) {
const type = getType(value);
if (clone2[key] !== void 0 && getType(clone2[key]) === type && ["array", "object"].includes(type)) {
clone2[key] = _deepMerge(clone2[key], value);
} else {
clone2[key] = structuredClone(value);
}
}
}
let clone = structuredClone(objs2.shift());
for (const obj of objs2) {
const type = getType(obj);
if (getType(clone) !== type) {
clone = structuredClone(obj);
continue;
}
if (type === "array") {
clone = [...clone, ...structuredClone(obj)];
} else if (type === "object") {
mergeObj(clone, obj);
} else {
clone = obj;
}
}
return clone;
}
return _deepMerge(...objs);
}
};
var objectUtils = new ObjectUtils();
// src/config/ConfigHydrator.ts
import { Enum as Enum2 } from "@ncoderz/superenum";
// src/config/raw/cardSets.ts
var CARDSETS = {
//
// flashcard
//
[CardSetConfigKey.flashcard]: {
jsonKey: "cards",
exportJsonKey: { cards: "$" },
sides: [
{
name: "question",
variants: [
{
jsonKey: "question.text",
exportJsonKey: [{ "@absent": { question: {} } }, { question: { text: "$" } }],
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the flashcard."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the flashcard."
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the flashcard.",
jsonKey: "question.icon|resource(type=image, key=image)",
exportJsonKey: { question: { icon: { type: "image", image: { src: "$" } } } }
},
{
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker for the flashcard question.",
format: TagFormat.bitmarkText,
nullable: true
}
]
}
]
},
{
name: "answer",
variants: [
{
jsonKey: "answer.text",
exportJsonKey: [{ "@absent": { answer: {} } }, { answer: { text: "$" } }],
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the flashcard."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the flashcard."
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the flashcard.",
jsonKey: "answer.icon|resource(type=image, key=image)",
exportJsonKey: { answer: { icon: { type: "image", image: { src: "$" } } } }
},
{
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker for the flashcard answer.",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
{
jsonKey: "alternativeAnswers[].text",
exportJsonKey: { alternativeAnswers: [{ text: "$" }] },
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the flashcard."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the flashcard."
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the flashcard.",
jsonKey: "alternativeAnswers[].icon|resource(type=image, key=image)",
exportJsonKey: {
alternativeAnswers: [{ icon: { type: "image", image: { src: "$" } } }]
}
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// flashcard1 — single-card variant of flashcard. Same shape, but capped
// at one card via `sections.default.maxCount = 1` (PLAN-085). Used by
// the `flashcard-1` bit type; the multi-card `flashcard` and
// `q-and-a-card` bit types continue to use the unbounded `flashcard`
// cardset.
//
[CardSetConfigKey.flashcard1]: {
jsonKey: "cards",
exportJsonKey: { cards: "$" },
sections: {
default: {
jsonKey: "cards",
exportJsonKey: { cards: "$" },
isDefault: true,
maxCount: 1
}
},
sides: [
{
name: "question",
variants: [
{
jsonKey: "question.text",
exportJsonKey: [{ "@absent": { question: {} } }, { question: { text: "$" } }],
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the flashcard."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the flashcard."
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the flashcard.",
jsonKey: "question.icon|resource(type=image, key=image)",
exportJsonKey: { question: { icon: { type: "image", image: { src: "$" } } } }
},
{
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker for the flashcard question.",
format: TagFormat.bitmarkText,
nullable: true
}
]
}
]
},
{
name: "answer",
variants: [
{
jsonKey: "answer.text",
exportJsonKey: [{ "@absent": { answer: {} } }, { answer: { text: "$" } }],
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the flashcard."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the flashcard."
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the flashcard.",
jsonKey: "answer.icon|resource(type=image, key=image)",
exportJsonKey: { answer: { icon: { type: "image", image: { src: "$" } } } }
},
{
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker for the flashcard answer.",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
{
jsonKey: "alternativeAnswers[].text",
exportJsonKey: { alternativeAnswers: [{ text: "$" }] },
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the flashcard."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the flashcard."
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the flashcard.",
jsonKey: "alternativeAnswers[].icon|resource(type=image, key=image)",
exportJsonKey: {
alternativeAnswers: [{ icon: { type: "image", image: { src: "$" } } }]
}
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// definition-list
//
[CardSetConfigKey.definitionList]: {
jsonKey: "definitions",
exportJsonKey: { definitions: "$" },
// PLAN-141 F1 (NISO import): each <def-item> is one card; the sides
// read <term>/<def>; the enclosing <legend>'s <title> lands on the
// term side's heading tag ([#Key] header card).
// PLAN-141 F3: a <tbx:langSet> is ALSO one card (a terminology entry —
// its enclosing <tbx:termEntry> is a transparent claim wrapper); its
// children compose the term/definition sides via the side rules below.
mappingKeys: {
"xml-niso-iec": [
{ "@el": "def-item", "@children": "$" },
{ "@el": "tbx:langSet", "@children": "$" }
]
},
sides: [
{
name: "term",
// F3: every <tbx:tig>'s <tbx:term> STACKS one-per-line onto the
// single term side (the "$\n" template's authored separator; the
// trailing one is trimmed) — the PDF's Maus/Mus/M/Müsli stack.
mappingKeys: {
"xml-niso-iec": [
{ "@el": "term", "@text": "$" },
{ "@el": "tbx:tig", "@children": { "@el": "tbx:term", "@text": "$\n" } }
]
},
variants: [
{
jsonKey: "term.text",
exportJsonKey: [{ "@absent": { term: {} } }, { term: { text: "$" } }],
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the definition."
},
{
key: ConfigKey.tag_title,
description: "Title of the definition.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forKeys",
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } },
mappingKeys: { "xml-niso-iec": { "@el": "title", "@text": "$" } }
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the definition.",
format: TagFormat.plainText,
jsonKey: "term.icon|resource(type=image, key=image)",
exportJsonKey: { term: { icon: { type: "image", image: { src: "$" } } } }
},
{
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker for the definition term.",
format: TagFormat.bitmarkText,
nullable: true
}
]
}
]
},
{
name: "definition",
// F3: the definition side COMPOSES from the entry's fields in
// document order, with the ES-rendered locale affixes AUTHORED as
// value-position templates (never engine text): the subject field
// in ⟨⟩ glues (trailing space) onto the definition line; example /
// notes / source follow as blank-line-separated lines. The TBX
// grammar flags (normativeAuthorization, termType, partOfSpeech,
// grammaticalNumber) are unrendered machine-only data — dropped and
// recorded (catalog R-2, reviewed under D7 max-preservation).
mappingKeys: {
"xml-niso-iec": [
{ "@el": "def", "@children": "$" },
{ "@el": "tbx:subjectField", "@text": "\u27E8$\u27E9 " },
{ "@el": "tbx:definition", "@children": "$" },
{ "@el": "tbx:example", "@children": "BEISPIEL $" },
{ "@el": "tbx:note", "@children": "Anmerkung zu Begriff: $" },
{ "@el": "tbx:source", "@text": "[QUELLE: $]" }
]
},
variants: [
{
jsonKey: "definition.text",
exportJsonKey: [{ "@absent": { definition: {} } }, { definition: { text: "$" } }],
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the definition."
},
{
key: ConfigKey.tag_title,
description: "Title of the definition.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forValues",
exportJsonKey: { "@bit": { heading: { forValues: "$" } } }
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the definition.",
jsonKey: "definition.icon|resource(type=image, key=image)",
exportJsonKey: { definition: { icon: { type: "image", image: { src: "$" } } } }
},
{
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker for the definition.",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
{
jsonKey: "alternativeDefinitions[].text",
exportJsonKey: { alternativeDefinitions: [{ text: "$" }] },
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the definition."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the definition."
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the definition.",
jsonKey: "alternativeDefinitions[].icon|resource(type=image, key=image)",
exportJsonKey: {
alternativeDefinitions: [{ icon: { type: "image", image: { src: "$" } } }]
}
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// definition-list-plain
//
// Same shape as `definitionList`, but every variant body is rendered as
// plain text (string), not bitmark+ AST. Used by
// `meta-search-default-terms` / `meta-search-default-topics`, which need
// term/definition text emitted verbatim instead of as a ProseMirror tree.
//
[CardSetConfigKey.definitionListPlain]: {
jsonKey: "definitions",
exportJsonKey: { definitions: "$" },
sides: [
{
name: "term",
variants: [
{
jsonKey: "term.text",
exportJsonKey: [{ "@absent": { term: {} } }, { term: { text: "$" } }],
format: TextFormat.plainText,
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the definition."
},
{
key: ConfigKey.tag_title,
description: "Title of the definition.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forKeys",
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the definition.",
format: TagFormat.plainText,
jsonKey: "term.icon|resource(type=image, key=image)",
exportJsonKey: { term: { icon: { type: "image", image: { src: "$" } } } }
},
{
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker for the definition term.",
format: TagFormat.bitmarkText,
nullable: true
}
]
}
]
},
{
name: "definition",
variants: [
{
jsonKey: "definition.text",
exportJsonKey: [{ "@absent": { definition: {} } }, { definition: { text: "$" } }],
format: TextFormat.plainText,
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the definition."
},
{
key: ConfigKey.tag_title,
description: "Title of the definition.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forValues",
exportJsonKey: { "@bit": { heading: { forValues: "$" } } }
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the definition.",
jsonKey: "definition.icon|resource(type=image, key=image)",
exportJsonKey: { definition: { icon: { type: "image", image: { src: "$" } } } }
},
{
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker for the definition.",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
{
jsonKey: "alternativeDefinitions[].text",
exportJsonKey: { alternativeDefinitions: [{ text: "$" }] },
format: TextFormat.plainText,
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the definition."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the definition."
},
{
key: ConfigKey.group_resourceIcon,
description: "Icon resource for the definition.",
jsonKey: "alternativeDefinitions[].icon|resource(type=image, key=image)",
exportJsonKey: {
alternativeDefinitions: [{ icon: { type: "image", image: { src: "$" } } }]
}
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// match-pairs
//
[CardSetConfigKey.matchPairs]: {
jsonKey: "pairs",
exportJsonKey: { pairs: "$" },
sides: [
{
name: "key",
variants: [
{
jsonKey: "key",
exportJsonKey: { key: "$" },
format: TextFormat.plainText,
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the match pair."
},
{
// Pair-level example: emit `isExample` alongside `example`
// (matches OLD parser's per-pair allow-list behaviour).
//
// PLAN-074: a valued cascade source (`[@example:V]` anywhere
// in the cardset) fills each pair's `example` with `V`
// once per pair from the keys-side cascade fire (single
// parsed_variant per pair — no per-value duplication).
//
// PLAN-076 NOTE: matchPairs (TAG 415) KEEPS `example: '$'`
// at the pair-object level. BPG `parseMatchPairs`
// (`CardContentProcessor.ts:714-723`) emits ONE example
// per PAIR (the `exampleCard` accumulator), placed at the
// pair object. Unlike matchMatrix (per-cell example),
// matchPairs has no nested per-cell layer, so the pair-key
// `[@example:V]` must double-emit as the visible field.
// Confirmed via `chaining` fixture: `match-solution-grouped`
// expects `example: "packen"` at pair-object level from
// `die Koffer[%1][@example:packen]`. Original PLAN-076
// draft applied to both keys-side tags; the matchPairs
// half was reverted after fixture parity testing.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, "@bit": { isExample: true } } },
{
predicates: ["@absent", { $cascade: "*" }],
rule: {
isExample: true,
example: "$cascade",
"@bit": { isExample: true }
}
},
{ "@absent": { isExample: true } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker / value on the match pair.",
format: TagFormat.bitmarkText,
nullable: true
},
{
key: ConfigKey.tag_title,
description: "Title of the match pair.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forKeys",
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
},
{
// PLAN-084: gate the key-side `@absent → $ancestor` cascade
// synth with `@variantLacksTag=isCaseSensitive` so the rule
// only fires when NO variant in the active card explicitly
// carries `[@isCaseSensitive]`. When a value-side variant
// carries the tag (e.g. `[@isCaseSensitive:false]`), the
// synth is skipped here; the value-side explicit fire owns
// the pair-level emission.
key: ConfigKey.property_isCaseSensitive,
exportJsonKey: [
{
predicates: ["@absent", { "@variantLacksTag": "@isCaseSensitive" }],
rule: { isCaseSensitive: "$ancestor" }
},
{ isCaseSensitive: "$" }
],
description: "Property to indicate if the match is case sensitive.",
format: TagFormat.boolean
},
{
key: ConfigKey.resource_audio,
description: "Audio resource for the match pair.",
jsonKey: "keyAudio",
exportJsonKey: { keyAudio: "$" }
},
{
key: ConfigKey.resource_image,
description: "Image resource for the match pair.",
jsonKey: "keyImage",
exportJsonKey: { keyImage: "$" }
}
]
}
]
},
{
name: "values",
repeat: true,
variants: [
{
jsonKey: "values[]",
exportJsonKey: { values: ["$"] },
format: TextFormat.plainText,
tags: [
{
// PLAN-084: value-side excludes item/lead/pageNumber/marginNumber.
// BPG `parseMatchPairs` does `delete tags.item; delete tags.lead;`
// for sideIdx > 0; the schema-side equivalent is to use a
// group_standardTags variant that omits group_standardItemLead.
key: ConfigKey.group_standardTagsNoItemLead,
description: "Standard tags for the match pair (values side, no item/lead)."
},
{
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.6: cascade-fired
// `@example` from the bit header fills each pair's
// `example` with the pair's first value (mirrors BPG's
// `fillStringExample(pairs, __defaultExample=values[0])`).
//
// PLAN-074: the cascade-source-VALUE branch is handled by
// TAG 415 (keys side) which fires once per pair — keeping
// it here would emit `example` twice per pair (one per
// values-side parsed_variant) and merge them into a
// multi-paragraph array.
//
// PLAN-075: bare `$parent` reads the parent variant fire's
// source `$` value (the current `++`-iteration's value
// text). For single-value pairs this matches the old
// `$parent.values[0]`; for multi-value pairs with
// `[@example]` attached to a non-first value, it correctly
// resolves to that value.
//
// PLAN-077: dual `[@absent, { '$cascade': '*' }]` rule
// routes pair-key `[@example:V]` (a per-pair cascade
// source, bound via per-card CascadeCounters scope) and
// bit-header `[@example:V]` (cardset cascade) into the
// pair-level `example` via `$cascade`. The aggregator in
// `populate_variants` fires this rule ONCE per pair (BPG
// `parseMatchPairs` `exampleCard` precedence), avoiding
// per-iter pollution.
key: ConfigKey.property_example,
exportJsonKey: [
// PLAN-084: bare `[@example]` on a values-side variant fires
// once per pair (first-wins, mirrors BPG `parseMatchPairs`
// `exampleCard` + `pushDownTree` set-if-null semantic).
// `maxEmits: 1` enforced inside the per-pair cascade-counter
// scope opened by `build_card_item`.
{
predicates: ["@keyonly"],
maxEmits: 1,
rule: {
isExample: true,
example: "$parent",
"@bit": { isExample: true }
}
},
{
predicates: ["@absent", { $cascade: "*" }],
rule: {
isExample: true,
example: "$cascade",
"@bit": { isExample: true }
}
},
{
"@absent": {
isExample: true,
example: "$parent",
"@bit": { isExample: true }
}
},
// PLAN-084: same cap applies to the explicit-value `[@example:V]`
// form so per-`++`-iter values-side fires also first-win.
{
predicates: [],
maxEmits: 1,
rule: { isExample: true, example: "$", "@bit": { isExample: true } }
}
],
description: "Example marker / value on the match pair (values side).",
format: TagFormat.bitmarkText,
nullable: true
},
{
key: ConfigKey.tag_title,
description: "Title of the match pair.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forValues",
exportJsonKey: { "@bit": { heading: { forValues: "$" } } }
},
{
// PLAN-084: symmetric @variantLacksTag gating with the key-side.
key: ConfigKey.property_isCaseSensitive,
exportJsonKey: [
{
predicates: ["@absent", { "@variantLacksTag": "@isCaseSensitive" }],
rule: { isCaseSensitive: "$ancestor" }
},
{ isCaseSensitive: "$" }
],
description: "Property to indicate if the match is case sensitive.",
format: TagFormat.boolean
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// match-audio-pairs
//
[CardSetConfigKey.matchAudioPairs]: {
jsonKey: "pairs",
exportJsonKey: { pairs: "$" },
sides: [
{
name: "key",
variants: [
{
jsonKey: "key",
exportJsonKey: { key: "$" },
format: TextFormat.plainText,
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the audio match pair."
},
{
key: ConfigKey.tag_title,
description: "Title of the audio match pair.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forKeys",
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
},
{
key: ConfigKey.resource_audio,
description: "Audio resource for the match pair.",
jsonKey: "keyAudio",
exportJsonKey: { keyAudio: "$" }
}
]
}
]
},
{
name: "values",
repeat: true,
variants: [
{
jsonKey: "values[]",
exportJsonKey: { values: ["$"] },
format: TextFormat.plainText,
tags: [
{
// PLAN-084: value-side excludes item/lead/pageNumber/marginNumber.
key: ConfigKey.group_standardTagsNoItemLead,
description: "Standard tags for the audio match pair (values side, no item/lead)."
},
{
key: ConfigKey.tag_title,
description: "Title of the audio match pair.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forValues",
exportJsonKey: { "@bit": { heading: { forValues: "$" } } }
},
{
key: ConfigKey.resource_audio,
description: "Audio resource for the match pair."
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// match-image-pairs
//
[CardSetConfigKey.matchImagePairs]: {
jsonKey: "pairs",
exportJsonKey: { pairs: "$" },
sides: [
{
name: "key",
variants: [
{
jsonKey: "key",
exportJsonKey: { key: "$" },
format: TextFormat.plainText,
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the image match pair."
},
{
key: ConfigKey.tag_title,
description: "Title of the image match pair.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forKeys",
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
},
{
key: ConfigKey.resource_image,
description: "Image resource for the match pair.",
jsonKey: "keyImage",
exportJsonKey: { keyImage: "$" }
}
]
}
]
},
{
name: "values",
repeat: true,
variants: [
{
jsonKey: "values[]",
exportJsonKey: { values: ["$"] },
format: TextFormat.plainText,
tags: [
{
// PLAN-084: value-side excludes item/lead/pageNumber/marginNumber.
key: ConfigKey.group_standardTagsNoItemLead,
description: "Standard tags for the image match pair (values side, no item/lead)."
},
{
key: ConfigKey.tag_title,
description: "Title of the image match pair.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forValues",
exportJsonKey: { "@bit": { heading: { forValues: "$" } } }
},
{
key: ConfigKey.resource_image,
description: "Image resource for the match pair."
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// match-matrix
//
[CardSetConfigKey.matchMatrix]: {
jsonKey: "matrix",
exportJsonKey: { matrix: "$" },
sides: [
{
name: "key",
variants: [
{
jsonKey: "key",
exportJsonKey: { key: "$" },
format: TextFormat.plainText,
tags: [
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Standard tags for lead, instruction, and hint."
},
{
// PLAN-076: drop `example: '$'` from valued rule — pair-key
// `[@example:V]` is consumed as the per-pair cascade source.
// PLAN-077 R14 follow-up: investigation showed the fixture
// reference is INCONSISTENT — bits without a cardset-level
// cascade (e.g. bit 3 `[@example:Y]should be...`) expect
// pair-level `example: V` at pair scope, but bits WITH a
// cardset cascade (e.g. bit 8 `[@example:99]` in title-row
// PLUS pair-key `[@example:Y]`) expect NO pair-level
// example. Emitting `example: $` here satisfies bit 3 but
// breaks bit 8; dropping it satisfies bit 8 but breaks bit
// 3. Conditional emission would require runtime gating on
// the cardset cascade scope state. Left dropped pending a
// dedicated plan — accepted divergence on bit 3's pair-
// level emission (cells still get the per-cell `example`
// correctly via PLAN-077 §4.2 + §4.3).
key: ConfigKey.property_example,
exportJsonKey: [
{
"@keyonly": {
isExample: true,
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{
"@absent": {
isExample: true,
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{ isExample: true, "@bit": { isExample: true }, "@card": { isExample: true } }
],
description: "Example text for the match matrix.",
format: TagFormat.plainText,
nullable: true
},
{
key: ConfigKey.tag_title,
description: "Title of the match matrix.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forKeys",
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
},
{
// Axis-side `[@isCaseSensitive]` is a cascade SOURCE
// only: it must fire (so value-side cells inherit via
// `$ancestor`) but emit nothing itself — emission is
// owned by the value-side's cell-nested rule
// (`cells[$s].isCaseSensitive`, PLAN-122). The earlier
// flat `$ancestor` rule here double-emitted at bit
// scope and broke the `optimize(fullize(x)) ==
// optimize(x)` law on match-matrix.
key: ConfigKey.property_isCaseSensitive,
exportJsonKey: {},
description: "Property to indicate if the match is case sensitive.",
format: TagFormat.boolean
}
]
}
]
},
{
name: "cell",
repeat: true,
variants: [
{
jsonKey: "cells[{s}].values[]",
exportJsonKey: [
// PLAN-085 / R14: the positional-blank emission for an
// effectively-absent cell side fires ONLY when the active
// card has at least one `[#]` variant fire. This matches
// BPG `extractHeadingCard`'s `isHeading` gate: when no
// `[#]` exists anywhere in the title card, BPG returns
// undefined and no `heading` is emitted. The phantom
// `forValues:['']` placeholder is appropriate only when
// the card is being treated as a title-row.
{
predicates: ["@absent", { "@variantHasTag": "#" }],
rule: { "@bit": { heading: { forValues: [""] } } }
},
{ cells: { $s: { values: ["$"] } } }
],
format: TextFormat.plainText,
tags: [
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Standard tags for lead, instruction, and hint."
},
{
// PLAN-075: bare `$parent` reads the parent variant fire's
// source `$` value (the current `++`-iteration's cell text).
// Replaces `$parent.values[0]` which pinned to position 0
// regardless of which `++`-value `[@example]` was on.
//
// PLAN-077: dual `[@absent, { '$cascade': '*' }]` rule
// routes pair-key `[@example:V]` (a per-pair cascade
// source, bound via per-card CascadeCounters scope) and
// bit-header `[@example:V]` (cardset cascade) into the
// per-cell `example` via `$cascade`. The aggregator in
// `populate_variants` fires this rule ONCE per cell (BPG
// `parseMatchMatrix` `exampleSide` precedence), avoiding
// per-iter pollution.
// @card scope-shift writes matrix-row container isExample.
key: ConfigKey.property_example,
exportJsonKey: [
{
"@keyonly": {
cells: { $s: { isExample: true, example: "$parent" } },
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{
predicates: ["@absent", { $cascade: "*" }],
rule: {
cells: { $s: { isExample: true, example: "$cascade" } },
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{
"@absent": {
cells: { $s: { isExample: true, example: "$parent" } },
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{
cells: { $s: { isExample: true, example: "$" } },
"@bit": { isExample: true },
"@card": { isExample: true }
}
],
description: "Example text for the match matrix.",
format: TagFormat.bitmarkText,
nullable: true
},
{
// PLAN-085 / R14: bare `[#]` on a value-side emits the
// empty-string positional blank into `forValues` so the
// array stays aligned with the cell positions (matches BPG
// `extractHeadingCard`'s "all value-side headings present"
// shape). The `@keyonly` rule is required for two reasons:
// 1) it provides an emission for bare `[#]` (the `$` sigil
// in the unconditional rule has no source for bare); and
// 2) the presence of any `@keyonly` rule disables the
// optimised-mode `should_suppress_outcome` strip (see
// `forward_emit.rs`) so the resulting `forValues: [""]`
// survives the all-natural-defaults check.
key: ConfigKey.tag_title,
description: "Title of the match matrix.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forValues",
exportJsonKey: [
{ "@keyonly": { "@bit": { heading: { forValues: [""] } } } },
{ "@bit": { heading: { forValues: ["$"] } } }
]
},
{
// matchMatrix cells are NESTED (`cells[$s]`), unlike matchPairs'
// flat pair object — so `isCaseSensitive` must be written INTO the
// cell, mirroring the sibling `example` rule above (`cells: { $s:
// {...} }`). The earlier flat `{ isCaseSensitive: '$' }` (copied
// from matchPairs) wrote at bit scope, so an authored cell-side
// `[@isCaseSensitive:false]` (e.g. on a `++` value) was dropped
// instead of landing on the cell. PLAN-122.
key: ConfigKey.property_isCaseSensitive,
exportJsonKey: [
{ "@absent": { cells: { $s: { isCaseSensitive: "$ancestor" } } } },
{ cells: { $s: { isCaseSensitive: "$" } } }
],
description: "Property to indicate if the match is case sensitive.",
format: TagFormat.boolean
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// statements
//
[CardSetConfigKey.statements]: {
jsonKey: "statements",
exportJsonKey: { statements: "$" },
sides: [
{
name: "statement",
variants: [
{
jsonKey: "statement",
exportJsonKey: { statement: "$" },
tags: [
{
key: ConfigKey.tag_true,
description: "Tag for true statements.",
jsonKey: "statement|set(isCorrect=true)",
exportJsonKey: { statement: "$", isCorrect: true },
maxCount: 1
},
{
key: ConfigKey.tag_false,
description: "Tag for false statements.",
jsonKey: "statement|set(isCorrect=false)",
exportJsonKey: { statement: "$", isCorrect: false },
maxCount: 1
},
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the statement."
},
{
// PLAN-072: cascade-fired `@example` from the bit header
// fills each statement entry's `example` with the entry's
// own `isCorrect`, mirroring BPG's `pushExampleDownTree`
// for the statements cardset. Per-array-item strip removes
// `example: false` from incorrect entries so only correct
// entries retain `example: true`.
key: ConfigKey.property_example,
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: true,
"@bit": { isExample: true }
}
},
{
"@absent": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true }
}
},
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker on the statement. Bare `[@example]` emits the boolean marker; valued `[@example:true|false]` emits the literal value. Cascade from bit-header fills `example` with the entry\u2019s own `isCorrect`.",
format: TagFormat.boolean,
nullable: true
}
],
bodyAllowed: false
}
]
}
]
},
//
// quiz
//
[CardSetConfigKey.quiz]: {
jsonKey: "quizzes",
exportJsonKey: { quizzes: "$" },
sides: [
{
name: "choices",
variants: [
{
jsonKey: null,
tags: [
{
key: ConfigKey.group_trueFalse,
description: "Group for true/false questions."
},
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the quiz."
},
{
// ALIGN-EXAMPLE-CASCADE §3.2: variant-level cascade for
// quiz choices is delegated to group_trueFalse's
// chain-child `@example` (`+` chain with maxEmits:1 +
// `@parent.isCorrect=true`). Variant-level `@absent`
// would over-fire on subsequent correct choices and on
// incorrect ones — drop it.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, "@bit": { isExample: true } } },
{ isExample: true, "@bit": { isExample: true } }
],
description: "Example marker on the quiz choice (entry-local only; cascade handled by group_trueFalse chain-children).",
format: TagFormat.boolean,
nullable: true
}
],
bodyAllowed: false
}
]
}
]
},
//
// quiz (responses)
//
[CardSetConfigKey.quizResponses]: {
jsonKey: "quizzes",
exportJsonKey: { quizzes: "$" },
sides: [
{
name: "responses",
variants: [
{
jsonKey: null,
tags: [
{
key: ConfigKey.group_trueFalseResponses,
description: "Group for true/false questions."
},
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the quiz."
},
{
// PLAN-072: cascade-fired `@example` from the bit header
// fills each response entry's `example` with the entry's
// own `isCorrect`, mirroring BPG's `pushExampleDownTree`
// for the quiz-responses cardset (statements-style).
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, "@bit": { isExample: true } } },
{
"@absent": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true }
}
},
{ isExample: true, "@bit": { isExample: true } }
],
description: "Example marker on the quiz response; cascade from bit-header fills `example` with the entry\u2019s own `isCorrect`.",
format: TagFormat.boolean,
nullable: true
}
],
bodyAllowed: false
}
]
}
]
},
//
// feedback
//
[CardSetConfigKey.feedback]: {
jsonKey: "feedbacks",
exportJsonKey: { feedbacks: "$" },
sides: [
{
name: "choices",
variants: [
{
jsonKey: null,
tags: [
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Standard tags for lead, instruction, and hint."
},
{
// Feedback choices: `[+]` / `[-]` map to choice + `requireReason`
// (NOT `isCorrect` like trueFalse). Shadow the inherited
// `tag_true` / `tag_false` from `group_trueFalse` to emit the
// feedback-specific shape.
//
// Chain @example: `[+ X][@example]` writes
// `isExample/example` onto the X choice only. BPG's
// `setIsExampleFlags` (Builder.ts:4041-4172) does NOT
// iterate `cardNode.feedbacks` — so unlike multipleChoice,
// there is no bubble to bit-level or feedbacks-level
// isExample. Pattern omits `@bit` scope-shift accordingly.
// No `@absent`/`@parent.isCorrect` cascade rules either:
// BPG's `pushExampleDownTree` (Builder.ts:3777-3849) has
// no feedback dispatch, so bit-header `[@example]` does
// not flow into feedback choices.
key: ConfigKey.tag_true,
exportJsonKey: { choices: [{ choice: "$", requireReason: true }] },
description: "Reason-required choice for feedback (`[+]`).",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true } },
{ isExample: true, example: "$" }
],
description: "Per-choice example marker for feedback `[+]`.",
format: TagFormat.boolean,
maxCount: 1,
nullable: true
},
{
// `[+ X][! foo]` chains `!` (instruction) onto the
// choice; same for `[?]` (hint), `[%]` (item),
// `[%2]`/`[%3]`/`[%4]` (lead/pageNumber/marginNumber).
// Mirrors group_trueFalse's `+` chain shape.
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint chained onto the feedback choice."
}
]
},
{
key: ConfigKey.tag_false,
exportJsonKey: { choices: [{ choice: "$", requireReason: false }] },
description: "Reason-not-required choice for feedback (`[-]`).",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true } },
{ isExample: true, example: "$" }
],
description: "Per-choice example marker for feedback `[-]`.",
format: TagFormat.boolean,
maxCount: 1,
nullable: true
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint chained onto the feedback choice."
}
]
},
{
key: ConfigKey.group_trueFalse,
description: "Group for true/false feedback."
},
{
key: ConfigKey.tag_title,
description: "Title of the feedback.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forKeys",
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
}
],
bodyAllowed: false
}
]
},
{
name: "reason",
variants: [
{
// PLAN: feedback.reason — Object-only variant pattern. Per
// SYNTAX.md §8.1, sibling tag emissions do NOT auto-wrap
// under `reason.*`; each tag here spells out the full path.
// Body is a plain string (BPG `parseFeedback` uses
// `cardBodyStr ?? ''` for `reason.text`).
jsonKey: "reason.text",
exportJsonKey: { reason: { text: "$" } },
format: TextFormat.plainText,
tags: [
{
// Instruction lands under reason. Hint/item/lead/etc.
// omitted: not exercised by any fixture and BPG's
// `parseFeedback` spreads only the tags actually present
// — adding the rest can be done lazily as fixtures
// require them, with the same `{reason: {…}}` wrapper.
key: ConfigKey.tag_instruction,
exportJsonKey: { reason: { instruction: "$" } },
description: "Instruction for the feedback reason.",
format: TagFormat.bitmarkText
},
{
key: ConfigKey.property_reasonableNumOfChars,
exportJsonKey: [
{ "@absent": { reason: { reasonableNumOfChars: "$ancestor" } } },
{ reason: { reasonableNumOfChars: "$" } }
],
description: "Property for reasonable number of characters.",
format: TagFormat.number
},
{
// ALIGN-EXAMPLE-CASCADE §3.9: `feedback.reason` is NOT in
// BPG's `pushExampleDownTree` dispatch — no bit-header
// cascade. Local emission only.
//
// Both `reason.example` and `feedbacks[].example` are
// `TextAst` per BPG's schema — paragraph arrays. Format
// here is `bitmarkText` so `$` coerces to a paragraph
// array at both write sites.
key: ConfigKey.property_example,
exportJsonKey: [
{
// Bare `[@example]`: reason.example is TextAst — BPG
// coerces the boolean default to a paragraph
// containing the string "true". feedbacks[].example
// is not TextAst, stays a boolean. Two literal writes.
"@keyonly": {
reason: {
isExample: true,
example: [{ type: "paragraph", content: [{ text: "true", type: "text" }] }]
},
isExample: true,
example: true,
"@bit": { isExample: true }
}
},
{
reason: { isExample: true, example: "$" },
isExample: true,
example: "$",
"@bit": { isExample: true }
}
],
description: "Example text for the feedback reason (entry-local only; no bit-header cascade).",
format: TagFormat.bitmarkText,
nullable: true
},
{
key: ConfigKey.tag_title,
description: "Title of the feedback.",
format: TagFormat.plainText,
nullable: true,
jsonKey: "^heading.forValues",
exportJsonKey: { "@bit": { heading: { forValues: "$" } } }
}
],
bodyAllowed: true
}
]
}
]
},
//
// questions
//
[CardSetConfigKey.questions]: {
jsonKey: "questions",
exportJsonKey: { questions: "$" },
sides: [
{
name: "question",
variants: [
{
jsonKey: "question",
exportJsonKey: { question: "$" },
tags: [
{
key: ConfigKey.property_reasonableNumOfChars,
exportJsonKey: [
{ "@absent": { reasonableNumOfChars: "$ancestor" } },
{ reasonableNumOfChars: "$" }
],
description: "Property for reasonable number of characters.",
format: TagFormat.number
},
{
exportJsonKey: { sampleSolution: "$" },
key: ConfigKey.tag_sampleSolution,
description: "Sample solution for the question.",
format: TagFormat.plainText
},
{
key: ConfigKey.property_sampleSolution,
description: "Property for sample solution text.",
format: TagFormat.plainText
},
{
key: ConfigKey.property_additionalSolutions,
description: "Property for additional solutions.",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_partialAnswer,
description: "Property for partial answer text.",
format: TagFormat.plainText
},
{
key: ConfigKey.group_standardTags,
description: "Standard tags for the question."
},
{
// ALIGN-EXAMPLE-CASCADE §3.10: `questions` is NOT in BPG's
// `pushExampleDownTree` dispatch — bit-header `[@example]`
// does not propagate `example`/`isExample` onto question
// entries. Only entry-local `[@example]` fires.
//
// BPG's `BaseBuilder.toExample` for interview questions
// (Builder.ts:2575,2588) uses `data.__sampleSolutionAst`
// as `defaultExample` — bare `[@example]` (which sets
// `__isDefaultExample=true, example=undefined`) emits the
// parsed sample-solution AST in the `example` slot. The
// `@keyonly` rule mirrors that runtime behaviour via
// `$parent.sampleSolution` — the chain-walker's post-walk
// retry resolves this once the sibling `[@sampleSolution]`
// chain has merged into the shared scope (interview's
// `[@example]` typically precedes `[@sampleSolution]` in
// source). Format coercion turns the resolved string
// value into a PM tree.
key: ConfigKey.property_example,
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: "$parent.sampleSolution",
"@bit": { isExample: true }
}
},
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example marker for the question (entry-local only; no bit-header cascade).",
format: TagFormat.bitmarkText,
nullable: true
}
]
}
]
}
]
},
//
// elements
//
[CardSetConfigKey.elements]: {
jsonKey: null,
sides: [
{
name: "element",
repeat: true,
variants: [
{
jsonKey: "elements[]",
exportJsonKey: { elements: ["$"] },
tags: [
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Standard tags for lead, instruction, and hint."
},
{
// ALIGN-EXAMPLE-CASCADE §3.11: `elements` is NOT in BPG's
// `pushExampleDownTree` dispatch — no bit-header cascade.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example text for the element (entry-local only; no bit-header cascade).",
format: TagFormat.plainText,
nullable: true
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// table
//
// PLAN-079: supports BOTH source formats and collapses to the same basic
// output (`{ columns?, data, columnWidths? }`):
//
// 1. Basic format — first card carries `[#X]` markers (fire `tag_title`
// → `^table.columns[]`); subsequent cards are body rows. Cards have
// no qualifier, so they route to the `table-body` (default) section.
//
// 2. Extended format — `==== table-header ====` / `==== table-footer ====`
// qualifiers. The `table-header` section uses `@cardIndex=0` to route
// the first header card's cells to `columns` and demote remaining
// header cards (rows) into `data`. Per-cell `[@tableRowSpan]` /
// `[@tableScope]` / `[@tableCellType]` tags are valid-but-unmapped
// here (no per-cell metadata in basic shape) — declared on the cell
// variant below.
//
[CardSetConfigKey.table]: {
jsonKey: null,
exportJsonKey: null,
// PLAN-109 / HTML.md §8 — idiomatic HTML mapping. The classic (non-extended)
// table produces the same `<table>`/`<thead>`/`<tbody>`/`<tfoot>` structure as
// table-extended, so the structural htmlKeys mirror that cardSet. NOTE: the
// JSON side routes the FIRST header card → `columns` (header) and SUBSEQUENT
// header cards → `data` (body); HTML uses a uniform `<thead>` for the whole
// `table-header` section, so a multi-card header (e.g. a second header card
// that JSON sends to `data`) renders as additional `<thead>` rows rather than
// moving to `<tbody>`. Per-card header/body splitting needs engine-level
// routing not yet expressible in the pattern vocabulary — see PLAN-109.
htmlKey: { "@el": "table", "@children": "$" },
sections: {
"table-header": {
// First header card's row of cells → `columns`. Subsequent header
// cards' rows → `data` (per-card append; `[$]` shape because
// per-card emission fires once per card, multiple fires deep_merge-
// append). PLAN-079 + PLAN-080: predicates inside exportJsonKey
// both ROUTE cards to this section (when no explicit `==== table-
// header ====` qualifier) and SELECT the rule body once routed.
// First rule = first card with any `[#]` → `columns`; second rule
// = subsequent cards in this section → `data` (per-row append).
jsonKey: "table.columns",
exportJsonKey: [
{
predicates: [{ "@cardIndex": 0 }, { "@variantHasTag": "#" }],
rule: { "@bit": { table: { columns: "$" } } }
},
{ "@cardIndex=0": { "@bit": { table: { columns: "$" } } } },
{ "@bit": { table: { data: ["$"] } } }
],
// Header section → `<thead>` rows of `<th>`; `@tableColWidth` bubbles a
// `width` attr onto the enclosing `<th>` (HTML-C2).
htmlKey: { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } },
sideHtmlKey: { "@el": "th", "@children": "$" }
},
"table-body": {
// Batched emission: `$` is the entire 2D array of body card rows.
// This is the default section (no qualifier → cards land here).
jsonKey: "table.data",
exportJsonKey: { "@bit": { table: { data: "$" } } },
htmlKey: { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } },
isDefault: true
},
"table-footer": {
// Batched emission: `$` is the entire 2D array of footer rows.
jsonKey: "table.data",
exportJsonKey: { "@bit": { table: { data: "$" } } },
// Footer cells route to `data` (not `columns`), so they are `<td>` (no
// sideHtmlKey override — the cell side default applies).
htmlKey: { "@el": "tfoot", "@children": { "@el": "tr", "@children": "$" } }
}
},
sides: [
{
name: "cell",
repeat: true,
// Default (body/footer) cell → `<td>`; the header section overrides to
// `<th>` via its sideHtmlKey (HTML.md §8).
htmlKey: { "@el": "td", "@children": "$" },
variants: [
{
jsonKey: null,
tags: [
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Standard tags for lead, instruction, and hint."
},
{
key: ConfigKey.tag_title,
jsonKey: "^table.columns[]",
exportJsonKey: { "@bit": { table: { columns: ["$"] } } },
description: "Title of the table."
},
{
key: ConfigKey.property_tableCellType,
// HTML.md §8: first-match predicate selecting `<th>` vs `<td>`.
htmlKey: [{ "@value=th": { "@el": "th" } }, { "@absent": { "@el": "td" } }],
description: "Table cell type (th/td).",
format: TagFormat.plainText
},
{
key: ConfigKey.property_tableRowSpan,
// HTML-C2: contribute a `rowspan` attribute onto the enclosing
// cell element (`<th>`/`<td>`).
htmlKey: { "@el": { "@attr": { rowspan: "$" } } },
description: "Number of rows the cell spans.",
format: TagFormat.number
},
{
key: ConfigKey.property_tableColSpan,
// HTML-C2: contribute a `colspan` attribute onto the enclosing
// cell element (`<th>`/`<td>`).
htmlKey: { "@el": { "@attr": { colspan: "$" } } },
description: "Number of columns the cell spans.",
format: TagFormat.number
},
{
key: ConfigKey.property_tableScope,
// HTML-C2: contribute a `scope` attribute onto the enclosing
// cell element (`<th>`/`<td>`).
htmlKey: { "@el": { "@attr": { scope: "$" } } },
description: "Scope attribute for header cells.",
format: TagFormat.plainText
},
{
key: ConfigKey.property_tableColWidth,
jsonKey: "^table.columnWidths[{s}]",
exportJsonKey: { "@bit": { table: { columnWidths: { $s: "$" } } } },
// HTML.md §8 + HTML-C2: contribute a `width` attribute onto the
// enclosing cell element (`<th>`/`<td>`).
htmlKey: { "@el": { "@attr": { width: "$" } } },
description: "Width for the column.",
format: TagFormat.number
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// table-extended
//
[CardSetConfigKey.tableExtended]: {
jsonKey: null,
// PLAN-109 / HTML.md §8 — idiomatic HTML mapping for the extended table.
// The card-set container is the `<table>` element; each section is a
// table section element (`<thead>`/`<tbody>`/`<tfoot>`) whose children are
// `<tr>` rows, and each cell side is a `<td>` (body) or `<th>` (header /
// footer, via the section sideHtmlKey override).
htmlKey: { "@el": "table", "@children": "$" },
sections: {
"table-header": {
jsonKey: "table.header.rows",
// PLAN-080: section dispatch via predicates inside exportJsonKey.
// Cards with no explicit `==== table-header ====` qualifier
// route here if FIRST CARD contains any `[#]` tag in any
// variant. Explicit-qualifier cards fall through to the
// unconditional fallback rule. Both rules use the per-card
// append shape `rows: ['$']` — the runtime triggers per-card
// emission whenever the pattern carries predicates, and each
// fire appends a single-element array (multi-fire deep-merge
// concatenates).
exportJsonKey: [
{
predicates: [{ "@cardIndex": 0 }, { "@variantHasTag": "#" }],
rule: { table: { header: { rows: ["$"] } } }
},
{ table: { header: { rows: ["$"] } } }
],
htmlKey: { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } },
// PLAN-141 F2 (NISO import): the XHTML table model NISO uses — a
// <thead> SECTION whose <tr> rows are the header cards.
mappingKeys: {
"xml-niso-iec": { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } }
},
sideJsonKey: "cells[{s}]|set(title=true)",
sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } },
// Header cells are `<th>`. The `width` attribute is contributed by the
// `@tableColWidth` tag, which bubbles it onto the enclosing `<th>`
// (HTML-C2) — so it is not repeated here.
sideHtmlKey: { "@el": "th", "@children": "$" }
},
"table-body": {
jsonKey: "table.body.rows",
exportJsonKey: { table: { body: { rows: "$" } } },
htmlKey: { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } },
// PLAN-141 F2 (NISO import): <tbody> rows are the (default) data cards.
mappingKeys: {
"xml-niso-iec": { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } }
},
isDefault: true
},
"table-footer": {
jsonKey: "table.footer.rows",
exportJsonKey: { table: { footer: { rows: "$" } } },
htmlKey: { "@el": "tfoot", "@children": { "@el": "tr", "@children": "$" } },
// PLAN-141 F2 (NISO import): NISO has no <tfoot> — the footer card's
// source is the <table-wrap-foot> block (CONTENT-shaped: no rows; its
// prose/notes flatten into one footer card — catalog table-wrap-foot
// row; a contained <def-list> promotes as a sibling legend bit).
mappingKeys: { "xml-niso-iec": { "@el": "table-wrap-foot", "@children": "$" } },
sideJsonKey: "cells[{s}]|set(title=true)",
sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } },
// Footer cells render as `<th>` (mirrors the header `title: true`
// mapping); §8 lists only the header side, so this parallels it.
sideHtmlKey: { "@el": "th", "@children": "$" }
}
},
sides: [
{
name: "cell",
repeat: true,
jsonKey: "cells[{s}]",
exportJsonKey: { cells: { $s: "$" } },
// Default (body) cell → `<td>`; the header/footer sections override the
// side to `<th>` via their sideHtmlKey (HTML.md §8).
htmlKey: { "@el": "td", "@children": "$" },
// PLAN-141 F2 (NISO import): a <td> row child is a body cell side.
mappingKeys: { "xml-niso-iec": { "@el": "td", "@children": "$" } },
variants: [
{
jsonKey: "content",
exportJsonKey: { content: "$" },
tags: [
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Standard tags for lead, instruction, and hint."
},
{
key: ConfigKey.tag_title,
// PLAN-080: the `[#]` tag writes its text to the cell's
// `content` field. When the card is in the `table-header`
// section, the section's sideExportJsonKey wraps each
// cell with `title: true` automatically.
jsonKey: "content",
exportJsonKey: { content: "$" },
description: "Title text of the table cell (header rows).",
// PLAN-141 F2 (NISO import): a <th> row child is a TITLE cell —
// its content lands in the `[#…]` tag (an empty <th> keeps its
// slot as a bare `[#]`).
mappingKeys: { "xml-niso-iec": { "@el": "th", "@children": "$" } }
},
{
key: ConfigKey.property_tableCellType,
jsonKey: "title|bool(th)",
exportJsonKey: [{ "@value=th": { title: true } }],
// HTML.md §8: same first-match predicate machinery selecting
// `<th>` vs `<td>` instead of `title:true` vs nothing.
htmlKey: [{ "@value=th": { "@el": "th" } }, { "@absent": { "@el": "td" } }],
description: "Table cell type (th/td).",
format: TagFormat.plainText
},
{
key: ConfigKey.property_tableRowSpan,
jsonKey: "rowspan",
exportJsonKey: { rowspan: "$" },
// HTML-C2: contribute a `rowspan` attribute onto the enclosing
// cell element (`<th>`/`<td>`).
htmlKey: { "@el": { "@attr": { rowspan: "$" } } },
// PLAN-141 F2 (NISO import): recover the source cell's own
// `rowspan` attribute (element-scope attr form).
mappingKeys: { "xml-niso-iec": { "@el": { "@attr": { rowspan: "$" } } } },
description: "Number of rows the cell spans.",
format: TagFormat.number
},
{
key: ConfigKey.property_tableColSpan,
jsonKey: "colspan",
exportJsonKey: { colspan: "$" },
// HTML-C2: contribute a `colspan` attribute onto the enclosing
// cell element (`<th>`/`<td>`).
htmlKey: { "@el": { "@attr": { colspan: "$" } } },
// PLAN-141 F2 (NISO import): recover the source cell's own
// `colspan` attribute (element-scope attr form).
mappingKeys: { "xml-niso-iec": { "@el": { "@attr": { colspan: "$" } } } },
description: "Number of columns the cell spans.",
format: TagFormat.number
},
{
key: ConfigKey.property_tableScope,
jsonKey: "scope",
exportJsonKey: { scope: "$" },
// HTML-C2: contribute a `scope` attribute onto the enclosing
// cell element (`<th>`/`<td>`).
htmlKey: { "@el": { "@attr": { scope: "$" } } },
description: "Scope attribute for header cells.",
format: TagFormat.plainText
},
{
key: ConfigKey.property_tableColWidth,
// PLAN-080: write to the cell's `colwidth` field.
jsonKey: "colwidth",
exportJsonKey: { colwidth: "$" },
// HTML.md §8 + HTML-C2: contribute a `width` attribute onto the
// enclosing cell element (`<th>`/`<td>`).
htmlKey: { "@el": { "@attr": { width: "$" } } },
// PLAN-141 F2 (NISO import): NISO carries widths as PER-COLUMN
// <col width="…"> descriptors at container level, not on cells.
// The matched values distribute positionally onto the first
// (header) card's sides. Number format ⇒ only an integral
// percentage survives ("20.0000%" → 20); a fractional one
// (renderer-equalised "4.7619%") is dropped — catalog `col` row.
mappingKeys: { "xml-niso-iec": { "@el": "col", "@attr": { width: "$" } } },
description: "Width for the column.",
format: TagFormat.number
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// pronunciation-table
//
[CardSetConfigKey.pronunciationTable]: {
jsonKey: "pronunciationTable.data",
exportJsonKey: { pronunciationTable: { data: "$" } },
sides: [
{
name: "cell",
repeat: true,
variants: [
{
jsonKey: "body",
exportJsonKey: { body: "$" },
tags: [
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Standard tags for lead, instruction, and hint."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the pronunciation table."
},
{
key: ConfigKey.resource_audio,
description: "Audio resource for the pronunciation table.",
jsonKey: "audio",
exportJsonKey: { audio: { type: "audio", audio: { src: "$" } } }
}
],
repeatCount: Count.infinity
}
]
}
]
},
//
// bot-action-responses
//
[CardSetConfigKey.botActionResponses]: {
jsonKey: "responses",
exportJsonKey: { responses: "$" },
sides: [
{
name: "response",
variants: [
{
jsonKey: "feedback",
exportJsonKey: { feedback: "$" },
format: TextFormat.plainText,
tags: [
{
key: ConfigKey.property_reaction,
description: "Reaction to the bot action.",
format: TagFormat.plainText
},
{
key: ConfigKey.tag_item,
jsonKey: "item",
exportJsonKey: { item: "$" },
format: TagFormat.plainText,
description: "The item for the bit",
chain: [
{
key: ConfigKey.tag_item,
jsonKey: "lead",
exportJsonKey: { lead: "$" },
description: "The lead for the bit",
maxCount: 1,
chain: [
{
key: ConfigKey.tag_item,
jsonKey: "pageNumber",
exportJsonKey: { pageNumber: "$" },
description: "The page number for the bit",
maxCount: 1,
chain: [
{
key: ConfigKey.tag_item,
jsonKey: "marginNumber",
exportJsonKey: { marginNumber: "$" },
description: "The margin number for the bit",
maxCount: 1
}
]
}
]
}
]
},
{
key: ConfigKey.tag_instruction,
description: "The response label for the bot action.",
jsonKey: "response",
exportJsonKey: { response: "$" },
format: TagFormat.plainText
},
{
// ALIGN-EXAMPLE-CASCADE §3.12: `bot-action-responses` is
// NOT in BPG's `pushExampleDownTree` dispatch — no bit-
// header cascade.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "Example text for the bot action response (entry-local only; no bit-header cascade).",
format: TagFormat.plainText,
nullable: true
}
]
}
]
}
]
},
//
// cloze-list
//
[CardSetConfigKey.clozeList]: {
jsonKey: "listItems",
exportJsonKey: { listItems: "$" },
sides: [
{
name: "content",
variants: [
{
jsonKey: "body",
exportJsonKey: { body: "$" },
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for cloze items."
},
{
key: ConfigKey.group_gap,
description: "Group for gap tags in cloze items."
}
]
}
]
}
]
},
//
// example-bit-list
//
[CardSetConfigKey.exampleBitList]: {
jsonKey: "listItems",
exportJsonKey: { listItems: "$" },
sides: [
{
name: "item",
variants: [
{
jsonKey: "body",
exportJsonKey: { body: "$" },
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for example bits."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the example bit."
}
]
}
]
}
]
},
//
// page-footer-sections — mirrors example-bit-list but emits cards under
// `sections` instead of `listItems`. BPG's JsonGenerator special-cases
// BitType.pageFooter in code; this dedicated cardSet expresses the same
// routing as pure config so the Rust serializer can pick it up.
//
[CardSetConfigKey.pageFooterSections]: {
jsonKey: "sections",
exportJsonKey: { sections: "$" },
sides: [
{
name: "item",
variants: [
{
jsonKey: "body",
exportJsonKey: { body: "$" },
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for page-footer section bits."
},
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the page-footer section."
}
]
}
]
}
]
},
//
// ingredients
//
[CardSetConfigKey.ingredients]: {
jsonKey: "ingredients",
exportJsonKey: { ingredients: "$" },
sides: [
{
name: "ingredient",
variants: [
{
jsonKey: "ingredient",
exportJsonKey: { ingredient: "$" },
tags: [
{
exportJsonKey: { title: "$" },
key: ConfigKey.tag_title,
description: "Title of the ingredient."
},
{
key: ConfigKey.tag_true,
description: "Checked state for the ingredient.",
jsonKey: "|set(checked=true)",
exportJsonKey: { checked: true, $: "$" },
maxCount: 1
// nullable: true,
},
{
key: ConfigKey.tag_false,
description: "Unchecked state for the ingredient.",
jsonKey: "|set(checked=false)",
exportJsonKey: { checked: false, $: "$" },
maxCount: 1
// nullable: true,
},
{
key: ConfigKey.group_standardItemLead,
description: "Item, lead, page number, and margin number for the ingredient."
},
{
key: ConfigKey.tag_instruction,
description: "The quantity of the ingredient.",
jsonKey: "quantity",
exportJsonKey: { quantity: "$" },
format: TagFormat.number
},
{
exportJsonKey: { hint: "$" },
key: ConfigKey.tag_hint,
description: "The hint for the ingredient."
},
{
key: ConfigKey.property_unit,
description: "Unit of measurement for the ingredient (singular).",
format: TagFormat.plainText
},
{
key: ConfigKey.property_unitPlural,
description: "Unit of measurement for the ingredient (plural).",
format: TagFormat.plainText
},
{
key: ConfigKey.property_unitAbbr,
description: "Abbreviation for the unit of measurement (singular).",
format: TagFormat.plainText
},
{
key: ConfigKey.property_unitAbbrPlural,
description: "Abbreviation for the unit of measurement (plural).",
format: TagFormat.plainText
},
{
key: ConfigKey.property_decimalPlaces,
description: "Number of decimal places for the ingredient quantity.",
format: TagFormat.number,
defaultValue: "1"
},
{
key: ConfigKey.property_disableCalculation,
description: "Disable calculation for the ingredient.",
format: TagFormat.boolean
}
]
}
]
}
]
},
//
// book-reference-list
//
[CardSetConfigKey.bookReferenceList]: {
jsonKey: "bookReferences",
exportJsonKey: { bookReferences: "$" },
sides: [
{
name: "reference",
variants: [
{
jsonKey: null,
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for book references."
},
{
key: ConfigKey.property_refAuthor,
description: "Author of the book.",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_refBookTitle,
description: "Title of the book.",
format: TagFormat.plainText
},
{
key: ConfigKey.property_refPublisher,
description: "Publisher of the book.",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_refPublicationYear,
description: "Year of publication of the book.",
format: TagFormat.plainText
},
{
key: ConfigKey.property_citationStyle,
description: "Citation style for the book reference.",
format: TagFormat.plainText
}
]
}
]
}
]
}
};
// src/config/raw/groups.ts
var GROUPS = {
[ConfigKey.group_standardAllBits]: {
name: "Standard",
description: "Standard tags which apply to all bits",
type: GroupConfigType.standard,
tags: [
{
key: ConfigKey.property_id,
description: "The unique identifier(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_gUri,
description: "Global URI for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_customerId,
description: "The customer-specific identifier for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_externalId,
description: "The external identifier for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity,
// PLAN-140 refs family (NISO import): the machine identifier of a
// referenced standard. NESTED content slot — the tag reads (and
// consumes) ONLY the <std-id> inside a <std> child; the remaining
// designation/title text stays bit body. A <std> without <std-id>
// does not fire the key.
// PLAN-141 F5: on the [.book] bit, the DATED <std-ref> designation
// is the document's external id. Safe rule order: <std-ref> is a
// direct bit-element child only under <std-meta> (everywhere else
// it nests inside <std>, which the tag spine never reaches).
mappingKeys: {
"xml-niso-iec": [
{ "@el": "std", "@children": { "@el": "std-id", "@text": "$" } },
{ "@el": "std-ref", "@attr": { type: "dated" }, "@text": "$" }
]
}
},
{
key: ConfigKey.property_sourceRL,
description: "The original location of the information in the original source material",
format: TagFormat.plainText
},
{
key: ConfigKey.property_copyright,
description: "The copyright information for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_license,
description: "The license for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_rightsPolicy,
description: "The rights policy or policies for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_isTemplate,
description: "If true, the bit is a template",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_isTemplateStripTheme,
description: "If true, the bit is a template strip theme",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_needsValidation,
description: "If true, the bit needs validation",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_validationDate,
description: "The date when the bit was validated",
format: TagFormat.plainText
},
{
key: ConfigKey.property_aiGenerated,
description: "If true, the bit is AI-generated",
format: TagFormat.boolean,
nullable: true
},
{
key: ConfigKey.property_machineTranslated,
description: "If true, the bit is machine-translated",
format: TagFormat.plainText
},
{
key: ConfigKey.property_translationOf,
description: "Translation source reference",
format: TagFormat.plainText
},
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible",
format: TagFormat.boolean
},
{
key: ConfigKey.property_spansPageBreak,
description: "If true, the bit spans a page break",
format: TagFormat.boolean,
nullable: true
},
{
key: ConfigKey.property_searchIndex,
description: "The search index(es) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_analyticsTag,
description: "The analytics tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_categoryTag,
description: "The category tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_topicTag,
description: "The topic tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_reportTag,
description: "The report tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_altLangTag,
description: "The alternative language tag for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_feedbackEngine,
description: "The feedback engine for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_feedbackType,
description: "The feedback type for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_disableFeedback,
description: "If true, feedback is disabled for the bit",
format: TagFormat.boolean
},
{
key: ConfigKey.property_diffTo,
description: "The diff to version for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_diffOp,
description: "The diff operation for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_diffRef,
description: "The diff reference for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_diffContext,
description: "The diff context for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_diffTime,
description: "The diff time for the bit",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_ageRange,
description: "The age range for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_lang,
description: "The language for the bit",
format: TagFormat.plainText,
// PLAN-141 F3 (NISO import): a term-sec's language rides
// <tbx:termEntry><tbx:langSet xml:lang> — a nested content slot
// whose leaf is a $-in-ATTR-position slot (the nested counterpart
// of the value-attr slot). Only termEntry elements match, so the
// shared tag is inert everywhere else.
mappingKeys: {
"xml-niso-iec": {
"@el": "tbx:termEntry",
"@children": { "@el": "tbx:langSet", "@attr": { "xml:lang": "$" } }
}
}
},
{
key: ConfigKey.property_publisher,
description: "The publisher(s) of the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_publisherName,
description: "The name of the publisher of the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_theme,
description: "The theme(s) for the bit",
format: TagFormat.plainText,
nullable: true,
maxCount: Count.infinity
},
{
key: ConfigKey.property_rtl,
description: "If true, the book is right-to-left",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_target,
description: "The target(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_tag,
description: "The tag(s) for the bit",
format: TagFormat.plainText,
nullable: true,
maxCount: Count.infinity
},
{
key: ConfigKey.property_groupTag,
jsonKey: "groupTag.name",
// PLAN-063: starter-array shape with `@id` declares entity-merge
// semantics — repeated `[@groupTag: name]` occurrences with the
// same value fold into one entry; chained `@tag` children's
// arrays merge as sets.
exportJsonKey: { groupTag: [{ "@id": "name", name: "$" }] },
description: "The group tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity,
chain: [
{
key: ConfigKey.property_tag,
exportJsonKey: { tags: ["$"] },
description: "The tag(s) for the group",
format: TagFormat.plainText,
nullable: true,
maxCount: Count.infinity
}
]
},
{
key: ConfigKey.property_reviewTag,
description: "The review tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_reviewerTag,
description: "The reviewer tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_reductionTag,
description: "The reduction tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_bubbleTag,
description: "The bubble tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_extractorTag,
description: "The extractor tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_extractorInternal,
description: "Internal metadata used by the extractor for processing purposes",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_sourceBB,
description: "The source bounding box(es) for the bit (x, y, x1, y1)",
format: TagFormat.coordinates,
maxCount: Count.infinity
},
{
key: ConfigKey.property_bitFingerprint,
description: "Fingerprint(s) used by the AI extractor to identify the bit (font, style, color, pixels, etc.)",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_levelCEFRp,
description: "The CEFRp level for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_levelCEFR,
description: "The CEFR level for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_levelILR,
description: "The ILR level for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_levelACTFL,
description: "The ACTFL level for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_icon,
description: "The icon for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_iconTag,
description: "The icon tag for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_colorTag,
description: "The color tag(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
exportJsonKey: { anchor: "$" },
key: ConfigKey.tag_anchor,
name: "Anchor",
format: TagFormat.plainText,
description: "The anchor for the bit"
},
{
key: ConfigKey.property_search,
description: "The search text for the bit",
format: TagFormat.plainText
},
{
key: ConfigKey.property_showInIndex,
description: "If true, the bit is shown in the index",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_layer,
description: "The layer(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_layerRole,
description: "The layer role(s) for the bit",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
// Universal @internalComment — declares in config what Config.ts also
// injects programmatically into every bit (its push runs last and wins
// the keyed merge, so runtime behaviour is unchanged). Declared here so
// the config export (ConfigBuilder → assets/config → configurator →
// bitmark.json) carries the tag for downstream parsers. Empty
// exportJsonKey: consumed but never emitted to the bit JSON (the value
// is routed to parser.internalComments).
key: ConfigKey.property_internalComment,
description: "Internal comment for the bit.",
format: TagFormat.plainText,
maxCount: Count.infinity,
exportJsonKey: {}
}
]
},
[ConfigKey.group_standardItemLead]: {
type: GroupConfigType.standard,
description: "Standard group for item, lead, page number, and margin number tags",
tags: [
{
key: ConfigKey.tag_item,
jsonKey: "item",
exportJsonKey: { item: "$" },
// PLAN-140 W3 (NISO import): the clause/note label of a mapped
// source element ([%1], [%ANMERKUNG 1]).
mappingKeys: { "xml-niso-iec": { "@el": "label", "@text": "$" } },
description: "The item for the bit",
chain: [
{
key: ConfigKey.tag_item,
jsonKey: "lead",
exportJsonKey: { lead: "$" },
description: "The lead for the bit",
maxCount: 1,
chain: [
{
key: ConfigKey.tag_item,
jsonKey: "pageNumber",
exportJsonKey: { pageNumber: "$" },
description: "The page number for the bit",
maxCount: 1,
chain: [
{
key: ConfigKey.tag_item,
jsonKey: "marginNumber",
exportJsonKey: { marginNumber: "$" },
description: "The margin number for the bit",
maxCount: 1
}
]
}
]
}
]
}
]
},
[ConfigKey.group_standardItemLeadInstructionHint]: {
type: GroupConfigType.standard,
description: "Standard group for item, lead, page number, margin number, instruction and hint tags",
tags: [
{
key: ConfigKey.group_standardItemLead,
description: "Item, lead, page number, and margin number tags"
},
{
exportJsonKey: { instruction: "$" },
key: ConfigKey.tag_instruction,
name: "Instruction",
description: "The instruction for the bit"
},
{
exportJsonKey: { hint: "$" },
key: ConfigKey.tag_hint,
name: "Hint",
description: "The hint for the bit"
}
]
},
[ConfigKey.group_standardInstructionHint]: {
type: GroupConfigType.standard,
description: "Standard group for instruction and hint tags only (no item/lead/pageNumber/marginNumber chain). Used by sides where item/lead are owned by another side (e.g. matchPairs value-side, which BPG strips item/lead from at sideIdx > 0).",
tags: [
{
exportJsonKey: { instruction: "$" },
key: ConfigKey.tag_instruction,
name: "Instruction",
description: "The instruction for the bit"
},
{
exportJsonKey: { hint: "$" },
key: ConfigKey.tag_hint,
name: "Hint",
description: "The hint for the bit"
}
]
},
[ConfigKey.group_standardTags]: {
type: GroupConfigType.standard,
description: "Standard tags which apply to MOST (but not all) bits",
tags: [
{
key: ConfigKey.group_standardAllBits,
description: "All standard tags which apply to all bits"
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint tags"
},
{
// Bit-level example tag (default — non-`isTopLevelExample` bits):
// - bare `[@example]`: consume only (records cascade marker, emits
// nothing at bit-level — OLD parser scrubs bit-level isExample
// for bits NOT in its `isTopLevelExample` allow-list).
// - synthesized (descendant cascade): still emits `isExample: true`
// at the descendant scope.
// - valued `[@example:text]`: emit the example value only; bit-level
// `isExample` is left to bubble from descendants.
// Bits in the OLD parser's allow-list (flashcard, definitionList,
// sequence, multipleChoice, trueFalse, cloze*, mark, interview,
// matchMatrix, match, essay, etc.) override this entry with the
// legacy emit-isExample shape.
key: ConfigKey.property_example,
exportJsonKey: [{ "@keyonly": {} }, { "@absent": { isExample: true } }, { example: "$" }],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
[ConfigKey.group_standardTagsNoItemLead]: {
type: GroupConfigType.standard,
description: "Standard tags mirror of group_standardTags but excluding the item/lead/pageNumber/marginNumber chain. Used by sides where item/lead are owned by another side (e.g. matchPairs value-side \u2014 BPG `parseMatchPairs` strips tags.item / tags.lead for sideIdx > 0).",
tags: [
{
key: ConfigKey.group_standardAllBits,
description: "All standard tags which apply to all bits"
},
{
key: ConfigKey.group_standardInstructionHint,
description: "Instruction and hint tags only (no item/lead chain)."
},
{
// Mirror of group_standardTags property_example entry.
key: ConfigKey.property_example,
exportJsonKey: [{ "@keyonly": {} }, { "@absent": { isExample: true } }, { example: "$" }],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
[ConfigKey.group_imageSource]: {
type: GroupConfigType.standard,
description: "Image source chain",
tags: [
{
key: ConfigKey.property_imageSource,
jsonKey: "imageSource.url",
exportJsonKey: { imageSource: { url: "$" } },
description: "The source of an image",
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.property_mockupId,
description: "The mockup ID for the image",
format: TagFormat.plainText
},
{
key: ConfigKey.property_size,
description: "The size of the image",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_format,
description: "The format of the image",
format: TagFormat.plainText
},
{
key: ConfigKey.property_trim,
description: "If true, the image is trimmed",
format: TagFormat.boolean
}
]
}
]
},
[ConfigKey.group_technicalTerm]: {
type: GroupConfigType.standard,
description: "Technical term chain",
tags: [
{
key: ConfigKey.property_technicalTerm,
jsonKey: "technicalTerm.technicalTerm",
exportJsonKey: { technicalTerm: { technicalTerm: "$" } },
description: "The technical term",
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.property_lang,
description: "The language of the technical term",
format: TagFormat.plainText
}
]
}
]
},
[ConfigKey.group_person]: {
type: GroupConfigType.standard,
description: "Person chain",
tags: [
{
key: ConfigKey.property_person,
jsonKey: "person.name",
exportJsonKey: { person: { name: "$" } },
description: "A person",
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.property_title,
description: "The person's title",
format: TagFormat.plainText
},
{
key: ConfigKey.group_resourceImage,
jsonKey: "avatarImage|resource(type=image, key=image)",
exportJsonKey: { avatarImage: { type: "image", image: { src: "$" } } },
description: "The image of the person"
}
]
},
{
// Deprecated (parter renamed to person)
key: ConfigKey.property_partner,
jsonKey: "person.name",
exportJsonKey: { person: { name: "$" } },
description: "A partner",
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.property_title,
description: "The partner's title",
format: TagFormat.plainText
},
{
key: ConfigKey.group_resourceImage,
jsonKey: "avatarImage|resource(type=image, key=image)",
exportJsonKey: { avatarImage: { type: "image", image: { src: "$" } } },
description: "The image of the partner"
}
]
}
]
},
[ConfigKey.group_gap]: {
type: GroupConfigType.standard,
description: "Gap chain",
tags: [
{
exportJsonKey: {
"@body-inline": { type: "gap", attrs: { solutions: ["$"] } }
},
key: ConfigKey.tag_gap,
description: "The value of a gap in the content",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.tag_gap,
jsonKey: "solutions[]",
exportJsonKey: { solutions: ["$"] },
description: "Alternative values for the gaps in the content",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint tags"
},
{
// BPG ExampleTagContentProcessor.ts:91-95 reads
// `__solutionsAst[length - 1]` — the solution closest to the
// `[@example]` tag at parse time. Our chain accumulator is built
// incrementally (scope.rs `parent.merge` after each tag), so at
// the moment `[@example]` fires, `solutions` only contains the
// preceding `[_x]` entries — `[-1]` resolves to the closest one.
// E.g. `[_cloze][_gap][@example]` → solutions=["cloze","gap"],
// `[-1]`="gap"; `[_solution][@example][_problem]` →
// solutions=["solution"], `[-1]`="solution".
key: ConfigKey.property_example,
jsonKey: "example",
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: "$parent.solutions[-1]",
"@bit": { isExample: true }
}
},
{
"@absent": {
isExample: true,
example: "$parent.solutions[-1]",
"@bit": { isExample: true }
}
},
{ isExample: true, example: "$", "@bit": { isExample: true } }
],
description: "An example for the gap",
format: TagFormat.bitmarkText,
nullable: true
},
{
key: ConfigKey.property_isCaseSensitive,
jsonKey: "isCaseSensitive",
exportJsonKey: [
{ "@absent": { isCaseSensitive: "$ancestor" } },
{ isCaseSensitive: "$" }
],
description: "If true, the gap text is case sensitive",
format: TagFormat.boolean,
defaultValue: "true"
}
]
}
]
},
[ConfigKey.group_trueFalse]: {
type: GroupConfigType.standard,
description: "True/False chain",
tags: [
{
exportJsonKey: { choices: [{ choice: "$", isCorrect: true }] },
key: ConfigKey.tag_true,
description: "True value for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.tag_true,
jsonKey: "choices[]|set(isCorrect=true)",
exportJsonKey: { choices: [{ choice: "$", isCorrect: true }] },
description: "True values for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.tag_false,
jsonKey: "choices[]|set(isCorrect=false)",
exportJsonKey: { choices: [{ choice: "$", isCorrect: false }] },
description: "False values for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint tags"
},
{
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.2, corrected by
// bpg 5.31 probing: cascade-fired `@example` decorates only
// the FIRST correct choice (`example: true` via the
// `@parent.isCorrect`-gated `@absent` rule, `maxEmits:1` —
// BPG `fillBooleanExample(..., firstCorrectOnly=true)`).
// There is deliberately NO plain `@absent` rule: an ancestor
// bare example must leave the remaining choices untouched
// (a plain `@absent` decoration broke the
// `optimize(fullize(x)) == optimize(x)` law — the
// synthesized `isExample` keys survived re-optimization).
// The `serialize_cards` cascade-counter scope holds the cap
// across the whole cardset.
// @card scope-shift writes choice-container isExample (e.g.
// `quizzes[i].isExample`) — was previously provided by the
// universal `bubble_is_example` post-walk, which was removed
// in favour of explicit per-tag config. SYNTAX.md §8.
key: ConfigKey.property_example,
jsonKey: "example",
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{
predicates: ["@absent", { "@parent.isCorrect": true }],
maxEmits: 1,
rule: {
isExample: true,
example: true,
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{
isExample: true,
example: "$",
"@bit": { isExample: true },
"@card": { isExample: true }
}
],
description: "An example for the true/false statement/question",
format: TagFormat.boolean,
nullable: true
}
]
},
{
exportJsonKey: { choices: [{ choice: "$", isCorrect: false }] },
key: ConfigKey.tag_false,
description: "False value for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.tag_true,
jsonKey: "choices[]|set(isCorrect=true)",
exportJsonKey: { choices: [{ choice: "$", isCorrect: true }] },
description: "True values for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.tag_false,
jsonKey: "choices[]|set(isCorrect=false)",
exportJsonKey: { choices: [{ choice: "$", isCorrect: false }] },
description: "False values for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint tags"
},
{
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.2, corrected by
// bpg 5.31 probing: an ancestor bare `@example` leaves
// incorrect choices UNTOUCHED — no cascade decoration at
// all (no `@absent` rule; a plain `@absent → isExample`
// decoration broke the `optimize(fullize(x)) ==
// optimize(x)` law). Only an explicit `[@example]` on the
// choice itself emits.
// @card scope-shift writes choice-container isExample.
key: ConfigKey.property_example,
jsonKey: "example",
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{
isExample: true,
example: "$",
"@bit": { isExample: true },
"@card": { isExample: true }
}
],
description: "An example for the true/false statement/question (incorrect entry \u2014 no cascade decoration: an ancestor bare example leaves incorrect choices untouched, bpg 5.31)",
format: TagFormat.boolean,
nullable: true
}
]
}
]
},
[ConfigKey.group_trueFalseResponses]: {
type: GroupConfigType.standard,
description: "True/False Responses chain",
tags: [
{
exportJsonKey: { responses: [{ response: "$", isCorrect: true }] },
key: ConfigKey.tag_true,
description: "True value for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.tag_true,
jsonKey: "responses[]|set(isCorrect=true)",
exportJsonKey: { responses: [{ response: "$", isCorrect: true }] },
description: "True values for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.tag_false,
jsonKey: "responses[]|set(isCorrect=false)",
exportJsonKey: { responses: [{ response: "$", isCorrect: false }] },
description: "False values for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint tags"
},
{
// @card scope-shift writes response-container isExample.
key: ConfigKey.property_example,
jsonKey: "example",
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{
"@absent": {
isExample: "$parent.isCorrect",
example: "$parent.isCorrect",
"@bit": { isExample: "$parent.isCorrect" },
"@card": { isExample: "$parent.isCorrect" }
}
},
{
isExample: true,
example: "$",
"@bit": { isExample: true },
"@card": { isExample: true }
}
],
description: "An example for the true/false statement/question",
format: TagFormat.boolean,
nullable: true
}
]
},
{
exportJsonKey: { responses: [{ response: "$", isCorrect: false }] },
key: ConfigKey.tag_false,
description: "False value for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.tag_true,
jsonKey: "responses[]|set(isCorrect=true)",
exportJsonKey: { responses: [{ response: "$", isCorrect: true }] },
description: "True values for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.tag_false,
jsonKey: "responses[]|set(isCorrect=false)",
exportJsonKey: { responses: [{ response: "$", isCorrect: false }] },
description: "False values for a true/false statement/question",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint tags"
},
{
// @card scope-shift writes response-container isExample.
key: ConfigKey.property_example,
jsonKey: "example",
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{
"@absent": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true },
"@card": { isExample: true }
}
},
{
isExample: true,
example: "$",
"@bit": { isExample: true },
"@card": { isExample: true }
}
],
description: "An example for the true/false statement/question",
format: TagFormat.boolean,
nullable: true
}
]
}
]
},
[ConfigKey.group_trueFalseInlineSelect]: {
type: GroupConfigType.standard,
description: "True/False chain composed inline (select body-bit). +/- tags are consume-only at bit level.",
tags: [
{
exportJsonKey: {
"@body-inline": {
type: "select",
attrs: { options: [{ text: "$", isCorrect: true }] }
}
},
key: ConfigKey.tag_true,
description: "True value for an inline select body bit",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.tag_true,
exportJsonKey: {},
description: "True values for an inline select body bit",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.tag_false,
exportJsonKey: {},
description: "False values for an inline select body bit",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint tags"
},
{
// PLAN-083: cascade-fired `@example` from bit header fires
// `isExample:true, example:true` on the FIRST CORRECT option
// only (BPG `fillBooleanExample(select.options,
// firstCorrectOnly=true)`). Mirrors group_trueFalseInlineHighlight.
// Every rule also bubbles `@bit:{isExample:true}` (idempotent
// OR — any cascade ⇒ bit marked) and
// `@body-inline:{attrs:{isExample:true}}` (node-level marker
// on the select node).
key: ConfigKey.property_example,
jsonKey: "example",
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
},
{
predicates: ["@absent", { "@parent.isCorrect": true }],
maxEmits: 1,
rule: {
isExample: true,
example: true,
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
},
{
isExample: true,
example: "$",
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
],
description: "An example for the inline select option (first correct only)",
format: TagFormat.boolean,
nullable: true
}
]
},
{
exportJsonKey: {
"@body-inline": {
type: "select",
attrs: { options: [{ text: "$", isCorrect: false }] }
}
},
key: ConfigKey.tag_false,
description: "False value for an inline select body bit",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.tag_true,
exportJsonKey: {},
description: "True values for an inline select body bit",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.tag_false,
exportJsonKey: {},
description: "False values for an inline select body bit",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint tags"
},
{
// PLAN-083: identical to the `+` lead's chain @example —
// cascade reaches the first correct option regardless of
// which lead (+/-) opens the chain. Every firing rule bubbles
// `@bit:{isExample:true}` and `@body-inline:{attrs:{isExample:true}}`.
key: ConfigKey.property_example,
jsonKey: "example",
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
},
{
predicates: ["@absent", { "@parent.isCorrect": true }],
maxEmits: 1,
rule: {
isExample: true,
example: true,
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
},
{
isExample: true,
example: "$",
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
],
description: "An example for the inline select option (first correct only)",
format: TagFormat.boolean,
nullable: true
}
]
}
]
},
[ConfigKey.group_trueFalseInlineHighlight]: {
type: GroupConfigType.standard,
description: 'True/False chain composed inline (highlight body-bit). +/- tags emit `{type:"highlight", attrs:{texts:[{text,isCorrect?}]}}` inline body nodes. Used by highlight-text bit.',
tags: [
{
exportJsonKey: {
"@body-inline": {
type: "highlight",
attrs: { texts: [{ text: "$", isCorrect: true }] }
}
},
key: ConfigKey.tag_true,
description: "Highlighted span in the body (correct)",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.tag_true,
exportJsonKey: {},
description: "Highlighted span values (correct)",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.tag_false,
exportJsonKey: {},
description: "Highlighted span values (incorrect)",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint tags"
},
{
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.13: cascade-fired
// `@example` from the bit header fires `example: true` +
// `isExample: true` on the FIRST correct highlight only
// (BPG `fillBooleanExample(highlight.texts,
// firstCorrectOnly=true)`). Incorrect highlights get
// nothing (unlike choices, the highlight-text fixture
// shows no `isExample` on incorrect entries).
key: ConfigKey.property_example,
jsonKey: "example",
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
},
{
predicates: ["@absent", { "@parent.isCorrect": true }],
maxEmits: 1,
rule: {
isExample: true,
example: true,
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
},
{
isExample: true,
example: "$",
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
],
description: "An example for the highlighted span (first correct only)",
format: TagFormat.boolean,
nullable: true
}
]
},
{
// PLAN-083 follow-up (2026-05-22): mirror the select `-` lead shape
// by writing `isCorrect: false`. Without it, `$parent.isCorrect` in
// chain-child @example rules resolves to ParentPathMissing for `-`
// entries (e.g. `[-fish][@example]`), so the @keyonly rule falls
// through and nothing fires. The natural-default strip removes
// `isCorrect: false` from the final texts entries (matches fixture).
exportJsonKey: {
"@body-inline": {
type: "highlight",
attrs: { texts: [{ text: "$", isCorrect: false }] }
}
},
key: ConfigKey.tag_false,
description: "Highlighted span in the body (incorrect)",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.tag_true,
exportJsonKey: {},
description: "Highlighted span values (correct)",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.tag_false,
exportJsonKey: {},
description: "Highlighted span values (incorrect)",
maxCount: Count.infinity,
format: TagFormat.plainText
},
{
key: ConfigKey.group_standardItemLeadInstructionHint,
description: "Item, lead, page number, margin number, instruction and hint tags"
},
{
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.13: cascade-fired
// `@example` on a `-` (incorrect highlight) emits nothing
// (highlight-text fixture has no `isExample` on incorrect
// entries; firstCorrectOnly cascade skips them entirely).
// No `@absent` rule.
// PLAN-083: identical to the `+` lead's chain @example —
// cascade reaches the first correct option regardless of
// which lead (+/-) opens the chain.
key: ConfigKey.property_example,
jsonKey: "example",
exportJsonKey: [
{
"@keyonly": {
isExample: true,
example: "$parent.isCorrect",
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
},
{
predicates: ["@absent", { "@parent.isCorrect": true }],
maxEmits: 1,
rule: {
isExample: true,
example: true,
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
},
{
isExample: true,
example: "$",
"@bit": { isExample: true },
"@body-inline": { attrs: { isExample: true } }
}
],
description: "An example for the highlighted span (first correct only)",
format: TagFormat.boolean,
nullable: true
}
]
}
]
},
[ConfigKey.group_markConfig]: {
type: GroupConfigType.standard,
description: "Mark configuration chain",
tags: [
{
key: ConfigKey.property_mark,
jsonKey: "marks.mark",
// `emphasis: 'underline'` is the per-entry default for mark
// configs — mirrors the hardcoded default in BPG
// `MarkConfigChainContentProcessor.ts:55-58`. Chained
// `[@emphasis:…]` overrides via deep-merge into `marks[-1]`.
exportJsonKey: { marks: [{ mark: "$", emphasis: "underline" }] },
description: "The mark configuration",
format: TagFormat.plainText,
maxCount: Count.infinity,
chain: [
{
key: ConfigKey.property_color,
description: "The color of the mark",
format: TagFormat.plainText
},
{
key: ConfigKey.property_emphasis,
description: "The emphasis of the mark",
format: TagFormat.plainText
}
]
}
]
},
[ConfigKey.group_mark]: {
type: GroupConfigType.standard,
description: "Mark chain",
tags: [
{
exportJsonKey: {
"@body-inline": { type: "mark", attrs: { solution: "$" } }
},
key: ConfigKey.tag_mark,
description: "The marked content",
maxCount: Count.infinity,
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.property_mark,
jsonKey: "mark",
exportJsonKey: { mark: "$" },
description: "The mark configuration",
format: TagFormat.plainText
},
{
key: ConfigKey.property_example,
jsonKey: "example",
// Per-mark `@example` is a boolean toggle. BPG's runtime
// `ExampleTagContentProcessor.handleDefaultOnlyExample`
// (lines 147-161) normalises ANY `[@example]` or
// `[@example:V]` on a mark body-bit to
// `__isDefaultExample=true, example=undefined`; the
// downstream `toExample(_, _, defaultExample=true)` in
// `BaseBuilder.ts:80-87` then emits `example: true`
// regardless of source. Declare a single unconditional
// boolean-shape rule so the exported schema matches that
// runtime semantic instead of the misleading `example: $`
// shape (which would only fire if `handleDefaultOnlyExample`
// were removed). `@bit: {isExample: true}` cascades the
// marker — but NOT the `example` value — to bit root.
exportJsonKey: { isExample: true, example: true, "@bit": { isExample: true } },
description: "An example for the marked content",
format: TagFormat.boolean,
nullable: true
}
]
}
]
},
[ConfigKey.group_bookCommon]: {
type: GroupConfigType.standard,
description: "Common book tags",
tags: [
{
key: ConfigKey.property_language,
description: "The language of the book",
format: TagFormat.plainText,
maxCount: Count.infinity,
// PLAN-141 F5 (NISO import): only the PRIMARY (first)
// <content-language> survives — the maxEmits cap realises the
// catalog's document-language rule (extras drop, R-4).
mappingKeys: {
"xml-niso-iec": [
{ predicates: [], maxEmits: 1, rule: { "@el": "content-language", "@text": "$" } }
]
}
},
{
key: ConfigKey.property_displayName,
description: "The display name of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_customerExternalId,
description: "The customer-specific external identifier for the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_spaceId,
description: "The space ID for the book",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_kind,
description: "The kind of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_hasMarkAsDone,
description: 'If true, the book has a "Mark as done" feature',
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_processHandIn,
description: "If true, the book will be processed when upon hand-in",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_processHandInLocation,
description: "The location where the book was handed-in",
format: TagFormat.plainText
},
{
key: ConfigKey.property_isPublic,
description: "If true, the book is public",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_isTemplate,
description: "If true, the book is a template",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_isTemplateStripTheme,
description: "If true, the book is a template strip theme",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_isEditable,
description: "If true, the book is editable",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_chatWithBook,
description: "If true, the user can chat about the book with an AI assistant",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_chatWithBookBrainKey,
description: "The BookBrain key for the book chat",
format: TagFormat.plainText
},
{
key: ConfigKey.tag_title,
name: "Title",
description: "The title of the book",
maxCount: 2,
jsonKey: "title|multi(count=2, key=subtitle)",
exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }],
// PLAN-141 F5 (NISO import): the PRIMARY (first) <title-wrap>'s
// assembled <full> title → [#]; the per-language extras drop (R-4).
mappingKeys: {
"xml-niso-iec": [
{
predicates: [],
maxEmits: 1,
rule: { "@el": "title-wrap", "@children": { "@el": "full", "@text": "$" } }
}
]
}
},
{
key: ConfigKey.property_subtype,
description: "The subtype of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_coverImage,
description: "The cover image(s) of the book",
format: TagFormat.plainText,
nullable: true,
maxCount: Count.infinity
},
{
key: ConfigKey.resource_coverImage,
jsonKey: "coverImage|resource(type=image, key=image)",
exportJsonKey: { coverImage: { type: "image", image: { src: "$" } } },
description: "The cover image of the book",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the cover image resource"
}
]
},
{
key: ConfigKey.property_backgroundImage,
description: "The background image(s) of the book",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.resource_backgroundImage,
jsonKey: "backgroundImage|resource(type=image, key=image)",
exportJsonKey: { backgroundImage: { type: "image", image: { src: "$" } } },
description: "The background image of the book",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the background image resource"
}
]
},
{
key: ConfigKey.property_coverColor,
description: "The cover color of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_subject,
description: "The subject(s) of the book",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_author,
description: "The author(s) of the book",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_publications,
description: "The publication(s) of the book",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_relatedBook,
description: "Books related to this book",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_translationOfBook,
description: "External Id of the translated book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_userFeedbackTranslationUrl,
description: "URL to an external feedback form for book translation feedback",
format: TagFormat.plainText
},
{
key: ConfigKey.property_userFeedbackContentUrl,
description: "URL to an external feedback form for book content feedback",
format: TagFormat.plainText
},
{
key: ConfigKey.property_duration,
description: "The duration of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_maxTocChapterLevel,
description: "The maximum table of contents chapter level",
format: TagFormat.number,
defaultValue: "-1"
},
{
key: ConfigKey.property_sourceDocument,
description: "Url of the source document for the book (for example, a PDF file)",
format: TagFormat.plainText
},
{
key: ConfigKey.property_internalPrintPdf,
description: "Url of the internal print PDF for the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_allowPrint,
description: "If true, the book allows printing",
format: TagFormat.enumeration,
values: ["enforceFalse", "enforceTrue", "useSpaceConfiguration"],
defaultValue: "useSpaceConfiguration"
},
{
key: ConfigKey.property_hasPrintRestriction,
description: "If true, the book has print restrictions",
format: TagFormat.boolean,
defaultValue: "true"
},
{
key: ConfigKey.property_enforceUpdateOverUserInput,
description: "If true, prioritize new content over legacy content from the instance API",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_extractorExtractionTimestamp,
description: "Extraction timestamps for book conversion",
format: TagFormat.plainText,
maxCount: Count.infinity
}
]
},
[ConfigKey.group_learningPathCommon]: {
type: GroupConfigType.standard,
description: "Common learning path tags",
tags: [
{
key: ConfigKey.property_action,
description: "The action for the learning path",
format: TagFormat.plainText
},
{
key: ConfigKey.property_duration,
description: "The duration of the learning path",
format: TagFormat.plainText
},
{
key: ConfigKey.property_date,
description: "The date of the learning path",
format: TagFormat.plainText
},
{
key: ConfigKey.property_dateEnd,
description: "The end date of the learning path",
format: TagFormat.plainText
},
{
key: ConfigKey.property_location,
description: "The location of the learning path",
format: TagFormat.plainText
},
{
key: ConfigKey.property_list,
description: "The list of items in the learning path",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_textReference,
description: "The text reference for the learning path",
format: TagFormat.plainText
},
{
key: ConfigKey.property_isTracked,
description: "If true, the learning path is tracked",
format: TagFormat.boolean,
defaultValue: "true"
},
{
key: ConfigKey.property_isInfoOnly,
description: "If true, the learning path is info only",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_deeplink,
description: "The deeplink(s) for the learning path",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_buttonCaption,
description: "The caption for the button of the learning path",
format: TagFormat.plainText
},
{
key: ConfigKey.property_book,
description: "The book(s) associated with the learning path",
format: TagFormat.plainText,
chain: [
{
exportJsonKey: { reference: "$" },
key: ConfigKey.tag_reference,
format: TagFormat.plainText,
description: "The reference for the book(s) in the learning path",
maxCount: 1,
chain: [
{
key: ConfigKey.tag_reference,
jsonKey: "referenceEnd",
exportJsonKey: { referenceEnd: "$" },
format: TagFormat.plainText,
description: "The referenceEnd for the book(s) in the learning path",
maxCount: 1
}
]
}
]
}
]
},
[ConfigKey.group_advertisingCommon]: {
type: GroupConfigType.standard,
description: "Common advertising tags",
tags: [
{
key: ConfigKey.property_advertisingClickUrl,
description: "The URL to which the advertisement should link",
format: TagFormat.plainText
}
]
},
[ConfigKey.group_platformStylesCommon]: {
type: GroupConfigType.standard,
description: "Common platform styles",
tags: [
{
key: ConfigKey.property_platformPrimaryColor,
description: "The platform system primary color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformSecondaryColor,
description: "The platform section secondary color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformBackgroundColor,
description: "The platform system background color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformButtonPrimaryColor,
description: "The platform section header button primary color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformButtonBackgroundColor,
description: "The platform section header button background color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformScrollbarColor,
description: "The platform main scrollbar color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformSelectionColor,
description: "The platform main selection color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformTextSelectionColor,
description: "The platform main input text selection color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformSeparatorColor,
description: "The platform main separator color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformPlaceholderColor,
description: "The platform main input placeholder color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformMessageBackgroundColor,
description: "The platform section chat message background color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformBorderColor,
description: "The platform border color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformSelectionTextColor,
description: "The platform selection text color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformBaseLayerColor,
description: "The platform base layer color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_platformMargin,
description: "The platform margin",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_platformBorderRadius,
description: "The platform border radius",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_platformSelectionBorderRadius,
description: "The platform selection border radius",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_platformNeedsShadow,
description: "If true, the platform needs shadow",
format: TagFormat.boolean,
nullable: true
}
]
},
[ConfigKey.group_quizCommon]: {
type: GroupConfigType.standard,
description: "Common quiz tags",
tags: [
{
key: ConfigKey.property_revealSolutions,
description: "If true, the quiz solutions are revealed",
format: TagFormat.boolean,
nullable: true
// defaultValue: 'false',
},
{
key: ConfigKey.property_additionalSolutions,
description: "Additional solutions",
format: TagFormat.plainText,
maxCount: Count.infinity
}
]
},
[ConfigKey.group_backgroundWallpaper]: {
type: GroupConfigType.standard,
description: "Background wallpaper tags",
tags: [
{
key: ConfigKey.resource_backgroundWallpaper,
exportJsonKey: { backgroundWallpaper: { type: "image", image: { src: "$" } } },
description: "Background wallpaper for the image, used to set a background for the image",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common resource image tags for images"
}
]
}
]
},
[ConfigKey.group_imageNoZoom]: {
type: GroupConfigType.standard,
description: "Image bit tags",
tags: [
{
key: ConfigKey.group_backgroundWallpaper,
description: "Background wallpaper tags for images, used to define background properties for images"
},
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for images, used to define additional properties for images"
},
{
key: ConfigKey.group_resourceImageNoZoom,
description: "Resource image tags for images, used to attach images to the bit",
minCount: 1
}
]
},
//
// Resource groups
//
[ConfigKey.group_resourceBitTags]: {
type: GroupConfigType.standard,
description: "Resource bit tags",
tags: [
{
key: ConfigKey.resource_imagePlaceholder,
jsonKey: "imagePlaceholder|resource(type=image, key=image)",
exportJsonKey: { imagePlaceholder: { type: "image", image: { src: "$" } } },
description: "Placeholder image for the resource",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the placeholder image"
}
],
maxCount: 1
}
]
},
//
// Common resource properties
//
[ConfigKey.group_resourceCommon]: {
type: GroupConfigType.standard,
description: "Common resource properties",
tags: [
{
key: ConfigKey.property_license,
description: "The license for the resource",
format: TagFormat.plainText
},
{
key: ConfigKey.property_copyright,
description: "The copyright information for the resource",
format: TagFormat.plainText
},
{
key: ConfigKey.property_caption,
description: "The caption for the resource",
format: TagFormat.bitmarkText,
// PLAN-141 F1 (NISO import): a figure's <caption><title> — the
// nested content slot reads the title text; the caption rides the
// resource chain ([&image:…][@caption:…], catalog D5).
mappingKeys: {
"xml-niso-iec": { "@el": "caption", "@children": { "@el": "title", "@text": "$" } }
}
},
{
key: ConfigKey.property_showInIndex,
description: "If true, the resource is shown in the index",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_search,
description: "The search text for the resource",
format: TagFormat.plainText
},
{
key: ConfigKey.property_selected,
description: "If true, the resource is selected",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_srcAlt,
description: "An alternative source for the resource",
format: TagFormat.plainText,
maxCount: Count.infinity
}
]
},
[ConfigKey.group_resourceImageCommon]: {
type: GroupConfigType.standard,
description: "Common properties for image resources",
tags: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties"
},
{
key: ConfigKey.property_src1x,
description: "The source URL for the image at 1x resolution",
format: TagFormat.plainText
},
{
key: ConfigKey.property_src2x,
description: "The source URL for the image at 2x resolution",
format: TagFormat.plainText
},
{
key: ConfigKey.property_src3x,
description: "The source URL for the image at 3x resolution",
format: TagFormat.plainText
},
{
key: ConfigKey.property_src4x,
description: "The source URL for the image at 4x resolution",
format: TagFormat.plainText
},
{
key: ConfigKey.property_width,
description: "The width of the image",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_height,
description: "The height of the image",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_alt,
description: "The alternative text for the image",
format: TagFormat.plainText
},
{
key: ConfigKey.property_zoomDisabled,
description: "If true, zooming is disabled for the image",
format: TagFormat.boolean
}
]
},
[ConfigKey.group_resourceImageCommonNoZoom]: {
type: GroupConfigType.standard,
description: "Common properties for image resources where @zoomDisabled defaults to true",
tags: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties"
},
{
key: ConfigKey.property_src1x,
description: "The source URL for the image at 1x resolution",
format: TagFormat.plainText
},
{
key: ConfigKey.property_src2x,
description: "The source URL for the image at 2x resolution",
format: TagFormat.plainText
},
{
key: ConfigKey.property_src3x,
description: "The source URL for the image at 3x resolution",
format: TagFormat.plainText
},
{
key: ConfigKey.property_src4x,
description: "The source URL for the image at 4x resolution",
format: TagFormat.plainText
},
{
key: ConfigKey.property_width,
description: "The width of the image",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_height,
description: "The height of the image",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_alt,
description: "The alternative text for the image",
format: TagFormat.plainText
},
// @zoomDisabled with a true default
{
key: ConfigKey.property_zoomDisabled,
description: "If true, zooming is disabled for the image (defaults to true)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[ConfigKey.group_resourceAudioCommon]: {
type: GroupConfigType.standard,
description: "Common properties for audio resources",
tags: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties"
},
{
key: ConfigKey.property_duration,
description: "The duration of the audio resource",
format: TagFormat.plainText
},
{
key: ConfigKey.property_mute,
description: "If true, the audio is muted",
format: TagFormat.boolean
},
{
key: ConfigKey.property_autoplay,
description: "If true, the audio plays automatically",
format: TagFormat.boolean
}
]
},
[ConfigKey.group_resourceVideoCommon]: {
type: GroupConfigType.standard,
description: "Common properties for video resources",
tags: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties"
},
{
key: ConfigKey.property_width,
description: "The width of the video",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_height,
description: "The height of the video",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_duration,
description: "The duration of the video",
format: TagFormat.plainText
},
{
key: ConfigKey.property_mute,
description: "If true, the video is muted",
format: TagFormat.boolean
},
{
key: ConfigKey.property_autoplay,
description: "If true, the video plays automatically",
format: TagFormat.boolean
},
{
key: ConfigKey.property_allowSubtitles,
description: "If true, subtitles are allowed for the video",
format: TagFormat.boolean
},
{
key: ConfigKey.property_showSubtitles,
description: "If true, subtitles are shown in the video",
format: TagFormat.boolean
},
{
key: ConfigKey.property_alt,
description: "The alternative text for the video",
format: TagFormat.plainText
},
{
key: ConfigKey.property_posterImage,
jsonKey: "posterImage.src",
exportJsonKey: { posterImage: { src: "$" } },
description: "The poster image for the video",
format: TagFormat.plainText,
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the poster image"
}
]
},
{
key: ConfigKey.property_src1x,
description: "The source URL for the video at 1x resolution",
format: TagFormat.plainText,
exportJsonKey: { thumbnails: [{ src: "$" }] }
},
{
key: ConfigKey.property_src2x,
description: "The source URL for the video at 2x resolution",
format: TagFormat.plainText,
exportJsonKey: { thumbnails: [{ src: "$" }] }
},
{
key: ConfigKey.property_src3x,
description: "The source URL for the video at 3x resolution",
format: TagFormat.plainText,
exportJsonKey: { thumbnails: [{ src: "$" }] }
},
{
key: ConfigKey.property_src4x,
description: "The source URL for the video at 4x resolution",
format: TagFormat.plainText,
exportJsonKey: { thumbnails: [{ src: "$" }] }
}
]
},
//
// Single resources
//
[ConfigKey.group_resourceIcon]: {
type: GroupConfigType.standard,
description: "Common properties for icon resources",
tags: [
{
key: ConfigKey.resource_icon,
exportJsonKey: { resource: { type: "icon", icon: { src: "$" } } },
description: "The icon resource",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the icon resource"
}
]
}
]
},
[ConfigKey.group_resourceImage]: {
type: GroupConfigType.resource,
description: "Common properties for image resources",
tags: [
{
key: ConfigKey.resource_image,
exportJsonKey: { resource: { type: "image", image: { src: "$" } } },
// PLAN-141 F1 (NISO import): the image URL rides the `$`-in-attr
// value slot — a <graphic xlink:href> child of the mapped bit
// element roots the [&image:URL] chain.
mappingKeys: { "xml-niso-iec": { "@el": "graphic", "@attr": { "xlink:href": "$" } } },
description: "The image resource",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the image resource"
}
]
}
]
},
[ConfigKey.group_resourceImageNoZoom]: {
type: GroupConfigType.resource,
description: "Image resource where @zoomDisabled defaults to true",
tags: [
{
key: ConfigKey.resource_image,
exportJsonKey: { resource: { type: "image", image: { src: "$" } } },
description: "The image resource (no-zoom default)",
chain: [
{
key: ConfigKey.group_resourceImageCommonNoZoom,
description: "Common image properties with @zoomDisabled defaulting to true"
}
]
}
]
},
[ConfigKey.group_resourceImagePortrait]: {
type: GroupConfigType.resource,
description: "Portrait image resource",
tags: [
{
key: ConfigKey.resource_imagePortrait,
exportJsonKey: { resource: { type: "image-portrait", imagePortrait: { src: "$" } } },
description: "The portrait image resource",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the portrait image resource"
}
]
}
]
},
[ConfigKey.group_resourceImageLandscape]: {
type: GroupConfigType.resource,
description: "Landscape image resource",
tags: [
{
key: ConfigKey.resource_imageLandscape,
exportJsonKey: { resource: { type: "image-landscape", imageLandscape: { src: "$" } } },
description: "The landscape image resource",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the landscape image resource"
}
]
}
]
},
[ConfigKey.group_resourceImageEmbed]: {
type: GroupConfigType.resource,
description: "Embedded image resource",
tags: [
{
key: ConfigKey.resource_imageEmbed,
exportJsonKey: { resource: { type: "image-embed", imageEmbed: { src: "$" } } },
description: "The embedded image resource",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the embedded image resource"
}
]
}
]
},
[ConfigKey.group_resourceImageLink]: {
type: GroupConfigType.resource,
description: "Link to an image resource",
tags: [
{
key: ConfigKey.resource_imageLink,
exportJsonKey: { resource: { type: "image-link", imageLink: { url: "$" } } },
description: "The link to the image resource",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the linked image resource"
}
]
}
]
},
[ConfigKey.group_resourceAudio]: {
type: GroupConfigType.resource,
description: "Audio resource",
tags: [
{
key: ConfigKey.resource_audio,
exportJsonKey: { resource: { type: "audio", audio: { src: "$" } } },
description: "The audio resource",
chain: [
{
key: ConfigKey.group_resourceAudioCommon,
description: "Common audio properties for the audio resource"
}
]
}
]
},
[ConfigKey.group_resourceAudioEmbed]: {
type: GroupConfigType.resource,
description: "Embedded audio resource",
tags: [
{
key: ConfigKey.resource_audioEmbed,
exportJsonKey: { resource: { type: "audio-embed", audioEmbed: { src: "$" } } },
description: "The embedded audio resource",
chain: [
{
key: ConfigKey.group_resourceAudioCommon,
description: "Common audio properties for the embedded audio resource"
}
]
}
]
},
[ConfigKey.group_resourceAudioLink]: {
type: GroupConfigType.resource,
description: "Link to an audio resource",
tags: [
{
key: ConfigKey.resource_audioLink,
exportJsonKey: { resource: { type: "audio-link", audioLink: { url: "$" } } },
description: "The link to the audio resource",
chain: [
{
key: ConfigKey.group_resourceAudioCommon,
description: "Common audio properties for the linked audio resource"
}
]
}
]
},
[ConfigKey.group_resourceVideo]: {
type: GroupConfigType.resource,
description: "Video resource",
tags: [
{
key: ConfigKey.resource_video,
exportJsonKey: { resource: { type: "video", video: { src: "$" } } },
description: "The video resource",
chain: [
{
key: ConfigKey.group_resourceVideoCommon,
description: "Common video properties for the video resource"
}
]
}
]
},
[ConfigKey.group_resourceVideoEmbed]: {
type: GroupConfigType.resource,
description: "Embedded video resource",
tags: [
{
key: ConfigKey.resource_videoEmbed,
exportJsonKey: { resource: { type: "video-embed", videoEmbed: { url: "$" } } },
description: "The embedded video resource",
chain: [
{
key: ConfigKey.group_resourceVideoCommon,
description: "Common video properties for the embedded video resource"
}
]
}
]
},
[ConfigKey.group_resourceVideoLink]: {
type: GroupConfigType.resource,
description: "Link to a video resource",
tags: [
{
key: ConfigKey.resource_videoLink,
exportJsonKey: { resource: { type: "video-link", videoLink: { url: "$" } } },
description: "The link to the video resource",
chain: [
{
key: ConfigKey.group_resourceVideoCommon,
description: "Common video properties for the linked video resource"
}
]
}
]
},
[ConfigKey.group_resourceStillImageFilmEmbed]: {
type: GroupConfigType.resource,
description: "Embedded still image film resource",
tags: [
{
key: ConfigKey.resource_stillImageFilmEmbed,
exportJsonKey: {
resource: { type: "still-image-film-embed", stillImageFilmEmbed: { url: "$" } }
},
description: "The embedded still image film resource",
chain: [
{
key: ConfigKey.group_resourceVideoCommon,
description: "Common video properties for the embedded still image film resource"
}
]
}
]
},
[ConfigKey.group_resourceStillImageFilmLink]: {
type: GroupConfigType.resource,
description: "Link to a still image film resource",
tags: [
{
key: ConfigKey.resource_stillImageFilmLink,
exportJsonKey: {
resource: { type: "still-image-film-link", stillImageFilmLink: { url: "$" } }
},
description: "The link to the still image film resource",
chain: [
{
key: ConfigKey.group_resourceVideoCommon,
description: "Common video properties for the linked still image film resource"
}
]
}
]
},
[ConfigKey.group_resourceArticle]: {
type: GroupConfigType.resource,
description: "Article resource",
tags: [
{
key: ConfigKey.resource_article,
exportJsonKey: { resource: { type: "article", article: { body: "$" } } },
description: "The article resource",
chain: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties for the article resource"
}
]
}
]
},
[ConfigKey.group_resourceArticleEmbed]: {
type: GroupConfigType.resource,
description: "Embedded article resource",
tags: [
{
key: ConfigKey.resource_articleEmbed,
exportJsonKey: { resource: { type: "article-embed", articleEmbed: { url: "$" } } },
description: "The embedded article resource",
chain: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties for the embedded article resource"
}
]
}
]
},
[ConfigKey.group_resourceArticleLink]: {
type: GroupConfigType.resource,
description: "Link to an article resource",
tags: [
{
key: ConfigKey.resource_articleLink,
exportJsonKey: { resource: { type: "article-link", articleLink: { url: "$" } } },
description: "The link to the article resource",
chain: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties for the linked article resource"
}
]
}
]
},
[ConfigKey.group_resourceDocument]: {
type: GroupConfigType.resource,
description: "Document resource",
tags: [
{
key: ConfigKey.resource_document,
exportJsonKey: { resource: { type: "document", document: { url: "$" } } },
description: "The document resource",
chain: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties for the document resource"
}
]
}
]
},
[ConfigKey.group_resourceDocumentEmbed]: {
type: GroupConfigType.resource,
description: "Embedded document resource",
tags: [
{
key: ConfigKey.resource_documentEmbed,
exportJsonKey: { resource: { type: "document-embed", documentEmbed: { url: "$" } } },
description: "The embedded document resource",
chain: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties for the embedded document resource"
}
]
}
]
},
[ConfigKey.group_resourceDocumentLink]: {
type: GroupConfigType.resource,
description: "Link to a document resource",
tags: [
{
key: ConfigKey.resource_documentLink,
exportJsonKey: { resource: { type: "document-link", documentLink: { url: "$" } } },
description: "The link to the document resource",
chain: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties for the linked document resource"
}
]
}
]
},
[ConfigKey.group_resourceDocumentDownload]: {
type: GroupConfigType.resource,
description: "Downloadable document resource",
tags: [
{
key: ConfigKey.resource_documentDownload,
exportJsonKey: { resource: { type: "document-download", documentDownload: { url: "$" } } },
description: "The downloadable document resource",
chain: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties for the downloadable document resource"
}
]
}
]
},
[ConfigKey.group_resourceAppLink]: {
type: GroupConfigType.resource,
description: "Link to an app resource",
tags: [
{
key: ConfigKey.resource_appLink,
exportJsonKey: { resource: { type: "app-link", appLink: { url: "$" } } },
description: "The link to the app resource",
chain: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties for the linked app resource"
}
]
}
]
},
[ConfigKey.group_resourceWebsiteLink]: {
type: GroupConfigType.resource,
description: "Link to a website resource",
tags: [
{
key: ConfigKey.resource_websiteLink,
exportJsonKey: { resource: { type: "website-link", websiteLink: { url: "$" } } },
description: "The link to the website resource",
chain: [
{
key: ConfigKey.group_resourceCommon,
description: "Common resource properties for the linked website resource"
}
]
}
]
},
//
// Combo resources - these are resources made up of a combination of multiple resources.
//
[ConfigKey.group_resourceStillImageFilm]: {
type: GroupConfigType.comboResource,
description: "Resource for still image film",
comboResourceConfigKey: ConfigKey.resource_stillImageFilm,
tags: [
{
key: ConfigKey.group_resourceImage,
description: "The image resource for the still image film",
maxCount: 1,
minCount: 1
},
{
key: ConfigKey.group_resourceAudio,
description: "The audio resource for the still image film",
maxCount: 1,
minCount: 1
}
]
},
[ConfigKey.group_resourceImageResponsive]: {
type: GroupConfigType.comboResource,
description: "Responsive image resource",
comboResourceConfigKey: ConfigKey.resource_imageResponsive,
tags: [
{
key: ConfigKey.group_resourceImagePortrait,
description: "The portrait image resource",
maxCount: 1,
minCount: 1
},
{
key: ConfigKey.group_resourceImageLandscape,
description: "The landscape image resource",
maxCount: 1,
minCount: 1
}
]
},
[ConfigKey.group_previewImages]: {
type: GroupConfigType.resource,
description: "Array of preview images for catalog items",
tags: [
{
key: ConfigKey.resource_previewImage,
jsonKey: "previewImage|resource(type=image, key=image)",
exportJsonKey: { previewImage: { type: "image", image: { src: "$" } } },
description: "The preview image resource",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the image resource"
}
]
}
]
},
[ConfigKey.group_previewVideos]: {
type: GroupConfigType.resource,
description: "Array of preview videos for catalog items",
tags: [
{
key: ConfigKey.resource_previewVideo,
jsonKey: "previewVideo|resource(type=video, key=video)",
exportJsonKey: { previewVideo: { type: "video", video: { src: "$" } } },
description: "The preview video resource",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the image resource"
}
]
}
]
}
};
// src/config/ConfigHydrator.ts
var MAX_COUNT_DEFAULT = 1;
var MIN_COUNT_DEFAULT = 0;
var ConfigHydrator = class {
hydrateTagsConfig(_tags) {
const tagsWithInfo = {
tags: {}
};
for (const t of _tags) {
let expandedTags;
const type = typeFromConfigKey(t.key);
switch (type) {
case BitTagConfigKeyType.tag:
expandedTags = this.hydrateTagConfig(t);
break;
case BitTagConfigKeyType.property:
expandedTags = this.hydratePropertyTagConfig(t);
break;
case BitTagConfigKeyType.resource:
expandedTags = this.hydrateResourceTagConfig(t);
break;
case BitTagConfigKeyType.group:
expandedTags = this.hydrateTagGroupConfig(t);
break;
default:
throw new Error(`Unknown tag type for config key '${t.key}'`);
}
if (expandedTags) {
tagsWithInfo.tags = {
...tagsWithInfo.tags,
...expandedTags.tags
};
tagsWithInfo.info = Object.assign(tagsWithInfo.info ?? {}, expandedTags.info);
}
}
return tagsWithInfo;
}
hydrateCardSetConfig(_cardSet) {
if (!_cardSet) return void 0;
const _cardSetConfig = CARDSETS[_cardSet];
if (!_cardSetConfig) throw new Error(`No config found for card set config key '${_cardSet}'`);
const sides = [];
for (const _side of _cardSetConfig.sides) {
const variantsOfSide = [];
for (const _variant of _side.variants) {
const v = this.hydrateCardVariantConfig(_variant);
variantsOfSide.push(v);
}
const sideConfig = new CardSideConfig(
_side.name,
_side.repeat ?? false,
variantsOfSide,
_side.jsonKey,
_side.exportJsonKey,
Object.prototype.hasOwnProperty.call(_side, "exportJsonKey"),
_side.htmlKey,
Object.prototype.hasOwnProperty.call(_side, "htmlKey")
);
sides.push(sideConfig);
}
const cardSetConfig = new CardSetConfig(
_cardSet,
_cardSetConfig.jsonKey,
_cardSetConfig.exportJsonKey,
Object.prototype.hasOwnProperty.call(_cardSetConfig, "exportJsonKey"),
this.hydrateCardSections(_cardSetConfig.sections),
sides,
_cardSetConfig.htmlKey,
Object.prototype.hasOwnProperty.call(_cardSetConfig, "htmlKey")
);
return cardSetConfig;
}
hydrateCardSections(sections) {
if (!sections) return void 0;
const out = {};
for (const [k, v] of Object.entries(sections)) {
out[k] = {
jsonKey: v.jsonKey,
exportJsonKey: v.exportJsonKey,
hasExportJsonKey: Object.prototype.hasOwnProperty.call(v, "exportJsonKey"),
htmlKey: v.htmlKey,
hasHtmlKey: Object.prototype.hasOwnProperty.call(v, "htmlKey"),
isDefault: v.isDefault,
sideJsonKey: v.sideJsonKey,
sideExportJsonKey: v.sideExportJsonKey,
hasSideExportJsonKey: Object.prototype.hasOwnProperty.call(v, "sideExportJsonKey"),
sideHtmlKey: v.sideHtmlKey,
hasSideHtmlKey: Object.prototype.hasOwnProperty.call(v, "sideHtmlKey"),
// PLAN-085: cardinality fields pass through; defaults applied
// downstream by the config consumer (Rust treats `0` / undefined
// as unbounded).
minCount: v.minCount,
maxCount: v.maxCount
};
}
return out;
}
hydrateTagConfig(_tag) {
const {
key: _configKey,
maxCount,
minCount,
chain: _chain,
deprecated,
jsonKey,
exportJsonKey,
htmlKey
} = _tag;
const configKey = Enum2(ConfigKey).fromValue(_configKey) || ConfigKey._unknown;
if (!configKey) throw new Error(`No tag key found for config key '${configKey}'`);
const tag = Enum2(Tag).fromValue(configKey);
if (!tag) throw new Error(`No tag found for tag config key '${configKey}'`);
let chain;
if (_chain) {
chain = this.hydrateTagsConfig(_chain).tags;
}
const hydratedTag = new MarkupTagConfig({
configKey,
tag,
maxCount: maxCount ?? MAX_COUNT_DEFAULT,
minCount: minCount ?? MIN_COUNT_DEFAULT,
chain,
jsonKey,
exportJsonKey,
hasExportJsonKey: Object.prototype.hasOwnProperty.call(_tag, "exportJsonKey"),
htmlKey,
hasHtmlKey: Object.prototype.hasOwnProperty.call(_tag, "htmlKey"),
deprecated
});
return {
tags: {
[configKey]: hydratedTag
}
};
}
hydratePropertyTagConfig(_tag) {
const {
key: _configKey,
maxCount,
minCount,
chain: _chain,
deprecated,
format,
values,
defaultValue,
jsonKey,
exportJsonKey,
htmlKey
} = _tag;
const configKey = Enum2(ConfigKey).fromValue(_configKey) || ConfigKey._unknown;
if (!configKey) throw new Error(`No property key found for config key '${configKey}'`);
const tag = _configKey.substring(1);
let chain;
if (_chain) {
chain = this.hydrateTagsConfig(_chain).tags;
}
const hydratedTag = new PropertyTagConfig({
configKey,
tag,
maxCount: maxCount ?? MAX_COUNT_DEFAULT,
minCount: minCount ?? MIN_COUNT_DEFAULT,
chain,
jsonKey,
exportJsonKey,
hasExportJsonKey: Object.prototype.hasOwnProperty.call(_tag, "exportJsonKey"),
htmlKey,
hasHtmlKey: Object.prototype.hasOwnProperty.call(_tag, "htmlKey"),
format,
values,
defaultValue,
deprecated
});
return {
tags: {
[configKey]: hydratedTag
}
};
}
hydrateResourceTagConfig(_tag) {
const {
key: _configKey,
maxCount,
minCount,
chain: _chain,
deprecated,
jsonKey,
exportJsonKey,
htmlKey
} = _tag;
const configKey = Enum2(ConfigKey).fromValue(_configKey) || ConfigKey._unknown;
if (!configKey) throw new Error(`No resource key found for config key '${configKey}'`);
const tag = _configKey.substring(1);
let chain;
if (_chain) {
chain = this.hydrateTagsConfig(_chain).tags;
}
const hydratedTag = new ResourceTagConfig({
configKey,
tag,
maxCount: maxCount ?? MAX_COUNT_DEFAULT,
minCount: minCount ?? MIN_COUNT_DEFAULT,
chain,
jsonKey,
exportJsonKey,
hasExportJsonKey: Object.prototype.hasOwnProperty.call(_tag, "exportJsonKey"),
htmlKey,
hasHtmlKey: Object.prototype.hasOwnProperty.call(_tag, "htmlKey"),
deprecated
});
return {
tags: {
[configKey]: hydratedTag
}
};
}
hydrateTagGroupConfig(_tag) {
const { key: _configKey } = _tag;
const configKey = Enum2(ConfigKey).fromValue(_configKey) || ConfigKey._unknown;
if (!configKey) throw new Error(`No group key found for config key '${configKey}'`);
const _groupConfig = GROUPS[configKey];
if (!_groupConfig) throw new Error(`No config found for group config key '${_configKey}'`);
const { tags: _tags } = _groupConfig;
const tags2 = this.hydrateTagsConfig(_tags).tags;
const tagValues = Object.values(tags2);
if (tagValues.length > 0) {
const firstTag = tagValues[0];
const firstTagAny = firstTag;
firstTagAny.maxCount = _tag.maxCount ?? firstTag.maxCount;
firstTagAny.minCount = _tag.minCount ?? firstTag.minCount;
}
return {
tags: tags2,
info: {
comboResourceConfigKey: _groupConfig.comboResourceConfigKey
}
};
}
hydrateCardVariantConfig(_variant) {
const {
tags: _tags,
bodyAllowed,
bodyRequired,
repeatCount,
jsonKey,
exportJsonKey,
htmlKey,
format
} = _variant;
const tags2 = this.hydrateTagsConfig(_tags);
const cardSetConfig = new CardVariantConfig(
tags2.tags,
bodyAllowed,
bodyRequired,
repeatCount,
jsonKey,
exportJsonKey,
Object.prototype.hasOwnProperty.call(_variant, "exportJsonKey"),
format,
htmlKey,
Object.prototype.hasOwnProperty.call(_variant, "htmlKey")
);
return cardSetConfig;
}
};
var instance = new ConfigHydrator();
// src/config/raw/bits.ts
var BITS = {
[BitType._error]: {
since: "1.3.0",
description: "An error bit, rendered when the bit cannot be interpreted"
},
[BitType._comment]: {
since: "1.4.12",
description: "A comment bit, used when a bit is commented out by adding | between the . and the bit name, e.g [.|article]"
},
[BitType._standard]: {
since: "3.2.0",
description: "The standard bit type, used as a base for other bits, should not be used directly",
tags: [
{
key: ConfigKey.group_standardTags,
description: "Standard tags for (almost) all bits"
}
]
},
[BitType.appFlashcards]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "App flashcards bit, used for flashcard quizzes in the app",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common tags for quiz bits"
},
{
maxCount: 2,
jsonKey: "title|multi(count=2, key=subtitle)",
exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }],
key: ConfigKey.tag_title,
description: "The title of the flashcard quiz"
},
{
key: ConfigKey.property_flashcardSet,
description: "The flashcard set to use for the app flashcards",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
// Override inherited @example from group_standardTags: string root
// example. Bare/synthesized emits `example: "true"` (a text literal
// that field-coerces to a paragraph "true" in bitmark+ contexts);
// valued [@example:text] emits the user's text via `$`.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: "true" } },
{ "@absent": { isExample: true, example: "true" } },
{ isExample: true, example: "$" }
],
description: "The example text for the bit",
format: TagFormat.plainText,
nullable: true
}
],
rootExampleType: ExampleType.string
},
[BitType.appFlashcardsQuiz]: {
since: "1.3.0",
baseBitType: BitType.appFlashcards,
description: "App flashcards quiz bit"
},
[BitType.appFlashcardsLearn]: {
since: "1.3.0",
baseBitType: BitType.appFlashcards,
description: "App flashcards learn bit"
},
[BitType.appLink]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "App link bit, used to link to other bits in the app",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Tags for the resource bit"
},
{
key: ConfigKey.group_resourceAppLink,
description: "Tags for the app link resource",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.appRag]: {
since: "5.14.0",
baseBitType: BitType.article,
description: "App RAG (Retrieval-Augmented Generation) bit, equal to article"
},
[BitType.appSearch]: {
since: "5.14.0",
baseBitType: BitType.article,
description: "App search bit, equal to article"
},
[BitType.author]: {
since: "4.2.0",
baseBitType: BitType._standard,
description: "Represents an author of something",
tags: [
{
key: ConfigKey.property_fullName,
description: "Full name of the author"
},
{
key: ConfigKey.property_pseudonym,
description: "A pseudonym of the author, if any"
},
{
key: ConfigKey.property_title,
description: 'The title of the author, e.g. "Dr.", "Prof.", etc.',
jsonKey: "title"
},
{
key: ConfigKey.property_jobTitle,
description: 'The job title of the author, e.g. "Software Engineer", "Professor", etc.'
}
]
},
[BitType.bookAuthor]: {
since: "4.2.0",
baseBitType: BitType.author,
description: "Represents an author of a book"
},
[BitType.articleAuthor]: {
since: "4.2.0",
baseBitType: BitType.author,
description: "Represents an author of a book"
},
[BitType.article]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Article bit, used for articles / paragraphs",
tags: [
{
maxCount: 2,
jsonKey: "title|multi(count=2, key=subtitle)",
exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }],
// PLAN-140 W3 (NISO import): the title of a mapped source element.
mappingKeys: { "xml-niso-iec": { "@el": "title", "@text": "$" } },
key: ConfigKey.tag_title,
description: "The title of the article"
}
]
},
[BitType.articleAlt]: {
since: "1.15.0",
baseBitType: BitType.article,
description: "Alternative article bit"
},
[BitType.articleResponsive]: {
since: "1.21.0",
baseBitType: BitType.article,
description: "Responsive article bit, used for articles that adapt to the screen size",
tags: [
{
key: ConfigKey.property_imageFirst,
description: "If the image should be displayed first in the responsive article",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.articleResponsiveAlt]: {
since: "2.0.0",
baseBitType: BitType.articleResponsive,
description: "Alternative responsive article bit"
},
[BitType.standardArticleNormative]: {
since: "1.16.0",
baseBitType: BitType.article,
description: "Standard normative article bit"
},
[BitType.standardArticleNonNormative]: {
since: "1.16.0",
baseBitType: BitType.article,
description: "Standard non-normative article bit"
},
[BitType.smartStandardArticleNormative]: {
since: "1.28.0",
baseBitType: BitType.standardArticleNormative,
description: "Smart standard normative article bit",
// PLAN-140 W3 (NISO import, tranche 1): one article bit per NISO
// paragraph (catalog D1). CONTENT keys (@children): the subtree is the
// bit's body; nothing inside it promotes. (Ancestor-context normativity
// is the non-normative sibling's @ancestor key — plan Q4, resolved.)
//
// Second rule (catalog D1 push-down): a NUMBERED-PARAGRAPH sec is an
// article, not a chapter — the sec-type literal outranks the chapter
// key's plain sec|app rule (most-specific-wins). Content key: the sec's
// label rides [%] via the label tag key, its inner <p> is the body, its
// id is the bit's customerId, and the anchor slot marks it
// anchor-bearing.
mappingKeys: {
"xml-niso-iec": [
{ "@el": "p", "@attr": { id: "$customerId" }, "@children": "$" },
{
"@el": "sec",
"@attr": { "sec-type": "numbered-paragraph", id: "$customerId" },
"@children": "$",
"\u25BC": "$anchor"
}
]
}
},
[BitType.smartStandardArticleNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardArticleNonNormative,
description: "Smart standard non-normative article bit",
// PLAN-140 Q4 (normativity inheritance): a <p> is non-normative because
// of an ANCESTOR element — a foreword/intro sec or an informative annex.
// @ancestor is a reverse-match-only constraint (forward emission ignores
// it); the rules are alternatives, first-satisfied wins, and the
// @ancestor literals give this key higher specificity than the plain
// normative-article <p> key wherever the context applies.
mappingKeys: {
"xml-niso-iec": [
{
"@el": "p",
"@attr": { id: "$customerId" },
"@ancestor": { "@attr": { "sec-type": "foreword|intro" } },
"@children": "$"
},
{
"@el": "p",
"@attr": { id: "$customerId" },
"@ancestor": { "@attr": { "content-type": "informative" } },
"@children": "$"
}
]
}
},
[BitType.smartStandardArticleNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardArticleNormative,
description: "Smart standard normative article bit that is collapsible",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardArticleNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardArticleNonNormative,
description: "Smart standard non-normative article bit that is collapsible",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.statement]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Statement bit, used for statements in documents"
},
[BitType.pageArticle]: {
since: "1.15.0",
baseBitType: BitType.article,
description: "Page article bit"
},
[BitType.pageArticleAlt]: {
since: "1.15.0",
baseBitType: BitType.article,
description: "Alternative page article bit"
},
[BitType.pageArticleResponsive]: {
since: "1.21.0",
baseBitType: BitType.article,
description: "Responsive page article bit, used for articles that adapt to the screen size",
tags: [
{
key: ConfigKey.property_imageFirst,
description: "If the image should be displayed first in the responsive article",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.buttonCopyText]: {
since: "1.4.3",
baseBitType: BitType.article,
description: "Button copy text bit, used to create a button that copies text to the clipboard",
tags: [
{
key: ConfigKey.property_buttonCaption,
description: "The caption of the button",
format: TagFormat.plainText
}
]
},
[BitType.catalogItemBook]: {
since: "4.15.0",
baseBitType: BitType._standard,
description: "Catalog item book bit, used to represent a book product in a catalog with cover and description",
tags: [
{
key: ConfigKey.tag_title,
description: "Title and subtitle of the catalog item",
maxCount: 2,
jsonKey: "title|multi(count=2, key=subtitle)",
exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }]
},
{
key: ConfigKey.property_coverImage,
description: "Cover image of the catalog item",
format: TagFormat.plainText,
nullable: true
},
{
key: ConfigKey.resource_coverImage,
jsonKey: "coverImage|resource(type=image, key=image)",
exportJsonKey: { coverImage: { type: "image", image: { src: "$" } } },
description: "Cover image of the catalog item",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the cover image resource"
}
]
},
{
key: ConfigKey.property_content2Buy,
description: "Content to buy identifier",
format: TagFormat.plainText
},
{
key: ConfigKey.property_listPrice,
description: "List price of the catalog item",
format: TagFormat.plainText
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the buy button",
format: TagFormat.plainText
},
{
key: ConfigKey.property_customerProductId,
description: "Customer-specific product identifier",
format: TagFormat.plainText
},
{
key: ConfigKey.property_currency,
description: "Currency for the catalog item",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookIsbn,
description: "ISBN for iBook version",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookAuthor,
description: "Author of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookType,
description: "Type of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookBindingType,
description: "Binding type of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookNumberOfPages,
description: "Number of pages in the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookRating,
description: "Rating of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookSeriesTitle,
description: "Title of the book series",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookSeriesId,
description: "ID of the book series",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookSeriesNumber,
description: "Number in the book series",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookLanguage,
description: "Language of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookPublishingDate,
description: "Publishing date of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookPublisher,
description: "Publisher of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookTranslator,
description: "Translator of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookOriginalTitle,
description: "Original title of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_gmbExternalShop,
description: "External shop identifier",
format: TagFormat.plainText
},
{
key: ConfigKey.property_gmbExternalShopItems,
description: "Array of external shop items",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.group_imageSource,
description: "Image source chain"
},
{
key: ConfigKey.group_previewImages,
description: "Array of preview images for the catalog item",
maxCount: Count.infinity
},
{
key: ConfigKey.group_previewVideos,
description: "Array of preview videos for the catalog item",
maxCount: Count.infinity
},
{
// Override inherited @example from group_standardTags: string root example.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: "true" } },
{ "@absent": { isExample: true, example: "true" } },
{ isExample: true, example: "$" }
],
description: "The example text for the bit",
format: TagFormat.plainText,
nullable: true
}
],
rootExampleType: ExampleType.string
},
[BitType.catalogItemExternalBook]: {
since: "4.15.0",
baseBitType: BitType._standard,
description: "Catalog item external book bit, used to represent an external book product in a catalog with link to external shop",
tags: [
{
key: ConfigKey.tag_title,
description: "Title and subtitle of the catalog item",
maxCount: 2,
jsonKey: "title|multi(count=2, key=subtitle)",
exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }]
},
{
key: ConfigKey.property_coverImage,
description: "Cover image of the catalog item",
format: TagFormat.plainText,
nullable: true
},
{
key: ConfigKey.resource_coverImage,
jsonKey: "coverImage|resource(type=image, key=image)",
exportJsonKey: { coverImage: { type: "image", image: { src: "$" } } },
description: "Cover image of the catalog item",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the cover image resource"
}
]
},
{
key: ConfigKey.property_customerProductShopLink,
description: "Link to external shop for the product",
format: TagFormat.plainText
},
{
key: ConfigKey.property_listPrice,
description: "List price of the catalog item",
format: TagFormat.plainText
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the buy button",
format: TagFormat.plainText
},
{
key: ConfigKey.property_customerProductId,
description: "Customer-specific product identifier",
format: TagFormat.plainText
},
{
key: ConfigKey.property_currency,
description: "Currency for the catalog item",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookIsbn,
description: "ISBN for iBook version",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookAuthor,
description: "Author of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookType,
description: "Type of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookBindingType,
description: "Binding type of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookNumberOfPages,
description: "Number of pages in the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookRating,
description: "Rating of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookSeriesTitle,
description: "Title of the book series",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookSeriesId,
description: "ID of the book series",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookSeriesNumber,
description: "Number in the book series",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookLanguage,
description: "Language of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookPublishingDate,
description: "Publishing date of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookPublisher,
description: "Publisher of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookTranslator,
description: "Translator of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookOriginalTitle,
description: "Original title of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_gmbExternalShop,
description: "External shop identifier",
format: TagFormat.plainText
},
{
key: ConfigKey.property_gmbExternalShopItems,
description: "Array of external shop items",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.group_imageSource,
description: "Image source chain"
},
{
key: ConfigKey.group_previewImages,
description: "Array of preview images for the catalog item",
maxCount: Count.infinity
},
{
key: ConfigKey.group_previewVideos,
description: "Array of preview videos for the catalog item",
maxCount: Count.infinity
},
{
// Override inherited @example from group_standardTags: string root example.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: "true" } },
{ "@absent": { isExample: true, example: "true" } },
{ isExample: true, example: "$" }
],
description: "The example text for the bit",
format: TagFormat.plainText,
nullable: true
}
],
rootExampleType: ExampleType.string
},
[BitType.catalogItemProduct]: {
since: "4.15.0",
baseBitType: BitType._standard,
description: "Catalog item product bit, used to represent a general product in a catalog with cover and description",
tags: [
{
key: ConfigKey.tag_title,
description: "Title and subtitle of the catalog item",
maxCount: 2,
jsonKey: "title|multi(count=2, key=subtitle)",
exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }]
},
{
key: ConfigKey.property_coverImage,
description: "Cover image of the catalog item",
format: TagFormat.plainText,
nullable: true
},
{
key: ConfigKey.property_content2Buy,
description: "Content to buy identifier",
format: TagFormat.plainText
},
{
key: ConfigKey.property_listPrice,
description: "List price of the catalog item",
format: TagFormat.plainText
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the buy button",
format: TagFormat.plainText
},
{
key: ConfigKey.property_customerProductId,
description: "Customer-specific product identifier",
format: TagFormat.plainText
},
{
key: ConfigKey.property_currency,
description: "Currency for the catalog item",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookIsbn,
description: "ISBN for iBook version",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookAuthor,
description: "Author of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookType,
description: "Type of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookBindingType,
description: "Binding type of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookNumberOfPages,
description: "Number of pages in the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookRating,
description: "Rating of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookSeriesTitle,
description: "Title of the book series",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookSeriesId,
description: "ID of the book series",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookSeriesNumber,
description: "Number in the book series",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookLanguage,
description: "Language of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookPublishingDate,
description: "Publishing date of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookPublisher,
description: "Publisher of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookTranslator,
description: "Translator of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_bookOriginalTitle,
description: "Original title of the book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_gmbExternalShop,
description: "External shop identifier",
format: TagFormat.plainText
},
{
key: ConfigKey.property_gmbExternalShopItems,
description: "Array of external shop items",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.group_imageSource,
description: "Image source chain"
},
{
key: ConfigKey.group_previewImages,
description: "Array of preview images for the catalog item",
maxCount: Count.infinity
},
{
key: ConfigKey.group_previewVideos,
description: "Array of preview videos for the catalog item",
maxCount: Count.infinity
},
{
// Override inherited @example from group_standardTags: string root example.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: "true" } },
{ "@absent": { isExample: true, example: "true" } },
{ isExample: true, example: "$" }
],
description: "The example text for the bit",
format: TagFormat.plainText,
nullable: true
}
],
rootExampleType: ExampleType.string
},
[BitType.callToAction]: {
since: "1.15.0",
baseBitType: BitType._standard,
description: "Call to action bit, used to create a call to action button",
tags: [
{
key: ConfigKey.property_buttonCaption,
description: "The caption of the call to action button",
format: TagFormat.plainText
},
{
key: ConfigKey.property_callToActionUrl,
description: "The URL to navigate to when the call to action button is clicked",
format: TagFormat.plainText,
minCount: 1
}
]
},
[BitType.callToActionSubscribe]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action subscribe bit"
},
[BitType.callToActionContact]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action contact bit"
},
[BitType.callToActionJoin]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action join bit"
},
[BitType.callToActionMail]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action mail bit"
},
[BitType.callToActionLearnMore]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action learn more bit"
},
[BitType.callToActionSeeMore]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action see more bit"
},
[BitType.callToActionWatch]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action watch bit"
},
[BitType.callToActionStartNow]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action start now bit"
},
[BitType.callToActionGetOffer]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action get offer bit"
},
[BitType.callToActionBookNow]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action book now bit"
},
[BitType.callToActionShopNow]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action shop now bit"
},
[BitType.callToActionGetNow]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action get now bit"
},
[BitType.callToActionDownload]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action download bit"
},
[BitType.callToActionCreateAccount]: {
since: "1.15.0",
baseBitType: BitType.callToAction,
description: "Call to action create account bit"
},
[BitType.appBitmarkFromJavascript]: {
since: "1.4.5",
baseBitType: BitType._standard,
description: "App bitmark from JavaScript, used to create bits from JavaScript in the app",
tags: [
{
key: ConfigKey.property_maxCreatedBits,
description: "The maximum number of bits that can be created from this bit",
format: TagFormat.number,
nullable: true
}
],
textFormatDefault: TextFormat.plainText
},
[BitType.appBitmarkFromEditor]: {
since: "1.4.5",
baseBitType: BitType.appBitmarkFromJavascript,
description: "App bitmark from editor, used to create bits from the editor in the app"
},
[BitType.articleEmbed]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Article embed bit, used to embed articles from other sources",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Tags for the resource bit"
},
{
key: ConfigKey.group_resourceArticleEmbed,
description: "Tags for the article embed resource",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.articleLink]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Article link bit, used to link to articles from other sources",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Tags for the resource bit"
},
{
key: ConfigKey.group_resourceArticleLink,
description: "Tags for the article link resource",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.audio]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Audio bit, used for audio files",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Tags for the resource bit"
},
{
key: ConfigKey.group_resourceAudio,
description: "Tags for the audio resource",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.audioEmbed]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Audio embed bit, used to embed audio files from other sources",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Tags for the resource bit"
},
{
key: ConfigKey.group_resourceAudioEmbed,
description: "Tags for the audio embed resource",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.audioLink]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Audio link bit, used to link to audio files from other sources",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Tags for the resource bit"
},
{
key: ConfigKey.group_resourceAudioLink,
description: "Tags for the audio link resource",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.bitAlias]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Bit alias, used to create an alias for a bit",
tags: [
{
exportJsonKey: { reference: "$" },
key: ConfigKey.tag_reference,
format: TagFormat.plainText,
description: "The reference to the bit that this alias points to"
},
{
exportJsonKey: { anchor: "$" },
key: ConfigKey.tag_anchor,
format: TagFormat.plainText,
description: "The anchor for the bit alias, used for linking"
}
]
},
[BitType.diff]: {
since: "3.13.0",
baseBitType: BitType._standard,
description: "Diff bit, used to show differences between two bits",
tags: [
{
key: ConfigKey.property_diffTo,
description: "The reference to the bit that this diff compares to",
format: TagFormat.plainText
}
]
},
[BitType.book]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Book bit, used to represent a book",
// PLAN-141 F5 (NISO import): the <std-meta> front-matter container is
// the [.book] bit (STRUCTURAL, first in document order): primary
// title-wrap → [#], primary content-language → [@language], dated
// std-ref → [@externalId] (tag keys with maxEmits caps — the NISO
// convention puts the document language first). std-meta has no id, so
// no anchor/customerId. Remaining leaves drop as recorded loss (R-4).
mappingKeys: { "xml-niso-iec": { "@el": "std-meta" } },
tags: [
{
key: ConfigKey.group_bookCommon,
description: "Common tags for book bits"
}
]
},
[BitType.bookEnd]: {
since: "3.27.0",
baseBitType: BitType.article,
description: "End of book bit"
},
[BitType.bookAcknowledgements]: {
since: "1.17.0",
baseBitType: BitType.article,
description: "Acknowledgements section of a book"
},
[BitType.bookAddendum]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Addendum section of a book"
},
[BitType.bookAfterword]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Afterword section of a book"
},
[BitType.bookAppendix]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Appendix section of a book"
},
[BitType.bookArticle]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Article section of a book"
},
[BitType.bookAutherBio]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Author biography section of a book"
},
[BitType.bookBibliography]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Bibliography section of a book"
},
[BitType.bookComingSoon]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Coming soon section of a book"
},
[BitType.bookConclusion]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Conclusion section of a book"
},
[BitType.bookCopyright]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Copyright section of a book"
},
[BitType.bookCopyrightPermissions]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Copyright permissions section of a book"
},
[BitType.bookDedication]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Dedication section of a book"
},
[BitType.bookEndnotes]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Endnotes section of a book"
},
[BitType.bookEpigraph]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Epigraph section of a book"
},
[BitType.bookEpilogue]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Epilogue section of a book"
},
[BitType.bookForword]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Foreword section of a book"
},
[BitType.bookFrontispiece]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Frontispiece section of a book"
},
[BitType.bookImprint]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Imprint section of a book"
},
[BitType.bookIncitingIncident]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Inciting incident section of a book"
},
[BitType.bookIntroduction]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Introduction section of a book"
},
[BitType.bookListOfContributors]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "List of contributors section of a book"
},
[BitType.bookNotes]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Notes section of a book"
},
[BitType.bookPostscript]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Postscript section of a book"
},
[BitType.bookPreface]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Preface section of a book"
},
[BitType.bookPrologue]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Prologue section of a book"
},
[BitType.bookReadMore]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Read more section of a book"
},
[BitType.bookReferenceList]: {
//
since: "3.2.0",
baseBitType: BitType._standard,
description: "Book reference list, used to list references in a book",
cardSet: CardSetConfigKey.bookReferenceList
},
[BitType.bookRequestForABookReview]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Request for a book review section of a book"
},
[BitType.bookSummary]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Summary section of a book"
},
[BitType.bookTeaser]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Teaser section of a book"
},
[BitType.bookTitle]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Title section of a book"
},
[BitType.bookCover]: {
since: "3.27.0",
baseBitType: BitType.image,
description: "Cover image of a book",
tags: [
{
key: ConfigKey.tag_title,
description: "The title of the book cover",
maxCount: 2,
// title & subtitle
jsonKey: "title|multi(count=2, key=subtitle)",
exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }]
},
{
key: ConfigKey.property_coverColor,
description: "The color of the book cover",
format: TagFormat.plainText
}
]
},
[BitType.bookAlias]: {
since: "1.4.3",
baseBitType: BitType.book,
description: "Book alias, used to create an alias for a book",
tags: [
{
key: ConfigKey.property_bookAlias,
description: "The alias for the book",
format: TagFormat.plainText,
maxCount: Count.infinity
}
]
},
[BitType.bookDiff]: {
since: "3.10.0",
baseBitType: BitType.book,
description: "Book diff, used to show differences between two book versions",
tags: [
{
key: ConfigKey.property_bookDiff,
description: "The reference to the book that this diff compares to",
format: TagFormat.plainText
}
]
},
[BitType.bookClose]: {
since: "1.18.0",
baseBitType: BitType.article,
description: "Close section of a book, used to close the book",
tags: [
{
key: ConfigKey.property_buttonCaption,
description: "The caption of the close button",
format: TagFormat.plainText
}
]
},
[BitType.bookReference]: {
since: "2.2.0",
baseBitType: BitType.article,
description: "Book reference, used to reference a book in another book",
tags: [
{
key: ConfigKey.property_refAuthor,
description: "The author of the referenced book",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_refBookTitle,
description: "The title of the referenced book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_refPublisher,
description: "The publisher of the referenced book",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_refPublicationYear,
description: "The publication year of the referenced book",
format: TagFormat.plainText
},
{
key: ConfigKey.property_citationStyle,
description: "The citation style to use for the book reference",
format: TagFormat.plainText
}
]
},
[BitType.botActionResponse]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Bot action response bit, used to define responses for bot actions",
cardSet: CardSetConfigKey.botActionResponses
},
[BitType.botActionSend]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Bot action send bit, used to define actions for sending messages",
tags: [
{
key: ConfigKey.property_date,
description: "The date when the message should be sent",
format: TagFormat.plainText
}
]
},
[BitType.browserImage]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Browser image bit, used to display images in the browser",
tags: [
{
key: ConfigKey.property_focusX,
description: "The X coordinate for focusing the image in the browser",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_focusY,
description: "The Y coordinate for focusing the image in the browser",
format: TagFormat.number,
nullable: true
}
]
},
[BitType.card1]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Card bit, used for cards in quizzes",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common tags for quiz bits"
}
]
},
[BitType.question1]: {
since: "1.3.0",
baseBitType: BitType.card1,
description: "Question card bit"
},
[BitType.survey1]: { since: "1.3.0", baseBitType: BitType.card1, description: "Survey card bit" },
[BitType.surveyAnonymous1]: {
since: "1.3.0",
baseBitType: BitType.card1,
description: "Anonymous survey card bit"
},
[BitType.chapter]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Chapter bit, used to define chapters in books or articles",
// PLAN-140 W3 (NISO import, tranche 1): recover a chapter from a NISO
// section or annex. STRUCTURAL key (no @children content slot): the
// element carries no body of its own — tag children (label/title) map
// via tag keys, every mapped descendant promotes to a following sibling
// bit, and the chapter level derives from nesting depth (catalog D1).
// The source id lands as [@customerId] (two-id model, D2).
mappingKeys: {
"xml-niso-iec": { "@el": "sec|app", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
},
tags: [
{
exportJsonKey: { anchor: "$" },
key: ConfigKey.tag_anchor,
format: TagFormat.plainText,
description: "The anchor for the chapter, used for linking"
},
{
key: ConfigKey.tag_title,
description: "The title of the chapter",
jsonKey: "title|setMulti(level)",
exportJsonKey: { title: "$", level: "$level" },
// PLAN-140 W3 (NISO import): the section/annex title; the heading
// level rides the normalizer-derived nesting depth (plan Q2).
mappingKeys: { "xml-niso-iec": { "@el": "title", "@text": "$" } }
},
{
key: ConfigKey.property_toc,
description: "If the chapter should be included in the table of contents",
format: TagFormat.boolean,
defaultValue: "true"
},
{
key: ConfigKey.property_progress,
description: "The progress of the chapter, used for tracking reading progress",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.clozeAndMultipleChoiceText]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Cloze and multiple choice text bit, used for quizzes with cloze and multiple choice questions",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common tags for quiz bits"
},
{
key: ConfigKey.group_gap,
description: "Tags for gaps in cloze and multiple choice text bits"
},
{
key: ConfigKey.group_trueFalseInlineSelect,
description: "Tags for true/false questions in cloze and multiple choice text bits"
},
{
// Bit-level @isCaseSensitive — empty exportJsonKey (no bit-level emission);
// defaultValue 'true' phantom-fires onto the ancestor stack so the gap chain's
// `@absent: $ancestor` rule resolves to true. Mirrors BPG Builder.ts:1643-1652
// `pushDownTree(... 'isCaseSensitive', data.isCaseSensitive ?? true)` for cloze bits.
key: ConfigKey.property_isCaseSensitive,
exportJsonKey: {},
description: "If the cloze answers are case sensitive",
format: TagFormat.boolean,
defaultValue: "true"
},
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
[BitType.coachCallToActionClozeAndMultipleChoiceText]: {
since: "1.3.0",
baseBitType: BitType.clozeAndMultipleChoiceText,
description: "Coach call to action cloze and multiple choice text bit"
},
[BitType.cloze]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Cloze bit, used for fill-in-the-blank questions in quizzes",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common tags for quiz bits"
},
{
key: ConfigKey.property_isCaseSensitive,
exportJsonKey: {},
description: "If the cloze answers are case sensitive",
format: TagFormat.boolean,
defaultValue: "true"
},
{
key: ConfigKey.property_quizCountItems,
description: "The number of items in the cloze quiz",
format: TagFormat.boolean,
nullable: true
},
{
key: ConfigKey.property_quizStrikethroughSolutions,
description: "If the cloze solutions should be strikethrough",
format: TagFormat.boolean,
nullable: true
},
{
key: ConfigKey.group_gap,
description: "Tags for gaps in cloze bits"
},
{
// Override inherited @example: cloze is in OLD parser's
// `isTopLevelExample` allow-list, so [@example] at bit-header
// emits `isExample: true`.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
[BitType.clozeInstructionGrouped]: {
since: "1.3.0",
baseBitType: BitType.cloze,
description: "Cloze instruction grouped bit, used for cloze questions with grouped instructions",
tags: [
{
key: ConfigKey.property_quizCountItems,
description: "The number of items in the cloze quiz (instruction-grouped default: true)",
format: TagFormat.boolean,
defaultValue: "true"
}
// Note: quizStrikethroughSolutions defaults to false for instruction-grouped per OLD
// JsonGenerator.ts:1689-1690, which equals the Boolean natural default → no override needed.
]
},
[BitType.clozeSolutionGrouped]: {
since: "1.3.0",
baseBitType: BitType.cloze,
description: "Cloze solution grouped bit, used for cloze questions with grouped solutions",
tags: [
{
key: ConfigKey.property_quizCountItems,
description: "The number of items in the cloze quiz (solution-grouped default: true)",
format: TagFormat.boolean,
defaultValue: "true"
},
{
key: ConfigKey.property_quizStrikethroughSolutions,
description: "If the cloze solutions should be strikethrough (solution-grouped default: true)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.clozeSeveral]: {
since: "3.5.0",
baseBitType: BitType.cloze,
description: "Cloze several bit, used for cloze questions with multiple answers"
},
[BitType.gapText]: {
since: "1.5.15",
baseBitType: BitType.cloze,
description: "Gap text bit, used for fill-in-the-blank questions in quizzes with text gaps"
},
[BitType.gapTextInstructionGrouped]: {
since: "1.5.15",
baseBitType: BitType.clozeInstructionGrouped,
description: "Gap text instruction grouped bit, used for fill-in-the-blank questions with grouped instructions"
},
[BitType.coachSelfReflectionCloze]: {
since: "1.3.0",
baseBitType: BitType.cloze,
description: "Coach self-reflection cloze bit, used for self-reflection questions in coaching"
},
[BitType.coachCallToActionCloze]: {
since: "1.3.0",
baseBitType: BitType.cloze,
description: "Coach call to action cloze bit, used for call to action questions in coaching"
},
[BitType.clozeList]: {
since: "1.4.13",
baseBitType: BitType._standard,
description: "Cloze list bit, used for lists of cloze questions in quizzes",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common tags for quiz bits"
},
{
// PLAN-084 (R12): bit-level @isCaseSensitive — empty exportJsonKey
// (no bit-level emission); defaultValue 'true' phantom-fires onto
// the ancestor stack so each listItem's gap chain `@absent: $ancestor`
// rule resolves to true. Mirrors the cloze bit (bits.ts:1326-1336).
key: ConfigKey.property_isCaseSensitive,
exportJsonKey: {},
description: "If the cloze answers are case sensitive",
format: TagFormat.boolean,
defaultValue: "true"
}
],
cardSet: CardSetConfigKey.clozeList
},
[BitType.code]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Code bit, used for code snippets in articles or documents",
// PLAN-141 F4 (NISO import): a <code> block is a CONTENT bit with a
// literal text body; @language recovers as [@computerLanguage] (absent
// attr → no tag). Anchor = the source id (D2 revised).
mappingKeys: {
"xml-niso-iec": {
"@el": "code",
"@attr": { id: "$customerId", language: "$computerLanguage" },
"\u25BC": "$anchor",
"@text": "$"
}
},
tags: [
{
key: ConfigKey.property_computerLanguage,
description: "The programming language of the code snippet",
format: TagFormat.plainText,
// PLAN-141 F4 (NISO import): the bit key's `language` sigil recovers
// a present attr; this `@absent` rule supplies the AUTHORED default
// when the source carries none (catalog code row — an untyped block
// is `[@computerLanguage:text]`). Fires only on foreign imports,
// never the carrier round-trip.
mappingKeys: { "xml-niso-iec": [{ "@absent": "text" }] }
},
{
key: ConfigKey.property_codeLineNumbers,
description: "If line numbers should be displayed for the code snippet",
format: TagFormat.boolean
},
{
key: ConfigKey.property_codeMinimap,
description: "If a minimap should be displayed for the code snippet",
format: TagFormat.boolean
}
],
textFormatDefault: TextFormat.plainText
},
[BitType.formula]: {
since: "1.35.0",
baseBitType: BitType._standard,
description: "Formula bit, used for mathematical formulas in articles or documents",
textFormatDefault: TextFormat.latex
},
[BitType.formulaImage]: {
since: "5.23.0",
baseBitType: BitType.formula,
description: "Formula image bit, used for mathematical formulas rendered as images"
},
[BitType.smartStandardFormula]: {
since: "3.11.0",
baseBitType: BitType.formula,
description: "Smart standard formula bit, used for mathematical formulas in smart standards"
},
[BitType.smartStandardFormulaNonNormative]: {
since: "3.11.0",
baseBitType: BitType.formula,
description: "Smart standard non-normative formula bit, used for mathematical formulas in smart standards that are non-normative"
},
[BitType.smartStandardFormulaNormative]: {
since: "3.11.0",
baseBitType: BitType.formula,
description: "Smart standard normative formula bit, used for mathematical formulas in smart standards that are normative",
// PLAN-141 F4 (NISO import): a <disp-formula> is a CONTENT bit whose
// latex-format body carries the <mml:math> subtree VERBATIM (G1 — no
// MathML↔LaTeX transform; ids stripped); <label> → [%]; a contained
// <legend> PROMOTES as a sibling legend bit (cards cannot nest in a
// body). Anchor = the source id (D2 revised).
mappingKeys: {
"xml-niso-iec": {
"@el": "disp-formula",
"@attr": { id: "$customerId" },
"\u25BC": "$anchor",
"@children": "$"
}
}
},
[BitType.smartStandardRemarkFormula]: {
since: "3.11.0",
baseBitType: BitType.formula,
description: "Smart standard remark formula bit, used for mathematical formulas in smart standards remarks"
},
[BitType.smartStandardRemarkFormulaNonNormative]: {
since: "3.11.0",
baseBitType: BitType.formula,
description: "Smart standard non-normative remark formula bit, used for mathematical formulas in smart standards remarks that are non-normative"
},
[BitType.smartStandardRemarkFormulaNormative]: {
since: "3.11.0",
baseBitType: BitType.formula,
description: "Smart standard normative remark formula bit, used for mathematical formulas in smart standards remarks that are normative"
},
[BitType.appCodeCell]: {
since: "1.4.3",
baseBitType: BitType.code,
description: "App code cell, used to create code cells in the app editor"
},
[BitType.appCodeEditor]: {
since: "1.4.3",
baseBitType: BitType.code,
description: "App code editor, used to create code editors in the app editor"
},
[BitType.appCodeIde]: {
since: "1.4.3",
baseBitType: BitType.code,
description: "App code IDE, used to create code IDEs in the app editor"
},
[BitType.codeRuntime]: {
since: "1.4.3",
baseBitType: BitType.code,
description: "Code runtime, used to define the runtime environment for code snippets"
},
[BitType.consoleLog]: {
since: "1.4.3",
baseBitType: BitType.code,
description: "Console log, used to log messages to the console in code snippets"
},
[BitType.output]: {
since: "1.4.3",
baseBitType: BitType.code,
description: "Output bit, used to display output from code snippets"
},
[BitType.stdout]: {
since: "1.4.3",
baseBitType: BitType.code,
description: "Standard output bit, used to display standard output from code snippets"
},
[BitType.sandbox]: {
since: "4.10.0",
baseBitType: BitType.code,
description: "A sandbox bit, used to create a sandboxed environment for code execution"
},
[BitType.sandboxOutputJson]: {
since: "4.10.0",
baseBitType: BitType.code,
description: "A sandbox output JSON bit, used to display JSON output from sandboxed code"
},
[BitType.sandboxOutputMarkup]: {
since: "4.10.0",
baseBitType: BitType.code,
description: "A sandbox output markup bit, used to display markup output from sandboxed code"
},
[BitType.sandboxOutputRender]: {
since: "4.10.0",
baseBitType: BitType.code,
description: "A sandbox output render bit, used to display rendered output from sandboxed code"
},
[BitType.step]: {
since: "1.5.1",
baseBitType: BitType.article,
description: "Step bit, used to define steps in a process or guide"
},
[BitType.stepImageScreenshot]: {
since: "1.5.1",
baseBitType: BitType.image,
description: "Step image screenshot bit, used to display screenshots in steps"
},
[BitType.stepImageScreenshotWithPointer]: {
since: "1.5.1",
baseBitType: BitType.image,
description: "Step image screenshot with pointer bit, used to display screenshots with pointers in steps",
tags: [
{
key: ConfigKey.property_pointerTop,
description: "The top position of the pointer in the screenshot",
format: TagFormat.plainText,
minCount: 1,
maxCount: 1
},
{
key: ConfigKey.property_pointerLeft,
description: "The left position of the pointer in the screenshot",
format: TagFormat.plainText,
minCount: 1,
maxCount: 1
}
]
},
[BitType.milestone]: {
since: "1.20.0",
baseBitType: BitType.step,
description: "Milestone bit, used to define milestones in a process or guide"
},
[BitType.conversationLeft1]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Conversation left bit, used to create a conversation on the left side of the screen",
tags: [
{
key: ConfigKey.group_person,
exportJsonKey: { partner: { name: "$" } },
description: "Tags for the person in the conversation"
}
]
},
[BitType.conversationLeft1Scream]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Conversation left scream bit, used to create a conversation with a scream on the left side of the screen"
},
[BitType.conversationLeft1Thought]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Conversation left thought bit, used to create a conversation with a thought on the left side of the screen"
},
[BitType.conversationRight1]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Conversation right bit, used to create a conversation on the right side of the screen"
},
[BitType.conversationRight1Scream]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Conversation right scream bit, used to create a conversation with a scream on the right side of the screen"
},
[BitType.conversationRight1Thought]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Conversation right thought bit, used to create a conversation with a thought on the right side of the screen"
},
[BitType.clozeAndMultipleChoiceTextConversationLeft1]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Cloze and multiple choice text conversation left bit, used for conversations with cloze and multiple choice text on the left side of the screen"
},
[BitType.clozeAndMultipleChoiceTextConversationLeft1Scream]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Cloze and multiple choice text conversation left scream bit, used for conversations with cloze and multiple choice text and a scream on the left side of the screen"
},
[BitType.clozeAndMultipleChoiceTextConversationLeft1Thought]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Cloze and multiple choice text conversation left thought bit, used for conversations with cloze and multiple choice text and a thought on the left side of the screen"
},
[BitType.clozeAndMultipleChoiceTextConversationRight1]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Cloze and multiple choice text conversation right bit, used for conversations with cloze and multiple choice text on the right side of the screen"
},
[BitType.clozeAndMultipleChoiceTextConversationRight1Scream]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Cloze and multiple choice text conversation right scream bit, used for conversations with cloze and multiple choice text and a scream on the right side of the screen"
},
[BitType.clozeAndMultipleChoiceTextConversationRight1Thought]: {
since: "1.3.0",
baseBitType: BitType.conversationLeft1,
description: "Cloze and multiple choice text conversation right thought bit, used for conversations with cloze and multiple choice text and a thought on the right side of the screen"
},
[BitType.advertising]: {
since: "4.2.0",
baseBitType: BitType.article,
description: "General advertising content bit",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingAdvertorial]: {
since: "4.2.0",
baseBitType: BitType.article,
description: "Advertorial content bit (sponsored article)",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingCallToAction]: {
since: "4.2.0",
baseBitType: BitType.callToAction,
description: "Call-to-action for advertising",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingCallToActionMeeting]: {
since: "4.2.0",
baseBitType: BitType.callToAction,
description: "Call-to-action for meeting or appointment",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingBanner]: {
since: "4.2.0",
baseBitType: BitType.image,
description: "Banner advertisement bit",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingSkyscraper]: {
since: "4.2.0",
baseBitType: BitType.image,
description: "Skyscraper (vertical) advertisement bit",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingRectangle]: {
since: "4.2.0",
baseBitType: BitType.image,
description: "Rectangle advertisement bit",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingFullPage]: {
since: "4.2.0",
baseBitType: BitType.image,
description: "Full-page advertisement bit",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingHalfPage]: {
since: "4.2.0",
baseBitType: BitType.image,
description: "Half-page advertisement bit",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingQuarterPage]: {
since: "4.2.0",
baseBitType: BitType.image,
description: "Quarter-page advertisement bit",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingClassifiedPage]: {
since: "4.2.0",
baseBitType: BitType.image,
description: "Classifieds advertisement page bit",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingLandscape]: {
since: "4.2.0",
baseBitType: BitType.image,
description: "Landscape format advertisement bit",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.advertisingPortrait]: {
since: "4.2.0",
baseBitType: BitType.image,
description: "Portrait format advertisement bit",
tags: [
{
key: ConfigKey.group_advertisingCommon,
description: "Common advertising tags"
}
]
},
[BitType.cookPreparation]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook preparation bit"
},
[BitType.cookStep]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook step bit"
},
[BitType.cookIngredients]: {
since: "1.5.16",
baseBitType: BitType._standard,
description: "Cook ingredients bit, used to define ingredients for recipes",
tags: [
{
key: ConfigKey.group_technicalTerm,
description: "Tags for technical terms related to ingredients"
},
{
key: ConfigKey.property_servings,
jsonKey: "servings.servings",
exportJsonKey: { servings: { servings: "$" } },
description: "The number of servings for the ingredients",
format: TagFormat.number,
chain: [
{
key: ConfigKey.property_unit,
description: "The unit of measurement for the ingredients (singular)",
format: TagFormat.plainText
},
{
key: ConfigKey.property_unitPlural,
description: "The unit of measurement for the ingredients (plural)",
format: TagFormat.plainText
},
{
key: ConfigKey.property_unitAbbr,
description: "The abbreviation for the unit of measurement (singular)",
format: TagFormat.plainText
},
{
key: ConfigKey.property_unitAbbrPlural,
description: "The abbreviation for the unit of measurement (plural)",
format: TagFormat.plainText
},
{
key: ConfigKey.property_decimalPlaces,
description: "The number of decimal places for the ingredient quantity",
format: TagFormat.number,
defaultValue: "1"
},
{
key: ConfigKey.property_disableCalculation,
description: "If the ingredient quantity calculation should be disabled",
format: TagFormat.boolean
},
{
exportJsonKey: { hint: "$" },
key: ConfigKey.tag_hint,
description: "Hint for the ingredient, used to provide additional information",
format: TagFormat.plainText
}
]
}
],
cardSet: CardSetConfigKey.ingredients
},
[BitType.recipe]: {
since: "1.5.24",
baseBitType: BitType.cookIngredients,
description: "Recipe bit, used to define a recipe"
},
[BitType.cookRemark]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook remark bit, used to add remarks to recipes"
},
[BitType.cookVariation]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook variation bit, used to define variations of recipes"
},
[BitType.cookInsert]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook insert bit, used to insert additional content in recipes"
},
[BitType.cookNoteOnQuantity]: {
since: "3.27.0",
baseBitType: BitType.article,
description: "Cook note on quantity bit, used to add notes on ingredient quantities in recipes"
},
[BitType.cookArrangement]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook arrangement bit, used to arrange ingredients or steps in recipes"
},
[BitType.cookPracticeAdvise]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook practice advice bit, used to provide practical advice in recipes"
},
[BitType.cookPlate]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook plate bit, used to define the presentation of a dish"
},
[BitType.cookRecommendation]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook recommendation bit, used to recommend dishes or ingredients"
},
[BitType.cookPersonalRecommendation]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook personal recommendation bit, used to provide personal recommendations for dishes or ingredients"
},
[BitType.cookSideDrink]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook side drink bit, used to define side drinks for dishes"
},
[BitType.cookSideDish]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook side dish bit, used to define side dishes for main courses"
},
[BitType.cookTimer]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Cook timer bit, used to set timers for cooking steps"
},
[BitType.document]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Document bit, used to define documents in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Tags for resource bits, used to categorize documents"
},
{
key: ConfigKey.group_resourceDocument,
description: "Tags for document resources, used to define documents in articles or books",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.documentDownload]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Document download bit, used to define downloadable documents in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Tags for resource bits, used to categorize downloadable documents"
},
{
key: ConfigKey.group_resourceDocumentDownload,
description: "Tags for downloadable document resources, used to define downloadable documents in articles or books",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.leDocumentDownload]: {
since: "3.18.0",
baseBitType: BitType.documentDownload,
description: "LE Document download bit, used to define downloadable documents in learning experiences"
},
[BitType.documentEmbed]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Document embed bit, used to embed documents in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Tags for resource bits, used to categorize embedded documents"
},
{
key: ConfigKey.group_resourceDocumentEmbed,
description: "Tags for embedded document resources, used to define embedded documents in articles or books",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.documentLink]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Document link bit, used to link to documents in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Tags for resource bits, used to categorize document links"
},
{
key: ConfigKey.group_resourceDocumentLink,
description: "Tags for document link resources, used to define links to documents in articles or books",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.essay]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Essay bit, used to define essays in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common tags for quiz bits"
},
{
key: ConfigKey.property_reasonableNumOfChars,
description: "The reasonable number of characters for the essay",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_sampleSolution,
description: "Sample solution for the essay, used as a reference answer",
format: TagFormat.plainText
},
{
key: ConfigKey.property_partialAnswer,
description: "Partial answer for the essay, used to indicate incomplete answers",
format: TagFormat.plainText
},
{
key: ConfigKey.property_reference,
description: "Reference for the essay, used to link to external resources",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
// Override inherited @example from group_standardTags: emits both
// `isExample` (bool) AND `example` (paragraph array). Declared as
// bitmarkText because BPG `JsonGenerator.toExample` renders the
// root-example value through the bit's textFormat (not this tag's
// declared format) — `TagFormat.plainText` here would be ignored
// at runtime and is misleading.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: "true" } },
{ "@absent": { isExample: true, example: "true" } },
{ isExample: true, example: "$" }
],
description: "The example text for the essay bit",
format: TagFormat.bitmarkText,
nullable: true
}
],
rootExampleType: ExampleType.string
},
[BitType.coachSelfReflectionEssay]: {
since: "1.3.0",
baseBitType: BitType.essay,
description: "Coach self-reflection essay bit"
},
[BitType.coachCallToActionEssay]: {
since: "1.3.0",
baseBitType: BitType.essay,
description: "Coach call to action essay bit"
},
[BitType.example]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Example bit, used to provide examples in articles or books",
tags: [
{
maxCount: 2,
jsonKey: "title|multi(count=2, key=subtitle)",
exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }],
key: ConfigKey.tag_title,
description: "The title of the example"
},
{
// Override inherited @example from group_standardTags: string root example.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: "true" } },
{ "@absent": { isExample: true, example: "true" } },
{ isExample: true, example: "$" }
],
description: "The example text for the example bit",
format: TagFormat.plainText,
nullable: true
}
],
rootExampleType: ExampleType.string
},
[BitType.exampleAlt]: {
since: "1.16.0",
baseBitType: BitType.example,
description: "Alternative example bit, used to provide alternative examples in articles or books"
},
[BitType.standardExampleNormative]: {
since: "1.16.0",
baseBitType: BitType.example,
description: "Standard normative example bit, used to provide normative examples in articles or books"
},
[BitType.standardExampleNonNormative]: {
since: "1.16.0",
baseBitType: BitType.example,
description: "Standard non-normative example bit, used to provide non-normative examples in articles or books"
},
[BitType.smartStandardExampleNormative]: {
since: "1.28.0",
baseBitType: BitType.standardExampleNormative,
description: "Smart standard normative example bit, used to provide normative examples in smart standards"
},
[BitType.smartStandardExampleNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardExampleNonNormative,
description: "Smart standard non-normative example bit, used to provide non-normative examples in smart standards"
},
[BitType.smartStandardExampleNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardExampleNormative,
description: "Smart standard normative example collapsible bit, used to provide normative examples in smart standards that can be collapsed",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardExampleNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardExampleNonNormative,
description: "Smart standard non-normative example collapsible bit, used to provide non-normative examples in smart standards that can be collapsed",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.authorContentBitGenerator]: {
since: "4.2.0",
baseBitType: BitType.article,
description: "Body contains the content of one or more bits to be created by the bit generator",
tags: [
{
key: ConfigKey.property_classification,
description: "Classification for the created bits",
format: TagFormat.plainText,
maxCount: Count.infinity
}
]
},
[BitType.appAiPrompt]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "App AI prompt bit, used to create AI prompts in the app editor"
},
[BitType.aiPrompt]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "AI prompt bit, used to create AI prompts in articles or books"
},
[BitType.aiChat]: {
since: "4.2.0",
baseBitType: BitType.article,
description: "AI chat bit, used to create an AI chat"
},
[BitType.aiEditor]: {
since: "4.2.0",
baseBitType: BitType.article,
description: "AI editor bit, used to create an AI editor"
},
[BitType.aiTutor]: {
since: "4.2.0",
baseBitType: BitType.article,
description: "AI tutor bit, used to create an AI tutor"
},
[BitType.aiWbt]: {
since: "4.2.0",
baseBitType: BitType.article,
description: "AI WBT bit, used to create an AI WBT (Web-Based Training)"
},
[BitType.articleAi]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Article AI bit, used to create AI-generated articles",
tags: [
{
// Shadows the inherited @aiGenerated from the standard group: this
// bit type forces aiGenerated:true regardless of source value or
// absence. Literal `true` in the export pattern ignores `$`.
key: ConfigKey.property_aiGenerated,
description: "AI-generated flag, forced true for article-ai bits",
format: TagFormat.boolean,
defaultValue: "true",
exportJsonKey: { aiGenerated: true }
}
]
},
[BitType.articleAttachment]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Article attachment bit, used to attach files to articles"
},
[BitType.assignment]: {
since: "1.3.0",
baseBitType: BitType.essay,
description: "Assignment bit, used to define assignments in articles or books"
},
[BitType.audioTranscript]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Audio transcript bit, used to provide transcripts for audio files in articles or books"
},
[BitType.bitmarkExample]: {
since: "1.3.0",
baseBitType: BitType.example,
description: "Bitmark example bit, used to provide examples for bitmarks in articles or books"
},
[BitType.blogArticle]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Blog article bit, used to define blog articles in articles or books"
},
[BitType.bug]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Bug bit, used to report bugs in articles or books"
},
[BitType.bugAlt]: {
since: "1.16.0",
baseBitType: BitType.bug,
description: "Alternative bug bit, used to report bugs in articles or books with alternative styling"
},
[BitType.checklist]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Checklist bit, used to create checklists in articles or books"
},
[BitType.coachAudioTranscript]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Coach audio transcript bit, used to provide transcripts for audio files in coaching articles or books"
},
[BitType.coachCallToActionChecklist]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Coach call to action checklist bit, used to create checklists in coaching articles or books"
},
[BitType.coachHomeRules]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Coach home rules bit, used to define home rules in coaching articles or books"
},
[BitType.coachVideoTranscript]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Coach video transcript bit, used to provide transcripts for video files in coaching articles or books"
},
[BitType.correction]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Correction bit, used to provide corrections in articles or books"
},
[BitType.danger]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Danger bit, used to highlight dangerous or critical information in articles or books"
},
[BitType.dangerAlt]: {
since: "1.16.0",
baseBitType: BitType.article,
description: "Alternative danger bit, used to highlight dangerous or critical information in articles or books with alternative styling"
},
[BitType.definitionTerm]: {
since: "1.34.0",
baseBitType: BitType.article,
description: "Definition term bit, used to define terms in articles or books"
},
[BitType.deleted]: {
since: "3.9.0",
baseBitType: BitType.article,
description: "Deleted bit, used to indicate deleted content in articles or books"
},
[BitType.details1]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Details bit, used to provide additional details in articles or books"
},
[BitType.details]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Details bit, used to provide additional details in articles or books"
},
[BitType.qAndA]: {
since: "3.5.0",
baseBitType: BitType.article,
description: "Q&A bit, used to create question and answer sections in articles or books"
},
[BitType.editorial]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Editorial bit, used to provide editorial content in articles or books"
},
[BitType.editorNote]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Editor note bit, used to provide notes from the editor in articles or books"
},
[BitType.featured]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Featured bit, used to highlight featured content in articles or books"
},
[BitType.glossaryTerm]: {
since: "1.33.0",
baseBitType: BitType.article,
description: "Glossary term bit, used to define glossary terms in articles or books"
},
[BitType.help]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Help bit, used to provide help or support information in articles or books"
},
[BitType.helpAlt]: {
since: "1.16.0",
baseBitType: BitType.help,
description: "Alternative help bit, used to provide help or support information in articles or books with alternative styling"
},
[BitType.hint]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Hint bit, used to provide hints or tips in articles or books"
},
[BitType.hintAlt]: {
since: "1.16.0",
baseBitType: BitType.hint,
description: "Alternative hint bit, used to provide hints or tips in articles or books with alternative styling"
},
[BitType.indexTerm]: {
since: "1.33.0",
baseBitType: BitType.article,
description: "Index term bit, used to define index terms in articles or books"
},
[BitType.info]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Info bit, used to provide informational content in articles or books"
},
[BitType.infoAlt]: {
since: "1.16.0",
baseBitType: BitType.info,
description: "Alternative info bit, used to provide informational content in articles or books with alternative styling"
},
[BitType.langLearningOutcomes]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language learning outcomes bit, used to define learning outcomes in language articles or books"
},
[BitType.langEnablingLanguageSkills]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language enabling skills bit, used to define enabling language skills in language articles or books"
},
[BitType.langLifeSkills]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language life skills bit, used to define life skills in language articles or books"
},
[BitType.langEnglishAroundWorld]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language English around the world bit, used to provide information about English usage around the world in language articles or books"
},
[BitType.langGoodToKnow]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language good to know bit, used to provide useful information in language articles or books"
},
[BitType.langLearningGoal]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language learning goal bit, used to define learning goals in language articles or books"
},
[BitType.langLearningStrategy]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language learning strategy bit, used to define learning strategies in language articles or books"
},
[BitType.langLikeALocal]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language like a local bit, used to provide tips for speaking like a local in language articles or books"
},
[BitType.langMaterial]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language material bit, used to provide language learning materials in language articles or books"
},
[BitType.langUsefulPhrases]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language useful phrases bit, used to provide useful phrases in language articles or books"
},
[BitType.langLevelDown]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language level down bit, used to provide information about lower language levels in language articles or books"
},
[BitType.langLevelUp]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language level up bit, used to provide information about higher language levels in language articles or books"
},
[BitType.langExtraActivity]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language extra activity bit, used to provide additional activities in language articles or books"
},
[BitType.langVideoScript]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language video script bit, used to provide scripts for language learning videos in articles or books"
},
[BitType.langAudioScript]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language audio script bit, used to provide scripts for language learning audio files in articles or books"
},
[BitType.langVocabulary]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language vocabulary bit, used to provide vocabulary lists in language articles or books"
},
[BitType.langHomework]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language homework bit, used to define homework assignments in language articles or books"
},
[BitType.langTeacherNote]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language teacher note bit, used to provide notes for teachers in language articles or books"
},
[BitType.langTeacherPronunciation]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Language teacher pronunciation bit, used to provide pronunciation guidance for teachers in language articles or books"
},
[BitType.list]: {
since: "1.22.0",
baseBitType: BitType.article,
description: "List bit, used to create lists in articles or books"
},
[BitType.standardList]: {
since: "1.22.0",
baseBitType: BitType.article,
description: "Standard list bit, used to create standard lists in articles or books"
},
[BitType.smartStandardList]: {
since: "1.28.0",
baseBitType: BitType.standardList,
description: "Smart standard list bit, used to create smart standard lists in articles or books",
// PLAN-140 refs family (NISO import): a reference list is a STRUCTURAL
// bit (no @children — it takes a path ordinal anchor and its <ref>
// children promote to sibling list-item bits); its <title> rides [#]
// via the title tag key.
mappingKeys: {
"xml-niso-iec": { "@el": "ref-list", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
}
},
[BitType.smartStandardListCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardList,
description: "Smart standard list collapsible bit, used to create collapsible smart standard lists in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.message]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Message bit, used to create messages in articles or books"
},
[BitType.newspaperArticle]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Newspaper article bit, used to define newspaper articles in articles or books"
},
[BitType.note]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Note bit, used to provide notes in articles or books"
},
[BitType.noteAlt]: {
since: "1.16.0",
baseBitType: BitType.note,
description: "Alternative note bit, used to provide notes in articles or books with alternative styling"
},
[BitType.standardNoteNormative]: {
since: "1.16.0",
baseBitType: BitType.note,
description: "Standard normative note bit, used to provide normative notes in articles or books"
},
[BitType.standardNoteNonNormative]: {
since: "1.16.0",
baseBitType: BitType.note,
description: "Standard non-normative note bit, used to provide non-normative notes in articles or books"
},
[BitType.smartStandardNoteNormative]: {
since: "1.28.0",
baseBitType: BitType.standardNoteNormative,
description: "Smart standard normative note bit, used to provide normative notes in smart standards"
},
[BitType.smartStandardNoteNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardNoteNonNormative,
description: "Smart standard non-normative note bit, used to provide non-normative notes in smart standards",
// PLAN-140 W3 (NISO import, tranche 1). CONTENT key (@children): a
// note's inner paragraphs are note body, never separate bits.
mappingKeys: {
"xml-niso-iec": {
"@el": "non-normative-note",
"@attr": { id: "$customerId" },
"@children": "$",
"\u25BC": "$anchor"
}
}
},
[BitType.smartStandardNoteNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardNoteNormative,
description: "Smart standard normative note collapsible bit, used to provide normative notes in smart standards that can be collapsed",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardNoteNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardNoteNonNormative,
description: "Smart standard non-normative note collapsible bit, used to provide non-normative notes in smart standards that can be collapsed",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.noteAi]: {
since: "1.3.0",
baseBitType: BitType.note,
description: "Note AI bit, used to create AI-generated notes in articles or books",
tags: [
{
// Shadows the inherited @aiGenerated from the standard group: this
// bit type forces aiGenerated:true regardless of source value or
// absence. Literal `true` in the export pattern ignores `$`.
key: ConfigKey.property_aiGenerated,
description: "AI-generated flag, forced true for article-ai bits",
format: TagFormat.boolean,
defaultValue: "true",
exportJsonKey: { aiGenerated: true }
}
]
},
[BitType.notebookArticle]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Notebook article bit, used to define notebook articles in articles or books"
},
[BitType.preparationNote]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Preparation note bit, used to provide preparation notes in articles or books"
},
[BitType.printThisBook]: {
since: "5.4.0",
baseBitType: BitType._standard,
description: "Print this book bit, used to create a button that prints the entire book",
tags: [
{
key: ConfigKey.property_buttonCaption,
description: "The caption of the print button",
format: TagFormat.plainText
}
]
},
[BitType.printThisChapter]: {
since: "5.4.0",
baseBitType: BitType._standard,
description: "Print this chapter bit, used to create a button that prints the current chapter",
tags: [
{
key: ConfigKey.property_buttonCaption,
description: "The caption of the print button",
format: TagFormat.plainText
},
{
key: ConfigKey.property_printParentChapterLevel,
description: "The parent chapter level to print",
format: TagFormat.number,
defaultValue: "-1"
}
]
},
[BitType.printPageBreak]: {
since: "5.4.0",
baseBitType: BitType.separator,
description: "Print page break bit, used to create page breaks for printing in articles or books"
},
[BitType.releaseNotesSummary]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Release notes summary bit, used to summarize release notes in articles or books"
},
[BitType.remark]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Remark bit, used to provide remarks in articles or books"
},
[BitType.remarkAlt]: {
since: "1.16.0",
baseBitType: BitType.remark,
description: "Alternative remark bit, used to provide remarks in articles or books with alternative styling"
},
[BitType.standardRemarkNormative]: {
since: "1.16.0",
baseBitType: BitType.article,
description: "Standard normative remark bit, used to provide normative remarks in articles or books"
},
[BitType.standardRemarkNonNormative]: {
since: "1.16.0",
baseBitType: BitType.article,
description: "Standard non-normative remark bit, used to provide non-normative remarks in articles or books"
},
[BitType.smartStandardRemarkNormative]: {
since: "1.28.0",
baseBitType: BitType.standardRemarkNormative,
description: "Smart standard normative remark bit, used to provide normative remarks in smart standards"
},
[BitType.smartStandardRemarkNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardRemarkNonNormative,
description: "Smart standard non-normative remark bit, used to provide non-normative remarks in smart standards",
// PLAN-140 Q4 rider: a warning/caution note routes to the remark bit —
// the same element as the note key, distinguished by the content-type
// attr literal (alias value: forward emits the first alternative,
// reverse accepts any). Most-specific-wins beats the plain note key.
mappingKeys: {
"xml-niso-iec": {
"@el": "non-normative-note",
"@attr": { "content-type": "warning|caution", id: "$customerId" },
"@children": "$",
"\u25BC": "$anchor"
}
}
},
[BitType.smartStandardRemarkNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardRemarkNormative,
description: "Smart standard normative remark collapsible bit, used to provide normative remarks in smart standards that can be collapsed",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardRemarkNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardRemarkNonNormative,
description: "Smart standard non-normative remark collapsible bit, used to provide non-normative remarks in smart standards that can be collapsed",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.selfAssessment]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Self-assessment bit, used to create self-assessment sections in articles or books"
},
[BitType.separator]: {
since: "1.4.15",
baseBitType: BitType.article,
description: "Separator bit, used to create visual separators in articles or books"
},
[BitType.separatorAlt]: {
since: "1.16.0",
baseBitType: BitType.separator,
description: "Alternative separator bit, used to create visual separators in articles or books with alternative styling"
},
[BitType.sticker]: {
since: "1.5.28",
baseBitType: BitType.article,
description: "Sticker bit, used to create stickers in articles or books"
},
[BitType.sideNote]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Side note bit, used to provide side notes in articles or books"
},
[BitType.summary]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Summary bit, used to provide summaries in articles or books"
},
[BitType.summaryAi]: {
since: "1.3.0",
baseBitType: BitType.summary,
description: "AI-generated summary bit",
tags: [
{
// Shadows the inherited @aiGenerated from the standard group: this
// bit type forces aiGenerated:true regardless of source value or
// absence. Literal `true` in the export pattern ignores `$`.
key: ConfigKey.property_aiGenerated,
description: "AI-generated flag, forced true for article-ai bits",
format: TagFormat.boolean,
defaultValue: "true",
exportJsonKey: { aiGenerated: true }
}
]
},
[BitType.videoTranscript]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Video transcript bit, used to provide transcripts for video files in articles or books"
},
[BitType.warning]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Warning bit, used to highlight warnings in articles or books"
},
[BitType.warningAlt]: {
since: "1.16.0",
baseBitType: BitType.warning,
description: "Alternative warning bit, used to highlight warnings in articles or books with alternative styling"
},
[BitType.workbookArticle]: {
since: "1.3.0",
baseBitType: BitType.article,
description: "Workbook article bit, used to define workbook articles in articles or books"
},
[BitType.collapsible]: {
since: "1.21.0",
deprecated: "5.37.0",
baseBitType: BitType.article,
description: "Collapsible bit, used to create collapsible sections in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.sideNoteCollapsible]: {
since: "1.21.0",
deprecated: "5.37.0",
baseBitType: BitType.sideNote,
description: "Side note collapsible bit, used to create collapsible side notes in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.infoCollapsible]: {
since: "1.21.0",
deprecated: "5.37.0",
baseBitType: BitType.info,
description: "Info collapsible bit, used to create collapsible informational sections in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.remarkCollapsible]: {
since: "1.21.0",
deprecated: "5.37.0",
baseBitType: BitType.remark,
description: "Remark collapsible bit, used to create collapsible remarks in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.warningCollapsible]: {
since: "1.21.0",
deprecated: "5.37.0",
baseBitType: BitType.warning,
description: "Warning collapsible bit, used to create collapsible warnings in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.dangerCollapsible]: {
since: "1.21.0",
deprecated: "5.37.0",
baseBitType: BitType.danger,
description: "Danger collapsible bit, used to create collapsible danger sections in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.noteCollapsible]: {
since: "1.21.0",
deprecated: "5.37.0",
baseBitType: BitType.note,
description: "Note collapsible bit, used to create collapsible notes in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.exampleCollapsible]: {
since: "1.21.0",
deprecated: "5.37.0",
baseBitType: BitType.example,
description: "Example collapsible bit, used to create collapsible examples in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.hintCollapsible]: {
since: "1.21.0",
deprecated: "5.37.0",
baseBitType: BitType.hint,
description: "Hint collapsible bit, used to create collapsible hints in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.bugCollapsible]: {
since: "1.21.0",
deprecated: "5.37.0",
baseBitType: BitType.bug,
description: "Bug collapsible bit, used to create collapsible bug reports in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.platformPath]: {
since: "3.14.1",
baseBitType: BitType._standard,
description: "Platform path bit, used to define paths in the platform",
tags: [
{
key: ConfigKey.property_path,
description: "The path for the platform, used to define navigation paths",
format: TagFormat.plainText
}
]
},
[BitType.appCalculatorLatex]: {
since: "4.10.0",
baseBitType: BitType.article,
description: "A LaTeX calculator bit, used to create a calculator that can interpret LaTeX expressions",
tags: [
{
key: ConfigKey.property_formula,
description: "The LaTeX formula for the calculator, used to define the calculation logic",
format: TagFormat.plainText
}
]
},
[BitType.container]: {
since: "1.9.0",
baseBitType: BitType.article,
description: "Container bit, used to group bits together in articles or books",
tags: [
{
key: ConfigKey.property_allowedBit,
description: "Allowed bits in the container, used to define which bits can be placed inside",
format: TagFormat.plainText,
maxCount: Count.infinity
}
]
},
[BitType.containerWrap]: {
since: "1.9.0",
baseBitType: BitType.container,
description: "Container wrap bit, used to wrap content in a container"
},
[BitType.containerNowrap]: {
since: "1.9.0",
baseBitType: BitType.container,
description: "Container nowrap bit, used to prevent wrapping of content in a container"
},
[BitType.containerNowrapStretch]: {
since: "1.9.0",
baseBitType: BitType.container,
description: "Container nowrap stretch bit, used to stretch content without wrapping in a container"
},
[BitType.containerGroup]: {
since: "1.9.0",
baseBitType: BitType.container,
description: "Container group bit, used to group multiple containers together"
},
[BitType.containerFolder]: {
since: "1.9.0",
baseBitType: BitType.container,
description: "Container folder bit, used to create folders for organizing bits"
},
[BitType.containerCarousel]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container carousel bit, used to create carousels for displaying bits"
},
[BitType.containerCards]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container cards bit, used to create card layouts for displaying bits"
},
[BitType.containerGrid]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container grid bit, used to create grid layouts for displaying bits"
},
[BitType.containerStack]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container stack bit, used to create stacked layouts for displaying bits"
},
[BitType.containerSlides]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container slides bit, used to create slide presentations for displaying bits"
},
[BitType.containerGallery]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container gallery bit, used to create galleries for displaying bits"
},
[BitType.containerScroller]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container scroller bit, used to create scrolling sections for displaying bits"
},
[BitType.containerTabs]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container tabs bit, used to create tabbed sections for displaying bits"
},
[BitType.containerAccordionTabs]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container accordion tabs bit, used to create accordion-style tabbed sections for displaying bits"
},
[BitType.containerFolderAll]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container folder all bit, used to create folders that can contain all types of bits"
},
[BitType.containerBits2]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container bits 2 bit, used to create a container for bits with additional features"
},
[BitType.containerCookRecipe]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container cook recipe bit, used to create recipe containers for displaying cooking recipes"
},
[BitType.containerNewsArticle]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container news article bit, used to create containers for news articles"
},
[BitType.containerPreview]: {
since: "1.11.0",
baseBitType: BitType.container,
description: "Container preview bit, used to create preview sections for displaying bits"
},
[BitType.pageContainer]: {
since: "1.9.0",
baseBitType: BitType.container,
description: "Page container bit, used to create containers for pages in articles or books"
},
[BitType.pageContainerWrap]: {
since: "1.9.0",
baseBitType: BitType.pageContainer,
description: "Page container wrap bit, used to wrap content in a page container"
},
[BitType.pageContainerNowrap]: {
since: "1.9.0",
baseBitType: BitType.pageContainer,
description: "Page container nowrap bit, used to prevent wrapping of content in a page container"
},
[BitType.pageContainerNowrapStretch]: {
since: "1.9.0",
baseBitType: BitType.pageContainer,
description: "Page container nowrap stretch bit, used to stretch content without wrapping in a page container"
},
[BitType.pageContainerFolder]: {
since: "1.9.0",
baseBitType: BitType.pageContainer,
description: "Page container folder bit, used to create folders for organizing bits in a page container"
},
[BitType.pageContainerGroup]: {
since: "1.9.0",
baseBitType: BitType.pageContainer,
description: "Page container group bit, used to group multiple page containers together"
},
[BitType.metalevelExplanation]: {
since: "1.10.0",
baseBitType: BitType.article,
description: "Metalevel explanation bit, used to provide explanations for metalevels in articles or books"
},
[BitType.module]: {
since: "1.5.26",
baseBitType: BitType.article,
description: "Module bit, used to define modules in articles or books",
tags: [
{
key: ConfigKey.property_hasBookNavigation,
description: "Indicates if the module has book navigation",
format: TagFormat.boolean,
defaultValue: "true"
},
{
key: ConfigKey.property_productId,
description: "Product ID for the module, used to link to a specific product",
format: TagFormat.plainText,
minCount: 1,
maxCount: Count.infinity
},
{
key: ConfigKey.property_duration,
description: "Duration of the module, used to indicate how long it takes to complete",
format: TagFormat.plainText
}
]
},
[BitType.moduleProduct]: {
since: "1.9.0",
baseBitType: BitType.module,
description: "Module product bit, used to define products in modules",
tags: [
{
key: ConfigKey.property_productId,
description: "Product ID for the module product, used to link to a specific product",
format: TagFormat.plainText,
minCount: 1,
maxCount: 1
}
]
},
[BitType.exampleList]: {
since: "1.4.13",
baseBitType: BitType.example,
description: "Example list bit, used to create lists of examples in articles or books",
cardSet: CardSetConfigKey.exampleBitList,
rootExampleType: ExampleType.string
},
[BitType.extractorPage]: {
since: "1.5.17",
baseBitType: BitType.image,
description: "Extractor page bit, used to extract pages from images"
},
[BitType.extractorPageCollapsible]: {
since: "1.30.0",
deprecated: "5.37.0",
baseBitType: BitType.extractorPage,
description: "Collapsible extractor page bit, used to extract pages from images with collapsible functionality",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.extractorPageWithBlocks]: {
since: "1.5.21",
baseBitType: BitType.image,
description: "Extractor page with blocks bit, used to extract pages with blocks from images"
},
[BitType.extractorPageWithBlocksCollapsible]: {
since: "1.30.0",
deprecated: "5.37.0",
baseBitType: BitType.extractorPageWithBlocks,
description: "Collapsible extractor page with blocks bit, used to extract pages with blocks from images with collapsible functionality",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.extractorConfiguration]: {
since: "1.7.1",
baseBitType: BitType._standard,
description: "Extractor configuration bit, used to configure extractors in articles or books",
textFormatDefault: TextFormat.plainText
},
[BitType.extractorBlueprintConfiguration]: {
since: "5.11.0",
baseBitType: BitType._standard,
description: "Extractor blueprint configuration bit, used to specify extractor blueprint configuration when extracting from blueprints",
textFormatDefault: TextFormat.plainText
},
[BitType.extractorImage]: {
since: "4.3.0",
baseBitType: BitType._standard,
description: "Extractor images bit, used for images extracted from PDFs",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for logo grave images, used to define additional properties"
},
{
// Image resource — fully-expanded shape so each `&image` emission
// produces a complete `{type, image: {src}}` object inside the
// `images[]` array. Chain attrs like `[@zoomDisabled]` fold under
// `images[].image` via the `[]` last-element-merge semantic.
key: ConfigKey.group_resourceImageNoZoom,
description: "Resource image tags for logo grave images, used to attach images",
exportJsonKey: { images: [{ type: "image", image: { src: "$" } }] },
minCount: 1,
maxCount: Count.infinity
}
],
resourceAttachmentAllowed: false,
textFormatDefault: TextFormat.plainText
},
[BitType.extractorImageCollapsible]: {
since: "4.3.0",
deprecated: "5.37.0",
baseBitType: BitType.extractorImage,
description: "Collapsible extractor images bit, used for images extracted from PDFs",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.extractorBlueprint]: {
since: "5.12.0",
baseBitType: BitType._standard,
description: "Extractor blueprint bit, used to provide blueprint information about extractors",
textFormatDefault: TextFormat.json,
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for the blueprint reference image"
},
{
key: ConfigKey.group_resourceImage,
description: "Optional uncropped reference page image, used so authoring tools can compute crops against the original page dimensions"
}
],
resourceAttachmentAllowed: false
},
[BitType.extractorInformation]: {
since: "3.8.0",
baseBitType: BitType._standard,
description: "Extractor information bit, used to provide information about extractors in articles or books",
textFormatDefault: TextFormat.json
},
[BitType.extractorTheme]: {
since: "5.7.0",
baseBitType: BitType._standard,
description: "Extractor theme bit, used to store design/theme JSON extracted from documents",
textFormatDefault: TextFormat.json
},
[BitType.extractorReconcile]: {
since: "5.35.0",
baseBitType: BitType._standard,
description: "Extractor reconcile bit, used to store PDF-compare reconciliation decisions (config and issues) in JSON",
textFormatDefault: TextFormat.json
},
[BitType.extractorAiChat]: {
since: "3.19.0",
baseBitType: BitType._standard,
description: "Extractor AI chat bit, used to create AI chat interfaces for extractors in articles or books"
},
[BitType.extractorBlock]: {
since: "1.5.16",
baseBitType: BitType._standard,
description: "Extractor block bit, used to define blocks within extractor pages",
tags: [
{
key: ConfigKey.property_blockId,
description: "Unique identifier for the block, used to reference the block in extractor pages",
format: TagFormat.plainText
},
{
key: ConfigKey.property_pageNo,
description: "Page number for the block, used to indicate the page on which the block appears",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_x,
description: "X-coordinate for the block, used to position the block on the page",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_y,
description: "Y-coordinate for the block, used to position the block on the page",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_width,
description: "Width of the block, used to define the size of the block on the page",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_height,
description: "Height of the block, used to define the size of the block on the page",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_index,
description: "Index of the block, used to order blocks within the extractor page",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_classification,
description: "Classification for the block, used to categorize the block within the extractor",
format: TagFormat.plainText
},
{
key: ConfigKey.property_availableClassifications,
description: "Available classifications for the block, used to define possible classifications",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.group_resourceImage,
description: "Resource image for the block, used to attach images to the block",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.extractorRepeatedText]: {
since: "1.5.21",
baseBitType: BitType.article,
description: "Extractor repeated text bit, used to define repeated text in extractor pages"
},
[BitType.extractorRule]: {
since: "5.17.0",
baseBitType: BitType._standard,
description: "Extractor rule bit, used to define extraction rules with reference images and instructions",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for extraction rule images"
},
{
key: ConfigKey.group_resourceImage,
description: "Resource image tags for the rule reference image",
minCount: 1
}
],
resourceAttachmentAllowed: false,
textFormatDefault: TextFormat.plainText
},
[BitType.extractorPageNumber]: {
since: "1.5.21",
baseBitType: BitType.article,
description: "Extractor page number bit, used to define page numbers in extractor pages"
},
[BitType.extractorPageNumberCollapsible]: {
since: "1.30.0",
deprecated: "5.37.0",
baseBitType: BitType.extractorPageNumber,
description: "Collapsible extractor page number bit, used to define page numbers in extractor pages with collapsible functionality",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.extractorPageHeader]: {
since: "1.5.21",
baseBitType: BitType.article,
description: "Extractor page header bit, used to define headers in extractor pages"
},
[BitType.extractorPageHeaderCollapsible]: {
since: "1.30.0",
deprecated: "5.37.0",
baseBitType: BitType.extractorPageHeader,
description: "Collapsible extractor page header bit, used to define headers in extractor pages with collapsible functionality",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.extractorPageFooter]: {
since: "1.5.21",
baseBitType: BitType.article,
description: "Extractor page footer bit, used to define footers in extractor pages"
},
[BitType.extractorPageFooterCollapsible]: {
since: "1.30.0",
deprecated: "5.37.0",
baseBitType: BitType.extractorPageFooter,
description: "Collapsible extractor page footer bit, used to define footers in extractor pages with collapsible functionality",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.pageOpenBook]: {
since: "1.5.10",
baseBitType: BitType.article,
description: "Page open book bit, used to create pages that open books in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the book, used to identify the book in the system",
format: TagFormat.plainText
},
{
key: ConfigKey.property_book,
description: "Book reference for the page, used to link to a specific book",
format: TagFormat.plainText,
chain: [
{
exportJsonKey: { reference: "$" },
key: ConfigKey.tag_reference,
format: TagFormat.plainText,
description: "Reference tag for the book, used to link to the book in the system",
maxCount: 1,
chain: [
{
key: ConfigKey.tag_reference,
jsonKey: "referenceEnd",
exportJsonKey: { referenceEnd: "$" },
format: TagFormat.plainText,
description: "End reference tag for the book, used to specify a range",
maxCount: 1
}
]
}
]
},
{
/* Allow incorrectly chained reference tag */
exportJsonKey: { reference: "$" },
key: ConfigKey.tag_reference,
format: TagFormat.plainText,
description: "Reference tag for the book, used to link to the book in the system"
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the button, used to define the text displayed on the button"
},
{
key: ConfigKey.property_authorFullName,
description: "Full name of the author",
format: TagFormat.plainText
},
{
key: ConfigKey.property_authorPseudonym,
description: "A pseudonym of the author, if any",
format: TagFormat.plainText
},
{
key: ConfigKey.property_authorTitle,
description: 'The title of the author, e.g. "Dr.", "Prof.", etc.',
format: TagFormat.plainText
},
{
key: ConfigKey.property_authorJobTitle,
description: 'The job title of the author, e.g. "Software Engineer", "Professor", etc.',
format: TagFormat.plainText
}
]
},
[BitType.printBook]: {
since: "5.5.0",
baseBitType: BitType.article,
description: "Print book bit, used to create pages that open books without author information in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the book, used to identify the book in the system",
format: TagFormat.plainText
},
{
key: ConfigKey.property_book,
description: "Book reference for the page, used to link to a specific book",
format: TagFormat.plainText,
chain: [
{
exportJsonKey: { reference: "$" },
key: ConfigKey.tag_reference,
format: TagFormat.plainText,
description: "Reference tag for the book, used to link to the book in the system",
maxCount: 1,
chain: [
{
key: ConfigKey.tag_reference,
jsonKey: "referenceEnd",
exportJsonKey: { referenceEnd: "$" },
format: TagFormat.plainText,
description: "End reference tag for the book, used to specify a range",
maxCount: 1
}
]
}
]
},
{
/* Allow incorrectly chained reference tag */
exportJsonKey: { reference: "$" },
key: ConfigKey.tag_reference,
format: TagFormat.plainText,
description: "Reference tag for the book, used to link to the book in the system",
maxCount: 1,
chain: [
{
key: ConfigKey.tag_reference,
jsonKey: "referenceEnd",
exportJsonKey: { referenceEnd: "$" },
format: TagFormat.plainText,
description: "End reference tag for the book, used to specify a range",
maxCount: 1
}
]
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the button, used to define the text displayed on the button"
}
]
},
[BitType.openBookChapter]: {
since: "4.16.0",
baseBitType: BitType.pageOpenBook,
description: "Open book chapter bit, derived from page-open-book, used to create chapter pages that open books"
},
[BitType.openBookChapterTeaser]: {
since: "4.16.0",
baseBitType: BitType.openBookChapter,
description: "Open book chapter teaser bit, equal to open-book-chapter, used to create teaser chapter pages that open books"
},
[BitType.pageOpenBookList]: {
since: "2.1.0",
baseBitType: BitType.article,
description: "Page open book list bit, used to create pages that open lists of books in articles or books",
tags: [
{
key: ConfigKey.property_book,
// Starter-array shape: each `[@book:value]` chain appends a fresh
// `{ book, reference }` entry. The chained `►` (tag_reference) lands
// inside the most-recently-appended entry as `reference: $`.
exportJsonKey: { book: [{ book: "$" }] },
description: "Book reference for the page, used to link to a specific book",
maxCount: Count.infinity,
chain: [
{
exportJsonKey: { reference: "$" },
key: ConfigKey.tag_reference,
format: TagFormat.plainText,
description: "Reference tag for the book, used to link to the book in the system",
maxCount: 1,
chain: [
{
key: ConfigKey.tag_reference,
jsonKey: "referenceEnd",
exportJsonKey: { referenceEnd: "$" },
format: TagFormat.plainText,
description: "End reference tag for the book, used to specify a range",
maxCount: 1
}
]
}
]
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the button, used to define the text displayed on the button",
format: TagFormat.plainText
}
]
},
[BitType.pageSubscribe]: {
since: "1.5.10",
baseBitType: BitType.article,
description: "Page subscribe bit, used to create pages that allow users to subscribe to content",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the subscription page, used to identify the page in the system",
format: TagFormat.plainText
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the subscription button, used to define the text displayed on the button",
format: TagFormat.plainText
},
{
key: ConfigKey.property_mailingList,
description: "Mailing list for the subscription, used to link to a specific mailing list",
format: TagFormat.plainText
}
]
},
[BitType.assignmentList]: {
since: "1.4.13",
baseBitType: BitType.exampleList,
description: "Assignment list bit, used to create lists of assignments in articles or books"
},
[BitType.pageFooter]: {
since: "1.4.13",
baseBitType: BitType.article,
description: "Page footer bit, used to create footers in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the footer, used to identify the footer in the system",
format: TagFormat.plainText
}
],
// page-footer emits cards under `sections` (vs `listItems` for the
// sibling exampleList / assignmentList bits) — see JsonGenerator.ts.
cardSet: CardSetConfigKey.pageFooterSections
},
[BitType.legend]: {
since: "3.12.0",
baseBitType: BitType._standard,
description: "Legend bit, used to provide legends in articles or books",
cardSet: CardSetConfigKey.definitionList
},
[BitType.smartStandardLegend]: {
since: "3.12.0",
baseBitType: BitType.legend,
description: "Smart standard legend bit, used to provide smart standard legends in articles or books"
},
[BitType.smartStandardLegendNonNormative]: {
since: "3.12.0",
baseBitType: BitType.legend,
description: "Smart standard non-normative legend bit, used to provide non-normative smart standard legends in articles or books"
},
[BitType.smartStandardLegendNormative]: {
since: "3.12.0",
baseBitType: BitType.legend,
description: "Smart standard normative legend bit, used to provide normative smart standard legends in articles or books",
// PLAN-141 F2 (NISO import): a free-standing <legend> (a formula's) or
// bare <def-list> (a table footer's — no <legend> wrapper there) that is
// NOT the claimed card content of its enclosing bit is a legend bit:
// STRUCTURAL-shaped, its <title> and <def-item>s build the
// definition-list cards via the card set's authored keys. A <legend>
// inside a figure/table bit stays that bit's claimed card content and
// never reaches this key. Anchor = the source id (D2 revised).
mappingKeys: {
"xml-niso-iec": [
{ "@el": "legend", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" },
{ "@el": "def-list", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
]
}
},
[BitType.smartStandardRemarkLegend]: {
since: "3.12.0",
baseBitType: BitType.legend,
description: "Smart standard remark legend bit, used to provide smart standard remarks in legends"
},
[BitType.smartStandardRemarkLegendNonNormative]: {
since: "3.12.0",
baseBitType: BitType.legend,
description: "Smart standard non-normative remark legend bit, used to provide non-normative smart standard remarks in legends"
},
[BitType.smartStandardRemarkLegendNormative]: {
since: "3.12.0",
baseBitType: BitType.legend,
description: "Smart standard normative remark legend bit, used to provide normative smart standard remarks in legends"
},
[BitType.definitionList]: {
since: "1.34.0",
baseBitType: BitType._standard,
description: "Definition list bit, used to create lists of definitions in articles or books",
tags: [
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
],
cardSet: CardSetConfigKey.definitionList
},
[BitType.definitionListCollapsible]: {
since: "5.25.0",
deprecated: "5.37.0",
baseBitType: BitType.definitionList,
description: "Collapsible definition list bit, used to create collapsible lists of definitions in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.standardDefinitionListNormative]: {
since: "5.24.0",
baseBitType: BitType.definitionList,
description: "Standard normative definition list bit, used to create normative standard definition lists in articles or books"
},
[BitType.standardDefinitionListNonNormative]: {
since: "5.24.0",
baseBitType: BitType.definitionList,
description: "Standard non-normative definition list bit, used to create non-normative standard definition lists in articles or books"
},
[BitType.smartStandardDefinitionListNormative]: {
since: "5.24.0",
baseBitType: BitType.standardDefinitionListNormative,
description: "Smart standard normative definition list bit, used to create normative smart standard definition lists in articles or books",
// PLAN-141 F3 (NISO import): a terms-and-definitions clause. STRUCTURAL-
// shaped: <label> → [%], the tbx:termEntry/langSet subtree builds the
// definition-list card via the card set's authored keys (all tig terms
// stacked on the term side; definition/example/notes/source composed on
// the definition side with authored locale affixes), langSet @xml:lang →
// [@lang] (nested attr slot on the tag key). Anchor = the source id.
mappingKeys: {
"xml-niso-iec": { "@el": "term-sec", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
}
},
[BitType.smartStandardDefinitionListNonNormative]: {
since: "5.24.0",
baseBitType: BitType.standardDefinitionListNonNormative,
description: "Smart standard non-normative definition list bit, used to create non-normative smart standard definition lists in articles or books"
},
[BitType.metaSearchDefaultTerms]: {
since: "3.12.0",
baseBitType: BitType._standard,
description: "Meta search default terms bit, used to define default search terms in articles",
cardSet: CardSetConfigKey.definitionListPlain
},
[BitType.metaSearchDefaultTopics]: {
since: "3.12.0",
baseBitType: BitType.metaSearchDefaultTerms,
description: "Meta search default topics bit, used to define default search topics in articles"
},
[BitType.flashcard]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Flashcard bit, used to create flashcards in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for flashcards"
},
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
],
cardSet: CardSetConfigKey.flashcard
},
[BitType.flashcard1]: {
since: "1.3.0",
baseBitType: BitType.flashcard,
description: "Flashcard 1 bit",
// PLAN-085: use the flashcard1 cardset (sections.default.maxCount=1)
// instead of inheriting `flashcard`'s unbounded cardset, since this
// bit type structurally allows only a single card.
cardSet: CardSetConfigKey.flashcard1
},
[BitType.qAndACard]: {
since: "3.25.0",
baseBitType: BitType.flashcard1,
description: "Q&A card bit, used to create question and answer cards in articles or books",
// PLAN-085: q-and-a-card permits multiple cards (some fixtures carry
// two). Override the inherited `flashcard1` (max=1) cardset back to
// the unbounded `flashcard` cardset.
cardSet: CardSetConfigKey.flashcard
},
[BitType.focusImage]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Focus image bit, used to create images with focus points in articles or books",
tags: [
{
key: ConfigKey.property_focusX,
description: "X-coordinate for the focus point, used to define the focus area in the image",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_focusY,
description: "Y-coordinate for the focus point, used to define the focus area in the image",
format: TagFormat.number,
nullable: true
}
]
},
[BitType.highlightText]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Highlight text bit, used to create highlighted text in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for highlighted text"
},
{
// Dedicated highlight inline-select chain: tag_true / tag_false
// emit `{type:"highlight", attrs:{texts:[{text,isCorrect?}]}}`
// inline body nodes via @body-inline. Bit shares no tag IDs with
// multiple-choice / multiple-choice-1 (which use group_trueFalse).
key: ConfigKey.group_trueFalseInlineHighlight,
description: "True/False quiz tags for highlighted text (inline body nodes)"
},
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
[BitType.image]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Image bit, used to create images in articles or books",
tags: [
{
key: ConfigKey.group_backgroundWallpaper,
description: "Background wallpaper tags for images, used to define background properties for images"
},
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for images, used to define additional properties for images"
},
{
key: ConfigKey.group_resourceImage,
description: "Resource image tags for images, used to attach images to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.appCreateBitsFromImage]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "App create bits from image bit, used to create bits from images in the app"
},
[BitType.appGetScreenshot]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "App get screenshot bit, used to capture screenshots in the app"
},
[BitType.detailsImage]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Details image bit, used to create detailed images in articles or books"
},
[BitType.figure]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Figure bit, used to create figures in articles or books",
cardSet: CardSetConfigKey.definitionList
},
[BitType.imageBanner]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Image banner bit, used to create banners in articles or books"
},
[BitType.imageFigure]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Image figure bit, used to create figures with images in articles or books",
cardSet: CardSetConfigKey.definitionList
},
[BitType.imageFigureAlt]: {
since: "1.16.0",
baseBitType: BitType.imageFigure,
description: "Alternative image figure bit, used to create figures with alternative images in articles or books"
},
[BitType.standardImageFigureNormative]: {
since: "1.16.0",
baseBitType: BitType.imageFigure,
description: "Standard normative image figure bit, used to create normative image figures in articles or books"
},
[BitType.standardImageFigureNonNormative]: {
since: "1.16.0",
baseBitType: BitType.imageFigure,
description: "Standard non-normative image figure bit, used to create non-normative image figures in articles or books"
},
[BitType.smartStandardImageFigureNormative]: {
since: "1.28.0",
baseBitType: BitType.standardImageFigureNormative,
description: "Smart standard normative image figure bit, used to create smart standard normative image figures in articles or books",
// PLAN-141 F1 (NISO import): a figure is STRUCTURAL-shaped (no
// @children — no body): <label> → [%], <caption>/<graphic> ride the
// [&image] resource chain, the <legend> def-list becomes the bit's
// definition-list cards, and an inner note PROMOTES as a sibling bit.
// Anchor = the source id (D2 revised).
mappingKeys: {
"xml-niso-iec": { "@el": "fig", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
}
},
[BitType.smartStandardImageFigureNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardImageFigureNonNormative,
description: "Smart standard non-normative image figure bit, used to create smart standard non-normative image figures in articles or books"
},
[BitType.smartStandardImageFigureNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardImageFigureNormative,
description: "Collapsible smart standard normative image figure bit, used to create collapsible smart standard normative image figures in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardImageFigureNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardImageFigureNonNormative,
description: "Collapsible smart standard non-normative image figure bit, used to create collapsible smart standard non-normative image figures in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.imageLandscape]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Image landscape bit, used to create landscape images in articles or books"
},
[BitType.imageMood]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Image mood bit, used to create mood images in articles or books"
},
[BitType.imagePortrait]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Image portrait bit, used to create portrait images in articles or books"
},
[BitType.imagePrototype]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Image prototype bit, used to create prototype images in articles or books"
},
[BitType.imageSeparator]: {
since: "1.4.15",
baseBitType: BitType._standard,
description: "Image separator bit, used to create separators in articles or books",
tags: [
{
key: ConfigKey.group_imageNoZoom,
description: "Image no zoom tags for images, used to define whether images should have zoom functionality"
}
],
resourceAttachmentAllowed: false
},
[BitType.imageSeparatorAlt]: {
since: "1.16.0",
baseBitType: BitType.imageSeparator,
description: "Alternative image separator bit, used to create alternative separators in articles or books"
},
[BitType.imageScreenshot]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Image screenshot bit, used to create screenshots in articles or books"
},
[BitType.imageStyled]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Image styled bit, used to create styled images in articles or books"
},
[BitType.imageSuperWide]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Image super wide bit, used to create super wide images in articles or books"
},
[BitType.imageZoom]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Image zoom bit, used to create zoomable images in articles or books"
},
[BitType.langLifeSkillIcon]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Language life skill icon bit, used to create icons for life skills in articles or books"
},
[BitType.lifeSkillSticker]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Life skill sticker bit, used to create stickers for life skills in articles or books"
},
[BitType.pageBanner]: {
since: "1.4.3",
baseBitType: BitType._standard,
description: "Page banner bit, used to create banners for pages in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the page banner, used to identify the banner in the system",
format: TagFormat.plainText
},
{
key: ConfigKey.group_imageNoZoom,
description: "Image no zoom tags for images, used to define whether images should have zoom functionality"
}
],
resourceAttachmentAllowed: false
},
[BitType.pageHero]: {
since: "1.11.0",
baseBitType: BitType.pageBanner,
description: "Page hero bit, used to create hero sections in articles or books"
},
[BitType.screenshot]: {
since: "1.3.0",
deprecated: "1.4.0",
baseBitType: BitType.image,
description: "Screenshot bit, used to create screenshots in articles or books"
},
[BitType.tableImage]: {
since: "1.5.15",
baseBitType: BitType.tableExtended,
description: "Table image bit, used to create images in tables in articles or books",
tags: [
{
key: ConfigKey.property_caption,
description: "Caption for the table image, used to provide a description for the image",
format: TagFormat.bitmarkText,
// HTML-10 / HTML-C2: render as the table's `<caption>` (see table bit).
htmlKey: { "@el": "caption", "@children": "$" }
},
{
key: ConfigKey.group_backgroundWallpaper,
description: "Background wallpaper tags for images, used to define background properties for images"
},
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for images, used to define additional properties for images"
},
{
key: ConfigKey.group_resourceImage,
description: "Resource image tags for images, used to attach images to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.tableImageAlt]: {
since: "1.16.0",
baseBitType: BitType.tableImage,
description: "Alternative table image bit, used to create alternative images in tables in articles or books"
},
[BitType.standardTableImageNormative]: {
since: "1.16.0",
baseBitType: BitType.tableImage,
description: "Standard normative table image bit, used to create normative images in tables in articles or books"
},
[BitType.standardTableImageNonNormative]: {
since: "1.16.0",
baseBitType: BitType.tableImage,
description: "Standard non-normative table image bit, used to create non-normative images in tables in articles or books"
},
[BitType.standardRemarkTableImageNormative]: {
since: "1.17.0",
baseBitType: BitType.tableImage,
description: "Standard normative remark table image bit, used to create normative remark images in tables in articles or books"
},
[BitType.standardRemarkTableImageNonNormative]: {
since: "1.17.0",
baseBitType: BitType.tableImage,
description: "Standard non-normative remark table image bit, used to create non-normative remark images in tables in articles or books"
},
[BitType.standardTableExtendedImageNormative]: {
since: "1.16.0",
baseBitType: BitType.tableExtendedImage,
description: "Standard normative extended table image bit, used to create normative extended images in tables in articles or books"
},
[BitType.standardTableExtendedImageNonNormative]: {
since: "1.16.0",
baseBitType: BitType.tableExtendedImage,
description: "Standard non-normative extended table image bit, used to create non-normative extended images in tables in articles or books"
},
[BitType.standardRemarkTableExtendedImageNormative]: {
since: "1.17.0",
baseBitType: BitType.tableExtendedImage,
description: "Standard normative remark extended table image bit, used to create normative remark extended images in tables in articles or books"
},
[BitType.standardRemarkTableExtendedImageNonNormative]: {
since: "1.17.0",
baseBitType: BitType.tableExtendedImage,
description: "Standard non-normative remark extended table image bit, used to create non-normative remark extended images in tables in articles or books"
},
[BitType.smartStandardTableImageNormative]: {
since: "1.28.0",
baseBitType: BitType.standardTableImageNormative,
description: "Smart standard normative table image bit, used to create smart standard normative images in tables in articles or books"
},
[BitType.smartStandardTableImageNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardTableImageNonNormative,
description: "Smart standard non-normative table image bit, used to create smart standard non-normative images in tables in articles or books"
},
[BitType.smartStandardRemarkTableImageNormative]: {
since: "1.28.0",
baseBitType: BitType.standardRemarkTableImageNormative,
description: "Smart standard normative remark table image bit, used to create smart standard normative remark images in tables in articles or books"
},
[BitType.smartStandardRemarkTableImageNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardRemarkTableImageNonNormative,
description: "Smart standard non-normative remark table image bit, used to create smart standard non-normative remark images in tables in articles or books"
},
[BitType.smartStandardTableExtendedImageNormative]: {
since: "1.28.0",
baseBitType: BitType.standardTableExtendedImageNormative,
description: "Smart standard normative extended table image bit, used to create smart standard normative extended images in tables in articles or books"
},
[BitType.smartStandardTableExtendedImageNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardTableExtendedImageNonNormative,
description: "Smart standard non-normative extended table image bit, used to create smart standard non-normative extended images in tables in articles or books"
},
[BitType.smartStandardRemarkTableExtendedImageNormative]: {
since: "1.28.0",
baseBitType: BitType.standardRemarkTableExtendedImageNormative,
description: "Smart standard normative remark extended table image bit, used to create smart standard normative remark extended images in tables in articles or books"
},
[BitType.smartStandardRemarkTableExtendedImageNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardRemarkTableExtendedImageNonNormative,
description: "Smart standard non-normative remark extended table image bit, used to create smart standard non-normative remark extended images in tables in articles or books"
},
[BitType.smartStandardTableImageNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardTableImageNormative,
description: "Collapsible smart standard normative table image bit, used to create collapsible smart standard normative images in tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardTableImageNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardTableImageNonNormative,
description: "Collapsible smart standard non-normative table image bit, used to create collapsible smart standard non-normative images in tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardRemarkTableImageNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardRemarkTableImageNormative,
description: "Collapsible smart standard normative remark table image bit, used to create collapsible smart standard normative remark images in tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardRemarkTableImageNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardRemarkTableImageNonNormative,
description: "Collapsible smart standard non-normative remark table image bit, used to create collapsible smart standard non-normative remark images in tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardTableExtendedImageNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardTableExtendedImageNormative,
description: "Collapsible smart standard normative extended table image bit, used to create collapsible smart standard normative extended images in tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardTableExtendedImageNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardTableExtendedImageNonNormative,
description: "Collapsible smart standard non-normative extended table image bit, used to create collapsible smart standard non-normative extended images in tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardRemarkTableExtendedImageNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardRemarkTableExtendedImageNormative,
description: "Collapsible smart standard normative remark extended table image bit, used to create collapsible smart standard normative remark extended images in tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardRemarkTableExtendedImageNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardRemarkTableExtendedImageNonNormative,
description: "Collapsible smart standard non-normative remark extended table image bit, used to create collapsible smart standard non-normative remark extended images in tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.imageLink]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Image link bit, used to create links with images in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for image links, used to define additional properties"
},
{
key: ConfigKey.group_resourceImageLink,
description: "Resource image link tags, used to attach images to the link",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.imageOnDevice]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Image on device bit, used to create images stored on the device in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for images on device, used to define additional properties"
},
{
key: ConfigKey.group_resourceImage,
description: "Resource image tags for images on device, used to attach images"
},
{
key: ConfigKey.group_imageSource,
description: "Image source tags for images on device, used to define the source of the image"
}
],
resourceAttachmentAllowed: false
},
[BitType.imageResponsive]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Responsive image bit, used to create responsive images in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for responsive images, used to define additional properties"
},
{
// Combo resource
key: ConfigKey.group_resourceImageResponsive,
description: "Resource image responsive tags, used to attach responsive images"
}
],
resourceAttachmentAllowed: false
},
[BitType.imagesLogoGrave]: {
since: "1.5.11",
baseBitType: BitType._standard,
description: "Logo grave images bit, used to create logo grave images in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for logo grave images, used to define additional properties"
},
{
// Image resource — fully-expanded shape (see BitType.extractorImage).
key: ConfigKey.group_resourceImageNoZoom,
description: "Resource image tags for logo grave images, used to attach images",
exportJsonKey: { logos: [{ type: "image", image: { src: "$" } }] },
minCount: 1,
maxCount: Count.infinity
}
],
resourceAttachmentAllowed: false
},
[BitType.prototypeImages]: {
since: "1.6.1",
baseBitType: BitType.imagesLogoGrave,
description: "Prototype images bit, used to create prototype images in articles or books",
tags: [
{
// Override inherited `logos` wrapper with `images` for this bit type.
key: ConfigKey.group_resourceImageNoZoom,
description: "Resource image tags for prototype images",
exportJsonKey: { images: [{ type: "image", image: { src: "$" } }] },
minCount: 1,
maxCount: Count.infinity
}
]
},
[BitType.internalLink]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Internal link bit, used to create links to other bits in articles or books",
tags: [
{
exportJsonKey: { reference: "$" },
key: ConfigKey.tag_reference,
format: TagFormat.plainText,
description: "Reference tag for the internal link, used to link to the target bit"
}
]
},
[BitType.interview]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Interview bit, used to create interviews in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for interviews"
},
{
key: ConfigKey.property_reasonableNumOfChars,
exportJsonKey: {},
description: "Reasonable number of characters for the interview, used to limit input size",
format: TagFormat.number
},
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
],
cardSet: CardSetConfigKey.questions
},
[BitType.interviewInstructionGrouped]: {
since: "1.3.0",
baseBitType: BitType.interview,
description: "Grouped interview instruction bit"
},
[BitType.botInterview]: {
since: "1.3.0",
baseBitType: BitType.interview,
description: "Bot interview bit, used to create bot interviews in articles or books"
},
[BitType.brandColor]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Brand color bit, used to define brand colors",
tags: [
{
key: ConfigKey.property_brandColor,
description: "The brand color",
format: TagFormat.plainText
},
{
key: ConfigKey.property_brandColorName,
description: "The brand color name",
format: TagFormat.plainText
}
]
},
[BitType.learningPathBook]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Learning path book bit, used to create learning paths in articles or books",
tags: [
{
key: ConfigKey.group_learningPathCommon,
description: "Common tags for learning path books, used to define properties of learning paths"
}
]
},
[BitType.bookLink]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Book link bit, used to create links to books in learning paths"
},
[BitType.bookLinkNext]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Next book link bit, used to create links to the next book in learning paths"
},
[BitType.bookLinkPrev]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Previous book link bit, used to create links to the previous book in learning paths"
},
[BitType.learningPathClassroomEvent]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path classroom event bit, used to create classroom events in learning paths"
},
[BitType.learningPathClassroomTraining]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path classroom training bit, used to create classroom training in learning paths"
},
[BitType.learningPathClosing]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path closing bit, used to close learning paths"
},
[BitType.learningPathFeedback]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path feedback bit, used to provide feedback in learning paths"
},
[BitType.learningPathLearningGoal]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path learning goal bit, used to define learning goals in learning paths"
},
[BitType.learningPathLti]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path LTI bit, used to integrate LTI tools in learning paths"
},
[BitType.learningPathSign]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path sign bit, used to create signs in learning paths"
},
[BitType.learningPathStep]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path step bit, used to create steps in learning paths"
},
[BitType.learningPathBotTraining]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path bot training bit, used to create bot training in learning paths",
tags: [
{
key: ConfigKey.property_bot,
description: "Bot reference for the training, used to link to a specific bot",
format: TagFormat.plainText
}
]
},
[BitType.learningPathExternalLink]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path external link bit, used to create external links in learning paths",
tags: [
{
key: ConfigKey.property_externalLink,
description: "External link for the learning path, used to link to external resources",
format: TagFormat.plainText
},
{
key: ConfigKey.property_externalLinkText,
description: "Text for the external link, used to define the link text displayed",
format: TagFormat.plainText
}
]
},
[BitType.learningPathVideoCall]: {
since: "1.3.0",
baseBitType: BitType.learningPathBook,
description: "Learning path video call bit, used to create video calls in learning paths",
tags: [
{
key: ConfigKey.property_videoCallLink,
description: "Video call link for the learning path, used to link to video calls",
format: TagFormat.plainText
}
]
},
[BitType.leLearningObjectives]: {
since: "1.25.0",
baseBitType: BitType.learningPathLearningGoal,
description: "Learning objectives bit, used to define learning objectives in learning paths",
tags: [
{
key: ConfigKey.property_activityType,
description: "Activity type for the learning objectives, used to categorize the activity",
format: TagFormat.plainText
}
]
},
[BitType.leVideoCall]: {
since: "1.25.0",
baseBitType: BitType.learningPathVideoCall,
description: "Video call bit, used to create video calls in learning paths",
tags: [
{
key: ConfigKey.property_activityType,
description: "Activity type for the video call, used to categorize the activity",
format: TagFormat.plainText
}
]
},
[BitType.leClassroomEvent]: {
since: "1.25.0",
baseBitType: BitType.learningPathClassroomEvent,
description: "Classroom event bit, used to create classroom events in learning paths",
tags: [
{
key: ConfigKey.property_activityType,
description: "Activity type for the classroom event, used to categorize the activity",
format: TagFormat.plainText
}
]
},
[BitType.leMultiDayEvent]: {
since: "3.32.0",
baseBitType: BitType.leClassroomEvent,
description: "Multi-day event bit, used to create multi-day events in learning paths"
},
[BitType.leCompletion]: {
since: "1.25.0",
baseBitType: BitType.learningPathClosing,
description: "Completion bit, used to mark the completion of learning paths",
tags: [
{
key: ConfigKey.property_activityType,
description: "Activity type for the completion, used to categorize the activity",
format: TagFormat.plainText
}
]
},
[BitType.leExternalLink]: {
since: "1.25.0",
baseBitType: BitType.learningPathExternalLink,
description: "External link bit, used to create external links in learning paths",
tags: [
{
key: ConfigKey.property_activityType,
description: "Activity type for the external link, used to categorize the activity",
format: TagFormat.plainText
}
]
},
[BitType.leReadBook]: {
since: "1.25.0",
baseBitType: BitType.learningPathBook,
description: "Read book bit, used to create reading tasks in learning paths",
tags: [
{
key: ConfigKey.property_activityType,
description: "Activity type for the reading task, used to categorize the activity",
format: TagFormat.plainText
}
]
},
[BitType.leLearningStep]: {
since: "1.25.0",
baseBitType: BitType.learningPathStep,
description: "Learning step bit, used to create steps in learning paths",
tags: [
{
key: ConfigKey.property_activityType,
description: "Activity type for the learning step, used to categorize the activity",
format: TagFormat.plainText
}
]
},
[BitType.lePreparationTask]: {
since: "1.26.0",
baseBitType: BitType.learningPathBook,
description: "Preparation task bit, used to create preparation tasks in learning paths",
tags: [
{
key: ConfigKey.property_activityType,
format: TagFormat.plainText,
description: "Activity type for the preparation task, used to categorize the activity"
}
]
},
[BitType.leRead]: {
since: "1.27.0",
baseBitType: BitType.lePreparationTask,
description: "Read task bit, used to create reading tasks in learning paths"
},
[BitType.leTask]: {
since: "1.27.0",
baseBitType: BitType.lePreparationTask,
description: "Task bit, used to create tasks in learning paths"
},
[BitType.leTodo]: {
since: "1.27.0",
baseBitType: BitType.lePreparationTask,
description: "Todo bit, used to create todo tasks in learning paths"
},
[BitType.leFollowUpTask]: {
since: "1.27.0",
baseBitType: BitType.lePreparationTask,
description: "Follow-up task bit, used to create follow-up tasks in learning paths"
},
[BitType.leFinishingTask]: {
since: "1.27.0",
baseBitType: BitType.lePreparationTask,
description: "Finishing task bit, used to create finishing tasks in learning paths"
},
[BitType.leAssignment]: {
since: "1.27.0",
baseBitType: BitType.lePreparationTask,
description: "Assignment bit, used to create assignments in learning paths"
},
[BitType.leWatchVideoEmbed]: {
since: "1.27.0",
baseBitType: BitType.learningPathBook,
description: "Watch video embed bit, used to embed videos in learning paths",
tags: [
{
key: ConfigKey.property_activityType,
description: "Activity type for the video embed, used to categorize the activity",
format: TagFormat.plainText
},
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for video embeds, used to define additional properties"
},
{
key: ConfigKey.group_resourceVideoEmbed,
description: "Resource video embed tags, used to attach video embeds to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.leListenAudioEmbed]: {
since: "1.27.0",
baseBitType: BitType.learningPathBook,
description: "Listen audio embed bit, used to embed audio in learning paths",
tags: [
{
key: ConfigKey.property_activityType,
description: "Activity type for the audio embed, used to categorize the activity",
format: TagFormat.plainText
},
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for audio embeds, used to define additional properties"
},
{
key: ConfigKey.group_resourceAudioEmbed,
description: "Resource audio embed tags, used to attach audio embeds to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.listItem]: {
since: "1.22.0",
baseBitType: BitType.article,
description: "List item bit, used to create list items in articles or books",
tags: [
{
key: ConfigKey.property_listItemIndent,
description: "Indentation level for the list item, used to define the hierarchy of the list",
format: TagFormat.number,
defaultValue: "0"
}
]
},
[BitType.standardListItem]: {
since: "1.22.0",
baseBitType: BitType.listItem,
description: "Standard list item bit, used to create standard list items in articles or books"
},
[BitType.smartStandardListItem]: {
since: "1.28.0",
baseBitType: BitType.standardListItem,
description: "Smart standard list item bit, used to create smart standard list items in articles or books",
// PLAN-140 refs family (NISO import): one list-item bit per <ref> —
// CONTENT key (its citation subtree is the body), counter anchor scoped
// to the containing list, <label> → [%], nested <std-id> → [@externalId]
// (the externalId tag key's nested content slot).
mappingKeys: {
"xml-niso-iec": {
"@el": "ref",
"@attr": { id: "$customerId" },
"@children": "$",
"\u25BC": "$anchor"
}
}
},
[BitType.smartStandardListItemCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardListItem,
description: "Collapsible smart standard list item bit, used to create collapsible smart standard list items in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.mark]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Mark bit, used to create marks in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for marks"
},
{
key: ConfigKey.group_markConfig,
description: "Configuration tags for marks, used to define properties of marks"
},
{
key: ConfigKey.group_mark,
description: "Mark tags, used to define the content of marks"
},
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
[BitType.match]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Match bit, used to create matching pairs in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for matching pairs"
},
{
key: ConfigKey.property_isCaseSensitive,
exportJsonKey: {},
description: "Case sensitivity for matching pairs, used to define if matches are case-sensitive",
format: TagFormat.boolean,
defaultValue: "true"
},
{
// Override inherited @example: in `isTopLevelExample` allow-list
// (covers match, matchAll, matchReverse, matchAllReverse,
// matchSolutionGrouped, matchMatrix, matchAudio, matchPicture).
// Bit-level fires emit only `isExample`; the @example value (when
// provided) is recorded in the ancestor stack and consumed by
// per-pair/per-cell variant overrides — not surfaced at bit scope.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
],
cardSet: CardSetConfigKey.matchPairs
},
[BitType.matchAll]: {
since: "1.3.0",
baseBitType: BitType.match,
description: "Match all bit, used to create matching pairs with all options in articles or books"
},
[BitType.matchReverse]: {
since: "1.3.0",
baseBitType: BitType.match,
description: "Match reverse bit, used to create matching pairs with reversed options in articles or books"
},
[BitType.matchAllReverse]: {
since: "1.3.0",
baseBitType: BitType.match,
description: "Match all reverse bit, used to create matching pairs with all options and reversed in articles or books"
},
[BitType.matchSolutionGrouped]: {
since: "1.3.0",
baseBitType: BitType.match,
description: "Grouped match solution bit, used to create grouped matching solutions in articles or books"
},
[BitType.matchMatrix]: {
since: "1.3.0",
baseBitType: BitType.match,
description: "Match matrix bit, used to create matching pairs in a matrix format in articles or books",
cardSet: CardSetConfigKey.matchMatrix
},
[BitType.matchAudio]: {
since: "1.3.0",
baseBitType: BitType.match,
description: "Match audio bit, used to create matching pairs with audio in articles or books",
cardSet: CardSetConfigKey.matchAudioPairs
},
[BitType.matchPicture]: {
since: "1.3.0",
baseBitType: BitType.match,
description: "Match picture bit, used to create matching pairs with pictures in articles or books",
cardSet: CardSetConfigKey.matchImagePairs
},
[BitType.feedback]: {
since: "3.13.0",
baseBitType: BitType._standard,
description: "Feedback bit, used to provide feedback in articles or books",
tags: [
{
key: ConfigKey.property_reasonableNumOfChars,
exportJsonKey: {},
description: "Reasonable number of characters for feedback, used to limit input size",
format: TagFormat.number
}
],
cardSet: CardSetConfigKey.feedback
},
[BitType.learningDocumentationFeedback]: {
since: "3.13.0",
baseBitType: BitType.feedback,
description: "Feedback for learning documentation"
},
[BitType.handInFeedbackExpert]: {
since: "3.30.0",
baseBitType: BitType.feedback,
description: "Feedback for expert hand-in"
},
[BitType.handInFeedbackSelf]: {
since: "3.30.0",
baseBitType: BitType.feedback,
description: "Self-feedback for hand-in"
},
[BitType.multipleChoice1]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Multiple choice 1 bit, used to create single-choice questions in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for multiple choice 1 questions"
},
{
key: ConfigKey.group_trueFalse,
description: "True/False tags for multiple choice 1 questions"
},
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
[BitType.coachSelfReflectionMultipleChoice1]: {
since: "1.3.0",
baseBitType: BitType.multipleChoice1,
description: "Coach self-reflection multiple choice 1 bit, used for self-reflection in coaching"
},
[BitType.multipleChoice]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Multiple choice bit, used to create multiple-choice questions in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for multiple choice questions"
},
{
key: ConfigKey.group_trueFalse,
description: "True/False tags for multiple choice questions"
},
// This is actually for multiple-choice-1, but we support it here as well (as many bits are wrong)
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
],
cardSet: CardSetConfigKey.quiz
},
[BitType.coachSelfReflectionMultipleChoice]: {
since: "1.3.0",
baseBitType: BitType.multipleChoice,
description: "Coach self-reflection multiple choice bit, used for self-reflection in coaching"
},
[BitType.multipleChoiceText]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Multiple choice text bit, used to create multiple-choice questions with text in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for multiple choice text questions"
},
{
key: ConfigKey.group_trueFalseInlineSelect,
description: "True/False tags for multiple choice text questions"
},
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
[BitType.coachCallToActionMultipleChoiceText]: {
since: "1.3.0",
baseBitType: BitType.multipleChoiceText,
description: "Coach call to action multiple choice text bit, used for self-reflection in coaching"
},
[BitType.coachSelfReflectionMultipleChoiceText]: {
since: "1.3.0",
baseBitType: BitType.multipleChoiceText,
description: "Coach self-reflection multiple choice text bit, used for self-reflection in coaching"
},
[BitType.multipleResponse1]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Multiple response 1 bit, used to create multiple-response questions in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for multiple response 1 questions"
},
{
key: ConfigKey.group_trueFalseResponses,
description: "True/False tags for multiple response 1 questions"
},
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
]
},
[BitType.coachSelfReflectionMultipleResponse1]: {
since: "1.3.0",
baseBitType: BitType.multipleResponse1,
description: "Coach self-reflection multiple response 1 bit, used for self-reflection in coaching"
},
[BitType.multipleResponse]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Multiple response bit, used to create multiple-response questions in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for multiple response questions"
},
{
key: ConfigKey.group_trueFalseResponses,
description: "True/False tags for multiple response questions"
},
// This is actually for multiple-response-1, but we support it here as well (as many bits are wrong)
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
],
cardSet: CardSetConfigKey.quizResponses
},
[BitType.coachSelfReflectionMultipleResponse]: {
since: "1.3.0",
baseBitType: BitType.multipleResponse,
description: "Coach self-reflection multiple response bit, used for self-reflection in coaching"
},
[BitType.page]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Page bit, used to create pages in articles or books",
tags: [
{
maxCount: 2,
jsonKey: "title|multi(count=2, key=subtitle)",
exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }],
key: ConfigKey.tag_title,
description: "Title of the page, used to display the page title"
},
{
key: ConfigKey.property_thumbImage,
description: "Thumbnail image for the page, used to display a small image representation",
format: TagFormat.plainText
}
]
},
[BitType.pageCollapsible]: {
since: "1.30.0",
deprecated: "5.37.0",
baseBitType: BitType.page,
description: "Collapsible page bit, used to create collapsible sections in pages",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.pageCoverImage]: {
since: "1.22.0",
baseBitType: BitType.page,
description: "Page cover image bit, used to create cover images for pages"
},
[BitType.advertisingAdvertorialPage]: {
since: "4.5.0",
baseBitType: BitType.page,
description: "Advertorial Page bit, used to create advertorial pages"
},
[BitType.pageBuyButton]: {
since: "1.4.3",
baseBitType: BitType._standard,
description: "Page buy button bit, used to create buy buttons on pages",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the page, used to identify the page in URLs",
format: TagFormat.plainText
},
{
key: ConfigKey.property_content2Buy,
description: "Content to buy for the page, used to define what is being sold",
format: TagFormat.plainText
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the buy button, used to define the text displayed on the button",
format: TagFormat.plainText
}
]
},
[BitType.pageBuyButtonAlt]: {
since: "1.31.0",
baseBitType: BitType.pageBuyButton,
description: "Alternative page buy button bit, used to create alternative buy buttons on pages"
},
[BitType.pageBuyButtonPromotion]: {
since: "1.5.11",
baseBitType: BitType.pageBuyButton,
description: "Page buy button for promotions, used to create buy buttons on promotional pages"
},
[BitType.pageSubpage]: {
since: "1.6.6",
baseBitType: BitType._standard,
description: "Page subpage bit, used to create subpages in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the subpage, used to identify the subpage in URLs",
format: TagFormat.plainText
},
{
key: ConfigKey.property_page,
description: "Page reference for the subpage, used to link to the parent page",
format: TagFormat.plainText
}
]
},
[BitType.pageShopInShop]: {
since: "1.6.6",
baseBitType: BitType.pageSubpage,
description: "Page shop-in-shop bit, used to create shop-in-shop pages in articles or books"
},
[BitType.pageCategory]: {
since: "1.6.6",
baseBitType: BitType.pageSubpage,
description: "Page category bit, used to create category pages in articles or books"
},
[BitType.pageAcademy]: {
since: "1.6.6",
baseBitType: BitType.pageSubpage,
description: "Page academy bit, used to create academy pages in articles or books"
},
[BitType.pagePromotion]: {
since: "1.6.6",
baseBitType: BitType.pageSubpage,
description: "Page promotion bit, used to create promotional pages in articles or books"
},
[BitType.pageSpecial]: {
since: "1.6.6",
baseBitType: BitType.pageSubpage,
description: "Page special bit, used to create special pages in articles or books"
},
[BitType.pageEnd]: {
since: "5.33.0",
baseBitType: BitType.article,
description: "Page end bit, used to mark the end of a page section in articles or books"
},
[BitType.pageStart]: {
since: "5.33.0",
baseBitType: BitType.article,
description: "Page start bit, used to mark the start of a page section in articles or books"
},
[BitType.pagePerson]: {
since: "1.5.16",
baseBitType: BitType._standard,
description: "Page person bit, used to create person pages in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the person page, used to identify the person in URLs",
format: TagFormat.plainText
},
{
key: ConfigKey.group_person,
description: "Person tags, used to define properties of the person"
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the button on the person page, used to define the text displayed",
format: TagFormat.plainText
}
]
},
[BitType.pageProduct]: {
since: "1.4.17",
baseBitType: BitType._standard,
description: "Page product bit, used to create product pages in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the product page, used to identify the product in URLs",
format: TagFormat.plainText
},
{
key: ConfigKey.property_product,
description: "Product reference for the product page, used to link to the product",
format: TagFormat.plainText,
nullable: true
}
]
},
[BitType.pageProductList]: {
since: "1.4.17",
baseBitType: BitType._standard,
description: "Page product list bit, used to create product lists in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the product list page, used to identify the product list in URLs",
format: TagFormat.plainText
},
// NOTE: Only one of productVideo or productVideoList should be used, not both - TODO: fix when know which one!
{
key: ConfigKey.property_product,
description: "Product reference for the product list, used to link to the product",
format: TagFormat.plainText,
nullable: true,
maxCount: Count.infinity
}
// {
// key: ConfigKey.property_productList,
// format: TagFormat.plainText,
// maxCount: Count.infinity,
// },
]
},
[BitType.pageProductVideo]: {
since: "1.4.17",
baseBitType: BitType._standard,
description: "Page product video bit, used to create product video pages in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the product video page, used to identify the product video in URLs",
format: TagFormat.plainText
},
{
key: ConfigKey.property_productVideo,
description: "Product video reference for the product video page, used to link to the video",
format: TagFormat.plainText
}
]
},
[BitType.pageProductVideoList]: {
since: "1.4.17",
baseBitType: BitType._standard,
description: "Page product video list bit, used to create product video lists in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the product video list page, used to identify the product video list in URLs",
format: TagFormat.plainText
},
// NOTE: Only one of productVideo or productVideoList should be used, not both - TODO: fix when know which one!
{
key: ConfigKey.property_productVideo,
description: "Product video reference for the product video list, used to link to the video",
format: TagFormat.plainText,
maxCount: Count.infinity
}
// {
// key: ConfigKey.property_productVideoList,
// format: TagFormat.plainText,
// maxCount: Count.infinity,
// },
]
},
[BitType.pageSectionFolder]: {
since: "1.4.17",
baseBitType: BitType._standard,
description: "Page section folder bit, used to create section folders in articles or books",
tags: [
{
key: ConfigKey.property_slug,
description: "Slug for the section folder page, used to identify the section in URLs",
format: TagFormat.plainText
},
{
key: ConfigKey.property_productFolder,
description: "Product folder reference for the section folder page, used to link to the folder",
format: TagFormat.plainText
}
]
},
[BitType.photo]: {
since: "1.3.0",
baseBitType: BitType.image,
description: "Photo bit, used to create photo bits in articles or books"
},
[BitType.platform]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform bit, used to define platform properties",
tags: [
{
key: ConfigKey.property_platformName,
description: "The platform name",
format: TagFormat.plainText
},
{
key: ConfigKey.resource_platformIcon,
jsonKey: "platformIcon|resource(type=image, key=image)",
exportJsonKey: { platformIcon: { type: "image", image: { src: "$" } } },
description: "The platform icon",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the platform icon resource"
}
],
maxCount: 1
},
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
}
]
},
[BitType.platformBrandTarget]: {
since: "4.15.0",
baseBitType: BitType._standard,
description: "Platform brand target bit, used to define the brand target for publishing",
tags: [
{
key: ConfigKey.property_platformBrandTarget,
description: 'The platform brand target (typically "light", "dark" or "none")',
format: TagFormat.plainText,
defaultValue: "none"
}
]
},
[BitType.platformHeader]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform header bit, used to define platform header properties",
tags: [
{
key: ConfigKey.resource_platformLogo,
jsonKey: "platformLogo|resource(type=image, key=image)",
exportJsonKey: { platformLogo: { type: "image", image: { src: "$" } } },
description: "The platform logo",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the platform logo resource"
}
],
maxCount: 1
},
{
key: ConfigKey.property_platformLogoMaxHeight,
description: "Maximum height for the platform logo",
format: TagFormat.number,
defaultValue: "40"
},
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
}
]
},
[BitType.platformMain]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform main bit, used to define platform main properties",
tags: [
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
}
]
},
[BitType.platformMainButton]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform main button bit, used to define platform main button properties",
tags: [
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
}
]
},
[BitType.platformMainInput]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform main input bit, used to define platform main input properties",
tags: [
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
}
]
},
[BitType.platformSection]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform section bit, used to define platform section properties",
tags: [
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
}
]
},
[BitType.platformSectionButton]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform section button bit, used to define platform section button properties",
tags: [
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
}
]
},
[BitType.platformSectionChat]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform section chat bit, used to define platform section chat properties",
tags: [
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
},
{
key: ConfigKey.resource_platformBackgroundImage,
jsonKey: "platformBackgroundImage|resource(type=image, key=image)",
exportJsonKey: { platformBackgroundImage: { type: "image", image: { src: "$" } } },
description: "The platform section chat background image",
chain: [
{
key: ConfigKey.group_resourceImageCommon,
description: "Common image properties for the platform background image resource"
}
]
}
]
},
[BitType.platformSectionHeader]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform section header bit, used to define platform section header properties",
tags: [
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
}
]
},
[BitType.platformSectionInput]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform section input bit, used to define platform section input properties",
tags: [
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
}
]
},
[BitType.platformSystemIcon]: {
since: "4.12.0",
baseBitType: BitType._standard,
description: "Platform system icon bit, used to define platform system icon properties",
tags: [
{
key: ConfigKey.group_platformStylesCommon,
description: "Common platform styles"
}
]
},
[BitType.quote]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Quote bit, used to create quotes in articles or books",
tags: [
{
key: ConfigKey.property_quotedPerson,
description: "Quoted person for the quote, used to define who is being quoted",
format: TagFormat.plainText
}
]
},
[BitType.rating]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Rating bit, used to create rating bits in articles or books"
},
[BitType.coachSelfReflectionRating]: {
since: "1.3.0",
baseBitType: BitType.rating,
description: "Coach self-reflection rating bit, used for self-reflection in coaching"
},
[BitType.releaseNote]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Release note bit, used to create release notes in articles or books",
tags: [
{
key: ConfigKey.property_releaseVersion,
description: "Release version for the release note, used to define the version of the release",
format: TagFormat.plainText
},
{
key: ConfigKey.property_releaseKind,
description: "Release kind for the release note, used to define the type of release",
format: TagFormat.plainText
},
{
key: ConfigKey.property_releaseDate,
description: "Release date for the release note, used to define when the release was made",
format: TagFormat.plainText
}
]
},
[BitType.reviewNote]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Review note bit, used to create review notes in articles or books",
tags: [
{
maxCount: 2,
jsonKey: "title|multi(count=2, key=subtitle)",
exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }],
key: ConfigKey.tag_title,
description: "Title of the review note, used to display the note title"
},
{
key: ConfigKey.property_resolved,
description: "Resolved status for the review note, used to indicate if the note is resolved",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_resolvedDate,
description: "Resolved date for the review note, used to define when the note was resolved",
format: TagFormat.plainText
},
{
key: ConfigKey.property_resolvedBy,
description: "Resolved by for the review note, used to define who resolved the note",
format: TagFormat.plainText
},
{
// Override inherited @example from group_standardTags: string root example.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: "true" } },
{ "@absent": { isExample: true, example: "true" } },
{ isExample: true, example: "$" }
],
description: "The example text for the review note bit",
format: TagFormat.plainText,
nullable: true
}
],
rootExampleType: ExampleType.string
},
[BitType.reviewAuthorNote]: {
since: "1.3.0",
baseBitType: BitType.reviewNote,
description: "Author review note bit, used to create author notes in reviews"
},
[BitType.reviewCustomerNote]: {
//
since: "3.5.0",
baseBitType: BitType.reviewNote,
description: "Customer review note bit, used to create customer notes in reviews"
},
[BitType.reviewError]: {
since: "5.8.0",
baseBitType: BitType.reviewNote,
description: "Review error note bit, used to create error notes in reviews"
},
[BitType.reviewErrorAi]: {
since: "5.8.0",
baseBitType: BitType.reviewNote,
description: "Review error AI note bit, used to create AI error notes in reviews"
},
[BitType.reviewErrorTranslation]: {
since: "5.8.0",
baseBitType: BitType.reviewNote,
description: "Review error translation note bit, used to create translation error notes in reviews"
},
[BitType.reviewReviewerNote]: {
since: "1.3.0",
baseBitType: BitType.reviewNote,
description: "Reviewer review note bit, used to create reviewer notes in reviews"
},
[BitType.reviewRequestForReviewNote]: {
since: "1.3.0",
baseBitType: BitType.reviewNote,
description: "Request for review note bit, used to create notes requesting reviews"
},
[BitType.reviewApprovedNote]: {
since: "1.3.0",
baseBitType: BitType.reviewNote,
description: "Approved review note bit, used to create notes for approved reviews"
},
[BitType.sampleSolution]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Sample solution bit, used to create sample solutions in articles or books",
tags: [
// Not sure if these are actually valid here, but include them as they are in the test bit.
{
exportJsonKey: { anchor: "$" },
key: ConfigKey.tag_anchor,
format: TagFormat.plainText,
description: "Anchor for the sample solution, used to link to the solution"
},
{
exportJsonKey: { reference: "$" },
key: ConfigKey.tag_reference,
format: TagFormat.plainText,
description: "Reference for the sample solution, used to link to the source of the solution"
}
]
},
[BitType.sequence]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Sequence bit, used to create sequences in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for sequences"
},
{
// Override inherited @example from group_standardTags: this bit has
// a boolean root example (rootExampleType: boolean), so bit-level
// [@example] writes `example: true` in addition to `isExample: true`.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true } },
{ "@absent": { isExample: true, example: true } },
{ isExample: true, example: "$" }
],
description: "The example flag for the bit (boolean)",
format: TagFormat.boolean,
nullable: true
}
],
cardSet: CardSetConfigKey.elements,
rootExampleType: ExampleType.boolean
},
[BitType.stillImageFilm]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Still image film bit, used to create still image films in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for still image films, used to define additional properties"
},
{
// Combo resource
key: ConfigKey.group_resourceStillImageFilm,
description: "Resource still image film tags, used to attach still image films to the bit"
}
],
resourceAttachmentAllowed: false
},
[BitType.stillImageFilmEmbed]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Still image film embed bit, used to embed still image films in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for still image films, used to define additional properties"
},
{
key: ConfigKey.group_resourceStillImageFilmEmbed,
description: "Resource still image film embed tags, used to attach still image films to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.stillImageFilmLink]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Still image film link bit, used to create still image film links in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for still image films, used to define additional properties"
},
{
key: ConfigKey.group_resourceStillImageFilmLink,
description: "Resource still image film link tags, used to attach still image films to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.surveyAnonymous]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Anonymous survey bit, used to create anonymous surveys in articles or books"
},
[BitType.survey]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Survey bit, used to create surveys in articles or books"
},
[BitType.surveyMatrix]: {
since: "1.6.2",
baseBitType: BitType.image,
description: "Survey matrix bit, used to create survey matrices in articles or books",
tags: [
{
key: ConfigKey.property_pointerTop,
description: "Pointer for the top of the survey matrix, used to define the top label",
format: TagFormat.plainText,
minCount: 1,
maxCount: 1
},
{
key: ConfigKey.property_pointerLeft,
description: "Pointer for the left of the survey matrix, used to define the left label",
format: TagFormat.plainText,
minCount: 1,
maxCount: 1
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the button in the survey matrix, used to define the button text",
format: TagFormat.plainText,
minCount: 1,
maxCount: 1
}
]
},
[BitType.surveyMatrixMe]: {
since: "1.6.2",
baseBitType: BitType.surveyMatrix,
description: "Survey matrix me bit, used to create personal survey matrices in articles or books"
},
[BitType.surveyRating]: {
since: "1.6.0",
baseBitType: BitType._standard,
description: "Survey rating bit, used to create rating surveys in articles or books",
tags: [
{
key: ConfigKey.property_ratingLevelStart,
jsonKey: "ratingLevelStart.level",
exportJsonKey: { ratingLevelStart: { level: "$" } },
description: "Start level for the rating survey, used to define the lowest rating",
format: TagFormat.number,
chain: [
{
key: ConfigKey.property_label,
description: "Label for the start level of the rating survey, used to define the text displayed",
format: TagFormat.bitmarkText
}
]
},
{
key: ConfigKey.property_ratingLevelEnd,
jsonKey: "ratingLevelEnd.level",
exportJsonKey: { ratingLevelEnd: { level: "$" } },
description: "End level for the rating survey, used to define the highest rating",
format: TagFormat.number,
chain: [
{
key: ConfigKey.property_label,
description: "Label for the end level of the rating survey, used to define the text displayed",
format: TagFormat.bitmarkText
}
]
},
{
key: ConfigKey.property_ratingLevelSelected,
description: "Selected level for the rating survey, used to define the default rating",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the button in the rating survey, used to define the button text",
format: TagFormat.plainText
}
]
},
[BitType.surveyRatingOnce]: {
since: "1.6.0",
baseBitType: BitType.surveyRating,
description: "Survey rating once bit, used to create one-time rating surveys in articles or books"
},
[BitType.surveyRatingDisplay]: {
since: "1.6.0",
baseBitType: BitType.surveyRating,
description: "Survey rating display bit, used to display rating surveys in articles or books"
},
[BitType.scorm]: {
since: "1.5.11",
baseBitType: BitType._standard,
description: "SCORM bit, used to embed SCORM content in articles or books",
tags: [
{
key: ConfigKey.property_scormSource,
description: "Source for the SCORM content, used to define the SCORM package URL",
format: TagFormat.plainText
},
{
key: ConfigKey.property_posterImage,
description: "Poster image for the SCORM content, used to display a preview image",
format: TagFormat.plainText
}
]
},
[BitType.pronunciationTable]: {
since: "3.1.0",
baseBitType: BitType._standard,
description: "Pronunciation table bit, used to create pronunciation tables in articles or books",
cardSet: CardSetConfigKey.pronunciationTable
},
[BitType.table]: {
since: "1.5.19",
baseBitType: BitType._standard,
description: "Table bit, used to create tables in articles or books",
tags: [
{
key: ConfigKey.property_caption,
description: "Caption for the table, used to define the title of the table",
format: TagFormat.bitmarkText,
// HTML-10 / HTML-C2: a bit-level caption renders as the table's
// `<caption>` — placed inside the idiomatic `<table>` container as
// its first child. Outside a table context it falls back to the
// residual carrier.
htmlKey: { "@el": "caption", "@children": "$" },
// PLAN-141 F2 (NISO import): a table-wrap's <caption><title> — the
// nested content slot reads the title text (catalog D5: the caption
// is a TOP-LEVEL bit property on table bits).
mappingKeys: {
"xml-niso-iec": { "@el": "caption", "@children": { "@el": "title", "@text": "$" } }
}
},
{
key: ConfigKey.property_tableFixedHeader,
description: "Fixed header for the table, used to keep the header visible while scrolling",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_tableHeaderWhitespaceNoWrap,
description: "No wrap for table header whitespace, used to prevent header text wrapping",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_tableSearch,
description: "Search functionality for the table, used to enable searching within the table",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_tableSort,
description: "Sorting functionality for the table, used to enable sorting of table columns",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_tablePagination,
description: "Pagination for the table, used to limit the number of rows displayed at once",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_tablePaginationLimit,
description: "Pagination limit for the table, used to define the number of rows per page",
format: TagFormat.number,
defaultValue: "0"
},
{
key: ConfigKey.property_tableHeight,
description: "Height for the table, used to define the height of the table in pixels",
format: TagFormat.number,
defaultValue: "0"
},
{
key: ConfigKey.property_tableWhitespaceNoWrap,
description: "No wrap for table whitespace, used to prevent text wrapping in table cells",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_tableAutoWidth,
description: "Auto width for table columns, used to automatically adjust column widths",
format: TagFormat.boolean,
defaultValue: "true"
},
{
key: ConfigKey.property_tableResizableColumns,
description: "Resizable columns for the table, used to allow users to resize table columns",
format: TagFormat.boolean,
defaultValue: "false"
},
{
key: ConfigKey.property_tableColumnMinWidth,
description: "Minimum width for table columns, used to define the minimum width of columns",
format: TagFormat.number,
defaultValue: "0"
}
],
cardSet: CardSetConfigKey.table
},
[BitType.tableExtended]: {
since: "4.14.0",
baseBitType: BitType.table,
description: "Extended table bit, used to create complex tables with all HTML table features",
cardSet: CardSetConfigKey.tableExtended
},
[BitType.tableExtendedImage]: {
since: "5.13.0",
baseBitType: BitType.tableExtended,
description: "Extended table image bit, used to create complex tables with images and all HTML table features",
tags: [
{
key: ConfigKey.property_caption,
description: "Caption for the table image, used to provide a description for the image",
format: TagFormat.bitmarkText,
// HTML-10 / HTML-C2: render as the table's `<caption>` (see table bit).
htmlKey: { "@el": "caption", "@children": "$" }
},
{
key: ConfigKey.group_backgroundWallpaper,
description: "Background wallpaper tags for images, used to define background properties for images"
},
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for images, used to define additional properties for images"
},
{
key: ConfigKey.group_resourceImage,
description: "Resource image tags for images, used to attach images to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.tableAlt]: {
since: "1.16.0",
baseBitType: BitType.table,
description: "Alternative table bit, used to create alternative tables in articles or books"
},
[BitType.standardTableNormative]: {
since: "1.16.0",
baseBitType: BitType.table,
description: "Standard normative table bit, used to create standard normative tables in articles or books"
},
[BitType.standardTableNonNormative]: {
since: "1.16.0",
baseBitType: BitType.table,
description: "Standard non-normative table bit, used to create standard non-normative tables in articles or books"
},
[BitType.standardRemarkTableNormative]: {
since: "1.17.0",
baseBitType: BitType.table,
description: "Standard normative remark table bit, used to create standard normative remark tables in articles or books"
},
[BitType.standardRemarkTableNonNormative]: {
since: "1.17.0",
baseBitType: BitType.table,
description: "Standard non-normative remark table bit, used to create standard non-normative remark tables in articles or books"
},
[BitType.standardTableExtendedNormative]: {
since: "1.16.0",
baseBitType: BitType.tableExtended,
description: "Standard normative extended table bit, used to create standard normative extended tables in articles or books"
},
[BitType.standardTableExtendedNonNormative]: {
since: "1.16.0",
baseBitType: BitType.tableExtended,
description: "Standard non-normative extended table bit, used to create standard non-normative extended tables in articles or books"
},
[BitType.standardRemarkTableExtendedNormative]: {
since: "1.17.0",
baseBitType: BitType.tableExtended,
description: "Standard normative remark extended table bit, used to create standard normative remark extended tables in articles or books"
},
[BitType.standardRemarkTableExtendedNonNormative]: {
since: "1.17.0",
baseBitType: BitType.tableExtended,
description: "Standard non-normative remark extended table bit, used to create standard non-normative remark extended tables in articles or books"
},
[BitType.smartStandardTableNormative]: {
since: "1.28.0",
baseBitType: BitType.standardTableNormative,
description: "Smart standard normative table bit, used to create smart standard normative tables in articles or books"
},
[BitType.smartStandardTableNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardTableNonNormative,
description: "Smart standard non-normative table bit, used to create smart standard non-normative tables in articles or books"
},
[BitType.smartStandardRemarkTableNormative]: {
since: "1.28.0",
baseBitType: BitType.standardRemarkTableNormative,
description: "Smart standard normative remark table bit, used to create smart standard normative remark tables in articles or books"
},
[BitType.smartStandardRemarkTableNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardRemarkTableNonNormative,
description: "Smart standard non-normative remark table bit, used to create smart standard non-normative remark tables in articles or books"
},
[BitType.smartStandardTableExtendedNormative]: {
since: "1.28.0",
baseBitType: BitType.standardTableExtendedNormative,
description: "Smart standard normative extended table bit, used to create smart standard normative extended tables in articles or books",
// PLAN-141 F2 (NISO import): a table-wrap is STRUCTURAL-shaped (no
// @children — no body): <label> → [%], <caption><title> → the bit-level
// [@caption] (D5), the inner <table>/<table-wrap-foot> build the
// table-extended card grid via the card set's authored keys, and a
// footer <def-list> PROMOTES as a sibling legend bit. Anchor = the
// source id (D2 revised).
mappingKeys: {
"xml-niso-iec": { "@el": "table-wrap", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
}
},
[BitType.smartStandardTableExtendedNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardTableExtendedNonNormative,
description: "Smart standard non-normative extended table bit, used to create smart standard non-normative extended tables in articles or books"
},
[BitType.smartStandardRemarkTableExtendedNormative]: {
since: "1.28.0",
baseBitType: BitType.standardRemarkTableExtendedNormative,
description: "Smart standard normative remark extended table bit, used to create smart standard normative remark extended tables in articles or books"
},
[BitType.smartStandardRemarkTableExtendedNonNormative]: {
since: "1.28.0",
baseBitType: BitType.standardRemarkTableExtendedNonNormative,
description: "Smart standard non-normative remark extended table bit, used to create smart standard non-normative remark extended tables in articles or books"
},
[BitType.smartStandardTableNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardTableNormative,
description: "Collapsible smart standard normative table bit, used to create collapsible smart standard normative tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardTableNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardTableNonNormative,
description: "Collapsible smart standard non-normative table bit, used to create collapsible smart standard non-normative tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardRemarkTableNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardRemarkTableNormative,
description: "Collapsible smart standard normative remark table bit, used to create collapsible smart standard normative remark tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardRemarkTableNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardRemarkTableNonNormative,
description: "Collapsible smart standard non-normative remark table bit, used to create collapsible smart standard non-normative remark tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardTableExtendedNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardTableExtendedNormative,
description: "Collapsible smart standard normative extended table bit, used to create collapsible smart standard normative extended tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardTableExtendedNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardTableExtendedNonNormative,
description: "Collapsible smart standard non-normative extended table bit, used to create collapsible smart standard non-normative extended tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardRemarkTableExtendedNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardRemarkTableExtendedNormative,
description: "Collapsible smart standard normative remark extended table bit, used to create collapsible smart standard normative remark extended tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.smartStandardRemarkTableExtendedNonNormativeCollapsible]: {
since: "1.28.0",
deprecated: "5.37.0",
baseBitType: BitType.smartStandardRemarkTableExtendedNonNormative,
description: "Collapsible smart standard non-normative remark extended table bit, used to create collapsible smart standard non-normative remark extended tables in articles or books",
tags: [
{
key: ConfigKey.property_isCollapsible,
description: "If true, the bit is collapsible (defaults true for this deprecated bit type)",
format: TagFormat.boolean,
defaultValue: "true"
}
]
},
[BitType.parameters]: {
since: "1.18.0",
baseBitType: BitType.table,
description: "Parameters bit, used to create parameter tables in articles or books"
},
[BitType.toc]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Table of contents bit, used to create a table of contents in articles or books"
},
[BitType.tocChapter]: {
since: "1.5.5",
baseBitType: BitType.toc,
description: "Table of contents chapter bit, used to create chapters in the table of contents",
tags: [
{
key: ConfigKey.property_maxDisplayLevel,
description: "Maximum display level for the chapter in the table of contents",
format: TagFormat.number,
nullable: true
}
]
},
[BitType.tocInline]: {
since: "3.24.0",
baseBitType: BitType.toc,
description: "Inline table of contents bit, used to create inline tables of contents in articles or books",
tags: [
{
key: ConfigKey.property_maxTocChapterLevel,
description: "Maximum chapter level for the inline table of contents",
format: TagFormat.number,
nullable: true
}
]
},
[BitType.tocResource]: {
since: "3.31.0",
baseBitType: BitType.toc,
description: "Table of contents resource bit, used to create resources in the table of contents",
tags: [
{
key: ConfigKey.property_tocResource,
description: "Resource content for the table of contents, used to define the resource",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the button in the table of contents resource, used to define the text displayed on the button",
format: TagFormat.plainText
}
]
},
[BitType.tocContent]: {
since: "3.31.0",
baseBitType: BitType.toc,
description: "Table of contents content bit, used to create content sections in the table of contents",
tags: [
{
key: ConfigKey.property_tocContent,
description: "Content for the table of contents, used to define the content section",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the button in the table of contents content, used to define the text displayed on the button",
format: TagFormat.plainText
}
]
},
[BitType.anchor]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Anchor bit, used to create anchors in articles or books"
},
[BitType.bitBookEnding]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Bit book ending bit, used to create endings in articles or books"
},
[BitType.bitBookSummary]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Bit book summary bit, used to create summaries in articles or books"
},
[BitType.botActionAnnounce]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Bot action announce bit, used to create announcements in articles or books"
},
[BitType.botActionRatingNumber]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Bot action rating number bit, used to create rating number actions in articles or books"
},
[BitType.botActionRemind]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Bot action remind bit, used to create reminders in articles or books"
},
[BitType.botActionSave]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Bot action save bit, used to create save actions in articles or books"
},
[BitType.botActionTrueFalse]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Bot action true/false bit, used to create true/false actions in articles or books"
},
[BitType.chapterSubjectMatter]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Chapter subject matter bit, used to create subject matter chapters in articles or books"
},
[BitType.chat]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Chat bit, used to create chat sections in articles or books"
},
[BitType.conclusion]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Conclusion bit, used to create conclusions in articles or books"
},
[BitType.conclusionAlt]: {
since: "1.16.0",
baseBitType: BitType.conclusion,
description: "Alternative conclusion bit, used to create alternative conclusions in articles or books"
},
[BitType.documentUpload]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Document upload bit, used to create document upload sections in articles or books"
},
[BitType.footNote]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Footnote bit, used to create footnotes in articles or books"
},
[BitType.formFreeText]: {
since: "5.3.0",
baseBitType: BitType.interview,
description: "Form free text bit, used to create free text forms in articles or books"
},
[BitType.groupBorn]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Group born bit, used to create group born sections in articles or books"
},
[BitType.groupDied]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Group died bit, used to create group died sections in articles or books"
},
[BitType.recordAudio]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Record audio bit, used to create audio recording sections in articles or books"
},
[BitType.recordVideo]: {
since: "1.5.24",
baseBitType: BitType._standard,
description: "Record video bit, used to create video recording sections in articles or books"
},
[BitType.stickyNote]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Sticky note bit, used to create sticky notes in articles or books"
},
[BitType.takePicture]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Take picture bit, used to create picture taking sections in articles or books"
},
[BitType.handInAudio]: {
since: "1.5.15",
baseBitType: BitType._standard,
description: "Hand in audio bit, used to create audio submission sections in articles or books"
},
[BitType.handInContact]: {
since: "1.5.15",
baseBitType: BitType._standard,
description: "Hand in contact bit, used to create contact submission sections in articles or books"
},
[BitType.handInDocument]: {
since: "1.5.15",
baseBitType: BitType._standard,
description: "Hand in document bit, used to create document submission sections in articles or books"
},
[BitType.handInFile]: {
//
since: "3.2.0",
baseBitType: BitType._standard,
description: "Hand in file bit, used to create file submission sections in articles or books",
tags: [
{
key: ConfigKey.property_handInAcceptFileType,
description: "Accepted file types for the hand-in file, used to define allowed file formats",
format: TagFormat.plainText,
maxCount: Count.infinity
}
]
},
[BitType.handInFreeText]: {
since: "5.3.0",
baseBitType: BitType.interview,
description: "Hand in free text bit, used to create free text submission sections in articles or books"
},
[BitType.handInFreeTextExpert]: {
since: "5.3.0",
baseBitType: BitType.interview,
description: "Hand in free text expert bit, used to create free text expert submission sections in articles or books"
},
[BitType.handInLocation]: {
since: "1.5.15",
baseBitType: BitType._standard,
description: "Hand in location bit, used to create location submission sections in articles or books"
},
[BitType.handInPhoto]: {
since: "1.5.15",
baseBitType: BitType._standard,
description: "Hand in photo bit, used to create photo submission sections in articles or books"
},
[BitType.handInScan]: {
since: "1.5.15",
baseBitType: BitType._standard,
description: "Hand in scan bit, used to create scan submission sections in articles or books"
},
[BitType.handInSystemMedia]: {
//
since: "1.5.15",
baseBitType: BitType._standard,
description: "Hand in system media bit, used to create system media submission sections in articles or books",
tags: [
{
key: ConfigKey.property_handInAcceptFileType,
description: "Accepted file types for the hand-in system media, used to define allowed file formats",
format: TagFormat.plainText,
maxCount: Count.infinity
}
]
},
[BitType.handInSubmit]: {
//
since: "3.2.0",
baseBitType: BitType._standard,
description: "Hand in submit bit, used to create submission sections in articles or books",
tags: [
{
key: ConfigKey.property_handInRequirement,
description: "Requirement for the hand-in submission, used to define what is needed",
format: TagFormat.plainText,
maxCount: Count.infinity
},
{
key: ConfigKey.property_handInInstruction,
description: "Instruction for the hand-in submission, used to guide the user",
format: TagFormat.plainText
},
{
key: ConfigKey.property_buttonCaption,
description: "Caption for the button in the hand-in submission, used to define the button text",
format: TagFormat.plainText
}
]
},
[BitType.handInVideo]: {
since: "1.5.15",
baseBitType: BitType._standard,
description: "Hand in video bit, used to create video submission sections in articles or books"
},
[BitType.handInVoice]: {
since: "1.5.15",
baseBitType: BitType._standard,
description: "Hand in voice bit, used to create voice submission sections in articles or books"
},
[BitType.trueFalse1]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "True/False bit, used to create true/false questions in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for true/false questions"
},
{
key: ConfigKey.property_labelTrue,
description: "Label for the true option in the true/false question",
format: TagFormat.plainText
},
{
key: ConfigKey.property_labelFalse,
description: "Label for the false option in the true/false question",
format: TagFormat.plainText
},
{
// PLAN-067 Edit #1: true-false-1's `+` / `-` emit a flat
// `{ statement, isCorrect }` at bit root (single-statement bit),
// not a `choices[]` array. Format is bitmarkText so the statement
// body renders as a ProseMirror tree, matching reference fixtures.
exportJsonKey: { statement: "$", isCorrect: true },
key: ConfigKey.tag_true,
description: "Tag for the true option in the true/false question",
format: TagFormat.bitmarkText
},
{
exportJsonKey: { statement: "$", isCorrect: false },
key: ConfigKey.tag_false,
description: "Tag for the false option in the true/false question",
format: TagFormat.bitmarkText
},
{
// Override inherited @example from group_standardTags: boolean root
// example. Note: the legacy reference parser ties the bare-form
// example value to `statement.isCorrect`, but the statement is not
// yet emitted when this tag fires at bit-header. The pattern below
// emits a literal `true` for bare/synthesized and `$` for valued —
// sufficient for `[+...]` (correct) statements which dominate the
// fixtures; `[-...]` (incorrect) bits with bare `[@example]` would
// need either a runtime extension or an explicit `[@example:false]`.
//
// Format is `boolean` so `[@example:false]` coerces to JSON `false`
// (a natural default that gets stripped in optimised mode, matching
// BPG's `rootExampleType: boolean`). PlainText would emit the
// STRING `"false"` which doesn't strip.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true, example: true } },
{ "@absent": { isExample: true, example: true } },
// `[@example:false]`: emit `isExample: true` only — `example: false`
// is the natural default for boolean, stripped in optimised mode.
// Mirrors BPG `removeUnwantedProperties` `ignoreAllFalse: true`.
{ "@value=false": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example flag for the bit (boolean)",
format: TagFormat.boolean,
nullable: true
}
],
rootExampleType: ExampleType.boolean
},
[BitType.trueFalse]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "True/False bit, used to create true/false questions in articles or books",
quizBit: true,
tags: [
{
key: ConfigKey.group_quizCommon,
description: "Common quiz tags for true/false questions"
},
{
key: ConfigKey.property_labelTrue,
description: "Label for the true option in the true/false question",
format: TagFormat.plainText
},
{
key: ConfigKey.property_labelFalse,
description: "Label for the false option in the true/false question",
format: TagFormat.plainText
},
{
// Override inherited @example: in `isTopLevelExample` allow-list.
key: ConfigKey.property_example,
exportJsonKey: [
{ "@keyonly": { isExample: true } },
{ "@absent": { isExample: true } },
{ isExample: true, example: "$" }
],
description: "The example(s) for the bit",
format: TagFormat.bitmarkText,
nullable: true
}
],
cardSet: CardSetConfigKey.statements
},
[BitType.vendorAmcharts5Chart]: {
since: "1.5.8",
baseBitType: BitType.code,
description: "AmCharts 5 chart bit, used to embed AmCharts 5 charts in articles or books",
textFormatDefault: TextFormat.json
},
[BitType.vendorDatadogDashboard]: {
since: "5.2.0",
baseBitType: BitType.nonProductionPrototypeIframe,
description: "A Datadog dashboard bit, used to embed Datadog dashboards"
},
[BitType.vendorDatadogDashboardEmbed]: {
since: "3.12.0",
baseBitType: BitType.code,
description: "Datadog dashboard embed bit, used to embed Datadog dashboards in articles or books",
tags: [
{
key: ConfigKey.property_vendorDashboardId,
description: "Dashboard ID for the Datadog dashboard, used to define which dashboard to embed",
format: TagFormat.plainText
}
],
textFormatDefault: TextFormat.plainText
},
[BitType.vendorFormbricksEmbed]: {
since: "3.8.0",
baseBitType: BitType.code,
description: "Formbricks embed bit, used to embed Formbricks surveys in articles or books",
tags: [
{
key: ConfigKey.property_vendorSurveyId,
description: "Survey ID for the Formbricks survey, used to define which survey to embed",
format: TagFormat.plainText
}
],
textFormatDefault: TextFormat.plainText
},
[BitType.vendorFormbricksEmbedAnonymous]: {
since: "3.9.0",
baseBitType: BitType.vendorFormbricksEmbed,
description: "Anonymous Formbricks embed bit, used to embed anonymous Formbricks surveys in articles or books"
},
[BitType.vendorFormbricksLink]: {
since: "3.8.0",
baseBitType: BitType.code,
description: "Formbricks link bit, used to create links to Formbricks surveys in articles or books",
tags: [
{
key: ConfigKey.property_vendorSurveyId,
description: "Survey ID for the Formbricks survey, used to define which survey to link to",
format: TagFormat.plainText
}
],
textFormatDefault: TextFormat.plainText
},
[BitType.vendorFormbricksLinkAnonymous]: {
since: "3.9.0",
baseBitType: BitType.vendorFormbricksLink,
description: "Anonymous Formbricks link bit, used to create links to anonymous Formbricks surveys in articles or books"
},
[BitType.vendorHighchartsChart]: {
since: "1.5.28",
baseBitType: BitType.vendorAmcharts5Chart,
description: "Highcharts chart bit, used to embed Highcharts charts in articles or books"
},
[BitType.vendorIframelyEmbed]: {
since: "1.5.10",
baseBitType: BitType.code,
description: "Iframely embed bit, used to embed content from various sources in articles or books",
tags: [
{
key: ConfigKey.property_width,
// Same as image
description: "Width for the embedded content, used to define the width in pixels",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_height,
// Same as image
description: "Height for the embedded content, used to define the height in pixels",
format: TagFormat.number,
nullable: true
},
{
key: ConfigKey.property_vendorUrl,
description: "URL for the content to be embedded, used to define the source URL",
format: TagFormat.plainText
}
],
textFormatDefault: TextFormat.plainText
},
[BitType.vendorIframelyCard]: {
since: "1.5.10",
baseBitType: BitType.vendorIframelyEmbed,
description: "Iframely card bit, used to create cards for embedded content in articles or books"
},
[BitType.vendorIframelyPreview]: {
since: "1.5.10",
baseBitType: BitType.vendorIframelyEmbed,
description: "Iframely preview bit, used to create previews for embedded content in articles or books"
},
[BitType.vendorIframelyPreviewMini]: {
since: "1.5.10",
baseBitType: BitType.vendorIframelyEmbed,
description: "Iframely preview mini bit, used to create mini previews for embedded content in articles or books"
},
[BitType.vendorJupyterOutput]: {
since: "1.4.3",
baseBitType: BitType.code,
description: "Jupyter output bit, used to embed Jupyter notebook outputs in articles or books",
tags: [
{
key: ConfigKey.property_jupyterId,
description: "Jupyter ID for the notebook, used to define which notebook to embed",
format: TagFormat.plainText
},
{
key: ConfigKey.property_jupyterExecutionCount,
description: "Execution count for the Jupyter output, used to define the execution order",
format: TagFormat.number,
nullable: true
}
],
textFormatDefault: TextFormat.plainText
},
[BitType.vendorJupyterCellCode]: {
since: "1.4.3",
baseBitType: BitType.vendorJupyterOutput,
description: "Jupyter cell code bit, used to embed Jupyter notebook cell code in articles or books"
},
[BitType.vendorJupyterCellMarkdown]: {
since: "1.4.3",
baseBitType: BitType.vendorJupyterOutput,
description: "Jupyter cell markdown bit, used to embed Jupyter notebook cell markdown in articles or books"
},
[BitType.vendorJupyterCellRaw]: {
since: "1.4.3",
baseBitType: BitType.vendorJupyterOutput,
description: "Jupyter cell raw bit, used to embed Jupyter notebook cell raw content in articles or books"
},
[BitType.vendorJupyterIpynb]: {
since: "1.4.3",
baseBitType: BitType.vendorJupyterOutput,
description: "Jupyter IPYNB bit, used to embed Jupyter notebook files in articles or books"
},
[BitType.vendorPadletEmbed]: {
since: "1.3.0",
baseBitType: BitType.code,
description: "Padlet embed bit, used to embed Padlet boards in articles or books",
tags: [
{
key: ConfigKey.property_padletId,
description: "Padlet ID for the board, used to define which Padlet to embed",
format: TagFormat.plainText
}
],
textFormatDefault: TextFormat.plainText
},
[BitType.vendorStripePricingTable]: {
since: "1.20.0",
baseBitType: BitType.article,
description: "Stripe pricing table bit, used to embed Stripe pricing tables in articles or books",
tags: [
{
key: ConfigKey.property_stripePricingTableId,
description: "Stripe pricing table ID, used to define which pricing table to embed",
format: TagFormat.plainText,
minCount: 1
},
{
key: ConfigKey.property_stripePublishableKey,
description: "Stripe publishable key, used to authenticate the Stripe pricing table",
format: TagFormat.plainText,
minCount: 1
}
]
},
[BitType.vendorStripePricingTableExternal]: {
since: "3.13.0",
baseBitType: BitType.vendorStripePricingTable,
description: "External Stripe pricing table bit, used to embed external Stripe pricing tables in articles or books"
},
[BitType.vendorStripePricingTablePrintOnRequest]: {
since: "5.21.0",
baseBitType: BitType.vendorStripePricingTable,
description: "Stripe pricing table bit with print-on-request customer/product IDs, used to embed Stripe pricing tables tied to a specific print-on-request customer and product",
tags: [
{
key: ConfigKey.property_stripePricingTableDescriptiveName,
description: "Descriptive name for the Stripe pricing table",
format: TagFormat.plainText
},
{
key: ConfigKey.property_printOnRequestCustomerId,
description: "Print-on-request customer ID, used to identify the customer for the print-on-request flow",
format: TagFormat.plainText,
minCount: 1
},
{
key: ConfigKey.property_printOnRequestProductId,
description: "Print-on-request product ID, used to identify the product for the print-on-request flow",
format: TagFormat.plainText,
minCount: 1
}
]
},
[BitType.video]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Video bit, used to embed videos in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for videos, used to define additional properties"
},
{
key: ConfigKey.group_resourceVideo,
description: "Resource video tags, used to attach videos to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.videoLandscape]: {
since: "1.3.0",
baseBitType: BitType.video,
description: "Landscape video bit, used to embed landscape videos in articles or books"
},
[BitType.videoPortrait]: {
since: "1.3.0",
baseBitType: BitType.video,
description: "Portrait video bit, used to embed portrait videos in articles or books"
},
[BitType.videoEmbed]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Video embed bit, used to embed videos from external sources in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for video embeds, used to define additional properties"
},
{
key: ConfigKey.group_resourceVideoEmbed,
description: "Resource video embed tags, used to attach video embeds to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.videoEmbedLandscape]: {
since: "1.3.0",
baseBitType: BitType.videoEmbed,
description: "Landscape video embed bit, used to embed landscape videos from external sources in articles or books"
},
[BitType.videoEmbedPortrait]: {
since: "1.3.0",
baseBitType: BitType.videoEmbed,
description: "Portrait video embed bit, used to embed portrait videos from external sources in articles or books"
},
[BitType.videoLink]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Video link bit, used to create links to videos in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for video links, used to define additional properties"
},
{
key: ConfigKey.group_resourceVideoLink,
description: "Resource video link tags, used to attach video links to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.videoLinkLandscape]: {
since: "1.3.0",
baseBitType: BitType.videoLink,
description: "Landscape video link bit, used to create links to landscape videos in articles or books"
},
[BitType.videoLinkPortrait]: {
since: "1.3.0",
baseBitType: BitType.videoLink,
description: "Portrait video link bit, used to create links to portrait videos in articles or books"
},
[BitType.websiteLink]: {
since: "1.3.0",
baseBitType: BitType._standard,
description: "Website link bit, used to create links to websites in articles or books",
tags: [
{
key: ConfigKey.group_resourceBitTags,
description: "Resource bit tags for website links, used to define additional properties"
},
{
key: ConfigKey.group_resourceWebsiteLink,
description: "Resource website link tags, used to attach website links to the bit",
minCount: 1
}
],
resourceAttachmentAllowed: false
},
[BitType.nonProductionPrototypeIframe]: {
since: "4.20.0",
baseBitType: BitType._standard,
description: "A non-production prototype iframe bit, used to embed prototype iframes",
tags: [
{
key: ConfigKey.property_iframeSrc,
description: "Iframe source URL, used to define the source of the iframe",
minCount: 1
},
{
key: ConfigKey.property_iframeName,
description: "Iframe name, used to define the name of the iframe"
}
]
},
[BitType.vendorLearndashEmbed]: {
since: "5.0.0",
baseBitType: BitType.nonProductionPrototypeIframe,
description: "Learndash embed bit, used to embed Learndash content in an iframe"
}
};
// src/config/Config.ts
var COLLAPSIBLE_BIT_TYPE_SUFFIX = "-collapsible";
var Config = class {
constructor() {
__publicField(this, "bitLevelMin", 1);
__publicField(this, "bitLevelMax", 7);
__publicField(this, "bitCache", /* @__PURE__ */ new Map());
__publicField(this, "migratedBitCache", /* @__PURE__ */ new Map());
__publicField(this, "allResourcesCache");
__publicField(this, "comboResourcesCache", /* @__PURE__ */ new Map());
}
/**
* Return the bitType given a bit type that may be invalid
*
* If the bit type is not found, the _error bit type will be returned.
*
* @param bitType bit type in (may be invalid)
* @returns valid bitType, or _error if the bit type is invalid
*/
getBitType(bitType) {
if (bitType?.startsWith("|")) bitType = bitType.substring(1);
return Enum3(BitType).fromValue(bitType) ?? BitType._error;
}
/**
* PLAN-017: Return the migration target for a deprecated `*-collapsible` bit type.
*
* Every `*-collapsible` bit type declares its non-collapsible cousin as its `baseBitType`, so the
* target is read from the config rather than a hand-maintained table — a new `*-collapsible` bit
* type is covered automatically.
*
* `BitType.collapsible` is the one exclusion: it has no non-collapsible cousin (its `baseBitType`
* is `article`, which is not a migration target).
*
* @param bitType the bit type
* @returns the bit type to migrate to, or undefined if the bit type does not migrate
*/
getMigratedBitType(bitType) {
const cached = this.migratedBitCache.get(bitType);
if (cached !== void 0 || this.migratedBitCache.has(bitType)) return cached;
let target;
if (bitType !== BitType.collapsible && bitType.endsWith(COLLAPSIBLE_BIT_TYPE_SUFFIX)) {
target = BITS[bitType]?.baseBitType;
}
this.migratedBitCache.set(bitType, target);
return target;
}
/**
* Check if a bit type is and instance of the given root bit type.
*
* @param bitType the bit type
* @param baseBitType the root bit type or types to check
*/
isOfBitType(bitType, baseBitType) {
if (!bitType) return false;
if (bitType === baseBitType) return true;
const bitConfig = this.getBitConfig(bitType);
if (!bitConfig) return false;
if (Array.isArray(baseBitType)) {
for (const bt of baseBitType) {
if (bitConfig.inheritedBitTypesSet.has(bt)) return true;
}
return false;
}
return bitConfig.inheritedBitTypesSet.has(baseBitType);
}
/**
* Return true if the bit type is commented.
*
* @param bitType bit type in (may be invalid)
* @returns true if the bit type is commented
*/
isBitTypeCommented(bitType) {
return !!bitType?.startsWith("|");
}
/**
* Get the configuration for a bit.
*
* @param bitType
* @returns the bit configuration
*/
getBitConfig(bitType) {
let bitConfig = this.bitCache.get(bitType);
if (!bitConfig) {
const throwNotFoundError = () => {
throw new Error(`No config found for bit: ${bitType}`);
};
const inheritedBitTypes = [];
const _bitConfigTree = [];
let bitTypeCurrent = bitType;
while (bitTypeCurrent) {
const _bitConfig = BITS[bitTypeCurrent];
if (!_bitConfig) throwNotFoundError();
inheritedBitTypes.push(bitTypeCurrent);
_bitConfigTree.push(_bitConfig);
bitTypeCurrent = _bitConfig.baseBitType;
}
_bitConfigTree.reverse();
const _mergedBitConfig = _bitConfigTree.reduce((acc, cur) => {
acc = objectUtils.deepMerge(acc, cur);
return acc;
}, {});
const {
since,
textFormatDefault,
quizBit,
tags: _tags,
cardSet: _cardSet,
deprecated,
bodyAllowed,
bodyRequired,
footerAllowed,
footerRequired,
resourceAttachmentAllowed,
rootExampleType
} = _mergedBitConfig;
_tags?.push({
key: ConfigKey.property_internalComment,
description: "Internal comment for the bit.",
format: TagFormat.plainText,
maxCount: Count.infinity,
minCount: 0
});
const zeroCountAllResourcesTags = this.getAllResourcesTagsWithZeroCounts();
const bitTags = instance.hydrateTagsConfig(_tags ?? []);
const cardSet = instance.hydrateCardSetConfig(_cardSet);
const comboResourceConfigKey = bitTags.info?.comboResourceConfigKey;
const tags2 = {
...bitTags.tags
};
for (const [k, v] of Object.entries(zeroCountAllResourcesTags)) {
if (!tags2[k]) tags2[k] = v;
}
bitConfig = new BitConfig({
since,
bitType,
inheritedBitTypes,
textFormatDefault: textFormatDefault ?? TextFormat.bitmarkText,
tags: tags2,
cardSet,
quizBit,
deprecated,
bodyAllowed,
bodyRequired,
footerAllowed,
footerRequired,
resourceAttachmentAllowed,
rootExampleType,
comboResourceConfigKey
});
this.bitCache.set(bitType, bitConfig);
}
return bitConfig;
}
/**
* Look up the tag configuration by tag (rather than by config key).
*
* @param bitType
* @param key the configKey to look up - if undefined, will return undefined
* @returns
*/
getTagConfigForTag(tagsConfig, key) {
if (!tagsConfig) return void 0;
return tagsConfig[key];
}
/**
* Look up the tag configuration for a cardSet variant.
*
* @param bitType
* @param sideNo
* @param variantNo
* @param tag
* @param parentTagConfig
* @returns
*/
getTagsConfigForCardSet(bitType, sideNo, variantNo) {
const variantConfig = this.getCardSetVariantConfig(bitType, sideNo, variantNo);
if (!variantConfig) return void 0;
return variantConfig.tags;
}
/**
* Get the configuration for a particular card side and variant
* (checking for infinitely repeating variants)
*
* @param config all variant configurations
* @param sideNo side index
* @param variantNo variant index
*
* @returns the config if found, otherwise undefined
*/
getCardSetVariantConfig(bitType, sideNo, variantNo) {
let ret;
const bitConfig = this.getBitConfig(bitType);
if (!bitConfig) return void 0;
const variants = bitConfig.cardSet?.variants;
if (!variants) return void 0;
const sideIdx = Math.min(sideNo, variants.length - 1);
const variant = variants[sideIdx];
const maxVariantIndex = variant.length - 1;
if (variantNo > maxVariantIndex) {
ret = variant[maxVariantIndex];
} else {
ret = variant[variantNo];
}
return ret;
}
/**
* Get the resource configuration for a bit.
*
* The configuration returned potentially depends on the resourceTypeAttachment which can change the allowed
* count of the resource (or comboResource) it matches.
*
* The function is a bit complicated, but it is actually just manipulating the configuration into a format that
* the rest of the app can use easily.
*
* @param bitType
* @param resourceTypeAttachment the resource type specified in the bit header
* @returns the definitive resource configuration for the bit
*/
// TODO - this will need fixing.
getBitResourcesConfig(bitType, resourceTypeAttachment) {
let finalResourceTags = {};
const comboResourceConfigKeysMap = /* @__PURE__ */ new Map();
const configKeyResourceAttachment = resourceTypeAttachment ? resourceTypeToConfigKey(resourceTypeAttachment) : void 0;
const bitConfig = this.getBitConfig(bitType);
const resourceTags = {};
for (const [k, v] of Object.entries(bitConfig.tags)) {
if (v.type === BitTagConfigKeyType.resource) {
resourceTags[k] = v;
}
}
const comboResourceConfigKey = bitConfig.comboResourceConfigKey ?? (bitConfig.resourceAttachmentAllowed ? configKeyResourceAttachment : void 0);
if (comboResourceConfigKey) {
const comboResourcesMap = /* @__PURE__ */ new Map();
const comboResource = this.getComboResource(comboResourceConfigKey);
if (comboResource) {
const comboResourceTagConfigs = Object.values(comboResource).filter((t) => {
return t.type === BitTagConfigKeyType.resource;
});
const comboResourceTypes = comboResourceTagConfigs.map((t) => t.configKey);
comboResourceTypes.forEach((type) => {
const tags2 = comboResourceTagConfigs.find((t) => t.configKey === type);
if (tags2) comboResourcesMap.set(type, tags2);
});
const comboResourceConfigKeys = this.getComboResourceConfigKeys(comboResourceConfigKey);
if (comboResourceConfigKeys && comboResourceConfigKeys.length > 0) {
comboResourceConfigKeysMap.set(comboResourceConfigKey, comboResourceConfigKeys);
}
}
if (resourceTypeAttachment) {
for (const [k, tag] of Object.entries(resourceTags)) {
const singleTagMatch = comboResourceConfigKey === tag.configKey;
const comboTagMatch = comboResourcesMap ? comboResourcesMap.has(tag.configKey) : false;
if (singleTagMatch) {
const newTag = new ResourceTagConfig({
configKey: tag.configKey,
tag: tag.tag,
minCount: 1,
maxCount: 1,
chain: tag.chain,
jsonKey: tag.jsonKey,
exportJsonKey: tag.exportJsonKey,
hasExportJsonKey: tag.hasExportJsonKey,
htmlKey: tag.htmlKey,
hasHtmlKey: tag.hasHtmlKey,
deprecated: tag.deprecated
});
finalResourceTags[k] = newTag;
} else if (comboTagMatch) {
if (comboResourcesMap) {
const newTag = comboResourcesMap.get(tag.configKey);
if (newTag) finalResourceTags[k] = newTag;
}
} else {
finalResourceTags[k] = tag;
}
}
} else {
finalResourceTags = resourceTags;
}
} else {
finalResourceTags = resourceTags;
}
const resourcesConfig = new ResourcesConfig(
finalResourceTags,
bitConfig.resourceAttachmentAllowed,
configKeyResourceAttachment,
comboResourceConfigKeysMap
);
return resourcesConfig;
}
/**
* Get the resourceType tags for a combo resource.
*
* @param configKeyResourceAttachment
* @returns resourceTypes for the combo resource (or undefined if not a combo resource)
*/
getComboResourceConfigKeys(configKeyResourceAttachment) {
const comboResource = this.getComboResource(configKeyResourceAttachment);
if (comboResource) {
const comboResourceTypes = Object.values(comboResource).filter((t) => {
return t.type === BitTagConfigKeyType.resource;
}).map((t) => t.configKey);
return comboResourceTypes;
}
return void 0;
}
/**
* Get the tags for a combo resource.
*
* Combo resources are resources that contain more than one resource, such as still-image-film.
* These resources require special handling in the code and therefore also special configuration.
*
* @returns TagsConfig for the combo resource or undefined if not found
*/
getComboResource(resourceConfigKey) {
if (!resourceConfigKey) return void 0;
let comboResourcesTags = this.comboResourcesCache.get(resourceConfigKey);
if (!comboResourcesTags) {
comboResourcesTags = {};
Object.values(GROUPS).filter(
(g) => g.type === GroupConfigType.comboResource && g.comboResourceConfigKey === resourceConfigKey
).forEach((g) => {
comboResourcesTags = {
...comboResourcesTags,
...instance.hydrateTagsConfig(g.tags).tags
};
});
if (Object.keys(comboResourcesTags).length === 0) comboResourcesTags = void 0;
this.comboResourcesCache.set(resourceConfigKey, comboResourcesTags);
}
return comboResourcesTags;
}
/**
* Get the tags for all resources with 0 max/min counts.
*
* This is required so that any bit can parse all resources correctly. It is necessary to parse all resources
* for any bit because of the requirement to add excess resources to the 'parser' output.
*
* NOTE: These are not used for validation, only for parsing. They will be overridden for individual bits for
* validation.
*
* @returns TagsConfig for all resources
*/
getAllResourcesTagsWithZeroCounts() {
let allResourcesTags = this.allResourcesCache;
if (!allResourcesTags) {
allResourcesTags = {};
Object.values(GROUPS).filter((g) => g.type === GroupConfigType.resource).forEach((g) => {
allResourcesTags = {
...allResourcesTags,
...instance.hydrateTagsConfig(g.tags).tags
};
});
for (const tag of Object.values(allResourcesTags)) {
const tagAny = tag;
tagAny.maxCount = 0;
tagAny.minCount = 0;
}
this.allResourcesCache = allResourcesTags;
}
return allResourcesTags;
}
};
var instance2 = new Config();
// src/generated/package_info.ts
var PACKAGE_INFO = {
"name": "@gmb/bitmark-parser-generator",
"version": "5.37.0",
"author": "Get More Brain Ltd",
"license": "ISC",
"description": "A bitmark parser and generator using Peggy.js"
};
// src/utils/env/Environment.ts
import "@ncoderz/superenum";
var Environment = {
unknown: "",
node: "node",
chrome: "chrome",
safari: "safari",
firefox: "firefox",
edge: "edge",
ie: "ie"
};
// src/utils/env/Os.ts
import "@ncoderz/superenum";
var Os = {
unknown: "",
macos: "macos",
windows: "windows",
linux: "linux",
android: "android",
ios: "ios"
};
// src/utils/env/userAgent.ts
function parseUserAgent() {
if (typeof window === "undefined" || !window.navigator || !window.navigator.userAgent)
return void 0;
const uai = {
browser: Environment.unknown,
os: Os.unknown
};
const userAgent = window.navigator.userAgent;
const tua = tokenizeUserAgent(userAgent);
const containsFirefox = tua.keys.has("firefox");
const containsChrome = tua.keys.has("chrome");
const containsChromium = tua.keys.has("chromium");
const containsSafari = tua.keys.has("safari");
const containsMacintosh = tua.keys.has("macintosh");
const isSafari = containsSafari && !containsChrome && !containsChromium;
const isChrome = containsChrome;
const isFirefox = containsFirefox;
const isMacOS = containsMacintosh;
if (isSafari) {
uai.browser = Environment.safari;
uai.browserVersion = tua.versions.version;
} else if (isChrome) {
uai.browser = Environment.chrome;
uai.browserVersion = tua.versions.chrome;
} else if (isFirefox) {
uai.browser = Environment.firefox;
uai.browserVersion = tua.versions.firefox;
}
if (isMacOS) {
uai.os = Os.macos;
uai.osVersion = tua.versions.x;
}
return uai;
}
function tokenizeUserAgent(userAgent) {
const rawKeys = userAgent.toLowerCase().split(new RegExp("[ /,;()]", "g")).reduce((acc, val) => {
if (val) acc.push(val);
return acc;
}, []);
const tua = {
keys: /* @__PURE__ */ new Set(),
versions: {}
};
let prevRawKey = "";
for (let i = 0, len = rawKeys.length; i < len; i++) {
const rawKey = rawKeys[i];
let isNumber = false;
for (let i2 = 0; i2 <= 9; i2++) {
if (rawKey.startsWith(`${i2}`)) {
isNumber = true;
break;
}
}
if (isNumber) {
tua.versions[prevRawKey] = rawKey.replace("_", ".");
} else {
tua.keys.add(rawKey);
}
prevRawKey = rawKey;
}
return tua;
}
// src/utils/env/Version.ts
var EMPTY_VERSION = {
full: "",
major: "",
minor: "",
patch: "",
build: ""
};
// src/utils/env/Env.ts
var Env = class {
constructor() {
__publicField(this, "bootTimestamp");
__publicField(this, "app");
__publicField(this, "appVersion");
__publicField(this, "environment");
__publicField(this, "environmentVersion");
__publicField(this, "os");
__publicField(this, "osVersion");
__publicField(this, "isBrowser");
__publicField(this, "isNode");
__publicField(this, "isCI");
__publicField(this, "NODE_ENV");
// public username: string;
// public usergroup: string;
// public uid: number;
// public gid: number;
// public homedir: string;
// public shell: string;
__publicField(this, "userAgentInfo");
Object.defineProperty(this, "userAgentInfo", {
value: parseUserAgent(),
enumerable: false
});
this.bootTimestamp = Date.now();
this.app = "";
this.appVersion = EMPTY_VERSION;
this.environment = this.getEnvironment();
this.environmentVersion = this.getEnvironmentVersion();
this.isBrowser = this.getIsBrowser();
this.isNode = this.environment === Environment.node;
this.isCI = this.getIsCI();
this.NODE_ENV = this.getNodeEnv();
this.os = this.getOs();
this.osVersion = this.getOsVersion();
}
init(app, version) {
this.app = app;
this.appVersion = this.parseMMBVersion(version);
}
get upTimestamp() {
return Date.now() - this.bootTimestamp;
}
getEnvironment() {
if (this.userAgentInfo) {
return this.userAgentInfo.browser;
} else if (typeof process !== "undefined") {
if (process.versions !== void 0) {
if (process.versions.node) return Environment.node;
}
}
return Environment.unknown;
}
getEnvironmentVersion() {
if (this.userAgentInfo) {
return this.parseMMBVersion(this.userAgentInfo.browserVersion);
} else if (typeof process !== "undefined") {
return this.parseMMBVersion(process.version);
}
return EMPTY_VERSION;
}
getOs() {
if (this.userAgentInfo) {
return this.userAgentInfo.os;
} else if (typeof process !== "undefined") {
if (process.versions !== void 0) {
if (process.versions.node) return Os.unknown;
}
}
return Os.unknown;
}
getOsVersion() {
if (this.userAgentInfo) {
return this.parseMMBVersion(this.userAgentInfo.osVersion);
} else if (typeof process !== "undefined") {
return this.parseMMBVersion("");
}
return EMPTY_VERSION;
}
getIsBrowser() {
return !!(typeof window !== "undefined" && typeof document !== "undefined" && typeof navigator !== "undefined" && navigator.userAgent);
}
getIsCI() {
return !!(typeof process !== "undefined" && process.env && process.env.CI);
}
getNodeEnv() {
return typeof process !== "undefined" && process.env && process.env.NODE_ENV || "";
}
/**
* TODO:
* Parse Senver 2.0 ?? https://semver.org/
* and X.Y.Z.B using regex
*
* @param version
*/
parseMMBVersion(version) {
if (!version) return EMPTY_VERSION;
if (version.startsWith("v")) {
version = version.substring(1);
}
const s = version.split(".", 1e3);
let major = "";
let minor = "";
let build = "";
let patch = "";
for (let i = 0, len = s.length; i < len; i++) {
const v = s[i];
if (i === 0) major = v;
else if (i === 1) minor = v;
else if (i === 2) patch = v;
else if (i === 3) build = v;
}
return {
full: version,
major,
minor,
patch,
build
};
}
};
var env = new Env();
var initEnv = env.init.bind(env);
// src/init/init.ts
var initialised = false;
function init() {
if (initialised) return;
initEnv(PACKAGE_INFO.name, PACKAGE_INFO.version);
initialised = true;
}
// src/ast/Ast.ts
import { Enum as Enum4 } from "@ncoderz/superenum";
// src/model/enum/BodyBitType.ts
import "@ncoderz/superenum";
var BodyBitTypeRaw = {
text: "text",
gap: "gap",
mark: "mark",
select: "select",
highlight: "highlight"
};
var BodyBitType = BodyBitTypeRaw;
// src/model/enum/TextMarkType.ts
import "@ncoderz/superenum";
var TextMarkType = {
// Styles
bold: "bold",
light: "light",
italic: "italic",
highlight: "highlight",
highlightOrange: "highlightOrange",
highlightYellow: "highlightYellow",
highlightGreen: "highlightGreen",
highlightBlue: "highlightBlue",
highlightPurple: "highlightPurple",
highlightPink: "highlightPink",
highlightBrown: "highlightBrown",
highlightBlack: "highlightBlack",
highlightWhite: "highlightWhite",
highlightGray: "highlightGray",
// Inline only styles
strike: "strike",
subscript: "subscript",
superscript: "superscript",
ins: "ins",
del: "del",
underline: "underline",
doubleUnderline: "doubleUnderline",
smallcaps: "smallcaps",
circle: "circle",
languageEmRed: "languageEmRed",
languageEmOrange: "languageEmOrange",
languageEmYellow: "languageEmYellow",
languageEmGreen: "languageEmGreen",
languageEmBlue: "languageEmBlue",
languageEmPurple: "languageEmPurple",
languageEmPink: "languageEmPink",
languageEmBrown: "languageEmBrown",
languageEmBlack: "languageEmBlack",
languageEmWhite: "languageEmWhite",
languageEmGray: "languageEmGray",
languageEm: "languageEm",
userUnderline: "userUnderline",
userDoubleUnderline: "userDoubleUnderline",
userStrike: "userStrike",
userCircle: "userCircle",
userHighlight: "userHighlight",
notranslate: "notranslate",
// Attribute chain
link: "link",
ref: "ref",
xref: "xref",
extref: "extref",
footnote: "footnote",
footnoteStar: "footnote*",
symbol: "symbol",
var: "var",
code: "code",
timer: "timer",
duration: "duration",
// legacy: no longer produced by the parser (>= 8.41.1), accepted as generator input
color: "color",
// legacy: no longer produced by the parser (>= 8.41.1, now textStyle), accepted as generator input
textStyle: "textStyle",
colorPicker: "colorPicker",
comment: "comment"
};
// src/model/enum/TextNodeType.ts
import "@ncoderz/superenum";
var TextNodeType = {
// Text
text: "text",
// Blocks
hardBreak: "hardBreak",
paragraph: "paragraph",
heading: "heading",
section: "section",
noBulletList: "noBulletList",
bulletList: "bulletList",
orderedList: "orderedList",
orderedListRoman: "orderedListRoman",
orderedListRomanLower: "orderedListRomanLower",
letteredList: "letteredList",
letteredListLower: "letteredListLower",
taskList: "taskList",
listItem: "listItem",
taskItem: "taskItem",
image: "image",
imageInline: "imageInline",
codeBlock: "codeBlock",
// Inline blocks
latex: "latex",
// Body bits
gap: "gap",
select: "select",
highlight: "highlight",
mark: "mark"
};
// src/model/ast/NodeType.ts
import "@ncoderz/superenum";
var NodeType = {
unknown: "unknown",
// unknown
__isDefaultExample: "__isDefaultExample",
__typeAlias: "__typeAlias",
action: "action",
actionValue: "actionValue",
activityType: "activityType",
activityTypeValue: "activityTypeValue",
additionalSolutions: "additionalSolutions",
additionalSolutionsValue: "additionalSolutionsValue",
advertisingClickUrl: "advertisingClickUrl",
advertisingClickUrlValue: "advertisingClickUrlValue",
ageRange: "ageRange",
ageRangeValue: "ageRangeValue",
aiGenerated: "aiGenerated",
aiGeneratedValue: "aiGeneratedValue",
alias: "alias",
// bit type (alias)
alignment: "alignment",
allowedBit: "allowedBit",
allowedBitValue: "allowedBitValue",
allowPrint: "allowPrint",
allowPrintValue: "allowPrintValue",
alt: "alt",
alternativeAnswers: "alternativeAnswers",
alternativeAnswersValue: "alternativeAnswersValue",
alternativeDefinitions: "alternativeDefinitions",
alternativeDefinitionsValue: "alternativeDefinitionsValue",
altLangTag: "altLangTag",
altLangTagValue: "altLangTagValue",
analyticsTag: "analyticsTag",
analyticsTagValue: "analyticsTagValue",
anchor: "anchor",
answer: "answer",
appLink: "appLink",
attrs: "attrs",
audio: "audio",
audioEmbed: "audioEmbed",
audioLink: "audioLink",
author: "author",
authorFullName: "authorFullName",
authorFullNameValue: "authorFullNameValue",
authorJobTitle: "authorJobTitle",
authorJobTitleValue: "authorJobTitleValue",
authorPseudonym: "authorPseudonym",
authorPseudonymValue: "authorPseudonymValue",
authorTitle: "authorTitle",
authorTitleValue: "authorTitleValue",
authorValue: "authorValue",
availableClassifications: "availableClassifications",
availableClassificationsValue: "availableClassificationsValue",
avatarImage: "avatarImage",
backgroundWallpaper: "backgroundWallpaper",
backgroundWallpaperValue: "backgroundWallpaperValue",
backgroundImage: "backgroundImage",
backgroundImageValue: "backgroundImageValue",
bitLevel: "bitLevel",
// bit level
bitmarkAst: "bitmarkAst",
// bitmarkAst
bitmarkVersion: "bitmarkVersion",
bits: "bits",
// bits
bitsValue: "bitsValue",
// bit
bitType: "bitType",
// bit type
bitFingerprint: "bitFingerprint",
bitFingerprintValue: "bitFingerprintValue",
blockId: "blockId",
blockIdValue: "blockIdValue",
body: "body",
bodyBit: "bodyBit",
bodyBits: "bodyBits",
bodyJson: "bodyJson",
bodyParts: "bodyParts",
bodyPartsValue: "bodyPartsValue",
bodyPartText: "bodyPartText",
bodyText: "bodyText",
bodyValue: "bodyValue",
book: "book",
bookAlias: "bookAlias",
bookAliasValue: "bookAliasValue",
bookAuthor: "bookAuthor",
bookBindingType: "bookBindingType",
bookDiff: "bookDiff",
bookDiffValue: "bookDiffValue",
bookIsbn: "bookIsbn",
bookLanguage: "bookLanguage",
bookNumberOfPages: "bookNumberOfPages",
bookOriginalTitle: "bookOriginalTitle",
bookPublisher: "bookPublisher",
bookPublishingDate: "bookPublishingDate",
bookRating: "bookRating",
bookSeriesId: "bookSeriesId",
bookSeriesNumber: "bookSeriesNumber",
bookSeriesTitle: "bookSeriesTitle",
bookTranslator: "bookTranslator",
bookType: "bookType",
bookValue: "bookValue",
bot: "bot",
botResponses: "botResponses",
botResponsesValue: "botResponsesValue",
botValue: "botValue",
brandColor: "brandColor",
brandColorName: "brandColorName",
brandColorNameValue: "brandColorNameValue",
brandColorValue: "brandColorValue",
bubbleTag: "bubbleTag",
bubbleTagValue: "bubbleTagValue",
buttonCaption: "buttonCaption",
buttonCaptionValue: "buttonCaptionValue",
callToActionUrl: "callToActionUrl",
callToActionUrlValue: "callToActionUrlValue",
caption: "caption",
captionDefinitionList: "captionDefinitionList",
captionValue: "captionValue",
cardBits: "cardBits",
cardBitsValue: "cardBitsValue",
cardNode: "cardNode",
categoryTag: "categoryTag",
categoryTagValue: "categoryTagValue",
cells: "cells",
cellsValue: "cellsValue",
chatWithBook: "chatWithBook",
chatWithBookBrainKey: "chatWithBookBrainKey",
chatWithBookBrainKeyValue: "chatWithBookBrainKeyValue",
chatWithBookValue: "chatWithBookValue",
checked: "checked",
choice: "choice",
choices: "choices",
choicesValue: "choicesValue",
citationStyle: "citationStyle",
citationStyleValue: "citationStyleValue",
class: "class",
classification: "classification",
classificationValue: "classificationValue",
codeLineNumbers: "codeLineNumbers",
codeLineNumbersValue: "codeLineNumbersValue",
codeMinimap: "codeMinimap",
codeMinimapValue: "codeMinimapValue",
color: "color",
colorTag: "colorTag",
colorTagValue: "colorTagValue",
column: "column",
columns: "columns",
columnsValue: "columnsValue",
comment: "comment",
commentedBitType: "commentedBitType",
computerLanguage: "computerLanguage",
computerLanguageValue: "computerLanguageValue",
content: "contentValue",
content2Buy: "content2Buy",
contentValue: "contentValue",
contentValueValue: "contentValueValue",
copyright: "copyright",
coverColor: "coverColor",
coverColorValue: "coverColorValue",
coverImage: "coverImage",
coverImageValue: "coverImageValue",
currency: "currency",
customerExternalId: "customerExternalId",
customerId: "customerId",
customerIdValue: "customerIdValue",
customerProductId: "customerProductId",
customerProductShopLink: "customerProductShopLink",
data: "data",
dataValue: "dataValue",
dataValueValue: "dataValueValue",
date: "date",
dateEnd: "dateEnd",
dateEndValue: "dateEndValue",
dateValue: "dateValue",
decimalPlaces: "decimalPlaces",
deeplink: "deeplink",
deeplinkValue: "deeplinkValue",
definition: "definition",
definitions: "definitions",
definitionsValue: "definitionsValue",
displayName: "displayName",
displayNameValue: "displayNameValue",
diffContext: "diffContext",
diffContextValue: "diffContextValue",
diffOp: "diffOp",
diffOpValue: "diffOpValue",
diffRef: "diffRef",
diffRefValue: "diffRefValue",
diffTime: "diffTime",
diffTimeValue: "diffTimeValue",
diffTo: "diffTo",
diffToValue: "diffToValue",
disableCalculation: "disableCalculation",
disableFeedback: "disableFeedback",
disableFeedbackValue: "disableFeedbackValue",
document: "document",
documentDownload: "documentDownload",
documentEmbed: "documentEmbed",
documentLink: "documentLink",
duration: "duration",
durationValue: "durationValue",
elements: "elements",
elementsValue: "elementsValue",
elementsValueValue: "elementsValueValue",
emphasis: "emphasis",
end: "end",
enforceUpdateOverUserInput: "enforceUpdateOverUserInput",
enforceUpdateOverUserInputValue: "enforceUpdateOverUserInputValue",
errors: "errors",
errorsValue: "errorsValue",
example: "example",
exampleValue: "exampleValue",
externalId: "externalId",
externalIdValue: "externalIdValue",
externalLink: "externalLink",
externalLinkText: "externalLinkText",
extractorExtractionTimestamp: "extractorExtractionTimestamp",
extractorExtractionTimestampValue: "extractorExtractionTimestampValue",
extractorTag: "extractorTag",
extractorTagValue: "extractorTagValue",
extractorInternal: "extractorInternal",
extractorInternalValue: "extractorInternalValue",
extraProperties: "extraProperties",
feedback: "feedback",
feedbackEngine: "feedbackEngine",
feedbackEngineValue: "feedbackEngineValue",
feedbacks: "feedbacks",
feedbacksValue: "feedbacksValue",
feedbackType: "feedbackType",
flashcards: "flashcards",
flashcardSet: "flashcardSet",
flashcardSetValue: "flashcardSetValue",
flashcardsValue: "flashcardsValue",
focusX: "focusX",
focusXValue: "focusXValue",
focusY: "focusY",
focusYValue: "focusYValue",
footer: "footer",
footerText: "footerText",
footerValue: "footerValue",
forKeys: "forKeys",
format: "format",
formula: "formula",
formulaValue: "formulaValue",
forValues: "forValues",
forValuesValue: "forValuesValue",
fullName: "fullName",
fullNameValue: "fullNameValue",
gap: "gap",
gmbExternalShop: "gmbExternalShop",
gmbExternalShopItems: "gmbExternalShopItems",
groupTag: "groupTag",
groupTagValue: "groupTagValue",
gUri: "gUri",
gUriValue: "gUriValue",
handInAcceptFileType: "handInAcceptFileType",
handInAcceptFileTypeValue: "handInAcceptFileTypeValue",
handInInstruction: "handInInstruction",
handInInstructionValue: "handInInstructionValue",
handInRequirement: "handInRequirement",
handInRequirementValue: "handInRequirementValue",
hasBookNavigation: "hasBookNavigation",
hasBookNavigationValue: "hasBookNavigationValue",
hasMarkAsDone: "hasMarkAsDone",
hasMarkAsDoneValue: "hasMarkAsDoneValue",
hasPrintRestriction: "hasPrintRestriction",
hasPrintRestrictionValue: "hasPrintRestrictionValue",
head: "head",
heading: "heading",
height: "height",
heightValue: "heightValue",
highlight: "highlight",
hint: "hint",
hintValue: "hintValue",
href: "href",
icon: "icon",
iconTag: "iconTag",
iconTagValue: "iconTagValue",
iconValue: "iconValue",
id: "id",
idValue: "idValue",
iframeName: "iframeName",
iframeSrc: "iframeSrc",
image: "image",
imageFirst: "imageFirst",
imageFirstValue: "imageFirstValue",
imageLandscape: "imageLandscape",
imageLink: "imageLink",
imagePlaceholder: "imagePlaceholder",
imagePlaceholderValue: "imagePlaceholderValue",
imagePortrait: "imagePortrait",
imageSource: "imageSource",
index: "index",
indexValue: "indexValue",
ingredients: "ingredients",
ingredientsValue: "ingredientsValue",
ingredient: "ingredient",
ingredientValue: "ingredientValue",
instruction: "instruction",
instructionValue: "instructionValue",
internalComment: "internalComment",
internalCommentValue: "internalCommentValue",
internalPrintPdf: "internalPrintPdf",
internalPrintPdfValue: "internalPrintPdfValue",
isCaseSensitive: "isCaseSensitive",
isCollapsible: "isCollapsible",
isCollapsibleValue: "isCollapsibleValue",
isCommented: "isCommented",
isCorrect: "isCorrect",
isEditable: "isEditable",
isEditableValue: "isEditableValue",
isExample: "isExample",
isInfoOnly: "isInfoOnly",
isInfoOnlyValue: "isInfoOnlyValue",
isPlain: "isPlain",
isPublic: "isPublic",
isPublicValue: "isPublicValue",
isTemplate: "isTemplate",
isTemplateStripTheme: "isTemplateStripTheme",
isTemplateStripThemeValue: "isTemplateStripThemeValue",
isTemplateValue: "isTemplateValue",
isTracked: "isTracked",
isTrackedValue: "isTrackedValue",
item: "item",
itemLead: "itemLead",
itemValue: "itemValue",
jobTitle: "jobTitle",
jobTitleValue: "jobTitleValue",
jupyterExecutionCount: "jupyterExecutionCount",
jupyterExecutionCountValue: "jupyterExecutionCountValue",
jupyterId: "jupyterId",
jupyterIdValue: "jupyterIdValue",
key: "key",
keyAudio: "keyAudio",
keyImage: "keyImage",
kind: "kind",
kindValue: "kindValue",
label: "label",
labelFalse: "labelFalse",
labelFalseValue: "labelFalseValue",
labelTrue: "labelTrue",
labelTrueValue: "labelTrueValue",
lang: "lang",
language: "language",
languageValue: "languageValue",
langValue: "langValue",
layer: "layer",
layerRole: "layerRole",
layerRoleValue: "layerRoleValue",
layerValue: "layerValue",
lead: "lead",
leadValue: "leadValue",
level: "level",
levelACTFL: "levelACTFL",
levelACTFLValue: "levelACTFLValue",
levelCEFR: "levelCEFR",
levelCEFRp: "levelCEFRp",
levelCEFRpValue: "levelCEFRpValue",
levelCEFRValue: "levelCEFRValue",
levelILR: "levelILR",
levelILRValue: "levelILRValue",
license: "license",
line: "line",
list: "list",
listItemIndent: "listItemIndent",
listItemIndentValue: "listItemIndentValue",
listPrice: "listPrice",
listValue: "listValue",
location: "location",
locationValue: "locationValue",
machineTranslated: "machineTranslated",
machineTranslatedValue: "machineTranslatedValue",
mailingList: "mailingList",
marginNumber: "marginNumber",
marginNumberValue: "marginNumberValue",
mark: "mark",
markConfig: "markConfig",
markConfigValue: "markConfigValue",
marks: "marks",
marksValue: "marksValue",
markup: "markup",
// bitmark markup
matrix: "matrix",
matrixValue: "matrixValue",
maxCreatedBits: "maxCreatedBits",
maxCreatedBitsValue: "maxCreatedBitsValue",
maxDisplayLevel: "maxDisplayLevel",
maxDisplayLevelValue: "maxDisplayLevelValue",
maxTocChapterLevel: "maxTocChapterLevel",
maxTocChapterLevelValue: "maxTocChapterLevelValue",
message: "message",
mockupId: "mockupId",
name: "name",
needsValidation: "needsValidation",
needsValidationValue: "needsValidationValue",
offset: "offset",
options: "options",
optionsValue: "optionsValue",
original: "original",
padletId: "padletId",
padletIdValue: "padletIdValue",
page: "page",
pageNo: "pageNo",
pageNoValue: "pageNoValue",
pageNumber: "pageNumber",
pageNumberValue: "pageNumberValue",
pageValue: "pageValue",
pairs: "pairs",
pairsValue: "pairsValue",
parent: "parent",
parser: "parser",
partialAnswer: "partialAnswer",
partialAnswerValue: "partialAnswerValue",
path: "path",
pathValue: "pathValue",
person: "person",
platformBackgroundColor: "platformBackgroundColor",
platformBackgroundColorValue: "platformBackgroundColorValue",
platformBackgroundImage: "platformBackgroundImage",
platformBackgroundImageValue: "platformBackgroundImageValue",
platformBaseLayerColor: "platformBaseLayerColor",
platformBaseLayerColorValue: "platformBaseLayerColorValue",
platformBorderColor: "platformBorderColor",
platformBorderColorValue: "platformBorderColorValue",
platformBorderRadius: "platformBorderRadius",
platformBorderRadiusValue: "platformBorderRadiusValue",
platformBrandTarget: "platformBrandTarget",
platformBrandTargetValue: "platformBrandTargetValue",
platformButtonBackgroundColor: "platformButtonBackgroundColor",
platformButtonBackgroundColorValue: "platformButtonBackgroundColorValue",
platformButtonPrimaryColor: "platformButtonPrimaryColor",
platformButtonPrimaryColorValue: "platformButtonPrimaryColorValue",
platformIcon: "platformIcon",
platformIconValue: "platformIconValue",
platformLogo: "platformLogo",
platformLogoMaxHeight: "platformLogoMaxHeight",
platformLogoMaxHeightValue: "platformLogoMaxHeightValue",
platformLogoValue: "platformLogoValue",
platformMargin: "platformMargin",
platformMarginValue: "platformMarginValue",
platformMessageBackgroundColor: "platformMessageBackgroundColor",
platformMessageBackgroundColorValue: "platformMessageBackgroundColorValue",
platformName: "platformName",
platformNameValue: "platformNameValue",
platformNeedsShadow: "platformNeedsShadow",
platformNeedsShadowValue: "platformNeedsShadowValue",
platformPlaceholderColor: "platformPlaceholderColor",
platformPlaceholderColorValue: "platformPlaceholderColorValue",
platformPrimaryColor: "platformPrimaryColor",
platformPrimaryColorValue: "platformPrimaryColorValue",
platformScrollbarColor: "platformScrollbarColor",
platformScrollbarColorValue: "platformScrollbarColorValue",
platformSecondaryColor: "platformSecondaryColor",
platformSecondaryColorValue: "platformSecondaryColorValue",
platformSelectionBorderRadius: "platformSelectionBorderRadius",
platformSelectionBorderRadiusValue: "platformSelectionBorderRadiusValue",
platformSelectionColor: "platformSelectionColor",
platformSelectionColorValue: "platformSelectionColorValue",
platformSelectionTextColor: "platformSelectionTextColor",
platformSelectionTextColorValue: "platformSelectionTextColorValue",
platformSeparatorColor: "platformSeparatorColor",
platformSeparatorColorValue: "platformSeparatorColorValue",
platformTextSelectionColor: "platformTextSelectionColor",
platformTextSelectionColorValue: "platformTextSelectionColorValue",
pointerLeft: "pointerLeft",
pointerLeftValue: "pointerLeftValue",
pointerTop: "pointerTop",
pointerTopValue: "pointerTopValue",
posterImage: "posterImage",
posterImageValue: "posterImageValue",
postfix: "postfix",
prefix: "prefix",
previewImage: "previewImage",
previewImageValue: "previewImageValue",
previewVideo: "previewVideo",
previewVideoValue: "previewVideoValue",
printParentChapterLevel: "printParentChapterLevel",
printParentChapterLevelValue: "printParentChapterLevelValue",
printOnRequestCustomerId: "printOnRequestCustomerId",
printOnRequestCustomerIdValue: "printOnRequestCustomerIdValue",
printOnRequestProductId: "printOnRequestProductId",
printOnRequestProductIdValue: "printOnRequestProductIdValue",
processHandIn: "processHandIn",
processHandInLocation: "processHandInLocation",
processHandInLocationValue: "processHandInLocationValue",
processHandInValue: "processHandInValue",
product: "product",
productFolder: "productFolder",
productFolderValue: "productFolderValue",
productId: "productId",
productIdValue: "productIdValue",
productList: "productList",
productListValue: "productListValue",
productValue: "productValue",
productVideo: "productVideo",
productVideoList: "productVideoList",
productVideoListValue: "productVideoListValue",
productVideoValue: "productVideoValue",
progress: "progress",
progressValue: "progressValue",
pronunciationTable: "pronunciationTable",
properties: "properties",
property: "property",
propertyKey: "propertyKey",
propertyValue: "propertyValue",
propertyValues: "propertyValues",
provider: "provider",
pseudonym: "pseudonym",
pseudonymValue: "pseudonymValue",
publications: "publications",
publicationsValue: "publicationsValue",
publisher: "publisher",
publisherName: "publisherName",
publisherNameValue: "publisherNameValue",
publisherValue: "publisherValue",
quantity: "quantity",
question: "question",
questions: "questions",
questionsValue: "questionsValue",
questionValue: "questionValue",
quizCountItems: "quizCountItems",
quizCountItemsValue: "quizCountItemsValue",
quizStrikethroughSolutions: "quizStrikethroughSolutions",
quizStrikethroughSolutionsValue: "quizStrikethroughSolutionsValue",
quizzes: "quizzes",
quizzesValue: "quizzesValue",
quotedPerson: "quotedPerson",
ratingLevelEnd: "ratingLevelEnd",
ratingLevelSelected: "ratingLevelSelected",
ratingLevelSelectedValue: "ratingLevelSelectedValue",
ratingLevelStart: "ratingLevelStart",
reaction: "reaction",
reason: "reason",
reasonableNumOfChars: "reasonableNumOfChars",
reasonableNumOfCharsValue: "reasonableNumOfCharsValue",
reductionTag: "reductionTag",
reductionTagValue: "reductionTagValue",
refAuthor: "refAuthor",
refBookTitle: "refBookTitle",
reference: "reference",
referenceEnd: "referenceEnd",
referenceProperty: "referenceProperty",
referencePropertyValue: "referencePropertyValue",
refPublicationYear: "refPublicationYear",
refPublisher: "refPublisher",
relatedBook: "relatedBook",
relatedBookValue: "relatedBookValue",
translationOfBook: "translationOfBook",
translationOfBookValue: "translationOfBookValue",
userFeedbackTranslationUrl: "userFeedbackTranslationUrl",
userFeedbackTranslationUrlValue: "userFeedbackTranslationUrlValue",
userFeedbackContentUrl: "userFeedbackContentUrl",
userFeedbackContentUrlValue: "userFeedbackContentUrlValue",
releaseDate: "releaseDate",
releaseDateValue: "releaseDateValue",
releaseKind: "releaseKind",
releaseKindValue: "releaseKindValue",
releaseVersion: "releaseVersion",
releaseVersionValue: "releaseVersionValue",
reportTag: "reportTag",
reportTagValue: "reportTagValue",
resolved: "resolved",
resolvedBy: "resolvedBy",
resolvedByValue: "resolvedByValue",
resolvedDate: "resolvedDate",
resolvedDateValue: "resolvedDateValue",
resolvedValue: "resolvedValue",
resources: "resources",
resourcesValue: "resourcesValue",
resourceType: "resourceType",
response: "response",
responses: "responses",
responsesValue: "responsesValue",
revealSolutions: "revealSolutions",
reviewTag: "reviewTag",
reviewTagValue: "reviewTagValue",
reviewerTag: "reviewerTag",
reviewerTagValue: "reviewerTagValue",
rightsPolicy: "rightsPolicy",
rightsPolicyValue: "rightsPolicyValue",
root: "root",
// bit type (root)
rows: "rows",
rowsValue: "rowsValue",
rtl: "rtl",
rtlValue: "rtlValue",
rubric: "rubric",
rubricValue: "rubricValue",
sampleSolution: "sampleSolution",
sampleSolutionValue: "sampleSolutionValue",
scormSource: "scormSource",
scormSourceValue: "scormSourceValue",
search: "search",
searchIndex: "searchIndex",
searchIndexValue: "searchIndexValue",
searchValue: "searchValue",
section: "section",
select: "select",
selected: "selected",
servings: "servings",
servingsValue: "servingsValue",
showInIndex: "showInIndex",
showInIndexValue: "showInIndexValue",
size: "size",
slug: "slug",
slugValue: "slugValue",
solution: "solution",
solutions: "solutions",
solutionsValue: "solutionsValue",
sourceBB: "sourceBB",
sourceBBValue: "sourceBBValue",
sourceDocument: "sourceDocument",
sourceDocumentValue: "sourceDocumentValue",
sourceRL: "sourceRL",
sourceRLValue: "sourceRLValue",
spaceId: "spaceId",
spaceIdValue: "spaceIdValue",
spansPageBreak: "spansPageBreak",
spansPageBreakValue: "spansPageBreakValue",
src: "src",
src1x: "src1x",
src2x: "src2x",
src3x: "src3x",
src4x: "src4x",
srcAlt: "srcAlt",
srcAltValue: "srcAltValue",
start: "start",
statement: "statement",
statementValue: "statementValue",
statements: "statements",
statementsValue: "statementsValue",
statementText: "statementText",
stillImageFilmEmbed: "stillImageFilmEmbed",
stillImageFilmLink: "stillImageFilmLink",
stripePricingTableId: "stripePricingTableId",
stripePricingTableIdValue: "stripePricingTableIdValue",
stripePricingTableDescriptiveName: "stripePricingTableDescriptiveName",
stripePricingTableDescriptiveNameValue: "stripePricingTableDescriptiveNameValue",
stripePublishableKey: "stripePublishableKey",
stripePublishableKeyValue: "stripePublishableKeyValue",
subject: "subject",
subjectValue: "subjectValue",
subtitle: "subtitle",
subtitleValue: "subtitleValue",
subtype: "subtype",
subtypeValue: "subtypeValue",
table: "table",
tableAutoWidth: "tableAutoWidth",
tableAutoWidthValue: "tableAutoWidthValue",
tableColWidth: "tableColWidth",
tableColWidthValue: "tableColWidthValue",
tableColumnMinWidth: "tableColumnMinWidth",
tableColumnMinWidthValue: "tableColumnMinWidthValue",
tableFixedHeader: "tableFixedHeader",
tableFixedHeaderValue: "tableFixedHeaderValue",
tableHeaderWhitespaceNoWrap: "tableHeaderWhitespaceNoWrap",
tableHeaderWhitespaceNoWrapValue: "tableHeaderWhitespaceNoWrapValue",
tableHeight: "tableHeight",
tableHeightValue: "tableHeightValue",
tablePagination: "tablePagination",
tablePaginationLimit: "tablePaginationLimit",
tablePaginationLimitValue: "tablePaginationLimitValue",
tablePaginationValue: "tablePaginationValue",
tableResizableColumns: "tableResizableColumns",
tableResizableColumnsValue: "tableResizableColumnsValue",
tableSearch: "tableSearch",
tableSearchValue: "tableSearchValue",
tableSort: "tableSort",
tableSortValue: "tableSortValue",
tableWhitespaceNoWrap: "tableWhitespaceNoWrap",
tableWhitespaceNoWrapValue: "tableWhitespaceNoWrapValue",
tag: "tag",
tagValue: "tagValue",
target: "target",
targetValue: "targetValue",
technicalTerm: "technicalTerm",
technicalTermValue: "technicalTermValue",
term: "term",
text: "text",
textAlign: "textAlign",
textAst: "textAst",
textAstValue: "textAstValue",
textFormat: "textFormat",
textReference: "textReference",
textReferenceValue: "textReferenceValue",
texts: "texts",
textsValue: "textsValue",
theme: "theme",
themeValue: "themeValue",
thumbImage: "thumbImage",
thumbImageValue: "thumbImageValue",
thumbnails: "thumbnails",
thumbnailsValue: "thumbnailsValue",
title: "title",
titleString: "titleString",
titleValue: "titleValue",
toc: "toc",
tocContent: "tocContent",
tocContentValue: "tocContentValue",
tocResource: "tocResource",
tocResourceValue: "tocResourceValue",
tocValue: "tocValue",
topicTag: "topicTag",
topicTagValue: "topicTagValue",
translationOf: "translationOf",
translationOfValue: "translationOfValue",
trim: "trim",
type: "type",
unit: "unit",
unitAbbr: "unitAbbr",
unitAbbrPlural: "unitAbbrPlural",
unitPlural: "unitPlural",
url: "url",
validationDate: "validationDate",
validationDateValue: "validationDateValue",
value: "value",
values: "values",
valuesValue: "valuesValue",
vendorDashboardId: "vendorDashboardId",
vendorDashboardIdValue: "vendorDashboardIdValue",
vendorSurveyId: "vendorSurveyId",
vendorSurveyIdValue: "vendorSurveyIdValue",
vendorUrl: "vendorUrl",
vendorUrlValue: "vendorUrlValue",
version: "version",
video: "video",
videoCallLink: "videoCallLink",
videoCallLinkValue: "videoCallLinkValue",
videoEmbed: "videoEmbed",
videoLink: "videoLink",
warnings: "warnings",
warningsValue: "warningsValue",
websiteLink: "websiteLink",
width: "width",
widthValue: "widthValue",
x: "x",
xValue: "xValue",
y: "y",
yValue: "yValue",
zoomDisabled: "zoomDisabled"
};
// src/ast/Ast.ts
var Ast = class {
constructor() {
init();
}
/**
* Walk bitmark AST, decending each branch and calling callbacks when entering, leaving, and when in between child
* nodes.
*
* Walking the tree can be used to convert it to another format (e.g. bitmark markup or JSON or text) or for analysis.
*
* The tree is navigated from root to leaf, decending each branch greedily.
*
* e.g. for the tree:
* ```
* A
* |__B1
* | |__C1
* | |__C2
* |__B2
* |__C3
*
* Enter A1
* Enter B1
* Leaf C1
* Betwe B1 (C1, C2)
* Leaf C2
* Exit B1
* Betwe A1 (B1, B2)
* Enter B2
* Leaf C3
* Exit B2
* Exit A1
* ```
*
* @param context - context object to pass to callbacks
* @param ast - bitmark / text AST
* @param type - type of AST to walk (i.e. NodeType.bitmarkAst, NodeType.textAst)
* @param callbacks - set of callbacks to call while walking the tree
*/
walk(ast, type, callbacks, context) {
this.walkRecursive(ast, [{ index: 0, key: type, value: ast }], callbacks, context);
}
/**
* Convert a route to a unique key that describes that route.
*
* For the route A1 -> B4 -> C2 the route key would be A1_B4_C2
*
* @param route - the tree path from the root to the curent node
* @returns
*/
getRouteKey(route) {
return route.reduce((acc, val, idx) => {
if (+val.key !== val.index) {
acc += `${val.key}`;
if (idx < route.length - 1) {
acc += "_";
}
}
return acc;
}, "");
}
/**
* Print an AST to the console.
* Useful for debug / development purposes
*
* @param ast - AST
* @param rootKey - root node key
*/
printTree(ast, rootKey = NodeType.bitmarkAst) {
this.walkRecursive(
ast,
[{ index: 0, key: rootKey, value: ast }],
{
enter: (_node, route) => {
console.log("Enter: " + this.getRouteKey(route));
if (route[route.length - 1].key === NodeType.bodyJson) return false;
return true;
},
between: (_node, _left, _right, route) => {
console.log("Between: " + this.getRouteKey(route));
},
exit: (_node, route) => {
console.log("Exit: " + this.getRouteKey(route));
},
leaf: (_node, route) => {
console.log("Leaf: " + this.getRouteKey(route));
}
},
void 0
);
}
/**
* Preprocess bitmark AST into a standard format (BitmarkAst object) from bitmark AST either as a string
* or a plain JS object
*
* @param ast - bitmark AST as a string or a plain JS object
* @returns bitmark AST in a standard format (BitmarkAst object)
*/
preprocessAst(ast) {
if (stringUtils.isString(ast)) {
const str = ast;
try {
ast = JSON.parse(str);
} catch (_e) {
return void 0;
}
}
if (this.isAst(ast)) {
return ast;
}
return void 0;
}
/**
* Check if a plain JS object is valid AST
*
* @param ast - a plain JS object that might be AST
* @returns true if AST, otherwise false
*/
isAst(ast) {
if (Object.prototype.hasOwnProperty.call(ast, "bits")) {
if (Array.isArray(ast.bits)) return true;
}
return false;
}
walkRecursive(node, route, callbacks, context) {
const { enter, between, exit, leaf } = callbacks;
const parentKey = route[route.length - 1].key;
const isValue = this.isValue(node);
const isBranch = !isValue;
const nodeInfo = route[route.length - 1];
if (isBranch) {
if (enter) {
const res = enter(nodeInfo, route, context);
if (res === false) return;
}
} else {
if (leaf) leaf(nodeInfo, route, context);
}
if (isBranch) {
const isArray = this.isArray(node);
const keys = Object.keys(node);
for (let i = 0, len = keys.length; i < len; i++) {
const key = keys[i];
const lastChild = i === len - 1;
const nodeAsAny = node;
const child = nodeAsAny[key];
if (child != null) {
const childNodeInfo = {
key: this.getAstKey(key, parentKey, isArray),
index: i,
value: child
};
const r = route.slice();
r.push(childNodeInfo);
this.walkRecursive(child, r, callbacks, context);
if (!lastChild) {
const nextKey = keys[i + 1];
const nextChild = nodeAsAny[nextKey];
const nextChildNodeInfo = {
key: this.getAstKey(nextKey, parentKey, isArray),
index: i + 1,
value: nextChild
};
if (between) {
const res = between(nodeInfo, childNodeInfo, nextChildNodeInfo, route, context);
if (res === false) break;
}
}
}
}
}
if (isBranch) {
if (exit) exit(nodeInfo, route, context);
}
}
getAstKey(key, parentKey, isParentArray) {
let astKey = key;
if (isParentArray && parentKey) {
astKey = `${parentKey}Value`;
}
return Enum4(NodeType).fromKey(astKey) || `unknown(${astKey})`;
}
isArray(x) {
return Array.isArray(x);
}
isObject(x) {
return Object.prototype.toString.call(x) === "[object Object]";
}
isValue(x) {
return !this.isObject(x) && !this.isArray(x);
}
};
// src/ast/Builder.ts
import { Enum as Enum7 } from "@ncoderz/superenum";
// src/model/enum/TextLocation.ts
import "@ncoderz/superenum";
var TextLocation = {
tag: "tag",
body: "body"
};
// src/model/enum/BodyTextFormat.ts
import "@ncoderz/superenum";
var BodyTextFormat = {
plainText: "text",
// plain text
latex: "latex",
// LaTeX code
json: "json",
// json
xml: "xml",
// xml
// bitmark++ text
bitmarkPlusPlus: "bitmark++"
};
// src/breakscaping/RegexConfigs.ts
var REGEX_MARKS = /([*`_!=])(?=\1)/;
var REGEX_BLOCKS = /^(\|)(code[\s]*|code:|image:|[\s]*$)/;
var REGEX_TITLE_BLOCKS = /^([#]{1,3})([^\S\r\n]+)/;
var REGEX_LIST_BLOCKS = /^(•)([0-9]+[iI]*|[a-zA-Z]{1}|_|\+|-|)([^\S\r\n]+)/;
var REGEX_START_OF_TAG = /(\[)([.@#▼►%!?+\-$_=&])/;
var REGEX_FOOTER_DIVIDER = /^(~)(~~~[ \t]*)$/;
var REGEX_PLAIN_TEXT_DIVIDER = /^(\$)(\$\$\$[ \t]*)$/;
var REGEX_END_OF_TAG = /(\^*])/;
var REGEX_BIT_START = /^(\[)(\^*)(\.)/;
var REGEX_HATS = /(\^+)/;
var BREAKSCAPE_BITMARK_TAG_REGEX_SOURCE = `${REGEX_MARKS.source}|${REGEX_END_OF_TAG.source}|${REGEX_HATS.source}`;
var BREAKSCAPE_PLAIN_TAG_REGEX_SOURCE = `${REGEX_END_OF_TAG.source}|${REGEX_HATS.source}`;
var BREAKSCAPE_BITMARK_BODY_REGEX_SOURCE = `${REGEX_MARKS.source}|${REGEX_BLOCKS.source}|${REGEX_TITLE_BLOCKS.source}|${REGEX_LIST_BLOCKS.source}|${REGEX_START_OF_TAG.source}|${REGEX_FOOTER_DIVIDER.source}|${REGEX_PLAIN_TEXT_DIVIDER.source}|${REGEX_HATS.source}`;
var BREAKSCAPE_PLAIN_BODY_REGEX_SOURCE = `${REGEX_BIT_START.source}`;
var BREAKSCAPE_BITMARK_TAG_REGEX = new RegExp(BREAKSCAPE_BITMARK_TAG_REGEX_SOURCE, "gm");
var BREAKSCAPE_BITMARK_TAG_REGEX_REPLACER = "$1$3^$2";
var BREAKSCAPE_PLAIN_TAG_REGEX = new RegExp(BREAKSCAPE_PLAIN_TAG_REGEX_SOURCE, "gm");
var BREAKSCAPE_PLAIN_TAG_REGEX_REPLACER = "$2^$1";
var BREAKSCAPE_BITMARK_BODY_REGEX = new RegExp(BREAKSCAPE_BITMARK_BODY_REGEX_SOURCE, "gm");
var BREAKSCAPE_BITMARK_BODY_REGEX_REPLACER = "$1$2$4$6$9$11$13$15^$3$5$7$8$10$12$14";
var BREAKSCAPE_PLAIN_BODY_REGEX = new RegExp(BREAKSCAPE_PLAIN_BODY_REGEX_SOURCE, "gm");
var BREAKSCAPE_PLAIN_BODY_REGEX_REPLACER = "$1^$2$3";
var BREAKSCAPE_V2_BODY_REGEX = new RegExp("^(?:(\\[)(\\^*)(\\.))|(\\^+)", "gm");
var BREAKSCAPE_V2_BODY_REGEX_REPLACER = "$1$4^$2$3";
var UNBREAKSCAPE_REGEX = new RegExp("\\^([\\^]*)", "gm");
var UNBREAKSCAPE_REGEX_REPLACER = "$1";
var UNBREAKSCAPE_PLAIN_IN_BODY_REGEX = new RegExp("^(\\[)\\^(\\^*)(\\.)", "gm");
var UNBREAKSCAPE_PLAIN_IN_BODY_REGEX_REPLACER = "$1$2$3";
var BREAKSCAPE_CODE_REGEX = new RegExp("^(\\||\u2022|#)", "gm");
var BREAKSCAPE_CODE_REGEX_REPLACER = "$1^";
// src/breakscaping/BreakscapeRegex.ts
var DEF = {
format: BodyTextFormat.bitmarkPlusPlus,
location: TextLocation.body,
v2: false
};
function isString(obj) {
return typeof obj === "string" || obj instanceof String;
}
function selectBreakscapeRegexAndReplacer(textFormat, textLocation, v2 = false) {
if (textLocation === TextLocation.tag) {
if (!v2 && textFormat === BodyTextFormat.bitmarkPlusPlus) {
return {
regex: BREAKSCAPE_BITMARK_TAG_REGEX,
replacer: BREAKSCAPE_BITMARK_TAG_REGEX_REPLACER
};
}
return {
regex: BREAKSCAPE_PLAIN_TAG_REGEX,
replacer: BREAKSCAPE_PLAIN_TAG_REGEX_REPLACER
};
} else {
if (textFormat === BodyTextFormat.bitmarkPlusPlus) {
if (v2) {
return {
regex: BREAKSCAPE_V2_BODY_REGEX,
replacer: BREAKSCAPE_V2_BODY_REGEX_REPLACER
};
}
return {
regex: BREAKSCAPE_BITMARK_BODY_REGEX,
replacer: BREAKSCAPE_BITMARK_BODY_REGEX_REPLACER
};
}
return {
regex: BREAKSCAPE_PLAIN_BODY_REGEX,
replacer: BREAKSCAPE_PLAIN_BODY_REGEX_REPLACER
};
}
}
function selectUnbreakscapeRegexAndReplacer(textFormat, textLocation) {
const isPlain = textFormat !== BodyTextFormat.bitmarkPlusPlus;
if (textLocation === TextLocation.body && isPlain) {
return {
regex: UNBREAKSCAPE_PLAIN_IN_BODY_REGEX,
replacer: UNBREAKSCAPE_PLAIN_IN_BODY_REGEX_REPLACER
};
}
return {
regex: UNBREAKSCAPE_REGEX,
replacer: UNBREAKSCAPE_REGEX_REPLACER
};
}
var Breakscape = class {
constructor() {
__publicField(this, "EMPTY_STRING", "");
}
breakscape(val, opts = {}) {
if (val == null) return void 0;
const options = Object.assign({}, DEF, opts);
if (Array.isArray(val)) {
return val.map((v) => this.breakscape(v, options));
}
if (!isString(val)) return val;
const { regex, replacer } = selectBreakscapeRegexAndReplacer(
options.format,
options.location,
options.v2
);
let str = val;
str = str.replace(regex, replacer);
return str;
}
unbreakscape(val, opts = {}) {
if (val == null) return void 0;
const options = Object.assign({}, DEF, opts);
if (Array.isArray(val)) {
return val.map((v) => this.unbreakscape(v, options));
}
if (!isString(val)) return val;
const { regex, replacer } = selectUnbreakscapeRegexAndReplacer(
options.format,
options.location
);
let str = val;
str = str.replace(regex, replacer);
return str;
}
/**
* Breakscape a code string or an array of code strings.
* If the input is an array, a new array will be returned.
*
* @param val input value
* @param modifyArray if true, the original array will be modified rather than a copy being made
* @returns the input value with any strings breakscaped
*/
breakscapeCode(val, options) {
if (val == null) return val;
const opts = Object.assign({}, DEF, options);
const breakscapeStr = (str) => {
if (!str) return str;
return str.replace(BREAKSCAPE_CODE_REGEX, BREAKSCAPE_CODE_REGEX_REPLACER);
};
if (Array.isArray(val)) {
const newVal = opts.inPlaceArray ? val : [val.length];
for (let i = 0, len = val.length; i < len; i++) {
const v = val[i];
if (isString(v)) {
val[i] = breakscapeStr(v);
}
}
val = newVal;
} else if (isString(val)) {
val = breakscapeStr(val);
}
return val;
}
/**
* Gets the version of the breakscape library.
*
* @returns The current version string of the library.
*
* @example
* ```typescript
* const breakscape = new Breakscape();
* console.log(breakscape.version()); // e.g., "1.0.0"
* ```
*/
version() {
return PACKAGE_INFO.version;
}
/**
* Gets the license information for the breakscape library.
*
* @returns The license string for the library.
*
* @example
* ```typescript
* const breakscape = new Breakscape();
* console.log(breakscape.license()); // e.g., "MIT"
* ```
*/
license() {
return PACKAGE_INFO.license;
}
};
// src/breakscaping/Breakscape.ts
var DEFAULT_BREAKSCAPE_OPTIONS = {
format: TextFormat.bitmarkText,
location: TextLocation.body,
inPlaceArray: false,
v2: false
};
var externalBreakscape = new Breakscape();
var Breakscape2 = class {
constructor() {
__publicField(this, "EMPTY_STRING", "");
}
/**
* Breakscape a string or an array of strings.
* If the input is an array, a new array will be returned.
*
* @param val input value
* @param options options for breakscaping
* @param modifyArray
* @returns the input value with any strings breakscaped.
*/
breakscape(val, options) {
const opts = Object.assign({}, DEFAULT_BREAKSCAPE_OPTIONS, options);
return externalBreakscape.breakscape(val, {
format: opts.format,
location: opts.location,
inPlaceArray: opts.inPlaceArray,
v2: opts.v2
});
}
/**
* Unbreakscape a string or an array of strings.
* If the input is an array, a new array will be returned.
*
* @param val input value
* @param modifyArray if true, the original array will be modified rather than a copy being made
* @returns the input value with any strings unbreakscaped.
*/
unbreakscape(val, options) {
const opts = Object.assign({}, DEFAULT_BREAKSCAPE_OPTIONS, options);
return externalBreakscape.unbreakscape(val, opts);
}
/**
* Breakscape a code string or an array of code strings.
* If the input is an array, a new array will be returned.
*
* @param val input value
* @param modifyArray if true, the original array will be modified rather than a copy being made
* @returns the input value with any strings breakscaped
*/
breakscapeCode(val, options) {
const opts = Object.assign({}, DEFAULT_BREAKSCAPE_OPTIONS, options);
return externalBreakscape.breakscapeCode(val, opts);
}
/**
* Concatenate two breakscaped strings.
*
* @param s1 first string
* @param s2 second string
* @returns the concatenated string
*/
concatenate(s1, s2) {
return s1 + s2;
}
};
var instance3 = new Breakscape2();
// src/model/enum/DeprecatedTextFormat.ts
import "@ncoderz/superenum";
var DeprecatedTextFormat = {
bitmarkMinusMinus: "bitmark--"
// bitmark-- text format, deprecated
};
// src/parser/json/TableUtils.ts
function isTableBasicFormat(table) {
const t = table;
return !!(t.columns || t.data) && !(t.header || t.body || t.footer);
}
function isTableExtendedFormat(table) {
const t = table;
return !!(t.header || t.body || t.footer);
}
function isMixedTableFormat(table) {
const t = table;
const hasOld = !!(t.columns || t.data);
const hasNew = !!(t.header || t.body || t.footer);
return hasOld && hasNew;
}
function convertBasicToExtendedTableFormat(table) {
const tableExtended = {};
if (table.columns && table.columns.length > 0) {
tableExtended.header = {
rows: [
{
cells: table.columns.map((col) => ({
content: col,
title: true
}))
}
]
};
}
if (table.data && table.data.length > 0) {
tableExtended.body = {
rows: table.data.map((row) => ({
cells: row.map((cell) => ({
content: cell
}))
}))
};
}
if (Array.isArray(table.columnWidths) && table.columnWidths.length > 0) {
const targetCells = tableExtended.header?.rows[0]?.cells ?? tableExtended.body?.rows[0]?.cells;
if (targetCells) {
for (let i = 0; i < table.columnWidths.length; i++) {
const width = table.columnWidths[i];
if (typeof width === "number" && width > 0 && targetCells[i]) {
targetCells[i].colwidth = width;
}
}
}
}
return tableExtended;
}
function convertExtendedToBasicTableFormat(tableExtended) {
const table = {};
const extractRowCells = (row) => {
if (!row || !Array.isArray(row.cells)) {
return [];
}
return row.cells.map((cell) => cell?.content).filter((content) => content !== void 0);
};
const dataRows = [];
const widths = [];
let hasAnyWidth = false;
let maxCellCount = 0;
const collectWidths = (cells) => {
if (!cells) return;
if (cells.length > maxCellCount) maxCellCount = cells.length;
cells.forEach((cell, idx) => {
if (cell && typeof cell.colwidth === "number" && cell.colwidth > 0) {
widths[idx] = cell.colwidth;
hasAnyWidth = true;
}
});
};
const headRows = tableExtended.header?.rows ?? [];
headRows.forEach((row) => collectWidths(row?.cells));
(tableExtended.body?.rows ?? []).forEach((row) => collectWidths(row?.cells));
(tableExtended.footer?.rows ?? []).forEach((row) => collectWidths(row?.cells));
if (headRows.length > 0) {
const primaryHeader = extractRowCells(headRows[0]);
if (primaryHeader.length > 0) {
table.columns = primaryHeader;
}
const remainingHeadRows = headRows.slice(1);
remainingHeadRows.forEach((row) => {
const cells = extractRowCells(row);
if (cells.length > 0) {
dataRows.push(cells);
}
});
}
const appendSectionRows = (section) => {
if (!section || !Array.isArray(section.rows)) {
return;
}
section.rows.forEach((row) => {
const cells = extractRowCells(row);
if (cells.length > 0) {
dataRows.push(cells);
}
});
};
appendSectionRows(tableExtended.body);
appendSectionRows(tableExtended.footer);
if (dataRows.length > 0) {
table.data = dataRows;
}
if (hasAnyWidth) {
const columnCount = table.columns?.length ?? maxCellCount;
const columnWidths = new Array(columnCount).fill(null);
for (let i = 0; i < columnCount; i++) {
const w = widths[i];
if (typeof w === "number") columnWidths[i] = w;
}
table.columnWidths = columnWidths;
}
return table;
}
function normalizeTableFormat(bitType, table) {
if (isMixedTableFormat(table)) {
const t = table;
delete t.columns;
delete t.data;
}
const isExtended = instance2.isOfBitType(bitType, BitType.tableExtended) || instance2.isOfBitType(bitType, BitType.tableImage);
if (isExtended && isTableBasicFormat(table)) {
return convertBasicToExtendedTableFormat(table);
}
if (!isExtended && isTableExtendedFormat(table)) {
return convertExtendedToBasicTableFormat(table);
}
return table;
}
// src/utils/ArrayUtils.ts
var ArrayUtils = class {
/**
* Convert a value to an array, unless it is already an array.
*
* This function is useful for handling parameters that can be a single value or an array of values.
*
* @param val input value
* @returns the input value if it is an array, otherwise a new array containing the input value
*/
asArray(val) {
if (val == null) return void 0;
if (Array.isArray(val)) return val;
return [val];
}
/**
* Convert an array or value to a value, unless it is already a value.
* The first item in the array will be selected
*
* This function is useful for handling parameters that can be a single value or an array of values.
*
* @param val input value
* @param last if true, the last item in the array will be selected, rather than the first
* @returns the input value if it is not an array, otherwise the first item in the array
*/
asSingle(val, last) {
if (val == null) return void 0;
if (Array.isArray(val)) {
if (val.length > 0) return last ? val[val.length - 1] : val[0];
return void 0;
}
return val;
}
/**
* Removes all duplicate items, keeping the first duplicate only.
* The input array is modified in place.
*
* @param arr array in which to make items unique
* @param keyFunc function(item) to return the key to check for equality. - optional
* @returns the input array with all duplicate items removed
*/
removeDuplicates(arr, keyFunc) {
const seen = /* @__PURE__ */ new Set();
let write = 0;
for (let read = 0; read < arr.length; read++) {
const item = arr[read];
const k = keyFunc ? keyFunc(item) : item;
if (!seen.has(k)) {
seen.add(k);
if (write !== read) arr[write] = item;
write++;
}
}
if (write < arr.length) arr.length = write;
return arr;
}
};
var instance4 = new ArrayUtils();
// src/generator/text/TextGenerator.ts
import { Enum as Enum5 } from "@ncoderz/superenum";
// src/model/enum/BitmarkVersion.ts
import "@ncoderz/superenum";
var BitmarkVersion = {
v2: 2,
v3: 3
};
var DEFAULT_BITMARK_VERSION = BitmarkVersion.v3;
// src/generator/AstWalkerGenerator.ts
var AstWalkerGenerator = class {
constructor() {
__publicField(this, "ast", new Ast());
// Debugging
__publicField(this, "debugGenerationInline", false);
__publicField(this, "printed", false);
}
enter(node, route, context) {
let res = void 0;
const gen = this;
const funcName = `enter_${node.key}`;
if (!this.printed) {
this.printed = true;
}
if (this.debugGenerationInline) this.writeInlineDebug(node.key, { open: true });
if (typeof gen[funcName] === "function") {
res = gen[funcName](node, route, context);
}
return res;
}
between(node, left, right, route, context) {
let res = void 0;
const gen = this;
const funcName = `between_${node.key}`;
if (this.debugGenerationInline) this.writeInlineDebug(node.key, { single: true });
if (typeof gen[funcName] === "function") {
res = gen[funcName](node, left, right, route, context);
}
return res;
}
exit(node, route, context) {
const gen = this;
const funcName = `exit_${node.key}`;
if (this.debugGenerationInline) this.writeInlineDebug(node.key, { close: true });
if (typeof gen[funcName] === "function") {
gen[funcName](node, route, context);
}
}
leaf(node, route, context) {
const gen = this;
const funcName = `leaf_${node.key}`;
if (this.debugGenerationInline) this.writeInlineDebug(node.key, { open: true });
if (typeof gen[funcName] === "function") {
gen[funcName](node, route, context);
}
if (this.debugGenerationInline) this.writeInlineDebug(node.key, { close: true });
}
//
// HELPER FUNCTIONS
//
getParentNode(route, nodesBack = 1) {
if (route.length > nodesBack) {
return route[route.length - nodesBack - 1];
}
return void 0;
}
getSiblingNodes(route) {
const parentNode = this.getParentNode(route);
if (!parentNode || !parentNode.value || !Array.isArray(parentNode.value)) {
return {};
}
const siblings = parentNode.value;
const index = siblings.findIndex((s) => s === route[route.length - 1]?.value);
if (index === -1) {
return {};
}
return {
left: index > 0 ? siblings[index - 1] : void 0,
right: index < siblings.length - 1 ? siblings[index + 1] : void 0
};
}
isEmptyParagraph(node) {
if (!node) return false;
if (node.type !== TextNodeType.paragraph) return false;
if (!node.content || !Array.isArray(node.content)) return true;
for (const contentNode of node.content) {
if (contentNode.type !== TextNodeType.text) return false;
if (contentNode.text && contentNode.text.trim() !== "") return false;
}
return true;
}
};
// src/generator/text/TextGenerator.ts
var DEFAULT_OPTIONS = {
bodyBitCallback: void 0,
debugGenerationInline: false
};
var FIRST_PARAGRAPH_CONTENT_DEPTH = 3;
var BOLD_HALF_MARK = "*";
var LIGHT_HALF_MARK = "`";
var ITALIC_HALF_MARK = "_";
var HIGHLIGHT_HALF_MARK = "!";
var INLINE_HALF_MARK = "=";
var BOLD_MARK = BOLD_HALF_MARK + BOLD_HALF_MARK;
var LIGHT_MARK = LIGHT_HALF_MARK + LIGHT_HALF_MARK;
var ITALIC_MARK = ITALIC_HALF_MARK + ITALIC_HALF_MARK;
var HIGHLIGHT_MARK = HIGHLIGHT_HALF_MARK + HIGHLIGHT_HALF_MARK;
var INLINE_MARK = INLINE_HALF_MARK + INLINE_HALF_MARK;
var ALL_HALF_MARKS = [
BOLD_HALF_MARK,
LIGHT_HALF_MARK,
ITALIC_HALF_MARK,
HIGHLIGHT_HALF_MARK,
INLINE_HALF_MARK
];
var ALL_TAG_OPENING_ENDS = "@#\u25BC\u25BA%!?+-$_=&";
var HEADING_TAG = "#";
var STANDARD_MARKS = {
[TextMarkType.bold]: BOLD_MARK,
[TextMarkType.light]: LIGHT_MARK,
[TextMarkType.italic]: ITALIC_MARK,
[TextMarkType.highlight]: HIGHLIGHT_MARK
};
var STANDARD_MARK_TYPES = [
TextMarkType.bold,
TextMarkType.light,
TextMarkType.italic,
TextMarkType.highlight
];
var INLINE_MARK_TYPES = [
TextMarkType.highlightOrange,
TextMarkType.highlightYellow,
TextMarkType.highlightGreen,
TextMarkType.highlightBlue,
TextMarkType.highlightPurple,
TextMarkType.highlightPink,
TextMarkType.highlightBrown,
TextMarkType.highlightBlack,
TextMarkType.highlightWhite,
TextMarkType.highlightGray,
TextMarkType.strike,
TextMarkType.subscript,
TextMarkType.superscript,
TextMarkType.ins,
TextMarkType.del,
TextMarkType.underline,
TextMarkType.doubleUnderline,
TextMarkType.smallcaps,
TextMarkType.circle,
TextMarkType.languageEmRed,
TextMarkType.languageEmOrange,
TextMarkType.languageEmYellow,
TextMarkType.languageEmGreen,
TextMarkType.languageEmBlue,
TextMarkType.languageEmPurple,
TextMarkType.languageEmPink,
TextMarkType.languageEmBrown,
TextMarkType.languageEmBlack,
TextMarkType.languageEmWhite,
TextMarkType.languageEmGray,
TextMarkType.languageEm,
TextMarkType.userUnderline,
TextMarkType.userDoubleUnderline,
TextMarkType.userStrike,
TextMarkType.userCircle,
TextMarkType.userHighlight,
TextMarkType.notranslate,
//
TextMarkType.link,
TextMarkType.ref,
TextMarkType.xref,
TextMarkType.extref,
TextMarkType.footnote,
TextMarkType.footnoteStar,
TextMarkType.symbol,
TextMarkType.var,
TextMarkType.code,
TextMarkType.timer,
TextMarkType.color,
// legacy input, written as color: (same as textStyle)
TextMarkType.textStyle,
TextMarkType.colorPicker,
TextMarkType.comment
];
var HIGHLIGHT_COLORS = [
"orange",
"yellow",
"green",
"blue",
"purple",
"pink",
"brown",
"white",
"black",
"gray"
];
var INDENTATION_REGEX = new RegExp(/(\n|\r\n)/, "g");
var LINK_REGEX = new RegExp(/https?:\/\/|mailto:(.*)/, "g");
var LINK_BREAKSCAPE_REGEX = new RegExp(/\]/, "g");
var Stage = {
enter: "enter",
between: "between",
exit: "exit"
};
var TextGenerator = class _TextGenerator extends AstWalkerGenerator {
/**
* Generate text from a bitmark text AST
*
* @param bitmarkVersion - bitmark version, use null to use latest version
* @param options - text generation options
*/
constructor(bitmarkVersion, options) {
super();
__publicField(this, "ast", new Ast());
__publicField(this, "bitmarkVersion");
__publicField(this, "options");
__publicField(this, "internalTextGenerator", null);
// State
__publicField(this, "generateOptions", {});
__publicField(this, "textFormat", TextFormat.bitmarkText);
__publicField(this, "textLocation", TextLocation.body);
__publicField(this, "writerText", "");
__publicField(this, "nodeIndex", 0);
__publicField(this, "currentIndent", 0);
__publicField(this, "prevIndent", 0);
__publicField(this, "indentationStringCache", "");
__publicField(this, "inParagraph", false);
__publicField(this, "inHeading", false);
__publicField(this, "inCodeBlock", false);
__publicField(this, "inBulletList", false);
__publicField(this, "inInline", false);
__publicField(this, "markDepth", 0);
__publicField(this, "textDepth", 0);
__publicField(this, "placeholderIndex", 0);
__publicField(this, "placeholders", {});
// For pre-text
__publicField(this, "rootParagraphNodeContentIndex", 0);
__publicField(this, "previousRootParagraphContextType", TextNodeType.text);
__publicField(this, "inPreText", false);
__publicField(this, "thisNodeIsPreText", false);
__publicField(this, "preTextIndexTemp", -1);
__publicField(this, "havePreText", false);
__publicField(this, "preTextIndex", -1);
this.bitmarkVersion = Enum5(BitmarkVersion).fromValue(bitmarkVersion) ?? DEFAULT_BITMARK_VERSION;
this.options = {
...DEFAULT_OPTIONS,
...options
};
this.debugGenerationInline = this.options.debugGenerationInline ?? false;
if (this.bitmarkVersion === BitmarkVersion.v2) {
} else {
}
this.enter = this.enter.bind(this);
this.between = this.between.bind(this);
this.exit = this.exit.bind(this);
this.leaf = this.leaf.bind(this);
if (!this.options.isInternal) {
this.internalTextGenerator = new _TextGenerator(bitmarkVersion, {
...this.options,
writeCallback: void 0,
isInternal: true
});
}
}
/**
* Generate text from a bitmark text AST
*
* @param ast bitmark text AST
*/
async generate(ast, textFormat, textLocation, options) {
return this.generateSync(ast, textFormat, textLocation, options);
}
/**
* Generate text from a bitmark text AST synchronously
*
* @param ast bitmark text AST
*/
generateSync(ast, textFormat, textLocation, options) {
if (!ast || !Array.isArray(ast)) return "";
this.generateOptions = Object.assign({}, options);
this.validateGenerateOptions(ast);
if (!this.generateOptions.plainTextDividerAllowed) {
this.resetState(textFormat, textLocation);
this.walkAndWrite(ast);
} else {
const writeCallback = this.options.writeCallback;
const bodyBitCallback = this.options.bodyBitCallback;
this.options.writeCallback = void 0;
this.options.bodyBitCallback = void 0;
this.resetState(textFormat, textLocation);
this.walkAndWrite(ast);
this.options.writeCallback = writeCallback;
this.options.bodyBitCallback = bodyBitCallback;
const havePreText = this.havePreText;
const pti = this.preTextIndex;
this.resetState(textFormat, textLocation);
this.havePreText = havePreText;
this.preTextIndex = pti;
this.walkAndWrite(ast);
}
return this.writerText;
}
getPlaceholders() {
return this.placeholders;
}
resetState(textFormat, textLocation) {
this.printed = false;
this.textFormat = textFormat ?? TextFormat.bitmarkText;
this.textLocation = textLocation;
this.writerText = "";
this.nodeIndex = 0;
this.currentIndent = 0;
this.prevIndent = 0;
this.indentationStringCache = "";
this.inParagraph = false;
this.inHeading = false;
this.inCodeBlock = false;
this.inBulletList = false;
this.inInline = false;
this.markDepth = 0;
this.textDepth = 0;
this.placeholderIndex = 0;
this.placeholders = {};
this.rootParagraphNodeContentIndex = 0;
this.previousRootParagraphContextType = TextNodeType.hardBreak;
this.inPreText = false;
this.thisNodeIsPreText = false;
this.preTextIndexTemp = -1;
this.havePreText = false;
this.preTextIndex = -1;
}
walkAndWrite(ast) {
this.ast.walk(ast, NodeType.textAst, this, void 0);
}
//
// NODE HANDLERS
//
// * -> textAstValue
enter_textAstValue(node, route) {
return this.handleEnterNode(node.value, route);
}
between_textAstValue(node, left, right, route) {
return this.handleBetweenNode(node.value, left, right, route);
}
exit_textAstValue(node, route) {
return this.handleExitNode(node.value, route);
}
// * -> contentValue
enter_contentValue(node, route) {
return this.handleEnterContentValueNode(node, route);
}
between_contentValue(node, left, right, route) {
this.handleBetweenContentValueNode(node, left, right, route);
}
exit_contentValue(node, route) {
this.handleExitContentValueNode(node, route);
}
// * -> contentValueValue
enter_contentValueValue(node, route) {
return this.handleEnterNode(node.value, route);
}
between_contentValueValue(node, left, right, route) {
this.handleBetweenNode(node.value, left, right, route);
}
exit_contentValueValue(node, route) {
return this.handleExitNode(node.value, route);
}
// * -> marks
enter_marks(_node, _route) {
this.markDepth++;
}
exit_marks(_node, _route) {
this.markDepth--;
}
// END NODE HANDLERS
handleEnterNode(node, route) {
if (this.markDepth > 0) return;
this.handleEnterNodePreTextCheck(node, route);
this.handleIndent(node);
switch (node.type) {
case TextNodeType.paragraph:
this.inParagraph = true;
this.writeParagraph(node, route);
break;
case TextNodeType.hardBreak:
this.writeHardBreak(node);
break;
case TextNodeType.text: {
this.writeMarks(node, Stage.enter);
this.writeText(node);
this.writeMarks(node, Stage.between);
if (this.textDepth === 0) {
this.inInline = true;
}
this.textDepth++;
break;
}
case TextNodeType.heading:
this.writeHeading(node);
this.inHeading = true;
break;
case TextNodeType.section:
this.writeSection(node);
break;
case TextNodeType.listItem:
case TextNodeType.taskItem:
this.writeBullet(node, route);
break;
case TextNodeType.image:
this.writeImage(node);
break;
case TextNodeType.imageInline:
this.writeImage(node);
break;
case TextNodeType.codeBlock:
this.inCodeBlock = true;
this.writeCodeBlock(node);
break;
case TextNodeType.latex:
this.writeLatex(node);
break;
case TextNodeType.noBulletList:
case TextNodeType.bulletList:
case TextNodeType.orderedList:
case TextNodeType.orderedListRoman:
case TextNodeType.orderedListRomanLower:
case TextNodeType.letteredList:
case TextNodeType.letteredListLower:
case TextNodeType.taskList:
this.inBulletList = true;
break;
case TextNodeType.gap:
case TextNodeType.select:
case TextNodeType.highlight:
case TextNodeType.mark:
this.writeBodyBit(node, route);
return false;
default:
}
}
handleBetweenNode(node, _left, _right, _route) {
switch (node.type) {
default:
}
}
handleExitNode(node, _route) {
if (this.markDepth > 0) return;
switch (node.type) {
case TextNodeType.text:
this.textDepth--;
if (this.textDepth === 0) {
this.inInline = false;
}
this.writeMarks(node, Stage.exit);
break;
case TextNodeType.paragraph:
this.writeNL();
this.inParagraph = false;
break;
case TextNodeType.heading:
this.inHeading = false;
case TextNodeType.section:
case TextNodeType.image:
if (!this.inParagraph) {
this.writeNL();
this.writeNL();
}
break;
case TextNodeType.codeBlock:
this.writeNL();
this.writeNL();
this.inCodeBlock = false;
break;
case TextNodeType.noBulletList:
case TextNodeType.bulletList:
case TextNodeType.orderedList:
case TextNodeType.orderedListRoman:
case TextNodeType.orderedListRomanLower:
case TextNodeType.letteredList:
case TextNodeType.letteredListLower:
case TextNodeType.taskList:
if (this.currentIndent <= 1) {
this.writeNL();
this.inBulletList = false;
}
break;
default:
}
this.handleDedent(node);
this.nodeIndex++;
}
handleIndent(node) {
switch (node.type) {
case TextNodeType.noBulletList:
case TextNodeType.bulletList:
case TextNodeType.orderedList:
case TextNodeType.orderedListRoman:
case TextNodeType.orderedListRomanLower:
case TextNodeType.letteredList:
case TextNodeType.letteredListLower:
case TextNodeType.taskList:
this.currentIndent++;
break;
default:
}
}
handleDedent(node) {
switch (node.type) {
case TextNodeType.noBulletList:
case TextNodeType.bulletList:
case TextNodeType.orderedList:
case TextNodeType.orderedListRoman:
case TextNodeType.orderedListRomanLower:
case TextNodeType.letteredList:
case TextNodeType.letteredListLower:
case TextNodeType.taskList:
this.currentIndent--;
break;
default:
}
}
handleEnterContentValueNode(_node, route) {
if (route.length === FIRST_PARAGRAPH_CONTENT_DEPTH) {
this.thisNodeIsPreText = true;
this.inPreText = false;
this.previousRootParagraphContextType = TextNodeType.hardBreak;
}
}
handleBetweenContentValueNode(_node, left, _right, route) {
if (route.length === FIRST_PARAGRAPH_CONTENT_DEPTH) {
const leftNode = left.value;
this.updatePreTextState();
this.thisNodeIsPreText = true;
this.previousRootParagraphContextType = leftNode.type;
this.rootParagraphNodeContentIndex++;
}
}
handleExitContentValueNode(_node, route) {
if (route.length === FIRST_PARAGRAPH_CONTENT_DEPTH) {
this.updatePreTextState();
this.thisNodeIsPreText = false;
if (this.generateOptions.plainTextDividerAllowed && this.inPreText) {
this.havePreText = true;
this.preTextIndex = this.preTextIndexTemp;
}
this.inPreText = false;
}
}
updatePreTextState() {
if (!this.inPreText && this.thisNodeIsPreText && this.previousRootParagraphContextType === TextNodeType.hardBreak) {
this.inPreText = true;
this.preTextIndexTemp = this.rootParagraphNodeContentIndex;
} else if (this.inPreText && !this.thisNodeIsPreText) {
this.inPreText = false;
this.preTextIndexTemp = -1;
}
}
handleEnterNodePreTextCheck(node, route) {
const firstParagraphContent = route.length === 4;
if (firstParagraphContent) {
if (this.inPreText && node.type === TextNodeType.hardBreak) return;
if (node.type === TextNodeType.text) return;
}
this.thisNodeIsPreText = false;
}
/**
* Check if a text node has a link mark, and if so, return the href
*
* @param node the node to check for a link mark
* @returns the href, or false if no link mark
*/
getLinkHref(node) {
if (node.type === TextNodeType.text && node.marks) {
const href = node.marks.reduce((acc, mark) => {
if (mark.type === TextMarkType.link) {
const linkMark = mark;
const href2 = linkMark.attrs?.href;
if (href2) return href2;
}
return acc;
}, "");
if (href) return href;
}
return false;
}
/**
* Get the current indentation string
* @returns
*/
getIndentationString() {
if (this.currentIndent === this.prevIndent) return this.indentationStringCache;
let s = "";
for (let i = 1; i < this.currentIndent; i++) s += " ";
this.indentationStringCache = s;
this.prevIndent = this.currentIndent;
return s;
}
//
// WRITE FUNCTIONS
//
writeBodyBit(node, route) {
const placeholder = `[!${this.placeholderIndex}]`;
this.placeholders[placeholder] = node;
if (this.options.bodyBitCallback) {
const bodyBit = this.options.bodyBitCallback(
node,
this.placeholderIndex,
route
);
this.write(bodyBit);
} else {
this.write(placeholder);
}
this.placeholderIndex++;
}
writeText(node) {
if (node.text == null) return;
const codeBreakscaping = this.inCodeBlock;
let s = node.text;
const linkText = this.getLinkText(node);
if (linkText) s = linkText;
if (linkText) {
s = linkText;
} else {
if (!codeBreakscaping) {
s = this.breakscape(s, {
format: this.textFormat,
location: this.textLocation
});
} else {
s = this.breakscape(s, {
format: this.textFormat,
location: this.textLocation
});
}
}
if (this.currentIndent > 1) {
const indentationString = this.getIndentationString();
s = s.replace(INDENTATION_REGEX, `$1${indentationString}`);
}
if (this.havePreText && this.rootParagraphNodeContentIndex === this.preTextIndex) {
this.writePlainTextDivider();
this.writeNL();
}
if (this.havePreText && this.rootParagraphNodeContentIndex >= this.preTextIndex) {
const s2 = this.breakscape(node.text, {
format: TextFormat.plainText,
location: this.textLocation
// Must be body for pre-text
});
this.write(s2);
} else {
this.write(s);
}
if (this.currentIndent > 1 || codeBreakscaping) {
this.thisNodeIsPreText = false;
}
if (!this.inPreText && node.text === s) {
this.thisNodeIsPreText = false;
}
}
/**
* Check if a node is a link node and if so and the node has only the link mark, and the link without the
* protocol is the same as the text, return true. Otherwise return false.
*
* @param node
* @returns true if a simple link, otherwise false
*/
isSimpleLink(node) {
if (node.text == null) return false;
if (node.marks?.length !== 1) return false;
const href = this.getLinkHref(node);
if (href) {
const hrefText = href.replace(LINK_REGEX, "$1");
return hrefText === node.text;
}
return false;
}
/**
* Get the link text if the node is a link, otherwise return false
*
* @param node
* @returns
*/
getLinkText(node) {
if (node.text == null) return false;
const href = this.getLinkHref(node);
if (href) {
let res;
const hrefText = href.replace(LINK_REGEX, "$1");
if (hrefText === node.text) {
res = href;
} else {
res = node.text;
}
res = (res ?? "").replace(LINK_BREAKSCAPE_REGEX, "^]");
return res;
}
return false;
}
/**
* If the existing written text ends with a half-mark, and the new text starts with the same half-mark, insert a
* breakscape ^ between them to break the sequence.
*
* Only applies to bitmark version > 2
*
* @param s
*/
getInterTextBreakscape(s) {
if (this.bitmarkVersion === BitmarkVersion.v2) return "";
if (this.textFormat === TextFormat.plainText) return "";
const lastChar = this.writerText.slice(-1);
const firstChar = s.slice(0, 1);
if (lastChar === firstChar && ALL_HALF_MARKS.indexOf(lastChar) !== -1) {
return "^";
}
if (this.textLocation === TextLocation.body) {
if (lastChar === "[" && ALL_TAG_OPENING_ENDS.indexOf(firstChar) !== -1) {
return "^";
}
}
return "";
}
// protected getHrefTextFromHref
validateGenerateOptions(ast) {
if (this.generateOptions.plainTextDividerAllowed) {
if (ast.length !== 1) {
this.generateOptions.plainTextDividerAllowed = false;
} else if (ast.length === 1 && ast[0].type !== TextNodeType.paragraph) {
this.generateOptions.plainTextDividerAllowed = false;
}
}
}
/**
* Write the marks for the node.
*
* All marks need to be considered at once, because if there are multiple marks, they need to be written in
* a combined manner.
*
* e.g. if there are bold and italic marks, they need to be written as
*
* ==the text==|bold|italic|
* rather than
* **__the text__**
* or
* **__the text**__
*
* @param node the text node
* @param start true if starting (before) the text, false if ending (after) the text
*/
writeMarks(node, stage) {
if (node.marks) {
const forceSingleMark = this.generateOptions.forceInline || !!(this.inInline || this.inHeading);
this.thisNodeIsPreText = false;
const emptyMarks = node.marks.length === 0;
if (emptyMarks) {
this.writeMarkTextWrapper(INLINE_MARK);
return;
}
const marks = this.getWritableMarks(node.marks);
if (marks.length === 0) return;
const singleMark = marks.length === 1 && forceSingleMark;
const markStartEnd = marks.reduce(
(acc, mark) => {
if (acc) return acc;
if (this.isStandardMark(mark)) {
if (singleMark) return STANDARD_MARKS[mark.type];
return INLINE_MARK;
} else if (this.isInlineMark(mark)) {
return INLINE_MARK;
} else {
}
return acc;
},
void 0
);
const isSimpleLinkMark = this.isSimpleLink(node);
const haveMark = markStartEnd != null && !isSimpleLinkMark;
if (haveMark) {
if (stage == Stage.enter || stage == Stage.between) {
this.writeMarkTextWrapper(markStartEnd);
}
if (stage == Stage.between) {
for (const mark of marks) {
if (this.isStandardMark(mark)) {
if (!singleMark) {
this.writeInlineMarkStartEnd();
this.writeInlineMark(mark);
}
} else if (TextMarkType.comment === mark.type) {
this.writeInlineMarkStartEnd();
this.writeCommentMark(mark);
} else if (TextMarkType.link === mark.type) {
this.writeInlineMarkStartEnd();
this.writeLinkMark(mark);
} else if (TextMarkType.ref === mark.type) {
this.writeInlineMarkStartEnd();
this.writeRefMark(mark);
} else if (TextMarkType.xref === mark.type) {
this.writeInlineMarkStartEnd();
this.writeXRefMark(mark);
} else if (TextMarkType.extref === mark.type) {
this.writeInlineMarkStartEnd();
this.writeExtRefMark(mark);
} else if (TextMarkType.footnote === mark.type) {
this.writeInlineMarkStartEnd();
this.writeFootnoteMark(mark);
} else if (TextMarkType.footnoteStar === mark.type) {
this.writeInlineMarkStartEnd();
this.writeFootnoteStarMark(mark);
} else if (TextMarkType.symbol === mark.type) {
this.writeInlineMarkStartEnd();
this.writeSymbolMark(mark);
} else if (this.isInlineMark(mark)) {
this.writeInlineMarkStartEnd();
this.writeInlineMark(mark);
} else {
}
}
}
if (stage == Stage.exit) {
let inlineMarkWritten = false;
for (const mark of marks) {
if (this.isStandardMark(mark)) {
if (!singleMark) {
inlineMarkWritten = true;
}
} else if (TextMarkType.comment === mark.type || TextMarkType.link === mark.type || TextMarkType.ref === mark.type || TextMarkType.xref === mark.type || TextMarkType.footnote === mark.type || TextMarkType.footnoteStar === mark.type || this.isInlineMark(mark)) {
inlineMarkWritten = true;
} else {
}
}
if (inlineMarkWritten) this.writeInlineMarkStartEnd();
}
}
}
}
writeParagraph(node, route) {
if (this.textFormat === TextFormat.bitmarkText) {
if (this.inBulletList) return;
const siblingNodes = this.getSiblingNodes(route);
const prevSiblingNodeType = siblingNodes.left?.type;
const inBody = this.textLocation === TextLocation.body;
const isEmptyParagraph = this.isEmptyParagraph(node);
if (!inBody || !isEmptyParagraph && !(prevSiblingNodeType === TextNodeType.codeBlock || prevSiblingNodeType === TextNodeType.paragraph)) {
return;
}
this.write("|");
this.writeNL();
if (prevSiblingNodeType === TextNodeType.codeBlock) {
this.writeNL();
}
}
}
writeHardBreak(_node) {
this.writeNL();
if (this.currentIndent > 1) {
const indentationString = this.getIndentationString();
this.write(indentationString);
}
}
writeHeading(node) {
let s = "";
const level = node.attrs?.level ?? 1;
for (let i = 0; i < +level; i++) s += HEADING_TAG;
s += " ";
this.write(s);
}
writeSection(node) {
let s = "";
if (node.section) {
s = `|${node.section}: `;
} else {
s = "|";
}
this.write(s);
}
writeBullet(node, route) {
let bullet = this.getIndentationString();
const listParent = this.getParentNode(route, 2);
const listParentNode = listParent?.value;
const listType = listParentNode?.type;
const start = listParentNode?.attrs?.start ?? 1;
if (listType === TextNodeType.bulletList) {
bullet += "\u2022 ";
} else if (listType === TextNodeType.noBulletList) {
bullet += "\u2022_ ";
} else if (listType === TextNodeType.orderedList) {
bullet += `\u2022${start} `;
} else if (listType === TextNodeType.orderedListRoman) {
bullet += `\u2022${start}I `;
} else if (listType === TextNodeType.orderedListRomanLower) {
bullet += `\u2022${start}i `;
} else if (listType === TextNodeType.letteredList) {
bullet += "\u2022A ";
} else if (listType === TextNodeType.letteredListLower) {
bullet += "\u2022a ";
} else if (listType === TextNodeType.taskList) {
const taskList = node;
const checked = taskList.attrs?.checked ?? false;
bullet += checked ? "\u2022+ " : "\u2022- ";
}
if (bullet) this.write(bullet);
}
writeImage(node) {
if (node.attrs == null || !node.attrs.src) return;
const attrs = node.attrs;
const inlineImage = node.type === TextNodeType.imageInline;
let ignoreAttributes;
if (inlineImage) {
ignoreAttributes = /* @__PURE__ */ new Set(["alt", "zoomDisabled", "title"]);
const a = attrs;
if (a.alignmentVertical === "top") ignoreAttributes.add("alignmentVertical");
if (a.size === "line-height") ignoreAttributes.add("size");
}
const mediaAttrs = this.getMediaAttrs(inlineImage ? "imageInline" : "image", attrs, {
ignoreAttributes
});
let s = "";
if (inlineImage) {
s = `==${attrs.alt ?? ""}==`;
}
s += mediaAttrs ? `|${mediaAttrs}|` : "";
this.write(s);
}
writeCodeBlock(node) {
if (node.attrs == null || !node.attrs.language) return;
const attrs = node.attrs;
const s = `|code:${attrs.language}
`;
this.write(s);
}
writeLatex(node) {
if (node.attrs == null || !node.attrs.formula) return;
const attrs = node.attrs;
const formula = this.breakscape(attrs.formula, {
format: this.textFormat,
location: this.textLocation
});
const s = `==${formula}==|latex|`;
this.write(s);
}
writeMarkTextWrapper(s) {
if (s) this.write(s);
}
writeInlineMark(mark) {
const attrs = mark.attrs ?? {};
let s;
switch (mark.type) {
case TextMarkType.textStyle:
case TextMarkType.color:
s = `color:${attrs.color ?? ""}`;
break;
case TextMarkType.timer:
s = attrs.name ? `timer:${attrs.name}` : "timer";
s += `|duration:${attrs.duration ?? ""}`;
break;
case TextMarkType.highlight:
case TextMarkType.userHighlight:
s = attrs.color ? `${mark.type}|color:${attrs.color}` : `${mark.type}`;
break;
default: {
s = `${mark.type}`;
for (const [k, v] of Object.entries(attrs)) {
if (k === "language" && v !== "plain text" || k === "propertyRef" || k === "name") {
s = `${s}:${v}`;
}
}
}
}
this.write(s);
}
/**
* Resolve the marks of a text node into the marks that will actually be written.
*
* - Merges a legacy standalone 'duration' mark into its sibling 'timer' mark.
* - Drops marks the current grammar cannot express (standalone 'duration', 'timer'
* without a duration).
* - Moves a 'textStyle'/'color' mark before a preceding bare 'highlight'/'userHighlight'
* mark when its color is a valid highlight color; written in the original order the
* output would re-parse as a highlight-with-color compound mark and change meaning.
*/
getWritableMarks(nodeMarks) {
const attrsOf = (m) => m.attrs ?? {};
let marks = [...nodeMarks];
const timer = marks.find((m) => m.type === TextMarkType.timer);
const duration = marks.find((m) => m.type === TextMarkType.duration);
if (timer && duration && !attrsOf(timer).duration) {
marks = marks.map(
(m) => m === timer ? {
...timer,
attrs: { ...attrsOf(timer), duration: attrsOf(duration).duration }
} : m
);
}
marks = marks.filter((m) => {
if (m.type === TextMarkType.duration) return false;
if (m.type === TextMarkType.timer) return !!attrsOf(m).duration;
return true;
});
const isBareHighlight = (m) => (m.type === TextMarkType.highlight || m.type === TextMarkType.userHighlight) && !attrsOf(m).color;
const isHighlightColorStyle = (m) => (m.type === TextMarkType.textStyle || m.type === TextMarkType.color) && HIGHLIGHT_COLORS.indexOf(attrsOf(m).color) !== -1;
for (let i = 0; i < marks.length - 1; i++) {
if (isBareHighlight(marks[i]) && isHighlightColorStyle(marks[i + 1])) {
const tmp = marks[i];
marks[i] = marks[i + 1];
marks[i + 1] = tmp;
}
}
return marks;
}
/**
* True if the mark is written using its standard form (e.g. **bold**, !!highlight!!)
* when it is the only mark. A highlight mark with a color attribute is excluded - it
* can only be expressed using the inline chain form (==text==|highlight|color:x|).
*/
isStandardMark(mark) {
if (STANDARD_MARK_TYPES.indexOf(mark.type) === -1) return false;
return !(mark.attrs && mark.attrs.color);
}
/**
* True if the mark is written using the inline chain form (==text==|mark|).
*/
isInlineMark(mark) {
if (INLINE_MARK_TYPES.indexOf(mark.type) !== -1) return true;
return mark.type === TextMarkType.highlight && !!(mark.attrs && mark.attrs.color);
}
writeCommentMark(mark) {
const comment = mark.comment || "";
const s = `#${comment}`;
this.write(s);
}
writeLinkMark(mark) {
const href = mark.attrs?.href || "";
const linkText = (href ?? "").replace(LINK_BREAKSCAPE_REGEX, "^]");
const s = `link:${linkText}`;
this.write(s);
}
writeRefMark(mark) {
const ref = mark.attrs?.reference ?? "";
const s = `\u25BA${ref}`;
this.write(s);
}
writeXRefMark(mark) {
const xref = mark.attrs?.xref ?? "";
const ref = mark.attrs?.reference ?? "";
let s = `xref:${xref}`;
if (ref) s += `|\u25BA${ref}`;
this.write(s);
}
writeExtRefMark(mark) {
const extref = mark.attrs?.extref ?? "";
const refs = mark.attrs?.references ?? [];
const provider = mark.attrs?.provider ?? "";
let s = `extref:${extref}`;
for (const ref of refs) {
s += `|\u25BA${ref ?? ""}`;
}
s += `|provider:${provider}`;
this.write(s);
}
writeFootnoteMark(mark) {
const s = `footnote:`;
this.write(s);
const text = this.internalTextGenerator?.generateSync(
mark.attrs?.content,
this.textFormat,
this.textLocation,
{
...this.generateOptions,
forceInline: true
}
) ?? "";
this.write(text);
}
writeFootnoteStarMark(mark) {
const s = `footnote*:`;
this.write(s);
const text = this.internalTextGenerator?.generateSync(
mark.attrs?.content,
this.textFormat,
this.textLocation,
{
...this.generateOptions,
forceInline: true
}
) ?? "";
this.write(text);
}
writeSymbolMark(mark) {
if (mark.attrs == null) return;
const attrs = mark.attrs;
const mediaAttrs = this.getMediaAttrs("symbol", attrs);
const s = mediaAttrs ?? "";
this.write(s);
}
writeInlineMarkStartEnd() {
this.write("|");
}
writePlainTextDivider() {
this.write("==== text ====");
}
writeString(s) {
if (s != null) this.write(`${s}`);
}
writeNL() {
if (this.options.debugGenerationInline) {
this.write("\\n");
return;
}
this.write("\n");
}
writeInlineDebug(key, state) {
let tag = key;
if (state.open) {
tag = `<${key}>`;
} else if (state.close) {
tag = `</${key}>`;
} else if (state.single) {
tag = `<${key} />`;
}
this.writeString(tag);
}
getMediaAttrs(mediaType, attrs, options) {
if (!mediaType) return void 0;
const opts = Object.assign({}, options);
const ignoreAttributes = opts.ignoreAttributes ?? /* @__PURE__ */ new Set();
let s = `${mediaType}:${attrs?.src ?? ""}`;
const entries = Object.entries(attrs).filter(([k, _v]) => k !== "src");
for (let i = 0; i < entries.length; i++) {
const [k, v] = entries[i];
if (ignoreAttributes.has(k)) continue;
switch (k) {
case "textAlign":
if (v !== "left") s += `|captionAlign:${v}`;
break;
case "alignment":
if (v !== "center") {
if (v) s += `|alignment:${v}`;
}
break;
case "title":
if (v) s += `|caption:${v}`;
break;
case "class":
if (v !== "center") {
if (v) s += `|align:${v}`;
}
break;
case "comment":
if (v) s += `|#${v}`;
break;
case "":
if (stringUtils.isString(v)) s += `|:${v}`;
else s += `|`;
break;
case "zoomDisabled":
if (!v) s += "|zoomDisabled:false";
break;
case "alt":
case "width":
case "height":
default:
if (k && v) s += `|${k}:${v}`;
break;
}
}
return s;
}
//
// Helper functions
//
breakscape(s, options) {
if (this.generateOptions.noBreakscaping) return s;
return instance3.breakscape(s, options);
}
getBitType(route) {
for (const node of route) {
if (node.key === NodeType.bitsValue) {
const n = node.value;
return n?.bitType;
}
}
return void 0;
}
//
// Writer interface
//
/**
* Writes a string value to the output.
* @param value - The string value to be written.
*/
write(value) {
value = this.getInterTextBreakscape(value) + value;
if (this.options.writeCallback) {
this.options.writeCallback(value);
}
this.writerText += value;
return this;
}
/**
* Writes a new line to the output. The line is indented automatically. The line is ended with the endOfLineString.
* @param value - The line to write. When omitted, only the endOfLineString is written.
*/
writeLine(value) {
this.write(`${value}
`);
return this;
}
/**
* Writes a collection of lines to the output. Each line is indented automatically and ended with the endOfLineString.
* @param values - The lines to write.
* @param delimiter - An optional delimiter to be written at the end of each line, except for the last one.
*/
writeLines(values, delimiter) {
for (const value of values) {
this.write(`${value}${delimiter ?? ""}
`);
}
return this;
}
/**
* Writes a single whitespace character to the output.
*/
writeWhiteSpace() {
this.write(" ");
return this;
}
};
// src/generated/parser/text/text-peggy-parser.js
var VERSION = "8.41.1";
function unbreakscape(str) {
if (typeof str !== "string") return null;
return instance3.unbreakscape(str);
}
function removeTempParsingParent(obj) {
if (obj && typeof obj === "object") {
delete obj._tempParsingParent;
delete obj._tempListStart;
Object.keys(obj).forEach((key) => {
removeTempParsingParent(obj[key]);
});
}
}
function cleanEmptyTextNodes(root) {
const isEmptyTextNode = (n) => n && n.type === "text" && !n.text;
function cleanContentArray(arr) {
for (let i = arr.length - 1; i >= 0; i--) {
const item = arr[i];
if (isEmptyTextNode(item)) {
arr.splice(i, 1);
} else if (item && Array.isArray(item.content)) {
cleanContentArray(item.content);
}
}
}
if (Array.isArray(root)) {
cleanContentArray(root);
} else if (root && Array.isArray(root.content)) {
cleanContentArray(root.content);
}
return root;
}
function bitmarkPlusString(_str) {
if (typeof parser !== "undefined") {
return parser.parse(_str, { startRule: "bitmarkPlusString" });
}
return peg$parse(_str, { startRule: "bitmarkPlusString" });
}
var peg$SyntaxError = class extends SyntaxError {
constructor(message, expected, found, location) {
super(message);
this.expected = expected;
this.found = found;
this.location = location;
this.name = "SyntaxError";
}
format(sources) {
let str = "Error: " + this.message;
if (this.location) {
let src = null;
const st = sources.find((s2) => s2.source === this.location.source);
if (st) {
src = st.text.split(/\r\n|\n|\r/g);
}
const s = this.location.start;
const offset_s = this.location.source && typeof this.location.source.offset === "function" ? this.location.source.offset(s) : s;
const loc = this.location.source + ":" + offset_s.line + ":" + offset_s.column;
if (src) {
const e = this.location.end;
const filler = "".padEnd(offset_s.line.toString().length, " ");
const line = src[s.line - 1];
const last = s.line === e.line ? e.column : line.length + 1;
const hatLen = last - s.column || 1;
str += "\n --> " + loc + "\n" + filler + " |\n" + offset_s.line + " | " + line + "\n" + filler + " | " + "".padEnd(s.column - 1, " ") + "".padEnd(hatLen, "^");
} else {
str += "\n at " + loc;
}
}
return str;
}
static buildMessage(expected, found) {
function hex(ch) {
return ch.codePointAt(0).toString(16).toUpperCase();
}
const nonPrintable = Object.prototype.hasOwnProperty.call(RegExp.prototype, "unicode") ? new RegExp("[\\p{C}\\p{Mn}\\p{Mc}]", "gu") : null;
function unicodeEscape(s) {
if (nonPrintable) {
return s.replace(nonPrintable, (ch) => "\\u{" + hex(ch) + "}");
}
return s;
}
function literalEscape(s) {
return unicodeEscape(s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\0/g, "\\0").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/[\x00-\x0F]/g, (ch) => "\\x0" + hex(ch)).replace(/[\x10-\x1F\x7F-\x9F]/g, (ch) => "\\x" + hex(ch)));
}
function classEscape(s) {
return unicodeEscape(s.replace(/\\/g, "\\\\").replace(/\]/g, "\\]").replace(/\^/g, "\\^").replace(/-/g, "\\-").replace(/\0/g, "\\0").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/[\x00-\x0F]/g, (ch) => "\\x0" + hex(ch)).replace(/[\x10-\x1F\x7F-\x9F]/g, (ch) => "\\x" + hex(ch)));
}
const DESCRIBE_EXPECTATION_FNS = {
literal(expectation) {
return '"' + literalEscape(expectation.text) + '"';
},
class(expectation) {
const escapedParts = expectation.parts.map(
(part) => Array.isArray(part) ? classEscape(part[0]) + "-" + classEscape(part[1]) : classEscape(part)
);
return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]" + (expectation.unicode ? "u" : "");
},
any() {
return "any character";
},
end() {
return "end of input";
},
other(expectation) {
return expectation.description;
}
};
function describeExpectation(expectation) {
return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
}
function describeExpected(expected2) {
const descriptions = expected2.map(describeExpectation);
descriptions.sort();
if (descriptions.length > 0) {
let j = 1;
for (let i = 1; i < descriptions.length; i++) {
if (descriptions[i - 1] !== descriptions[i]) {
descriptions[j] = descriptions[i];
j++;
}
}
descriptions.length = j;
}
switch (descriptions.length) {
case 1:
return descriptions[0];
case 2:
return descriptions[0] + " or " + descriptions[1];
default:
return descriptions.slice(0, -1).join(", ") + ", or " + descriptions[descriptions.length - 1];
}
}
function describeFound(found2) {
return found2 ? '"' + literalEscape(found2) + '"' : "end of input";
}
return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
}
};
function peg$parse(input, options) {
options = options !== void 0 ? options : {};
const peg$FAILED = {};
const peg$source = options.grammarSource;
const peg$startRuleFunctions = {
version: peg$parseversion,
bitmarkPlusPlus: peg$parsebitmarkPlusPlus,
bitmarkPlus: peg$parsebitmarkPlus,
bitmarkMinusMinus: peg$parsebitmarkMinusMinus,
bitmarkPlusString: peg$parsebitmarkPlusString,
bitmarkMinusMinusString: peg$parsebitmarkMinusMinusString
};
let peg$startRuleFunction = peg$parseversion;
const peg$c0 = "|";
const peg$c1 = "### ";
const peg$c2 = "## ";
const peg$c3 = "# ";
const peg$c4 = "code";
const peg$c5 = ":";
const peg$c6 = "bitmark++";
const peg$c7 = "bitmark--";
const peg$c8 = "JavaScript";
const peg$c9 = "\u2022 ";
const peg$c10 = "\u2022_ ";
const peg$c11 = "\u2022";
const peg$c12 = " ";
const peg$c13 = "I ";
const peg$c14 = "i ";
const peg$c15 = "\u2022A ";
const peg$c16 = "\u2022a ";
const peg$c17 = "\u2022+ ";
const peg$c18 = "\u2022- ";
const peg$c19 = " ";
const peg$c20 = "image";
const peg$c21 = "#";
const peg$c22 = "@";
const peg$c23 = "width";
const peg$c24 = "height";
const peg$c25 = "alignment";
const peg$c26 = "captionAlign";
const peg$c27 = "left";
const peg$c28 = "center";
const peg$c29 = "right";
const peg$c30 = "alt";
const peg$c31 = "caption";
const peg$c32 = "zoomDisabled";
const peg$c33 = "true";
const peg$c34 = "false";
const peg$c35 = "=";
const peg$c36 = "|latex|";
const peg$c37 = "|imageInline:";
const peg$c38 = "srcAlt";
const peg$c39 = "alignmentVertical";
const peg$c40 = "size";
const peg$c41 = "top";
const peg$c42 = "middle";
const peg$c43 = "bottom";
const peg$c44 = "baseline";
const peg$c45 = "sub";
const peg$c46 = "super";
const peg$c47 = "text-bottom";
const peg$c48 = "text-top";
const peg$c49 = "line-height";
const peg$c50 = "font-height";
const peg$c51 = "explicit";
const peg$c52 = "link:";
const peg$c53 = "extref:";
const peg$c54 = "provider:";
const peg$c55 = "xref:";
const peg$c56 = "\u25BA";
const peg$c57 = "symbol:";
const peg$c58 = "footnote:";
const peg$c59 = "footnote*:";
const peg$c60 = "var:";
const peg$c61 = "code:";
const peg$c62 = "timer";
const peg$c63 = "duration:";
const peg$c64 = "P";
const peg$c65 = "timer:";
const peg$c66 = "highlight";
const peg$c67 = "color:";
const peg$c68 = "userHighlight";
const peg$c69 = "colorPicker:";
const peg$c70 = "|\u25BA";
const peg$c71 = "bold";
const peg$c72 = "italic";
const peg$c73 = "light";
const peg$c74 = "highlightOrange";
const peg$c75 = "highlightYellow";
const peg$c76 = "highlightGreen";
const peg$c77 = "highlightBlue";
const peg$c78 = "highlightPurple";
const peg$c79 = "highlightPink";
const peg$c80 = "highlightBrown";
const peg$c81 = "highlightBlack";
const peg$c82 = "highlightWhite";
const peg$c83 = "highlightGray";
const peg$c84 = "strike";
const peg$c85 = "subscript";
const peg$c86 = "superscript";
const peg$c87 = "ins";
const peg$c88 = "del";
const peg$c89 = "underline";
const peg$c90 = "doubleUnderline";
const peg$c91 = "smallcaps";
const peg$c92 = "circle";
const peg$c93 = "languageEmRed";
const peg$c94 = "languageEmOrange";
const peg$c95 = "languageEmYellow";
const peg$c96 = "languageEmGreen";
const peg$c97 = "languageEmBlue";
const peg$c98 = "languageEmPurple";
const peg$c99 = "languageEmPink";
const peg$c100 = "languageEmBrown";
const peg$c101 = "languageEmBlack";
const peg$c102 = "languageEmWhite";
const peg$c103 = "languageEmGray";
const peg$c104 = "languageEm";
const peg$c105 = "userUnderline";
const peg$c106 = "userDoubleUnderline";
const peg$c107 = "userStrike";
const peg$c108 = "userCircle";
const peg$c109 = "notranslate";
const peg$c110 = "aqua";
const peg$c111 = "black";
const peg$c112 = "blue";
const peg$c113 = "brown";
const peg$c114 = "fuchsia";
const peg$c115 = "lightgrey";
const peg$c116 = "gray";
const peg$c117 = "darkgray";
const peg$c118 = "green";
const peg$c119 = "lime";
const peg$c120 = "magenta";
const peg$c121 = "maroon";
const peg$c122 = "navy";
const peg$c123 = "olive";
const peg$c124 = "orange";
const peg$c125 = "pink";
const peg$c126 = "purple";
const peg$c127 = "red";
const peg$c128 = "silver";
const peg$c129 = "teal";
const peg$c130 = "violet";
const peg$c131 = "white";
const peg$c132 = "yellow";
const peg$c133 = "*";
const peg$c134 = "_";
const peg$c135 = "`";
const peg$c136 = "!";
const peg$c137 = "[!";
const peg$c138 = "]";
const peg$c139 = "\n";
const peg$c140 = "\r\n";
const peg$c141 = "http";
const peg$c142 = "s";
const peg$c143 = "://";
const peg$c144 = "mailto:";
const peg$r0 = /^[ \t]/;
const peg$r1 = /^[0-9]/;
const peg$r2 = /^[\r\u2028-\u2029]/;
const peg$r3 = /^[^\n\r\u2028\u2029]/;
const peg$r4 = /^[!#-;=?-[\]-_a-{}-~]/;
const peg$e0 = peg$anyExpectation();
const peg$e1 = peg$otherExpectation("StyledText");
const peg$e2 = peg$literalExpectation("|", false);
const peg$e3 = peg$literalExpectation("### ", false);
const peg$e4 = peg$literalExpectation("## ", false);
const peg$e5 = peg$literalExpectation("# ", false);
const peg$e6 = peg$literalExpectation("code", false);
const peg$e7 = peg$classExpectation([" ", " "], false, false, false);
const peg$e8 = peg$literalExpectation(":", false);
const peg$e9 = peg$literalExpectation("bitmark++", false);
const peg$e10 = peg$literalExpectation("bitmark--", false);
const peg$e11 = peg$literalExpectation("JavaScript", false);
const peg$e12 = peg$literalExpectation("\u2022 ", false);
const peg$e13 = peg$literalExpectation("\u2022_ ", false);
const peg$e14 = peg$literalExpectation("\u2022", false);
const peg$e15 = peg$classExpectation([["0", "9"]], false, false, false);
const peg$e16 = peg$literalExpectation(" ", false);
const peg$e17 = peg$literalExpectation("I ", false);
const peg$e18 = peg$literalExpectation("i ", false);
const peg$e19 = peg$literalExpectation("\u2022A ", false);
const peg$e20 = peg$literalExpectation("\u2022a ", false);
const peg$e21 = peg$literalExpectation("\u2022+ ", false);
const peg$e22 = peg$literalExpectation("\u2022- ", false);
const peg$e23 = peg$literalExpectation(" ", false);
const peg$e24 = peg$literalExpectation("image", false);
const peg$e25 = peg$literalExpectation("#", false);
const peg$e26 = peg$literalExpectation("@", false);
const peg$e27 = peg$literalExpectation("width", false);
const peg$e28 = peg$literalExpectation("height", false);
const peg$e29 = peg$literalExpectation("alignment", false);
const peg$e30 = peg$literalExpectation("captionAlign", false);
const peg$e31 = peg$literalExpectation("left", false);
const peg$e32 = peg$literalExpectation("center", false);
const peg$e33 = peg$literalExpectation("right", false);
const peg$e34 = peg$literalExpectation("alt", false);
const peg$e35 = peg$literalExpectation("caption", false);
const peg$e36 = peg$literalExpectation("zoomDisabled", false);
const peg$e37 = peg$literalExpectation("true", false);
const peg$e38 = peg$literalExpectation("false", false);
const peg$e39 = peg$literalExpectation("=", false);
const peg$e40 = peg$literalExpectation("|latex|", false);
const peg$e41 = peg$literalExpectation("|imageInline:", false);
const peg$e42 = peg$literalExpectation("srcAlt", false);
const peg$e43 = peg$literalExpectation("alignmentVertical", false);
const peg$e44 = peg$literalExpectation("size", false);
const peg$e45 = peg$literalExpectation("top", false);
const peg$e46 = peg$literalExpectation("middle", false);
const peg$e47 = peg$literalExpectation("bottom", false);
const peg$e48 = peg$literalExpectation("baseline", false);
const peg$e49 = peg$literalExpectation("sub", false);
const peg$e50 = peg$literalExpectation("super", false);
const peg$e51 = peg$literalExpectation("text-bottom", false);
const peg$e52 = peg$literalExpectation("text-top", false);
const peg$e53 = peg$literalExpectation("line-height", false);
const peg$e54 = peg$literalExpectation("font-height", false);
const peg$e55 = peg$literalExpectation("explicit", false);
const peg$e56 = peg$literalExpectation("link:", false);
const peg$e57 = peg$literalExpectation("extref:", false);
const peg$e58 = peg$literalExpectation("provider:", false);
const peg$e59 = peg$literalExpectation("xref:", false);
const peg$e60 = peg$literalExpectation("\u25BA", false);
const peg$e61 = peg$literalExpectation("symbol:", false);
const peg$e62 = peg$literalExpectation("footnote:", false);
const peg$e63 = peg$literalExpectation("footnote*:", false);
const peg$e64 = peg$literalExpectation("var:", false);
const peg$e65 = peg$literalExpectation("code:", false);
const peg$e66 = peg$literalExpectation("timer", false);
const peg$e67 = peg$literalExpectation("duration:", false);
const peg$e68 = peg$literalExpectation("P", false);
const peg$e69 = peg$literalExpectation("timer:", false);
const peg$e70 = peg$literalExpectation("highlight", false);
const peg$e71 = peg$literalExpectation("color:", false);
const peg$e72 = peg$literalExpectation("userHighlight", false);
const peg$e73 = peg$literalExpectation("colorPicker:", false);
const peg$e74 = peg$literalExpectation("|\u25BA", false);
const peg$e75 = peg$literalExpectation("bold", false);
const peg$e76 = peg$literalExpectation("italic", false);
const peg$e77 = peg$literalExpectation("light", false);
const peg$e78 = peg$literalExpectation("highlightOrange", false);
const peg$e79 = peg$literalExpectation("highlightYellow", false);
const peg$e80 = peg$literalExpectation("highlightGreen", false);
const peg$e81 = peg$literalExpectation("highlightBlue", false);
const peg$e82 = peg$literalExpectation("highlightPurple", false);
const peg$e83 = peg$literalExpectation("highlightPink", false);
const peg$e84 = peg$literalExpectation("highlightBrown", false);
const peg$e85 = peg$literalExpectation("highlightBlack", false);
const peg$e86 = peg$literalExpectation("highlightWhite", false);
const peg$e87 = peg$literalExpectation("highlightGray", false);
const peg$e88 = peg$literalExpectation("strike", false);
const peg$e89 = peg$literalExpectation("subscript", false);
const peg$e90 = peg$literalExpectation("superscript", false);
const peg$e91 = peg$literalExpectation("ins", false);
const peg$e92 = peg$literalExpectation("del", false);
const peg$e93 = peg$literalExpectation("underline", false);
const peg$e94 = peg$literalExpectation("doubleUnderline", false);
const peg$e95 = peg$literalExpectation("smallcaps", false);
const peg$e96 = peg$literalExpectation("circle", false);
const peg$e97 = peg$literalExpectation("languageEmRed", false);
const peg$e98 = peg$literalExpectation("languageEmOrange", false);
const peg$e99 = peg$literalExpectation("languageEmYellow", false);
const peg$e100 = peg$literalExpectation("languageEmGreen", false);
const peg$e101 = peg$literalExpectation("languageEmBlue", false);
const peg$e102 = peg$literalExpectation("languageEmPurple", false);
const peg$e103 = peg$literalExpectation("languageEmPink", false);
const peg$e104 = peg$literalExpectation("languageEmBrown", false);
const peg$e105 = peg$literalExpectation("languageEmBlack", false);
const peg$e106 = peg$literalExpectation("languageEmWhite", false);
const peg$e107 = peg$literalExpectation("languageEmGray", false);
const peg$e108 = peg$literalExpectation("languageEm", false);
const peg$e109 = peg$literalExpectation("userUnderline", false);
const peg$e110 = peg$literalExpectation("userDoubleUnderline", false);
const peg$e111 = peg$literalExpectation("userStrike", false);
const peg$e112 = peg$literalExpectation("userCircle", false);
const peg$e113 = peg$literalExpectation("notranslate", false);
const peg$e114 = peg$literalExpectation("aqua", false);
const peg$e115 = peg$literalExpectation("black", false);
const peg$e116 = peg$literalExpectation("blue", false);
const peg$e117 = peg$literalExpectation("brown", false);
const peg$e118 = peg$literalExpectation("fuchsia", false);
const peg$e119 = peg$literalExpectation("lightgrey", false);
const peg$e120 = peg$literalExpectation("gray", false);
const peg$e121 = peg$literalExpectation("darkgray", false);
const peg$e122 = peg$literalExpectation("green", false);
const peg$e123 = peg$literalExpectation("lime", false);
const peg$e124 = peg$literalExpectation("magenta", false);
const peg$e125 = peg$literalExpectation("maroon", false);
const peg$e126 = peg$literalExpectation("navy", false);
const peg$e127 = peg$literalExpectation("olive", false);
const peg$e128 = peg$literalExpectation("orange", false);
const peg$e129 = peg$literalExpectation("pink", false);
const peg$e130 = peg$literalExpectation("purple", false);
const peg$e131 = peg$literalExpectation("red", false);
const peg$e132 = peg$literalExpectation("silver", false);
const peg$e133 = peg$literalExpectation("teal", false);
const peg$e134 = peg$literalExpectation("violet", false);
const peg$e135 = peg$literalExpectation("white", false);
const peg$e136 = peg$literalExpectation("yellow", false);
const peg$e137 = peg$literalExpectation("*", false);
const peg$e138 = peg$literalExpectation("_", false);
const peg$e139 = peg$literalExpectation("`", false);
const peg$e140 = peg$literalExpectation("!", false);
const peg$e141 = peg$literalExpectation("[!", false);
const peg$e142 = peg$literalExpectation("]", false);
const peg$e143 = peg$otherExpectation("Line Terminator");
const peg$e144 = peg$literalExpectation("\n", false);
const peg$e145 = peg$literalExpectation("\r\n", false);
const peg$e146 = peg$classExpectation(["\r", ["\u2028", "\u2029"]], false, false, false);
const peg$e147 = peg$classExpectation(["\n", "\r", "\u2028", "\u2029"], true, false, false);
const peg$e148 = peg$literalExpectation("http", false);
const peg$e149 = peg$literalExpectation("s", false);
const peg$e150 = peg$literalExpectation("://", false);
const peg$e151 = peg$literalExpectation("mailto:", false);
const peg$e152 = peg$classExpectation(["!", ["#", ";"], "=", ["?", "["], ["]", "_"], ["a", "{"], ["}", "~"]], false, false, false);
function peg$f0() {
return VERSION;
}
function peg$f1(b) {
const cleaned_ = cleanEmptyTextNodes({ ...b });
return cleaned_;
}
function peg$f2(b) {
let lb_ = { ...b };
removeTempParsingParent(lb_);
const cleaned_ = cleanEmptyTextNodes(lb_);
return cleaned_;
}
function peg$f3(b) {
return { ...b };
}
function peg$f4(b) {
return { ...b };
}
function peg$f5(b) {
const cleaned_ = cleanEmptyTextNodes({ ...b });
return cleaned_;
}
function peg$f6() {
return [];
}
function peg$f7(h, t) {
return { type: "heading", content: bitmarkPlusString(t), attrs: { level: h.length - 1 } };
}
function peg$f8(t) {
return t;
}
function peg$f9(h, b) {
return { ...h, content: b };
}
function peg$f10() {
return { type: "codeBlock", language: "" };
}
function peg$f11(l) {
return { type: "codeBlock", attrs: { language: l.trim().toLowerCase() } };
}
function peg$f12(c) {
return [{ type: "text", text: unbreakscape(c.trim()) }];
}
function peg$f13(t) {
return t;
}
function peg$f14(c, bl) {
return { ...c, content: bl, attrs: {} };
}
function peg$f15(c, bl) {
return { ...c, content: bl, attrs: {} };
}
function peg$f16(c, bl) {
let start = bl[0]._tempListStart;
return { ...c, attrs: { start }, content: bl };
}
function peg$f17(c, bl) {
let start = bl[0]._tempListStart;
return { ...c, attrs: { start }, content: bl };
}
function peg$f18(c, bl) {
let start = bl[0]._tempListStart;
return { ...c, attrs: { start }, content: bl };
}
function peg$f19(c, bl) {
return { ...c, content: bl, attrs: {} };
}
function peg$f20(c, bl) {
return { ...c, content: bl, attrs: {} };
}
function peg$f21(c, bl) {
return { ...c, content: bl, attrs: {} };
}
function peg$f22() {
return { type: "bulletList" };
}
function peg$f23() {
return { type: "noBulletList" };
}
function peg$f24() {
return { type: "orderedList" };
}
function peg$f25() {
return { type: "orderedListRoman" };
}
function peg$f26() {
return { type: "orderedListRomanLower" };
}
function peg$f27() {
return { type: "letteredList" };
}
function peg$f28() {
return { type: "letteredListLower" };
}
function peg$f29() {
return { type: "taskList" };
}
function peg$f30(lt, listItem, lines, c) {
return c;
}
function peg$f31(lt, listItem, lines, children) {
let _tempParsingParent = "bulletList";
const matchOrdered = lt.match(/•([0-9]+)([Ii]?) /);
let start = 1;
if (matchOrdered) {
_tempParsingParent = "orderedList";
if (matchOrdered.length > 1) {
start = Number(matchOrdered[1]);
}
if (matchOrdered.length > 2) {
let roman = matchOrdered[2];
if ("I" == roman) {
_tempParsingParent = "orderedListRoman";
}
if ("i" == roman) {
_tempParsingParent = "orderedListRomanLower";
}
}
}
if ("\u2022_ " == lt) {
_tempParsingParent = "noBulletList";
}
if ("\u2022A " == lt) {
_tempParsingParent = "letteredList";
}
if ("\u2022a " == lt) {
_tempParsingParent = "letteredListLower";
}
if ("\u2022+ " == lt || "\u2022- " == lt) {
_tempParsingParent = "taskList";
}
let li = (listItem + lines.join("")).trim();
let item = {
type: "paragraph",
attrs: {},
content: bitmarkPlusString(li)
};
let content = [item];
if (children && children[0] && children[0]._tempParsingParent) {
let sublist = {
type: children[0]._tempParsingParent,
attrs: { start },
content: children
};
if (sublist.type.startsWith("orderedList") || sublist.type.startsWith("letteredList")) {
if (children[0]._tempListStart) {
const start2 = children[0]._tempListStart;
if (start2 > 1) sublist.attrs.start = start2;
}
}
content.push(sublist);
}
let t = "listItem";
let attrs = null;
if ("taskList" == _tempParsingParent) {
t = "taskItem";
let checked = false;
if ("\u2022+ " == lt) {
checked = true;
}
attrs = { checked };
}
const _tempListStart = start;
if (attrs) {
return { type: t, attrs, content, _tempParsingParent, _tempListStart };
} else {
return { type: t, content, _tempParsingParent, _tempListStart };
}
}
function peg$f32(ll) {
return ll;
}
function peg$f33(i) {
return i.join("") === indent;
}
function peg$f34(i) {
return i.length > indent.length;
}
function peg$f35(i) {
indentStack.push(indent);
indent = i.join("");
}
function peg$f36() {
indent = indentStack.pop();
return true;
}
function peg$f37(body) {
return { type: "paragraph", content: bitmarkPlusString(body.trim()), attrs: {} };
}
function peg$f38(body) {
return { type: "paragraph", content: bitmarkPlusString(body.trim()), attrs: {} };
}
function peg$f39(body) {
return { type: "paragraph", content: bitmarkPlusString(body.trim()), attrs: {} };
}
function peg$f40(t) {
return t;
}
function peg$f41(t, u, ch) {
const chain = Object.assign({}, ...ch);
let imageAlignment_ = chain.alignment || "center";
delete chain.alignment;
let textAlign_ = chain.captionAlign || "left";
delete chain.captionAlign;
let alt_ = chain.alt || null;
delete chain.alt;
let title_ = chain.caption || null;
delete chain.caption;
let class_ = chain.align || "center";
delete chain.align;
let zoomDisabled_ = chain.zoomDisabled === void 0 ? true : typeof chain.zoomDisabled === "boolean" ? chain.zoomDisabled : String(chain.zoomDisabled).toLowerCase() === "true";
delete chain.zoomDisabled;
let image = {
type: t,
attrs: {
alignment: imageAlignment_,
textAlign: textAlign_,
src: u,
alt: unbreakscape(alt_),
title: unbreakscape(title_),
class: class_,
zoomDisabled: zoomDisabled_,
...chain
}
};
return image;
}
function peg$f42(ch) {
return ch;
}
function peg$f43(str) {
return { comment: str.trim() };
}
function peg$f44(p, v) {
return { [p]: parseInt(v) };
}
function peg$f45(p, v) {
return { [p]: v.trim() };
}
function peg$f46(p, v) {
return { [p]: v };
}
function peg$f47(p, v) {
return { [p]: v };
}
function peg$f48(p) {
return { [p]: true };
}
function peg$f49() {
return;
}
function peg$f50(p) {
return { error: "Found unknown property or invalid value: " + p };
}
function peg$f51(bs) {
return [{ type: "paragraph", content: bs, attrs: {} }];
}
function peg$f52(first, more) {
const cleaned_ = cleanEmptyTextNodes(first ? [first, ...more.flat()] : more.flat());
return cleaned_;
}
function peg$f53() {
return { "type": "hardBreak" };
}
function peg$f54(t) {
return { text: unbreakscape(t), type: "text" };
}
function peg$f55(t) {
return { index: +t, type: "bit" };
}
function peg$f56(t) {
return { attrs: { formula: unbreakscape(t) }, type: "latex" };
}
function peg$f57(alt, u, ch) {
return { attrs: { alt, src: (u.pr + u.t).trim(), "alignmentVertical": "top", size: "line-height", ...Object.assign({}, ...ch), zoomDisabled: true }, type: "imageInline" };
}
function peg$f58(t, marks) {
if (!marks) marks = [];
return { marks, text: unbreakscape(t), type: "text" };
}
function peg$f59(t) {
return { marks: [{ type: "bold" }], text: unbreakscape(t), type: "text" };
}
function peg$f60(t) {
return { marks: [{ type: "italic" }], text: unbreakscape(t), type: "text" };
}
function peg$f61(t) {
return { marks: [{ type: "light" }], text: unbreakscape(t), type: "text" };
}
function peg$f62(t) {
return { marks: [{ type: "highlight" }], text: unbreakscape(t), type: "text" };
}
function peg$f63(u) {
return { marks: [{ type: "link", attrs: { href: (u.pr + u.t).trim(), target: "_blank" } }], text: u.t, type: "text" };
}
function peg$f64(ch) {
return ch;
}
function peg$f65(ch) {
return ch;
}
function peg$f66(str) {
return { comment: str };
}
function peg$f67(p, v) {
return { [p]: v };
}
function peg$f68(p, v) {
return { [p]: parseInt(v) };
}
function peg$f69(p, v) {
return { type: "error", msg: p + " must be a positive integer.", found: v };
}
function peg$f70(p, v) {
return { [p]: v };
}
function peg$f71(p, v) {
return { [p]: v };
}
function peg$f72(str) {
return { type: "link", attrs: { href: str.trim(), target: "_blank" } };
}
function peg$f73(str, rc, p) {
return { type: "extref", attrs: { extref: str.trim(), references: rc, provider: p.trim() } };
}
function peg$f74(str, str2) {
return { type: "xref", attrs: { xref: str.trim(), reference: str2.trim() } };
}
function peg$f75(str) {
return { type: "xref", attrs: { xref: str.trim(), reference: "" } };
}
function peg$f76(str) {
return { type: "ref", attrs: { reference: str.trim() } };
}
function peg$f77(str, ch) {
const chain = Object.assign({}, ...ch);
return { type: "symbol", attrs: { src: str.trim(), ...chain } };
}
function peg$f78(str) {
return { type: "footnote", attrs: { content: bitmarkPlusString(str.trim()) } };
}
function peg$f79(str) {
return { type: "footnote*", attrs: { content: bitmarkPlusString(str.trim()) } };
}
function peg$f80(str) {
return { type: "var", attrs: { name: str.trim() } };
}
function peg$f81() {
return { type: "code", attrs: { language: "plain text" } };
}
function peg$f82(lang) {
return { type: "code", attrs: { language: lang.trim().toLowerCase() } };
}
function peg$f83(d) {
return { type: "timer", attrs: { name: "", duration: d.trim() } };
}
function peg$f84(str, d) {
return { type: "timer", attrs: { name: str.trim(), duration: d.trim() } };
}
function peg$f85(color) {
return { type: "highlight", attrs: { color } };
}
function peg$f86(color) {
return { type: "userHighlight", attrs: { color } };
}
function peg$f87(color) {
return { type: "textStyle", attrs: { color } };
}
function peg$f88(str) {
return { type: "colorPicker", attrs: { propertyRef: str.trim() } };
}
function peg$f89(style) {
return { type: style };
}
function peg$f90(str) {
return { type: "comment", comment: str };
}
function peg$f91(r) {
return r.trim();
}
function peg$f92(bs) {
return [{ type: "paragraph", content: bs, attrs: {} }];
}
function peg$f93(first, more) {
const cleaned_ = cleanEmptyTextNodes(first ? [first, ...more.flat()] : more.flat());
return cleaned_;
}
function peg$f94() {
return { "type": "hardBreak" };
}
function peg$f95(t) {
return { text: unbreakscape(t), type: "text" };
}
function peg$f96(t) {
return { index: +t, type: "bit" };
}
function peg$f97(t) {
return { marks: [{ type: "bold" }], text: unbreakscape(t), type: "text" };
}
function peg$f98(t) {
return { marks: [{ type: "italic" }], text: unbreakscape(t), type: "text" };
}
function peg$f99(t) {
return { marks: [{ type: "light" }], text: unbreakscape(t), type: "text" };
}
function peg$f100(t) {
return { marks: [{ type: "highlight" }], text: unbreakscape(t), type: "text" };
}
function peg$f101(pr, t) {
return { pr, t };
}
let peg$currPos = options.peg$currPos | 0;
let peg$savedPos = peg$currPos;
const peg$posDetailsCache = [{ line: 1, column: 1 }];
let peg$maxFailPos = peg$currPos;
let peg$maxFailExpected = options.peg$maxFailExpected || [];
let peg$silentFails = options.peg$silentFails | 0;
let peg$result;
if (options.startRule) {
if (!(options.startRule in peg$startRuleFunctions)) {
throw new Error(`Can't start parsing from rule "` + options.startRule + '".');
}
peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
}
function text() {
return input.substring(peg$savedPos, peg$currPos);
}
function offset() {
return peg$savedPos;
}
function range() {
return {
source: peg$source,
start: peg$savedPos,
end: peg$currPos
};
}
function location() {
return peg$computeLocation(peg$savedPos, peg$currPos);
}
function expected(description, location2) {
location2 = location2 !== void 0 ? location2 : peg$computeLocation(peg$savedPos, peg$currPos);
throw peg$buildStructuredError(
[peg$otherExpectation(description)],
input.substring(peg$savedPos, peg$currPos),
location2
);
}
function error(message, location2) {
location2 = location2 !== void 0 ? location2 : peg$computeLocation(peg$savedPos, peg$currPos);
throw peg$buildSimpleError(message, location2);
}
function peg$getUnicode(pos = peg$currPos) {
const cp = input.codePointAt(pos);
if (cp === void 0) {
return "";
}
return String.fromCodePoint(cp);
}
function peg$literalExpectation(text2, ignoreCase) {
return { type: "literal", text: text2, ignoreCase };
}
function peg$classExpectation(parts, inverted, ignoreCase, unicode) {
return { type: "class", parts, inverted, ignoreCase, unicode };
}
function peg$anyExpectation() {
return { type: "any" };
}
function peg$endExpectation() {
return { type: "end" };
}
function peg$otherExpectation(description) {
return { type: "other", description };
}
function peg$computePosDetails(pos) {
let details = peg$posDetailsCache[pos];
let p;
if (details) {
return details;
} else {
if (pos >= peg$posDetailsCache.length) {
p = peg$posDetailsCache.length - 1;
} else {
p = pos;
while (!peg$posDetailsCache[--p]) {
}
}
details = peg$posDetailsCache[p];
details = {
line: details.line,
column: details.column
};
while (p < pos) {
if (input.charCodeAt(p) === 10) {
details.line++;
details.column = 1;
} else {
details.column++;
}
p++;
}
peg$posDetailsCache[pos] = details;
return details;
}
}
function peg$computeLocation(startPos, endPos, offset2) {
const startPosDetails = peg$computePosDetails(startPos);
const endPosDetails = peg$computePosDetails(endPos);
const res = {
source: peg$source,
start: {
offset: startPos,
line: startPosDetails.line,
column: startPosDetails.column
},
end: {
offset: endPos,
line: endPosDetails.line,
column: endPosDetails.column
}
};
if (offset2 && peg$source && typeof peg$source.offset === "function") {
res.start = peg$source.offset(res.start);
res.end = peg$source.offset(res.end);
}
return res;
}
function peg$fail(expected2) {
if (peg$currPos < peg$maxFailPos) {
return;
}
if (peg$currPos > peg$maxFailPos) {
peg$maxFailPos = peg$currPos;
peg$maxFailExpected = [];
}
peg$maxFailExpected.push(expected2);
}
function peg$buildSimpleError(message, location2) {
return new peg$SyntaxError(message, null, null, location2);
}
function peg$buildStructuredError(expected2, found, location2) {
return new peg$SyntaxError(
peg$SyntaxError.buildMessage(expected2, found),
expected2,
found,
location2
);
}
function peg$parseversion() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = [];
if (input.length > peg$currPos) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
while (s2 !== peg$FAILED) {
s1.push(s2);
if (input.length > peg$currPos) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
}
peg$savedPos = s0;
s1 = peg$f0();
s0 = s1;
return s0;
}
function peg$parsebitmarkPlusPlus() {
let s0, s1;
peg$silentFails++;
s0 = [];
s1 = peg$parseBlock();
if (s1 !== peg$FAILED) {
while (s1 !== peg$FAILED) {
s0.push(s1);
s1 = peg$parseBlock();
}
} else {
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$parseNoContent();
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e1);
}
}
return s0;
}
function peg$parseBlock() {
let s0, s1;
s0 = peg$currPos;
s1 = peg$parseTitleBlock();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f1(s1);
}
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseListBlock();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f2(s1);
}
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseImageBlock();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f3(s1);
}
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseCodeBlock();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f4(s1);
}
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseParagraph();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f5(s1);
}
s0 = s1;
}
}
}
}
return s0;
}
function peg$parseBlockStart() {
let s0;
s0 = peg$parseTitleTags();
if (s0 === peg$FAILED) {
s0 = peg$parseListTags();
if (s0 === peg$FAILED) {
s0 = peg$parseImageBlock();
if (s0 === peg$FAILED) {
s0 = peg$parseCodeHeader();
if (s0 === peg$FAILED) {
s0 = peg$parseExplicitParagraphHeader();
}
}
}
}
return s0;
}
function peg$parseBlockTag() {
let s0;
if (input.charCodeAt(peg$currPos) === 124) {
s0 = peg$c0;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e2);
}
}
return s0;
}
function peg$parseNoContent() {
let s0, s1;
s0 = peg$currPos;
s1 = "";
peg$savedPos = s0;
s1 = peg$f6();
s0 = s1;
return s0;
}
function peg$parseTitleTags() {
let s0;
if (input.substr(peg$currPos, 4) === peg$c1) {
s0 = peg$c1;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e3);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 3) === peg$c2) {
s0 = peg$c2;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e4);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 2) === peg$c3) {
s0 = peg$c3;
peg$currPos += 2;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e5);
}
}
}
}
return s0;
}
function peg$parseTitleBlock() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$parseTitleTags();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$parsechar();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parsechar();
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseEOL();
if (s3 !== peg$FAILED) {
s4 = peg$parseNL();
if (s4 === peg$FAILED) {
s4 = null;
}
peg$savedPos = s0;
s0 = peg$f7(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseCodeType() {
let s0;
if (input.substr(peg$currPos, 4) === peg$c4) {
s0 = peg$c4;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e6);
}
}
return s0;
}
function peg$parseCodeTag() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseBlockTag();
if (s1 !== peg$FAILED) {
s2 = peg$parseCodeType();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f8(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseCodeBlock() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseCodeHeader();
if (s1 !== peg$FAILED) {
s2 = peg$parseCodeBody();
peg$savedPos = s0;
s0 = peg$f9(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseCodeHeader() {
let s0, s1, s2, s3, s4, s5, s6, s7;
s0 = peg$currPos;
s1 = peg$parseCodeTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = peg$currPos;
s4 = [];
s5 = input.charAt(peg$currPos);
if (peg$r0.test(s5)) {
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = input.charAt(peg$currPos);
if (peg$r0.test(s5)) {
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
}
s5 = peg$parseEOL();
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = input.substring(s2, peg$currPos);
} else {
s2 = s3;
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f10();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseCodeTag();
if (s1 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s2 = peg$c5;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s2 !== peg$FAILED) {
s3 = peg$parseCodeLanguage();
if (s3 !== peg$FAILED) {
s4 = peg$currPos;
s5 = peg$currPos;
s6 = [];
s7 = input.charAt(peg$currPos);
if (peg$r0.test(s7)) {
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = input.charAt(peg$currPos);
if (peg$r0.test(s7)) {
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
}
s7 = peg$parseEOL();
if (s7 !== peg$FAILED) {
s6 = [s6, s7];
s5 = s6;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s4 = input.substring(s4, peg$currPos);
} else {
s4 = s5;
}
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f11(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parseCodeLanguage() {
let s0, s1, s2;
if (input.substr(peg$currPos, 9) === peg$c6) {
s0 = peg$c6;
peg$currPos += 9;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e9);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 9) === peg$c7) {
s0 = peg$c7;
peg$currPos += 9;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e10);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 10) === peg$c8) {
s0 = peg$c8;
peg$currPos += 10;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e11);
}
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = [];
s2 = peg$parsechar();
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parsechar();
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
if (s0 === peg$FAILED) {
s0 = "";
}
}
}
}
return s0;
}
function peg$parseCodeBody() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = [];
s3 = peg$parseCodeLine();
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseCodeLine();
}
s1 = input.substring(s1, peg$currPos);
peg$savedPos = s0;
s1 = peg$f12(s1);
s0 = s1;
return s0;
}
function peg$parseCodeLine() {
let s0, s1, s2, s3, s4, s5;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseBlockStart();
peg$silentFails--;
if (s2 === peg$FAILED) {
s1 = void 0;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = peg$currPos;
s4 = [];
s5 = peg$parsechar();
if (s5 !== peg$FAILED) {
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parsechar();
}
} else {
s4 = peg$FAILED;
}
if (s4 !== peg$FAILED) {
s5 = peg$parseEOL();
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = input.substring(s2, peg$currPos);
} else {
s2 = s3;
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f13(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$parseNL();
}
return s0;
}
function peg$parseBulletListTag() {
let s0;
if (input.substr(peg$currPos, 2) === peg$c9) {
s0 = peg$c9;
peg$currPos += 2;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
return s0;
}
function peg$parseNoBulletListTag() {
let s0;
if (input.substr(peg$currPos, 3) === peg$c10) {
s0 = peg$c10;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e13);
}
}
return s0;
}
function peg$parseOrderedListTag() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 8226) {
s2 = peg$c11;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e14);
}
}
if (s2 !== peg$FAILED) {
s3 = [];
s4 = input.charAt(peg$currPos);
if (peg$r1.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = input.charAt(peg$currPos);
if (peg$r1.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 32) {
s4 = peg$c12;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
if (s4 !== peg$FAILED) {
s2 = [s2, s3, s4];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
return s0;
}
function peg$parseOrderedListRomanTag() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 8226) {
s2 = peg$c11;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e14);
}
}
if (s2 !== peg$FAILED) {
s3 = [];
s4 = input.charAt(peg$currPos);
if (peg$r1.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = input.charAt(peg$currPos);
if (peg$r1.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
if (input.substr(peg$currPos, 2) === peg$c13) {
s4 = peg$c13;
peg$currPos += 2;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e17);
}
}
if (s4 !== peg$FAILED) {
s2 = [s2, s3, s4];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
return s0;
}
function peg$parseOrderedListRomanLowerTag() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 8226) {
s2 = peg$c11;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e14);
}
}
if (s2 !== peg$FAILED) {
s3 = [];
s4 = input.charAt(peg$currPos);
if (peg$r1.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = input.charAt(peg$currPos);
if (peg$r1.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
if (input.substr(peg$currPos, 2) === peg$c14) {
s4 = peg$c14;
peg$currPos += 2;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e18);
}
}
if (s4 !== peg$FAILED) {
s2 = [s2, s3, s4];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
return s0;
}
function peg$parseLetteredListTag() {
let s0;
if (input.substr(peg$currPos, 3) === peg$c15) {
s0 = peg$c15;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e19);
}
}
return s0;
}
function peg$parseLetteredListLowerTag() {
let s0;
if (input.substr(peg$currPos, 3) === peg$c16) {
s0 = peg$c16;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e20);
}
}
return s0;
}
function peg$parseTaskListTag() {
let s0;
if (input.substr(peg$currPos, 3) === peg$c17) {
s0 = peg$c17;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e21);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 3) === peg$c18) {
s0 = peg$c18;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e22);
}
}
}
return s0;
}
function peg$parseListTags() {
let s0;
s0 = peg$parseBulletListTag();
if (s0 === peg$FAILED) {
s0 = peg$parseNoBulletListTag();
if (s0 === peg$FAILED) {
s0 = peg$parseOrderedListTag();
if (s0 === peg$FAILED) {
s0 = peg$parseOrderedListRomanTag();
if (s0 === peg$FAILED) {
s0 = peg$parseOrderedListRomanLowerTag();
if (s0 === peg$FAILED) {
s0 = peg$parseLetteredListTag();
if (s0 === peg$FAILED) {
s0 = peg$parseLetteredListLowerTag();
if (s0 === peg$FAILED) {
s0 = peg$parseTaskListTag();
}
}
}
}
}
}
}
return s0;
}
function peg$parseListBlock() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parseBulletListContainer();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseBulletListLine();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseBulletListLine();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseNL();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f14(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseNoBulletListContainer();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseBulletListLine();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseBulletListLine();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseNL();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f15(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseOrderedListContainer();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseBulletListLine();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseBulletListLine();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseNL();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f16(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseOrderedListRomanContainer();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseBulletListLine();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseBulletListLine();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseNL();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f17(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseOrderedListRomanLowerContainer();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseBulletListLine();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseBulletListLine();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseNL();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f18(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseLetteredListContainer();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseBulletListLine();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseBulletListLine();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseNL();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f19(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseLetteredListLowerContainer();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseBulletListLine();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseBulletListLine();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseNL();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f20(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseTaskListContainer();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseBulletListLine();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseBulletListLine();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseNL();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f21(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
}
}
}
}
}
}
return s0;
}
function peg$parseBulletListContainer() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseBulletListTag();
peg$silentFails--;
if (s2 !== peg$FAILED) {
peg$currPos = s1;
s1 = void 0;
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f22();
}
s0 = s1;
return s0;
}
function peg$parseNoBulletListContainer() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseNoBulletListTag();
peg$silentFails--;
if (s2 !== peg$FAILED) {
peg$currPos = s1;
s1 = void 0;
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f23();
}
s0 = s1;
return s0;
}
function peg$parseOrderedListContainer() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseOrderedListTag();
peg$silentFails--;
if (s2 !== peg$FAILED) {
peg$currPos = s1;
s1 = void 0;
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f24();
}
s0 = s1;
return s0;
}
function peg$parseOrderedListRomanContainer() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseOrderedListRomanTag();
peg$silentFails--;
if (s2 !== peg$FAILED) {
peg$currPos = s1;
s1 = void 0;
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f25();
}
s0 = s1;
return s0;
}
function peg$parseOrderedListRomanLowerContainer() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseOrderedListRomanLowerTag();
peg$silentFails--;
if (s2 !== peg$FAILED) {
peg$currPos = s1;
s1 = void 0;
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f26();
}
s0 = s1;
return s0;
}
function peg$parseLetteredListContainer() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseLetteredListTag();
peg$silentFails--;
if (s2 !== peg$FAILED) {
peg$currPos = s1;
s1 = void 0;
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f27();
}
s0 = s1;
return s0;
}
function peg$parseLetteredListLowerContainer() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseLetteredListLowerTag();
peg$silentFails--;
if (s2 !== peg$FAILED) {
peg$currPos = s1;
s1 = void 0;
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f28();
}
s0 = s1;
return s0;
}
function peg$parseTaskListContainer() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseTaskListTag();
peg$silentFails--;
if (s2 !== peg$FAILED) {
peg$currPos = s1;
s1 = void 0;
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f29();
}
s0 = s1;
return s0;
}
function peg$parseBulletListLine() {
let s0, s1, s2, s3, s4, s5, s6, s7, s8;
s0 = peg$currPos;
s1 = peg$parseSAMEDENT();
if (s1 !== peg$FAILED) {
s2 = peg$parseListTags();
if (s2 !== peg$FAILED) {
s3 = peg$currPos;
s4 = peg$currPos;
s5 = [];
s6 = peg$currPos;
s7 = peg$currPos;
peg$silentFails++;
s8 = peg$parseNL();
peg$silentFails--;
if (s8 === peg$FAILED) {
s7 = void 0;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
if (s7 !== peg$FAILED) {
if (input.length > peg$currPos) {
s8 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s8 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s8 !== peg$FAILED) {
s7 = [s7, s8];
s6 = s7;
} else {
peg$currPos = s6;
s6 = peg$FAILED;
}
} else {
peg$currPos = s6;
s6 = peg$FAILED;
}
while (s6 !== peg$FAILED) {
s5.push(s6);
s6 = peg$currPos;
s7 = peg$currPos;
peg$silentFails++;
s8 = peg$parseNL();
peg$silentFails--;
if (s8 === peg$FAILED) {
s7 = void 0;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
if (s7 !== peg$FAILED) {
if (input.length > peg$currPos) {
s8 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s8 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s8 !== peg$FAILED) {
s7 = [s7, s8];
s6 = s7;
} else {
peg$currPos = s6;
s6 = peg$FAILED;
}
} else {
peg$currPos = s6;
s6 = peg$FAILED;
}
}
s6 = peg$parseNL();
if (s6 === peg$FAILED) {
s6 = null;
}
s5 = [s5, s6];
s4 = s5;
s3 = input.substring(s3, peg$currPos);
s4 = [];
s5 = peg$parseListLine();
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parseListLine();
}
s5 = peg$currPos;
s6 = peg$parseINDENT();
if (s6 !== peg$FAILED) {
s7 = [];
s8 = peg$parseBulletListLine();
while (s8 !== peg$FAILED) {
s7.push(s8);
s8 = peg$parseBulletListLine();
}
s8 = peg$parseDEDENT();
if (s8 !== peg$FAILED) {
peg$savedPos = s5;
s5 = peg$f30(s2, s3, s4, s7);
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 === peg$FAILED) {
s5 = null;
}
peg$savedPos = s0;
s0 = peg$f31(s2, s3, s4, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseListLine() {
let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseBlankLine();
peg$silentFails--;
if (s2 === peg$FAILED) {
s1 = void 0;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$parseSAMEDENT();
if (s2 !== peg$FAILED) {
s3 = peg$currPos;
peg$silentFails++;
s4 = peg$parseListTags();
peg$silentFails--;
if (s4 === peg$FAILED) {
s3 = void 0;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s4 = peg$currPos;
s5 = peg$currPos;
s6 = [];
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseNL();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
if (input.length > peg$currPos) {
s9 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s9 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
if (s7 !== peg$FAILED) {
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseNL();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
if (input.length > peg$currPos) {
s9 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s9 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
}
} else {
s6 = peg$FAILED;
}
if (s6 !== peg$FAILED) {
s7 = peg$parseEOL();
if (s7 !== peg$FAILED) {
s6 = [s6, s7];
s5 = s6;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s4 = input.substring(s4, peg$currPos);
} else {
s4 = s5;
}
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f32(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseBlankLine() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = [];
s2 = input.charAt(peg$currPos);
if (peg$r0.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = input.charAt(peg$currPos);
if (peg$r0.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
}
s2 = peg$parseNL();
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseSAMEDENT() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = [];
if (input.charCodeAt(peg$currPos) === 9) {
s2 = peg$c19;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e23);
}
}
while (s2 !== peg$FAILED) {
s1.push(s2);
if (input.charCodeAt(peg$currPos) === 9) {
s2 = peg$c19;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e23);
}
}
}
peg$savedPos = peg$currPos;
s2 = peg$f33(s1);
if (s2) {
s2 = void 0;
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseINDENT() {
let s0, s1, s2, s3;
s0 = peg$currPos;
peg$silentFails++;
s1 = peg$currPos;
s2 = [];
if (input.charCodeAt(peg$currPos) === 9) {
s3 = peg$c19;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e23);
}
}
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
if (input.charCodeAt(peg$currPos) === 9) {
s3 = peg$c19;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e23);
}
}
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
peg$savedPos = peg$currPos;
s3 = peg$f34(s2);
if (s3) {
s3 = void 0;
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
peg$savedPos = s1;
s1 = peg$f35(s2);
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
peg$silentFails--;
if (s1 !== peg$FAILED) {
peg$currPos = s0;
s0 = void 0;
} else {
s0 = peg$FAILED;
}
return s0;
}
function peg$parseDEDENT() {
let s0;
peg$savedPos = peg$currPos;
s0 = peg$f36();
if (s0) {
s0 = void 0;
} else {
s0 = peg$FAILED;
}
return s0;
}
function peg$parseParagraph() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseBlockStart();
peg$silentFails--;
if (s2 === peg$FAILED) {
s1 = void 0;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$parseParagraphBody();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f37(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseExplicitParagraphHeader();
if (s1 !== peg$FAILED) {
s2 = peg$parseParagraphBody();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f38(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseExplicitParagraphHeader();
if (s1 !== peg$FAILED) {
s2 = "";
peg$savedPos = s0;
s0 = peg$f39(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
}
return s0;
}
function peg$parseParagraphBody() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = [];
s2 = peg$parseParagraphLine();
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseParagraphLine();
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
return s0;
}
function peg$parseParagraphLine() {
let s0, s1, s2, s3, s4, s5;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$parseBlockStart();
peg$silentFails--;
if (s2 === peg$FAILED) {
s1 = void 0;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = peg$currPos;
s4 = [];
s5 = peg$parsechar();
if (s5 !== peg$FAILED) {
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parsechar();
}
} else {
s4 = peg$FAILED;
}
if (s4 !== peg$FAILED) {
s5 = peg$parseEOL();
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = input.substring(s2, peg$currPos);
} else {
s2 = s3;
}
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$parseNL();
}
return s0;
}
function peg$parseExplicitParagraphHeader() {
let s0, s1, s2, s3, s4, s5;
s0 = peg$currPos;
s1 = peg$parseBlockTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = peg$currPos;
s4 = [];
s5 = input.charAt(peg$currPos);
if (peg$r0.test(s5)) {
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = input.charAt(peg$currPos);
if (peg$r0.test(s5)) {
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
}
s5 = peg$parseEOL();
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = input.substring(s2, peg$currPos);
} else {
s2 = s3;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseNL();
if (s3 === peg$FAILED) {
s3 = null;
}
s1 = [s1, s2, s3];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseImageType() {
let s0;
if (input.substr(peg$currPos, 5) === peg$c20) {
s0 = peg$c20;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e24);
}
}
return s0;
}
function peg$parseImageTag() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseBlockTag();
if (s1 !== peg$FAILED) {
s2 = peg$parseImageType();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f40(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseImageBlock() {
let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11;
s0 = peg$currPos;
s1 = peg$parseImageTag();
if (s1 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s2 = peg$c5;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 32) {
s3 = peg$c12;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
if (s3 === peg$FAILED) {
s3 = null;
}
s4 = peg$parseUrlHttp();
if (s4 !== peg$FAILED) {
s5 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s6 = peg$c12;
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s6 !== peg$FAILED) {
s5.push(s6);
if (input.charCodeAt(peg$currPos) === 32) {
s6 = peg$c12;
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s6 = peg$parseBlockTag();
if (s6 !== peg$FAILED) {
s7 = peg$parseMediaChain();
s8 = peg$currPos;
s9 = peg$currPos;
s10 = [];
s11 = input.charAt(peg$currPos);
if (peg$r0.test(s11)) {
peg$currPos++;
} else {
s11 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
while (s11 !== peg$FAILED) {
s10.push(s11);
s11 = input.charAt(peg$currPos);
if (peg$r0.test(s11)) {
peg$currPos++;
} else {
s11 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
}
s11 = peg$parseEOL();
if (s11 !== peg$FAILED) {
s10 = [s10, s11];
s9 = s10;
} else {
peg$currPos = s9;
s9 = peg$FAILED;
}
if (s9 !== peg$FAILED) {
s8 = input.substring(s8, peg$currPos);
} else {
s8 = s9;
}
if (s8 !== peg$FAILED) {
s9 = peg$parseNL();
if (s9 === peg$FAILED) {
s9 = null;
}
peg$savedPos = s0;
s0 = peg$f41(s1, s4, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseMediaChain() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = [];
s2 = peg$parseMediaChainItem();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseMediaChainItem();
}
peg$savedPos = s0;
s1 = peg$f42(s1);
s0 = s1;
return s0;
}
function peg$parseMediaChainItem() {
let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 35) {
s1 = peg$c21;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e25);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f43(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseMediaSizeTags();
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s3 = peg$c5;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s5 = peg$currPos;
s6 = [];
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = input.charAt(peg$currPos);
if (peg$r1.test(s9)) {
peg$currPos++;
} else {
s9 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
if (s7 !== peg$FAILED) {
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = input.charAt(peg$currPos);
if (peg$r1.test(s9)) {
peg$currPos++;
} else {
s9 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
}
} else {
s6 = peg$FAILED;
}
if (s6 !== peg$FAILED) {
s5 = input.substring(s5, peg$currPos);
} else {
s5 = s6;
}
if (s5 !== peg$FAILED) {
s6 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s7 = peg$c12;
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s7 !== peg$FAILED) {
s6.push(s7);
if (input.charCodeAt(peg$currPos) === 32) {
s7 = peg$c12;
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s7 = peg$parseBlockTag();
if (s7 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f44(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseMediaTextTags();
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s3 = peg$c5;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s5 = peg$currPos;
s6 = [];
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = peg$parsechar();
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = peg$parsechar();
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
}
s5 = input.substring(s5, peg$currPos);
s6 = peg$parseBlockTag();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f45(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseMediaAlignmentTags();
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s3 = peg$c5;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s5 = peg$parseMediaAlignment();
if (s5 !== peg$FAILED) {
s6 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s7 = peg$c12;
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s7 !== peg$FAILED) {
s6.push(s7);
if (input.charCodeAt(peg$currPos) === 32) {
s7 = peg$c12;
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s7 = peg$parseBlockTag();
if (s7 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f46(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseMediaBooleanTags();
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s3 = peg$c5;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s5 = peg$parseBoolean();
if (s5 !== peg$FAILED) {
s6 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s7 = peg$c12;
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s7 !== peg$FAILED) {
s6.push(s7);
if (input.charCodeAt(peg$currPos) === 32) {
s7 = peg$c12;
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s7 = peg$parseBlockTag();
if (s7 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f47(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseMediaBooleanTags();
if (s2 !== peg$FAILED) {
s3 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s4 = peg$c12;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s4 !== peg$FAILED) {
s3.push(s4);
if (input.charCodeAt(peg$currPos) === 32) {
s4 = peg$c12;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s4 = peg$parseBlockTag();
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f48(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseBlockTag();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f49();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f50(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
}
}
}
}
}
}
return s0;
}
function peg$parseMediaSizeTags() {
let s0;
if (input.substr(peg$currPos, 5) === peg$c23) {
s0 = peg$c23;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e27);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c24) {
s0 = peg$c24;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e28);
}
}
}
return s0;
}
function peg$parseMediaAlignmentTags() {
let s0;
if (input.substr(peg$currPos, 9) === peg$c25) {
s0 = peg$c25;
peg$currPos += 9;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e29);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 12) === peg$c26) {
s0 = peg$c26;
peg$currPos += 12;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e30);
}
}
}
return s0;
}
function peg$parseMediaAlignment() {
let s0;
if (input.substr(peg$currPos, 4) === peg$c27) {
s0 = peg$c27;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e31);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c28) {
s0 = peg$c28;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e32);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c29) {
s0 = peg$c29;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e33);
}
}
}
}
return s0;
}
function peg$parseMediaTextTags() {
let s0;
if (input.substr(peg$currPos, 3) === peg$c30) {
s0 = peg$c30;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e34);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 7) === peg$c31) {
s0 = peg$c31;
peg$currPos += 7;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e35);
}
}
}
return s0;
}
function peg$parseMediaBooleanTags() {
let s0;
if (input.substr(peg$currPos, 12) === peg$c32) {
s0 = peg$c32;
peg$currPos += 12;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e36);
}
}
return s0;
}
function peg$parseBoolean() {
let s0;
if (input.substr(peg$currPos, 4) === peg$c33) {
s0 = peg$c33;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e37);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c34) {
s0 = peg$c34;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e38);
}
}
}
return s0;
}
function peg$parsebitmarkPlus() {
let s0, s1;
peg$silentFails++;
s0 = peg$currPos;
s1 = peg$parseInlineTags();
peg$savedPos = s0;
s1 = peg$f51(s1);
s0 = s1;
peg$silentFails--;
return s0;
}
function peg$parsebitmarkPlusString() {
let s0;
peg$silentFails++;
s0 = peg$parseInlineTags();
peg$silentFails--;
return s0;
}
function peg$parseInlineTags() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parseInlinePlainText();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = [];
s3 = peg$parseInlineStyledText();
if (s3 === peg$FAILED) {
s3 = peg$parseInlinePlainText();
}
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseInlineStyledText();
if (s3 === peg$FAILED) {
s3 = peg$parseInlinePlainText();
}
}
peg$savedPos = s0;
s0 = peg$f52(s1, s2);
return s0;
}
function peg$parseInlinePlainText() {
let s0, s1, s2, s3, s4, s5, s6;
s0 = peg$currPos;
s1 = peg$parseNL();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f53();
}
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$currPos;
s2 = [];
s3 = peg$currPos;
s4 = peg$parseInlineTagTags();
if (s4 === peg$FAILED) {
s4 = null;
}
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseInlineStyledText();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s4 = [s4, s5, s6];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 === peg$FAILED) {
s3 = peg$currPos;
s4 = peg$parseInlineTagTags();
if (s4 !== peg$FAILED) {
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseInlineStyledText();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
}
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$currPos;
s4 = peg$parseInlineTagTags();
if (s4 === peg$FAILED) {
s4 = null;
}
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseInlineStyledText();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s4 = [s4, s5, s6];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 === peg$FAILED) {
s3 = peg$currPos;
s4 = peg$parseInlineTagTags();
if (s4 !== peg$FAILED) {
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseInlineStyledText();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
}
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s1 = input.substring(s1, peg$currPos);
} else {
s1 = s2;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f54(s1);
}
s0 = s1;
}
return s0;
}
function peg$parseInlineHalfTag() {
let s0;
if (input.charCodeAt(peg$currPos) === 61) {
s0 = peg$c35;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e39);
}
}
return s0;
}
function peg$parseInlineTag() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseInlineHalfTag();
if (s1 !== peg$FAILED) {
s2 = peg$parseInlineHalfTag();
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseInlineStyledText() {
let s0, s1, s2, s3, s4, s5, s6, s7;
s0 = peg$currPos;
s1 = peg$parseBodyBitOpenTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = input.charAt(peg$currPos);
if (peg$r1.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = input.charAt(peg$currPos);
if (peg$r1.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = input.substring(s2, peg$currPos);
} else {
s2 = s3;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseBodyBitCloseTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f55(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseInlineTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseInlineTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseInlineTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseInlineTag();
if (s3 !== peg$FAILED) {
if (input.substr(peg$currPos, 7) === peg$c36) {
s4 = peg$c36;
peg$currPos += 7;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e40);
}
}
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f56(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseInlineTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseInlineTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseInlineTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseInlineTag();
if (s3 !== peg$FAILED) {
if (input.substr(peg$currPos, 13) === peg$c37) {
s4 = peg$c37;
peg$currPos += 13;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e41);
}
}
if (s4 !== peg$FAILED) {
s5 = peg$parseUrl();
if (s5 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 124) {
s6 = peg$c0;
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e2);
}
}
if (s6 !== peg$FAILED) {
s7 = peg$parseInlineMediaChain();
peg$savedPos = s0;
s0 = peg$f57(s2, s5, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseInlineTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseInlineTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseInlineTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseInlineTag();
if (s3 !== peg$FAILED) {
s4 = peg$parseAttrChain();
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f58(s2, s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseBoldTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBoldTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBoldTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBoldTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f59(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseItalicTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseItalicTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseItalicTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseItalicTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f60(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseLightTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseLightTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseLightTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseLightTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f61(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseHighlightTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseHighlightTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseHighlightTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseHighlightTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f62(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseUrl();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f63(s1);
}
s0 = s1;
}
}
}
}
}
}
}
}
return s0;
}
function peg$parseInlineTagTags() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseInlineTag();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parseInlineHalfTag();
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parseInlineHalfTag();
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseLightTag();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parseLightHalfTag();
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parseLightHalfTag();
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseHighlightTag();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parseHighlightHalfTag();
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parseHighlightHalfTag();
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
}
}
return s0;
}
function peg$parseAttrChain() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 124) {
s1 = peg$c0;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e2);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseAttrChainItem();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseAttrChainItem();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f64(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseInlineMediaChain() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = [];
s2 = peg$parseInlineMediaChainItem();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseInlineMediaChainItem();
}
peg$savedPos = s0;
s1 = peg$f65(s1);
s0 = s1;
return s0;
}
function peg$parseInlineMediaChainItem() {
let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 35) {
s1 = peg$c21;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e25);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f66(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
if (input.substr(peg$currPos, 6) === peg$c38) {
s2 = peg$c38;
peg$currPos += 6;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e42);
}
}
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s3 = peg$c5;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s5 = peg$currPos;
s6 = [];
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = peg$parsechar();
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = peg$parsechar();
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
}
s5 = input.substring(s5, peg$currPos);
s6 = peg$parseBlockTag();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f67(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseMediaSizeTags();
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s3 = peg$c5;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s5 = peg$currPos;
s6 = [];
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = input.charAt(peg$currPos);
if (peg$r1.test(s9)) {
peg$currPos++;
} else {
s9 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
if (s7 !== peg$FAILED) {
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = input.charAt(peg$currPos);
if (peg$r1.test(s9)) {
peg$currPos++;
} else {
s9 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
}
} else {
s6 = peg$FAILED;
}
if (s6 !== peg$FAILED) {
s5 = input.substring(s5, peg$currPos);
} else {
s5 = s6;
}
if (s5 !== peg$FAILED) {
s6 = peg$parseBlockTag();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f68(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseMediaSizeTags();
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s3 = peg$c5;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s5 = peg$currPos;
s6 = [];
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = peg$parsechar();
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = peg$parsechar();
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
}
s5 = input.substring(s5, peg$currPos);
s6 = peg$parseBlockTag();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f69(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
if (input.substr(peg$currPos, 17) === peg$c39) {
s2 = peg$c39;
peg$currPos += 17;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e43);
}
}
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s3 = peg$c5;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s5 = peg$parseInlineMediaAlignment();
if (s5 !== peg$FAILED) {
s6 = peg$parseBlockTag();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f70(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 === peg$FAILED) {
s1 = null;
}
if (input.substr(peg$currPos, 4) === peg$c40) {
s2 = peg$c40;
peg$currPos += 4;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e44);
}
}
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s3 = peg$c5;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
while (s5 !== peg$FAILED) {
s4.push(s5);
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c12;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
s5 = peg$parseInlineMediaSize();
if (s5 !== peg$FAILED) {
s6 = peg$parseBlockTag();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f71(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
}
}
}
}
return s0;
}
function peg$parseInlineMediaAlignment() {
let s0;
if (input.substr(peg$currPos, 3) === peg$c41) {
s0 = peg$c41;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e45);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c42) {
s0 = peg$c42;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e46);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c43) {
s0 = peg$c43;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e47);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 8) === peg$c44) {
s0 = peg$c44;
peg$currPos += 8;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e48);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 3) === peg$c45) {
s0 = peg$c45;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e49);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c46) {
s0 = peg$c46;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e50);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 11) === peg$c47) {
s0 = peg$c47;
peg$currPos += 11;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e51);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 8) === peg$c48) {
s0 = peg$c48;
peg$currPos += 8;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e52);
}
}
}
}
}
}
}
}
}
return s0;
}
function peg$parseInlineMediaSize() {
let s0;
if (input.substr(peg$currPos, 11) === peg$c49) {
s0 = peg$c49;
peg$currPos += 11;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e53);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 11) === peg$c50) {
s0 = peg$c50;
peg$currPos += 11;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e54);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c46) {
s0 = peg$c46;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e50);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 3) === peg$c45) {
s0 = peg$c45;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e49);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 8) === peg$c51) {
s0 = peg$c51;
peg$currPos += 8;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e55);
}
}
}
}
}
}
return s0;
}
function peg$parseAttrChainItem() {
let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12;
s0 = peg$currPos;
if (input.substr(peg$currPos, 5) === peg$c52) {
s1 = peg$c52;
peg$currPos += 5;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e56);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f72(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 7) === peg$c53) {
s1 = peg$c53;
peg$currPos += 7;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e57);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseRefsChain();
s4 = peg$parseBlockTag();
if (s4 !== peg$FAILED) {
if (input.substr(peg$currPos, 9) === peg$c54) {
s5 = peg$c54;
peg$currPos += 9;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e58);
}
}
if (s5 !== peg$FAILED) {
s6 = peg$currPos;
s7 = [];
s8 = peg$currPos;
s9 = peg$currPos;
peg$silentFails++;
s10 = peg$parseBlockTag();
peg$silentFails--;
if (s10 === peg$FAILED) {
s9 = void 0;
} else {
peg$currPos = s9;
s9 = peg$FAILED;
}
if (s9 !== peg$FAILED) {
s10 = peg$parsechar();
if (s10 !== peg$FAILED) {
s9 = [s9, s10];
s8 = s9;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
while (s8 !== peg$FAILED) {
s7.push(s8);
s8 = peg$currPos;
s9 = peg$currPos;
peg$silentFails++;
s10 = peg$parseBlockTag();
peg$silentFails--;
if (s10 === peg$FAILED) {
s9 = void 0;
} else {
peg$currPos = s9;
s9 = peg$FAILED;
}
if (s9 !== peg$FAILED) {
s10 = peg$parsechar();
if (s10 !== peg$FAILED) {
s9 = [s9, s10];
s8 = s9;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
}
s6 = input.substring(s6, peg$currPos);
s7 = peg$parseBlockTag();
if (s7 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f73(s2, s3, s6);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 5) === peg$c55) {
s1 = peg$c55;
peg$currPos += 5;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e59);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 9658) {
s4 = peg$c56;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e60);
}
}
if (s4 !== peg$FAILED) {
s5 = peg$currPos;
s6 = [];
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = peg$parsechar();
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$currPos;
s8 = peg$currPos;
peg$silentFails++;
s9 = peg$parseBlockTag();
peg$silentFails--;
if (s9 === peg$FAILED) {
s8 = void 0;
} else {
peg$currPos = s8;
s8 = peg$FAILED;
}
if (s8 !== peg$FAILED) {
s9 = peg$parsechar();
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
}
s5 = input.substring(s5, peg$currPos);
s6 = peg$parseBlockTag();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f74(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 5) === peg$c55) {
s1 = peg$c55;
peg$currPos += 5;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e59);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f75(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 9658) {
s1 = peg$c56;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e60);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f76(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 7) === peg$c57) {
s1 = peg$c57;
peg$currPos += 7;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e61);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
s4 = peg$parseMediaChain();
peg$savedPos = s0;
s0 = peg$f77(s2, s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 9) === peg$c58) {
s1 = peg$c58;
peg$currPos += 9;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e62);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f78(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 10) === peg$c59) {
s1 = peg$c59;
peg$currPos += 10;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e63);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f79(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c60) {
s1 = peg$c60;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e64);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f80(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c4) {
s1 = peg$c4;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e6);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseBlockTag();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f81();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 5) === peg$c61) {
s1 = peg$c61;
peg$currPos += 5;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e65);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f82(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 5) === peg$c62) {
s1 = peg$c62;
peg$currPos += 5;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e66);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseBlockTag();
if (s2 !== peg$FAILED) {
if (input.substr(peg$currPos, 9) === peg$c63) {
s3 = peg$c63;
peg$currPos += 9;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e67);
}
}
if (s3 !== peg$FAILED) {
s4 = peg$currPos;
s5 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 80) {
s6 = peg$c64;
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e68);
}
}
if (s6 !== peg$FAILED) {
s7 = peg$currPos;
s8 = [];
s9 = peg$currPos;
s10 = peg$currPos;
peg$silentFails++;
s11 = peg$parseBlockTag();
peg$silentFails--;
if (s11 === peg$FAILED) {
s10 = void 0;
} else {
peg$currPos = s10;
s10 = peg$FAILED;
}
if (s10 !== peg$FAILED) {
s11 = peg$parsechar();
if (s11 !== peg$FAILED) {
s10 = [s10, s11];
s9 = s10;
} else {
peg$currPos = s9;
s9 = peg$FAILED;
}
} else {
peg$currPos = s9;
s9 = peg$FAILED;
}
while (s9 !== peg$FAILED) {
s8.push(s9);
s9 = peg$currPos;
s10 = peg$currPos;
peg$silentFails++;
s11 = peg$parseBlockTag();
peg$silentFails--;
if (s11 === peg$FAILED) {
s10 = void 0;
} else {
peg$currPos = s10;
s10 = peg$FAILED;
}
if (s10 !== peg$FAILED) {
s11 = peg$parsechar();
if (s11 !== peg$FAILED) {
s10 = [s10, s11];
s9 = s10;
} else {
peg$currPos = s9;
s9 = peg$FAILED;
}
} else {
peg$currPos = s9;
s9 = peg$FAILED;
}
}
s7 = input.substring(s7, peg$currPos);
s6 = [s6, s7];
s5 = s6;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s4 = input.substring(s4, peg$currPos);
} else {
s4 = s5;
}
if (s4 !== peg$FAILED) {
s5 = peg$parseBlockTag();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f83(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 6) === peg$c65) {
s1 = peg$c65;
peg$currPos += 6;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e69);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
if (input.substr(peg$currPos, 9) === peg$c63) {
s4 = peg$c63;
peg$currPos += 9;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e67);
}
}
if (s4 !== peg$FAILED) {
s5 = peg$currPos;
s6 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 80) {
s7 = peg$c64;
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e68);
}
}
if (s7 !== peg$FAILED) {
s8 = peg$currPos;
s9 = [];
s10 = peg$currPos;
s11 = peg$currPos;
peg$silentFails++;
s12 = peg$parseBlockTag();
peg$silentFails--;
if (s12 === peg$FAILED) {
s11 = void 0;
} else {
peg$currPos = s11;
s11 = peg$FAILED;
}
if (s11 !== peg$FAILED) {
s12 = peg$parsechar();
if (s12 !== peg$FAILED) {
s11 = [s11, s12];
s10 = s11;
} else {
peg$currPos = s10;
s10 = peg$FAILED;
}
} else {
peg$currPos = s10;
s10 = peg$FAILED;
}
while (s10 !== peg$FAILED) {
s9.push(s10);
s10 = peg$currPos;
s11 = peg$currPos;
peg$silentFails++;
s12 = peg$parseBlockTag();
peg$silentFails--;
if (s12 === peg$FAILED) {
s11 = void 0;
} else {
peg$currPos = s11;
s11 = peg$FAILED;
}
if (s11 !== peg$FAILED) {
s12 = peg$parsechar();
if (s12 !== peg$FAILED) {
s11 = [s11, s12];
s10 = s11;
} else {
peg$currPos = s10;
s10 = peg$FAILED;
}
} else {
peg$currPos = s10;
s10 = peg$FAILED;
}
}
s8 = input.substring(s8, peg$currPos);
s7 = [s7, s8];
s6 = s7;
} else {
peg$currPos = s6;
s6 = peg$FAILED;
}
if (s6 !== peg$FAILED) {
s5 = input.substring(s5, peg$currPos);
} else {
s5 = s6;
}
if (s5 !== peg$FAILED) {
s6 = peg$parseBlockTag();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f84(s2, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 9) === peg$c66) {
s1 = peg$c66;
peg$currPos += 9;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e70);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseBlockTag();
if (s2 !== peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c67) {
s3 = peg$c67;
peg$currPos += 6;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e71);
}
}
if (s3 !== peg$FAILED) {
s4 = peg$parseHighlightColor();
if (s4 !== peg$FAILED) {
s5 = peg$parseBlockTag();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f85(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 13) === peg$c68) {
s1 = peg$c68;
peg$currPos += 13;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e72);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseBlockTag();
if (s2 !== peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c67) {
s3 = peg$c67;
peg$currPos += 6;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e71);
}
}
if (s3 !== peg$FAILED) {
s4 = peg$parseHighlightColor();
if (s4 !== peg$FAILED) {
s5 = peg$parseBlockTag();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f86(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 6) === peg$c67) {
s1 = peg$c67;
peg$currPos += 6;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e71);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseColor();
if (s2 !== peg$FAILED) {
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f87(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 12) === peg$c69) {
s1 = peg$c69;
peg$currPos += 12;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e73);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f88(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseAlternativeStyleTags();
if (s1 !== peg$FAILED) {
s2 = peg$parseBlockTag();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f89(s1);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 35) {
s1 = peg$c21;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e25);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBlockTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f90(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
return s0;
}
function peg$parseRefsChain() {
let s0, s1;
s0 = [];
s1 = peg$parseRef();
while (s1 !== peg$FAILED) {
s0.push(s1);
s1 = peg$parseRef();
}
return s0;
}
function peg$parseRef() {
let s0, s1, s2, s3, s4, s5, s6;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c70) {
s1 = peg$c70;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e74);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
peg$savedPos = s0;
s0 = peg$f91(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseAlternativeStyleTags() {
let s0;
if (input.substr(peg$currPos, 4) === peg$c71) {
s0 = peg$c71;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e75);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c72) {
s0 = peg$c72;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e76);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c73) {
s0 = peg$c73;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e77);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 15) === peg$c74) {
s0 = peg$c74;
peg$currPos += 15;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e78);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 15) === peg$c75) {
s0 = peg$c75;
peg$currPos += 15;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e79);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 14) === peg$c76) {
s0 = peg$c76;
peg$currPos += 14;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e80);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 13) === peg$c77) {
s0 = peg$c77;
peg$currPos += 13;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e81);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 15) === peg$c78) {
s0 = peg$c78;
peg$currPos += 15;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e82);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 13) === peg$c79) {
s0 = peg$c79;
peg$currPos += 13;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e83);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 14) === peg$c80) {
s0 = peg$c80;
peg$currPos += 14;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e84);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 14) === peg$c81) {
s0 = peg$c81;
peg$currPos += 14;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e85);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 14) === peg$c82) {
s0 = peg$c82;
peg$currPos += 14;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e86);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 13) === peg$c83) {
s0 = peg$c83;
peg$currPos += 13;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e87);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 9) === peg$c66) {
s0 = peg$c66;
peg$currPos += 9;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e70);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c84) {
s0 = peg$c84;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e88);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 9) === peg$c85) {
s0 = peg$c85;
peg$currPos += 9;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e89);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 11) === peg$c86) {
s0 = peg$c86;
peg$currPos += 11;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e90);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 3) === peg$c87) {
s0 = peg$c87;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e91);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 3) === peg$c88) {
s0 = peg$c88;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e92);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 9) === peg$c89) {
s0 = peg$c89;
peg$currPos += 9;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e93);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 15) === peg$c90) {
s0 = peg$c90;
peg$currPos += 15;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e94);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 9) === peg$c91) {
s0 = peg$c91;
peg$currPos += 9;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e95);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c92) {
s0 = peg$c92;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e96);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 13) === peg$c93) {
s0 = peg$c93;
peg$currPos += 13;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e97);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 16) === peg$c94) {
s0 = peg$c94;
peg$currPos += 16;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e98);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 16) === peg$c95) {
s0 = peg$c95;
peg$currPos += 16;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e99);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 15) === peg$c96) {
s0 = peg$c96;
peg$currPos += 15;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e100);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 14) === peg$c97) {
s0 = peg$c97;
peg$currPos += 14;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e101);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 16) === peg$c98) {
s0 = peg$c98;
peg$currPos += 16;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e102);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 14) === peg$c99) {
s0 = peg$c99;
peg$currPos += 14;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e103);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 15) === peg$c100) {
s0 = peg$c100;
peg$currPos += 15;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e104);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 15) === peg$c101) {
s0 = peg$c101;
peg$currPos += 15;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e105);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 15) === peg$c102) {
s0 = peg$c102;
peg$currPos += 15;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e106);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 14) === peg$c103) {
s0 = peg$c103;
peg$currPos += 14;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e107);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 10) === peg$c104) {
s0 = peg$c104;
peg$currPos += 10;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e108);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 13) === peg$c105) {
s0 = peg$c105;
peg$currPos += 13;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e109);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 19) === peg$c106) {
s0 = peg$c106;
peg$currPos += 19;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e110);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 10) === peg$c107) {
s0 = peg$c107;
peg$currPos += 10;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e111);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 10) === peg$c108) {
s0 = peg$c108;
peg$currPos += 10;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e112);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 13) === peg$c68) {
s0 = peg$c68;
peg$currPos += 13;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e72);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 11) === peg$c109) {
s0 = peg$c109;
peg$currPos += 11;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e113);
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
return s0;
}
function peg$parseColor() {
let s0;
if (input.substr(peg$currPos, 4) === peg$c110) {
s0 = peg$c110;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e114);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c111) {
s0 = peg$c111;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e115);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c112) {
s0 = peg$c112;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e116);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c113) {
s0 = peg$c113;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e117);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 7) === peg$c114) {
s0 = peg$c114;
peg$currPos += 7;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e118);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 9) === peg$c115) {
s0 = peg$c115;
peg$currPos += 9;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e119);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c116) {
s0 = peg$c116;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e120);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 8) === peg$c117) {
s0 = peg$c117;
peg$currPos += 8;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e121);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c118) {
s0 = peg$c118;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e122);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c119) {
s0 = peg$c119;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e123);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 7) === peg$c120) {
s0 = peg$c120;
peg$currPos += 7;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e124);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c121) {
s0 = peg$c121;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e125);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c122) {
s0 = peg$c122;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e126);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c123) {
s0 = peg$c123;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e127);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c124) {
s0 = peg$c124;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e128);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c125) {
s0 = peg$c125;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e129);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c126) {
s0 = peg$c126;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e130);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 3) === peg$c127) {
s0 = peg$c127;
peg$currPos += 3;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e131);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c128) {
s0 = peg$c128;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e132);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c129) {
s0 = peg$c129;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e133);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c130) {
s0 = peg$c130;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e134);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c131) {
s0 = peg$c131;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e135);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c132) {
s0 = peg$c132;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e136);
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
return s0;
}
function peg$parseHighlightColor() {
let s0;
if (input.substr(peg$currPos, 6) === peg$c124) {
s0 = peg$c124;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e128);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c132) {
s0 = peg$c132;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e136);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c118) {
s0 = peg$c118;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e122);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c112) {
s0 = peg$c112;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e116);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c126) {
s0 = peg$c126;
peg$currPos += 6;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e130);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c125) {
s0 = peg$c125;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e129);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c113) {
s0 = peg$c113;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e117);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c131) {
s0 = peg$c131;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e135);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 5) === peg$c111) {
s0 = peg$c111;
peg$currPos += 5;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e115);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c116) {
s0 = peg$c116;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e120);
}
}
}
}
}
}
}
}
}
}
}
return s0;
}
function peg$parsebitmarkMinusMinus() {
let s0, s1;
peg$silentFails++;
s0 = peg$currPos;
s1 = peg$parsebitmarkMinusMinusString();
peg$savedPos = s0;
s1 = peg$f92(s1);
s0 = s1;
peg$silentFails--;
return s0;
}
function peg$parsebitmarkMinusMinusString() {
let s0, s1, s2, s3;
peg$silentFails++;
s0 = peg$currPos;
s1 = peg$parsePlainText();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = [];
s3 = peg$parseStyledText();
if (s3 === peg$FAILED) {
s3 = peg$parsePlainText();
}
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseStyledText();
if (s3 === peg$FAILED) {
s3 = peg$parsePlainText();
}
}
peg$savedPos = s0;
s0 = peg$f93(s1, s2);
peg$silentFails--;
return s0;
}
function peg$parsePlainText() {
let s0, s1, s2, s3, s4, s5, s6;
s0 = peg$currPos;
s1 = peg$parseNL();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f94();
}
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$currPos;
s2 = [];
s3 = peg$currPos;
s4 = peg$parseTagTags();
if (s4 === peg$FAILED) {
s4 = null;
}
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseStyledText();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s4 = [s4, s5, s6];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 === peg$FAILED) {
s3 = peg$currPos;
s4 = peg$parseTagTags();
if (s4 !== peg$FAILED) {
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseStyledText();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
}
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$currPos;
s4 = peg$parseTagTags();
if (s4 === peg$FAILED) {
s4 = null;
}
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseStyledText();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parsechar();
if (s6 !== peg$FAILED) {
s4 = [s4, s5, s6];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 === peg$FAILED) {
s3 = peg$currPos;
s4 = peg$parseTagTags();
if (s4 !== peg$FAILED) {
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseStyledText();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
}
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s1 = input.substring(s1, peg$currPos);
} else {
s1 = s2;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f95(s1);
}
s0 = s1;
}
return s0;
}
function peg$parseBoldHalfTag() {
let s0;
if (input.charCodeAt(peg$currPos) === 42) {
s0 = peg$c133;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e137);
}
}
return s0;
}
function peg$parseItalicHalfTag() {
let s0;
if (input.charCodeAt(peg$currPos) === 95) {
s0 = peg$c134;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e138);
}
}
return s0;
}
function peg$parseLightHalfTag() {
let s0;
if (input.charCodeAt(peg$currPos) === 96) {
s0 = peg$c135;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e139);
}
}
return s0;
}
function peg$parseHighlightHalfTag() {
let s0;
if (input.charCodeAt(peg$currPos) === 33) {
s0 = peg$c136;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e140);
}
}
return s0;
}
function peg$parseBoldTag() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseBoldHalfTag();
if (s1 !== peg$FAILED) {
s2 = peg$parseBoldHalfTag();
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseItalicTag() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseItalicHalfTag();
if (s1 !== peg$FAILED) {
s2 = peg$parseItalicHalfTag();
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseLightTag() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseLightHalfTag();
if (s1 !== peg$FAILED) {
s2 = peg$parseLightHalfTag();
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseHighlightTag() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseHighlightHalfTag();
if (s1 !== peg$FAILED) {
s2 = peg$parseHighlightHalfTag();
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseBodyBitOpenTag() {
let s0;
if (input.substr(peg$currPos, 2) === peg$c137) {
s0 = peg$c137;
peg$currPos += 2;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e141);
}
}
return s0;
}
function peg$parseBodyBitCloseTag() {
let s0;
if (input.charCodeAt(peg$currPos) === 93) {
s0 = peg$c138;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e142);
}
}
return s0;
}
function peg$parseStyledText() {
let s0, s1, s2, s3, s4, s5, s6;
s0 = peg$currPos;
s1 = peg$parseBodyBitOpenTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = input.charAt(peg$currPos);
if (peg$r1.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = input.charAt(peg$currPos);
if (peg$r1.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = input.substring(s2, peg$currPos);
} else {
s2 = s3;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseBodyBitCloseTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f96(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseBoldTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBoldTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBoldTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseBoldTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f97(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseItalicTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseItalicTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseItalicTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseItalicTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f98(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseLightTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseLightTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseLightTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseLightTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f99(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseHighlightTag();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseHighlightTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseHighlightTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
if (input.length > peg$currPos) {
s6 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
s3 = peg$parseHighlightTag();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f100(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
}
}
}
return s0;
}
function peg$parseTagTags() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseLightTag();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parseLightHalfTag();
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parseLightHalfTag();
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseHighlightTag();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parseHighlightHalfTag();
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parseHighlightHalfTag();
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
}
return s0;
}
function peg$parseNL() {
let s0, s1;
peg$silentFails++;
if (input.charCodeAt(peg$currPos) === 10) {
s0 = peg$c139;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e144);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 2) === peg$c140) {
s0 = peg$c140;
peg$currPos += 2;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e145);
}
}
if (s0 === peg$FAILED) {
s0 = input.charAt(peg$currPos);
if (peg$r2.test(s0)) {
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e146);
}
}
}
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e143);
}
}
return s0;
}
function peg$parsechar() {
let s0;
s0 = input.charAt(peg$currPos);
if (peg$r3.test(s0)) {
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e147);
}
}
return s0;
}
function peg$parseEOL() {
let s0, s1;
s0 = peg$parseNL();
if (s0 === peg$FAILED) {
s0 = peg$currPos;
peg$silentFails++;
if (input.length > peg$currPos) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
peg$silentFails--;
if (s1 === peg$FAILED) {
s0 = void 0;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parseUrlHttp() {
let s0, s1, s2, s3, s4, s5, s6, s7, s8;
s0 = peg$currPos;
s1 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c141) {
s2 = peg$c141;
peg$currPos += 4;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e148);
}
}
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 115) {
s3 = peg$c142;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e149);
}
}
if (s3 === peg$FAILED) {
s3 = null;
}
if (input.substr(peg$currPos, 3) === peg$c143) {
s4 = peg$c143;
peg$currPos += 3;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e150);
}
}
if (s4 !== peg$FAILED) {
s5 = [];
s6 = peg$currPos;
s7 = peg$currPos;
peg$silentFails++;
s8 = peg$parseBlockTag();
peg$silentFails--;
if (s8 === peg$FAILED) {
s7 = void 0;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
if (s7 !== peg$FAILED) {
s8 = peg$parseUrlChars();
if (s8 !== peg$FAILED) {
s7 = [s7, s8];
s6 = s7;
} else {
peg$currPos = s6;
s6 = peg$FAILED;
}
} else {
peg$currPos = s6;
s6 = peg$FAILED;
}
while (s6 !== peg$FAILED) {
s5.push(s6);
s6 = peg$currPos;
s7 = peg$currPos;
peg$silentFails++;
s8 = peg$parseBlockTag();
peg$silentFails--;
if (s8 === peg$FAILED) {
s7 = void 0;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
if (s7 !== peg$FAILED) {
s8 = peg$parseUrlChars();
if (s8 !== peg$FAILED) {
s7 = [s7, s8];
s6 = s7;
} else {
peg$currPos = s6;
s6 = peg$FAILED;
}
} else {
peg$currPos = s6;
s6 = peg$FAILED;
}
}
s2 = [s2, s3, s4, s5];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
return s0;
}
function peg$parseUrl() {
let s0, s1, s2, s3, s4, s5, s6;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c141) {
s3 = peg$c141;
peg$currPos += 4;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e148);
}
}
if (s3 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 115) {
s4 = peg$c142;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e149);
}
}
if (s4 === peg$FAILED) {
s4 = null;
}
if (input.substr(peg$currPos, 3) === peg$c143) {
s5 = peg$c143;
peg$currPos += 3;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e150);
}
}
if (s5 !== peg$FAILED) {
s3 = [s3, s4, s5];
s2 = s3;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
if (s2 === peg$FAILED) {
if (input.substr(peg$currPos, 7) === peg$c144) {
s2 = peg$c144;
peg$currPos += 7;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e151);
}
}
}
if (s2 !== peg$FAILED) {
s1 = input.substring(s1, peg$currPos);
} else {
s1 = s2;
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parseUrlChars();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$currPos;
peg$silentFails++;
s6 = peg$parseBlockTag();
peg$silentFails--;
if (s6 === peg$FAILED) {
s5 = void 0;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
if (s5 !== peg$FAILED) {
s6 = peg$parseUrlChars();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
s2 = input.substring(s2, peg$currPos);
peg$savedPos = s0;
s0 = peg$f101(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseUrlChars() {
let s0;
s0 = input.charAt(peg$currPos);
if (peg$r4.test(s0)) {
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e152);
}
}
return s0;
}
var indentStack = [], indent = "";
input = input.replace(/\s+$/, "");
input = input.trimStart();
peg$result = peg$startRuleFunction();
const peg$success = peg$result !== peg$FAILED && peg$currPos === input.length;
function peg$throw() {
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
peg$fail(peg$endExpectation());
}
throw peg$buildStructuredError(
peg$maxFailExpected,
peg$maxFailPos < input.length ? peg$getUnicode(peg$maxFailPos) : null,
peg$maxFailPos < input.length ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
);
}
if (options.peg$library) {
return (
/** @type {any} */
{
peg$result,
peg$currPos,
peg$FAILED,
peg$maxFailExpected,
peg$maxFailPos,
peg$success,
peg$throw: peg$success ? void 0 : peg$throw
}
);
}
if (peg$success) {
return peg$result;
} else {
peg$throw();
}
}
// src/parser/text/peg/TextPegParser.ts
var parse = peg$parse;
// src/parser/text/TextParser.ts
var TextParser = class {
/**
* Get the version of the text parser
*/
version() {
return parse("", {
startRule: "version"
});
}
/**
* Preprocess bitmark text AST into a standard format (TextAst object) from bitmark text AST either as a string
* or a plain JS object
*
* @param ast - bitmark text AST as a string or a plain JS object
* @returns bitmark text AST in a standard format (BitmarkAst object)
*/
preprocessAst(ast) {
if (stringUtils.isString(ast)) {
const str = ast;
try {
ast = JSON.parse(str);
} catch (_e) {
return void 0;
}
}
if (this.isAst(ast)) {
return ast;
}
return void 0;
}
/**
* Check if a plain JS object is valid text AST
*
* @param ast - a plain JS object that might be text AST
* @returns true if text AST, otherwise false
*/
isAst(ast) {
if (Array.isArray(ast)) {
if (ast.length === 0) return true;
if (Object.prototype.hasOwnProperty.call(ast[0], "type")) {
return true;
}
}
return false;
}
/**
* Convert Bitmark text to text AST.
*
* The Bitmark text should be a string.
* If parsed bitmark text AST is passed to this function, it will be returned as is.
*
* @param text - bitmark text
* @returns bitmark text AST as plain JS object
*/
toAst(text, options) {
if (Array.isArray(text)) return text;
let str = text ?? "";
if (!str) return [];
const opts = Object.assign({}, options);
let startRule = "bitmarkPlusPlus";
if (opts.location === TextLocation.tag) {
startRule = "bitmarkPlus";
}
str = str.trim();
return parse(str, {
startRule
});
}
/**
* Extract all the body bits from the text AST
*
* @param text
* @returns
*/
extractBodyBits(text) {
if (!Array.isArray(text)) return [];
const bodyBits = [];
const textAst = text;
for (const node of textAst) {
switch (node.type) {
case TextNodeType.gap:
case TextNodeType.select:
case TextNodeType.highlight:
case TextNodeType.mark:
bodyBits.push(node);
break;
default: {
const bits = this.extractBodyBits(node.content);
for (const bit of bits) {
bodyBits.push(bit);
}
}
}
}
return bodyBits;
}
/**
* Walk all the body bits from the text AST
*
* @param text
* @returns
*/
walkBodyBits(text, callback) {
if (!Array.isArray(text)) return;
const textAst = text;
for (let i = 0; i < textAst.length; i++) {
const node = textAst[i];
switch (node.type) {
case TextNodeType.gap:
case TextNodeType.select:
case TextNodeType.highlight:
case TextNodeType.mark:
callback(textAst, i, node);
break;
default: {
this.walkBodyBits(node.content, callback);
}
}
}
}
};
// src/utils/BooleanUtils.ts
var BooleanUtils = class {
/**
* Check if an object is a boolean.
*
* @param val input value
* @returns the input value if it is an array, otherwise a new array containing the input value
*/
isBoolean(val) {
if (val === true) return true;
if (val === false) return true;
return false;
}
/**
* Check if an object is a boolean, or a boolean string.
*
* @param val input value
* @returns the input value if it is an array, otherwise a new array containing the input value
*/
isBooleanString(val) {
if (this.isBoolean(val)) return true;
if (val === "true") return true;
if (val === "false") return true;
return false;
}
/**
* Convert a value to a boolean.
*
* By default, the return will be false unless the value is truthy (true, "true").
* Setting defaultVal to true will return true unless the value is falsy (false, "false").
*
* @param val input value
* @param defaultVal default value to return if val does not match a boolean or 'true' / 'false'
* @returns val, converted to a boolean
*/
toBoolean(val, defaultVal) {
if (defaultVal) {
if (val === false) return false;
if (val === "false") return false;
return true;
} else {
if (val === true) return true;
if (val === "true") return true;
return false;
}
}
};
var instance5 = new BooleanUtils();
// src/utils/NumberUtils.ts
var NumberUtils = class {
/**
* Convert a value to a number, unless it is already a number.
*
* The value returned if conversion cannot be performed defaults to undefined but can be configured
* via defaultVal.
*
* @param val input value
* @param defaultVal value to return if conversion cannot be performed
* @returns the input value if it is an array, otherwise a new array containing the input value
*/
asNumber(val, defaultVal) {
if (val == null) return defaultVal ?? void 0;
const number = +val;
if (Number.isNaN(number) || !isFinite(number)) return defaultVal ?? void 0;
return number;
}
/**
* Return true if the input value IS or CAN BE converted to a number.
*
* e.g.
* 123 -> true
* 123.45 -> true
* '123' -> true
* '123.45' -> true
* '-123.45' -> true
* '+123.45' -> true
* '0xAc1' -> true
* '0xAc1' -> true
* '0b101' -> true
* '0o701' -> true
* '1e4' -> true
* '1e-4' -> true
* '++1e-4' -> false
* '7^2' -> false
* '0xFFG' -> false
* '0b121' -> false
* '0o781' -> false
* '1x10e4' -> false
* '123abc' -> false
* 'abc' -> false
* null -> false
*
* @param val input value
*
* @returns true if the input value can be converted to a number.
*/
isNumeric(val) {
if (val == null) return false;
const number = +val;
return !Number.isNaN(number) && isFinite(number);
}
};
var instance6 = new NumberUtils();
// src/ast/BaseBuilder.ts
var BaseBuilder = class {
constructor() {
__publicField(this, "textParser");
__publicField(this, "textGenerator");
this.textParser = new TextParser();
this.textGenerator = new TextGenerator();
}
/**
* Convert example to an Example.
* - If example is set, then the isExample will be true and example with be example as a BreakscapedText.
* - Else if __isDefaultExample is true, then __isDefaultExample / isExample will both be true.
* - Else __isDefaultExample / isExample will both be false.
*
* @param __isDefaultExample - true if the example is the default value
* @param example - the example to convert (BreakscapedText, boolean) or undefined if none / default
* @returns example/__isDefaultExample resolved to an Example object
*/
toExample(__isDefaultExample, example, defaultExample) {
if (example != void 0) {
let exampleValue;
if (instance5.isBoolean(defaultExample)) {
exampleValue = instance5.toBoolean(example);
} else {
exampleValue = example;
}
return {
isExample: true,
example: exampleValue,
__defaultExample: defaultExample ?? null
};
}
if (__isDefaultExample) {
return {
isExample: true,
example: defaultExample ?? null,
__defaultExample: defaultExample ?? null
};
}
return {
isExample: false,
example: null,
__defaultExample: defaultExample ?? null
};
}
/**
* Convert a raw bitmark property to an AST property.
*
* @param key
* @param value
* @param options
* @returns
*/
toAstProperty(bitType, key, value, options) {
if (value == null) return void 0;
const bitConfig = instance2.getBitConfig(bitType);
let tagsConfig = bitConfig.tags;
if (options?.cardSet) {
const { sideNo, variantNo } = options.cardSet;
const cardTagsConfig = instance2.getTagsConfigForCardSet(bitType, sideNo, variantNo);
tagsConfig = cardTagsConfig ?? tagsConfig;
}
const propertyConfig = instance2.getTagConfigForTag(tagsConfig, key);
if (propertyConfig?.array && !Array.isArray(value)) {
value = [value];
} else if (!propertyConfig?.array && Array.isArray(value)) {
value = value[value.length - 1];
}
const processValue = (v) => {
if (v == null) return void 0;
const format = propertyConfig?.format ?? TagFormat.plainText;
switch (format) {
case TagFormat.plainText:
if (instance6.asNumber(v) != null) v = `${v}`;
return stringUtils.isString(v) ? stringUtils.trimmedString(v) : void 0;
case TagFormat.number:
return instance6.asNumber(v);
case TagFormat.boolean:
return instance5.toBoolean(v, true);
case TagFormat.invertedBoolean:
return !instance5.toBoolean(v, true);
}
return v;
};
if (Array.isArray(value)) {
const valueArray = value;
for (let i = 0, len = valueArray.length; i < len; i++) {
valueArray[i] = processValue(valueArray[i]);
}
} else {
value = processValue(value);
}
return value;
}
getEmptyTextAst(context) {
return this.handleJsonText(context, TextLocation.tag, " ");
}
/**
* Convert the JsonText from the JSON to the AST format:
* Input:
* - Bitmark v2: breakscaped string
* - Bitmark v3: bitmark text JSON (TextAst)
* Output:
* - bitmark text JSON (TextAst) with __tag property to indicate item is text
*
* In the case of Bitmark v2 type texts, there is nothing to do but cast the type.
*
* @param context BuildContext
* @param textFormat format of TextAst
* @param textLocation location of the text (body, tag, etc.)
* @param text JsonText or JsonText[] to convert
* @returns Breakscaped string or breakscaped string[]
*/
handleJsonText(context, textLocation, text) {
let res;
const { textFormat } = context;
if (text == null) {
res = [];
} else {
if (this.textParser.isAst(text)) {
res = text;
} else if (Array.isArray(text)) {
const strArray = [];
for (let i = 0, len = text.length; i < len; i++) {
const t = text[i];
if (this.textParser.isAst(t)) {
strArray[i] = t;
} else if (stringUtils.isString(t)) {
strArray[i] = this.textParser.toAst(
instance3.breakscape(t, {
format: textFormat,
location: textLocation
}),
{
format: textFormat,
location: textLocation
}
);
strArray[i].__tag = "text";
} else {
strArray[i] = [];
}
}
return strArray;
} else if (stringUtils.isString(text)) {
res = this.textParser.toAst(
instance3.breakscape(text, {
format: textFormat,
location: textLocation,
v2: true
}),
{
format: textFormat,
location: textLocation
}
);
} else {
res = [];
}
}
res.__tag = "text";
return res;
}
// /**
// * Remove any property with a key starting with an underscore.
// *
// * @param json
// */
// protected removeTemporaryProperties(json: Record<string, unknown> | unknown[]): void {
// const obj = json as Record<string, unknown>;
// for (const key in obj) {
// if (key.startsWith('_')) {
// delete obj[key];
// } else if (typeof obj[key] === 'object') {
// this.removeTemporaryProperties(obj[key] as Record<string, unknown>);
// }
// }
// }
};
// src/utils/BitUtils.ts
var textParser = new TextParser();
var BitUtils = class {
/**
* Helper function to fill in the example / isExample of a node based on the values of __isDefaultExample and example.
* This function if for 'string' examples.
*
* @param nodes
* @param __isDefaultExample
* @param example
* @param firstOnly
* @returns
*/
fillStringExample(textFormat, nodes, __isDefaultExample, example, firstOnly) {
if (!nodes) return;
if (!Array.isArray(nodes)) nodes = [nodes];
for (const node of nodes) {
if (!node.isExample) {
if (__isDefaultExample) {
node.example = node.__defaultExample;
node.isExample = true;
} else {
node.example = example ? textParser.toAst(example, {
format: textFormat,
location: TextLocation.tag
}) : void 0;
}
if (firstOnly) break;
}
}
}
/**
* Helper function to fill in the example / isExample of a node based on the values of __isDefaultExample and example.
* This function if for 'boolean' examples.
*
* @param nodes
* @param __isDefaultExample
* @param example
* @param firstCorrectOnly
* @returns
*/
fillBooleanExample(nodes, __isDefaultExample, example, firstCorrectOnly) {
if (!nodes) return;
if (!Array.isArray(nodes)) nodes = [nodes];
for (const node of nodes) {
if (!node.isExample && (!firstCorrectOnly || node.isCorrect)) {
if (__isDefaultExample) {
node.example = node.__defaultExample;
node.isExample = true;
} else {
node.example = instance5.toBoolean(example);
}
if (firstCorrectOnly) break;
}
}
}
};
var instance7 = new BitUtils();
// src/ast/ResourceBuilder.ts
import { Enum as Enum6 } from "@ncoderz/superenum";
// src/utils/UrlUtils.ts
var UrlUtils = class {
fileExtensionFromUrl(url) {
let format;
if (url) {
try {
const parsedUrl = new URL(url);
const pathParts = parsedUrl.pathname.split(".");
if (pathParts.length > 1) {
format = pathParts[pathParts.length - 1];
}
} catch (_e) {
}
}
return format;
}
domainFromUrl(url) {
let domain;
if (url) {
try {
const parsedUrl = new URL(url);
domain = parsedUrl.hostname;
} catch (_e) {
}
}
return domain;
}
};
var instance8 = new UrlUtils();
// src/ast/rules/NodeValidator.ts
var NodeValidator = class {
validateBit(bit) {
if (!bit) return bit;
const ret = bit;
return ret;
}
validateCardBit(bit) {
if (!bit) return bit;
const ret = bit;
return ret;
}
validateResource(resource) {
if (!resource) return void 0;
const ret = resource;
let valid = true;
switch (resource.type) {
case ResourceType.image: {
const imageResource = resource.image;
if (!stringUtils.isString(imageResource.src)) imageResource.src = "";
break;
}
case ResourceType.imageLink: {
const imageLink = resource.imageLink;
if (!stringUtils.isString(imageLink.url)) imageLink.url = "";
break;
}
case ResourceType.audio: {
const audio = resource.audio;
if (!stringUtils.isString(audio.src)) audio.src = "";
break;
}
case ResourceType.audioEmbed: {
const audioEmbed = resource.audioEmbed;
if (!stringUtils.isString(audioEmbed.src)) audioEmbed.src = "";
break;
}
case ResourceType.audioLink: {
const audioLink = resource.audioLink;
if (!stringUtils.isString(audioLink.url)) audioLink.url = "";
break;
}
case ResourceType.video: {
const video = resource.video;
if (!stringUtils.isString(video.src)) video.src = "";
break;
}
case ResourceType.videoEmbed: {
const videoEmbed = resource.videoEmbed;
if (!stringUtils.isString(videoEmbed.url)) videoEmbed.url = "";
break;
}
case ResourceType.videoLink: {
const videoLink = resource.videoLink;
if (!stringUtils.isString(videoLink.url)) videoLink.url = "";
break;
}
case ResourceType.stillImageFilm: {
const stillImageFilm = resource;
if (!stringUtils.isString(stillImageFilm.image.src)) stillImageFilm.image.src = "";
if (!stringUtils.isString(stillImageFilm.audio.src)) stillImageFilm.audio.src = "";
break;
}
case ResourceType.stillImageFilmEmbed: {
const stillImageFilmEmbed = resource.stillImageFilmEmbed;
if (!stringUtils.isString(stillImageFilmEmbed.url)) stillImageFilmEmbed.url = "";
break;
}
case ResourceType.stillImageFilmLink: {
const stillImageFilmLink = resource.stillImageFilmLink;
if (!stringUtils.isString(stillImageFilmLink.url)) stillImageFilmLink.url = "";
break;
}
case ResourceType.article: {
const article = resource.article;
if (!stringUtils.isString(article.body)) article.body = "";
break;
}
case ResourceType.document: {
const document2 = resource.document;
if (!stringUtils.isString(document2.url)) document2.url = "";
break;
}
case ResourceType.documentEmbed: {
const documentEmbed = resource.documentEmbed;
if (!stringUtils.isString(documentEmbed.url)) documentEmbed.url = "";
break;
}
case ResourceType.documentLink: {
const documentLink = resource.documentLink;
if (!stringUtils.isString(documentLink.url)) documentLink.url = "";
break;
}
case ResourceType.documentDownload: {
const documentDownload = resource.documentDownload;
if (!stringUtils.isString(documentDownload.url)) documentDownload.url = "";
break;
}
case ResourceType.appLink: {
const appLink = resource.appLink;
if (!stringUtils.isString(appLink.url)) appLink.url = "";
break;
}
case ResourceType.websiteLink: {
const websiteLink = resource.websiteLink;
if (!stringUtils.isString(websiteLink.url)) websiteLink.url = "";
break;
}
default:
valid = false;
}
if (!valid) {
if (resource.type) {
resource.__invalid = true;
}
}
return ret;
}
isRequired(val, name) {
if (val) return;
throw new Error(`${name} is required but is not set`);
}
isOneOfRequired(vals, names) {
if (Array.isArray(vals)) {
for (const v of vals) {
if (v) return;
}
}
throw new Error(`One of '${names.join(", ")}' is required but none is set`);
}
isString(val, name) {
if (stringUtils.isString(val)) return;
throw new Error(`${name} is required to be a string`);
}
isNonEmptyString(val, name) {
if (stringUtils.isString(val) && val !== "") return;
throw new Error(`${name} is required to be a non-empty string`);
}
isStringOrBoolean(val, name) {
if (stringUtils.isString(val)) return;
if (val === true || val === false) return;
throw new Error(`${name} is required to be a string or a boolean`);
}
isNonEmptyStringOrBoolean(val, name) {
if (stringUtils.isString(val) && val !== "") return;
if (val === true || val === false) return;
throw new Error(`${name} is required to be a non-empty string or a boolean`);
}
isArray(val, name) {
if (Array.isArray(val)) return;
throw new Error(`${name} is required to be a array`);
}
isNonEmptyArray(val, name) {
if (Array.isArray(val) && val.length > 0) return;
throw new Error(`${name} is required to be a non-empty array`);
}
isNumber(val, name) {
if (Number.isFinite(val)) return;
throw new Error(`${name} is required to be a number`);
}
isBoolean(val, name) {
if (val === true || val === false) return;
throw new Error(`${name} is required to be a boolean`);
}
isStringOrNumberOrBooleanOrNullOrUndefined(val, name) {
if (stringUtils.isString(val)) return;
if (Number.isFinite(val)) return;
if (val === true || val === false) return;
if (val == null) return;
throw new Error(
`${name} is required to be a string or a number or a boolean or null or undefined`
);
}
//
// private
//
// private validateInterviewBit(bit: Bit): Bit | undefined {
// // Ensure bit has a questions array as the
// // ===
// // ===
// // must be included in the markup
// if (!bit.cardNode) {
// bit.cardNode = {};
// }
// if (!bit.cardNode.questions) {
// bit.cardNode.questions = [];
// }
// return bit;
// }
};
var nodeValidator = new NodeValidator();
// src/ast/ResourceBuilder.ts
var ResourceBuilder = class extends BaseBuilder {
/**
* Build and validate resource JSON object(s) from external resource JSON object(s)
*
* Note: a single input resource object can be converted to multiple resource objects
* (e.g. stillImageFilm -> image + audio)
*
* @param bitType
* @param resource
* @returns
*/
resourceFromResourceJson(context, resource) {
if (!resource) return void 0;
const nodes = [];
if (!Array.isArray(resource)) resource = [resource];
for (const thisResource of resource) {
let type = Enum6(ResourceType).fromValue(thisResource.type);
if (!type) return void 0;
const resourceKey = Enum6(ResourceType).keyFromValue(type);
if (!resourceKey) return void 0;
const __typeAlias = Enum6(ResourceType).fromValue(thisResource.__typeAlias);
type = __typeAlias ?? type;
let data;
if (type === ResourceType.imageResponsive) {
const r = thisResource;
const imagePortraitNode = this.resourceFromResourceDataJson(
context,
ResourceType.imagePortrait,
r.imagePortrait
);
const imageLandscapeNode = this.resourceFromResourceDataJson(
context,
ResourceType.imageLandscape,
r.imageLandscape
);
if (imagePortraitNode) nodes.push(imagePortraitNode);
if (imageLandscapeNode) nodes.push(imageLandscapeNode);
} else if (type === ResourceType.stillImageFilm) {
const r = thisResource;
const imageNode = this.resourceFromResourceDataJson(context, ResourceType.image, r.image);
const audioNode = this.resourceFromResourceDataJson(context, ResourceType.audio, r.audio);
if (imageNode) nodes.push(imageNode);
if (audioNode) nodes.push(audioNode);
} else {
data = thisResource[resourceKey];
if (!data) return void 0;
const node = this.resourceFromResourceDataJson(context, type, data);
if (node) nodes.push(node);
}
}
if (nodes.length === 0) return void 0;
if (nodes.length === 1) return nodes[0];
return nodes;
}
resourceFromResourceDataJson(context, type, data) {
if (!data) return void 0;
type = Enum6(ResourceType).fromValue(type);
if (!type) return void 0;
const dataAsString = stringUtils.isString(data) ? data : void 0;
const url = data.url || data.src || data.body || dataAsString;
const posterImage = data.posterImage ? this.resourceFromResourceDataJson(
context,
ResourceType.image,
data.posterImage
)?.image : void 0;
const thumbnails = data.thumbnails ? data.thumbnails.map((t) => {
return this.resourceFromResourceDataJson(
context,
ResourceType.image,
t
)?.image;
}) : void 0;
const node = this.resource(context, {
type,
// Generic (except Article / Document)
value: url,
// Alternative source
srcAlt: data.srcAlt,
// ImageLikeResource / AudioLikeResource / VideoLikeResource / Article / Document
format: data.format,
// ImageLikeResource
src1x: data.src1x,
src2x: data.src2x,
src3x: data.src3x,
src4x: data.src4x,
caption: this.handleJsonText(context, TextLocation.tag, data.caption),
// ImageLikeResource / VideoLikeResource
width: data.width ?? void 0,
height: data.height ?? void 0,
alt: data.alt,
zoomDisabled: data.zoomDisabled,
// VideoLikeResource
duration: data.duration,
mute: data.mute,
autoplay: data.autoplay,
allowSubtitles: data.allowSubtitles,
showSubtitles: data.showSubtitles,
posterImage,
thumbnails,
// WebsiteLinkResource
siteName: void 0,
//data.siteName,
// Generic Resource
license: data.license,
copyright: data.copyright,
showInIndex: data.showInIndex,
search: data.search,
selected: data.selected
});
return node;
}
/**
* Build resource node
*
* @param data - data for the node
* @returns
*/
/* private */
resource(context, data) {
let node;
const { type, value: valueIn, format: formatIn, ...rest } = data;
const finalData = {
type,
value: valueIn ?? instance3.EMPTY_STRING,
format: formatIn ?? instance3.EMPTY_STRING,
...rest
};
switch (type) {
case ResourceType.video:
case ResourceType.videoEmbed:
case ResourceType.videoLink:
case ResourceType.stillImageFilmEmbed:
case ResourceType.stillImageFilmLink: {
const thumbnailKeys = ["src1x", "src2x", "src3x", "src4x"];
const thumbnails = [];
for (const k of thumbnailKeys) {
const dataAsAny = data;
const value = dataAsAny[k];
if (value) {
const image = this.resource(context, {
type: ResourceType.image,
value
});
if (image) thumbnails.push(image.image);
}
}
finalData.thumbnails = [...finalData.thumbnails || [], ...thumbnails];
}
}
switch (type) {
case ResourceType.image:
case ResourceType.imagePortrait:
case ResourceType.imageLandscape:
case ResourceType.backgroundWallpaper:
case ResourceType.imagePlaceholder:
case ResourceType.icon:
case ResourceType.platformIcon:
case ResourceType.platformLogo:
case ResourceType.platformBackgroundImage:
case ResourceType.previewImage:
case ResourceType.coverImage:
case ResourceType.backgroundImage:
node = this.imageResource(context, finalData, type);
break;
// case ResourceType.imageResponsive: {
// node = this.imageResponsiveResource({
// imagePortrait: this.imageResource(
// finalData.imagePortrait ?? {
// format: '',
// value: '',
// },
// ),
// imageLandscape: this.imageResource(
// finalData.imageLandscape ?? {
// format: '',
// value: '',
// },
// ),
// });
// break;
// }
case ResourceType.imageLink:
node = this.imageLinkResource(context, finalData);
break;
case ResourceType.audio:
node = this.audioResource(context, finalData);
break;
case ResourceType.audioEmbed:
node = this.audioEmbedResource(context, finalData);
break;
case ResourceType.audioLink:
node = this.audioLinkResource(context, finalData);
break;
case ResourceType.video:
case ResourceType.previewVideo:
node = this.videoResource(context, finalData);
break;
case ResourceType.videoEmbed:
node = this.videoEmbedResource(context, finalData);
break;
case ResourceType.videoLink:
node = this.videoLinkResource(context, finalData);
break;
// case ResourceType.stillImageFilm: {
// node = this.stillImageFilmResource({
// image: this.imageResource(context,
// finalData.image ?? {
// format: '',
// value: '',
// },
// ),
// audio: this.audioResource(context,
// finalData.audio ?? {
// format: '',
// value: '',
// },
// ),
// });
// break;
// }
case ResourceType.stillImageFilmEmbed:
node = this.stillImageFilmEmbedResource(context, finalData);
break;
case ResourceType.stillImageFilmLink:
node = this.stillImageFilmLinkResource(context, finalData);
break;
case ResourceType.article:
node = this.articleResource(context, finalData);
break;
case ResourceType.document:
node = this.documentResource(context, finalData);
break;
case ResourceType.documentEmbed:
node = this.documentEmbedResource(context, finalData);
break;
case ResourceType.documentLink:
node = this.documentLinkResource(context, finalData);
break;
case ResourceType.documentDownload:
node = this.documentDownloadResource(context, finalData);
break;
case ResourceType.appLink:
node = this.appLinkResource(context, finalData);
break;
case ResourceType.websiteLink:
node = this.websiteLinkResource(context, finalData);
break;
default:
}
return node;
}
/**
* Build imageResource node
*
* @param data - data for the node
* @returns
*/
imageResource(context, data, __typeAlias) {
const {
value,
srcAlt,
src1x,
src2x,
src3x,
src4x,
width,
height,
alt,
zoomDisabled,
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const { bitType } = context;
let zoomDisabledDefault = false;
if (instance2.isOfBitType(bitType, [
BitType.imageSeparator,
BitType.pageBanner,
BitType.imagesLogoGrave,
BitType.prototypeImages,
BitType.extractorImage
])) {
zoomDisabledDefault = true;
}
const node = {
type: ResourceType.image,
__typeAlias: __typeAlias ?? ResourceType.image,
__configKey: resourceTypeToConfigKey(__typeAlias ?? ResourceType.image),
image: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
src: value ?? "",
srcAlt: srcAlt ?? void 0,
src1x: src1x ?? void 0,
src2x: src2x ?? void 0,
src3x: src3x ?? void 0,
src4x: src4x ?? void 0,
width: width ?? null,
height: height ?? null,
alt: alt ?? "",
zoomDisabled: zoomDisabled ?? zoomDisabledDefault,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.image, {
ignoreFalse: ["zoomDisabled", "showInIndex", "selected"],
ignoreEmptyArrays: ["caption"],
ignoreUndefined: ["width", "height"],
ignoreEmptyString: ["src", "alt", "license", "copyright"]
});
return nodeValidator.validateResource(node);
}
/**
* Build imageLinkResource node
*
* @param data - data for the node
* @returns
*/
imageLinkResource(context, data) {
const {
value,
srcAlt,
src1x,
src2x,
src3x,
src4x,
width,
height,
alt,
zoomDisabled,
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const node = {
type: ResourceType.imageLink,
__typeAlias: ResourceType.imageLink,
__configKey: resourceTypeToConfigKey(ResourceType.imageLink),
imageLink: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
// src: value ?? '',
url: value ?? "",
srcAlt: srcAlt ?? void 0,
src1x: src1x ?? void 0,
src2x: src2x ?? void 0,
src3x: src3x ?? void 0,
src4x: src4x ?? void 0,
width: width ?? null,
height: height ?? null,
alt: alt ?? "",
zoomDisabled: zoomDisabled ?? false,
// TODO: Default depends on the bit(!)
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.imageLink, {
ignoreFalse: ["zoomDisabled", "showInIndex", "selected"],
ignoreEmptyArrays: ["caption"],
ignoreUndefined: ["width", "height"],
ignoreEmptyString: ["url", "alt", "license", "copyright"]
});
return nodeValidator.validateResource(node);
}
/**
* Build audioResource node
*
* @param data - data for the node
* @returns
*/
audioResource(context, data) {
const {
value,
srcAlt,
duration,
mute,
autoplay,
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const node = {
type: ResourceType.audio,
__typeAlias: ResourceType.audio,
__configKey: resourceTypeToConfigKey(ResourceType.audio),
audio: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
src: value ?? "",
srcAlt: srcAlt ?? void 0,
duration: duration ?? void 0,
mute: mute ?? void 0,
autoplay: autoplay ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.audio, {
ignoreEmptyArrays: ["caption"],
ignoreEmptyString: ["src", "alt", "license", "copyright"],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build audioEmbedResource node
*
* @param data - data for the node
* @returns
*/
audioEmbedResource(context, data) {
const {
value,
srcAlt,
duration,
mute,
autoplay,
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const node = {
type: ResourceType.audioEmbed,
__typeAlias: ResourceType.audioEmbed,
__configKey: resourceTypeToConfigKey(ResourceType.audioEmbed),
audioEmbed: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
src: value ?? "",
srcAlt: srcAlt ?? void 0,
duration: duration ?? void 0,
mute: mute ?? void 0,
autoplay: autoplay ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.audioEmbed, {
ignoreEmptyArrays: ["caption"],
ignoreEmptyString: [
"src",
/*'alt',*/
"license",
"copyright"
],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build audioLinkResource node
*
* @param data - data for the node
* @returns
*/
audioLinkResource(context, data) {
const {
value,
srcAlt,
duration,
mute,
autoplay,
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const node = {
type: ResourceType.audioLink,
__typeAlias: ResourceType.audioLink,
__configKey: resourceTypeToConfigKey(ResourceType.audioLink),
audioLink: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
// src: value ?? '',
url: value ?? "",
srcAlt: srcAlt ?? void 0,
duration: duration ?? void 0,
mute: mute ?? void 0,
autoplay: autoplay ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.audioLink, {
// ignoreEmptyArrays: ['caption'],
ignoreEmptyString: [
"url"
/*'alt', 'license', 'copyright'*/
],
ignoreFalse: ["selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build videoResource node
*
* @param data - data for the node
* @returns
*/
videoResource(context, data) {
const {
value,
srcAlt,
width,
height,
duration,
mute,
autoplay,
allowSubtitles,
showSubtitles,
alt,
posterImage,
thumbnails,
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const node = {
type: ResourceType.video,
__typeAlias: ResourceType.video,
__configKey: resourceTypeToConfigKey(ResourceType.video),
video: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
src: value ?? "",
srcAlt: srcAlt ?? void 0,
width: width ?? null,
height: height ?? null,
duration: duration ?? void 0,
mute: mute ?? void 0,
autoplay: autoplay ?? void 0,
allowSubtitles: allowSubtitles ?? void 0,
showSubtitles: showSubtitles ?? void 0,
alt: alt ?? "",
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false,
// Have sub-chains so must be at end of chain
posterImage: posterImage ?? void 0,
thumbnails: thumbnails ?? void 0
}
};
objectUtils.removeUnwantedProperties(node.video, {
ignoreEmptyArrays: ["caption"],
ignoreUndefined: ["width", "height"],
ignoreEmptyString: [
"src",
/*'alt',*/
"license",
"copyright"
],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build videoEmbedResource node
*
* @param data - data for the node
* @returns
*/
videoEmbedResource(context, data) {
const {
value,
srcAlt,
width,
height,
duration,
mute,
autoplay,
allowSubtitles,
showSubtitles,
alt,
posterImage,
thumbnails,
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const node = {
type: ResourceType.videoEmbed,
__typeAlias: ResourceType.videoEmbed,
__configKey: resourceTypeToConfigKey(ResourceType.videoEmbed),
videoEmbed: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
// src: value ?? '',
url: value ?? "",
srcAlt: srcAlt ?? void 0,
width: width ?? null,
height: height ?? null,
duration: duration ?? void 0,
mute: mute ?? void 0,
autoplay: autoplay ?? void 0,
allowSubtitles: allowSubtitles ?? void 0,
showSubtitles: showSubtitles ?? void 0,
alt: alt ?? "",
posterImage: posterImage ?? void 0,
thumbnails: thumbnails ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.videoEmbed, {
ignoreEmptyArrays: ["caption"],
ignoreUndefined: ["width", "height"],
ignoreEmptyString: [
"url",
/*'alt',*/
"license",
"copyright"
],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build videoLinkResource node
*
* @param data - data for the node
* @returns
*/
videoLinkResource(context, data) {
const {
value,
srcAlt,
width,
height,
duration,
mute,
autoplay,
allowSubtitles,
showSubtitles,
alt,
posterImage,
thumbnails,
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const node = {
type: ResourceType.videoLink,
__typeAlias: ResourceType.videoLink,
__configKey: resourceTypeToConfigKey(ResourceType.videoLink),
videoLink: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
// src: value ?? '',
url: value ?? "",
srcAlt: srcAlt ?? void 0,
width: width ?? null,
height: height ?? null,
duration: duration ?? void 0,
mute: mute ?? void 0,
autoplay: autoplay ?? void 0,
allowSubtitles: allowSubtitles ?? void 0,
showSubtitles: showSubtitles ?? void 0,
alt: alt ?? "",
posterImage: posterImage ?? void 0,
thumbnails: thumbnails ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.videoLink, {
ignoreEmptyArrays: ["caption"],
ignoreUndefined: ["width", "height"],
ignoreEmptyString: [
"url",
/*'alt',*/
"license",
"copyright"
],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
// /**
// * Build stillImageFilmResource node
// *
// * @param data - data for the node
// * @returns
// */
// stillImageFilmResource(context: BuildContext, data: { image?: ImageResource; audio?: AudioResource }): StillImageFilmResource {
// const { image, audio } = data;
// // NOTE: Node order is important and is defined here
// const node: StillImageFilmResource = {
// type: ResourceType.stillImageFilm,
// __typeAlias: ResourceType.stillImageFilm,
// image: image ?? this.imageResource({ format: '', value: '' }),
// audio: audio ?? this.audioResource({ format: '', value: '' }),
// };
// // Remove Unset Optionals
// ObjectUtils.removeUnwantedProperties(node);
// // Validate and correct invalid bits as much as possible
// return NodeValidator.validateResource(node) ;
// }
/**
* Build stillImageFilmEmbedResource node
*
* @param data - data for the node
* @returns
*/
stillImageFilmEmbedResource(context, data) {
const {
value,
srcAlt,
width,
height,
duration,
mute,
autoplay,
allowSubtitles,
showSubtitles,
alt,
posterImage,
thumbnails,
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const node = {
type: ResourceType.stillImageFilmEmbed,
__typeAlias: ResourceType.stillImageFilmEmbed,
__configKey: resourceTypeToConfigKey(ResourceType.stillImageFilmEmbed),
stillImageFilmEmbed: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
// src: value ?? '',
url: value ?? "",
srcAlt: srcAlt ?? void 0,
width: width ?? null,
height: height ?? null,
duration: duration ?? void 0,
mute: mute ?? void 0,
autoplay: autoplay ?? void 0,
allowSubtitles: allowSubtitles ?? void 0,
showSubtitles: showSubtitles ?? void 0,
alt: alt ?? "",
posterImage: posterImage ?? void 0,
thumbnails: thumbnails ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.stillImageFilmEmbed, {
ignoreEmptyArrays: ["caption"],
ignoreUndefined: ["width", "height"],
ignoreEmptyString: [
"url",
/*'alt',*/
"license",
"copyright"
],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build stillImageFilmLinkResource node
*
* @param data - data for the node
* @returns
*/
stillImageFilmLinkResource(context, data) {
const {
value,
srcAlt,
width,
height,
duration,
mute,
autoplay,
allowSubtitles,
showSubtitles,
alt,
posterImage,
thumbnails,
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const node = {
type: ResourceType.stillImageFilmLink,
__typeAlias: ResourceType.stillImageFilmLink,
__configKey: resourceTypeToConfigKey(ResourceType.stillImageFilmLink),
stillImageFilmLink: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
// src: value ?? '',
url: value ?? "",
srcAlt: srcAlt ?? void 0,
width: width ?? null,
height: height ?? null,
duration: duration ?? void 0,
mute: mute ?? void 0,
autoplay: autoplay ?? void 0,
allowSubtitles: allowSubtitles ?? void 0,
showSubtitles: showSubtitles ?? void 0,
alt: alt ?? "",
posterImage: posterImage ?? void 0,
thumbnails: thumbnails ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.stillImageFilmLink, {
ignoreEmptyArrays: ["caption"],
ignoreUndefined: ["width", "height"],
ignoreEmptyString: [
"url",
/*'alt',*/
"license",
"copyright"
],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build articleResource node
*
* @param data - data for the node
* @returns
*/
articleResource(context, data) {
const { value, srcAlt, license, copyright, showInIndex, caption, search, selected } = data;
const node = {
type: ResourceType.article,
__typeAlias: ResourceType.article,
__configKey: resourceTypeToConfigKey(ResourceType.article),
article: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
body: value ?? "",
srcAlt: srcAlt ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.article, {
ignoreEmptyArrays: ["caption"],
ignoreEmptyString: ["body", "alt", "license", "copyright"],
ignoreFalse: ["selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build documentResource node
*
* @param data - data for the node
* @returns
*/
documentResource(context, data) {
const { value, srcAlt, license, copyright, showInIndex, caption, search, selected } = data;
const node = {
type: ResourceType.document,
__typeAlias: ResourceType.document,
__configKey: resourceTypeToConfigKey(ResourceType.document),
document: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
url: value ?? "",
srcAlt: srcAlt ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.document, {
ignoreEmptyArrays: ["caption"],
ignoreEmptyString: ["url", "alt", "license", "copyright"],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build documentEmbedResource node
*
* @param data - data for the node
* @returns
*/
documentEmbedResource(context, data) {
const { value, srcAlt, license, copyright, showInIndex, caption, search, selected } = data;
const node = {
type: ResourceType.documentEmbed,
__typeAlias: ResourceType.documentEmbed,
__configKey: resourceTypeToConfigKey(ResourceType.documentEmbed),
documentEmbed: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
url: value ?? "",
srcAlt: srcAlt ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.documentEmbed, {
ignoreEmptyArrays: ["caption"],
ignoreEmptyString: ["url", "alt", "license", "copyright"],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build documentLinkResource node
*
* @param data - data for the node
* @returns
*/
documentLinkResource(context, data) {
const { value, srcAlt, license, copyright, showInIndex, caption, search, selected } = data;
const node = {
type: ResourceType.documentLink,
__typeAlias: ResourceType.documentLink,
__configKey: resourceTypeToConfigKey(ResourceType.documentLink),
documentLink: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
url: value ?? "",
srcAlt: srcAlt ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.documentLink, {
ignoreEmptyArrays: ["caption"],
ignoreEmptyString: ["url", "alt", "license", "copyright"],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build documentDownloadResource node
*
* @param data - data for the node
* @returns
*/
documentDownloadResource(context, data) {
const { value, srcAlt, license, copyright, showInIndex, caption, search, selected } = data;
const node = {
type: ResourceType.documentDownload,
__typeAlias: ResourceType.documentDownload,
__configKey: resourceTypeToConfigKey(ResourceType.documentDownload),
documentDownload: {
format: instance8.fileExtensionFromUrl(value) ?? void 0,
provider: instance8.domainFromUrl(value) ?? void 0,
url: value ?? "",
srcAlt: srcAlt ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.documentDownload, {
ignoreEmptyArrays: ["caption"],
ignoreEmptyString: ["url", "alt", "license", "copyright"],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build appLinkResource node
*
* @param data - data for the node
* @returns
*/
appLinkResource(context, data) {
const { value, srcAlt, license, copyright, showInIndex, caption, search, selected } = data;
const node = {
type: ResourceType.appLink,
__typeAlias: ResourceType.appLink,
__configKey: resourceTypeToConfigKey(ResourceType.appLink),
appLink: {
// format: (UrlUtils.fileExtensionFromUrl(value) ?? undefined) as string,
// provider: (UrlUtils.domainFromUrl(value) ?? undefined) as string,
provider: void 0,
url: value ?? "",
srcAlt: srcAlt ?? void 0,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.appLink, {
ignoreEmptyArrays: ["caption"],
ignoreEmptyString: [
"url",
/*'alt',*/
"license",
"copyright"
],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
/**
* Build websiteLinkResource node
*
* @param data - data for the node
* @returns
*/
websiteLinkResource(context, data) {
const {
value,
srcAlt,
/*siteName,*/
license,
copyright,
showInIndex,
caption,
search,
selected
} = data;
const node = {
type: ResourceType.websiteLink,
__typeAlias: ResourceType.websiteLink,
__configKey: resourceTypeToConfigKey(ResourceType.websiteLink),
websiteLink: {
// provider: (UrlUtils.domainFromUrl(value) ?? undefined) as string,
provider: void 0,
url: value ?? "",
srcAlt: srcAlt ?? void 0,
// siteName,
license: license ?? "",
copyright: copyright ?? "",
showInIndex: showInIndex ?? false,
caption: this.handleJsonText(context, TextLocation.tag, caption),
search: search ?? void 0,
selected: selected ?? false
}
};
objectUtils.removeUnwantedProperties(node.websiteLink, {
ignoreEmptyArrays: ["caption"],
ignoreEmptyString: ["url", "alt", "license", "copyright"],
ignoreFalse: ["showInIndex", "selected"]
});
return nodeValidator.validateResource(node);
}
//
//
//
//
// Private
//
};
// src/ast/Builder.ts
var Builder = class extends BaseBuilder {
constructor() {
super(...arguments);
__publicField(this, "resourceBuilder", new ResourceBuilder());
}
/**
* Build bitmark node
*
* @param data - data for the node
* @returns
*/
buildBitmark(data) {
const { bits, errors } = data;
const node = {
bits,
errors
};
objectUtils.removeUnwantedProperties(node);
return node;
}
/**
* Build bit node
*
* @param data - data for the node
* @returns
*/
buildBit(data, options) {
data = structuredClone(data);
const migratedBitType = instance2.getMigratedBitType(data.bitType);
if (migratedBitType) {
data.bitType = migratedBitType;
data.isCollapsible = data.isCollapsible ?? true;
}
const bitType = data.bitType;
const bitConfig = instance2.getBitConfig(bitType);
const isCollapsibleDefaultsTrue = bitConfig.tags[ConfigKey.property_isCollapsible]?.defaultValue === "true";
const deprecatedTextFormat = Enum7(DeprecatedTextFormat).fromValue(data.textFormat);
let textFormat = Enum7(TextFormat).fromValue(data.textFormat) ?? bitConfig.textFormatDefault;
if (deprecatedTextFormat === DeprecatedTextFormat.bitmarkMinusMinus) {
textFormat = TextFormat.bitmarkText;
}
const context = {
bitConfig,
bitType,
textFormat
};
const cardNode = this.buildCardNode(context, data);
const reasonableNumOfCharsProperty = instance2.isOfBitType(data.bitType, BitType.essay) ? this.toAstProperty(
bitType,
ConfigKey.property_reasonableNumOfChars,
data.reasonableNumOfChars,
options
) : void 0;
const convertedExample = {
...this.toExample(data.__isDefaultExample, data.example)
};
const node = {
bitType,
bitLevel: data.bitLevel,
textFormat,
resourceType: Enum7(ResourceType).fromValue(data.resourceType),
isCommented: data.isCommented,
// Properties
id: this.toAstProperty(bitType, ConfigKey.property_id, data.id, options),
gUri: this.toAstProperty(bitType, ConfigKey.property_gUri, data.gUri, options),
internalComment: this.toAstProperty(
bitType,
ConfigKey.property_internalComment,
data.internalComment,
options
),
customerId: this.toAstProperty(
bitType,
ConfigKey.property_customerId,
data.customerId,
options
),
customerExternalId: this.toAstProperty(
bitType,
ConfigKey.property_customerExternalId,
data.customerExternalId,
options
),
externalId: this.toAstProperty(
bitType,
ConfigKey.property_externalId,
data.externalId,
options
),
spaceId: this.toAstProperty(bitType, ConfigKey.property_spaceId, data.spaceId, options),
padletId: this.toAstProperty(bitType, ConfigKey.property_padletId, data.padletId, options),
jupyterId: this.toAstProperty(bitType, ConfigKey.property_jupyterId, data.jupyterId, options),
jupyterExecutionCount: this.toAstProperty(
bitType,
ConfigKey.property_jupyterExecutionCount,
data.jupyterExecutionCount,
options
),
copyright: this.toAstProperty(bitType, ConfigKey.property_copyright, data.copyright, options),
license: this.toAstProperty(bitType, ConfigKey.property_license, data.license, options),
rightsPolicy: this.toAstProperty(
bitType,
ConfigKey.property_rightsPolicy,
data.rightsPolicy,
options
),
sourceRL: this.toAstProperty(bitType, ConfigKey.property_sourceRL, data.sourceRL, options),
isPublic: this.toAstProperty(bitType, ConfigKey.property_isPublic, data.isPublic, options),
isTemplate: this.toAstProperty(
bitType,
ConfigKey.property_isTemplate,
data.isTemplate,
options
),
isTemplateStripTheme: this.toAstProperty(
bitType,
ConfigKey.property_isTemplateStripTheme,
data.isTemplateStripTheme,
options
),
isEditable: this.toAstProperty(
bitType,
ConfigKey.property_isEditable,
data.isEditable,
options
),
needsValidation: this.toAstProperty(
bitType,
ConfigKey.property_needsValidation,
data.needsValidation,
options
),
validationDate: this.toAstProperty(
bitType,
ConfigKey.property_validationDate,
data.validationDate,
options
),
aiGenerated: this.toAstProperty(
bitType,
ConfigKey.property_aiGenerated,
data.aiGenerated,
options
),
machineTranslated: this.toAstProperty(
bitType,
ConfigKey.property_machineTranslated,
data.machineTranslated,
options
),
translationOf: this.toAstProperty(
bitType,
ConfigKey.property_translationOf,
data.translationOf,
options
),
spansPageBreak: this.toAstProperty(
bitType,
ConfigKey.property_spansPageBreak,
data.spansPageBreak,
options
),
searchIndex: this.toAstProperty(
bitType,
ConfigKey.property_searchIndex,
data.searchIndex,
options
),
analyticsTag: this.toAstProperty(
bitType,
ConfigKey.property_analyticsTag,
data.analyticsTag,
options
),
categoryTag: this.toAstProperty(
bitType,
ConfigKey.property_categoryTag,
data.categoryTag,
options
),
topicTag: this.toAstProperty(bitType, ConfigKey.property_topicTag, data.topicTag, options),
reportTag: this.toAstProperty(bitType, ConfigKey.property_reportTag, data.reportTag, options),
altLangTag: this.toAstProperty(
bitType,
ConfigKey.property_altLangTag,
data.altLangTag,
options
),
feedbackEngine: this.toAstProperty(
bitType,
ConfigKey.property_feedbackEngine,
data.feedbackEngine,
options
),
feedbackType: this.toAstProperty(
bitType,
ConfigKey.property_feedbackType,
data.feedbackType,
options
),
disableFeedback: this.toAstProperty(
bitType,
ConfigKey.property_disableFeedback,
data.disableFeedback,
options
),
diffTo: this.toAstProperty(bitType, ConfigKey.property_diffTo, data.diffTo, options),
diffOp: this.toAstProperty(bitType, ConfigKey.property_diffOp, data.diffOp, options),
diffRef: this.toAstProperty(bitType, ConfigKey.property_diffRef, data.diffRef, options),
diffContext: this.toAstProperty(
bitType,
ConfigKey.property_diffContext,
data.diffContext,
options
),
diffTime: this.toAstProperty(bitType, ConfigKey.property_diffTime, data.diffTime, options),
path: this.toAstProperty(bitType, ConfigKey.property_path, data.path, options),
releaseVersion: this.toAstProperty(
bitType,
ConfigKey.property_releaseVersion,
data.releaseVersion,
options
),
releaseKind: this.toAstProperty(
bitType,
ConfigKey.property_releaseKind,
data.releaseKind,
options
),
releaseDate: this.toAstProperty(
bitType,
ConfigKey.property_releaseDate,
data.releaseDate,
options
),
iframeSrc: this.toAstProperty(bitType, ConfigKey.property_iframeSrc, data.iframeSrc, options),
iframeName: this.toAstProperty(
bitType,
ConfigKey.property_iframeName,
data.iframeName,
options
),
book: this.buildBooks(context, data.book),
ageRange: this.toAstProperty(bitType, ConfigKey.property_ageRange, data.ageRange, options),
lang: this.toAstProperty(bitType, ConfigKey.property_lang, data.lang, options),
language: this.toAstProperty(bitType, ConfigKey.property_language, data.language, options),
publisher: this.toAstProperty(bitType, ConfigKey.property_publisher, data.publisher, options),
publisherName: this.toAstProperty(
bitType,
ConfigKey.property_publisherName,
data.publisherName,
options
),
displayName: this.toAstProperty(
bitType,
ConfigKey.property_displayName,
data.displayName,
options
),
theme: this.toAstProperty(bitType, ConfigKey.property_theme, data.theme, options),
rtl: this.toAstProperty(bitType, ConfigKey.property_rtl, data.rtl, options),
computerLanguage: this.toAstProperty(
bitType,
ConfigKey.property_computerLanguage,
data.computerLanguage,
options
),
target: this.toAstProperty(bitType, ConfigKey.property_target, data.target, options),
slug: this.toAstProperty(bitType, ConfigKey.property_slug, data.slug, options),
tag: this.toAstProperty(bitType, ConfigKey.property_tag, data.tag, options),
groupTag: this.buildGroupTags(context, data.groupTag),
reviewTag: this.toAstProperty(bitType, ConfigKey.property_reviewTag, data.reviewTag, options),
reviewerTag: this.toAstProperty(
bitType,
ConfigKey.property_reviewerTag,
data.reviewerTag,
options
),
reductionTag: this.toAstProperty(
bitType,
ConfigKey.property_reductionTag,
data.reductionTag,
options
),
bubbleTag: this.toAstProperty(bitType, ConfigKey.property_bubbleTag, data.bubbleTag, options),
extractorTag: this.toAstProperty(
bitType,
ConfigKey.property_extractorTag,
data.extractorTag,
options
),
extractorInternal: this.toAstProperty(
bitType,
ConfigKey.property_extractorInternal,
data.extractorInternal,
options
),
sourceBB: this.normaliseSourceBB(data.sourceBB),
bitFingerprint: this.toAstProperty(
bitType,
ConfigKey.property_bitFingerprint,
data.bitFingerprint,
options
),
levelCEFRp: this.toAstProperty(
bitType,
ConfigKey.property_levelCEFRp,
data.levelCEFRp,
options
),
levelCEFR: this.toAstProperty(bitType, ConfigKey.property_levelCEFR, data.levelCEFR, options),
levelILR: this.toAstProperty(bitType, ConfigKey.property_levelILR, data.levelILR, options),
levelACTFL: this.toAstProperty(
bitType,
ConfigKey.property_levelACTFL,
data.levelACTFL,
options
),
icon: this.toAstProperty(bitType, ConfigKey.property_icon, data.icon, options),
iconTag: this.toAstProperty(bitType, ConfigKey.property_iconTag, data.iconTag, options),
colorTag: this.toAstProperty(bitType, ConfigKey.property_colorTag, data.colorTag, options),
flashcardSet: this.toAstProperty(
bitType,
ConfigKey.property_flashcardSet,
data.flashcardSet,
options
),
subtype: this.toAstProperty(bitType, ConfigKey.property_subtype, data.subtype, options),
bookAlias: this.toAstProperty(bitType, ConfigKey.property_bookAlias, data.bookAlias, options),
bookDiff: this.toAstProperty(bitType, ConfigKey.property_bookDiff, data.bookDiff, options),
coverImage: typeof data.coverImage === "string" || Array.isArray(data.coverImage) ? this.toAstProperty(
bitType,
ConfigKey.property_coverImage,
instance4.asArray(data.coverImage),
options
) : this.toImageResource(context, data.coverImage),
coverColor: this.toAstProperty(
bitType,
ConfigKey.property_coverColor,
data.coverColor,
options
),
backgroundImage: typeof data.backgroundImage === "string" || Array.isArray(data.backgroundImage) ? this.toAstProperty(
bitType,
ConfigKey.property_backgroundImage,
instance4.asArray(data.backgroundImage),
options
) : this.toImageResource(context, data.backgroundImage),
publications: this.toAstProperty(
bitType,
ConfigKey.property_publications,
data.publications,
options
),
relatedBook: this.toAstProperty(
bitType,
ConfigKey.property_relatedBook,
data.relatedBook,
options
),
translationOfBook: this.toAstProperty(
bitType,
ConfigKey.property_translationOfBook,
data.translationOfBook,
options
),
userFeedbackTranslationUrl: this.toAstProperty(
bitType,
ConfigKey.property_userFeedbackTranslationUrl,
data.userFeedbackTranslationUrl,
options
),
userFeedbackContentUrl: this.toAstProperty(
bitType,
ConfigKey.property_userFeedbackContentUrl,
data.userFeedbackContentUrl,
options
),
author: this.toAstProperty(bitType, ConfigKey.property_author, data.author, options),
subject: this.toAstProperty(bitType, ConfigKey.property_subject, data.subject, options),
date: this.toAstProperty(bitType, ConfigKey.property_date, data.date, options),
dateEnd: this.toAstProperty(bitType, ConfigKey.property_dateEnd, data.dateEnd, options),
location: this.toAstProperty(bitType, ConfigKey.property_location, data.location, options),
kind: this.toAstProperty(bitType, ConfigKey.property_kind, data.kind, options),
hasMarkAsDone: this.toAstProperty(
bitType,
ConfigKey.property_hasMarkAsDone,
data.hasMarkAsDone,
options
),
processHandIn: this.toAstProperty(
bitType,
ConfigKey.property_processHandIn,
data.processHandIn,
options
),
processHandInLocation: this.toAstProperty(
bitType,
ConfigKey.property_processHandInLocation,
data.processHandInLocation,
options
),
chatWithBook: this.toAstProperty(
bitType,
ConfigKey.property_chatWithBook,
data.chatWithBook,
options
),
chatWithBookBrainKey: this.toAstProperty(
bitType,
ConfigKey.property_chatWithBookBrainKey,
data.chatWithBookBrainKey,
options
),
currency: this.toAstProperty(bitType, ConfigKey.property_currency, data.currency, options),
bookIsbn: this.toAstProperty(bitType, ConfigKey.property_bookIsbn, data.bookIsbn, options),
bookAuthor: this.toAstProperty(
bitType,
ConfigKey.property_bookAuthor,
data.bookAuthor,
options
),
bookType: this.toAstProperty(bitType, ConfigKey.property_bookType, data.bookType, options),
bookBindingType: this.toAstProperty(
bitType,
ConfigKey.property_bookBindingType,
data.bookBindingType,
options
),
bookNumberOfPages: this.toAstProperty(
bitType,
ConfigKey.property_bookNumberOfPages,
data.bookNumberOfPages,
options
),
bookRating: this.toAstProperty(
bitType,
ConfigKey.property_bookRating,
data.bookRating,
options
),
bookSeriesTitle: this.toAstProperty(
bitType,
ConfigKey.property_bookSeriesTitle,
data.bookSeriesTitle,
options
),
bookSeriesId: this.toAstProperty(
bitType,
ConfigKey.property_bookSeriesId,
data.bookSeriesId,
options
),
bookSeriesNumber: this.toAstProperty(
bitType,
ConfigKey.property_bookSeriesNumber,
data.bookSeriesNumber,
options
),
bookLanguage: this.toAstProperty(
bitType,
ConfigKey.property_bookLanguage,
data.bookLanguage,
options
),
bookPublishingDate: this.toAstProperty(
bitType,
ConfigKey.property_bookPublishingDate,
data.bookPublishingDate,
options
),
bookPublisher: this.toAstProperty(
bitType,
ConfigKey.property_bookPublisher,
data.bookPublisher,
options
),
bookTranslator: this.toAstProperty(
bitType,
ConfigKey.property_bookTranslator,
data.bookTranslator,
options
),
bookOriginalTitle: this.toAstProperty(
bitType,
ConfigKey.property_bookOriginalTitle,
data.bookOriginalTitle,
options
),
gmbExternalShop: this.toAstProperty(
bitType,
ConfigKey.property_gmbExternalShop,
data.gmbExternalShop,
options
),
gmbExternalShopItems: this.toAstProperty(
bitType,
ConfigKey.property_gmbExternalShopItems,
data.gmbExternalShopItems,
options
),
action: this.toAstProperty(bitType, ConfigKey.property_action, data.action, options),
showInIndex: this.toAstProperty(
bitType,
ConfigKey.property_showInIndex,
data.showInIndex,
options
),
refAuthor: this.toAstProperty(bitType, ConfigKey.property_refAuthor, data.refAuthor, options),
refBookTitle: this.toAstProperty(
bitType,
ConfigKey.property_refBookTitle,
data.refBookTitle,
options
),
refPublisher: this.toAstProperty(
bitType,
ConfigKey.property_refPublisher,
data.refPublisher,
options
),
refPublicationYear: this.toAstProperty(
bitType,
ConfigKey.property_refPublicationYear,
data.refPublicationYear,
options
),
citationStyle: this.toAstProperty(
bitType,
ConfigKey.property_citationStyle,
data.citationStyle,
options
),
blockId: this.toAstProperty(bitType, ConfigKey.property_blockId, data.blockId, options),
pageNo: this.toAstProperty(bitType, ConfigKey.property_pageNo, data.pageNo, options),
x: this.toAstProperty(bitType, ConfigKey.property_x, data.x, options),
y: this.toAstProperty(bitType, ConfigKey.property_y, data.y, options),
width: this.toAstProperty(bitType, ConfigKey.property_width, data.width, options),
height: this.toAstProperty(bitType, ConfigKey.property_height, data.height, options),
index: this.toAstProperty(bitType, ConfigKey.property_index, data.index, options),
classification: this.toAstProperty(
bitType,
ConfigKey.property_classification,
data.classification,
options
),
availableClassifications: this.toAstProperty(
bitType,
ConfigKey.property_availableClassifications,
data.availableClassifications,
options
),
allowedBit: this.toAstProperty(
bitType,
ConfigKey.property_allowedBit,
data.allowedBit,
options
),
authorFullName: this.toAstProperty(
bitType,
ConfigKey.property_authorFullName,
data.authorFullName,
options
),
authorPseudonym: this.toAstProperty(
bitType,
ConfigKey.property_authorPseudonym,
data.authorPseudonym,
options
),
authorTitle: this.toAstProperty(
bitType,
ConfigKey.property_authorTitle,
data.authorTitle,
options
),
authorJobTitle: this.toAstProperty(
bitType,
ConfigKey.property_authorJobTitle,
data.authorJobTitle,
options
),
tableFixedHeader: this.toAstProperty(
bitType,
ConfigKey.property_tableFixedHeader,
data.tableFixedHeader,
options
),
tableHeaderWhitespaceNoWrap: this.toAstProperty(
bitType,
ConfigKey.property_tableHeaderWhitespaceNoWrap,
data.tableHeaderWhitespaceNoWrap,
options
),
tableSearch: this.toAstProperty(
bitType,
ConfigKey.property_tableSearch,
data.tableSearch,
options
),
tableSort: this.toAstProperty(bitType, ConfigKey.property_tableSort, data.tableSort, options),
tablePagination: this.toAstProperty(
bitType,
ConfigKey.property_tablePagination,
data.tablePagination,
options
),
tablePaginationLimit: this.toAstProperty(
bitType,
ConfigKey.property_tablePaginationLimit,
data.tablePaginationLimit,
options
),
tableHeight: this.toAstProperty(
bitType,
ConfigKey.property_tableHeight,
data.tableHeight,
options
),
tableWhitespaceNoWrap: this.toAstProperty(
bitType,
ConfigKey.property_tableWhitespaceNoWrap,
data.tableWhitespaceNoWrap,
options
),
tableAutoWidth: this.toAstProperty(
bitType,
ConfigKey.property_tableAutoWidth,
data.tableAutoWidth,
options
),
tableResizableColumns: this.toAstProperty(
bitType,
ConfigKey.property_tableResizableColumns,
data.tableResizableColumns,
options
),
tableColumnMinWidth: this.toAstProperty(
bitType,
ConfigKey.property_tableColumnMinWidth,
data.tableColumnMinWidth,
options
),
quizCountItems: this.toAstProperty(
bitType,
ConfigKey.property_quizCountItems,
data.quizCountItems,
options
),
quizStrikethroughSolutions: this.toAstProperty(
bitType,
ConfigKey.property_quizStrikethroughSolutions,
data.quizStrikethroughSolutions,
options
),
codeLineNumbers: this.toAstProperty(
bitType,
ConfigKey.property_codeLineNumbers,
data.codeLineNumbers,
options
),
codeMinimap: this.toAstProperty(
bitType,
ConfigKey.property_codeMinimap,
data.codeMinimap,
options
),
stripePricingTableId: this.toAstProperty(
bitType,
ConfigKey.property_stripePricingTableId,
data.stripePricingTableId,
options
),
stripePricingTableDescriptiveName: this.toAstProperty(
bitType,
ConfigKey.property_stripePricingTableDescriptiveName,
data.stripePricingTableDescriptiveName,
options
),
stripePublishableKey: this.toAstProperty(
bitType,
ConfigKey.property_stripePublishableKey,
data.stripePublishableKey,
options
),
thumbImage: this.toAstProperty(
bitType,
ConfigKey.property_thumbImage,
data.thumbImage,
options
),
scormSource: this.toAstProperty(
bitType,
ConfigKey.property_scormSource,
data.scormSource,
options
),
posterImage: this.toAstProperty(
bitType,
ConfigKey.property_posterImage,
data.posterImage,
options
),
focusX: this.toAstProperty(bitType, ConfigKey.property_focusX, data.focusX, options),
focusY: this.toAstProperty(bitType, ConfigKey.property_focusY, data.focusY, options),
pointerLeft: this.toAstProperty(
bitType,
ConfigKey.property_pointerLeft,
data.pointerLeft,
options
),
pointerTop: this.toAstProperty(
bitType,
ConfigKey.property_pointerTop,
data.pointerTop,
options
),
listItemIndent: this.toAstProperty(
bitType,
ConfigKey.property_listItemIndent,
data.listItemIndent,
options
),
backgroundWallpaper: this.toImageResource(context, data.backgroundWallpaper),
hasBookNavigation: this.toAstProperty(
bitType,
ConfigKey.property_hasBookNavigation,
data.hasBookNavigation,
options
),
duration: this.toAstProperty(bitType, ConfigKey.property_duration, data.duration, options),
deeplink: this.toAstProperty(bitType, ConfigKey.property_deeplink, data.deeplink, options),
externalLink: this.toAstProperty(
bitType,
ConfigKey.property_externalLink,
data.externalLink,
options
),
externalLinkText: this.toAstProperty(
bitType,
ConfigKey.property_externalLinkText,
data.externalLinkText,
options
),
videoCallLink: this.toAstProperty(
bitType,
ConfigKey.property_videoCallLink,
data.videoCallLink,
options
),
vendorDashboardId: this.toAstProperty(
bitType,
ConfigKey.property_vendorDashboardId,
data.vendorDashboardId,
options
),
vendorSurveyId: this.toAstProperty(
bitType,
ConfigKey.property_vendorSurveyId,
data.vendorSurveyId,
options
),
vendorUrl: this.toAstProperty(bitType, ConfigKey.property_vendorUrl, data.vendorUrl, options),
search: this.toAstProperty(bitType, ConfigKey.property_search, data.search, options),
list: this.toAstProperty(bitType, ConfigKey.property_list, data.list, options),
layer: this.toAstProperty(bitType, ConfigKey.property_layer, data.layer, options),
layerRole: this.toAstProperty(bitType, ConfigKey.property_layerRole, data.layerRole, options),
textReference: this.toAstProperty(
bitType,
ConfigKey.property_textReference,
data.textReference,
options
),
isTracked: this.toAstProperty(bitType, ConfigKey.property_isTracked, data.isTracked, options),
isInfoOnly: this.toAstProperty(
bitType,
ConfigKey.property_isInfoOnly,
data.isInfoOnly,
options
),
imageFirst: this.toAstProperty(
bitType,
ConfigKey.property_imageFirst,
data.imageFirst,
options
),
activityType: this.toAstProperty(
bitType,
ConfigKey.property_activityType,
data.activityType,
options
),
labelTrue: this.toAstProperty(bitType, ConfigKey.property_labelTrue, data.labelTrue, options),
labelFalse: this.toAstProperty(
bitType,
ConfigKey.property_labelFalse,
data.labelFalse,
options
),
content2Buy: this.toAstProperty(
bitType,
ConfigKey.property_content2Buy,
data.content2Buy,
options
),
listPrice: this.toAstProperty(bitType, ConfigKey.property_listPrice, data.listPrice, options),
customerProductId: this.toAstProperty(
bitType,
ConfigKey.property_customerProductId,
data.customerProductId,
options
),
customerProductShopLink: this.toAstProperty(
bitType,
ConfigKey.property_customerProductShopLink,
data.customerProductShopLink,
options
),
mailingList: this.toAstProperty(
bitType,
ConfigKey.property_mailingList,
data.mailingList,
options
),
buttonCaption: this.toAstProperty(
bitType,
ConfigKey.property_buttonCaption,
data.buttonCaption,
options
),
callToActionUrl: this.toAstProperty(
bitType,
ConfigKey.property_callToActionUrl,
data.callToActionUrl,
options
),
advertisingClickUrl: this.toAstProperty(
bitType,
ConfigKey.property_advertisingClickUrl,
data.advertisingClickUrl,
options
),
brandColor: this.toAstProperty(
bitType,
ConfigKey.property_brandColor,
data.brandColor,
options
),
brandColorName: this.toAstProperty(
bitType,
ConfigKey.property_brandColorName,
data.brandColorName,
options
),
caption: this.handleJsonText(context, TextLocation.tag, data.caption),
quotedPerson: this.toAstProperty(
bitType,
ConfigKey.property_quotedPerson,
data.quotedPerson,
options
),
reasonableNumOfChars: reasonableNumOfCharsProperty,
resolved: this.toAstProperty(bitType, ConfigKey.property_resolved, data.resolved, options),
resolvedDate: this.toAstProperty(
bitType,
ConfigKey.property_resolvedDate,
data.resolvedDate,
options
),
resolvedBy: this.toAstProperty(
bitType,
ConfigKey.property_resolvedBy,
data.resolvedBy,
options
),
handInAcceptFileType: this.toAstProperty(
bitType,
ConfigKey.property_handInAcceptFileType,
data.handInAcceptFileType,
options
),
handInRequirement: this.toAstProperty(
bitType,
ConfigKey.property_handInRequirement,
data.handInRequirement,
options
),
handInInstruction: this.toAstProperty(
bitType,
ConfigKey.property_handInInstruction,
data.handInInstruction,
options
),
maxCreatedBits: this.toAstProperty(
bitType,
ConfigKey.property_maxCreatedBits,
data.maxCreatedBits,
options
),
maxDisplayLevel: this.toAstProperty(
bitType,
ConfigKey.property_maxDisplayLevel,
data.maxDisplayLevel,
options
),
maxTocChapterLevel: this.toAstProperty(
bitType,
ConfigKey.property_maxTocChapterLevel,
data.maxTocChapterLevel,
options
),
sourceDocument: this.toAstProperty(
bitType,
ConfigKey.property_sourceDocument,
data.sourceDocument,
options
),
internalPrintPdf: this.toAstProperty(
bitType,
ConfigKey.property_internalPrintPdf,
data.internalPrintPdf,
options
),
hasPrintRestriction: this.toAstProperty(
bitType,
ConfigKey.property_hasPrintRestriction,
data.hasPrintRestriction,
options
),
enforceUpdateOverUserInput: this.toAstProperty(
bitType,
ConfigKey.property_enforceUpdateOverUserInput,
data.enforceUpdateOverUserInput,
options
),
extractorExtractionTimestamp: this.toAstProperty(
bitType,
ConfigKey.property_extractorExtractionTimestamp,
data.extractorExtractionTimestamp,
options
),
tocResource: this.toAstProperty(
bitType,
ConfigKey.property_tocResource,
data.tocResource,
options
),
tocContent: this.toAstProperty(
bitType,
ConfigKey.property_tocContent,
data.tocContent,
options
),
page: this.toAstProperty(bitType, ConfigKey.property_page, data.page, options),
allowPrint: this.toAstProperty(
bitType,
ConfigKey.property_allowPrint,
data.allowPrint,
options
),
printParentChapterLevel: this.toAstProperty(
bitType,
ConfigKey.property_printParentChapterLevel,
data.printParentChapterLevel,
options
),
printOnRequestCustomerId: this.toAstProperty(
bitType,
ConfigKey.property_printOnRequestCustomerId,
data.printOnRequestCustomerId,
options
),
printOnRequestProductId: this.toAstProperty(
bitType,
ConfigKey.property_printOnRequestProductId,
data.printOnRequestProductId,
options
),
platformBrandTarget: this.toAstProperty(
bitType,
ConfigKey.property_platformBrandTarget,
data.platformBrandTarget,
options
),
platformName: this.toAstProperty(
bitType,
ConfigKey.property_platformName,
data.platformName,
options
),
platformIcon: this.toImageResource(context, data.platformIcon),
platformLogo: this.toImageResource(context, data.platformLogo),
platformLogoMaxHeight: this.toAstProperty(
bitType,
ConfigKey.property_platformLogoMaxHeight,
data.platformLogoMaxHeight,
options
),
platformPrimaryColor: this.toAstProperty(
bitType,
ConfigKey.property_platformPrimaryColor,
data.platformPrimaryColor,
options
),
platformSecondaryColor: this.toAstProperty(
bitType,
ConfigKey.property_platformSecondaryColor,
data.platformSecondaryColor,
options
),
platformBackgroundColor: this.toAstProperty(
bitType,
ConfigKey.property_platformBackgroundColor,
data.platformBackgroundColor,
options
),
platformScrollbarColor: this.toAstProperty(
bitType,
ConfigKey.property_platformScrollbarColor,
data.platformScrollbarColor,
options
),
platformSelectionColor: this.toAstProperty(
bitType,
ConfigKey.property_platformSelectionColor,
data.platformSelectionColor,
options
),
platformSeparatorColor: this.toAstProperty(
bitType,
ConfigKey.property_platformSeparatorColor,
data.platformSeparatorColor,
options
),
platformPlaceholderColor: this.toAstProperty(
bitType,
ConfigKey.property_platformPlaceholderColor,
data.platformPlaceholderColor,
options
),
platformTextSelectionColor: this.toAstProperty(
bitType,
ConfigKey.property_platformTextSelectionColor,
data.platformTextSelectionColor,
options
),
platformButtonPrimaryColor: this.toAstProperty(
bitType,
ConfigKey.property_platformButtonPrimaryColor,
data.platformButtonPrimaryColor,
options
),
platformButtonBackgroundColor: this.toAstProperty(
bitType,
ConfigKey.property_platformButtonBackgroundColor,
data.platformButtonBackgroundColor,
options
),
platformMessageBackgroundColor: this.toAstProperty(
bitType,
ConfigKey.property_platformMessageBackgroundColor,
data.platformMessageBackgroundColor,
options
),
platformBackgroundImage: this.toImageResource(context, data.platformBackgroundImage),
platformBorderColor: this.toAstProperty(
bitType,
ConfigKey.property_platformBorderColor,
data.platformBorderColor,
options
),
platformSelectionTextColor: this.toAstProperty(
bitType,
ConfigKey.property_platformSelectionTextColor,
data.platformSelectionTextColor,
options
),
platformBaseLayerColor: this.toAstProperty(
bitType,
ConfigKey.property_platformBaseLayerColor,
data.platformBaseLayerColor,
options
),
platformMargin: this.toAstProperty(
bitType,
ConfigKey.property_platformMargin,
data.platformMargin,
options
),
platformBorderRadius: this.toAstProperty(
bitType,
ConfigKey.property_platformBorderRadius,
data.platformBorderRadius,
options
),
platformSelectionBorderRadius: this.toAstProperty(
bitType,
ConfigKey.property_platformSelectionBorderRadius,
data.platformSelectionBorderRadius,
options
),
platformNeedsShadow: this.toAstProperty(
bitType,
ConfigKey.property_platformNeedsShadow,
data.platformNeedsShadow,
options
),
productId: this.toAstProperty(bitType, ConfigKey.property_productId, data.productId, options),
product: this.toAstProperty(bitType, ConfigKey.property_product, data.product, options),
// productList: this.toAstProperty(
// bitType,
// ConfigKey.property_productList,
// data.productList,
// options,
// ),
productVideo: this.toAstProperty(
bitType,
ConfigKey.property_productVideo,
data.productVideo,
options
),
// productVideoList: this.toAstProperty(
// bitType,
// ConfigKey.property_productVideoList,
// data.productVideoList,
// options,
// ),
productFolder: this.toAstProperty(
bitType,
ConfigKey.property_productFolder,
data.productFolder,
options
),
technicalTerm: this.buildTechnicalTerm(context, data.technicalTerm),
servings: this.buildServings(context, data.servings),
ratingLevelStart: this.buildRatingLevelStartEnd(context, data.ratingLevelStart),
ratingLevelEnd: this.buildRatingLevelStartEnd(context, data.ratingLevelEnd),
ratingLevelSelected: this.toAstProperty(
bitType,
ConfigKey.property_ratingLevelSelected,
data.ratingLevelSelected,
options
),
formula: this.toAstProperty(bitType, ConfigKey.property_formula, data.formula, options),
markConfig: this.buildMarkConfigs(context, data.markConfig),
imageSource: this.buildImageSource(context, data.imageSource),
person: this.buildPerson(context, data.person),
bot: this.toAstProperty(bitType, ConfigKey.property_bot, data.bot, options),
referenceProperty: this.toAstProperty(
bitType,
ConfigKey.property_reference,
data.referenceProperty,
options
),
// Author data
fullName: this.toAstProperty(bitType, ConfigKey.property_fullName, data.fullName, options),
pseudonym: this.toAstProperty(bitType, ConfigKey.property_pseudonym, data.pseudonym, options),
titleString: this.toAstProperty(bitType, ConfigKey.property_title, data.titleString, options),
jobTitle: this.toAstProperty(bitType, ConfigKey.property_jobTitle, data.jobTitle, options),
// Book data
title: this.handleJsonText(context, TextLocation.tag, data.title),
subtitle: this.handleJsonText(context, TextLocation.tag, data.subtitle),
level: instance6.asNumber(data.level),
toc: this.toAstProperty(bitType, ConfigKey.property_toc, data.toc, options),
progress: this.toAstProperty(bitType, ConfigKey.property_progress, data.progress, options),
isCollapsible: this.toAstProperty(
bitType,
ConfigKey.property_isCollapsible,
data.isCollapsible ?? (isCollapsibleDefaultsTrue ? true : void 0),
options
),
anchor: data.anchor,
reference: data.reference,
referenceEnd: data.referenceEnd,
revealSolutions: this.toAstProperty(
bitType,
ConfigKey.property_revealSolutions,
data.revealSolutions,
options
),
// Item, Lead, Hint, Instruction
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
pageNumber: this.handleJsonText(context, TextLocation.tag, data.pageNumber),
marginNumber: this.handleJsonText(context, TextLocation.tag, data.marginNumber),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
partialAnswer: this.toAstProperty(
bitType,
ConfigKey.property_partialAnswer,
data.partialAnswer,
options
),
sampleSolution: this.toAstProperty(
bitType,
ConfigKey.property_sampleSolution,
data.sampleSolution,
options
),
additionalSolutions: this.toAstProperty(
bitType,
ConfigKey.property_additionalSolutions,
data.additionalSolutions,
options
),
// Example
...this.toExample(data.__isDefaultExample, data.example),
// Person
previewImage: this.toImageResources(context, data.previewImage),
previewVideo: this.toVideoResources(context, data.previewVideo),
imagePlaceholder: this.toImageResource(context, data.imagePlaceholder),
resources: instance4.asArray(
this.resourceBuilder.resourceFromResourceJson(context, data.resources)
),
// Body, Card, Footer, must be after all other properties except the extraProperties and markup / parser
body: this.buildBody(context, data.body),
cardNode,
footer: this.buildFooter(context, data.footer),
// Must be after other properties/tags in the AST so key clashes are avoided correctly
extraProperties: this.parseExtraProperties(data.extraProperties),
markup: data.markup,
parser: data.parser,
// Private properties
__isDefaultExample: data.__isDefaultExample ?? false
};
if (instance2.isOfBitType(node.bitType, BitType.interview)) {
this.pushDownTree(
context,
void 0,
void 0,
cardNode,
"questions",
"reasonableNumOfChars",
data.reasonableNumOfChars
);
}
if (instance2.isOfBitType(node.bitType, BitType.feedback)) {
this.pushDownTree(
context,
void 0,
void 0,
cardNode,
["feedbacks", "reason"],
"reasonableNumOfChars",
data.reasonableNumOfChars
);
}
this.pushDownTree(
context,
[node.body, ...cardNode?.cardBits?.map((cardBit) => cardBit.body) || []],
[BodyBitType.gap],
void 0,
void 0,
//'isCaseSensitive',
"isCaseSensitive",
data.isCaseSensitive ?? true
);
this.pushDownTree(
context,
void 0,
void 0,
cardNode,
"pairs",
"isCaseSensitive",
data.isCaseSensitive ?? true
);
this.pushDownTree(
context,
void 0,
void 0,
cardNode,
["matrix", "cells"],
"isCaseSensitive",
data.isCaseSensitive ?? true
);
this.pushExampleDownTree(
context,
node.body,
cardNode,
node.__isDefaultExample,
convertedExample.example
);
this.setDefaultBitValues(node);
this.setIsExampleFlags(node);
this.addVersionToParserInfo(node);
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreUndefined: ["example"],
ignoreEmptyArrays: ["item", "lead", "pageNumber", "marginNumber", "hint", "instruction"]
});
return nodeValidator.validateBit(node);
}
/**
* Build books[] node
*
* @param data - data for the node
* @returns
*/
buildBooks(context, data) {
if (stringUtils.isString(data)) return data;
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildBook(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build choice node
*
* @param data - data for the node
* @returns
*/
buildBook(_context, data) {
if (!data) return void 0;
const node = {
book: data.book ?? "",
reference: data.reference ?? "",
referenceEnd: data.referenceEnd ?? void 0
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyString: ["book", "reference"]
});
return node;
}
/**
* Build groupTag[] node
*
* @param data - data for the node
* @returns
*/
buildGroupTags(context, data) {
if (!data) return void 0;
if (!Array.isArray(data)) data = [data];
let nodes = data.map((d) => this.buildGroupTag(context, d)).filter((d) => d != null);
const combinedNodes = {};
for (const node of nodes) {
if (!node) continue;
if (!combinedNodes[node.name]) {
combinedNodes[node.name] = node;
} else {
combinedNodes[node.name].tags = instance4.removeDuplicates([
...combinedNodes[node.name].tags,
...node.tags
]);
}
}
nodes = Object.values(combinedNodes);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Normalise a sourceBB input into the AST shape (always number[][]).
* Accepts a single 4-number tuple or an array of tuples; returns undefined when empty.
*/
normaliseSourceBB(data) {
if (data == null) return void 0;
if (!Array.isArray(data) || data.length === 0) return void 0;
const isNested = data.some(Array.isArray);
const tuples = (isNested ? data : [data]).filter(
(t) => Array.isArray(t) && t.length === 4 && t.every((n) => typeof n === "number")
);
return tuples.length > 0 ? tuples : void 0;
}
/**
* Build groupTag node
*
* @param data - data for the node
* @returns
*/
buildGroupTag(_context, data) {
if (!data) return void 0;
const tags2 = Array.isArray(data.tags) ? data.tags : [];
const node = {
name: data.name ?? "",
tags: tags2.map((tag) => tag?.trim() ?? "") ?? []
};
return node;
}
/**
* Build feedbackChoices[] node
*
* @param data - data for the node
* @returns
*/
buildFeedbackChoices(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildFeedbackChoice(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build feedbackChoice node
*
* @param data - data for the node
* @returns
*/
buildFeedbackChoice(context, data) {
if (!data) return void 0;
const node = {
choice: data.choice ?? "",
requireReason: !!data.requireReason,
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, true)
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["item", "hint", "instruction"],
ignoreUndefined: ["example"]
});
return node;
}
/**
* Build feedbackReason node
*
* @param data - data for the node
* @returns
*/
buildFeedbackReason(context, data) {
if (!data) return void 0;
const defaultExample = this.handleJsonText(context, TextLocation.tag, "true");
const node = {
text: data.text ?? "",
reasonableNumOfChars: data.reasonableNumOfChars ?? void 0,
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, defaultExample),
__textAst: data.__textAst
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["item", "hint", "instruction"],
ignoreUndefined: ["example", "reasonableNumOfChars"]
});
return node;
}
/**
* Build choice[] node
*
* @param data - data for the node
* @returns
*/
buildChoices(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildChoice(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build choice node
*
* @param data - data for the node
* @returns
*/
buildChoice(context, data) {
if (!data) return void 0;
const node = {
choice: data.choice ?? "",
isCorrect: !!data.isCorrect,
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, !!data.isCorrect)
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["item", "hint", "instruction"],
ignoreUndefined: ["example"]
});
return node;
}
/**
* Build response[] node
*
* @param data - data for the node
* @returns
*/
buildResponses(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildResponse(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build response node
*
* @param data - data for the node
* @returns
*/
buildResponse(context, data) {
if (!data) return void 0;
const node = {
response: data.response ?? "",
isCorrect: !!data.isCorrect,
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, !!data.isCorrect)
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["item", "hint", "instruction"],
ignoreUndefined: ["example"]
});
return node;
}
/**
* Build bot response[] node
*
* @param data - data for the node
* @returns
*/
buildBotResponses(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.botResponse(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build bot response node
*
* @param data - data for the node
* @returns
*/
botResponse(context, data) {
if (!data) return void 0;
const node = {
response: data.response ?? "",
reaction: data.reaction ?? "",
feedback: data.feedback ?? "",
item: data.item ?? "",
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint)
};
objectUtils.removeUnwantedProperties(node, {
ignoreEmptyString: ["response", "reaction", "feedback", "item"],
ignoreAllFalse: true,
ignoreEmptyArrays: [],
ignoreUndefined: ["example"]
});
return node;
}
/**
* Build feedbacks[] node
*
* @param data - data for the node
* @returns
*/
buildFeedbacks(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildFeedback(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build feedback node
*
* @param data - data for the node
* @returns
*/
buildFeedback(context, data) {
if (!data) return void 0;
let choices;
let reason;
if (data.choices) {
choices = this.buildFeedbackChoices(context, data.choices);
} else {
return void 0;
}
if (data.reason) {
reason = this.buildFeedbackReason(context, data.reason);
}
const node = {
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
// isExample: !!data.__defaultExample,
choices,
reason
// Might be undefined
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["item", "hint", "instruction"]
});
return node;
}
/**
* Build quizzes[] node
*
* @param data - data for the node
* @returns
*/
buildQuizzes(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildQuiz(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build quiz node
*
* @param data - data for the node
* @returns
*/
buildQuiz(context, data) {
if (!data) return void 0;
const convertedExample = {
...this.toExample(data.__isDefaultExample, data.__defaultExample)
};
let choices;
let responses;
if (data.choices) {
choices = this.buildChoices(context, data.choices);
this.pushExampleDownTreeBoolean(
context,
data.__isDefaultExample,
convertedExample.example,
true,
choices
);
} else if (data.responses) {
responses = this.buildResponses(context, data.responses);
this.pushExampleDownTreeBoolean(
context,
data.__isDefaultExample,
convertedExample.example,
false,
responses
);
} else {
return void 0;
}
const node = {
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
isExample: !!data.__defaultExample,
choices,
responses
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["item", "hint", "instruction"]
});
return node;
}
/**
* Build heading node
*
* @param data - data for the node
* @returns
*/
buildHeading(_context, data) {
if (!data) return void 0;
if (data.forKeys == null) return void 0;
const node = {
forKeys: data.forKeys ?? "",
forValues: data.forValues ?? data.__forValuesDefault ?? ""
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyString: ["forKeys", "forValues"],
ignoreEmptyArrays: ["forValues"]
});
return node;
}
/**
* Build pair[] node
*
* @param data - data for the node
* @returns
*/
buildPairs(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildPair(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build pair node
*
* @param data - data for the node
* @returns
*/
buildPair(context, data) {
if (!data) return void 0;
const defaultExample = Array.isArray(data.__valuesAst) && data.__valuesAst.length > 0 ? data.__valuesAst[0] : null;
const keyAudio = this.resourceBuilder.resourceFromResourceJson(
context,
data.keyAudio
);
const keyImage = this.resourceBuilder.resourceFromResourceJson(
context,
data.keyImage
);
const node = {
key: data.key ?? "",
keyAudio,
keyImage,
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
isCaseSensitive: data.isCaseSensitive,
...this.toExample(data.__isDefaultExample, data.example, defaultExample),
values: data.values || [],
__valuesAst: data.__valuesAst
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["item", "hint", "instruction", "values"],
ignoreUndefined: ["example", "isCaseSensitive"]
});
return node;
}
/**
* Build matrix[] node
*
* @param data - data for the node
* @returns
*/
buildMatricies(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildMatrix(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build matrix node
*
* @param data - data for the node
* @returns
*/
buildMatrix(context, data) {
if (!data) return void 0;
let isExample = false;
for (const c of data.cells || []) {
if (!c) continue;
if (data.__isDefaultExample && !c.isExample) {
c.isExample = true;
}
isExample = c.isExample ? true : isExample;
}
const node = {
key: data.key ?? "",
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
isExample,
cells: (data.cells || []).map((c) => this.buildMatrixCell(context, c)).filter((c) => c != null)
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["hint", "item", "cells"],
ignoreUndefined: ["isCaseSensitive"]
});
return node;
}
/**
* Build matrixCell node
*
* @param data - data for the node
* @returns
*/
buildMatrixCell(context, data) {
if (!data) return void 0;
const defaultExample = Array.isArray(data.__valuesAst) && data.__valuesAst.length > 0 ? data.__valuesAst[0] : null;
const node = {
values: data.values || [],
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
isCaseSensitive: data.isCaseSensitive,
...this.toExample(data.__isDefaultExample, data.example, defaultExample),
__valuesAst: data.__valuesAst
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["instruction", "item", "values"],
ignoreUndefined: ["example", "isCaseSensitive"]
});
return node;
}
/**
* Build pronunciation table node
*
* @param data - data for the node
* @returns
*/
buildPronunciationTable(context, dataIn) {
if (!dataIn) return void 0;
const node = {
data: (dataIn.data || []).map(
(row) => (row || []).map((cell) => {
const audio = this.resourceBuilder.resourceFromResourceJson(
context,
cell.audio
);
return {
title: this.handleJsonText(context, TextLocation.tag, cell.title),
body: this.handleJsonText(context, TextLocation.tag, cell.body),
audio
};
})
)
};
return node;
}
/**
* Build table / table extended node
*
* @param data - data for the node
* @returns
*/
buildTable(context, dataIn) {
if (!dataIn) return void 0;
const node = normalizeTableFormat(context.bitType, dataIn);
const nodeTable = node;
const nodeTableExtended = node;
if (Array.isArray(nodeTable.columns) && nodeTable.columns.length > 0) {
nodeTable.columns = nodeTable.columns.map(
(col) => this.handleJsonText(context, TextLocation.tag, col)
);
}
if (Array.isArray(nodeTable.data) && nodeTable.data.length > 0) {
nodeTable.data = nodeTable.data.map(
(row) => (row || []).map((cell) => this.handleJsonText(context, TextLocation.tag, cell))
);
}
if (Array.isArray(nodeTable.columnWidths)) {
const columnCount = Array.isArray(nodeTable.columns) ? nodeTable.columns.length : void 0;
const source = columnCount != null ? nodeTable.columnWidths.slice(0, columnCount) : nodeTable.columnWidths;
const normalized = source.map((w) => typeof w === "number" && w > 0 ? w : null);
const hasAny = normalized.some((w) => w != null);
if (hasAny) {
nodeTable.columnWidths = normalized;
} else {
delete nodeTable.columnWidths;
}
}
const buildSection = (section) => {
if (!section || !Array.isArray(section.rows)) return void 0;
const rows = section.rows.map((row) => {
const cells = (row.cells || []).map((cell) => {
const tableCell = {
content: this.handleJsonText(context, TextLocation.tag, cell.content)
};
if (cell.title != null) tableCell.title = cell.title;
if (cell.rowspan != null) tableCell.rowspan = cell.rowspan;
if (cell.colspan != null) tableCell.colspan = cell.colspan;
if (cell.scope) tableCell.scope = cell.scope;
if (cell.colwidth != null) tableCell.colwidth = cell.colwidth;
return tableCell;
});
return {
cells
};
});
return {
rows
};
};
const header = buildSection(nodeTableExtended.header);
if (header && header.rows?.length > 0) nodeTableExtended.header = header;
const body = buildSection(nodeTableExtended.body);
if (body && body.rows?.length > 0) nodeTableExtended.body = body;
const footer = buildSection(nodeTableExtended.footer);
if (footer && footer.rows?.length > 0) nodeTableExtended.footer = footer;
return node;
}
/**
* Build question[] node
*
* @param data - data for the node
* @returns
*/
buildQuestions(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildQuestion(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build question node
*
* @param data - data for the node
* @returns
*/
buildQuestion(context, data) {
if (!data) return void 0;
const defaultExample = data.__sampleSolutionAst;
const node = {
question: this.handleJsonText(context, TextLocation.body, data.question),
partialAnswer: data.partialAnswer ?? "",
sampleSolution: data.sampleSolution ?? "",
additionalSolutions: data.additionalSolutions ?? void 0,
reasonableNumOfChars: data.reasonableNumOfChars ?? void 0,
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, defaultExample),
__sampleSolutionAst: data.__sampleSolutionAst
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["question", "item", "hint", "instruction"],
ignoreUndefined: ["example"],
ignoreEmptyString: ["partialAnswer", "sampleSolution"]
});
return node;
}
/**
* Build ingredient[] node
*
* @param data - data for the node
* @returns
*/
buildIngredients(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildIngredient(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build ingredient node
*
* @param data - data for the node
* @returns
*/
buildIngredient(context, data) {
if (!data) return void 0;
const node = {
title: this.handleJsonText(context, TextLocation.tag, data.title),
checked: data.checked ?? false,
ingredient: this.handleJsonText(context, TextLocation.tag, data.ingredient),
quantity: data.quantity ?? 0,
unit: data.unit ?? "",
unitPlural: data.unitPlural ?? "",
unitAbbr: data.unitAbbr ?? "",
unitAbbrPlural: data.unitAbbrPlural ?? "",
decimalPlaces: data.decimalPlaces ?? 1,
disableCalculation: data.disableCalculation ?? false,
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint)
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
// ignoreAllEmptyArrays: true,
// ignoreUndefined: ['example'],
ignoreEmptyString: ["item", "unit"]
});
return node;
}
/**
* Build body node
* - Handles JSON, Bitmark text, and plain text
* - body: set to either string, TextAst, or JSON
* - bodyBits: set to array of BodyBitJson (will be validated and merged into body if it is a string)
* - placeholders: set to placeholders from v2 JSON when body is a v2 JSON string
* - bodyString: set to the original string if body is a string (only used for card body)
*
* @param data - data for the node
* @returns
*/
buildBody(context, data) {
if (!data) return void 0;
let body;
const bodyBits = [];
const placeholders = data.placeholders;
const bodyString = data.bodyString;
const { textFormat } = context;
const handleJsonBody = () => {
if (typeof data.body === "string") {
try {
body = JSON.parse(data.body);
} catch (_e) {
body = null;
}
} else {
body = data.body;
}
};
const handleBitmarkTextBody = () => {
let rawBody = data.body;
let bodyStr;
const placeholderNodes = {};
if (stringUtils.isString(data.body)) {
bodyStr = (data.body ?? "").trim();
rawBody = [];
} else if (Array.isArray(data.body)) {
} else {
rawBody = [];
}
if (placeholders) {
for (const [key, val] of Object.entries(placeholders)) {
placeholderNodes[key] = val;
}
}
if (bodyStr) {
bodyStr = instance3.breakscape(bodyStr, {
format: textFormat,
location: TextLocation.body,
v2: true
});
let index = 0;
const newPlaceholderNodes = [];
for (const [key, val] of Object.entries(placeholderNodes)) {
if (bodyStr) {
const newKey = `[!${index}]`;
bodyStr = bodyStr.replace(`${key}`, newKey);
newPlaceholderNodes.push(val);
index++;
}
}
rawBody = this.textParser.toAst(bodyStr, {
format: textFormat,
location: TextLocation.body
});
const replaceBitsRecursive = (bodyText) => {
for (let i = 0, len = bodyText.length; i < len; i++) {
const bodyPart = bodyText[i];
if (bodyPart.type === "bit") {
const index2 = bodyPart.index;
if (newPlaceholderNodes[index2] != null) {
bodyText[i] = newPlaceholderNodes[index2];
}
} else {
if (bodyPart.content) replaceBitsRecursive(bodyPart.content);
}
}
};
replaceBitsRecursive(rawBody);
}
rawBody = structuredClone(rawBody);
this.textParser.walkBodyBits(rawBody, (parent, index, bodyBit) => {
let parsedBit;
switch (bodyBit.type) {
case BodyBitType.gap:
parsedBit = this.buildGap(context, bodyBit);
break;
case BodyBitType.mark:
parsedBit = this.buildMark(context, bodyBit);
break;
case BodyBitType.select:
parsedBit = this.buildSelect(context, bodyBit);
break;
case BodyBitType.highlight:
parsedBit = this.buildHighlight(context, bodyBit);
break;
default:
}
if (parsedBit != void 0) {
parent[index] = parsedBit;
bodyBits.push(parsedBit);
}
});
body = rawBody;
body.__tag = "text";
};
const handlePlainTextBody = () => {
body = data.body;
};
const isBitmarkText = textFormat === TextFormat.bitmarkText;
if (textFormat === TextFormat.json) {
handleJsonBody();
} else if (isBitmarkText) {
handleBitmarkTextBody();
} else {
handlePlainTextBody();
}
const node = {
body,
bodyBits,
bodyString
// Placeholders are only ever parsed into AST, never stored directly
};
return node;
}
/**
* Build footer node
*
* @param data - data for the node
* @returns
*/
buildFooter(context, data) {
if (!data) return void 0;
const node = {
footer: this.handleJsonText(context, TextLocation.body, data.footer)
};
return node;
}
/**
* Build gap node
*
* @param data - data for the node
* @returns
*/
buildGap(context, data) {
if (!data) return void 0;
const defaultExample = Array.isArray(data.__solutionsAst) && data.__solutionsAst.length > 0 ? data.__solutionsAst[0] : null;
data = this.bodyBitCopyFromAttrs(data);
const node = {
type: BodyBitType.gap,
solutions: data.solutions || [],
// Must be before other properties except type
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
isCaseSensitive: data.isCaseSensitive,
...this.toExample(data.__isDefaultExample, data.example, defaultExample),
__solutionsAst: data.__solutionsAst,
attrs: {}
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreAllEmptyArrays: true,
ignoreUndefined: ["example", "isCaseSensitive"]
});
return node;
}
/**
* Build mark configs node
*
* @param data - data for the node
* @returns
*/
buildMarkConfigs(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildMarkConfig(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build mark config node
*
* @param data - data for the node
* @returns
*/
buildMarkConfig(_context, data) {
if (!data) return void 0;
const node = {
mark: data.mark ?? "unknown",
color: data.color ?? "",
emphasis: data.emphasis ?? ""
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllEmptyString: true
});
return node;
}
/**
* Build mark node
*
* @param data - data for the node
* @returns
*/
buildMark(context, data) {
if (!data) return void 0;
data = this.bodyBitCopyFromAttrs(data);
const node = {
type: BodyBitType.mark,
solution: data.solution ?? "",
// Must be before other properties except type
mark: data.mark ?? "",
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, true),
attrs: {}
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreAllEmptyArrays: true,
ignoreUndefined: ["example"],
ignoreEmptyString: ["solution", "mark"]
});
return node;
}
/**
* Build select node
*
* @param data - data for the node
* @returns
*/
buildSelect(context, data) {
if (!data) return void 0;
data = this.bodyBitCopyFromAttrs(data);
const node = {
type: BodyBitType.select,
options: this.buildSelectOptions(context, data.options) || [],
// Must be before other properties except type
prefix: data.prefix ?? "",
postfix: data.postfix ?? "",
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(false, void 0, void 0),
// Will be set in later
__hintString: data.__hintString,
__instructionString: data.__instructionString,
attrs: {}
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreAllEmptyArrays: true,
ignoreEmptyString: ["prefix", "postfix"]
});
return node;
}
/**
* Build selectOption[] node
*
* @param data - data for the node
* @returns
*/
buildSelectOptions(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildSelectOption(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build selectOption node
*
* @param data - data for the node
* @returns
*/
buildSelectOption(context, data) {
if (!data) return void 0;
const node = {
text: data.text ?? "",
// Must be before other properties except type
isCorrect: !!data.isCorrect,
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, !!data.isCorrect)
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreAllEmptyArrays: true,
ignoreUndefined: ["example"]
});
return node;
}
/**
* Build highlight node
*
* @param data - data for the node
* @returns
*/
buildHighlight(context, data) {
if (!data) return void 0;
data = this.bodyBitCopyFromAttrs(data);
const node = {
type: BodyBitType.highlight,
texts: this.buildHighlightTexts(context, data.texts) || [],
// Must be before other properties except type
prefix: data.prefix ?? "",
postfix: data.postfix ?? "",
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(false, void 0, void 0),
// Will be set in later
attrs: {}
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreAllEmptyArrays: true,
ignoreEmptyString: ["prefix", "postfix"]
});
return node;
}
/**
* Build highlightText[] node
*
* @param data - data for the node
* @returns
*/
buildHighlightTexts(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildHighlightText(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build highlightText node
*
* @param data - data for the node
* @returns
*/
buildHighlightText(context, data) {
if (!data) return void 0;
const node = {
text: data.text ?? "",
// Must be before other properties except type
isCorrect: !!data.isCorrect,
isHighlighted: !!data.isHighlighted,
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, !!data.isCorrect)
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreAllEmptyArrays: true,
ignoreUndefined: ["example"]
});
return node;
}
/**
* Build flashcards node
*
* @param data - data for the node
* @returns
*/
buildFlashcards(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildFlashcard(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build flashcard node
*
* @param data - data for the node
* @returns
*/
buildFlashcard(context, data) {
if (!data) return void 0;
const node = {
question: this.buildTextAndIcon(context, data.question),
answer: this.buildTextAndIcon(context, data.answer),
alternativeAnswers: (data.alternativeAnswers || []).map((d) => this.buildTextAndIcon(context, d)).filter((d) => d != null),
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, true)
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: [
"question",
"answer",
"alternativeAnswers",
"item",
"hint",
"instruction"
],
ignoreUndefined: ["example"]
});
return node;
}
/**
* Build definitionListItem[] node
*
* @param data - data for the node
* @returns
*/
buildDefinitionList(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildDefinitionListItem(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build definitionListItem node
*
* @param data - data for the node
* @returns
*/
buildDefinitionListItem(context, data) {
if (!data) return void 0;
const { bitType } = context;
const textAsStrings = instance2.isOfBitType(bitType, [BitType.metaSearchDefaultTerms]);
const node = {
term: this.buildTextAndIcon(context, data.term, textAsStrings),
definition: this.buildTextAndIcon(context, data.definition, textAsStrings),
alternativeDefinitions: (data.alternativeDefinitions || []).map((d) => this.buildTextAndIcon(context, d, textAsStrings)).filter((d) => d != null),
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, true)
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: [
"question",
"answer",
"alternativeDefinitions",
"item",
"hint",
"instruction"
],
ignoreUndefined: ["example"]
});
return node;
}
buildTextAndIcon(context, data, textAsStrings = false) {
const icon = this.resourceBuilder.resourceFromResourceJson(
context,
data?.icon
);
let text = this.handleJsonText(
context,
TextLocation.tag,
data?.text || data
);
if (textAsStrings) {
text = this.textGenerator.generateSync(text, TextFormat.plainText, TextLocation.body, {
noBreakscaping: true
}).trim();
}
const node = {
text,
icon
};
objectUtils.removeUnwantedProperties(node, {
ignoreEmptyArrays: ["text"],
ignoreUndefined: ["icon"]
});
return node;
}
/**
* Build statement[] node
*
* @param data - data for the node
* @returns
*/
buildStatements(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildStatement(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build statement node
*
* @param data - data for the node
* @returns
*/
buildStatement(context, data) {
if (!data) return void 0;
const node = {
statement: this.handleJsonText(context, TextLocation.tag, data.statement),
isCorrect: !!data.isCorrect,
item: this.handleJsonText(context, TextLocation.tag, data.item),
lead: this.handleJsonText(context, TextLocation.tag, data.lead),
hint: this.handleJsonText(context, TextLocation.tag, data.hint),
instruction: this.handleJsonText(context, TextLocation.tag, data.instruction),
...this.toExample(data.__isDefaultExample, data.example, !!data.isCorrect)
};
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
ignoreEmptyArrays: ["statement"],
ignoreUndefined: ["example"]
});
return node;
}
/**
* Build (image-on-device) imageSource node
*
* @param data - data for the node
* @returns
*/
buildImageSource(_context, data) {
if (!data) return void 0;
const { url, mockupId, size, format, trim } = data;
const node = {
url: url ?? "",
mockupId: mockupId ?? "",
size: size ?? null,
format: format ?? null,
trim: instance5.isBoolean(trim) ? trim : null
};
objectUtils.removeUnwantedProperties(node, {
ignoreFalse: ["trim"],
ignoreEmptyString: ["url", "mockupId"],
ignoreAllUndefined: true
});
return node;
}
/**
* Build (chat) person node
*
* @param data - data for the node
* @returns
*/
buildPerson(context, data) {
if (!data) return void 0;
const { name, title, avatarImage } = data;
const node = {
name: name ?? "",
title: title ?? void 0,
avatarImage: this.resourceBuilder.resourceFromResourceJson(
context,
avatarImage
)
};
objectUtils.removeUnwantedProperties(node, {
ignoreEmptyString: ["name"],
ignoreAllUndefined: true
});
return node;
}
/**
* Build (cook-ingredients) technicalTerm node
*
* @param data - data for the node
* @returns
*/
buildTechnicalTerm(_context, data) {
if (!data) return void 0;
const { technicalTerm, lang } = data;
const node = {
technicalTerm: technicalTerm ?? "",
lang: lang ?? ""
};
objectUtils.removeUnwantedProperties(node, {
ignoreEmptyString: ["technicalTerm"]
// ignoreAllUndefined: true,
});
return node;
}
/**
* Build (cook-ingredients) servings node
*
* @param data - data for the node
* @returns
*/
buildServings(_context, data) {
if (!data) return void 0;
const {
servings,
unit,
unitPlural,
unitAbbr,
unitAbbrPlural,
decimalPlaces,
disableCalculation,
hint
} = data;
const node = {
servings: servings ?? 0,
unit: unit ?? "",
unitPlural: unitPlural ?? "",
unitAbbr: unitAbbr ?? "",
unitAbbrPlural: unitAbbrPlural ?? "",
decimalPlaces: decimalPlaces ?? 1,
disableCalculation: disableCalculation ?? false,
hint: hint ?? ""
};
objectUtils.removeUnwantedProperties(node, {
ignoreEmptyString: ["servings", "unit"]
// ignoreAllUndefined: true,
});
return node;
}
/**
* Build (survey-rating) ratingLevelStart / ratingLevelEnd node
*
* @param data - data for the node
* @returns
*/
buildRatingLevelStartEnd(context, data) {
if (!data) return void 0;
const { level, label } = data;
const node = {
level: level ?? 0,
label: this.handleJsonText(context, TextLocation.tag, label)
};
objectUtils.removeUnwantedProperties(node, {
// ignoreEmptyArrays: ['servings', 'unit'],
// ignoreAllUndefined: true,
});
return node;
}
// DEPRECATED - TO BE REMOVED IN FUTURE
// /**
// * Build captionDefinition node
// *
// * @param data - data for the node
// * @returns
// */
// protected buildCaptionDefinition(
// _context: BuildContext,
// data: Partial<CaptionDefinitionJson> | undefined,
// ): CaptionDefinitionJson | undefined {
// if (!data) return undefined;
// // NOTE: Node order is important and is defined here
// const node: CaptionDefinitionJson = {
// // term: this.convertJsonTextToAstText(data.term),
// // description: this.convertJsonTextToAstText(data.description),
// term: data.term ?? '',
// definition: data.definition ?? '',
// };
// // Remove Unset Optionals
// ObjectUtils.removeUnwantedProperties(node, {
// ignoreEmptyString: ['term', 'description'],
// // ignoreAllUndefined: true,
// });
// return node;
// }
// /**
// * Build captionDefinitionList node
// *
// * @param data - data for the node
// * @returns
// */
// protected buildCaptionDefinitionList(
// context: BuildContext,
// data: Partial<CaptionDefinitionListJson> | undefined,
// ): CaptionDefinitionListJson | undefined {
// if (!data) return undefined;
// // NOTE: Node order is important and is defined here
// const node: CaptionDefinitionListJson = {
// columns: data.columns || [],
// definitions: (data.definitions || [])
// .map((d) => {
// return this.buildCaptionDefinition(context, {
// term: d.term,
// definition: d.definition,
// });
// })
// .filter((d) => d != null),
// };
// // Remove Unset Optionals
// ObjectUtils.removeUnwantedProperties(node, {
// ignoreAllEmptyArrays: true,
// });
// return node;
// }
/**
* Build card bit[] node
*
* @param data - data for the node
* @returns
*/
buildCardBits(context, data) {
if (!Array.isArray(data)) return void 0;
const nodes = data.map((d) => this.buildCardBit(context, d)).filter((d) => d != null);
return nodes.length > 0 ? nodes : void 0;
}
/**
* Build card bit node
*
* @param data - data for the node
* @returns
*/
buildCardBit(context, data) {
if (!data) return void 0;
const { bitType, textFormat } = context;
const node = this.buildBit({ ...data, bitType, textFormat }, {
cardSet: {
sideNo: 0,
variantNo: 0
}
});
objectUtils.removeUnwantedProperties(node, {
ignoreAllFalse: true,
// ignoreEmptyArrays: ['example'],
ignoreUndefined: ["example"]
});
return nodeValidator.validateCardBit(node);
}
toImageResource(context, data) {
return instance4.asSingle(
this.resourceBuilder.resourceFromResourceDataJson(context, ResourceType.image, data?.image)
);
}
toImageResources(context, data) {
const arr = instance4.asArray(data);
const results = [];
if (arr && arr.length > 0) {
for (const item of arr) {
const res = this.resourceBuilder.resourceFromResourceDataJson(
context,
ResourceType.image,
item.image
);
results.push(res);
}
}
return results;
}
toVideoResources(context, data) {
const arr = instance4.asArray(data);
const results = [];
if (arr && arr.length > 0) {
for (const item of arr) {
const res = this.resourceBuilder.resourceFromResourceDataJson(
context,
ResourceType.video,
item.video
);
results.push(res);
}
}
return results;
}
//
// Private
//
buildCardNode(context, data) {
const node = {
heading: this.buildHeading(context, data.heading),
questions: this.buildQuestions(context, data.questions),
elements: this.handleJsonText(context, TextLocation.body, data.elements),
flashcards: this.buildFlashcards(context, data.flashcards),
definitions: this.buildDefinitionList(context, data.definitions ?? data.legend),
statement: this.buildStatement(context, data.statement),
statements: this.buildStatements(context, data.statements),
choices: this.buildChoices(context, data.choices),
responses: this.buildResponses(context, data.responses),
feedbacks: this.buildFeedbacks(context, data.feedbacks),
quizzes: this.buildQuizzes(context, data.quizzes),
pairs: this.buildPairs(context, data.pairs),
matrix: this.buildMatricies(context, data.matrix),
pronunciationTable: this.buildPronunciationTable(context, data.pronunciationTable),
table: this.buildTable(context, data.table),
botResponses: this.buildBotResponses(context, data.botResponses),
ingredients: this.buildIngredients(context, data.ingredients),
// DEPRECATED - TO BE REMOVED IN FUTURE
// captionDefinitionList: this.buildCaptionDefinitionList(context, data.captionDefinitionList),
cardBits: this.buildCardBits(context, data.cardBits)
};
objectUtils.removeUnwantedProperties(node, {
ignoreEmptyObjects: ["table"]
});
return Object.keys(node).length > 0 ? node : void 0;
}
/**
* Copy any attributes from 'attrs' property to the root node (necessary when the input if from JSON)
* The values in 'attrs' will overwrite any existing values in the node
*
* The original object is not modified, a new object is returned
*
* @param data
*/
bodyBitCopyFromAttrs(data) {
const copy = { ...data };
if (data.attrs) {
for (const [key, value] of Object.entries(data.attrs)) {
copy[key] = value;
}
}
return copy;
}
/**
* Set examples down the tree
*
* @param body
* @param cardNode
* @param __isDefaultExample
* @param example
* @returns true if any of the answers has an example, otherwise undefined
*/
pushExampleDownTree(context, body, cardNode, __isDefaultExample, example) {
if (__isDefaultExample || example != null) {
if (cardNode) {
this.pushExampleDownTreeString(
context,
__isDefaultExample,
example,
cardNode.pairs
);
this.pushExampleDownTreeBoolean(
context,
__isDefaultExample,
example,
false,
cardNode.flashcards
);
this.pushExampleDownTreeBoolean(
context,
__isDefaultExample,
example,
false,
cardNode.definitions
);
this.pushExampleDownTreeBoolean(
context,
__isDefaultExample,
example,
true,
cardNode.choices
);
this.pushExampleDownTreeBoolean(
context,
__isDefaultExample,
example,
false,
cardNode.responses,
cardNode.statements,
cardNode.statement
);
if (cardNode.quizzes) {
for (const quiz of cardNode.quizzes) {
this.pushExampleDownTreeBoolean(
context,
__isDefaultExample,
example,
true,
quiz.choices
);
this.pushExampleDownTreeBoolean(
context,
__isDefaultExample,
example,
false,
quiz.responses
);
}
}
if (cardNode.matrix) {
for (const m of cardNode.matrix) {
this.pushExampleDownTreeString(context, __isDefaultExample, example, m.cells);
}
}
}
if (body) {
this.pushExampleDownTreeBodyBits(context, __isDefaultExample, example, body);
}
}
}
/**
* Set examples for boolean nodes
*
* @param __isDefaultExample
* @param example
* @param onlyCorrect
* @param nodes
* @returns true if any of the answers has an example, otherwise undefined
*/
pushExampleDownTreeBoolean(_context, __isDefaultExample, example, onlyCorrect, ...nodes) {
if (!__isDefaultExample && example == null) return;
if (Array.isArray(nodes)) {
for (const ds of nodes) {
if (ds) {
const exampleNodes = Array.isArray(ds) ? ds : [ds];
instance7.fillBooleanExample(exampleNodes, __isDefaultExample, example, onlyCorrect);
}
}
}
}
/**
* Set examples for string nodes
*
* @param __isDefaultExample
* @param example
* @param nodes
* @returns true if any of the answers has an example, otherwise undefined
*/
pushExampleDownTreeString(context, __isDefaultExample, example, ...nodes) {
if (!__isDefaultExample && !example) return;
if (Array.isArray(nodes)) {
for (const ds of nodes) {
if (ds) {
const exampleNodes = Array.isArray(ds) ? ds : [ds];
instance7.fillStringExample(
context.textFormat,
exampleNodes,
__isDefaultExample,
example,
false
);
}
}
}
}
pushExampleDownTreeBodyBits(context, __isDefaultExample, example, body) {
if (!__isDefaultExample && !example) return;
const bodyBitsJson = this.textParser.extractBodyBits(body?.body);
for (const part of bodyBitsJson) {
if (part) {
switch (part.type) {
case BodyBitType.gap: {
const gap = part;
instance7.fillStringExample(
context.textFormat,
[gap],
__isDefaultExample,
example,
false
);
break;
}
case BodyBitType.mark: {
const mark = part;
instance7.fillBooleanExample([mark], __isDefaultExample, example, false);
break;
}
case BodyBitType.select: {
const select = part;
instance7.fillBooleanExample(select.options, __isDefaultExample, example, true);
break;
}
case BodyBitType.highlight: {
const highlight = part;
instance7.fillBooleanExample(highlight.texts, __isDefaultExample, example, true);
break;
}
}
}
}
}
/**
* Push a value down the tree, without overriding existing values
*
* This function is not type safe and should be used with care
*
* @param body/body[] set if the value should be passed down the body to the body bits / card body bits
* @param bodyBitTypes body bit types to push the value down to
* @param cardNode set if the value should be passed down the card node
* @param path path for the value
* @param value the value to push down
*/
pushDownTree(_context, body, bodyBitTypes, cardNode, cardNodePath, path5, value) {
if (value === void 0) return;
if (cardNode && cardNodePath) {
if (!Array.isArray(cardNodePath)) cardNodePath = [cardNodePath];
const data = objectUtils.flatMapPath(cardNode, cardNodePath);
for (const d of data) {
if (d[path5] == null) {
d[path5] = value;
}
}
}
if (body) {
const bodyArray = Array.isArray(body) ? body : [body];
for (const b of bodyArray) {
if (b && b.body && bodyBitTypes) {
const bodyBitsJson = this.textParser.extractBodyBits(b.body);
if (bodyBitTypes && bodyBitsJson && bodyBitsJson.length > 0) {
for (const part of bodyBitsJson) {
if (part) {
if (bodyBitTypes.indexOf(part.type) !== -1) {
const data = part;
if (data[path5] == null) {
data[path5] = value;
}
}
}
}
}
}
}
}
}
parseExtraProperties(extraProperties) {
if (!extraProperties) return void 0;
const entries = Object.entries(extraProperties);
if (entries.length === 0) return void 0;
const res = {};
for (const [key, value] of entries) {
res[key] = instance4.asArray(value) || [value];
}
return res;
}
/**
* Set the 'isExample' flags on the bit
*
* The flag is set if the bit has an example. The flag is set at each branch level up the tree from
* where the 'example' exists.
*
* @param bit
*/
setIsExampleFlags(bit) {
const checkIsExample = (example) => {
if (!example) return false;
if (
/*example.__isDefaultExample ||*/
example.isExample || example.example != void 0
) {
example.isExample = true;
bit.isExample = true;
} else {
if (example === bit) {
example.isExample = !!bit.isExample;
} else {
example.isExample = false;
}
}
return example.isExample;
};
const { body, cardNode } = bit;
const bodyBitsJson = this.textParser.extractBodyBits(body?.body);
for (const bodyPart of bodyBitsJson) {
switch (bodyPart.type) {
case BodyBitType.gap:
case BodyBitType.mark: {
checkIsExample(bodyPart);
break;
}
case BodyBitType.select: {
const select = bodyPart;
let hasExample = false;
for (const option of select.options) {
hasExample = checkIsExample(option) ? true : hasExample;
}
select.isExample = hasExample;
break;
}
case BodyBitType.highlight: {
const highlight = bodyPart;
let hasExample = false;
for (const text of highlight.texts) {
hasExample = checkIsExample(text) ? true : hasExample;
}
highlight.isExample = hasExample;
break;
}
}
}
if (cardNode) {
for (const v of cardNode.flashcards || []) {
checkIsExample(v);
}
for (const v of cardNode.definitions || []) {
checkIsExample(v);
}
for (const v of cardNode.pairs || []) {
checkIsExample(v);
}
for (const mx of cardNode.matrix || []) {
let hasExample = false;
for (const v of mx.cells || []) {
hasExample = checkIsExample(v) ? true : hasExample;
}
mx.isExample = hasExample;
}
for (const quiz of cardNode.quizzes || []) {
let hasExample = false;
for (const v of quiz.responses || []) {
hasExample = checkIsExample(v) ? true : hasExample;
}
for (const v of quiz.choices || []) {
hasExample = checkIsExample(v) ? true : hasExample;
}
quiz.isExample = hasExample;
}
for (const v of cardNode.responses || []) {
checkIsExample(v);
}
for (const v of cardNode.choices || []) {
checkIsExample(v);
}
for (const v of cardNode.statements || []) {
checkIsExample(v);
}
checkIsExample(cardNode.statement);
for (const v of cardNode.questions || []) {
checkIsExample(v);
}
}
checkIsExample(bit.statement);
for (const v of bit.responses || []) {
checkIsExample(v);
}
for (const v of bit.choices || []) {
checkIsExample(v);
}
checkIsExample(bit);
}
setDefaultBitValues(bit) {
const { bitType } = bit;
switch (bitType) {
case BitType.articleAi:
case BitType.noteAi:
case BitType.summaryAi:
bit.aiGenerated = this.toAstProperty(
bitType,
ConfigKey.property_aiGenerated,
true,
void 0
);
break;
}
}
addVersionToParserInfo(bit) {
const parser2 = bit.parser ?? {};
parser2.version = env.appVersion.full;
bit.parser = parser2;
}
};
// src/ast/writer/StringWriter.ts
var StringWriter = class {
constructor() {
__publicField(this, "_buffer");
__publicField(this, "_string", "");
__publicField(this, "endOfLineString", "\n");
__publicField(this, "lastWrite", "");
}
get isSync() {
return true;
}
/**
* Get the string which has been written.
*
* This cannot be called until after close() has resolved its Promise.
*
* @returns
*/
getString() {
return this._string;
}
async open() {
this.openSync();
return Promise.resolve();
}
async close() {
try {
this.closeSync();
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve();
}
openSync() {
this._buffer = [];
this._string = "";
}
closeSync() {
if (!this._buffer) {
throw new Error("open() or openSync() never called");
}
this._string = this._buffer.join("");
this._buffer = [];
}
writeLine(value) {
if (!this._buffer) return this;
let line;
if (value != null) {
line = value + this.endOfLineString;
} else {
line = this.endOfLineString;
}
this._buffer.push(line);
this.lastWrite = line;
return this;
}
writeLines(values, delimiter) {
if (!this._buffer) return this;
if (!values) return this;
let str = "";
for (let i = 0, len = values.length; i < len; i++) {
const value = values[i];
this._buffer.push(value);
str += value;
if (delimiter && i < len - 1) {
this._buffer.push(delimiter);
str += delimiter;
}
this._buffer.push(this.endOfLineString);
str += this.endOfLineString;
}
this.lastWrite = str;
return this;
}
write(value) {
if (!this._buffer) return this;
if (value == null) return this;
if (value) this.lastWrite = value;
this._buffer.push(value);
return this;
}
writeWhiteSpace() {
this.write(" ");
return this;
}
getLastWrite() {
return this.lastWrite;
}
};
// src/BitmarkParserGenerator.ts
import { Enum as Enum23 } from "@ncoderz/superenum";
// src/generator/bitmark/BitmarkGenerator.ts
import { Enum as Enum8 } from "@ncoderz/superenum";
// src/model/enum/CardSetVersion.ts
import "@ncoderz/superenum";
var CardSetVersion = {
v1: 1,
v2: 2
};
// src/generator/bitmark/BitmarkGenerator.ts
var DEFAULT_OPTIONS2 = {
debugGenerationInline: false
};
var DEFAULT_SPACES_AROUND_VALUES = 1;
var MAX_SPACES_AROUND_VALUES = 10;
var BitmarkGenerator = class extends AstWalkerGenerator {
/**
* Generate bitmark markup from a bitmark AST
*
* @param writer - destination for the output
* @param bitmarkVersion - The version of bitmark to output.
* If not specified, the version will default to 3.
*
* Specifying the version will set defaults for other options.
* - Bitmark v2:
* - cardSetVersion: 1
* - Bitmark v3:
* - cardSetVersion: 2
*
* @param options - bitmark generation options
*/
constructor(writer, options) {
super();
__publicField(this, "ast", new Ast());
__publicField(this, "textGenerator");
__publicField(this, "bitmarkVersion");
__publicField(this, "options");
__publicField(this, "mainWriter");
// Used for writing the bitmark
__publicField(this, "partWriter");
// Used for writing parts which need to be assembled before writing to the main writer
__publicField(this, "writer");
// The current writer being used
__publicField(this, "prettifySpace");
__publicField(this, "spacesAroundValues");
__publicField(this, "spacesAroundValuesStr");
// State
__publicField(this, "bitType", BitType._error);
__publicField(this, "textFormat", TextFormat.bitmarkText);
__publicField(this, "isBodyBitmarkText", false);
__publicField(this, "isCardAllowed", false);
__publicField(this, "firstBit", true);
__publicField(this, "hasCardSet", false);
__publicField(this, "hasFooter", false);
__publicField(this, "inTag", true);
__publicField(this, "skipTableTraversal", false);
this.enter = this.enter.bind(this);
this.between = this.between.bind(this);
this.exit = this.exit.bind(this);
this.leaf = this.leaf.bind(this);
this.write = this.write.bind(this);
this.bodyBitCallback = this.bodyBitCallback.bind(this);
this.bitmarkVersion = Enum8(BitmarkVersion).fromValue(options?.bitmarkVersion) ?? DEFAULT_BITMARK_VERSION;
this.options = {
...DEFAULT_OPTIONS2,
...options?.bitmarkOptions
};
this.debugGenerationInline = this.options.debugGenerationInline ?? false;
if (this.bitmarkVersion === BitmarkVersion.v2) {
if (this.options.cardSetVersion === void 0) {
this.options.cardSetVersion = CardSetVersion.v1;
}
} else {
if (this.options.cardSetVersion === void 0) {
this.options.cardSetVersion = CardSetVersion.v2;
}
}
this.prettifySpace = this.options.prettifyJson === true ? 2 : this.options.prettifyJson || void 0;
this.spacesAroundValues = this.calcSpacesAroundValues();
this.spacesAroundValuesStr = " ".repeat(this.spacesAroundValues);
this.textGenerator = new TextGenerator(this.bitmarkVersion, {
writeCallback: this.write,
bodyBitCallback: this.bodyBitCallback,
debugGenerationInline: this.debugGenerationInline
});
this.mainWriter = writer;
this.partWriter = new StringWriter();
this.writer = this.mainWriter;
this.generateResourceHandlers();
this.generatePropertyHandlers();
}
/**
* Generate bitmark markup from bitmark AST
*
* @param ast bitmark AST
*/
async generate(ast) {
this.resetState();
await this.writer.open();
this.walkAndWrite(ast);
this.writeLine();
await this.writer.close();
}
/**
* Generate text from a bitmark text AST synchronously
*
* @param ast bitmark text AST
*/
generateSync(ast) {
this.resetState();
this.writer.openSync();
this.walkAndWrite(ast);
this.writer.closeSync();
}
resetState() {
this.bitType = BitType._error;
this.textFormat = TextFormat.bitmarkText;
this.isBodyBitmarkText = false;
this.isCardAllowed = false;
this.firstBit = true;
this.hasCardSet = false;
this.hasFooter = false;
this.inTag = true;
this.printed = false;
}
walkAndWrite(ast) {
this.ast.walk(ast, NodeType.bitmarkAst, this, void 0);
this.writeLine();
}
//
// NODE HANDLERS
//
//
// Non-Terminal nodes (branches)
//
// bitmark
// bitmarkAst -> bits -> bitsValue
enter_bitsValue(node, _route) {
const bit = node.value;
const bitConfig = instance2.getBitConfig(bit.bitType);
const bitResourcesConfig = instance2.getBitResourcesConfig(bit.bitType, bit.resourceType);
this.bitType = bit.bitType;
this.textFormat = bit.textFormat ?? bitConfig.textFormatDefault;
this.isBodyBitmarkText = this.textFormat === TextFormat.bitmarkText;
this.isCardAllowed = this.calculateIsCardAllowed();
this.hasCardSet = this.haveValidCardSet(bit);
this.hasFooter = this.haveValidFooter(bit);
if (!this.firstBit) {
this.writeNL();
this.writeNL();
this.writeNL();
}
this.writeOPD(bit.bitLevel);
if (bit.isCommented) this.writeString("|");
this.writeTagKey(bit.bitType);
if (bit.textFormat) {
const write = this.isWriteTextFormat(bit.textFormat, bitConfig.textFormatDefault);
if (write) {
this.writeColon();
this.writeTagKey(bit.textFormat);
}
}
let resourceType;
if (bitConfig.resourceAttachmentAllowed && bit.resources && bit.resources.length > 0) {
const comboMap = bitResourcesConfig.comboResourceConfigKeysMap;
if (bitResourcesConfig.comboResourceConfigKeysMap.size > 0) {
for (const comboTagType of comboMap.keys()) {
resourceType = configKeyToResourceType(comboTagType);
}
} else {
resourceType = bit.resources[0].type;
}
}
if (resourceType) {
this.writeAmpersand();
this.writeTagKey(resourceType);
}
this.writeCL();
return true;
}
exit_bitsValue(_node, _route) {
this.firstBit = false;
}
// bitmarkAst -> bits -> bitsValue -> internalComment
enter_internalComment(node, route) {
const internalComment = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
for (let i = 0; i < internalComment.length; i++) {
const comment = internalComment[i];
this.writeNL();
this.writeProperty("internalComment", comment, route, {
format: TagFormat.plainText,
array: true
});
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> groupTag
enter_groupTag(node, route) {
const groupTag = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
for (const gt of groupTag) {
const { name, tags: tags2 } = gt;
this.writeProperty("groupTag", name, route, {
format: TagFormat.plainText,
writeEmpty: true
});
if (tags2 && tags2.length > 0) {
for (const t of tags2) {
this.writeProperty("tag", t, route, {
format: TagFormat.plainText,
writeEmpty: true,
forceChain: true
});
}
}
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> labelTrue / labelFalse
leaf_labelTrue(node, route) {
const value = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const bit = parent?.value;
if (bit) {
const haveTrue = value != "";
const haveFalse = bit.labelFalse && bit.labelFalse != "";
if (haveTrue || haveFalse) {
this.writeNL();
}
if (haveTrue)
this.writeProperty("labelTrue", value, route, {
format: TagFormat.plainText,
forceChain: true
});
if (haveFalse)
this.writeProperty("labelFalse", bit.labelFalse, route, {
format: TagFormat.plainText,
forceChain: true
});
}
return false;
}
leaf_labelFalse(_node, _route) {
return true;
}
// bitmarkAst -> bits -> bitsValue -> imageSource
enter_imageSource(node, route) {
const imageSource = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const { url, mockupId, size, format, trim } = imageSource;
this.writeProperty("imageSource", url, route, {
format: TagFormat.plainText
});
if (url) {
if (mockupId)
this.writeProperty("mockupId", mockupId, route, {
format: TagFormat.plainText,
forceChain: true
});
if (size)
this.writeProperty("size", size, route, {
format: TagFormat.plainText,
forceChain: true
});
if (format)
this.writeProperty("format", format, route, {
format: TagFormat.plainText,
forceChain: true
});
if (instance5.isBoolean(trim))
this.writeProperty("trim", trim, route, {
format: TagFormat.plainText,
forceChain: true
});
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> technicalTerm
enter_technicalTerm(node, route) {
const nodeValue = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const { technicalTerm, lang } = nodeValue;
this.writeProperty("technicalTerm", technicalTerm, route, {
format: TagFormat.plainText
});
if (lang != null) {
this.writeProperty("lang", lang, route, {
format: TagFormat.plainText,
forceChain: true
});
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> servings
enter_servings(node, route) {
const nodeValue = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const {
servings,
unit,
unitPlural,
unitAbbr,
unitAbbrPlural,
decimalPlaces,
disableCalculation,
hint
} = nodeValue;
this.writeProperty("servings", servings, route, {
format: TagFormat.plainText
});
if (unit != null) {
this.writeProperty("unit", unit, route, {
format: TagFormat.plainText,
forceChain: true
});
}
if (unitPlural != null) {
this.writeProperty("unitPlural", unitPlural, route, {
format: TagFormat.plainText,
forceChain: true
});
}
if (unitAbbr != null) {
this.writeProperty("unitAbbr", unitAbbr, route, {
format: TagFormat.plainText,
forceChain: true
});
}
if (unitAbbrPlural != null) {
this.writeProperty("unitAbbrPlural", unitAbbrPlural, route, {
format: TagFormat.plainText,
forceChain: true
});
}
if (decimalPlaces != null) {
this.writeProperty("decimalPlaces", decimalPlaces, route, {
format: TagFormat.plainText,
forceChain: true
});
}
if (disableCalculation != null) {
this.writeProperty("disableCalculation", disableCalculation, route, {
format: TagFormat.plainText,
forceChain: true
});
}
if (hint != null) {
this.writeOPQ();
this.writeTextOrValue(hint, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> person
enter_person(node, route) {
const person = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const { name, title, avatarImage } = person;
this.writeProperty("person", name, route, {
format: TagFormat.plainText
});
if (title) {
this.writeProperty("title", title, route, {
format: TagFormat.plainText,
forceChain: true
});
}
if (avatarImage) {
this.writeResource(ResourceType.image, avatarImage.image.src);
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> definitions -> definitionsValue -> term -> text
// bitmarkAst -> bits -> bitsValue -> cardNode -> definitions -> definitionsValue -> definition -> text
// bitmarkAst -> bits -> bitsValue -> cardNode -> definitions -> definitionsValue -> alternativeDefinitions
// -> alternativeDefinitionsValue -> text
// bitmarkAst -> bits -> bitsValue -> cardNode -> flashcards -> flashcardsValue -> question -> text
// bitmarkAst -> bits -> bitsValue -> cardNode -> flashcards -> flashcardsValue -> answer -> text
// bitmarkAst -> bits -> bitsValue -> cardNode -> flashcards -> flashcardsValue -> alternativeAnswers ->
// -> alternativeAnswersValue -> text
enter_text(node, route) {
const parent = this.getParentNode(route);
if (!parent || parent.key !== NodeType.term && parent.key !== NodeType.definition && parent.key !== NodeType.alternativeDefinitionsValue && parent.key !== NodeType.question && parent.key !== NodeType.answer && parent.key !== NodeType.alternativeAnswersValue) {
return true;
}
if (node.value) {
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
return false;
}
leaf_text(node, route) {
const parent = this.getParentNode(route);
if (!parent || parent.key !== NodeType.term && parent.key !== NodeType.definition && parent.key !== NodeType.alternativeDefinitionsValue && parent.key !== NodeType.question && parent.key !== NodeType.answer && parent.key !== NodeType.alternativeAnswersValue && parent.key !== NodeType.reason) {
return true;
}
if (stringUtils.isString(node.value)) {
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> * -> term -> icon
// bitmarkAst -> bits -> bitsValue -> * -> definition -> icon
// bitmarkAst -> bits -> bitsValue -> * -> alternativeDefinitionsValue -> icon
enter_icon(node, route) {
const resource = node.value;
const parent = this.getParentNode(route);
if (!parent) return true;
if (parent.key !== NodeType.term && parent.key !== NodeType.definition && parent.key !== NodeType.alternativeDefinitionsValue && parent.key !== NodeType.question && parent.key !== NodeType.answer && parent.key !== NodeType.alternativeAnswersValue) {
return true;
}
this.writeResource(ResourceType.icon, resource.image.src);
return true;
}
leaf_iconValue(node, route) {
this.writeNL();
this.writeProperty("icon", node.value, route, {
format: TagFormat.plainText
});
return false;
}
// bitmarkAst -> bits -> bitsValue -> ratingLevelStart
enter_ratingLevelStart(node, route) {
this.enterRatingLevelStartEndCommon(node, route);
return false;
}
// bitmarkAst -> bits -> bitsValue -> ratingLevelEnd
enter_ratingLevelEnd(node, route) {
this.enterRatingLevelStartEndCommon(node, route);
return false;
}
// Common code for ratingLevelStart and ratingLevelEnd
enterRatingLevelStartEndCommon(node, route) {
const n = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const { level, label } = n;
const levelKey = node.key === NodeType.ratingLevelStart ? "ratingLevelStart" : "ratingLevelEnd";
this.writeProperty(levelKey, level, route, {
format: TagFormat.plainText
});
if (label) {
this.writeProperty("label", label, route, {
format: TagFormat.bitmarkText,
forceChain: true
});
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> markConfig
enter_markConfig(_node, _route) {
return true;
}
// bitmarkAst -> bits -> bitsValue -> markConfig -> markConfigValue
enter_markConfigValue(node, route) {
const markConfig = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.markConfig) return true;
const { mark, color, emphasis } = markConfig;
if (mark) {
this.writeNL();
this.writeProperty("mark", mark, route, {
format: TagFormat.plainText
});
if (color) {
this.writeProperty("color", color, route, {
format: TagFormat.plainText,
forceChain: true
});
}
if (emphasis) {
this.writeProperty("emphasis", emphasis, route, {
format: TagFormat.plainText,
forceChain: true
});
}
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> partialAnswer
enter_partialAnswer(node, route) {
if (node.value) {
this.writeProperty("partialAnswer", node.value, route, {
format: TagFormat.plainText
});
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> questions -> questionsValue -> partialAnswer
leaf_partialAnswer(node, route) {
if (node.value) {
this.writeProperty("partialAnswer", node.value, route, {
format: TagFormat.plainText
});
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> sampleSolution
// bitmarkAst -> bits -> bitsValue -> questions -> questionsValue -> sampleSolution
enter_sampleSolution(node, route) {
if (node.value) {
this.writeProperty("sampleSolution", node.value, route, {
format: TagFormat.plainText
});
}
return false;
}
leaf_sampleSolution(node, route) {
if (node.value) {
this.writeProperty("sampleSolution", node.value, route, {
format: TagFormat.plainText
});
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> reasonableNumOfChars
// bitmarkAst -> bits -> bitsValue -> questions -> questionsValue -> reasonableNumOfChars
leaf_reasonableNumOfChars(node, route) {
this.writeProperty("reasonableNumOfChars", node.value, route, {
format: TagFormat.plainText
});
}
// bitmarkAst -> bits -> bitsValue -> sourceBB
enter_sourceBB(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return false;
const tuples = node.value;
if (!Array.isArray(tuples) || tuples.length === 0) return false;
this.writeProperty(
"sourceBB",
tuples.map((t) => t.join(", ")),
route,
{
format: TagFormat.plainText,
array: true
}
);
return false;
}
// bitmarkAst -> bits -> bitsValue -> questions -> questionsValue -> additionalSolutions -> additionalSolutionsValue
leaf_additionalSolutionsValue(node, route) {
const parent = this.getParentNode(route, 2);
if (parent?.key !== NodeType.questionsValue) return;
this.writeNL();
this.writeProperty("additionalSolutions", node.value, route, {
format: TagFormat.plainText,
array: true,
writeEmpty: true
});
}
// bitmarkAst -> bits -> bitsValue -> item
enter_item(node, route) {
const item = node.value;
const parent = this.getParentNode(route);
if (this.isEmptyText(item)) return false;
if (!this.isEmptyText(parent?.value?.lead)) return true;
if (!this.isEmptyText(parent?.value?.pageNumber)) return true;
if (!this.isEmptyText(parent?.value?.marginNumber)) return true;
this.writeNL_IfNotChain(route);
this.writeOPC();
this.writeTextOrValue(item, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
return true;
}
// bitmarkAst -> bits -> bitsValue -> lead
enter_lead(node, route) {
const lead = node.value;
const parent = this.getParentNode(route);
if (this.isEmptyText(lead)) return false;
if (!this.isEmptyText(parent?.value?.pageNumber)) return true;
if (!this.isEmptyText(parent?.value?.marginNumber)) return true;
this.writeNL_IfNotChain(route);
this.writeOPC();
this.writeTextOrValue(parent?.value?.item ?? "", TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
this.writeOPC();
this.writeTextOrValue(lead, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
return true;
}
// bitmarkAst -> bits -> bitsValue -> pageNumber
enter_pageNumber(node, route) {
const pageNumber = node.value;
const parent = this.getParentNode(route);
if (this.isEmptyText(pageNumber)) return false;
if (!this.isEmptyText(parent?.value?.marginNumber)) return true;
this.writeNL_IfNotChain(route);
this.writeOPC();
this.writeTextOrValue(parent?.value?.item ?? "", TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
this.writeOPC();
this.writeTextOrValue(parent?.value?.lead ?? "", TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
this.writeOPC();
this.writeTextOrValue(pageNumber ?? "", TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
return true;
}
// bitmarkAst -> bits -> bitsValue -> marginNumber
enter_marginNumber(node, route) {
const marginNumber = node.value;
const parent = this.getParentNode(route);
if (this.isEmptyText(marginNumber)) return false;
this.writeNL_IfNotChain(route);
this.writeOPC();
this.writeTextOrValue(parent?.value?.item ?? "", TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
this.writeOPC();
this.writeTextOrValue(parent?.value?.lead ?? "", TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
this.writeOPC();
this.writeTextOrValue(
parent?.value?.pageNumber ?? "",
TextFormat.bitmarkText,
TextLocation.tag
);
this.writeCL();
this.writeOPC();
this.writeTextOrValue(marginNumber, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
return true;
}
// bitmarkAst -> bits -> bitsValue -> body
enter_body(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue && parent?.key !== NodeType.cardBitsValue) return true;
this.inTag = false;
const body = node.value;
if (this.textFormat === TextFormat.json) {
const json = body.body ?? null;
if (Array.isArray(json) || objectUtils.isObject(json)) {
const text = JSON.stringify(json, null, this.prettifySpace);
if (text) {
this.writePlainTextDivider();
this.writeNL();
this.writeTextOrValue(text, TextFormat.plainText, TextLocation.body);
}
}
} else if (this.isBodyBitmarkText) {
const plainTextDividerAllowed = !(this.hasCardSet || this.hasFooter);
this.writeNL();
const b = Array.isArray(body.body) ? body.body : [];
this.writeTextOrValue(b, this.textFormat, TextLocation.body, {
plainTextDividerAllowed
});
} else {
this.writePlainTextDivider();
this.writeNL();
const s = stringUtils.isString(body.body) ? body.body : "";
this.writeTextOrValue(`${s}`, TextFormat.plainText, TextLocation.body);
}
return false;
}
exit_body(_node, _route) {
this.inTag = true;
}
bodyBitCallback(bodyBit, _index, _route) {
const nodeType = Enum8(NodeType).fromValue(bodyBit.type) ?? NodeType.bodyBit;
this.ast.walk(bodyBit, nodeType, this, void 0);
return "";
}
// bodyBit -> gap
enter_gap(node, _route) {
const gap = node.value;
if (gap.solutions && gap.solutions.length === 0) {
this.writeOPU();
this.writeCL();
} else {
for (const solution of gap.solutions) {
this.writeOPU();
this.writeTextOrValue(solution, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
}
return true;
}
// bodyBit -> mark
enter_mark(node, _route) {
const mark = node.value;
this.writeOPE();
this.writeTextOrValue(mark.solution, TextFormat.plainText, TextLocation.tag);
this.writeCL();
return true;
}
// bodyBit -> select
enter_select(_node, _route) {
return true;
}
// bodyBit -> highlight
enter_highlight(_node, _route) {
return true;
}
// bitmarkAst -> bits -> footer
enter_footer(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const footer = node.value;
if (this.textFormat === TextFormat.json) {
} else if (footer.footer && footer.footer.length > 0) {
if (this.isBodyBitmarkText) {
this.writeNL();
this.writeTextOrValue("==== footer ====", TextFormat.plainText, TextLocation.body);
this.writeNL();
this.writeTextOrValue(footer.footer, this.textFormat, TextLocation.body, {
plainTextDividerAllowed: true
// Always allowed for the footer.
});
} else {
}
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> body -> bodyParts -> bodyPartsValue -> data -> solutions
enter_solutions(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const solutions = node.value;
if (solutions && solutions.length === 0) {
this.writeOPU();
this.writeCL();
}
return true;
}
// bitmarkAst -> bits -> bitsValue -> body -> bodyParts -> bodyPartsValue -> data -> solution
leaf_solution(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return;
const solution = node.value;
const bodyPartsValue = this.getParentNode(route, 2)?.value;
if (bodyPartsValue?.type !== BodyBitType.mark) return;
if (solution) {
this.writeOPE();
this.writeTextOrValue(solution, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
}
// bitmarkAst -> bits -> bitsValue -> body -> bodyBit -> mark -> mark
leaf_mark(node, route) {
const root = route[0];
if (root?.key !== NodeType.mark) return;
const mark = node.value;
if (mark) {
this.writeProperty("mark", mark, route, {
format: TagFormat.plainText
});
}
}
// bitmarkAst -> bits -> bitsValue -> body -> bodyParts -> bodyPartsValue -> data -> options -> optionsValue
enter_optionsValue(node, _route) {
const selectOption = node.value;
if (selectOption.isCorrect) {
this.writeOPP();
} else {
this.writeOPM();
}
this.writeTextOrValue(selectOption.text, TextFormat.plainText, TextLocation.tag);
this.writeCL();
return true;
}
// bitmarkAst -> bits -> bitsValue -> body -> bodyParts -> bodyPartsValue -> data -> texts -> textsValue
enter_textsValue(node, _route) {
const highlightText = node.value;
if (highlightText.isCorrect) {
this.writeOPP();
} else {
this.writeOPM();
}
this.writeTextOrValue(highlightText.text, TextFormat.plainText, TextLocation.tag);
this.writeCL();
return true;
}
// bitmarkAst -> bits -> bitsValue -> cardNode
enter_cardNode(_node, _route) {
if (!this.isCardAllowed) return true;
this.writeCardSetStart();
return true;
}
between_cardNode(_node, _left, _right, _route) {
if (!this.isCardAllowed) return;
this.writeCardSetCardDivider();
}
exit_cardNode(_node, _route) {
if (!this.isCardAllowed) return;
this.writeCardSetEnd();
if (this.options.cardSetVersion === CardSetVersion.v1) {
}
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> elements
enter_elements(_node, _route) {
}
between_elements(_node, _left, _right, _route) {
this.writeCardSetSideDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> flashcards
between_flashcards(_node, _left, _right, _route) {
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> flashcards -> flashcardsValue
between_flashcardsValue(_node, _left, right, _route) {
if (!this.isCardAllowed) return;
if (right.key === NodeType.answer) {
this.writeCardSetSideDivider();
} else if (right.key === NodeType.alternativeAnswers && right.value?.length !== 0) {
this.writeCardSetVariantDivider();
}
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> flashcards -> flashcardsValue -> alternativeAnswers
between_alternativeAnswers(_node, _route) {
this.writeCardSetVariantDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> definitions -> definitionsValue -> alternativeDefintions
between_alternativeDefinitions(_node, _route) {
this.writeCardSetVariantDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> statements
enter_statements(_node, _route) {
}
between_statements(_node, _left, _right, _route) {
const isTrueFalse1 = this.isOfBitType(BitType.trueFalse1);
if (!isTrueFalse1) {
this.writeCardSetCardDivider();
}
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> statements -> statementsValue
enter_statementsValue(node, _route) {
const statement = node.value;
this.writeNL();
if (statement.isCorrect) {
this.writeOPP();
} else {
this.writeOPM();
}
this.writeTextOrValue(statement.statement, TextFormat.plainText, TextLocation.tag);
this.writeCL();
return true;
}
// bitmarkAst -> bits -> bitsValue -> choices -> choicesValue
// bitmarkAst -> bits -> bitsValue -> cardNode -> quizzes -> quizzesValue -> choices -> choicesValue
// bitmarkAst -> bits -> bitsValue -> cardNode -> feedbacks -> feedbacksValue -> choices -> choicesValue
enter_choicesValue(node, _route) {
const choice = node.value;
this.writeNL();
if (choice.isCorrect || choice.requireReason) {
this.writeOPP();
} else {
this.writeOPM();
}
this.writeTextOrValue(choice.choice, TextFormat.plainText, TextLocation.tag);
this.writeCL();
return true;
}
// bitmarkAst -> bits -> bitsValue -> responses -> responsesValue
// bitmarkAst -> bits -> bitsValue -> cardNode -> quizzes -> quizzesValue -> responses -> responsesValue
enter_responsesValue(node, _route) {
const response = node.value;
this.writeNL();
if (response.isCorrect) {
this.writeOPP();
} else {
this.writeOPM();
}
this.writeTextOrValue(response.response, TextFormat.plainText, TextLocation.tag);
this.writeCL();
return true;
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> feedbacks
between_feedbacks(_node, _left, _right, _route) {
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> feedbacks -> feedbacksValue -> reason
enter_reason(_node, _route) {
this.writeCardSetSideDivider();
return true;
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> quizzes
enter_quizzes(_node, _route) {
}
between_quizzes(_node, _left, _right, _route) {
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> heading
enter_heading(_node, _route) {
}
between_heading(_node, _left, _right, _route) {
if (!this.isCardAllowed) return;
this.writeCardSetSideDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> heading -> forValues
enter_forValues(_node, _route) {
}
between_forValues(_node, _left, _right, _route) {
if (!this.isCardAllowed) return;
this.writeCardSetSideDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> pairs
enter_pairs(_node, _route) {
}
between_pairs(_node, _left, _right, _route) {
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> pairs -> pairsValue -> keyAudio
enter_keyAudio(node, _route) {
const resource = node.value;
this.writeNL();
this.writeResource(ResourceType.audio, resource.audio.src);
return false;
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> pairs -> pairsValue -> keyImage
enter_keyImage(node, _route) {
const resource = node.value;
this.writeNL();
this.writeResource(ResourceType.image, resource.image.src);
return false;
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> matrix
enter_matrix(_node, _route) {
}
between_matrix(_node, _left, _right, _route) {
if (!this.isCardAllowed) return;
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> pairs -> pairsValue -> values
// bitmarkAst -> bits -> bitsValue -> cardNode -> matrix -> matrixValue -> cells -> cellsValue -> values
enter_values(_node, _route) {
if (!this.isCardAllowed) return;
this.writeCardSetSideDivider();
}
between_values(_node, _left, _right, _route) {
if (!this.isCardAllowed) return;
this.writeCardSetVariantDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> pronunciationTable
between_pronunciationTable(_node, _left, _right, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.cardNode) return;
if (!this.isCardAllowed) return;
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> table
enter_table(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.cardNode) return true;
const table = node.value;
if (!table) return true;
if (this.writeAdvancedTable(table)) {
return false;
}
return true;
}
between_table(_node, _left, _right, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.cardNode) return;
if (!this.isCardAllowed) return;
this.writeCardSetCardDivider();
}
writeAdvancedTable(table) {
const normalized = normalizeTableFormat(BitType.tableExtended, table);
const sections = [
{ key: "header", qualifier: "table-header", section: normalized.header },
{ key: "body", qualifier: void 0, section: normalized.body },
{ key: "footer", qualifier: "table-footer", section: normalized.footer }
];
let rowCount = 0;
for (const { key, qualifier, section } of sections) {
const rows = section?.rows ?? [];
if (rows.length === 0) {
continue;
}
for (const row of rows) {
this.writeCardSetCardDivider(qualifier);
this.writeTableRow(row, key);
rowCount++;
}
}
return rowCount > 0;
}
writeTableRow(row, section) {
const cells = row?.cells ?? [];
if (cells.length === 0) return;
for (let index = 0; index < cells.length; index++) {
if (index > 0) {
this.writeCardSetSideDivider();
}
this.writeNL();
this.writeTableCell(cells[index], section);
}
}
writeTableCell(cell, section) {
const defaultCellType = section === "body" ? "td" : "th";
const cellType = cell.title ? "th" : "td";
if (cellType !== defaultCellType) {
this.writeTableCellProperty("tableCellType", cellType);
this.writeNL();
}
if (cell.rowspan && cell.rowspan > 1) {
this.writeTableCellProperty("tableRowSpan", cell.rowspan);
this.writeNL();
}
if (cell.colspan && cell.colspan > 1) {
this.writeTableCellProperty("tableColSpan", cell.colspan);
this.writeNL();
}
if (cell.scope) {
this.writeTableCellProperty("tableScope", cell.scope);
this.writeNL();
}
if (cell.colwidth && cell.colwidth > 0) {
this.writeTableCellProperty("tableColWidth", cell.colwidth);
this.writeNL();
}
const content = cell.content ?? instance3.EMPTY_STRING;
const options = this.options.noBreakscaping ? { noBreakscaping: true } : void 0;
this.writeTextOrValue(content, this.textFormat, TextLocation.body, options);
}
writeTableCellProperty(name, value) {
this.writeOPA();
this.writeTagKey(name);
this.writeColon();
this.writeTextOrValue(`${value}`, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> table -> data
// bitmarkAst -> bits -> bitsValue -> cardNode -> pronunciationTable -> data
between_data(_node, _left, _right, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.table && parent?.key !== NodeType.pronunciationTable) return;
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> table -> columns
// bitmarkAst -> bits -> bitsValue -> cardNode -> captionDefinitionList -> columns
between_columns(_node, _left, _right, route) {
const parent = this.getParentNode(route);
const parentKey = parent?.key;
if (parentKey !== NodeType.table && parentKey !== NodeType.captionDefinitionList) return;
if (!this.isCardAllowed) return;
this.writeCardSetSideDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> table -> columns -> columnsValue
// bitmarkAst -> bits -> bitsValue -> cardNode -> captionDefinitionList -> columns -> columnsValue
leaf_columnsValue(node, _route) {
this.writeNL();
this.writeOPHASH();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
enter_columnsValue(node, _route) {
this.writeNL();
this.writeOPHASH();
this.writeTextOrValue(node.value, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> table -> data -> dataValue
// bitmarkAst -> bits -> bitsValue -> cardNode -> pronunciationTable -> data -> dataValue
between_dataValue(_node, _left, _right, route) {
const parent = this.getParentNode(route, 2);
if (parent?.key !== NodeType.table && parent?.key !== NodeType.pronunciationTable) return;
if (!this.isCardAllowed) return;
this.writeCardSetSideDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> table -> data -> dataValue -> dataValueValue
leaf_dataValueValue(node, route) {
const parent = this.getParentNode(route, 3);
if (parent?.key !== NodeType.table) return;
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> table -> data -> dataValue -> dataValueValue
// bitmarkAst -> bits -> bitsValue -> cardNode -> pronunciationTable -> data -> dataValue -> dataValueValue
enter_dataValueValue(node, route) {
const parent = this.getParentNode(route, 3);
if (parent?.key !== NodeType.table && parent?.key !== NodeType.pronunciationTable) return;
if (node.value) {
if (parent?.key === NodeType.pronunciationTable) {
const cell = node.value;
if (cell.title) {
this.writeNL();
this.writeOP();
this.writeHash();
this.writeTextOrValue(cell.title, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
}
if (cell.audio) {
this.writeNL();
this.writeResource(ResourceType.audio, cell.audio.audio.src);
}
if (cell.body) {
this.writeNL();
this.writeTextOrValue(cell.body, this.textFormat, TextLocation.body);
}
return false;
} else {
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
}
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> captionDefinitionList
between_captionDefinitionList(_node, _left, _right, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.cardNode) return;
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> captionDefinitionList -> definitions -> definitionsValue -> term
leaf_term(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.definitionsValue) return true;
if (node.value) {
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> captionDefinitionList -> definitions -> definitionsValue -> definition
leaf_definition(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.definitionsValue) return true;
if (node.value) {
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> captionDefinitionList -> definitions
// bitmarkAst -> bits -> bitsValue -> cardNode -> definitions
between_definitions(_node, _left, _right, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.cardNode && parent?.key !== NodeType.captionDefinitionList) return;
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> definitions -> definitionsValue
between_definitionsValue(_node, _left, right, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.definitions) return;
if (!this.isCardAllowed) return;
if (right.key === NodeType.definition) {
this.writeCardSetSideDivider();
} else if (right.key === NodeType.alternativeDefinitions && right.value?.length > 0) {
this.writeCardSetVariantDivider();
}
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> questions
enter_questions(_node, _route) {
}
between_questions(_node, _left, _right, _route) {
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> ingredients
enter_ingredients(_node, _route) {
}
between_ingredients(_node, _left, _right, _route) {
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> ingredients -> ingredientsValue
enter_ingredientsValue(node, route) {
const ingredient = node.value;
if (ingredient.title != null) {
this.writeNL();
this.writeOPHASH();
this.writeTextOrValue(ingredient.title, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
}
if (ingredient.ingredient != null) {
this.writeNL();
if (ingredient.checked) {
this.writeOPP();
} else {
this.writeOPM();
}
this.writeCL();
if (ingredient.quantity != null) {
this.writeOPB();
this.writeTextOrValue(`${ingredient.quantity}`, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
if (ingredient.unit != null)
this.writeProperty("unit", ingredient.unit, route, {
format: TagFormat.plainText
});
if (ingredient.unitPlural != null)
this.writeProperty("unitPlural", ingredient.unitPlural, route, {
format: TagFormat.plainText
});
if (ingredient.unitAbbr != null)
this.writeProperty("unitAbbr", ingredient.unitAbbr, route, {
format: TagFormat.plainText
});
if (ingredient.unitAbbrPlural != null)
this.writeProperty("unitAbbrPlural", ingredient.unitAbbrPlural, route, {
format: TagFormat.plainText
});
if (ingredient.decimalPlaces != null)
this.writeProperty("decimalPlaces", ingredient.decimalPlaces, route, {
format: TagFormat.plainText
});
if (ingredient.disableCalculation)
this.writeProperty("disableCalculation", true, route, {
format: TagFormat.plainText
});
this.writeNL();
this.writeTextOrValue(ingredient.ingredient, this.textFormat, TextLocation.body);
}
return true;
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> botResponses
enter_botResponses(_node, _route) {
}
between_botResponses(_node, _left, _right, _route) {
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> cardBits
enter_cardBits(_node, _route) {
}
between_cardBits(_node, _left, _right, _route) {
this.writeCardSetCardDivider();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> botResponses -> botResponsesValue -> item
leaf_item(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.botResponsesValue) return;
this.writeNL();
this.writeOPC();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> botResponses -> botResponsesValue -> response
leaf_response(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.botResponsesValue) return;
this.writeNL();
this.writeOPB();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> botResponses -> botResponsesValue -> reaction
leaf_reaction(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.botResponsesValue) return;
this.writeNL();
this.writeProperty("reaction", node.value, route, {
format: TagFormat.plainText
});
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> botResponses -> botResponsesValue -> feedback
leaf_feedback(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.botResponsesValue) return;
const feedback = node.value;
if (feedback) {
this.writeNL();
this.writeTextOrValue(feedback, this.textFormat, TextLocation.body);
}
}
// bitmarkAst -> bits -> bitsValue -> backgroundWallpaper
enter_backgroundWallpaper(node, _route) {
const resource = node.value;
this.writeNL();
this.writePropertyStyleResource(node.key, resource);
return true;
}
// bitmarkAst -> bits -> bitsValue -> imagePlaceholder
enter_imagePlaceholder(node, _route) {
const resource = node.value;
this.writeNL();
this.writePropertyStyleResource(node.key, resource);
return true;
}
// bitmarkAst -> bits -> bitsValue -> previewImageValue
enter_previewImageValue(node, _route) {
const resource = node.value;
this.writeNL();
this.writePropertyStyleResource("previewImage", resource);
return true;
}
// bitmarkAst -> bits -> bitsValue -> previewVideoValue
enter_previewVideoValue(node, _route) {
const resource = node.value;
this.writeNL();
this.writePropertyStyleResource("previewVideo", resource);
return true;
}
// bitmarkAst -> bits -> bitsValue -> platformIcon
enter_platformIcon(node, _route) {
const resource = node.value;
this.writeNL();
this.writePropertyStyleResource(node.key, resource);
return true;
}
// bitmarkAst -> bits -> bitsValue -> platformLogo
enter_platformLogo(node, _route) {
const resource = node.value;
this.writeNL();
this.writePropertyStyleResource(node.key, resource);
return true;
}
// bitmarkAst -> bits -> bitsValue -> platformBackgroundImage
enter_platformBackgroundImage(node, _route) {
const resource = node.value;
this.writeNL();
this.writePropertyStyleResource(node.key, resource);
return true;
}
// bitmarkAst -> bits -> bitsValue -> coverImage (when it's a resource object or property array)
enter_coverImage(node, route) {
const value = node.value;
if (value && typeof value === "object" && !Array.isArray(value) && "type" in value) {
const resource = value;
this.writeNL();
this.writePropertyStyleResource(node.key, resource);
return true;
}
if (Array.isArray(value)) {
if (value == null) return true;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const bitType = this.getBitType(route);
if (!bitType) return true;
const config = instance2.getBitConfig(bitType);
const propertyConfig = instance2.getTagConfigForTag(
config.tags,
ConfigKey.property_coverImage
);
if (!propertyConfig) return true;
this.writeNL_IfNotChain(route);
this.writeProperty(propertyConfig.tag, value, route, {
format: propertyConfig.format ?? TagFormat.plainText,
array: propertyConfig.array ?? false,
writeEmpty: true,
ignoreFalse: propertyConfig.defaultValue === "false",
ignoreTrue: propertyConfig.defaultValue === "true"
});
}
return true;
}
// bitmarkAst -> bits -> bitsValue -> coverImage (when it's a property string)
// This is called when coverImage is a simple string value
leaf_coverImage(node, route) {
const value = node.value;
if (value == null) return;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return;
const bitType = this.getBitType(route);
if (!bitType) return;
const config = instance2.getBitConfig(bitType);
const propertyConfig = instance2.getTagConfigForTag(config.tags, ConfigKey.property_coverImage);
if (!propertyConfig) return;
this.writeNL_IfNotChain(route);
this.writeProperty(propertyConfig.tag, node.value, route, {
format: propertyConfig.format ?? TagFormat.plainText,
array: propertyConfig.array ?? false,
writeEmpty: true,
ignoreFalse: propertyConfig.defaultValue === "false",
ignoreTrue: propertyConfig.defaultValue === "true"
});
}
// bitmarkAst -> bits -> bitsValue -> backgroundImage (when it's a resource object or property array)
enter_backgroundImage(node, route) {
const value = node.value;
if (value && typeof value === "object" && !Array.isArray(value) && "type" in value) {
const resource = value;
this.writeNL();
this.writePropertyStyleResource(node.key, resource);
return true;
}
if (Array.isArray(value)) {
if (value == null) return true;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const bitType = this.getBitType(route);
if (!bitType) return true;
const config = instance2.getBitConfig(bitType);
const propertyConfig = instance2.getTagConfigForTag(
config.tags,
ConfigKey.property_backgroundImage
);
if (!propertyConfig) return true;
this.writeNL_IfNotChain(route);
this.writeProperty(propertyConfig.tag, value, route, {
format: propertyConfig.format ?? TagFormat.plainText,
array: propertyConfig.array ?? false,
writeEmpty: true,
ignoreFalse: propertyConfig.defaultValue === "false",
ignoreTrue: propertyConfig.defaultValue === "true"
});
}
return true;
}
// bitmarkAst -> bits -> bitsValue -> backgroundImage (when it's a property string)
// This is called when backgroundImage is a simple string value
leaf_backgroundImage(node, route) {
const value = node.value;
if (value == null) return;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return;
const bitType = this.getBitType(route);
if (!bitType) return;
const config = instance2.getBitConfig(bitType);
const propertyConfig = instance2.getTagConfigForTag(
config.tags,
ConfigKey.property_backgroundImage
);
if (!propertyConfig) return;
this.writeNL_IfNotChain(route);
this.writeProperty(propertyConfig.tag, node.value, route, {
format: propertyConfig.format ?? TagFormat.plainText,
array: propertyConfig.array ?? false,
writeEmpty: true,
ignoreFalse: propertyConfig.defaultValue === "false",
ignoreTrue: propertyConfig.defaultValue === "true"
});
}
exit_imagePlaceholder(_node, _route) {
}
// bitmarkAst -> bits -> bitsValue -> posterImage
// bitmarkAst -> bits -> bitsValue -> resource -> * -> posterImage
enter_posterImage(node, route) {
const parent = this.getParentNode(route);
if (parent?.key === NodeType.bitsValue) {
const posterImage = node.value;
if (posterImage) {
this.writeProperty("posterImage", posterImage, route, {
format: TagFormat.plainText
});
}
} else {
const posterImage = node.value;
if (posterImage && posterImage.src) {
this.writeProperty("posterImage", posterImage.src, route, {
format: TagFormat.plainText
});
}
}
return true;
}
// bitmarkAst -> bits -> bitsValue -> resource -> thumbnails
// [src1x,src2x,src3x,src4x,width,height,alt,zoomDisabled,caption]
enter_thumbnails(node, route) {
const thumbnails = node.value;
if (Array.isArray(thumbnails)) {
const thumbnailKeys = ["src1x", "src2x", "src3x", "src4x"];
for (let i = 0; i < thumbnails.length; i++) {
if (i === thumbnailKeys.length) break;
const thumbnail = thumbnails[i];
const key = thumbnailKeys[i];
this.writeProperty(key, thumbnail.src, route, {
format: TagFormat.plainText
});
}
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> bitType
// bitmarkAst -> bits -> bitsValue -> textFormat
// bitmarkAst -> bits -> level
leaf_level(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
if (!instance2.isOfBitType(this.bitType, [BitType.chapter])) return true;
const level = node.value;
const bit = parent?.value;
if (level > 0 && bit.title == null) {
this.writeNL();
this.writeOP();
for (let i = 0; i < +level; i++) this.writeHash();
this.writeCL();
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> titleString
leaf_titleString(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
if (node.value) {
this.writeNL();
this.writeProperty("title", node.value, route, {
format: TagFormat.plainText
});
}
return false;
}
// bitmarkAst -> bits -> title
enter_title(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const value = node.value;
const title = value;
const bit = parent?.value;
const level = bit.level || 1;
if (level && title) {
this.writeNL();
this.writeOP();
for (let i = 0; i < +level; i++) this.writeHash();
this.writeTextOrValue(title, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
}
return false;
}
// bitmarkAst -> bits -> subtitle
enter_subtitle(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const value = node.value;
const subtitle = value;
const level = 2;
if (level && subtitle) {
this.writeNL();
this.writeOP();
for (let i = 0; i < level; i++) this.writeHash();
this.writeTextOrValue(subtitle, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> book (array)
// bitmarkAst -> bits -> bitsValue -> book (array) -> bookValue
enter_book(_node, _route) {
}
enter_bookValue(node, route) {
const book = node.value;
if (book) {
this.writeNL();
this.writeProperty("book", book.book, route, {
format: TagFormat.plainText,
writeEmpty: true
});
if (book.reference) {
this.writeOPRANGLE();
this.writeTextOrValue(book.reference, TextFormat.plainText, TextLocation.tag);
this.writeCL();
if (book.referenceEnd) {
this.writeOPRANGLE();
this.writeTextOrValue(book.referenceEnd, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
}
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> book (single)
leaf_book(node, route) {
const parent = this.getParentNode(route);
const bit = parent?.value;
if (bit && node.value) {
this.writeNL();
this.writeProperty("book", node.value, route, {
format: TagFormat.plainText,
writeEmpty: true
});
if (bit.reference) {
this.writeOPRANGLE();
this.writeTextOrValue(bit.reference, TextFormat.plainText, TextLocation.tag);
this.writeCL();
if (bit.referenceEnd) {
this.writeOPRANGLE();
this.writeTextOrValue(bit.referenceEnd, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
}
}
}
// bitmarkAst -> bits -> bitsValue -> anchor
leaf_anchor(node, _route) {
if (node.value) {
this.writeNL();
this.writeOPDANGLE();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
}
// bitmarkAst -> bits -> bitsValue -> reference
leaf_reference(node, route) {
const parent = this.getParentNode(route);
const bit = parent?.value;
if (bit && node.value) {
if (!bit.book) {
this.writeNL();
this.writeOPRANGLE();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
}
}
// * -> itemLead --> item
// * -> itemLead --> lead
// * -> hint
enter_hint(node, route) {
const value = node.value;
const text = value;
if (!this.isEmptyText(text)) {
this.writeNL_IfNotChain(route);
this.writeOPQ();
this.writeTextOrValue(text, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> * -> instruction
enter_instruction(node, route) {
const value = node.value;
const text = value;
if (!this.isEmptyText(text)) {
this.writeNL_IfNotChain(route);
this.writeOPB();
this.writeTextOrValue(text, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> * -> lang
leaf_lang(node, route) {
if (!node.value) return;
this.writeProperty("lang", node.value, route, {
format: TagFormat.boolean
});
}
// bitmarkAst -> bits -> bitsValue -> * -> refAuthor
enter_refAuthor(node, route) {
if (!node.value) return;
this.writeProperty("refAuthor", node.value, route, {
format: TagFormat.plainText,
array: true
});
}
// bitmarkAst -> bits -> bitsValue -> * -> refBookTitle
leaf_refBookTitle(node, route) {
if (!node.value) return;
this.writeProperty("refBookTitle", node.value, route, {
format: TagFormat.plainText,
array: true
});
}
// bitmarkAst -> bits -> bitsValue -> * -> refPublisher
enter_refPublisher(node, route) {
if (!node.value) return;
this.writeProperty("refPublisher", node.value, route, {
format: TagFormat.plainText,
array: true
});
}
// bitmarkAst -> bits -> bitsValue -> * -> refPublicationYear
leaf_refPublicationYear(node, route) {
if (!node.value) return;
this.writeProperty("refPublicationYear", node.value, route, {
format: TagFormat.plainText
});
}
// bitmarkAst -> bits -> bitsValue -> * -> citationStyle
leaf_citationStyle(node, route) {
if (!node.value) return;
this.writeProperty("citationStyle", node.value, route, {
format: TagFormat.plainText
});
}
// bitmarkAst -> bits -> bitsValue -> * -> isExample / example
leaf_isExample(node, route) {
const isExample = node.value;
if (!isExample) return;
const parent = this.getParentNode(route);
const example = parent?.value.example ?? null;
if (example != null && example !== "") {
this.writeNL_IfNotChain(route);
this.writeOPA();
this.writeString("example");
this.writeColon();
if (example === true) {
this.writeString(" true ");
} else if (example === false) {
this.writeString(" false ");
} else if (Array.isArray(example)) {
this.writeTextOrValue(example, TextFormat.bitmarkText, TextLocation.tag);
} else {
this.writeTextOrValue(example, TextFormat.plainText, TextLocation.tag);
}
this.writeCL();
} else {
}
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> elements -> elementsValue
enter_elementsValue(node, _route) {
if (node.value) {
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> body -> bodyValue -> gap -> solutions -> solution
// ? -> solutions -> solution
leaf_solutionsValue(node, route) {
const parent = this.getParentNode(route, 2);
if (parent?.key !== NodeType.bitsValue) return;
if (node.value != null) {
this.writeOPU();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
}
// bitmarkAst -> bits -> bitsValue-> body -> bodyValue -> select -> options -> prefix
// bitmarkAst -> bits -> bitsValue-> body -> bodyValue -> highlight -> options -> prefix
leaf_prefix(node, _route) {
if (node.value) {
this.writeOPPRE();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
}
// bitmarkAst -> bits -> bitsValue-> body -> bodyValue -> select -> options -> postfix
// bitmarkAst -> bits -> bitsValue-> body -> bodyValue -> highlight -> options -> postfix
leaf_postfix(node, _route) {
if (node.value) {
this.writeOPPOST();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
}
// bitmarkAst -> bits -> bitsValue -> * -> isCaseSensitive
leaf_isCaseSensitive(node, route) {
this.writeProperty("isCaseSensitive", node.value, route, {
format: TagFormat.boolean,
writeEmpty: true,
ignoreFalse: false,
ignoreTrue: true
});
}
// bitmarkAst -> bits -> bitsValue -> * -> isCorrect
// bitmarkAst -> bits -> bitsValue -> heading -> forKeys
leaf_forKeys(node, _route) {
this.writeNL();
this.writeOPHASH();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
// bitmarkAst -> bits -> bitsValue -> heading -> forValues
leaf_forValues(node, _route) {
this.writeNL();
this.writeOPHASH();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
// bitmarkAst -> bits -> bitsValue -> heading -> forValuesValue
leaf_forValuesValue(node, _route) {
this.writeNL();
this.writeOPHASH();
this.writeTextOrValue(node.value, TextFormat.plainText, TextLocation.tag);
this.writeCL();
}
// bitmarkAst -> bits -> bitsValue -> pairs -> pairsValue -> key
// bitmarkAst -> bits -> bitsValue -> matrix -> matrixValue -> key
leaf_key(node, _route) {
if (node.value) {
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
}
// bitmarkAst -> bits -> bitsValue -> pairs -> pairsValue -> values -> valuesValue
// bitmarkAst -> bits -> bitsValue -> matrix -> matrixValue -> cells -> cellsValue -> values -> valuesValue
leaf_valuesValue(node, _route) {
if (node.value) {
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
}
// bitmarkAst -> bits -> bitsValue -> questions -> questionsValue -> question
// bitmarkAst -> bits -> bitsValue -> cardNode -> questions -> questionsValue -> question
enter_question(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.questionsValue) return true;
if (node.value) {
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> flashcards -> flashcardsValue -> question
leaf_question(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.flashcardsValue) return;
if (node.value) {
this.writeNL();
this.writeTextOrValue(node.value, this.textFormat, TextLocation.body);
}
}
// bitmarkAst -> bits -> bitsValue -> statements -> text
// bitmarkAst -> bits -> bitsValue -> resource -> ...
// bitmarkAst -> bits -> bitsValue -> resource -> posterImage -> ...
// bitmarkAst -> bits -> bitsValue -> resource -> thumbnails -> thumbnailsValue -> ...
// [srcAlt,src1x,src2x,src3x,src4x,width,height,alt,zoomDisabled,caption]
leaf_srcAltValue(node, route) {
this.writeProperty("srcAlt", node.value, route, {
format: TagFormat.plainText
});
}
leaf_src1x(node, route) {
this.writeProperty("src1x", node.value, route, {
format: TagFormat.plainText
});
}
leaf_src2x(node, route) {
this.writeProperty("src2x", node.value, route, {
format: TagFormat.plainText
});
}
leaf_src3x(node, route) {
this.writeProperty("src3x", node.value, route, {
format: TagFormat.plainText
});
}
leaf_src4x(node, route) {
this.writeProperty("src4x", node.value, route, {
format: TagFormat.plainText
});
}
leaf_width(node, route) {
this.writeProperty("width", node.value, route, {
format: TagFormat.plainText
});
}
leaf_height(node, route) {
this.writeProperty("height", node.value, route, {
format: TagFormat.plainText
});
}
leaf_alt(node, route) {
this.writeProperty("alt", node.value, route, {
format: TagFormat.plainText
});
}
leaf_zoomDisabled(node, route) {
if (instance2.isOfBitType(this.bitType, [
BitType.imageSeparator,
BitType.pageBanner,
BitType.imagesLogoGrave,
BitType.prototypeImages,
BitType.extractorImage
])) {
this.writeProperty("zoomDisabled", node.value, route, {
format: TagFormat.boolean,
writeEmpty: true,
ignoreFalse: false,
ignoreTrue: true
});
} else {
this.writeProperty("zoomDisabled", node.value, route, {
format: TagFormat.boolean,
writeEmpty: true,
ignoreFalse: true,
ignoreTrue: false
});
}
}
leaf_license(node, route) {
this.writeProperty("license", node.value, route, {
format: TagFormat.plainText
});
}
leaf_copyright(node, route) {
this.writeProperty("copyright", node.value, route, {
format: TagFormat.plainText
});
}
leaf_provider(_node, _route) {
}
leaf_showInIndex(node, route) {
if (node.value == null) return;
this.writeNL_IfNotChain(route);
this.writeProperty("showInIndex", node.value, route, {
format: TagFormat.boolean,
ignoreFalse: true
});
}
enter_caption(node, route) {
const value = node.value;
this.writeNL_IfNotChain(route);
this.writeProperty("caption", value, route, {
format: TagFormat.bitmarkText
});
return false;
}
leaf_search(node, route) {
const value = node.value;
if (!value) return;
this.writeNL_IfNotChain(route);
this.writeProperty("search", value, route, {
format: TagFormat.plainText
});
}
leaf_selected(node, route) {
if (node.value == null) return;
this.writeNL_IfNotChain(route);
this.writeProperty("selected", node.value, route, {
format: TagFormat.boolean,
ignoreFalse: true
});
}
// bitmarkAst -> bits -> bitsValue -> resource -> ...
// bitmarkAst -> bits -> bitsValue -> resource -> posterImage -> ...
// bitmarkAst -> bits -> bitsValue -> resource -> thumbnails -> thumbnailsValue -> ...
// [duration,mute,autoplay,allowSubtitles,showSubtitles]
leaf_duration(node, route) {
const parent = this.getParentNode(route);
if (parent?.key === NodeType.bitsValue) {
this.writeNL();
}
this.writeProperty("duration", node.value, route, {
format: TagFormat.plainText
});
}
leaf_mute(node, route) {
this.writeProperty("mute", node.value, route, {
format: TagFormat.boolean
});
}
leaf_autoplay(node, route) {
this.writeProperty("autoplay", node.value, route, {
format: TagFormat.boolean
});
}
leaf_allowSubtitles(node, route) {
this.writeProperty("allowSubtitles", node.value, route, {
format: TagFormat.boolean
});
}
leaf_showSubtitles(node, route) {
this.writeProperty("showSubtitles", node.value, route, {
format: TagFormat.boolean
});
}
//
// Resources
//
//
// Generated Node Handlers
//
/**
* Generate the handlers for resources, as they are mostly the same, but not quite
*/
generateResourceHandlers() {
for (const tag of Enum8(ResourceType).keys()) {
if (tag === Enum8(ResourceType).keyFromValue(ResourceType.unknown)) continue;
const enterFuncName = `enter_${tag}`;
if (typeof this[enterFuncName] === "function") {
continue;
}
this[enterFuncName] = (node, route) => {
const resource = node.value;
if (resource == null) return false;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.resourcesValue) return true;
const alias = Enum8(ResourceType).fromValue(parent?.value.__typeAlias);
const type = alias ?? Enum8(ResourceType).fromValue(parent?.value.type);
if (!type) return false;
const url = resource.url || resource.src || resource.body || "";
this.writeNL();
this.writeResource(type, url);
return true;
};
this[enterFuncName] = this[enterFuncName].bind(this);
}
}
/**
* Generate the handlers for properties, as they are mostly the same, but not quite
*/
generatePropertyHandlers() {
for (const propertyConfigKey of Enum8(PropertyKey).values()) {
const propertyTag = configKeyToPropertyType(propertyConfigKey);
const funcNames = [`enter_${propertyTag}`, `leaf_${propertyTag}`];
for (const funcName of funcNames) {
if (typeof this[funcName] === "function") {
continue;
}
if (propertyTag === "example") continue;
this[funcName] = (node, route) => {
const value = node.value;
if (value == null) return;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return;
const bitType = this.getBitType(route);
if (!bitType) return;
const config = instance2.getBitConfig(bitType);
const propertyConfig = instance2.getTagConfigForTag(config.tags, propertyConfigKey);
if (!propertyConfig) return;
this.writeProperty(propertyConfig.tag, node.value, route, {
format: propertyConfig.format ?? TagFormat.plainText,
array: propertyConfig.array ?? false,
writeEmpty: true,
ignoreFalse: propertyConfig.defaultValue === "false",
ignoreTrue: propertyConfig.defaultValue === "true"
});
};
this[funcName] = this[funcName].bind(this);
}
}
}
// END NODE HANDLERS
//
// UTILITY FUNCTIONS
//
/**
* Get the bit type from any node
*
* @param route the route to the current node
* @returns the bit type
*/
getBitType(route) {
for (const node of route) {
if (node.key === NodeType.bitsValue) {
const n = node.value;
return n?.bitType;
}
}
return void 0;
}
/**
* Check if in a chain.
*
* Return false if at the root of the bit or a card bit, otherwise true.
* This is useful to determine if a newline should be written before certain properties.
*
* @param route
* @returns
*/
isChain(route) {
const parent = this.getParentNode(route);
if (parent?.key === NodeType.bitsValue) return false;
if (parent?.key === NodeType.cardBitsValue) return false;
if (parent?.key === NodeType.feedbacksValue) return false;
if (parent?.key === NodeType.quizzesValue) return false;
if (parent?.key === NodeType.pairsValue) return false;
if (parent?.key === NodeType.matrixValue) return false;
if (parent?.key === NodeType.definitionsValue) return false;
if (parent?.key === NodeType.questionsValue) return false;
if (parent?.key === NodeType.reason) return false;
return true;
}
haveValidCardSet(bit) {
const bitConfig = instance2.getBitConfig(bit.bitType);
if (!bitConfig) return false;
return !!bitConfig.cardSet;
}
haveValidFooter(bit) {
const footer = bit.footer;
if (!footer) return false;
const textFormat = bit.textFormat;
if (textFormat === TextFormat.json) {
return false;
} else if (footer.footer && footer.footer.length > 0) {
if (this.isBodyBitmarkText) {
return true;
} else {
return false;
}
}
return false;
}
/**
* Calculate the number of spaces around values from the options.
*
* This function should only be called from the constructor.
* Otherwise use this.spacesAroundValues.
*
* @returns
*/
calcSpacesAroundValues() {
const val = this.options.spacesAroundValues;
let spaces = DEFAULT_SPACES_AROUND_VALUES;
if (val != null) {
if (val === true) {
spaces = DEFAULT_SPACES_AROUND_VALUES;
} else if (val === false) {
spaces = 0;
} else {
spaces = val;
}
}
spaces = Math.min(Math.max(spaces, 0), MAX_SPACES_AROUND_VALUES);
return spaces;
}
// END UTILITY FUNCTIONS
//
// WRITE FUNCTIONS
//
/**
* Helper function to write a tag key, breakscaping appropriately.
* Use only to write a tag key.
*
* @param s
*/
writeTagKey(s) {
if (s != null) {
this.write(
instance3.breakscape(`${s}`, {
format: TextFormat.plainText,
location: TextLocation.tag
})
);
}
}
/**
* Write text or value, handling:
* - the format and location (i.e. conversion to text and breakscaping).
* - the spaces around values (if tag value).
*
* @param text
* @param format
* @param location
* @param options
*/
writeTextOrValue(text, format, location, options) {
const isTagValue = location === TextLocation.tag;
const s = typeof text === "string" ? text : void 0;
const ast = Array.isArray(text) ? text : void 0;
const spaces = isTagValue ? this.spacesAroundValuesStr : "";
const breakscapeFormat = format === TextFormat.bitmarkText ? format : TextFormat.plainText;
const wrap = (content) => isTagValue ? `${spaces}${content}${spaces}` : content;
const writeToPartWriter = (action) => {
const originalWriter = this.writer;
this.writer = this.partWriter;
this.partWriter.openSync();
action();
this.partWriter.closeSync();
const result = this.partWriter.getString().trim();
this.writer = originalWriter;
return result;
};
if (s != null) {
const writeString = () => this.write(
instance3.breakscape(s, {
format: breakscapeFormat,
location
})
);
if (isTagValue) {
const written = writeToPartWriter(writeString);
if (written) this.write(wrap(written));
} else {
writeString();
}
return;
}
if (ast != null) {
const generateAst = () => this.textGenerator.generateSync(ast, format, location, options);
if (isTagValue) {
const written = writeToPartWriter(generateAst);
if (written) this.write(wrap(written));
} else {
generateAst();
}
}
}
writeString(s) {
if (s != null) this.write(`${s}`);
}
writeOPBUL() {
this.write("[\u2022");
}
writeOPESC() {
this.write("[^");
}
writeOPRANGLE() {
this.write("[\u25BA");
}
writeOPDANGLE() {
this.write("[\u25BC");
}
writeOPD(level) {
this.write(`[${".".repeat(level)}`);
}
writeOPU() {
this.write("[_");
}
writeOPE() {
this.write("[=");
}
writeOPB() {
this.write("[!");
}
writeOPQ() {
this.write("[?");
}
writeOPA() {
this.write("[@");
}
writeOPP() {
this.write("[+");
}
writeOPM() {
this.write("[-");
}
writeOPS() {
this.write("[\\");
}
writeOPR() {
this.write("[*");
}
writeOPC() {
this.write("[%");
}
writeOPAMP() {
this.write("[&");
}
writeOPDOLLAR() {
this.write("[$");
}
writeOPHASH() {
this.write("[#");
}
writeOPPRE() {
this.write("['");
}
writeOPPOST() {
this.write("[");
}
writeOP() {
this.write("[");
}
writeCL() {
if (this.spacesAroundValues === 0) {
if (this.writer.getLastWrite().endsWith("^")) {
this.write(" ]");
} else {
this.write("]");
}
} else {
this.write("]");
}
}
writeAmpersand() {
this.write("&");
}
writeColon() {
this.write(":");
}
// protected writeDoubleColon(): void {
// this.write('::');
// }
writeHash() {
this.write("#");
}
writeSpacesAroundValues() {
this.write(this.spacesAroundValuesStr);
}
writePlainTextDivider() {
this.writeNL();
this.write("==== text ====");
}
writeCardSetStart() {
this.writeNL();
if (this.options.cardSetVersion === CardSetVersion.v1) {
this.write("===");
} else {
this.write("====");
}
}
writeCardSetEnd() {
this.writeNL();
if (this.options.cardSetVersion === CardSetVersion.v1) {
this.write("===");
} else {
}
}
writeCardSetCardDivider(qualifier) {
this.writeNL();
const divider = this.getCardDividerMarker();
this.write(divider);
if (qualifier) {
this.appendCardDividerQualifier(qualifier);
}
}
writeCardSetSideDivider() {
this.writeNL();
if (this.options.cardSetVersion === CardSetVersion.v1) {
this.write("==");
} else {
this.write("--");
}
}
writeCardSetVariantDivider() {
this.writeNL();
if (this.options.cardSetVersion === CardSetVersion.v1) {
this.write("--");
} else {
this.write("++");
}
}
getCardDividerMarker() {
return this.options.cardSetVersion === CardSetVersion.v1 ? "===" : "====";
}
appendCardDividerQualifier(qualifier) {
const marker = this.getCardDividerMarker();
const normalizedQualifier = instance3.breakscape(qualifier, {
format: TextFormat.plainText,
location: TextLocation.tag
});
this.write(" ");
this.write(normalizedQualifier);
this.write(" ");
this.write(marker);
}
writeNL_IfNotChain(route) {
if (!this.isChain(route)) {
this.writeNL();
}
}
writeNL() {
if (this.options.debugGenerationInline) {
this.write("\\n");
return;
}
this.write("\n");
}
writePropertyStyleResource(key, resource, deprecated_writeAsProperty = false) {
if (key && resource) {
const resourceTag = Enum8(ResourceType).keyFromValue(resource.type) ?? "";
const resourceData = resource[resourceTag];
const src = resourceData ? resourceData.src || resourceData.url || resourceData.body || "" : "";
if (deprecated_writeAsProperty) {
this.writeOPA();
} else {
this.writeOPAMP();
}
this.writeTagKey(key);
this.writeColon();
this.writeTextOrValue(src, TextFormat.plainText, TextLocation.tag);
if (resource.type === ResourceType.article) {
}
this.writeCL();
}
}
writeResource(type, value) {
if (type) {
this.writeOPAMP();
this.writeTagKey(type);
this.writeColon();
this.writeTextOrValue(value, TextFormat.plainText, TextLocation.tag);
if (type === ResourceType.article) {
}
this.writeCL();
}
}
writeProperty(name, values, route, options) {
let valuesArray;
if (values !== void 0) {
const isBitmarkText = options.format === TagFormat.bitmarkText;
if (isBitmarkText) {
if (!options.writeEmpty && isBitmarkText && this.isEmptyText(values)) return;
this.writeOPA();
this.writeTagKey(name);
this.writeColon();
this.writeTextOrValue(values, TextFormat.bitmarkText, TextLocation.tag);
this.writeCL();
} else {
if (!Array.isArray(values)) {
valuesArray = [values];
} else {
valuesArray = values;
}
if (valuesArray.length > 0) {
if (!options.array) valuesArray = valuesArray.slice(valuesArray.length - 1);
let propertyIndex = 0;
for (const val of valuesArray) {
if (val !== void 0) {
if (options.ignoreFalse && val === false) continue;
if (options.ignoreTrue && val === true) continue;
if (!options.writeEmpty && val === "") continue;
if (!options.forceChain && propertyIndex === 0) this.writeNL_IfNotChain(route);
this.writeOPA();
this.writeTagKey(name);
this.writeColon();
this.writeTextOrValue(`${val}`, TextFormat.plainText, TextLocation.tag);
this.writeCL();
propertyIndex++;
}
}
}
}
}
}
writeInlineDebug(key, state) {
let tag = key;
if (state.open) {
tag = `<${key}>`;
} else if (state.close) {
tag = `</${key}>`;
} else if (state.single) {
tag = `<${key} />`;
}
this.writeString(tag);
}
//
// Helper functions
//
isEmptyText(text) {
if (!text) return true;
if (Array.isArray(text)) return text.length === 0;
return true;
}
isWriteTextFormat(bitsValue, textFormatDefault) {
const isDefault = Enum8(TextFormat).fromValue(bitsValue) === textFormatDefault;
const writeFormat = !isDefault || this.options.explicitTextFormat;
return !!writeFormat;
}
calculateIsCardAllowed() {
return this.isBodyBitmarkText && !this.isOfBitType1() && !this.isTableBitType();
}
isTableBitType() {
return this.isOfBitType([BitType.table]);
}
isOfBitType1() {
return this.isOfBitType([
BitType.trueFalse1,
BitType.multipleChoice1,
BitType.multipleResponse1
]);
}
isOfBitType(baseBitType) {
return instance2.isOfBitType(this.bitType, baseBitType);
}
//
// Writer interface
//
/**
* Writes a string value to the output.
* @param value - The string value to be written.
*/
write(value) {
this.writer.write(value);
return this;
}
/**
* Writes a new line to the output. The line is indented automatically. The line is ended with the endOfLineString.
* @param value - The line to write. When omitted, only the endOfLineString is written.
*/
writeLine(value) {
this.writer.writeLine(value);
return this;
}
/**
* Writes a collection of lines to the output. Each line is indented automatically and ended with the endOfLineString.
* @param values - The lines to write.
* @param delimiter - An optional delimiter to be written at the end of each line, except for the last one.
*/
writeLines(values, delimiter) {
this.writer.writeLines(values, delimiter);
return this;
}
/**
* Writes a single whitespace character to the output.
*/
writeWhiteSpace() {
this.writer.writeWhiteSpace();
return this;
}
};
// src/generator/bitmark/BitmarkStringGenerator.ts
var BitmarkStringGenerator = class {
/**
* Generate bitmark markup from a bitmark AST as a string
*
* @param bitmarkVersion - bitmark version, use null to use latest version
* @param options - bitmark generation options
*/
constructor(options) {
__publicField(this, "generator");
__publicField(this, "writer");
this.writer = new StringWriter();
this.generator = new BitmarkGenerator(this.writer, options);
}
/**
* Generate bitmark markup from bitmark AST as a string
*
* @param ast bitmark AST
*/
async generate(ast) {
await this.generator.generate(ast);
return this.writer.getString();
}
/**
* Generate bitmark markup from bitmark AST as a string synchronously
*
* @param ast bitmark AST
*/
generateSync(ast) {
this.generator.generateSync(ast);
return this.writer.getString();
}
};
// src/generator/html/HtmlTableGenerator.ts
var MARK_TO_TAG = {
bold: "b",
italic: "i",
superscript: "sup",
subscript: "sub",
strike: "s",
del: "del",
ins: "ins",
underline: "u",
highlight: "mark",
code: "code"
};
var INLINE_IMAGE_RE = /==([^=]*(?:=(?!=)[^=]*)*)==\|imageInline:([^|\s]+)((?:\|(?:@?[A-Za-z][A-Za-z0-9_-]*:[^|\s]+|#[^|\s]*))*)\|?/g;
var MAX_IMAGE_DIMENSION = 9999;
var HtmlTableGenerator = class {
constructor() {
__publicField(this, "indentUnit", " ");
}
/**
* Generate HTML table fragments from a string or JSON value.
* @returns the concatenated `<table>` fragments, blank-line separated (empty string if none).
*/
generate(input) {
let data = input;
if (typeof data === "string") {
try {
data = JSON.parse(data);
} catch {
return "";
}
}
const bits = this.collectBits(data);
const fragments = [];
for (const bit of bits) {
const html = this.renderTableBit(bit);
if (html) fragments.push(html);
}
return fragments.join("\n\n");
}
// --- bit collection -------------------------------------------------------
collectBits(data) {
const out = [];
const visit = (value) => {
if (value == null || typeof value !== "object") return;
if (Array.isArray(value)) {
value.forEach(visit);
return;
}
const obj = value;
if ("bit" in obj && obj.bit && typeof obj.bit === "object") {
out.push(obj.bit);
} else if ("type" in obj) {
out.push(obj);
}
};
visit(data);
return out;
}
// --- table rendering ------------------------------------------------------
renderTableBit(bit) {
const table = bit.table;
if (!table) return "";
if (bit.type !== "table" && bit.type !== "table-extended") return "";
const extended = this.toExtended(table);
const lines = [];
lines.push("<table>");
if (bit.caption != null) {
const caption = this.cellContentToHtml(this.asTextAst(bit.caption));
if (caption) lines.push(`${this.indentUnit}<caption>${caption}</caption>`);
}
this.renderSection(lines, "thead", extended.header, "header");
this.renderSection(lines, "tbody", extended.body, "body");
this.renderSection(lines, "tfoot", extended.footer, "footer");
lines.push("</table>");
return lines.join("\n");
}
toExtended(table) {
const t = table;
if (t.header || t.body || t.footer) return table;
return convertBasicToExtendedTableFormat(table);
}
renderSection(lines, tag, section, key) {
if (!section || !section.rows || section.rows.length === 0) return;
lines.push(`${this.indentUnit}<${tag}>`);
for (const row of section.rows) {
lines.push(`${this.indentUnit.repeat(2)}<tr>`);
for (const cell of row.cells ?? []) {
lines.push(`${this.indentUnit.repeat(3)}${this.renderCell(cell, key)}`);
}
lines.push(`${this.indentUnit.repeat(2)}</tr>`);
}
lines.push(`${this.indentUnit}</${tag}>`);
}
renderCell(cell, _key) {
const tag = cell.title === true ? "th" : "td";
const attrs = [];
if (cell.rowspan && cell.rowspan > 1) attrs.push(`rowspan="${cell.rowspan}"`);
if (cell.colspan && cell.colspan > 1) attrs.push(`colspan="${cell.colspan}"`);
if (cell.scope) attrs.push(`scope="${cell.scope}"`);
if (cell.colwidth && cell.colwidth > 0) attrs.push(`width="${cell.colwidth}"`);
const open = attrs.length > 0 ? `<${tag} ${attrs.join(" ")}>` : `<${tag}>`;
return `${open}${this.cellContentToHtml(this.asTextAst(cell.content))}</${tag}>`;
}
// --- cell content (TextAst -> HTML) ---------------------------------------
asTextAst(content) {
if (content == null) return [];
if (typeof content === "string") return [{ type: "text", text: content }];
return content;
}
cellContentToHtml(blocks) {
if (blocks.length === 0) return "";
if (blocks.length === 1 && blocks[0].type === "paragraph") {
return this.inlineToHtml(blocks[0].content ?? []);
}
return blocks.map((b) => this.blockToHtml(b)).join("");
}
blockToHtml(node) {
switch (node.type) {
case "paragraph":
return `<p>${this.inlineToHtml(node.content ?? [])}</p>`;
case "bulletList":
case "noBulletList":
case "orderedList":
case "orderedListRoman":
case "orderedListRomanLower":
case "letteredList":
case "letteredListLower":
case "taskList":
return this.listToHtml(node);
case "image":
case "imageInline":
return this.imageToHtml(node);
case "latex":
return this.latexToHtml(node);
default:
return this.inlineToHtml([node]);
}
}
inlineToHtml(nodes) {
return nodes.map((n) => this.inlineNodeToHtml(n)).join("");
}
inlineNodeToHtml(node) {
if (node.type === "hardBreak") return "<br>";
if (node.type === "image" || node.type === "imageInline") return this.imageToHtml(node);
if (node.type === "latex") return this.latexToHtml(node);
if (node.type === "text") {
return this.wrapMarks(this.textToHtml(node.text ?? ""), node.marks);
}
if (node.content) return this.inlineToHtml(node.content);
return this.escapeText(node.text ?? "");
}
wrapMarks(text, marks) {
if (!marks || marks.length === 0) return text;
let open = "";
let close = "";
for (const mark of marks) {
if (mark.type === "link") {
const href = mark.attrs?.href ?? "";
open += `<a href="${this.escapeAttr(href)}">`;
close = `</a>${close}`;
} else {
const tag = MARK_TO_TAG[mark.type];
if (tag) {
open += `<${tag}>`;
close = `</${tag}>${close}`;
}
}
}
return `${open}${text}${close}`;
}
// --- lists ----------------------------------------------------------------
listToHtml(node) {
const { tag, attrs } = this.listTag(node);
const items = (node.content ?? []).map((item) => this.listItemToHtml(item, node.type === "taskList")).join("");
return `<${tag}${attrs}>${items}</${tag}>`;
}
listTag(node) {
const start = node.attrs?.start;
const startAttr = typeof start === "number" && start !== 1 ? ` start="${start}"` : "";
switch (node.type) {
case "orderedList":
return { tag: "ol", attrs: startAttr };
case "orderedListRoman":
return { tag: "ol", attrs: ` type="I"${startAttr}` };
case "orderedListRomanLower":
return { tag: "ol", attrs: ` type="i"${startAttr}` };
case "letteredList":
return { tag: "ol", attrs: ` type="A"${startAttr}` };
case "letteredListLower":
return { tag: "ol", attrs: ` type="a"${startAttr}` };
case "noBulletList":
return { tag: "ul", attrs: ' style="list-style:none"' };
case "taskList":
case "bulletList":
default:
return { tag: "ul", attrs: "" };
}
}
listItemToHtml(item, task) {
const inner = (item.content ?? []).map((b) => {
if (b.type === "paragraph") return this.inlineToHtml(b.content ?? []);
return this.blockToHtml(b);
}).join("");
if (task) {
const checked = item.attrs?.checked ? " checked" : "";
return `<li><input type="checkbox"${checked}>${inner}</li>`;
}
return `<li>${inner}</li>`;
}
// --- images ---------------------------------------------------------------
imageToHtml(node) {
const attrs = node.attrs ?? {};
const parts = [];
const src = attrs.src;
parts.push(`src="${this.escapeAttr(typeof src === "string" ? src : "")}"`);
if (typeof attrs.alt === "string") parts.push(`alt="${this.escapeAttr(attrs.alt)}"`);
if (typeof attrs.title === "string") parts.push(`title="${this.escapeAttr(attrs.title)}"`);
if (attrs.width != null) parts.push(`width="${attrs.width}"`);
if (attrs.height != null) parts.push(`height="${attrs.height}"`);
if (typeof attrs.class === "string" && attrs.class) {
parts.push(`class="${this.escapeAttr(attrs.class)}"`);
}
return `<img ${parts.join(" ")}>`;
}
textToHtml(text) {
let html = "";
let lastIndex = 0;
for (const match of text.matchAll(INLINE_IMAGE_RE)) {
const index = match.index ?? 0;
html += this.escapeText(text.slice(lastIndex, index));
html += this.inlineImageTokenToHtml(match);
lastIndex = index + match[0].length;
}
html += this.escapeText(text.slice(lastIndex));
return html;
}
inlineImageTokenToHtml(match) {
const attrs = {
alt: match[1],
src: match[2]
};
const rawAttrs = match[3] ?? "";
for (const attr of rawAttrs.split("|")) {
if (!attr || attr.startsWith("#")) continue;
const colon = attr.indexOf(":");
if (colon < 1) continue;
const key = attr.slice(0, colon).replace(/^@/, "");
const value = attr.slice(colon + 1);
if (key === "width" || key === "height") {
const n = Number.parseInt(value, 10);
if (!Number.isNaN(n) && n > 0 && n <= MAX_IMAGE_DIMENSION) attrs[key] = n;
} else if (key === "class" || key === "title") {
attrs[key] = value;
}
}
return this.imageToHtml({ type: "imageInline", attrs });
}
// --- formulas -------------------------------------------------------------
latexToHtml(node) {
const formula = node.attrs?.formula ?? "";
return `<math alttext="${this.escapeAttr(formula)}"></math>`;
}
// --- escaping -------------------------------------------------------------
escapeText(text) {
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
}
escapeAttr(text) {
return this.escapeText(text).replace(/"/g, """);
}
};
// src/generator/json/JsonGenerator.ts
import { Enum as Enum9 } from "@ncoderz/superenum";
var MOVE_BODY_RECURSION_LIMIT = 5e3;
var DEFAULT_OPTIONS3 = {
// debugGenerationInline: true,
};
var JsonGenerator = class extends AstWalkerGenerator {
/**
* Generate bitmark JSON from a bitmark AST
*
* @param writer - destination for the output
* @param options - JSON generation options
*/
constructor(writer, options) {
super();
__publicField(this, "ast", new Ast());
__publicField(this, "textGenerator");
__publicField(this, "bitmarkVersion");
__publicField(this, "textParserVersion");
__publicField(this, "textParser", new TextParser());
// TODO - move to context
__publicField(this, "options");
__publicField(this, "jsonPrettifySpace");
__publicField(this, "writer");
// State
__publicField(this, "json", []);
__publicField(this, "bitWrapperJson", {});
__publicField(this, "bitJson", {});
__publicField(this, "textDefault", instance3.EMPTY_STRING);
__publicField(this, "bodyDefault", instance3.EMPTY_STRING);
__publicField(this, "bodyJson", this.bodyDefault);
__publicField(this, "listItem");
__publicField(this, "placeholderIndex", 0);
this.enter = this.enter.bind(this);
this.between = this.between.bind(this);
this.exit = this.exit.bind(this);
this.leaf = this.leaf.bind(this);
this.bodyBitCallback = this.bodyBitCallback.bind(this);
this.bitmarkVersion = Enum9(BitmarkVersion).fromValue(options?.bitmarkVersion) ?? DEFAULT_BITMARK_VERSION;
this.textParserVersion = this.textParser.version();
this.options = {
...DEFAULT_OPTIONS3,
...options?.jsonOptions
};
this.debugGenerationInline = this.options.debugGenerationInline ?? false;
this.jsonPrettifySpace = this.options.prettify === true ? 2 : this.options.prettify || void 0;
if (this.bitmarkVersion === BitmarkVersion.v2) {
if (this.options.textAsPlainText === void 0) {
this.options.textAsPlainText = true;
}
} else {
if (this.options.textAsPlainText === void 0) {
this.options.textAsPlainText = false;
}
}
this.textGenerator = new TextGenerator(BitmarkVersion.v2, {
// writeCallback: this.write,
bodyBitCallback: this.bodyBitCallback,
debugGenerationInline: this.debugGenerationInline
});
this.writer = writer;
this.generatePropertyHandlers();
}
/**
* Generate bitmark markup from bitmark AST
*
* @param ast bitmark AST
*/
async generate(ast) {
this.resetState();
await this.writer.open();
this.walkAndWrite(ast);
this.write(JSON.stringify(this.json, null, this.jsonPrettifySpace));
await this.writer.close();
}
/**
* Generate text from a bitmark text AST synchronously
*
* @param ast bitmark text AST
*/
generateSync(ast) {
this.resetState();
this.writer.openSync();
this.walkAndWrite(ast);
this.write(JSON.stringify(this.json, null, this.jsonPrettifySpace));
this.writer.closeSync();
}
resetState() {
this.json = [];
this.bitWrapperJson = {};
this.bitJson = {};
this.textDefault = this.options.textAsPlainText ? instance3.EMPTY_STRING : [];
this.bodyDefault = this.options.textAsPlainText ? instance3.EMPTY_STRING : [];
this.bodyJson = this.bodyDefault;
this.listItem = void 0;
this.placeholderIndex = 0;
this.printed = false;
}
walkAndWrite(ast) {
this.ast.walk(ast, NodeType.bitmarkAst, this, void 0);
}
//
// NODE HANDLERS
//
//
// Non-Terminal nodes (branches)
//
// bitmark
enter_bitmarkAst(_node, _route) {
this.json = [];
}
exit_bitmarkAst(_node, _route) {
if (this.options.textAsPlainText) {
this.convertAllBitmarkTextsToStringsForPlainText(this.json);
}
this.removeTemporaryProperties(this.json);
}
// bitmarkAst -> bits
// bitmarkAst -> bits -> bitsValue
enter_bitsValue(node, _route) {
const bit = node.value;
this.bitWrapperJson = {
//
};
this.json.push(this.bitWrapperJson);
this.bitJson = this.createBitJson(bit);
this.bitWrapperJson.bit = this.bitJson;
const bitConfig = instance2.getBitConfig(bit.bitType);
const textFormat = this.getTextFormat(_route);
const hasRootExample = !!bitConfig.rootExampleType;
const isBoolean = bitConfig.rootExampleType === ExampleType.boolean;
if (hasRootExample) {
let defaultExample;
if (isBoolean) {
defaultExample = true;
if (instance2.isOfBitType(bit.bitType, BitType.trueFalse1)) {
if (bit.cardNode?.statement?.isCorrect !== void 0) {
defaultExample = bit.cardNode.statement.isCorrect;
}
}
} else {
defaultExample = "true";
}
const exampleRes = this.toExample(bit, {
defaultExample,
isBoolean,
textFormat
});
this.bitJson.isExample = exampleRes.isExample;
this.bitJson.example = exampleRes.example;
} else if (bit.isExample) {
this.bitJson.isExample = true;
}
this.placeholderIndex = 0;
}
exit_bitsValue(_node, _route) {
const isExample = this.bitJson.isExample;
const example = this.bitJson.example;
delete this.bitJson.isExample;
delete this.bitJson.example;
this.bitJson.isExample = isExample;
this.bitJson.example = example;
this.cleanBitJson(this.bitJson);
}
// bitmarkAst -> bits -> bitsValue -> imageSource
enter_imageSource(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> person
enter_person(node, route) {
const bitType = this.getBitType(route);
let keyOverride = "person";
if (instance2.isOfBitType(bitType, BitType.conversationLeft1)) {
keyOverride = "partner";
}
return this.standardHandler(node, route, NodeType.bitsValue, { array: false, keyOverride });
}
// bitmarkAst -> bits -> bitsValue -> ratingLevelStart
enter_ratingLevelStart(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> ratingLevelEnd
enter_ratingLevelEnd(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> sourceBB
enter_sourceBB(node, route) {
const tuples = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return false;
if (tuples && tuples.length > 0) {
this.bitJson.sourceBB = tuples.length === 1 ? tuples[0] : tuples;
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> productId
enter_productId(node, route) {
const productIds = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
const bitType = this.getBitType(route);
if (bitType === BitType.module) {
this.addProperty(this.bitJson, "productId", productIds, { array: true });
} else if (productIds.length > 0) {
this.addProperty(this.bitJson, "productId", productIds[productIds.length - 1], {
array: false
});
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> isCollapsible
/**
* PLAN-016: `isCollapsible` is emitted only when true. `false` is the implied
* default for every bit, so it is never written to the JSON — an absent key
* means the bit's configured default (`true` for the deprecated
* `*-collapsible` bit types, `false` everywhere else).
*
* Defining these handlers suppresses the generic property handlers that
* `generatePropertyHandlers()` would otherwise install for this tag.
*/
leaf_isCollapsible(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return false;
if (node.value === true) this.bitJson.isCollapsible = true;
return false;
}
enter_isCollapsible(node, route) {
return this.leaf_isCollapsible(node, route);
}
// bitmarkAst -> bits -> bitsValue -> markConfig
enter_markConfig(_node, _route) {
return true;
}
// bitmarkAst -> bits -> bitsValue -> markConfig -> markConfigValue
enter_markConfigValue(node, route) {
const markJson = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.markConfig) return true;
if (!this.bitJson.marks) this.bitJson.marks = [];
this.bitJson.marks.push(markJson);
return false;
}
// bitmarkAst -> bits -> bitsValue -> item
enter_item(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> lead
enter_lead(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> pageNumber
enter_pageNumber(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> marginNumber
enter_marginNumber(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> hint
enter_hint(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> instruction
enter_instruction(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> titleString
leaf_titleString(node, route) {
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return true;
return this.standardHandler(node, route, [NodeType.bitsValue], {
array: false,
keyOverride: "title"
});
}
// bitmarkAst -> bits -> bitsValue -> title
enter_title(node, route) {
return this.standardHandler(node, route, [NodeType.bitsValue, NodeType.cardNode], {
array: true
});
}
// bitmarkAst -> bits -> bitsValue -> subtitle
enter_subtitle(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> caption
enter_caption(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> width
enter_width(node, route) {
let value = node.value;
const bitType = this.getBitType(route);
const parent = this.getParentNode(route);
if (parent?.key === NodeType.bitsValue && instance2.isOfBitType(bitType, BitType.extractorBlock)) {
value = instance6.asNumber(value, 0) ?? 0;
}
return this.standardHandler(node, route, void 0, { array: false, valueOverride: value });
}
// bitmarkAst -> bits -> bitsValue -> height
enter_height(node, route) {
let value = node.value;
const bitType = this.getBitType(route);
const parent = this.getParentNode(route);
if (parent?.key === NodeType.bitsValue && instance2.isOfBitType(bitType, BitType.extractorBlock)) {
value = instance6.asNumber(value, 0) ?? 0;
}
return this.standardHandler(node, route, void 0, { array: false, valueOverride: value });
}
// bitmarkAst -> bits -> bitsValue -> extraProperties
enter_extraProperties(node, _route) {
const extraProperties = node.value;
if (!this.options.excludeUnknownProperties && extraProperties) {
for (const [key, values] of Object.entries(extraProperties)) {
let k = key;
if (Object.prototype.hasOwnProperty.call(this.bitJson, key)) {
k = `_${key}`;
}
this.addProperty(this.bitJson, k, values, { array: true });
}
}
return false;
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> cardBits -> cardBitsValue
enter_cardBitsValue(node, route) {
const cardBit = node.value;
const bitType = this.getBitType(route);
if (!bitType) return;
let listItems;
if (bitType === BitType.bookReferenceList) {
if (!this.bitJson.bookReferences) this.bitJson.bookReferences = [];
const bookReference = {};
this.addProperty(bookReference, "lang", cardBit.lang, { array: false });
this.addProperty(bookReference, "refAuthor", cardBit.refAuthor ?? [], { array: true });
this.addProperty(bookReference, "refBookTitle", cardBit.refBookTitle ?? [], { array: false });
this.addProperty(bookReference, "refPublisher", cardBit.refPublisher ?? [], { array: true });
this.addProperty(bookReference, "refPublicationYear", cardBit.refPublicationYear ?? [], {
array: false
});
this.addProperty(bookReference, "citationStyle", cardBit.citationStyle ?? [], {
array: false
});
this.bitJson.bookReferences.push(bookReference);
} else {
if (bitType === BitType.pageFooter) {
if (!this.bitJson.sections) this.bitJson.sections = [];
listItems = this.bitJson.sections;
} else {
if (!this.bitJson.listItems) this.bitJson.listItems = [];
listItems = this.bitJson.listItems;
}
this.listItem = {
item: cardBit.item ?? [],
lead: cardBit.lead ?? [],
hint: cardBit.hint ?? [],
instruction: cardBit.instruction ?? [],
// ...this.toItemLeadHintInstruction(node.value),
body: this.bodyDefault
};
listItems.push(this.listItem);
}
}
exit_cardBitsValue(_node, _route) {
this.listItem = void 0;
}
// bitmarkAst -> bits -> bitsValue -> body
enter_body(node, route) {
const value = node.value;
const parent = this.getParentNode(route);
if (!parent) return false;
const textFormat = this.getTextFormat(route);
const isBitmarkText = textFormat === TextFormat.bitmarkText;
this.bodyJson = value.body;
if (parent.key === NodeType.bitsValue) {
this.bitJson.body = this.bodyJson;
const bodyIsBitmarkText = isBitmarkText && this.isBitmarkText(this.bodyJson);
if (this.options.textAsPlainText && bodyIsBitmarkText) {
const textBody = this.textGenerator.generateSync(
this.bodyJson,
textFormat,
TextLocation.body,
{
noBreakscaping: true
}
);
this.bitJson.body = (textBody ?? "").trim();
} else if (bodyIsBitmarkText) {
this.bitJson.body = this.moveBodyBitPropertiesToAttrs(this.bodyJson);
}
} else if (parent.key === NodeType.cardBitsValue) {
if (this.listItem) this.listItem.body = this.bodyJson;
}
return false;
}
bodyBitCallback(bodyBit, _index, _route) {
const placeholder = `{${this.placeholderIndex}}`;
this.placeholderIndex++;
this.bitJson.placeholders = this.bitJson.placeholders ?? {};
this.bitJson.placeholders[placeholder] = bodyBit;
return placeholder;
}
/**
* Move all properties of body bits to the attrs property.
* This function is called recursively to process all body bits in the body tree.
*
* NOTE: Internally, the body bit is stored as it appears in bitmark v2, but in v3 all the properties
* should be in the `attrs` property. The properies are only moved to 'attrs' for the JSON output
*
* The original body bit is not modified.
* The function returns a new body bit with the properties moved.
*
* @param nodes the body to process, or the subtree to process
* @param recursion leave as default, used for recursion only
* @returns
*/
moveBodyBitPropertiesToAttrs(nodes, recursion = 0) {
if (recursion === 0) {
nodes = structuredClone(nodes);
} else if (recursion > MOVE_BODY_RECURSION_LIMIT) {
throw new Error("Recursion limit exceeded");
}
for (const node of nodes) {
if (node.type !== BodyBitType.text && Enum9(BodyBitType).values().includes(node.type)) {
const bodyBit = node;
bodyBit.attrs = {};
for (const [key, value] of Object.entries(bodyBit)) {
if (key === "type" || key === "attrs") continue;
bodyBit.attrs[key] = value;
delete bodyBit[key];
}
} else if (Array.isArray(node.content)) {
recursion++;
this.moveBodyBitPropertiesToAttrs(node.content, recursion);
}
}
return nodes;
}
// bitmarkAst -> bits -> bitsValue -> footer
enter_footer(node, route) {
return this.standardHandler(node, route, void 0, {
array: true,
valueOverride: node.value.footer
});
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> elements
enter_elements(node, route) {
return this.standardHandler(node, route, void 0, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> flashcards
enter_flashcards(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, {
array: true,
keyOverride: "cards"
});
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> definitions
enter_definitions(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> statement
enter_statement(node, route) {
const statement = node.value;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.cardNode) return;
if (statement) {
this.bitJson.statement = statement.statement ?? [];
this.bitJson.isCorrect = statement.isCorrect ?? false;
this.bitJson.example = statement.example;
this.bitJson.isExample = statement.isExample;
}
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> statements -> statementsValue
enter_statements(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> choices
// X bitmarkAst -> bits -> bitsValue -> cardNode -> quizzes -> quizzesValue -> choices
// X bitmarkAst -> bits -> bitsValue -> cardNode -> feedbacks -> feedbacksValue -> choices
enter_choices(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> responses
// X bitmarkAst -> bits -> bitsValue -> cardNode -> quizzes -> quizzesValue -> responses
enter_responses(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> feedbacks
enter_feedbacks(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> quizzes
enter_quizzes(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> heading
enter_heading(node, route) {
return this.standardHandler(node, route, void 0, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> pairs
enter_pairs(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> matrix
enter_matrix(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> pronunciationTable
enter_pronunciationTable(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> table
enter_table(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> questions
enter_questions(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> botResponses
enter_botResponses(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, {
array: true,
keyOverride: "responses"
});
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> ingredients -> ingredientsValue
enter_ingredients(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> cardNode -> captionDefinitionList
enter_captionDefinitionList(node, route) {
return this.standardHandler(node, route, NodeType.cardNode, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> backgroundWallpaper
enter_backgroundWallpaper(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> imagePlaceholder
enter_imagePlaceholder(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> previewImage
enter_previewImage(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> previewVideo
enter_previewVideo(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> platformIcon
enter_platformIcon(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> platformLogo
enter_platformLogo(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> platformBackgroundImage
enter_platformBackgroundImage(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> coverImage
enter_coverImage(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, {
array: Array.isArray(node.value)
});
}
// bitmarkAst -> bits -> bitsValue -> backgroundImage
enter_backgroundImage(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, {
array: Array.isArray(node.value)
});
}
// bitmarkAst -> bits -> bitsValue -> resources
enter_resources(node, route) {
const resources = node.value;
const bitType = this.getBitType(route);
const resourceType = this.getResourceType(route);
if (!resources || !bitType) return true;
let resourceJson;
const bitConfig = instance2.getBitConfig(bitType);
const bitResourcesConfig = instance2.getBitResourcesConfig(bitType, resourceType);
const comboMap = bitResourcesConfig.comboResourceConfigKeysMap;
if (comboMap.size > 0) {
for (const [comboConfigKey, resourceConfigKeys] of comboMap.entries()) {
const comboTagType = configKeyToResourceType(comboConfigKey);
const wrapper = {
type: comboTagType,
__typeAlias: comboTagType,
__configKey: comboConfigKey
};
for (const ck of resourceConfigKeys) {
const r = resources.find((r2) => r2.__configKey === ck);
if (r) {
const tagConfig = instance2.getTagConfigForTag(bitConfig.tags, r.__configKey);
if (tagConfig) {
const key = tagConfig.jsonKey ?? tagConfig.tag;
const configKey = tagConfig.configKey ?? r.__configKey;
const json = r.__configKey === configKey ? r : void 0;
if (json) {
for (const [k, v] of Object.entries(json)) {
if (k !== "type") {
wrapper[key] = v;
}
}
}
}
}
}
resourceJson = wrapper;
}
} else if (instance2.isOfBitType(bitType, [
BitType.imagesLogoGrave,
BitType.prototypeImages,
BitType.extractorImage
])) {
const images = [];
for (const r of resources) {
if (r.type === ResourceType.image) {
images.push(r);
}
}
if (bitType === BitType.imagesLogoGrave) {
this.bitJson.logos = images;
} else {
this.bitJson.images = images;
}
} else {
if (resources.length >= 1) {
resourceJson = resources[0];
}
}
this.bitJson.resource = resourceJson;
return false;
}
// bitmarkAst -> bits -> bitsValue -> level
leaf_level(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> book (array)
enter_book(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: true });
}
// bitmarkAst -> bits -> bitsValue -> book (single)
leaf_book(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> anchor
leaf_anchor(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> reference
leaf_reference(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> referenceEnd
leaf_referenceEnd(node, route) {
return this.standardHandler(node, route, NodeType.bitsValue, { array: false });
}
// bitmarkAst -> bits -> bitsValue -> markup
leaf_markup(node, _route) {
const bitmark = node.value;
if (bitmark) this.bitWrapperJson.bitmark = bitmark;
return false;
}
// bitmarkAst -> bits -> bitsValue -> * -> internalComment
enter_internalComment(_node, _route) {
return false;
}
// bitmarkAst -> bits -> bitsValue -> parser
enter_parser(node, route) {
const parser2 = node.value;
const bitType = this.getBitType(route);
const parent = this.getParentNode(route);
if (parser2 && bitType) {
const { version, warnings, errors, ...parserRest } = parser2;
const bitmarkVersion = `${this.bitmarkVersion}`;
const textParserVersion = this.textParserVersion;
const internalComments = this.getInternalComments(route);
if (parent?.key === NodeType.bitsValue) {
this.bitWrapperJson.parser = {
version,
bitmarkVersion,
textParserVersion,
internalComments,
...parserRest,
warnings,
errors
};
if (!this.options.enableWarnings) {
delete this.bitWrapperJson.parser.warnings;
}
} else {
}
}
}
// bitmarkAst -> errors
// protected enter_errors(node: NodeInfo, _route: NodeInfo[],
// context: Context): void {
// const errors = node.value as ParserError[] | undefined;
// if (errors && errors.length > 0) {
// // Complete bit is invalid
// // TODO - not sure where this error can be written
// // this.bitWrapperJson.parser = {
// // errors,
// // };
// }
// }
//
// Generated Node Handlers
//
/**
* Generate the handlers for properties, as they are mostly the same, but not quite
*/
generatePropertyHandlers() {
for (const propertyConfigKey of Enum9(PropertyKey).values()) {
const propertyTag = configKeyToPropertyType(propertyConfigKey);
const funcNames = [`enter_${propertyTag}`, `leaf_${propertyTag}`];
for (const funcName of funcNames) {
if (typeof this[funcName] === "function") {
continue;
}
if (propertyTag === "example") continue;
this[funcName] = (node, route) => {
const value = node.value;
if (value == null) return;
const parent = this.getParentNode(route);
if (parent?.key !== NodeType.bitsValue) return;
this.bitJson[propertyTag] = value;
};
this[funcName] = this[funcName].bind(this);
}
}
}
// // END NODE HANDLERS
//
// HELPER FUNCTIONS
//
/**
* Default handler for properties.
*
* @param node the node
* @param route the route to the node
* @param parentNodeTypes the parent node types for which to handle the node
* @returns
*/
standardHandler(node, route, parentNodeTypes, options) {
if (parentNodeTypes) {
const nodeTypeArray = Array.isArray(parentNodeTypes) ? parentNodeTypes : [parentNodeTypes];
const parent = this.getParentNode(route);
if (!parent?.key || !nodeTypeArray.includes(parent?.key)) return true;
}
this.addProperty(
this.bitJson,
options.keyOverride ?? node.key,
options.valueOverride ?? node.value,
options
);
return false;
}
addProperty(target, name, values, options) {
if (values !== void 0) {
let finalValue;
if (!Array.isArray(values)) values = [values];
const valuesArr = values;
if (!options.array && valuesArr.length >= 1) {
finalValue = valuesArr[valuesArr.length - 1];
} else {
finalValue = valuesArr;
}
if (options.allowNull || finalValue != null) {
target[name] = finalValue;
}
}
}
toExample(node, options) {
const { isExample, example, __isDefaultExample } = node;
const { defaultExample, isBoolean } = options;
if (!isExample) {
return {
isExample: false,
example: null
};
}
let exampleValue;
if (__isDefaultExample) {
exampleValue = isBoolean ? instance5.toBoolean(defaultExample) : this.convertBreakscapedStringToJsonText(
defaultExample,
options.textFormat,
TextLocation.tag
);
} else {
exampleValue = isBoolean ? instance5.toBoolean(example) : this.convertBreakscapedStringToJsonText(
example,
options.textFormat,
TextLocation.tag
);
}
return {
isExample: true,
example: exampleValue
};
}
/**
* Get the bit type from any node
*
* @param route the route to the current node
* @returns the bit type
*/
getBitType(route) {
for (const node of route) {
if (node.key === NodeType.bitsValue) {
const n = node.value;
return n?.bitType;
}
}
return void 0;
}
/**
* Get the text format from any node
*
* @param route the route to the current node
* @returns the text format
*/
getTextFormat(route) {
const bitType = this.getBitType(route);
if (bitType) {
const bitConfig = instance2.getBitConfig(bitType);
for (const node of route) {
if (node.key === NodeType.bitsValue) {
const n = node.value;
return Enum9(TextFormat).fromValue(n?.textFormat) ?? bitConfig.textFormatDefault;
}
}
}
return TextFormat.bitmarkText;
}
/**
* Get the bit resourceType atttachment from any node
*
* @param route the route to the current node
* @returns the bit type
*/
getResourceType(route) {
for (const node of route) {
if (node.key === NodeType.bitsValue) {
const n = node.value;
return n?.resourceType;
}
}
return void 0;
}
/**
* Get the internal comments from any node
*
* @param route the route to the current node
* @returns the text format
*/
getInternalComments(route) {
const bitType = this.getBitType(route);
if (bitType) {
for (const node of route) {
if (node.key === NodeType.bitsValue) {
const n = node.value;
return n.internalComment;
}
}
}
return void 0;
}
/**
* Convert the text from the AST to the JSON format:
* Input:
* - breakscaped string
* Output:
* - text: plain text
* - json: bitmark text JSON
* - Bitmark v2: breakscaped string
* - Bitmark v3: bitmark text JSON (TextAst)
*
* In the case of Bitmark v2 type texts, there is nothing to do but cast the type.
*
* @param text
* @returns
*/
convertBreakscapedStringToJsonText(text, format, textLocation) {
if (!text) return "";
const isBitmarkText = format === TextFormat.bitmarkText;
if (!isBitmarkText) {
return text || "";
}
const asPlainText = this.options.textAsPlainText;
if (asPlainText) {
return text || "";
}
const textAst = this.textParser.toAst(text, {
format,
location: textLocation
});
return textAst;
}
/**
* Concatenates a plain JSON text with a JsonText that may be plain (v2) or BitmarkText (v3)
* Returns the combined text.
*
* @param text the text to concatenate
* @param textPlain the plain text to concatenate
*/
concatenatePlainTextWithJsonTexts(text, extraBreaks, textPlain) {
if (Array.isArray(text)) {
textPlain = textPlain.trim();
if (textPlain) {
const splitText = textPlain.split("\n");
const content = [];
for (let i = 0; i < extraBreaks; i++) {
content.push({
type: TextNodeType.hardBreak
});
}
for (let i = 0; i < splitText.length; i++) {
const t = splitText[i];
if (t) {
content.push({
text: t,
type: TextNodeType.text
});
}
if (i < splitText.length - 1) {
content.push({
type: TextNodeType.hardBreak
});
}
}
const lastNode = text[text.length - 1];
if (lastNode && lastNode.type === TextNodeType.paragraph) {
lastNode.content = [...lastNode.content ?? [], ...content];
} else {
text.push({
type: TextNodeType.paragraph,
content,
attrs: {}
});
}
}
return text;
}
return `${text ?? ""}${"\n".repeat(extraBreaks)}${textPlain ?? ""}`;
}
/**
* Check if an object is bitmark text
* The check looks for a special tag on the array (__tag: 'text')
*
* @param obj object that might be bitmark text
* @returns
*/
isBitmarkText(obj) {
if (obj == null) return false;
if (!Array.isArray(obj)) return false;
if (obj.__tag === "text") return true;
return false;
}
//
// WRITE FUNCTIONS
//
writeInlineDebug(key, state) {
let tag = key;
if (state.open) {
tag = `<${key}>`;
} else if (state.close) {
tag = `</${key}>`;
} else if (state.single) {
tag = `<${key} />`;
}
this.writeString(tag);
}
writeString(s) {
if (s != null) this.write(`${s}`);
}
/**
* Create a new bit json object.
* - This function defines the order of the properties in the json.
*
* @param bit
* @returns
*/
createBitJson(bit) {
const format = bit.textFormat;
const bitJson = {
type: bit.isCommented ? BitType._comment : bit.bitType,
originalType: bit.isCommented ? bit.bitType : void 0,
format,
bitLevel: bit.bitLevel
};
return bitJson;
}
/**
* Remove wanted properties from bit json object.
* - This function defines the defaults for properties in the json.
*
* TODO: All these defaults should come from the config and be set in the Builder.
*
* @param bit
* @returns
*/
cleanBitJson(bitJson) {
const bitType = instance2.getBitType(bitJson.type);
if (bitJson.originalType == null) bitJson.originalType = void 0;
if (!bitJson.item) bitJson.item = void 0;
if (instance2.isOfBitType(bitType, [BitType._error, BitType._comment])) {
delete bitJson.format;
delete bitJson.item;
delete bitJson.lead;
delete bitJson.pageNumber;
delete bitJson.marginNumber;
delete bitJson.hint;
delete bitJson.instruction;
} else {
let isTopLevelExample = false;
let isTopLevelExampleValue = false;
if (instance2.isOfBitType(bitType, [BitType.page])) {
if (bitJson.item == null || bitJson.item?.length === 0) delete bitJson.item;
if (bitJson.lead == null || bitJson.lead?.length === 0) delete bitJson.lead;
if (bitJson.pageNumber == null || bitJson.pageNumber?.length === 0)
delete bitJson.pageNumber;
if (bitJson.marginNumber == null || bitJson.marginNumber?.length === 0)
delete bitJson.marginNumber;
if (bitJson.hint == null || bitJson.hint?.length === 0) delete bitJson.hint;
if (bitJson.instruction == null || bitJson.instruction?.length === 0)
delete bitJson.instruction;
} else {
if (bitJson.item == null) bitJson.item = this.textDefault;
if (bitJson.lead == null) bitJson.lead = this.textDefault;
if (bitJson.hint == null) bitJson.hint = this.textDefault;
if (bitJson.instruction == null) bitJson.instruction = this.textDefault;
if (bitJson.pageNumber == null) bitJson.pageNumber = this.textDefault;
if (bitJson.marginNumber == null) bitJson.marginNumber = this.textDefault;
}
if (bitJson.body == null) bitJson.body = this.bodyDefault;
if (instance2.isOfBitType(bitType, [BitType.article])) {
}
if (instance2.isOfBitType(bitType, [BitType.example])) {
isTopLevelExample = true;
isTopLevelExampleValue = true;
}
if (instance2.isOfBitType(bitType, [
BitType.multipleChoice1,
BitType.multipleResponse1,
BitType.multipleChoiceText,
BitType.highlightText,
BitType.gapText,
BitType.gapTextInstructionGrouped,
BitType.clozeAndMultipleChoiceText,
BitType.sequence,
BitType.mark,
BitType.flashcard,
BitType.definitionList
])) {
isTopLevelExample = true;
if (bitJson.body == null) bitJson.body = this.bodyDefault;
}
if (instance2.isOfBitType(bitType, [BitType.sequence])) {
isTopLevelExample = true;
isTopLevelExampleValue = true;
if (bitJson.body == null) bitJson.body = this.bodyDefault;
}
if (instance2.isOfBitType(bitType, BitType.cloze)) {
isTopLevelExample = true;
if (instance2.isOfBitType(bitType, BitType.clozeSolutionGrouped)) {
if (bitJson.quizCountItems == null) bitJson.quizCountItems = true;
if (bitJson.quizStrikethroughSolutions == null) bitJson.quizStrikethroughSolutions = true;
} else if (instance2.isOfBitType(bitType, BitType.clozeInstructionGrouped)) {
if (bitJson.quizCountItems == null) bitJson.quizCountItems = true;
if (bitJson.quizStrikethroughSolutions == null)
bitJson.quizStrikethroughSolutions = false;
}
}
if (instance2.isOfBitType(bitType, [BitType.multipleChoice, BitType.multipleResponse])) {
isTopLevelExample = true;
if (bitJson.body == null) bitJson.body = this.bodyDefault;
if (bitJson.footer == null) bitJson.footer = this.textDefault;
}
if (instance2.isOfBitType(bitType, BitType.essay)) {
isTopLevelExample = true;
isTopLevelExampleValue = true;
if (bitJson.body == null) bitJson.body = this.bodyDefault;
if (bitJson.partialAnswer == null) bitJson.partialAnswer = "";
}
if (instance2.isOfBitType(bitType, BitType.trueFalse1)) {
isTopLevelExample = true;
isTopLevelExampleValue = true;
if (bitJson.isCorrect == null) bitJson.isCorrect = false;
if (bitJson.body == null) bitJson.body = this.bodyDefault;
}
if (instance2.isOfBitType(bitType, BitType.trueFalse)) {
isTopLevelExample = true;
if (bitJson.labelFalse == null) bitJson.labelFalse = "";
if (bitJson.labelTrue == null) bitJson.labelTrue = "";
if (bitJson.body == null) bitJson.body = this.bodyDefault;
}
if (instance2.isOfBitType(bitType, BitType.chapter)) {
if (bitJson.toc == null) bitJson.toc = true;
if (bitJson.progress == null) bitJson.progress = true;
if (bitJson.level == null) bitJson.level = 1;
if (bitJson.body == null) bitJson.body = this.bodyDefault;
}
if (instance2.isOfBitType(bitType, BitType.interview)) {
isTopLevelExample = true;
if (bitJson.body == null) bitJson.body = this.bodyDefault;
if (bitJson.footer == null) bitJson.footer = this.textDefault;
if (bitJson.questions == null) bitJson.questions = [];
}
if (instance2.isOfBitType(bitType, BitType.sequence)) {
if (bitJson.elements == null) bitJson.elements = [];
}
if (bitType === BitType.matchMatrix) {
isTopLevelExample = true;
}
if (instance2.isOfBitType(bitType, BitType.match)) {
isTopLevelExample = true;
if (bitJson.heading == null) bitJson.heading = {};
}
if (instance2.isOfBitType(bitType, BitType.learningPathBook)) {
if (bitJson.isTracked == null) bitJson.isTracked = true;
if (bitJson.isInfoOnly == null) bitJson.isInfoOnly = false;
}
const tableImageHasTable = instance2.isOfBitType(bitType, BitType.tableImage) && !this.tableIsEmpty(bitJson.table);
if (instance2.isOfBitType(bitType, BitType.table) && !instance2.isOfBitType(bitType, BitType.tableImage) || tableImageHasTable) {
if (bitJson.tableFixedHeader == null) bitJson.tableFixedHeader = false;
if (bitJson.tableHeaderWhitespaceNoWrap == null) {
bitJson.tableHeaderWhitespaceNoWrap = false;
}
if (bitJson.tableSearch == null) bitJson.tableSearch = false;
if (bitJson.tableSort == null) bitJson.tableSort = false;
if (bitJson.tablePagination == null) bitJson.tablePagination = false;
if (bitJson.tablePaginationLimit == null) bitJson.tablePaginationLimit = 0;
if (bitJson.tableHeight == null) bitJson.tableHeight = 0;
if (bitJson.tableWhitespaceNoWrap == null) bitJson.tableWhitespaceNoWrap = false;
if (bitJson.tableAutoWidth == null) bitJson.tableAutoWidth = true;
if (bitJson.tableResizableColumns == null) bitJson.tableResizableColumns = false;
if (bitJson.tableColumnMinWidth == null) bitJson.tableColumnMinWidth = 0;
}
if (instance2.isOfBitType(bitType, BitType.tableImage) && !tableImageHasTable) {
delete bitJson.table;
}
if (instance2.isOfBitType(bitType, BitType.bookReference)) {
if (bitJson.refAuthor == null) bitJson.refAuthor = [];
if (bitJson.refBookTitle == null) bitJson.refBookTitle = "";
if (bitJson.refPublisher == null) bitJson.refPublisher = [];
if (bitJson.refPublicationYear == null) bitJson.refPublicationYear = "";
}
if (instance2.isOfBitType(bitType, [
BitType.pageBanner,
BitType.pageBuyButton,
BitType.pageBuyButtonAlt,
BitType.pageBuyButtonPromotion,
BitType.pageFooter,
BitType.pageOpenBook,
BitType.pagePerson,
BitType.printBook,
BitType.pageProduct,
BitType.pageProductList,
BitType.pageProductVideo,
BitType.pageProductVideoList,
BitType.pageSectionFolder,
BitType.pageSubscribe,
BitType.pageSubpage
])) {
if (bitJson.slug == null) bitJson.slug = "";
if (bitJson.body == null) bitJson.body = this.bodyDefault;
if (instance2.isOfBitType(bitType, BitType.pageBuyButton)) {
if (bitJson.content2Buy == null) bitJson.content2Buy = "";
}
}
if (instance2.isOfBitType(bitType, BitType.tocResource)) {
if (bitJson.tocResource == null) bitJson.tocResource = [];
}
if (instance2.isOfBitType(bitType, BitType.tocContent)) {
if (bitJson.tocContent == null) bitJson.tocContent = [];
}
if (instance2.isOfBitType(bitType, BitType.printThisChapter)) {
if (bitJson.printParentChapterLevel == null) bitJson.printParentChapterLevel = -1;
}
if (instance2.isOfBitType(bitType, BitType.book)) {
if (bitJson.maxTocChapterLevel == null) bitJson.maxTocChapterLevel = -1;
if (bitJson.allowPrint == null) bitJson.allowPrint = "useSpaceConfiguration";
if (bitJson.hasPrintRestriction == null) bitJson.hasPrintRestriction = true;
if (bitJson.enforceUpdateOverUserInput == null) bitJson.enforceUpdateOverUserInput = false;
if (bitJson.hasMarkAsDone == null) bitJson.hasMarkAsDone = false;
if (bitJson.processHandIn == null) bitJson.processHandIn = false;
if (bitJson.isPublic == null) bitJson.isPublic = false;
if (bitJson.chatWithBook == null) bitJson.chatWithBook = false;
if (bitJson.chatWithBookBrainKey == null) bitJson.chatWithBookBrainKey = "";
}
if (bitType === BitType.articleAi || bitType === BitType.noteAi || bitType === BitType.summaryAi) {
if (bitJson.aiGenerated == null) bitJson.aiGenerated = true;
}
if (instance2.isOfBitType(bitType, [BitType.articleResponsive, BitType.pageArticleResponsive])) {
if (bitJson.imageFirst == null) bitJson.imageFirst = true;
}
if (instance2.isOfBitType(bitType, BitType.reviewNote)) {
if (bitJson.resolved == null) bitJson.resolved = false;
if (bitJson.resolvedDate == null) bitJson.resolvedDate = "";
if (bitJson.resolvedBy == null) bitJson.resolvedBy = "";
}
if (instance2.isOfBitType(bitType, [
BitType.imagesLogoGrave,
BitType.prototypeImages,
BitType.extractorImage
])) {
if (bitType === BitType.imagesLogoGrave) {
if (bitJson.logos == null) {
bitJson.logos = [];
}
} else {
if (bitJson.images == null) bitJson.images = [];
}
}
if (instance2.isOfBitType(bitType, [BitType.stepImageScreenshotWithPointer, BitType.surveyMatrix])) {
if (bitJson.pointerTop == null) bitJson.pointerTop = "";
if (bitJson.pointerLeft == null) bitJson.pointerLeft = "";
if (instance2.isOfBitType(bitType, [BitType.surveyMatrix])) {
if (bitJson.buttonCaption == null) bitJson.buttonCaption = "";
}
}
if (instance2.isOfBitType(bitType, [BitType.listItem])) {
if (bitJson.listItemIndent == null) bitJson.listItemIndent = 0;
}
if (instance2.isOfBitType(bitType, BitType.surveyRating)) {
if (bitJson.ratingLevelStart == null) {
bitJson.ratingLevelStart = {
level: 0
};
}
if (bitJson.ratingLevelEnd == null) {
bitJson.ratingLevelEnd = {
level: 0
};
}
}
if (instance2.isOfBitType(bitType, BitType.vendorStripePricingTable)) {
if (bitJson.stripePricingTableId == null) bitJson.stripePricingTableId = "";
if (bitJson.stripePublishableKey == null) bitJson.stripePublishableKey = "";
if (instance2.isOfBitType(bitType, BitType.vendorStripePricingTablePrintOnRequest)) {
if (bitJson.printOnRequestCustomerId == null) bitJson.printOnRequestCustomerId = "";
if (bitJson.printOnRequestProductId == null) bitJson.printOnRequestProductId = "";
}
}
if (instance2.isOfBitType(bitType, BitType.callToAction)) {
if (bitJson.buttonCaption == null) bitJson.buttonCaption = "";
if (bitJson.callToActionUrl == null) bitJson.callToActionUrl = "";
}
if (instance2.isOfBitType(bitType, [BitType.handInFile, BitType.handInSystemMedia])) {
if (bitJson.handInAcceptFileType == null) bitJson.handInAcceptFileType = [];
}
if (instance2.isOfBitType(bitType, BitType.handInSubmit)) {
if (bitJson.handInRequirement == null) bitJson.handInRequirement = [];
if (bitJson.handInInstruction == null) bitJson.handInInstruction = "";
if (bitJson.buttonCaption == null) bitJson.buttonCaption = "";
}
if (instance2.isOfBitType(bitType, BitType.module)) {
if (bitJson.hasBookNavigation == null) bitJson.hasBookNavigation = true;
}
if (instance2.isOfBitType(bitType, BitType.container)) {
if (bitJson.allowedBit == null) bitJson.allowedBit = [];
}
if (instance2.isOfBitType(bitType, BitType.platformPath)) {
if (bitJson.path == null) bitJson.path = "";
}
if (instance2.isOfBitType(bitType, BitType.platformBrandTarget)) {
if (bitJson.platformBrandTarget == null) bitJson.platformBrandTarget = "none";
}
if (instance2.isOfBitType(bitType, BitType.platformHeader)) {
if (bitJson.platformLogoMaxHeight == null) bitJson.platformLogoMaxHeight = 40;
}
if (isTopLevelExample) {
if (bitJson.isExample == null) bitJson.isExample = false;
} else {
delete bitJson.isExample;
delete bitJson.example;
}
if (isTopLevelExampleValue) {
if (bitJson.example == null) bitJson.example = null;
} else {
delete bitJson.example;
}
}
return bitJson;
}
tableIsEmpty(table) {
if (!table) return true;
if (Object.keys(table).length === 0) return true;
const tableStandard = table;
if (tableStandard.columns?.length === 0 && tableStandard.data?.length) {
return true;
}
const tableExtended = table;
if (tableExtended.header?.rows.length === 0 && tableExtended.body?.rows.length === 0 && tableExtended.footer?.rows.length === 0) {
return true;
}
return false;
}
/**
* Convert any bitmark texts to strings.
*/
convertAllBitmarkTextsToStringsForPlainText(json) {
if (!this.options.textAsPlainText) return;
const obj = json;
for (const key in obj) {
const val = obj[key];
if (this.isBitmarkText(val)) {
const s = this.textGenerator.generateSync(
val,
TextFormat.bitmarkText,
TextLocation.tag,
{
noBreakscaping: true
}
);
obj[key] = (s ?? "").trim();
} else if (typeof obj[key] === "object") {
this.convertAllBitmarkTextsToStringsForPlainText(obj[key]);
}
}
}
/**
* Remove any property with a key starting with an double underscore.
*
* @param json
*/
removeTemporaryProperties(json) {
const obj = json;
for (const key in obj) {
if (key.startsWith("__")) {
delete obj[key];
} else if (typeof obj[key] === "object") {
this.removeTemporaryProperties(obj[key]);
}
}
}
//
// Writer interface
//
/**
* Writes a string value to the output.
* @param value - The string value to be written.
*/
write(value) {
this.writer.write(value);
return this;
}
/**
* Writes a new line to the output. The line is indented automatically. The line is ended with the endOfLineString.
* @param value - The line to write. When omitted, only the endOfLineString is written.
*/
writeLine(value) {
this.writer.writeLine(value);
return this;
}
/**
* Writes a collection of lines to the output. Each line is indented automatically and ended with the endOfLineString.
* @param values - The lines to write.
* @param delimiter - An optional delimiter to be written at the end of each line, except for the last one.
*/
writeLines(values, delimiter) {
this.writer.writeLines(values, delimiter);
return this;
}
/**
* Writes a single whitespace character to the output.
*/
writeWhiteSpace() {
this.writer.writeWhiteSpace();
return this;
}
};
// src/generator/json/JsonObjectGenerator.ts
var JsonObjectGenerator = class {
/**
* Generate bitmark JSON from a bitmark AST as a string
*
* @param options - JSON generation options
*/
constructor(options) {
__publicField(this, "generator");
__publicField(this, "writer");
this.writer = new StringWriter();
this.generator = new JsonGenerator(this.writer, options);
}
/**
* Generate bitmark JSON from bitmark AST as a plain JS object
*
* @param ast bitmark AST
*/
async generate(ast) {
await this.generator.generate(ast);
return JSON.parse(this.writer.getString());
}
/**
* Generate bitmark JSON from bitmark AST as a plain JS object synchronously
*
* @param ast bitmark AST
*/
generateSync(ast) {
this.generator.generateSync(ast);
return JSON.parse(this.writer.getString());
}
};
// src/info/InfoBuilder.ts
import { Enum as Enum10 } from "@ncoderz/superenum";
var InfoBuilder = class {
getSupportedBits(options) {
const includeNonDeprecated = options?.includeNonDeprecated ?? true;
const includeDeprecated = options?.includeDeprecated ?? false;
const supportedBits = [];
for (const bt of Enum10(BitType).values()) {
if (bt === BitType._error || bt === BitType._comment) continue;
const bitType = instance2.getBitType(bt);
const bitConfig = instance2.getBitConfig(bitType);
const inheritedBitTypes = bitConfig.inheritedBitTypes.length > 0 ? bitConfig.inheritedBitTypes : void 0;
const include = includeNonDeprecated && !bitConfig.deprecated || includeDeprecated && bitConfig.deprecated;
if (include) {
supportedBits.push({
name: bt,
since: bitConfig.since,
deprecated: bitConfig.deprecated,
inheritedBitTypes
});
}
}
return supportedBits;
}
getSupportedBitConfigs() {
const res = [];
for (const bt of Enum10(BitType).values()) {
if (bt === BitType._error || bt === BitType._comment) continue;
const bitType = instance2.getBitType(bt);
const config = instance2.getBitConfig(bitType);
res.push(config);
}
return res;
}
};
// src/model/enum/BitmarkParserType.ts
import "@ncoderz/superenum";
var BitmarkParserType = {
peggy: "peggy"
};
// src/model/info/enum/InfoFormat.ts
import "@ncoderz/superenum";
var InfoFormat = {
text: "text",
json: "json",
pojo: "pojo"
};
// src/model/info/enum/InfoType.ts
import "@ncoderz/superenum";
var InfoType = {
list: "list",
// List non-deprecated bits
deprecated: "deprecated",
// List deprecated bits
all: "all",
// List all bits
bit: "bit"
// Get information for a bit
};
// src/parser/json/JsonParser.ts
import { Enum as Enum11 } from "@ncoderz/superenum";
var builder = new Builder();
var JsonParser = class {
/**
* Convert JSON to AST.
*
* The JSON can be a bit or a bitwrapper and can be a string or a plain JS object
*
* @param json - bitmark JSON as a string or a plain JS object
* @returns bitmark AST
*/
toAst(json) {
const bitWrappers = this.preprocessJson(json);
const bitsNodes = [];
for (const bitWrapper of bitWrappers) {
const { bit, parser: parser2 } = bitWrapper;
const bitsNode = this.bitToAst(bit, parser2?.internalComments);
if (bitsNode) {
bitsNodes.push(bitsNode);
}
}
const bits = bitsNodes.length > 0 ? { bits: bitsNodes } : {};
const ast = builder.buildBitmark(bits);
return ast;
}
/**
* Preprocess bitmark JSON into a standard format (BitWrapperJson[] object) from either a bit or a bitwrapper
* as a string or a plain JS object
*
* @param json - bitmark JSON as a string or a plain JS object
* @returns bitmark JSON in a standard format (BitWrapperJson[] object)
*/
preprocessJson(json) {
const bitWrappers = [];
if (stringUtils.isString(json)) {
const str = json;
try {
json = JSON.parse(str);
} catch (_e) {
return [];
}
}
const pushItemAsBitWrapper = (item) => {
if (this.isBitWrapper(item)) {
const w = item;
bitWrappers.push(w);
} else if (this.isBit(item)) {
const b = item;
bitWrappers.push(this.bitToBitWrapper(b));
}
};
if (Array.isArray(json)) {
for (const item of json) {
pushItemAsBitWrapper(item);
}
} else {
pushItemAsBitWrapper(json);
}
return bitWrappers;
}
/**
* Check if a plain JS object is valid BitWrapper JSON
*
* @param bitWrapper - a plain JS object that might be BitWrapper JSON
* @returns true if BitWrapper JSON, otherwise false
*/
isBitWrapper(bitWrapper) {
if (Object.prototype.hasOwnProperty.call(bitWrapper, "bit")) {
const w = bitWrapper;
return this.isBit(w.bit);
}
return false;
}
/**
* Check if a plain JS object is valid Bit JSON
*
* @param bitWrapper - a plain JS object that might be Bit JSON
* @returns true if Bit JSON, otherwise false
*/
isBit(bit) {
if (Object.prototype.hasOwnProperty.call(bit, "type")) {
const b = bit;
return instance2.getBitType(b.type) !== BitType._error;
}
return false;
}
/**
* Convert a Bit to BitWrapper
*
* @param bit a valid Bit
* @returns the Bit wrapper in a BitWrapper
*/
bitToBitWrapper(bit) {
return {
bit
};
}
bitToAst(bit, internalComments) {
const { statement, reference: referenceBit, ...bitRest } = bit;
const isCommented = bit.type === BitType._comment && bit.originalType !== void 0;
const bitType = instance2.getBitType(isCommented ? bit.originalType : bit.type);
const bitConfig = instance2.getBitConfig(bitType);
const deprecatedTextFormat = Enum11(DeprecatedTextFormat).fromValue(bit.format);
let _textFormat = Enum11(TextFormat).fromValue(bit.format) ?? bitConfig.textFormatDefault;
if (deprecatedTextFormat === DeprecatedTextFormat.bitmarkMinusMinus) {
_textFormat = TextFormat.bitmarkText;
}
const bitNode = builder.buildBit({
...bitRest,
bitType,
bitLevel: Math.max(Math.min(bit.bitLevel ?? 1, instance2.bitLevelMax), instance2.bitLevelMin),
textFormat: bit.format,
resourceType: this.getResourceType(bit.resource),
isCommented,
internalComment: internalComments,
...this.processTitle(bitType, bit.title),
...this.processReference(referenceBit),
// reference and referenceProperty
...this.parseExample(bit.example),
person: bit.partner ?? bit.person,
markConfig: bit.marks,
resources: this.processResources(bitType, bit.resource, bit.images, bit.logos),
body: this.processBody(bit.body, bit.placeholders),
flashcards: bit.cards,
statements: this.processStatements(statement, bit.isCorrect, bit.statements, bit.example),
responses: this.processResponses(bitType, bit.responses),
botResponses: this.processBotResponse(bitType, bit.responses),
cardBits: this.processListItems(
bit.listItems ?? bit.sections ?? bit.bookReferences,
bit.placeholders
),
footer: this.processFooter(bit.footer)
});
return bitNode;
}
processStatements(statement, isCorrect, statements, example) {
const nodes = [];
if (statement) {
const node = {
statement: statement ?? "",
isCorrect: isCorrect ?? false,
...this.parseExample(example)
};
if (node) nodes.push(node);
}
if (Array.isArray(statements)) for (const item of statements) nodes.push(item);
if (nodes.length === 0) return void 0;
return nodes;
}
processResponses(bitType, responses) {
if (instance2.isOfBitType(bitType, BitType.botActionResponse)) return void 0;
if (!Array.isArray(responses)) return void 0;
return responses;
}
processBotResponse(bitType, responses) {
if (!instance2.isOfBitType(bitType, BitType.botActionResponse)) return void 0;
if (!Array.isArray(responses)) return void 0;
return responses;
}
processListItems(listItems, placeholders) {
const nodes = [];
if (Array.isArray(listItems)) {
for (const li of listItems) {
const node = {
...li
};
if (node.body) {
node.body = {
body: node.body,
placeholders
};
}
if (node) nodes.push(node);
}
}
if (nodes.length === 0) return void 0;
return nodes;
}
getResourceType(resource) {
if (resource) {
const resourceKey = Enum11(ResourceType).fromValue(resource.type);
return resourceKey;
}
return void 0;
}
processResources(bitType, resource, images, logos) {
const nodes = [];
if (resource) nodes.push(resource);
if (instance2.isOfBitType(bitType, [BitType.prototypeImages, BitType.extractorImage])) {
if (Array.isArray(images)) {
for (const image of images) {
if (image) nodes.push(image);
}
}
}
if (instance2.isOfBitType(bitType, [BitType.imagesLogoGrave])) {
if (Array.isArray(logos)) {
for (const image of logos) {
if (image) nodes.push(image);
}
}
}
return nodes;
}
processBody(body, placeholders) {
return {
body,
placeholders
};
}
processFooter(footerText) {
if (!footerText) return void 0;
return {
footer: footerText
};
}
processTitle(bitType, title) {
if (instance2.isOfBitType(bitType, BitType.author))
return {
titleString: title,
title: void 0
};
return {
titleString: void 0,
title
};
}
processReference(reference) {
if (Array.isArray(reference) && reference.length > 0) {
return {
reference: void 0,
referenceProperty: reference
};
}
return {
reference,
referenceProperty: void 0
};
}
parseExample(example) {
if (example == null) return void 0;
if (example === true) return { example: true };
if (example === false) return { example: false };
const exampleText = stringUtils.isString(example) || Array.isArray(example) ? example : void 0;
if (exampleText) {
return { example: exampleText };
}
return { example: !!example };
}
};
// src/parser/bitmark/peg/BitmarkPegParserTypes.ts
import "@ncoderz/superenum";
// src/parser/bitmark/peg/PeggyGrammarLocation.ts
var PeggyGrammarLocation = class {
/**
* Create an instance.
*
* @param {any} source The original grammarSource. Should be a string or
* have a toString() method.
* @param {import("./peg").Location} start The starting offset for the
* grammar in the larger file.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(source, start) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
__publicField(this, "source");
__publicField(this, "start");
this.source = source;
this.start = start;
}
/**
* Coerce to a string.
*
* @returns {string} The source, stringified.
*/
toString() {
return String(this.source);
}
/**
* Return a new Location offset from the given location by the start of the
* grammar.
*
* loc The location as if the start of the
* grammar was the start of the file.
* The offset location.
*/
offset(loc) {
return {
line: loc.line + this.start.line - 1,
column: loc.line === 1 ? loc.column + this.start.column - 1 : loc.column,
offset: loc.offset + this.start.offset
};
}
/**
* If the range has a grammarSource that is a GrammarLocation, offset the
* start of that range by the GrammarLocation.
*
* @param range The range to extract from.
* @returns The offset start if possible, or the
* original start.
*/
static offsetStart(range) {
if (range.source && typeof range.source.offset === "function") {
return range.source.offset(range.start);
}
return range.start;
}
/**
* If the range has a grammarSource that is a GrammarLocation, offset the
* end of that range by the GrammarLocation.
*
* @param range The range to extract from.
* @returns The offset end if possible, or the
* original end.
*/
static offsetEnd(range) {
if (range.source && typeof range.source.offset === "function") {
return range.source.offset(range.end);
}
return range.end;
}
};
// src/parser/bitmark/peg/BitmarkPegParserTypes.ts
var CARD_DIVIDER_V2 = "====";
var CARD_SIDE_DIVIDER_V2 = "--";
var CARD_VARIANT_DIVIDER_V2 = "++";
var CARD_DIVIDER_V1 = "===";
var CARD_SIDE_DIVIDER_V1 = "==";
var CARD_VARIANT_DIVIDER_V1 = "--";
var TypeKey = {
// Bit header
TextFormat: "TextFormat",
ResourceType: "ResourceType",
// Tags (NOTE: ALL TAGS MUST USE THEIR CORRECT BITMARK REPRESENTATION HERE so they work with the rest of the code)
Title: Tag.tag_title,
Anchor: Tag.tag_anchor,
Reference: Tag.tag_reference,
ItemLead: Tag.tag_item,
Instruction: Tag.tag_instruction,
Hint: Tag.tag_hint,
True: Tag.tag_true,
False: Tag.tag_false,
Gap: Tag.tag_gap,
Mark: Tag.tag_mark,
SampleSolution: Tag.tag_sampleSolution,
// Generic Tags (converted to specific tags by the BitTagValidator)
Property: "Property",
Resource: "Resource",
TagChain: "TagChain",
// Text
BodyChar: "BodyChar",
BodyText: "BodyText",
BodyTextPlain: "BodyTextPlain",
// Card Set
CardSet: "CardSet",
Card: "Card",
CardChar: "CardChar",
CardText: "CardText",
// Chains
GapChain: "GapChain",
TrueFalseChain: "TrueFalseChain",
// Dividers
PlainTextDivider: "PlainTextDivider",
FooterDivider: "FooterDivider"
};
var ContentDepth = {
Bit: "Bit",
Card: "Card",
Chain: "Chain"
};
// src/parser/bitmark/peg/BitmarkPegParserHelper.ts
import { Enum as Enum12 } from "@ncoderz/superenum";
var ENABLE_DEBUG = true;
var DEBUG_DATA = true;
var DEBUG_DATA_INCLUDE_PARSER = false;
var DEBUG_TRACE_RAW_BIT = true;
var DEBUG_TRACE_TEXT_FORMAT = false;
var DEBUG_TRACE_RESOURCE_TYPE = false;
var DEBUG_TRACE_BIT_CONTENT = false;
var DEBUG_TRACE_BIT_TAG = false;
var DEBUG_TRACE_TAGS = false;
var DEBUG_TRACE_PROPERTY_TAGS = false;
var DEBUG_TRACE_RESOURCE_TAGS = false;
var DEBUG_TRACE_TAGS_CHAIN = false;
var DEBUG_TRACE_CARD_SET = false;
var DEBUG_TRACE_CARD_SET_START = false;
var DEBUG_TRACE_CARD_SET_END = false;
var DEBUG_TRACE_CARD_LINE_OR_DIVIDER = false;
var DEBUG_TRACE_CARD_CONTENT = false;
var DEBUG_TRACE_CARD_TAGS = false;
var DEBUG_TRACE_CARD_PARSED = true;
var DEBUG = ENABLE_DEBUG && process.env.BPG_ENV === "development";
var BitmarkPegParserHelper = class {
constructor(options) {
__publicField(this, "cardIndex", 0);
__publicField(this, "cardSideIndex", 0);
__publicField(this, "cardVariantIndex", 0);
__publicField(this, "currentCardQualifier");
__publicField(this, "currentSideQualifier");
__publicField(this, "currentVariantQualifier");
__publicField(this, "parse");
__publicField(this, "parserText");
__publicField(this, "parserLocation");
this.parse = options.parse;
this.parserText = options.parserText;
this.parserLocation = options.parserLocation;
}
//
// PARSING
//
handleRawBit(rawBit) {
const rawBitTrimmed = rawBit.trim();
if (DEBUG_TRACE_RAW_BIT) this.debugPrint("RAW BIT", rawBitTrimmed);
if (!rawBitTrimmed) return void 0;
const location = this.parserLocation()?.start ?? {
line: 1,
column: 1,
offset: 0
};
const bitParserResult = this.parse(rawBit, {
startRule: "bit",
grammarSource: new PeggyGrammarLocation("bit", location)
});
if (bitParserResult.value) bitParserResult.value.markup = rawBitTrimmed;
return bitParserResult;
}
handleTextFormat(value) {
if (DEBUG_TRACE_TEXT_FORMAT) this.debugPrint(TypeKey.TextFormat, value);
return {
type: TypeKey.TextFormat,
value,
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
handleResourceType(value) {
if (DEBUG_TRACE_RESOURCE_TYPE) this.debugPrint(TypeKey.ResourceType, value);
return {
type: TypeKey.ResourceType,
value,
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
handleBitContent(value) {
if (DEBUG_TRACE_BIT_CONTENT) this.debugPrint("BitContent", value);
let content = this.reduceToArrayOfTypes(value);
content = this.mergeCharToText(content);
return content;
}
//
// Bit tags parsing
//
handleBitTag(value) {
if (DEBUG_TRACE_BIT_TAG) this.debugPrint("BitTag", value);
return value;
}
handleTag(type, value) {
if (DEBUG_TRACE_TAGS) this.debugPrint(type, { value });
return {
type,
key: Enum12(Tag).fromValue(type),
value,
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
handlePropertyTag(key, value) {
if (DEBUG_TRACE_PROPERTY_TAGS) this.debugPrint(TypeKey.Property, { key, value });
return {
type: TypeKey.Property,
key: `@${instance3.unbreakscape(key, {
format: TextFormat.plainText,
location: TextLocation.tag
})}`,
value,
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
handleResourceTag(key, value) {
if (DEBUG_TRACE_RESOURCE_TAGS) this.debugPrint(TypeKey.Resource, { key, value });
const uKey = instance3.unbreakscape(key, {
format: TextFormat.plainText,
location: TextLocation.tag
});
const camelKey = stringUtils.kebabToCamel(uKey);
return {
type: TypeKey.Resource,
key: `&${camelKey}`,
value,
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
handleTagChain(value) {
if (DEBUG_TRACE_TAGS_CHAIN) this.debugPrint("TagsChain", value);
const content = this.reduceToArrayOfTypes(value);
let newContent = content;
if (content.length > 1) {
const head = content[0];
head.chain = content.slice(1);
newContent = [head];
}
return newContent;
}
//
// Divider parsing
//
// Plain text divider
handlePlainTextDivider(value) {
value = this.reduceToString(value);
return {
type: TypeKey.PlainTextDivider,
value,
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
// Footer divider
handleFooterDivider(value) {
value = this.reduceToString(value);
return {
type: TypeKey.FooterDivider,
value,
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
//
// Card parsing
//
handleCardSet(value) {
if (DEBUG_TRACE_CARD_SET) this.debugPrint(TypeKey.CardSet, value);
const cards = value;
const unparsedCardSet = {
cards: []
};
const cardSet = {
cards: []
};
if (cards) {
const parser2 = {
text: this.parserText(),
location: this.parserLocation()
};
for (const content of cards) {
if (!content) continue;
const { type, value: cardData, parser: parser3 } = content;
if (!type || type !== TypeKey.Card) continue;
const {
cardIndex,
cardSideIndex,
cardVariantIndex: cardContentIndex,
value: value2,
cardQualifier,
cardSideQualifier,
cardVariantQualifier
} = cardData;
let card = unparsedCardSet.cards[cardIndex];
if (!card) {
card = {
sides: [],
qualifier: cardQualifier
};
unparsedCardSet.cards[cardIndex] = card;
} else if (cardQualifier && !card.qualifier) {
card.qualifier = cardQualifier;
}
let side = card.sides[cardSideIndex];
if (!side) {
side = {
variants: [],
qualifier: cardSideQualifier
};
card.sides[cardSideIndex] = side;
} else if (cardSideQualifier && !side.qualifier) {
side.qualifier = cardSideQualifier;
}
const variant = side.variants[cardContentIndex];
if (!variant) {
side.variants[cardContentIndex] = {
value: value2,
parser: parser3,
qualifier: cardVariantQualifier
};
} else {
side.variants[cardContentIndex].value += value2;
if (cardVariantQualifier && !variant.qualifier) {
variant.qualifier = cardVariantQualifier;
}
}
}
unparsedCardSet.cards = unparsedCardSet.cards.filter((card) => {
return card.sides.some((side) => {
return side.variants.some((variant) => {
const trimmed = stringUtils.trimmedString(variant.value);
return trimmed.length !== 0;
});
});
});
for (const unparsedCard of unparsedCardSet.cards) {
const card = {
sides: [],
qualifier: unparsedCard.qualifier
};
cardSet.cards.push(card);
for (const unparsedSide of unparsedCard.sides) {
const side = {
variants: [],
qualifier: unparsedSide.qualifier
};
card.sides.push(side);
for (const unparsedContent of unparsedSide.variants) {
let location = {
line: 1,
column: 1,
offset: 0
};
if (unparsedContent.parser.location) {
location = unparsedContent.parser.location.start;
const text = unparsedContent.parser.text;
const offsetCorrection = text ? text.length : 0;
const lineCorrection = 1;
location.offset += offsetCorrection;
location.line += lineCorrection;
}
let content = this.parse(unparsedContent.value, {
startRule: "cardContent",
grammarSource: new PeggyGrammarLocation("card-content", location)
});
content = this.reduceToArrayOfTypes(content);
if (DEBUG_TRACE_CARD_PARSED) this.debugPrint("parsedCardContent", content);
side.variants.push({
parser: parser2,
content,
qualifier: unparsedContent.qualifier
});
}
}
}
}
return {
type: TypeKey.CardSet,
value: cardSet,
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
handleCardSetStart() {
if (DEBUG_TRACE_CARD_SET_START) this.debugPrint("CardSetStart");
this.cardIndex = -1;
this.cardSideIndex = 0;
this.cardVariantIndex = 0;
this.currentCardQualifier = void 0;
this.currentSideQualifier = void 0;
this.currentVariantQualifier = void 0;
}
handleCardSetEnd() {
if (DEBUG_TRACE_CARD_SET_END) this.debugPrint("CardSetEnd");
this.cardIndex = 0;
this.cardSideIndex = 0;
this.cardVariantIndex = 0;
this.currentCardQualifier = void 0;
this.currentSideQualifier = void 0;
this.currentVariantQualifier = void 0;
}
handleCards(value) {
return value;
}
handleCardLineOrDivider(value, version) {
if (DEBUG_TRACE_CARD_LINE_OR_DIVIDER) this.debugPrint("CardLineOrDivider", value);
let isCardDivider = false;
let isSideDivider = false;
let isVariantDivider = false;
let qualifier;
if (Array.isArray(value) && value.length === 2) {
const [divider, maybeQualifier] = value;
if (typeof divider === "string") {
value = divider;
if (typeof maybeQualifier === "string" && maybeQualifier.trim() === maybeQualifier && maybeQualifier.length > 0) {
qualifier = maybeQualifier;
}
} else {
value = divider;
}
} else if (Array.isArray(value) && value.length === 1) {
const [divider] = value;
value = divider;
}
if (version === CardSetVersion.v1) {
isCardDivider = value === CARD_DIVIDER_V1;
isSideDivider = value === CARD_SIDE_DIVIDER_V1;
isVariantDivider = value === CARD_VARIANT_DIVIDER_V1;
} else {
isCardDivider = value === CARD_DIVIDER_V2;
isSideDivider = value === CARD_SIDE_DIVIDER_V2;
isVariantDivider = value === CARD_VARIANT_DIVIDER_V2;
}
if (isCardDivider) {
this.cardIndex++;
this.cardSideIndex = 0;
this.cardVariantIndex = 0;
this.currentCardQualifier = qualifier;
this.currentSideQualifier = void 0;
this.currentVariantQualifier = void 0;
} else if (isSideDivider) {
this.cardSideIndex++;
this.cardVariantIndex = 0;
this.currentSideQualifier = qualifier;
this.currentVariantQualifier = void 0;
} else if (isVariantDivider) {
this.cardVariantIndex++;
this.currentVariantQualifier = qualifier;
}
if (this.isType(value, TypeKey.Card)) return value;
return {
type: TypeKey.Card,
value: {
cardIndex: this.cardIndex,
cardSideIndex: this.cardSideIndex,
cardVariantIndex: this.cardVariantIndex,
value: "",
cardQualifier: this.currentCardQualifier,
cardSideQualifier: this.currentSideQualifier,
cardVariantQualifier: this.currentVariantQualifier
},
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
handleCardLine(value) {
if (DEBUG_TRACE_CARD_LINE_OR_DIVIDER) {
this.debugPrint(
"CardLine",
`(Card ${this.cardIndex}, Side ${this.cardSideIndex}, Variant: ${this.cardVariantIndex}): ${value}`
);
}
return {
type: TypeKey.Card,
value: {
cardIndex: this.cardIndex,
cardSideIndex: this.cardSideIndex,
cardVariantIndex: this.cardVariantIndex,
value,
cardQualifier: this.currentCardQualifier,
cardSideQualifier: this.currentSideQualifier,
cardVariantQualifier: this.currentVariantQualifier
},
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
handleCardContent(value) {
if (DEBUG_TRACE_CARD_CONTENT) this.debugPrint("CardContent", value);
const content = this.mergeCharToText(value);
return content;
}
handleCardTags(value) {
if (DEBUG_TRACE_CARD_TAGS) this.debugPrint("CardTags", value);
return value;
}
//
// Util functions
//
/**
* Merge types:
* - BodyChar => BodyText
* - CardChar => CardText
*
* Any adjacent BodyChar or CardChar are merged into a single BodyText or CardText
*
* @param bitContent
*/
mergeCharToText(bitContent) {
const bc = [];
let bodyText;
let cardText;
for (const c of bitContent) {
switch (c.type) {
case TypeKey.BodyChar: {
if (bodyText) {
const val = `${bodyText.value ?? ""}${c.value ?? ""}`;
bodyText.value = val;
} else {
bodyText = {
type: TypeKey.BodyText,
value: c.value ?? "",
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
break;
}
case TypeKey.CardChar: {
if (cardText) {
const val = `${cardText.value ?? ""}${c.value ?? ""}`;
cardText.value = val;
} else {
cardText = {
type: TypeKey.CardText,
value: c.value ?? "",
parser: {
text: this.parserText(),
location: this.parserLocation()
}
};
}
break;
}
default: {
if (bodyText) {
bc.push(bodyText);
bodyText = void 0;
}
if (cardText) {
bc.push(cardText);
cardText = void 0;
}
bc.push(c);
}
}
}
if (bodyText) bc.push(bodyText);
if (cardText) bc.push(cardText);
return bc;
}
/**
* Returns true if a value is a TypeKeyValue or TypeKey type with a type in the given types
*
* @param value The value to check
* @param validType The type or types to check, or undefined to check for any type
* @returns True if the value is a TypeKeyValue or TypeKey type with a type in the given types, otherwise False.
*/
isType(value, validType) {
if (!value) return false;
const { type } = value;
if (!validType) {
return !!Enum12(TypeKey).fromValue(type);
}
if (Array.isArray(validType)) {
return validType.indexOf(type) >= 0;
}
return validType === type;
}
/**
* Reduce the data to type objects.
*
* The input data can have any structure. It will be reduced to an array of BitContent objects.
*
* @param data the data to reduce
* @param validTypes types include in the reduced data
* @param recurseIntoTypes set to true to reduce types which have array values
* @returns an array of BitContent objects reduced from the input data
*/
reduceToArrayOfTypes(data, validTypes, recurseIntoTypes) {
if (!Array.isArray(data)) return [];
const res = data.reduce((acc, content, _index) => {
if (content == null) return acc;
const { type, value } = content;
if (Array.isArray(content)) {
const subValues = this.reduceToArrayOfTypes(content, validTypes);
acc.push(...subValues);
} else {
if (!this.isType(content, validTypes)) return acc;
if (recurseIntoTypes && Array.isArray(value)) {
const subValues = this.reduceToArrayOfTypes(value, validTypes);
acc.push(...subValues);
} else if (type) {
acc.push(content);
}
}
return acc;
}, []);
return res;
}
/**
* Reduce the data to a string.
*
* The input data can have any nested array structure. It will be reduced to a single string.
*
* @param data the data to reduce
* @returns a string concatenated from all the string values in the input data
*/
reduceToString(data) {
if (!Array.isArray(data)) return "";
const res = data.reduce((acc, content, _index) => {
if (content == null) return acc;
if (Array.isArray(content)) {
const subValue = this.reduceToString(content);
acc += subValue;
} else {
acc += content;
}
return acc;
}, []);
return res;
}
/**
* Print out data for debugging
*
* @param header
* @param data
*/
debugPrint(header, data) {
if (DEBUG) {
if (DEBUG_DATA) {
if (!DEBUG_DATA_INCLUDE_PARSER) {
if (data != void 0) {
data = JSON.parse(JSON.stringify(data, (k, v) => k === "parser" ? void 0 : v));
}
}
console.log(`----- TRACE: ${header} -----`);
console.log(JSON.stringify(data, null, 2));
console.log(`----- END: ${header} -----`);
} else {
console.log(`- TRACE: ${header}`);
}
}
}
};
// src/parser/bitmark/peg/BitmarkPegParserProcessor.ts
import { Enum as Enum21 } from "@ncoderz/superenum";
// src/parser/bitmark/peg/BitmarkPegParserValidator.ts
import { Enum as Enum13 } from "@ncoderz/superenum";
var COMMON_MISTAKE_STRINGS = [
// Card divider errors
"----",
"\n==\n",
"\n---\n"
// Remark errors
// Comment errors
];
var COMMON_STARTS_WITH_MISTAKE_STRINGS = [
// Card divider errors
"==\n",
"--\n"
];
var COMMON_ENDS_WITH_MISTAKE_STRINGS = [
// Card divider errors
"\n==",
"\n--"
];
var BitmarkPegParserValidator = class {
/**
* Validate the bit tags and tag chains.
*
* Any unknown tag chains will be 'unchained' and processed as individual tags.
*
* @param context
* @param bitType
* @param resourceType the resource type specified in the bit header
* @param data the unvalidated bit content from the parser
*
* @returns the validated and potentially unchained bit content
*/
validateBitTags(context, data) {
if (!data) return [];
const { bitConfig, bitType, resourceType } = context;
const { tags: bitTags, cardSet: cardSetConfig } = bitConfig;
const resourcesConfig = instance2.getBitResourcesConfig(bitType, resourceType);
const tags2 = { ...bitTags, ...resourcesConfig.tags };
const res = this.validateTagChainsRecursive(
context,
ContentDepth.Bit,
bitType,
data,
tags2,
cardSetConfig
).validated;
return res;
}
/**
* Check the body of the bit.
*
* This function also converts the body to JSON if the text format is JSON.
*
* @param context
* @param bitType
* @param _contentDepth
* @param body
* @returns
*/
checkBody(context, _contentDepth, bitType, _textFormat, bodyParts) {
if (!bodyParts) return bodyParts;
const { bitConfig } = context;
const { bodyAllowed } = bitConfig;
const hasBody = bodyParts.length > 0;
if (hasBody && !bodyAllowed) {
context.addWarning(`Bit '${bitType}' should not have a body.`);
}
return bodyParts;
}
/**
* Check the body part of the bit.
*
* @param context
* @param _contentDepth
* @param bitType
* @param body
* @returns
*/
checkBodyPart(context, contentDepth, bodyPart) {
if (!bodyPart) return bodyPart;
this.checkBodyForCommonPotentialMistakes(context, contentDepth, bodyPart);
return bodyPart;
}
/**
* Check the footer of the bit.
*
* @param context
* @param _contentDepth
* @param bitType
* @param body
* @returns
*/
checkFooter(context, contentDepth, footer) {
if (!footer) return footer;
const { bitConfig, bitType } = context;
const { footerAllowed } = bitConfig;
this.checkBodyForCommonPotentialMistakes(context, contentDepth, footer);
if (!footerAllowed) {
context.addWarning(`Bit '${bitType}' should not have a footer.`);
}
return footer;
}
/**
* Check the footer of the bit.
*
* @param context
* @param _contentDepth
* @param bitType
* @param cardBody
* @returns
*/
checkCardBody(context, _contentDepth, bitType, cardBody, cardNo, sideNo, variantNo) {
if (!cardBody || !cardBody.body) return cardBody;
const { bitConfig } = context;
if (!bitConfig.cardSet) return cardBody;
const variantConfig = instance2.getCardSetVariantConfig(bitType, sideNo, variantNo);
if (!variantConfig) return cardBody;
const { bodyAllowed } = variantConfig;
const hasBody = cardBody.bodyString.length > 0;
if (hasBody && !bodyAllowed) {
context.addWarning(
`Bit '${bitType}' should not have a card body at card:${cardNo + 1}, side:${sideNo + 1}, variant:${variantNo + 1}.`
);
}
return cardBody;
}
//
// Private
//
/**
* Validate tag chains as they are encountered, expanding any tag chains that are not valid,
* so they can be processed as individual tags.
*
* @param context
* @param contentDepth
* @param bitType
* @param data
* @param tags
* @param cardSetConfig
*/
validateTagChainsRecursive(context, contentDepth, bitType, data, tags2, cardSetConfig, chainHeadType) {
if (!data) {
return {
validated: []
};
}
let remaining;
const dataOrNull = [...data];
const validTypeKeys = this.convertTagsToTypeKeyMap(context, contentDepth, bitType, tags2);
const addExtraValidTypeKeys = (key, maxCount, minCount) => {
validTypeKeys.set(key, {
maxCount,
minCount,
_type: key,
_tag: key,
_seenCount: 0
});
};
if (contentDepth === ContentDepth.Bit) {
if (cardSetConfig) addExtraValidTypeKeys(TypeKey.CardSet, 1, 0);
addExtraValidTypeKeys(TypeKey.TextFormat, 1, 0);
addExtraValidTypeKeys(TypeKey.BodyText, Count.infinity, 0);
addExtraValidTypeKeys(TypeKey.BodyTextPlain, Count.infinity, 0);
addExtraValidTypeKeys(TypeKey.PlainTextDivider, Count.infinity, 0);
addExtraValidTypeKeys(TypeKey.FooterDivider, Count.infinity, 0);
} else if (contentDepth === ContentDepth.Card) {
addExtraValidTypeKeys(TypeKey.CardText, Count.infinity, 0);
}
for (let i = 0; i < dataOrNull.length; i++) {
const content = dataOrNull[i];
if (!content) continue;
const { type, key } = content;
let typeKey = Enum13(TypeKey).fromValue(type);
if (!typeKey) continue;
let validTypeKey = typeKey;
if (typeKey === TypeKey.Property || typeKey === TypeKey.Resource) {
validTypeKey = key;
}
let tagData = validTypeKeys.get(validTypeKey);
if (!tagData && typeKey === TypeKey.Property) {
const resourceKey = key.replace(/^@/, "&");
if (resourceKey === ConfigKey.resource_backgroundWallpaper || resourceKey === ConfigKey.resource_imagePlaceholder || resourceKey === ConfigKey.resource_platformIcon || resourceKey === ConfigKey.resource_platformLogo || resourceKey === ConfigKey.resource_platformBackgroundImage || resourceKey === ConfigKey.resource_coverImage) {
tagData = validTypeKeys.get(resourceKey);
if (tagData) {
typeKey = TypeKey.Resource;
content.type = TypeKey.Resource;
const warningMsg = `Falling back to '[${resourceKey}]' from '[${key}]'. Replace '[${key}]' with '[&${key}]' to avoid this warning.`;
context.addWarning(warningMsg);
}
}
}
const { validated: validatedTagContent, warning } = this.validateSingleTag(
context,
contentDepth,
bitType,
content,
typeKey,
tagData,
cardSetConfig
);
if (typeKey === TypeKey.Resource) chainHeadType = TypeKey.Resource;
let addWarning = true;
if (!validatedTagContent) {
if (contentDepth === ContentDepth.Chain && chainHeadType !== TypeKey.Resource) {
remaining = content;
const restOfChain = dataOrNull.slice(i + 1);
if (restOfChain.length > 0) {
remaining.chain = restOfChain;
}
dataOrNull.splice(i);
addWarning = false;
} else {
dataOrNull.splice(i, 1, null);
}
}
if (warning && addWarning) {
context.addWarning(warning.warning ?? "", warning.content, warning.previousContent);
}
if (contentDepth === ContentDepth.Chain && tagData && tagData.chain && validatedTagContent && !validatedTagContent.chain) {
const restOfChain = dataOrNull.slice(i + 1);
if (restOfChain.length > 0) {
validatedTagContent.chain = restOfChain;
}
dataOrNull.splice(i + 1);
}
if (validatedTagContent && Array.isArray(validatedTagContent.chain) && validatedTagContent.chain.length > 0) {
if (tagData && tagData.chain) {
const { validated: validatedTagChainContent, remaining: remainingTagChainContent } = this.validateTagChainsRecursive(
context,
ContentDepth.Chain,
bitType,
validatedTagContent.chain,
tagData.chain,
void 0,
validatedTagContent.type
);
if (validatedTagChainContent && validatedTagChainContent.length > 0) {
validatedTagContent.chain = validatedTagChainContent;
} else {
validatedTagContent.chain = void 0;
}
if (remainingTagChainContent) {
dataOrNull.splice(i + 1, 0, remainingTagChainContent);
}
} else {
if (validatedTagContent.type !== TypeKey.Resource) {
const remainingTagChainContent = validatedTagContent.chain[0];
if (validatedTagContent.chain.length > 1) {
remainingTagChainContent.chain = validatedTagContent.chain.slice(1);
}
dataOrNull.splice(i + 1, 0, remainingTagChainContent);
validatedTagContent.chain = void 0;
}
}
}
}
for (const data2 of validTypeKeys.values()) {
const { minCount, _seenCount } = data2;
if (minCount != null && _seenCount < minCount) {
const warningMsg = `${this.getTagSignature(data2)} is required at least ${minCount} time(s)`;
context.addWarning(warningMsg);
}
}
const res = dataOrNull.filter((content) => content != null);
return {
validated: res,
remaining
};
}
/**
* Validate a single bit tag
*
* @param context
* @param contentDepth
* @param bitType
* @param content
* @param typeKey
* @param tagValidationData
* @param cardSetConfig
*
* @returns validated tag, or undefined if the tag is invalid
*/
validateSingleTag(context, contentDepth, bitType, content, typeKey, tagValidationData, cardSetConfig) {
const { type, key } = content;
const keyStr = tagValidationData ? this.getTagSignature(tagValidationData) : this.getUnknownTagSignature(type, key);
const ignoredStr = " It will be ignored";
let warningStr = "";
let validatedContent;
let warning;
if (tagValidationData) {
tagValidationData._seenCount++;
switch (typeKey) {
case TypeKey.Property: {
const { content: c, warning: w } = this.validatePropertyTag(
context,
contentDepth,
bitType,
tagValidationData,
content
);
validatedContent = c;
warning = w;
break;
}
case TypeKey.Resource: {
const { content: c, warning: w } = this.validateResourceTag(
context,
contentDepth,
bitType,
tagValidationData,
content
);
validatedContent = c;
warning = w;
break;
}
case TypeKey.CardSet: {
const { content: c, warning: w } = this.validateCardSet(
context,
bitType,
tagValidationData,
content,
cardSetConfig
);
validatedContent = c;
warning = w;
break;
}
default: {
const { content: c, warning: w } = this.validateStandardTag(
context,
contentDepth,
bitType,
tagValidationData,
content
);
validatedContent = c;
warning = w;
}
}
} else {
switch (typeKey) {
case TypeKey.Property: {
warning = { extraProperty: true };
if (contentDepth !== ContentDepth.Chain) {
validatedContent = content;
}
break;
}
case TypeKey.Resource: {
warning = { excessResource: true };
validatedContent = content;
break;
}
default:
warning = { invalid: true };
}
}
if (warning) {
const previousContent = tagValidationData?._previous;
if (warning.invalid || warning.excessResource) {
warningStr = `${keyStr} is not valid here (incorrectly chained?).${ignoredStr}`;
} else if (warning.tooMany != null) {
warningStr = `${keyStr} is included more than the required ${warning.tooMany} time(s).`;
if (warning.tooMany === 0 && typeKey === TypeKey.Resource) {
warningStr += ` Did you forget the '${key}' in the bit header?`;
}
if (warning.tooMany > 0) warningStr += " The earlier ones will be ignored";
} else if (warning.extraProperty) {
warningStr = `${keyStr} is an unknown property. It can be excluded from the output using the 'excludeUnknownProperties' flag`;
} else if (warning.unexpectedCardSet) {
warningStr = `${keyStr} is not expected here.${ignoredStr}`;
} else if (warning.unexpectedCardSideVariant) {
warningStr = `${keyStr} has a card / side / variant that is not expected here.${ignoredStr}`;
}
warning.warning = warningStr;
warning.content = validatedContent;
warning.previousContent = previousContent;
}
if (tagValidationData) {
tagValidationData._previous = content;
}
return {
validated: validatedContent,
warning
};
}
/**
* Validates a standard bit tag
*
* @param context
* @param bitLevel
* @param bitType
* @param tagValidationData
* @param content
* @param seen
*
* @returns validated tag (plus warning message if one is generated) or undefined if the tag is invalid
*/
validateStandardTag(_context, _contentDepth, _bitType, tagValidationData, content) {
const validCount = tagValidationData.maxCount == null ? 1 : tagValidationData.maxCount;
if (validCount !== Count.infinity) {
if (tagValidationData._seenCount > validCount) {
return {
warning: { tooMany: validCount },
content
// Return content anyway when too many, as last wins
};
}
}
return {
content
};
}
/**
* Validates a single property tag
*
* @param context
* @param bitLevel
* @param bitType
* @param tagValidationData
* @param content
* @param seen
*
* @returns validated tag (plus warning message if one is generated) or undefined if the tag is invalid
*/
validatePropertyTag(_context, _contentDepth, _bitType, tagValidationData, content) {
const validCount = tagValidationData.maxCount == null ? 1 : tagValidationData.maxCount;
if (validCount !== Count.infinity) {
if (tagValidationData._seenCount > validCount) {
return {
warning: { tooMany: validCount },
content
// Return content anyway when too many, as last wins
};
}
}
return {
content
};
}
/**
* Validates a single resource tag
*
* @param context
* @param bitLevel
* @param bitType
* @param tagValidationData
* @param content
* @param seen
*
* @returns validated tag (plus warning message if one is generated) or undefined if the tag is invalid
*/
validateResourceTag(_context, _contentDepth, _bitType, tagValidationData, content) {
const validCount = tagValidationData.maxCount == null ? 1 : tagValidationData.maxCount;
if (validCount !== Count.infinity) {
if (tagValidationData._seenCount > validCount) {
return {
warning: { tooMany: validCount },
content
// Return content anyway when too many, as last wins
};
}
}
return {
content
};
}
/**
* Validates a card set
*
* @param context
* @param bitLevel
* @param bitType
* @param _tagValidationData
* @param content
* @param cardSetConfig
* @param depth recursion depth
*
* @returns validated tag (plus warning message if one is generated) or undefined if the tag is invalid
*/
validateCardSet(context, bitType, _tagValidationData, content, cardSetConfig) {
if (!cardSetConfig || !cardSetConfig.variants || cardSetConfig.variants.length === 0) {
return {
warning: { unexpectedCardSet: true }
};
}
const { value } = content;
const cardSet = value;
let warning;
let sideIndex = 0;
let variantIndex = 0;
for (const card of cardSet.cards) {
sideIndex = 0;
for (const side of card.sides) {
variantIndex = 0;
for (const variant of side.variants) {
const variantContent = variant.content;
let validatedContent;
const variantConfig = instance2.getCardSetVariantConfig(bitType, sideIndex, variantIndex);
if (variantConfig) {
const res = this.validateTagChainsRecursive(
context,
ContentDepth.Card,
bitType,
variantContent,
variantConfig.tags
);
validatedContent = res.validated;
} else {
warning = {
unexpectedCardSideVariant: true
};
}
if (validatedContent && validatedContent.length > 0) {
side.variants[variantIndex] = {
parser: variant.parser,
content: validatedContent
};
} else {
side.variants[variantIndex] = {
parser: variant.parser,
content: []
};
}
variantIndex++;
}
sideIndex++;
}
}
return {
content,
warning
};
}
/**
* Check the body of the bit for patterns which indicate common potential bitmark language mistakes.
*
* @param context
* @param _contentDepth
* @param bitType
* @param body
* @returns
*/
checkBodyForCommonPotentialMistakes(context, _contentDepth, body) {
if (!body) return;
const { bitType } = context;
for (const mistake of COMMON_MISTAKE_STRINGS) {
if (body.includes(mistake)) {
context.addWarning(`Bit '${bitType}' might contain a mistake: ${mistake}`);
}
}
for (const mistake of COMMON_STARTS_WITH_MISTAKE_STRINGS) {
if (body.startsWith(mistake)) {
context.addWarning(`Bit '${bitType}' might contain a mistake: ${mistake}`);
}
}
for (const mistake of COMMON_ENDS_WITH_MISTAKE_STRINGS) {
if (body.endsWith(mistake)) {
context.addWarning(`Bit '${bitType}' might contain a mistake: ${mistake}`);
}
}
}
/**
* Convert the tag data configuration to TypeKeys
*
* @param context
* @param _contentDepth
* @param bitType
* @param tags
* @param depth recursion depth
* @returns
*/
convertTagsToTypeKeyMap(_context, _contentDepth, _bitType, tags2) {
const res = /* @__PURE__ */ new Map();
for (const v of Object.values(tags2)) {
const tagValidationData = {
minCount: v.minCount,
maxCount: v.maxCount,
isTag: v.type === BitTagConfigKeyType.tag,
isProperty: v.type === BitTagConfigKeyType.property,
isResource: v.type === BitTagConfigKeyType.resource,
chain: v.chain,
// Private
_configKey: v.configKey,
_type: v.type,
_tag: v.tag,
_seenCount: 0,
_previous: void 0
};
if (tagValidationData.isProperty) {
res.set(v.configKey, tagValidationData);
} else if (tagValidationData.isResource) {
res.set(v.configKey, tagValidationData);
} else {
const typeKey = Enum13(TypeKey).fromValue(v.tag);
if (typeKey) {
res.set(typeKey, tagValidationData);
}
}
}
return res;
}
getTagSignature(tagValidationData) {
switch (tagValidationData._type) {
case BitTagConfigKeyType.tag:
case BitTagConfigKeyType.property:
case BitTagConfigKeyType.resource:
return `[${tagValidationData._configKey}]`;
default:
return `'${tagValidationData._type}' tag '${tagValidationData._tag}'`;
}
}
getUnknownTagSignature(type, tag) {
switch (type) {
case TypeKey.Property:
case TypeKey.Resource:
return `[${tag}]`;
default:
return tag ? `'${type}' tag '${tag}'` : `'${type}' tag`;
}
}
};
var instance9 = new BitmarkPegParserValidator();
// src/parser/bitmark/peg/contentProcessors/ContentProcessorUtils.ts
var ContentProcessorUtils = class {
/**
* Concatenates a plain JSON text with a JsonText that may be plain (v2) or BitmarkText (v3)
* Returns the combined text.
*
* @param text the text to concatenate
* @param extraBreaks extra breaks to add between concatenated texts
* @param textPlain the plain text to concatenate
*/
concatenatePlainTextWithAstTexts(text, extraBreaks, textPlain) {
if (Array.isArray(text)) {
textPlain = textPlain && textPlain.trim();
if (textPlain) {
const splitText = textPlain.split("\n");
const content = [];
for (let i = 0; i < extraBreaks; i++) {
content.push({
type: TextNodeType.hardBreak
});
}
for (let i = 0; i < splitText.length; i++) {
const t = splitText[i];
if (t) {
content.push({
text: t,
type: TextNodeType.text
});
}
if (i < splitText.length - 1) {
content.push({
type: TextNodeType.hardBreak
});
}
}
const lastNode = text[text.length - 1];
if (lastNode && lastNode.type === TextNodeType.paragraph) {
lastNode.content = [...lastNode.content ?? [], ...content];
} else {
text.push({
type: TextNodeType.paragraph,
content,
attrs: {}
});
}
}
return text;
}
return `${text ?? ""}${"\n".repeat(extraBreaks)}${textPlain ?? ""}`;
}
};
var instance10 = new ContentProcessorUtils();
// src/parser/bitmark/peg/contentProcessors/BodyContentProcessor.ts
var textParser2 = new TextParser();
var BodyContentProcessor = class {
process(context, contentDepth, bitType, textFormat, _tagsConfig, _target, bodyParts, isCardBody) {
const bodyBits = [];
let finalBody;
let finalBodyString;
const trimmedBodyParts = bodyParts.length > 0 ? this.trimBodyParts(bodyParts) : void 0;
const validatedBodyParts = isCardBody ? trimmedBodyParts : instance9.checkBody(
context,
contentDepth,
bitType,
textFormat,
trimmedBodyParts
);
if (textFormat === TextFormat.json && validatedBodyParts) {
finalBody = validatedBodyParts.reduce((acc, val) => {
if (val.type === BodyBitType.text && val.data) {
const bodyTextVal = val;
return acc + (bodyTextVal.data.bodyText ?? "");
}
return acc;
}, "");
try {
finalBody = JSON.parse(finalBody);
} catch (_e) {
finalBody = null;
context.addError(`Body JSON is invalid.`);
}
} else {
let bodyTextStr = "";
let plainBodyTextStr = "";
let bodyStr = "";
let placeholderIndex = 0;
const createPlaceholderKey = (i) => `[!${i}]`;
if (validatedBodyParts) {
for (let i = 0; i < validatedBodyParts.length; i++) {
const bodyPart = validatedBodyParts[i];
const isText = bodyPart.type === BodyBitType.text;
if (isText) {
const asText = bodyPart;
const bodyTextPart = asText.data.bodyText;
if (asText.data.isPlain) {
plainBodyTextStr = instance3.concatenate(plainBodyTextStr, bodyTextPart);
} else {
bodyTextStr = instance3.concatenate(bodyTextStr, bodyTextPart);
}
bodyStr = instance3.concatenate(bodyStr, bodyTextPart);
} else {
const placeholderKey = createPlaceholderKey(placeholderIndex);
bodyTextStr = instance3.concatenate(bodyTextStr, placeholderKey);
bodyBits.push(bodyPart);
placeholderIndex++;
}
}
}
const isBitmarkText = textFormat === TextFormat.bitmarkText;
const parsedBodyText = isBitmarkText ? textParser2.toAst(bodyTextStr, {
//
format: textFormat,
location: TextLocation.body
}) : instance3.unbreakscape(bodyTextStr, {
format: TextFormat.plainText,
location: TextLocation.body
});
const parserPlainText = instance3.unbreakscape(plainBodyTextStr, {
format: TextFormat.plainText,
location: TextLocation.body
});
const newlines = stringUtils.countOccurrencesAtEnd(bodyTextStr, "\n") + stringUtils.countOccurrencesAtStart(plainBodyTextStr, "\n");
finalBody = instance10.concatenatePlainTextWithAstTexts(
parsedBodyText,
newlines,
parserPlainText
);
finalBodyString = instance3.unbreakscape(bodyStr, {
format: textFormat,
location: TextLocation.body
}).trim();
const finalBodyIsAst = Array.isArray(finalBody);
const bodyAst = finalBodyIsAst ? finalBody : void 0;
if (bodyAst && validatedBodyParts) {
placeholderIndex = 0;
for (let i = 0; i < validatedBodyParts.length; i++) {
const bodyPart = validatedBodyParts[i];
const isText = bodyPart.type === BodyBitType.text;
if (isText) continue;
const bodyBit = bodyPart;
let bodyBitJson;
switch (bodyPart.type) {
case BodyBitType.gap: {
bodyBitJson = bodyBit;
break;
}
case BodyBitType.mark: {
bodyBitJson = bodyBit;
break;
}
case BodyBitType.select: {
bodyBitJson = bodyBit;
break;
}
case BodyBitType.highlight: {
bodyBitJson = bodyBit;
break;
}
}
if (bodyBitJson) {
this.replacePlaceholderWithBodyBit(bodyAst, bodyBitJson, placeholderIndex);
}
placeholderIndex++;
}
}
}
return {
body: finalBody,
bodyBits: bodyBits.length > 0 ? bodyBits : void 0,
bodyString: finalBodyString
};
}
buildBodyText(bodyTextPart, isPlain) {
const bodyText = {
type: BodyBitType.text,
data: {
bodyText: bodyTextPart,
isPlain
}
};
return bodyText;
}
/**
* Walk the body AST to find the placeholder and replace it with the body bit.
*
* @param bodyAst the body AST
* @param bodyBitJson the body bit json to insert at the placeholder position
* @param index the index of the placeholder to replace
*/
replacePlaceholderWithBodyBit(bodyAst, bodyBitJson, index) {
const walkRecursive = (node, parent, parentKey) => {
if (Array.isArray(node)) {
for (let i = 0; i < node.length; i++) {
const child = node[i];
const done = walkRecursive(child, node, i);
if (done) return true;
}
} else {
if (node.type === "bit" && node.index === index) {
parent[parentKey] = bodyBitJson;
return true;
}
if (node.content) {
const done = walkRecursive(node.content, node, "content");
if (done) return true;
}
}
return false;
};
walkRecursive(bodyAst, null, null);
}
/**
* Trim the body parts, removing any whitespace only parts at start and end of body
*
* @param parts the body parts to trim
* @returns the trimmed body parts
*/
trimBodyParts(parts) {
let foundText = false;
let trimmedParts = parts.reduce((acc, part) => {
const text = part;
if (!foundText && text.type === BodyBitType.text) {
const t = text.data.bodyText.trimStart();
if (t) {
foundText = true;
text.data.bodyText = t;
acc.push(text);
}
} else {
foundText = true;
acc.push(part);
}
return acc;
}, []);
foundText = false;
trimmedParts = trimmedParts.reduceRight((acc, part) => {
const text = part;
if (!foundText && text.type === BodyBitType.text) {
const t = text.data.bodyText.trimEnd();
if (t) {
foundText = true;
text.data.bodyText = t;
acc.unshift(text);
}
} else {
foundText = true;
acc.unshift(part);
}
return acc;
}, []);
return trimmedParts;
}
};
var instance11 = new BodyContentProcessor();
// src/parser/bitmark/peg/contentProcessors/CardContentProcessor.ts
function buildCards(context, bitType, textFormat, parsedCardSet, statementV1, statementsV1, choicesV1, responsesV1) {
const { bitConfig } = context;
if (context.DEBUG_CARD_SET) context.debugPrint("card set", parsedCardSet);
let result = {};
const processedCardSet = processCardSet(context, parsedCardSet);
const cardSetType = bitConfig.cardSet?.configKey;
switch (cardSetType) {
case CardSetConfigKey.flashcard:
case CardSetConfigKey.flashcard1:
case CardSetConfigKey.definitionList:
case CardSetConfigKey.definitionListPlain:
result = parseFlashcardOrDefinitionList(context, bitType, processedCardSet);
break;
case CardSetConfigKey.elements:
result = parseElements(context, bitType, processedCardSet);
break;
case CardSetConfigKey.statements:
result = parseStatements(context, bitType, processedCardSet, statementV1, statementsV1);
break;
case CardSetConfigKey.feedback:
result = parseFeedback(context, bitType, processedCardSet);
break;
case CardSetConfigKey.quiz:
case CardSetConfigKey.quizResponses:
result = parseQuiz(context, bitType, processedCardSet, choicesV1, responsesV1);
break;
case CardSetConfigKey.questions:
result = parseQuestions(context, bitType, processedCardSet);
break;
case CardSetConfigKey.matchPairs:
result = parseMatchPairs(context, bitType, processedCardSet);
break;
case CardSetConfigKey.matchMatrix:
result = parseMatchMatrix(context, bitType, processedCardSet);
break;
case CardSetConfigKey.pronunciationTable:
result = parsePronunciationTable(context, bitType, processedCardSet);
break;
case CardSetConfigKey.table:
case CardSetConfigKey.tableExtended:
result = parseTable(context, bitType, processedCardSet);
break;
case CardSetConfigKey.botActionResponses:
result = parseBotActionResponses(context, bitType, processedCardSet);
break;
case CardSetConfigKey.ingredients:
result = parseIngredients(context, bitType, processedCardSet);
break;
case CardSetConfigKey.clozeList:
case CardSetConfigKey.exampleBitList:
case CardSetConfigKey.pageFooterSections:
case CardSetConfigKey.bookReferenceList:
result = parseCardBits(context, bitType, textFormat, processedCardSet);
break;
// DEPRECATED - REMOVE IN THE FUTURE
// case CardSetConfigKey._captionDefinitionsList:
// // ==> captionDefinitionsList
// result = parseCaptionDefinitionsList(context, bitType, processedCardSet);
// break;
default:
}
result.internalComments = processedCardSet.internalComments.length > 0 ? processedCardSet.internalComments : void 0;
return result;
}
function processCardSet(context, parsedCardSet) {
const processedCardSet = {
cards: [],
internalComments: []
};
if (!parsedCardSet) return processedCardSet;
const { bitType } = context;
let cardNo = 0;
let sideNo = 0;
let variantNo = 0;
for (const card of parsedCardSet.cards) {
const processedCard = {
no: cardNo++,
sides: [],
qualifier: card.qualifier
};
processedCardSet.cards.push(processedCard);
for (const side of card.sides) {
const processedSide = {
no: sideNo++,
variants: [],
qualifier: side.qualifier
};
processedCard.sides.push(processedSide);
for (const variant of side.variants) {
const { parser: parser2, content } = variant;
const processedVariant = {
parser: parser2,
no: variantNo++,
qualifier: variant.qualifier
};
processedSide.variants.push(processedVariant);
const tagsConfig = instance2.getTagsConfigForCardSet(bitType, sideNo, variantNo);
const tags2 = context.bitContentProcessor(ContentDepth.Card, tagsConfig, content);
if (context.DEBUG_CARD_TAGS) context.debugPrint("card tags", tags2);
tags2.cardBody = instance9.checkCardBody(
context,
ContentDepth.Card,
bitType,
tags2.cardBody,
processedCard.no,
processedSide.no,
processedVariant.no
);
tags2.cardBodyStr = tags2.cardBody?.bodyString;
processedVariant.data = tags2;
if (tags2.internalComments) processedCardSet.internalComments.push(...tags2.internalComments);
}
variantNo = 0;
}
sideNo = 0;
}
return processedCardSet;
}
function parseFlashcardOrDefinitionList(context, bitType, cardSet) {
const flashcards = [];
const definitions = [];
let questionStr = "";
let question;
let answer;
let alternativeAnswers = [];
let variantIndex = 0;
let extraTags = {};
let questionVariant;
const onlyOneCardAllowed = bitType === BitType.flashcard1;
const headingData = extractHeadingCard(cardSet);
for (let cardIdx = 0; cardIdx < cardSet.cards.length; cardIdx++) {
const card = cardSet.cards[cardIdx];
if (headingData && cardIdx === 0) continue;
question = void 0;
answer = void 0;
alternativeAnswers = [];
variantIndex = 0;
extraTags = {};
for (const side of card.sides) {
for (const content of side.variants) {
const { cardBody, resources, ...tags2 } = content.data;
extraTags = {
...extraTags,
...tags2
};
const icon = resources && resources.length > 0 ? resources[0] : void 0;
const text = cardBody?.body;
const str = cardBody?.bodyString ?? "";
if (variantIndex === 0) {
questionVariant = content;
questionStr = str;
question = {
text,
icon
};
} else if (variantIndex === 1) {
answer = {
text,
icon
};
} else {
alternativeAnswers.push({
text,
icon
});
}
variantIndex++;
}
}
if (cardIdx === 0 || !onlyOneCardAllowed) {
if (instance2.isOfBitType(bitType, [BitType.flashcard])) {
const fc = {
question,
answer,
alternativeAnswers,
...extraTags
};
if (fc) flashcards.push(fc);
} else {
const dl = {
term: question,
definition: answer,
alternativeDefinitions: alternativeAnswers,
...extraTags
};
if (dl) definitions.push(dl);
}
} else {
context.addWarning(
`Bit '${bitType}' should only contain one card. Ignore subsequent card: '${questionStr}'`,
questionVariant
);
break;
}
}
return {
heading: headingData?.heading,
flashcards: flashcards.length > 0 ? flashcards : void 0,
definitions: definitions.length > 0 ? definitions : void 0
};
}
function parseElements(_context, _bitType, cardSet) {
const elements = [];
for (const card of cardSet.cards) {
for (const side of card.sides) {
for (const content of side.variants) {
const tags2 = content.data;
const element = tags2.cardBody?.body ?? tags2.cardBodyStr ?? instance3.EMPTY_STRING;
elements.push(element);
}
}
}
return {
elements: elements.length > 0 ? elements : void 0
};
}
function parseStatements(_context, _bitType, cardSet, statementV1, statementsV1) {
const statements = [];
for (const card of cardSet.cards) {
for (const side of card.sides) {
for (const content of side.variants) {
const { statements: chainedStatements, statement: _ignore, ...tags2 } = content.data;
if (Array.isArray(chainedStatements)) {
for (const s of chainedStatements) {
const statement = {
statement: s.statement ?? [],
isCorrect: s.isCorrect,
item: s.item,
lead: s.lead,
hint: s.hint,
instruction: s.instruction,
example: s.example,
...tags2
};
if (statement) statements.push(statement);
}
}
}
}
}
if (statementV1) {
statements.push(statementV1);
}
if (Array.isArray(statementsV1) && statementsV1.length > 0) {
statements.push(...statementsV1);
}
return {
statements: statements.length > 0 ? statements : void 0
};
}
function parseFeedback(_context, _bitType, cardSet) {
let feedback;
const feedbacks = [];
const headingData = extractHeadingCard(cardSet);
for (let cardIdx = 0; cardIdx < cardSet.cards.length; cardIdx++) {
const card = cardSet.cards[cardIdx];
if (headingData && cardIdx === 0) continue;
feedback = void 0;
for (let sideIdx = 0; sideIdx < card.sides.length; sideIdx++) {
const side = card.sides[sideIdx];
for (const content of side.variants) {
const tags2 = content.data;
const { cardBody, cardBodyStr, choices: choiceTags, ...restTags } = tags2;
if (sideIdx === 0) {
const choices = [];
if (choiceTags && choiceTags.length > 0) {
for (const tf of choiceTags) {
const { __isDefaultExample, example, ...tfTags } = tf;
const choice = {
choice: tfTags.choice,
requireReason: tfTags.isCorrect,
item: tfTags.item,
lead: tfTags.lead,
instruction: tfTags.instruction,
__isDefaultExample,
example
};
choices.push(choice);
}
}
feedback = {
...tags2,
choices
};
if (feedback) feedbacks.push(feedback);
} else if (sideIdx === 1) {
const reason = {
text: cardBodyStr ?? "",
__textAst: cardBody?.body,
...restTags
};
if (feedback) {
feedback.reason = reason;
}
}
}
}
}
return {
heading: headingData?.heading,
feedbacks: feedbacks.length > 0 ? feedbacks : void 0
};
}
function parseQuiz(_context, bitType, cardSet, choicesV1, responsesV1) {
const quizzes = [];
const insertChoices = instance2.isOfBitType(bitType, BitType.multipleChoice);
const insertResponses = instance2.isOfBitType(bitType, BitType.multipleResponse);
if (!insertChoices && !insertResponses) return {};
let isDefaultExampleCard = false;
let exampleCard;
for (const card of cardSet.cards) {
isDefaultExampleCard = false;
exampleCard = void 0;
for (const side of card.sides) {
for (const content of side.variants) {
const { __isDefaultExample, example, ...tags2 } = content.data;
isDefaultExampleCard = __isDefaultExample === true ? true : isDefaultExampleCard;
exampleCard = example ? example : exampleCard;
if (tags2.trueFalse && tags2.trueFalse.length > 0) {
const responsesOrChoices = [];
for (const tf of tags2.trueFalse) {
const { __isDefaultExample: isDefaultExampleTf, example: exampleTf, ...tfTags } = tf;
const __isDefaultExample2 = isDefaultExampleTf || isDefaultExampleCard;
const example2 = exampleTf || exampleCard;
const response = {
isCorrect: tfTags.isCorrect,
__isDefaultExample: __isDefaultExample2,
example: example2
};
if (insertResponses) response.response = tfTags.text;
else response.choice = tfTags.text;
if (response) responsesOrChoices.push(response);
}
if (insertResponses) tags2.responses = responsesOrChoices;
else tags2.choices = responsesOrChoices;
}
const quiz = {
...tags2,
choices: tags2.choices,
responses: tags2.responses,
__isDefaultExample: isDefaultExampleCard,
__defaultExample: exampleCard
};
if (quiz) quizzes.push(quiz);
}
}
}
if (insertChoices && Array.isArray(choicesV1) && choicesV1.length > 0) {
const quiz = {
choices: choicesV1
};
if (quiz) quizzes.push(quiz);
}
if (insertResponses && Array.isArray(responsesV1) && responsesV1.length > 0) {
const quiz = {
responses: responsesV1
};
if (quiz) quizzes.push(quiz);
}
return {
quizzes: quizzes.length > 0 ? quizzes : void 0
};
}
function parseQuestions(_context, _bitType, cardSet) {
const questions = [];
for (const card of cardSet.cards) {
for (const side of card.sides) {
for (const content of side.variants) {
const tags2 = content.data;
const q = {
question: tags2.cardBody?.body ?? tags2.cardBodyStr ?? instance3.EMPTY_STRING,
...tags2
};
if (q) questions.push(q);
}
}
}
return {
questions: questions.length > 0 ? questions : void 0
};
}
function parseMatchPairs(_context, bitType, cardSet) {
const pairs = [];
let pairKey = void 0;
let pairValues = [];
let _pairValuesAst = [];
let keyAudio = void 0;
let keyImage = void 0;
let extraTags = {};
let isDefaultExampleCard = false;
let exampleCard;
const headingValuesIsArray = instance2.isOfBitType(bitType, BitType.matchMatrix);
const headingData = extractHeadingCard(cardSet, headingValuesIsArray);
const isDefaultExampleCardSet = headingData?.isDefaultExampleCardSet ?? false;
let exampleCardSet = headingData?.exampleCardSet;
for (let cardIdx = 0; cardIdx < cardSet.cards.length; cardIdx++) {
const card = cardSet.cards[cardIdx];
if (headingData && cardIdx === 0) continue;
pairKey = void 0;
pairValues = [];
_pairValuesAst = [];
keyAudio = void 0;
keyImage = void 0;
extraTags = {};
isDefaultExampleCard = false;
exampleCard = void 0;
for (let sideIdx = 0; sideIdx < card.sides.length; sideIdx++) {
const side = card.sides[sideIdx];
for (const content of side.variants) {
const { cardBody, cardBodyStr, resources, __isDefaultExample: __isDefaultExample2, example: example2, ...tags2 } = content.data;
isDefaultExampleCard = __isDefaultExample2 === true ? true : isDefaultExampleCard;
exampleCard = example2 ? example2 : exampleCard;
if (sideIdx === 0) {
if (Array.isArray(resources) && resources.length > 0) {
const resource = resources[0];
if (resource.type === ResourceType.audio) {
keyAudio = resource;
} else if (resource.type === ResourceType.image) {
keyImage = resource;
}
} else {
pairKey = cardBodyStr;
}
} else {
const value = cardBodyStr ?? "";
const valueAst = cardBody?.body;
pairValues.push(value);
_pairValuesAst.push(valueAst);
if ((isDefaultExampleCardSet || isDefaultExampleCard) && !exampleCard)
exampleCard = valueAst;
delete tags2.item;
delete tags2.lead;
}
extraTags = {
...extraTags,
...tags2
};
}
}
if (isDefaultExampleCard) exampleCardSet = void 0;
const __isDefaultExample = isDefaultExampleCard || isDefaultExampleCardSet;
const example = exampleCard || exampleCardSet;
const pair = {
key: pairKey ?? "",
keyAudio,
keyImage,
values: pairValues,
__valuesAst: _pairValuesAst,
...extraTags,
__isDefaultExample,
example
};
if (pair) pairs.push(pair);
}
return {
heading: headingData?.heading,
pairs: pairs.length > 0 ? pairs : void 0
};
}
function parseMatchMatrix(_context, _bitType, cardSet) {
let matrixKey = void 0;
let matrixKeyTags = void 0;
const matrix = [];
let matrixCells = [];
let matrixCellValues = [];
let _matrixCellValuesAst = [];
let matrixCellTags = {};
let isDefaultExampleCard = false;
let exampleCard;
let isDefaultExampleSide = false;
let exampleSide;
let isCaseSensitiveMatrix;
let isCaseSensitiveCell;
const headingData = extractHeadingCard(cardSet, true);
const isDefaultExampleCardSet = headingData?.isDefaultExampleCardSet ?? false;
let exampleCardSet = headingData?.exampleCardSet;
for (let cardIdx = 0; cardIdx < cardSet.cards.length; cardIdx++) {
const card = cardSet.cards[cardIdx];
if (headingData && cardIdx === 0) continue;
matrixKey = void 0;
matrixKeyTags = void 0;
matrixCells = [];
matrixCellValues = [];
_matrixCellValuesAst = [];
isDefaultExampleCard = false;
exampleCard = void 0;
isCaseSensitiveMatrix = void 0;
for (let sideIdx = 0; sideIdx < card.sides.length; sideIdx++) {
const side = card.sides[sideIdx];
matrixCellValues = [];
_matrixCellValuesAst = [];
matrixCellTags = {};
isDefaultExampleSide = false;
exampleSide = void 0;
isCaseSensitiveCell = void 0;
for (const content of side.variants) {
const tags2 = content.data;
const { cardBody, cardBodyStr, __isDefaultExample, example, isCaseSensitive, ...restTags } = tags2;
isDefaultExampleSide = __isDefaultExample === true ? true : isDefaultExampleSide;
exampleSide = example ? example : exampleSide;
Object.assign(matrixCellTags, restTags);
if (sideIdx === 0) {
matrixKey = cardBodyStr;
matrixKeyTags = restTags;
isDefaultExampleCard = __isDefaultExample === true ? true : isDefaultExampleCard;
exampleCard = example ? example : exampleCard;
isCaseSensitiveMatrix = isCaseSensitive != null ? isCaseSensitive : isCaseSensitiveMatrix;
} else {
const value = cardBodyStr ?? instance3.EMPTY_STRING;
const valueAst = cardBody?.body;
matrixCellValues.push(value);
_matrixCellValuesAst.push(valueAst);
if ((isDefaultExampleCardSet || isDefaultExampleSide) && !exampleSide)
exampleSide = valueAst;
isCaseSensitiveCell = isCaseSensitive != null ? isCaseSensitive : isCaseSensitiveMatrix;
}
}
if (sideIdx > 0) {
if (isDefaultExampleSide) exampleCard = exampleCardSet = void 0;
if (isDefaultExampleCard) exampleCardSet = void 0;
const __isDefaultExample = isDefaultExampleSide || isDefaultExampleCard || isDefaultExampleCardSet;
const example = exampleSide || exampleCard || exampleCardSet;
const matrixCell = {
values: matrixCellValues,
__valuesAst: _matrixCellValuesAst,
...matrixCellTags,
__isDefaultExample,
example,
isCaseSensitive: isCaseSensitiveCell
};
if (matrixCell) matrixCells.push(matrixCell);
}
}
const m = {
key: matrixKey ?? instance3.EMPTY_STRING,
// item: matrixItem ?? Breakscape.EMPTY_STRING,
// keyAudio,
// keyImage,
cells: matrixCells,
...matrixKeyTags
};
if (m) matrix.push(m);
}
return {
heading: headingData?.heading,
matrix: matrix.length > 0 ? matrix : void 0
};
}
function parsePronunciationTable(_context, _bitType, cardSet) {
const rows = [];
let rowValues = [];
for (const card of cardSet.cards) {
rowValues = [];
for (const side of card.sides) {
for (const content of side.variants) {
const tags2 = content.data;
const { title, cardBody, resources } = tags2;
const heading = title && title[1].titleAst;
const audio = resources && resources.length > 0 ? resources.find((r) => r.type === ResourceType.audio) : void 0;
const value = {
title: heading ?? [],
body: cardBody?.body ?? [],
audio
};
rowValues.push(value);
}
}
rows.push(rowValues);
}
const table = {
data: rows
};
return { pronunciationTable: table };
}
function parseTable(context, _bitType, cardSet) {
const sectionRows = {
header: [],
body: [],
footer: []
};
const getNormalizedQualifier = (rawQualifier) => {
if (!rawQualifier) return void 0;
const normalized = rawQualifier.trim().toLowerCase();
if (normalized === "table-header") return "header";
if (normalized === "table-body") return "body";
if (normalized === "table-footer") return "footer";
context.addWarning(`Unknown table section qualifier '${rawQualifier}', defaulting to body.`);
return void 0;
};
const isLegacyHeadingRow = (card, cardIndex) => {
if (cardIndex !== 0) return false;
return card.sides.some(
(side) => side.variants.some((variant) => {
const titles = variant.data.title;
return Array.isArray(titles) && titles[1]?.titleAst;
})
);
};
const readExtraProperty = (extra, key) => {
if (!extra) return void 0;
const value = extra[key];
return Array.isArray(value) ? value[0] : value;
};
const cleanupExtraProperties = (tags2, keys) => {
const extra = tags2.extraProperties;
if (!extra) return;
for (const key of keys) {
if (Object.prototype.hasOwnProperty.call(extra, key)) delete extra[key];
}
if (Object.keys(extra).length === 0) {
delete tags2.extraProperties;
}
};
const normalizeCellType = (raw, section) => {
if (typeof raw === "string") {
const normalized = raw.trim().toLowerCase();
if (normalized === "th" || normalized === "td") {
return normalized;
}
context.addWarning(
`Invalid table cell type '${raw}', using default for section '${section}'.`
);
}
return section === "body" ? "td" : "th";
};
const normalizeSpan = (raw, kind) => {
if (raw == null) return 1;
const numeric = instance6.asNumber(raw);
if (numeric == null || !Number.isInteger(numeric) || numeric < 1) {
context.addWarning(`Invalid table ${kind} '${raw}', defaulting to 1.`);
return 1;
}
return numeric;
};
const normalizeScope = (raw) => {
if (raw == null) return void 0;
if (typeof raw !== "string") {
context.addWarning(`Invalid table scope '${raw}', ignoring.`);
return void 0;
}
const normalized = raw.trim().toLowerCase();
switch (normalized) {
case "row":
case "col":
case "rowgroup":
case "colgroup":
return normalized;
default:
context.addWarning(`Invalid table scope '${raw}', ignoring.`);
return void 0;
}
};
const buildCell = (variant, section) => {
const tags2 = variant.data;
const heading = tags2.title && tags2.title[1]?.titleAst;
const bodyContent = tags2.cardBody?.body ?? tags2.cardBodyStr ?? instance3.EMPTY_STRING;
const content = heading ?? bodyContent;
const cellTypeRaw = tags2.tableCellType ?? readExtraProperty(tags2.extraProperties, "tableCellType");
const rowSpanRaw = tags2.tableRowSpan ?? readExtraProperty(tags2.extraProperties, "tableRowSpan");
const colSpanRaw = tags2.tableColSpan ?? readExtraProperty(tags2.extraProperties, "tableColSpan");
const scopeRaw = tags2.tableScope ?? readExtraProperty(tags2.extraProperties, "tableScope");
const colWidthRaw = tags2.tableColWidth ?? readExtraProperty(tags2.extraProperties, "tableColWidth");
cleanupExtraProperties(tags2, [
"tableCellType",
"tableRowSpan",
"tableColSpan",
"tableScope",
"tableColWidth"
]);
const cellType = normalizeCellType(cellTypeRaw, section);
const rowspan = normalizeSpan(rowSpanRaw, "rowspan");
const colspan = normalizeSpan(colSpanRaw, "colspan");
const scope = normalizeScope(scopeRaw);
const colwidth = instance6.asNumber(colWidthRaw) ?? 0;
const cell = {
content
};
if (cellType === "th") cell.title = true;
if (rowspan > 1) cell.rowspan = rowspan;
if (colspan > 1) cell.colspan = colspan;
if (scope) cell.scope = scope;
if (colwidth > 0) cell.colwidth = colwidth;
return cell;
};
const resolveSectionQualifier = (card) => {
const cardQualifier = getNormalizedQualifier(card.qualifier);
if (cardQualifier) return cardQualifier;
for (const side of card.sides) {
const sideQualifier = getNormalizedQualifier(side.qualifier);
if (sideQualifier) return sideQualifier;
for (const variant of side.variants) {
const variantQualifier = getNormalizedQualifier(variant.qualifier);
if (variantQualifier) return variantQualifier;
}
}
return void 0;
};
for (let cardIdx = 0; cardIdx < cardSet.cards.length; cardIdx++) {
const card = cardSet.cards[cardIdx];
const qualifier = resolveSectionQualifier(card);
const section = qualifier ? qualifier : isLegacyHeadingRow(card, cardIdx) ? "header" : "body";
const cells = [];
for (const side of card.sides) {
for (const variant of side.variants) {
cells.push(buildCell(variant, section));
}
}
sectionRows[section].push({
cells
});
}
const table = {};
const hasHeadRows = sectionRows.header.length > 0;
const hasBodyRows = sectionRows.body.length > 0;
const hasFootRows = sectionRows.footer.length > 0;
if (hasHeadRows) {
table.header = {
rows: sectionRows.header
};
}
if (hasBodyRows) {
table.body = {
rows: sectionRows.body
};
}
if (hasFootRows) {
table.footer = {
rows: sectionRows.footer
};
}
if (!hasHeadRows && !hasBodyRows && !hasFootRows) {
table.header = { rows: [] };
table.body = { rows: [] };
table.footer = { rows: [] };
}
return { table };
}
function parseBotActionResponses(_context, _bitType, cardSet) {
const botResponses = [];
for (const card of cardSet.cards) {
for (const side of card.sides) {
for (const content of side.variants) {
const {
__instructionString,
__itemString,
reaction,
cardBodyStr: feedback,
...tags2
} = content.data;
const botResponse = {
response: __instructionString ?? instance3.EMPTY_STRING,
reaction: reaction ?? instance3.EMPTY_STRING,
feedback: feedback ?? instance3.EMPTY_STRING,
...tags2,
item: __itemString ?? instance3.EMPTY_STRING
};
if (botResponse) botResponses.push(botResponse);
}
}
}
return {
botResponses: botResponses.length > 0 ? botResponses : void 0
};
}
function parseIngredients(_context, _bitType, cardSet) {
const ingredients = [];
for (const card of cardSet.cards) {
for (const side of card.sides) {
for (const content of side.variants) {
const {
title: titleArray,
__instructionString,
unit,
unitPlural,
unitAbbr,
unitAbbrPlural,
decimalPlaces,
disableCalculation,
cardBodyStr,
cardBody,
...tags2
} = content.data;
const title = Array.isArray(titleArray) && titleArray.length > 0 ? titleArray[titleArray.length - 1].titleString : void 0;
let checked = false;
let quantity = instance6.asNumber(__instructionString);
if (cardBody && cardBody.bodyBits) {
const select = cardBody.bodyBits.find(
(c) => c.type === BodyBitType.select
);
if (select) {
quantity = select.__instructionString ? instance6.asNumber(select.__instructionString) : quantity;
if (select.options && select.options.length > 0) {
checked = select.options[0].isCorrect;
}
}
}
const ingredient = {
title,
checked,
// TS compiler very weird. It doesn't recognize that cardBodyStr is a string|undefined, even if cast!
// Casting to 'any' to avoid the error
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ingredient: cardBodyStr,
quantity,
unit: unit ?? instance3.EMPTY_STRING,
unitPlural,
unitAbbr: unitAbbr ?? instance3.EMPTY_STRING,
unitAbbrPlural,
decimalPlaces: decimalPlaces ?? 1,
disableCalculation,
...tags2
};
if (ingredient) ingredients.push(ingredient);
}
}
}
return {
ingredients: ingredients.length > 0 ? ingredients : void 0
};
}
function parseCardBits(_context, _bitType, _textFormat, cardSet) {
const cardBits = [];
for (const card of cardSet.cards) {
for (const side of card.sides) {
for (const content of side.variants) {
const { cardBody: body, ...rest } = content.data;
const cardBit = {
body,
...rest
};
if (cardBit) cardBits.push(cardBit);
}
}
}
return {
cardBits: cardBits.length > 0 ? cardBits : void 0
};
}
function extractHeadingCard(cardSet, valuesIsArray = false) {
let isHeading = false;
let heading;
let forKeys = void 0;
const forValues = [];
let isDefaultExampleCardSet = false;
let exampleCardSet;
if (cardSet.cards.length === 0) return void 0;
const card = cardSet.cards[0];
for (let sideIdx = 0; sideIdx < card.sides.length; sideIdx++) {
const side = card.sides[sideIdx];
if (side.variants.length === 0) continue;
const content = side.variants[0];
const tags2 = content.data;
const { title, __isDefaultExample, example } = tags2;
const heading2 = title && title[1].titleString;
if (heading2 != null) isHeading = true;
if (sideIdx === 0) {
if (isHeading) {
forKeys = heading2;
isDefaultExampleCardSet = __isDefaultExample === true ? true : isDefaultExampleCardSet;
exampleCardSet = example ? example : exampleCardSet;
}
} else {
if (isHeading) {
forValues.push(heading2 ?? "");
isDefaultExampleCardSet = __isDefaultExample === true ? true : isDefaultExampleCardSet;
exampleCardSet = example ? example : exampleCardSet;
}
}
}
if (isHeading) {
let forValuesFinal = forValues;
if (valuesIsArray) {
forValuesFinal = forValues;
} else {
if (forValues.length >= 1) {
forValuesFinal = forValues[forValues.length - 1];
} else {
forValuesFinal = "";
}
}
heading = {
forKeys: forKeys ?? "",
forValues: forValuesFinal
};
}
return heading ? {
heading,
isDefaultExampleCardSet,
exampleCardSet
} : void 0;
}
// src/parser/bitmark/peg/contentProcessors/DefaultTagContentProcessor.ts
function defaultTagContentProcessor(context, _contentDepth, _tagsConfig, content, target) {
const { type, value } = content;
const { textFormat } = context;
const textParser10 = new TextParser();
const trimmedStringValue = stringUtils.trimmedString(value);
switch (type) {
case TypeKey.Instruction: {
target.instruction = textParser10.toAst(trimmedStringValue, {
format: textFormat,
location: TextLocation.tag
});
target.__instructionString = instance3.unbreakscape(trimmedStringValue, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
});
break;
}
case TypeKey.Hint: {
target.hint = textParser10.toAst(trimmedStringValue, {
format: textFormat,
location: TextLocation.tag
});
target.__hintString = instance3.unbreakscape(trimmedStringValue, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
});
break;
}
case TypeKey.Anchor: {
target.anchor = instance3.unbreakscape(trimmedStringValue, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
});
break;
}
case TypeKey.Reference: {
target.reference = instance3.unbreakscape(trimmedStringValue, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
});
break;
}
// 16.08.2023 Deprecated, but currently still supported
case TypeKey.SampleSolution: {
target.sampleSolution = instance3.unbreakscape(trimmedStringValue, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
});
target.__sampleSolutionAst = textParser10.toAst(trimmedStringValue, {
format: textFormat,
location: TextLocation.tag
});
context.addWarning("[$...] tag is deprecated, use [@sampleSolution:...] instead", content);
break;
}
default:
}
}
// src/parser/bitmark/peg/contentProcessors/FooterContentProcessor.ts
var textParser3 = new TextParser();
var FooterContentProcessor = class {
process(context, contentDepth, _tagsConfig, _target, footer, footerPlainText) {
const { textFormat } = context;
let finalFooterText;
footer = footer.trimStart();
footerPlainText = footerPlainText.trimEnd();
const haveFooter = !!footer.trim();
const haveFooterPlainText = !!footerPlainText.trim();
let newLines = 0;
if (haveFooter && haveFooterPlainText) {
newLines = stringUtils.countOccurrencesAtEnd(footer, "\n") + stringUtils.countOccurrencesAtStart(footerPlainText, "\n");
}
if (footer || footerPlainText) {
const isBitmarkText = textFormat === TextFormat.bitmarkText;
if (footer) {
footer = instance9.checkFooter(context, contentDepth, footer);
}
const parsedFooterText = isBitmarkText ? textParser3.toAst(footer, {
//
format: textFormat,
location: TextLocation.body
}) : instance3.unbreakscape(footer, {
format: TextFormat.plainText,
location: TextLocation.body
});
const parsedFooterPlainText = instance3.unbreakscape(footerPlainText, {
format: TextFormat.plainText,
location: TextLocation.body
});
finalFooterText = instance10.concatenatePlainTextWithAstTexts(
parsedFooterText,
newLines,
parsedFooterPlainText
);
}
return finalFooterText ? { footer: finalFooterText } : void 0;
}
};
var instance12 = new FooterContentProcessor();
// src/parser/bitmark/peg/contentProcessors/GapChainContentProcessor.ts
import { Enum as Enum14 } from "@ncoderz/superenum";
// src/parser/bitmark/peg/contentProcessors/ClozeTagContentProcessor.ts
var textParser4 = new TextParser();
function clozeTagContentProcessor(context, _contentDepth, _tagsConfig, content, target) {
const { textFormat } = context;
const { value } = content;
const solutions = target.solutions;
const solutionsAst = target.__solutionsAst;
if (!solutions || !solutionsAst) return;
if (stringUtils.isString(value)) {
const trimmedStringValue = stringUtils.trimmedString(value);
solutions.push(
instance3.unbreakscape(trimmedStringValue, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
})
);
const solutionAst = textParser4.toAst(trimmedStringValue, {
format: textFormat,
location: TextLocation.tag
});
solutionsAst.push(solutionAst);
}
}
// src/parser/bitmark/peg/contentProcessors/GapChainContentProcessor.ts
function gapChainContentProcessor(context, contentDepth, tagsConfig, content, target, bodyParts) {
if (contentDepth === ContentDepth.Chain) {
clozeTagContentProcessor(context, contentDepth, tagsConfig, content, target);
} else {
const gap = buildGap(context, contentDepth, tagsConfig, content);
if (gap) bodyParts.push(gap);
}
}
function buildGap(context, _contentDepth, tagsConfig, content) {
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("gap content", content);
const gapConfig = instance2.getTagConfigForTag(tagsConfig, Enum14(Tag).fromValue(content.type));
const chainContent = [content, ...content.chain ?? []];
const chainTags = context.bitContentProcessor(
ContentDepth.Chain,
gapConfig?.chain,
chainContent
);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("gap TAGS", chainTags);
const gap = {
type: BodyBitType.gap,
...chainTags
};
return gap;
}
// src/parser/bitmark/peg/contentProcessors/ItemLeadChainContentProcessor.ts
import { Enum as Enum15 } from "@ncoderz/superenum";
// src/parser/bitmark/peg/contentProcessors/ItemLeadTagContentProcessor.ts
function itemLeadTagContentProcessor(context, _contentDepth, _tagsConfig, content, target) {
const { textFormat } = context;
const { value } = content;
const textParser10 = new TextParser();
const trimmedStringValue = stringUtils.trimmedString(value);
if (!target.itemLead) target.itemLead = [];
const text = textParser10.toAst(trimmedStringValue, {
format: textFormat,
location: TextLocation.tag
});
target.itemLead.push(text);
if (!target.__itemLeadString) target.__itemLeadString = [];
target.__itemLeadString.push(
instance3.unbreakscape(trimmedStringValue, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
})
);
}
// src/parser/bitmark/peg/contentProcessors/ItemLeadChainContentProcessor.ts
function flattenChainContent(content) {
const result = [content];
if (content.chain) {
for (const child of content.chain) {
result.push(...flattenChainContent(child));
}
}
return result;
}
function itemLeadChainContentProcessor(context, contentDepth, tagsConfig, content, target) {
if (contentDepth === ContentDepth.Chain) {
itemLeadTagContentProcessor(context, contentDepth, tagsConfig, content, target);
} else {
buildItemLead(context, contentDepth, tagsConfig, content, target);
}
}
function buildItemLead(context, _contentDepth, tagsConfig, content, target) {
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("item lead content", content);
const itemLeadConfig = instance2.getTagConfigForTag(tagsConfig, Enum15(Tag).fromValue(content.type));
const chainContent = flattenChainContent(content);
const chainTags = context.bitContentProcessor(
ContentDepth.Chain,
itemLeadConfig?.chain,
chainContent
);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("item lead TAGS", chainTags);
target.itemLead = chainTags.itemLead;
target.__itemLeadString = chainTags.__itemLeadString;
}
// src/parser/bitmark/peg/contentProcessors/MarkChainContentProcessor.ts
import { Enum as Enum16 } from "@ncoderz/superenum";
// src/parser/bitmark/peg/contentProcessors/MarkTagContentProcessor.ts
function markTagContentProcessor(_context, _contentDepth, content, target) {
const { value } = content;
if (stringUtils.isString(value)) {
target.solution = stringUtils.trimmedString(value);
}
}
// src/parser/bitmark/peg/contentProcessors/MarkChainContentProcessor.ts
function markChainContentProcessor(context, contentDepth, tagsConfig, content, target, bodyParts) {
if (contentDepth === ContentDepth.Chain) {
markTagContentProcessor(context, ContentDepth.Chain, content, target);
} else {
const mark = buildMark(
context,
contentDepth,
tagsConfig,
content
);
if (mark) bodyParts.push(mark);
}
}
function buildMark(context, _contentDepth, tagsConfig, content) {
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("mark content", content);
const markConfig = instance2.getTagConfigForTag(tagsConfig, Enum16(Tag).fromValue(content.type));
const tags2 = context.bitContentProcessor(ContentDepth.Chain, tagsConfig, [content]);
const chainTags = context.bitContentProcessor(
ContentDepth.Chain,
markConfig?.chain,
content.chain
);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("mark TAGS", chainTags);
const { solution } = tags2;
const { mark: markType, ...rest } = chainTags;
const mark = {
type: BodyBitType.mark,
solution: solution ?? "",
mark: instance4.asSingle(markType) ?? "",
...rest
};
return mark;
}
// src/parser/bitmark/peg/contentProcessors/PropertyContentProcessor.ts
import { Enum as Enum19 } from "@ncoderz/superenum";
// src/parser/bitmark/peg/contentProcessors/BookChainContentProcessor.ts
function bookChainContentProcessor(context, contentDepth, tagsConfig, content, target) {
if (contentDepth === ContentDepth.Chain) {
} else {
buildBook(context, contentDepth, tagsConfig, content, target);
}
}
function buildBook(context, _contentDepth, tagsConfig, content, target) {
const { bitConfig } = context;
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("book content", content);
const tags2 = context.bitContentProcessor(ContentDepth.Chain, tagsConfig, content.chain);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("book TAGS", tags2);
const { reference, referenceEnd } = tags2;
const book = instance3.unbreakscape(
stringUtils.trimmedString(content.value),
{
format: TextFormat.bitmarkText,
location: TextLocation.tag
}
);
const bookConfig = bitConfig.tags[ConfigKey.property_book];
if (bookConfig && (bookConfig.maxCount === Count.infinity || bookConfig.maxCount > 1)) {
if (!Array.isArray(target.book)) target.book = [];
target.book.push({
book,
reference: reference ?? "",
referenceEnd: referenceEnd ?? void 0
});
return;
}
target.book = book;
target.reference = reference;
target.referenceEnd = referenceEnd;
}
// src/parser/bitmark/peg/contentProcessors/ExampleTagContentProcessor.ts
var textParser5 = new TextParser();
function exampleTagContentProcessor(context, _contentDepth, content, target) {
const { bitType } = context;
const { value } = content;
const example = value;
if (instance2.isOfBitType(bitType, [
BitType.cloze,
BitType.clozeAndMultipleChoiceText,
BitType.multipleChoiceText,
BitType.highlightText,
BitType.trueFalse,
BitType.trueFalse1,
BitType.multipleResponse,
BitType.multipleResponse1,
BitType.multipleChoice,
BitType.multipleChoice1
])) {
handleGapOrSelectOrTrueFalseExample(context, content, example, target);
} else if (instance2.isOfBitType(bitType, BitType.mark)) {
handleDefaultOnlyExample(context, content, example, target);
} else {
handleStandardStringExample(context, content, example, target);
}
}
function handleGapOrSelectOrTrueFalseExample(context, content, example, target) {
const { bitType, textFormat } = context;
let trueFalse;
if (Array.isArray(target.trueFalse) && target.trueFalse.length > 0) {
trueFalse = target.trueFalse[target.trueFalse.length - 1] ?? void 0;
}
if (trueFalse) {
if (example === true) {
trueFalse.__isDefaultExample = true;
trueFalse.example = !!trueFalse.isCorrect;
} else {
if (instance5.isBooleanString(example)) {
trueFalse.example = instance5.toBoolean(example);
} else {
trueFalse.__isDefaultExample = true;
trueFalse.example = void 0;
context.addWarning(
`Only 'true' / 'false' / default are allowed here, using default`,
content
);
}
}
} else if (Array.isArray(target.__solutionsAst) && target.__solutionsAst.length > 0) {
if (example === true) {
target.example = target.__solutionsAst[target.__solutionsAst.length - 1] ?? void 0;
} else {
target.example = example ? textParser5.toAst(example, {
format: textFormat,
location: TextLocation.tag
}) : void 0;
}
} else {
if (instance2.isOfBitType(bitType, [
BitType.multipleChoiceText,
BitType.highlightText,
BitType.trueFalse,
BitType.trueFalse1,
BitType.multipleResponse,
BitType.multipleResponse1
])) {
handleStandardBooleanExample(context, content, example, target);
} else if (instance2.isOfBitType(bitType, [
BitType.clozeAndMultipleChoiceText,
BitType.multipleChoice,
BitType.multipleChoice1
])) {
target.__isDefaultExample = true;
target.example = void 0;
if (example !== true) {
context.addWarning(
`At this level, only default [@example] is allowed, using default`,
content
);
}
} else {
handleStandardStringExample(context, content, example, target);
}
}
}
function handleDefaultOnlyExample(context, content, example, target) {
target.__isDefaultExample = true;
target.example = void 0;
if (example !== true) {
context.addWarning(`Only default [@example] is allowed, using default`, content);
}
}
function handleStandardBooleanExample(context, content, example, target) {
if (example === true) {
target.__isDefaultExample = true;
target.example = void 0;
} else {
const exampleStr = example ? instance3.unbreakscape(example, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
}) : void 0;
if (instance5.isBooleanString(exampleStr)) {
target.example = instance5.toBoolean(exampleStr);
} else {
target.__isDefaultExample = true;
target.example = void 0;
context.addWarning(
`Only 'true' / 'false' / default are allowed here, using default`,
content
);
}
}
}
function handleStandardStringExample(context, _content, example, target) {
const { textFormat } = context;
if (example === true || example === "true") {
target.__isDefaultExample = true;
target.example = void 0;
} else {
target.example = example ? textParser5.toAst(example, { format: textFormat, location: TextLocation.tag }) : void 0;
}
}
// src/parser/bitmark/peg/contentProcessors/GroupTagChainContentProcessor.ts
function groupTagChainContentProcessor(context, _contentDepth, tagsConfig, content, target) {
const { value } = content;
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("groupTag content", content);
const tags2 = context.bitContentProcessor(ContentDepth.Chain, tagsConfig, content.chain);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("groupTag TAGS", tags2);
const { tag } = tags2;
const name = stringUtils.trimmedString(value) ?? "";
const node = {
name,
tags: tag ?? []
};
if (!Array.isArray(target.groupTag)) target.groupTag = [];
target.groupTag.push(node);
}
// src/parser/bitmark/peg/contentProcessors/ImageSourceChainContentProcessor.ts
import { Enum as Enum17 } from "@ncoderz/superenum";
function imageSourceChainContentProcessor(context, contentDepth, tagsConfig, content, target) {
if (contentDepth === ContentDepth.Chain) {
imageSourceTagContentProcessor(context, contentDepth, tagsConfig, content, target);
} else {
buildImageSource(context, contentDepth, tagsConfig, content, target);
}
}
function imageSourceTagContentProcessor(_context, _contentDepth, _tagsConfig, content, target) {
const { value } = content;
const url = instance3.unbreakscape(stringUtils.trimmedString(value), {
format: TextFormat.bitmarkText,
location: TextLocation.tag
});
target.imageSourceUrl = url;
}
function buildImageSource(context, _contentDepth, tagsConfig, content, target) {
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("imageSource content", content);
const { key: tag } = content;
const imageSourceConfig = instance2.getTagConfigForTag(tagsConfig, Enum17(ConfigKey).fromValue(tag));
const tags2 = context.bitContentProcessor(ContentDepth.Chain, tagsConfig, [content]);
const chainTags = context.bitContentProcessor(
ContentDepth.Chain,
imageSourceConfig?.chain,
content.chain
);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("imageSource TAGS", chainTags);
const { imageSourceUrl: url } = tags2;
const { mockupId, ...rest } = chainTags;
if (!url) {
context.addWarning("[@imageSource] is missing the image url", content);
}
if (!mockupId) {
context.addWarning("[@mockupId:xxx] is missing from [@imageSource]", content);
}
const imageSource = {
url: url ?? "",
mockupId: mockupId ?? "",
...rest
};
target.imageSource = imageSource;
}
// src/parser/bitmark/peg/contentProcessors/InternalCommentTagContentProcessor.ts
function commentTagContentProcessor(_context, _contentDepth, content, target) {
if (!target.internalComments) return;
const { value } = content;
const internalComment = stringUtils.isString(value) ? stringUtils.trimmedString(value) : void 0;
if (!internalComment) return;
target.internalComments.push(internalComment);
}
// src/parser/bitmark/peg/contentProcessors/MarkConfigChainContentProcessor.ts
import { Enum as Enum18 } from "@ncoderz/superenum";
function markConfigChainContentProcessor(context, _contentDepth, tagsConfig, content, target) {
const { key: tag } = content;
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("mark content", content);
const markConfig = target.markConfig;
if (!markConfig) return;
const markTagConfig = instance2.getTagConfigForTag(tagsConfig, Enum18(ConfigKey).fromValue(tag));
const { mark: _ignoreMark, ...tags2 } = context.bitContentProcessor(
ContentDepth.Chain,
markTagConfig?.chain,
content.chain
);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("mark TAGS", tags2);
const mark = instance3.unbreakscape(
stringUtils.trimmedString(content.value) ?? "unknown",
{
format: TextFormat.bitmarkText,
location: TextLocation.tag
}
);
const config = {
mark,
emphasis: "underline",
...tags2
};
if (config) markConfig.push(config);
}
// src/parser/bitmark/peg/contentProcessors/PersonChainContentProcessor.ts
function personChainContentProcessor(context, _contentDepth, tagsConfig, content, target) {
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("person content", content);
const tags2 = context.bitContentProcessor(ContentDepth.Chain, tagsConfig, content.chain);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("person TAGS", tags2);
const { propertyTitle, resources } = tags2;
const name = instance3.unbreakscape(
stringUtils.trimmedString(content.value),
{
format: TextFormat.bitmarkText,
location: TextLocation.tag
}
);
const title = stringUtils.trimmedString(propertyTitle);
const avatarImage = extractAvatarImage(context, resources);
const person = {
name,
title,
avatarImage
};
target.person = person;
}
function extractAvatarImage(context, resources) {
let avatarImage;
const excessResources = [];
if (resources) {
for (const r of resources.reverse()) {
if (!avatarImage && ResourceType.image === r.type) {
avatarImage = r;
} else {
excessResources.push(r);
}
}
}
if (excessResources.length > 0) {
context.addWarning(
`${excessResources.length} excess resource(s) present in the [@person] chain.`
);
}
return avatarImage;
}
// src/parser/bitmark/peg/contentProcessors/RatingLevelChainContentProcessor.ts
var textParser6 = new TextParser();
function ratingLevelChainContentProcessor(context, _contentDepth, tagsConfig, content, target) {
const { textFormat } = context;
const { key, value } = content;
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("ratingLevel content", content);
const tags2 = context.bitContentProcessor(ContentDepth.Chain, tagsConfig, content.chain);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("ratingLevel TAGS", tags2);
const { label } = tags2;
const level = instance6.asNumber(value) ?? 0;
const node = {
level,
label: label ? textParser6.toAst(label, { format: textFormat, location: TextLocation.tag }) : void 0
};
switch (key) {
case ConfigKey.property_ratingLevelStart:
target.ratingLevelStart = node;
break;
case ConfigKey.property_ratingLevelEnd:
target.ratingLevelEnd = node;
break;
default:
}
}
// src/parser/bitmark/peg/contentProcessors/ServingsChainContentProcessor.ts
function servingsChainContentProcessor(context, _contentDepth, tagsConfig, content, target) {
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("servings content", content);
const tags2 = context.bitContentProcessor(ContentDepth.Chain, tagsConfig, content.chain);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("servings TAGS", tags2);
const {
unit,
unitPlural,
unitAbbr,
unitAbbrPlural,
decimalPlaces,
disableCalculation,
__hintString
} = tags2;
const servings = instance6.asNumber(content.value) ?? 1;
const node = {
servings,
unit,
unitPlural,
unitAbbr,
unitAbbrPlural,
decimalPlaces: decimalPlaces ?? 1,
disableCalculation,
hint: __hintString
};
target.servings = node;
}
// src/parser/bitmark/peg/contentProcessors/TechnicalTermChainContentProcessor.ts
function technicalTermChainContentProcessor(context, _contentDepth, tagsConfig, content, target) {
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("technicalTerm content", content);
const tags2 = context.bitContentProcessor(ContentDepth.Chain, tagsConfig, content.chain);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("technicalTerm TAGS", tags2);
const { lang } = tags2;
const technicalTerm = instance3.unbreakscape(
stringUtils.trimmedString(content.value),
{
format: TextFormat.bitmarkText,
location: TextLocation.tag
}
);
const node = {
technicalTerm,
lang
};
target.technicalTerm = node;
}
// src/parser/bitmark/peg/contentProcessors/PropertyContentProcessor.ts
var textParser7 = new TextParser();
function propertyContentProcessor(context, contentDepth, tagsConfig, content, target) {
const { value } = content;
const { key: keyIn } = content;
const isChain = contentDepth === ContentDepth.Chain;
const propertyConfig = instance2.getTagConfigForTag(tagsConfig, Enum19(PropertyKey).fromValue(keyIn));
const configKey = propertyConfig ? propertyConfig.configKey : void 0;
let property = configKeyToPropertyType(keyIn);
if (configKey === ConfigKey.property_internalComment) {
commentTagContentProcessor(context, contentDepth, content, target);
return;
}
if (propertyConfig) {
if (configKey === ConfigKey.property_example) {
exampleTagContentProcessor(context, contentDepth, content, target);
return;
} else if (configKey === ConfigKey.property_groupTag) {
groupTagChainContentProcessor(context, contentDepth, propertyConfig.chain, content, target);
return;
} else if (configKey === ConfigKey.property_ratingLevelStart || configKey === ConfigKey.property_ratingLevelEnd) {
ratingLevelChainContentProcessor(
context,
contentDepth,
propertyConfig.chain,
content,
target
);
return;
} else if (configKey === ConfigKey.property_technicalTerm) {
technicalTermChainContentProcessor(
context,
contentDepth,
propertyConfig.chain,
content,
target
);
return;
} else if (configKey === ConfigKey.property_servings) {
servingsChainContentProcessor(context, contentDepth, propertyConfig.chain, content, target);
return;
} else if (configKey === ConfigKey.property_person || configKey === ConfigKey.property_partner) {
personChainContentProcessor(context, contentDepth, propertyConfig.chain, content, target);
return;
} else if (configKey === ConfigKey.property_imageSource) {
imageSourceChainContentProcessor(context, contentDepth, tagsConfig, content, target);
return;
} else if (configKey === ConfigKey.property_book) {
bookChainContentProcessor(context, contentDepth, propertyConfig.chain, content, target);
return;
} else if (configKey === ConfigKey.property_mark && !isChain) {
markConfigChainContentProcessor(context, contentDepth, tagsConfig, content, target);
return;
} else if (configKey === ConfigKey.property_title && isChain) {
property = "propertyTitle";
}
}
const addProperty = (obj, tag, v, c) => {
const processValue = (v2) => {
if (v2 == null) return void 0;
if (c) {
switch (c.format) {
// case PropertyFormat.string:
// return StringUtils.isString(v) ? StringUtils.string(v) : undefined;
case TagFormat.plainText:
return instance3.unbreakscape(
stringUtils.isString(v2) ? stringUtils.trimmedString(v2) : void 0,
{
format: TextFormat.plainText,
location: TextLocation.tag
}
);
case TagFormat.number:
return instance6.asNumber(
instance3.unbreakscape(v2, {
format: TextFormat.plainText,
location: TextLocation.tag
})
);
case TagFormat.boolean:
return instance5.toBoolean(
instance3.unbreakscape(v2, {
format: TextFormat.plainText,
location: TextLocation.tag
}),
true
);
case TagFormat.invertedBoolean:
return !instance5.toBoolean(
instance3.unbreakscape(v2, {
format: TextFormat.plainText,
location: TextLocation.tag
}),
true
);
case TagFormat.bitmarkText:
v2 = stringUtils.isString(v2) ? v2 : "";
return textParser7.toAst(v2, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
});
case TagFormat.coordinates:
const coordRaw = instance3.unbreakscape(v2, {
format: TextFormat.plainText,
location: TextLocation.tag
});
if (coordRaw == null) return void 0;
const coordParts = coordRaw.split(",").map((p) => instance6.asNumber(p.trim()));
if (coordParts.length !== 4 || coordParts.some((n) => n == null)) {
return void 0;
}
return coordParts;
}
}
return instance3.unbreakscape(v2, {
format: TextFormat.plainText,
location: TextLocation.tag
});
};
v = processValue(v);
if (c == null || c.array) {
if (Object.prototype.hasOwnProperty.call(obj, tag)) {
const originalValue = obj[tag];
obj[tag] = [...originalValue, v];
} else {
obj[tag] = [v];
}
} else {
obj[tag] = v;
}
};
if (propertyConfig) {
addProperty(target, property, value, propertyConfig);
} else {
addProperty(target.extraProperties, property, value, propertyConfig);
}
if (configKey === ConfigKey.property_sampleSolution) {
addProperty(
target,
"__sampleSolutionAst",
value,
new PropertyTagConfig({
configKey: ConfigKey.property_sampleSolution,
tag: "sampleSolution",
maxCount: 1,
minCount: 1,
chain: void 0,
jsonKey: void 0,
exportJsonKey: void 0,
hasExportJsonKey: false,
htmlKey: void 0,
hasHtmlKey: false,
format: TagFormat.bitmarkText,
values: void 0,
defaultValue: void 0,
deprecated: void 0
})
);
}
}
// src/parser/bitmark/peg/contentProcessors/ReferenceTagContentProcessor.ts
function referenceTagContentProcessor(context, _contentDepth, tagsConfig, content, target) {
const { value, chain } = content;
const trimmedStringValue = stringUtils.trimmedString(value);
target.reference = instance3.unbreakscape(trimmedStringValue, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
});
if (Array.isArray(chain) && chain.length > 0) {
const sub = context.bitContentProcessor(ContentDepth.Chain, tagsConfig, chain);
if (sub.reference != null) {
target.referenceEnd = sub.reference;
}
}
}
// src/parser/bitmark/peg/contentProcessors/ResourceContentProcessor.ts
import { Enum as Enum20 } from "@ncoderz/superenum";
var resourceBuilder = new ResourceBuilder();
function buildResources(context, resourceTypeAttachment, resources) {
const { bitType } = context;
const filteredResources = [];
const excessResources = [];
const validatedResourceTypeAttachemnt = Enum20(ResourceType).fromValue(resourceTypeAttachment);
const resourcesConfig = instance2.getBitResourcesConfig(bitType, validatedResourceTypeAttachemnt);
const resourceAttachmentAllowed = resourcesConfig.resourceAttachmentAllowed;
const countsMin = resourcesConfig.getCountsMin();
const countsMax = resourcesConfig.getCountsMax();
if (resources) {
for (const r of resources.reverse()) {
const configKey = r.__configKey;
let countMin = countsMin.get(configKey) ?? 0;
let countMax = countsMax.get(configKey) ?? 0;
countMin = Math.max(0, countMin - 1);
if (countMax === Count.infinity) {
filteredResources.unshift(r);
} else if (countMax > 0) {
filteredResources.unshift(r);
countMax--;
} else {
excessResources.unshift(r);
}
countsMin.set(configKey, countMin);
countsMax.set(configKey, countMax);
if (r.__invalid) {
context.addWarning(`Resource type [&${r.type}] is not valid`);
}
}
}
if (!resourceAttachmentAllowed && resourceTypeAttachment) {
const warningMsg = `Resource type [&${resourceTypeAttachment}] is specified in the bit header, but no extra resource is allowed for this bit.`;
context.addWarning(warningMsg);
} else if (filteredResources.length === 0 && resourceTypeAttachment) {
context.addWarning(
`Resource type [&${resourceTypeAttachment}] is specified in the bit header, but no such resource is present`
);
}
excessResources.forEach((r) => {
r.__excessResource = true;
filteredResources.push(r);
});
return filteredResources;
}
function resourceContentProcessor(context, _contentDepth, tagsConfig, content, target) {
const { type: _ignoreType, key, value, chain } = content;
const resources = target.resources;
if (!resources) return;
const configKey = Enum20(ConfigKey).fromValue(key);
if (!configKey) return;
const resourceType = Enum20(ResourceType).fromKey(configKey.replace(/^&/, "")) ?? ResourceType.unknown;
const resourceConfig = instance2.getTagConfigForTag(tagsConfig, configKey);
const { posterImage, ...tags2 } = context.bitContentProcessor(
ContentDepth.Chain,
resourceConfig?.chain,
chain
);
let posterImageResource;
if (posterImage) {
const { subConfig: posterImageChainConfig, subChain: posterImageChain } = extractSubChain(
resourceConfig?.chain,
chain,
ConfigKey.property_posterImage
);
const { posterImage: _unused, ...posterImageTags } = context.bitContentProcessor(
ContentDepth.Chain,
posterImageChainConfig,
posterImageChain
);
posterImageResource = resourceBuilder.resource(context, {
type: ResourceType.image,
value: posterImage,
...posterImageTags
}).image;
}
const resource = resourceBuilder.resource(context, {
type: resourceType,
value: instance3.unbreakscape(value, {
format: TextFormat.bitmarkText,
location: TextLocation.tag
}),
posterImage: posterImageResource,
...tags2
});
if (resource) {
if (configKey === ConfigKey.resource_backgroundWallpaper || configKey === ConfigKey.resource_imagePlaceholder || configKey === ConfigKey.resource_platformIcon || configKey === ConfigKey.resource_platformLogo || configKey === ConfigKey.resource_platformBackgroundImage || configKey === ConfigKey.resource_previewImage || configKey === ConfigKey.resource_previewVideo || configKey === ConfigKey.resource_coverImage || configKey === ConfigKey.resource_backgroundImage) {
if (target.propertyStyleResources) {
if (target.propertyStyleResources[resourceType]) {
if (!Array.isArray(target.propertyStyleResources[resourceType])) {
target.propertyStyleResources[resourceType] = [
target.propertyStyleResources[resourceType]
];
}
target.propertyStyleResources[resourceType].push(resource);
} else {
target.propertyStyleResources[resourceType] = resource;
}
}
} else {
resources.push(resource);
}
}
}
function extractSubChain(config, chain, configKey) {
let subConfig;
if (config) {
subConfig = config[configKey]?.chain;
}
const subChain = chain?.reduce(
(acc, c) => {
if (acc) return acc;
const tkv = c;
if (tkv.key === configKey) return tkv.chain;
return void 0;
},
void 0
);
return { subConfig, subChain };
}
// src/parser/bitmark/peg/contentProcessors/TitleTagContentProcessor.ts
var textParser8 = new TextParser();
function titleTagContentProcessor(context, _contentDepth, _tagsConfig, content, target) {
const { textFormat } = context;
const { value } = content;
const title = target.title;
if (!title) return;
const titleValue = value;
const titleText = stringUtils.trimmedString(titleValue.title);
const level = titleValue.level.length;
title[level] = {
//
titleAst: textParser8.toAst(titleText ?? "", {
format: textFormat,
location: TextLocation.tag
}),
titleString: titleText ?? ""
};
}
function buildTitles(_context, bitType, title) {
title = title ?? [];
if (instance2.isOfBitType(bitType, BitType.chapter)) {
let t;
if (title.length > 0) t = title[title.length - 1];
return {
title: t?.titleAst ?? [],
titleString: t?.titleString ?? "",
level: title.length > 0 ? title.length - 1 : void 0
};
} else {
if (Array.isArray(title)) {
return {
title: title[1]?.titleAst ?? void 0,
titleString: title[1]?.titleString ?? void 0,
subtitle: title[2]?.titleAst ?? void 0,
subtitleString: title[2]?.titleString ?? void 0
};
} else {
return {
title: void 0,
titleString: title ?? void 0,
subtitle: void 0,
subtitleString: void 0
};
}
}
}
// src/parser/bitmark/peg/contentProcessors/TrueFalseTagContentProcessor.ts
var textParser9 = new TextParser();
function trueFalseTagContentProcessor(context, _contentDepth, content, target) {
const { textFormat } = context;
const { type, value } = content;
const trueFalse = target.trueFalse;
if (!trueFalse) return;
const trimmedStringValue = instance3.unbreakscape(
stringUtils.trimmedString(value),
{
format: TextFormat.bitmarkText,
location: TextLocation.tag
}
);
const textAst = textParser9.toAst(trimmedStringValue ?? "", {
format: textFormat,
location: TextLocation.tag
});
trueFalse.push({
text: trimmedStringValue,
textAst,
isCorrect: type === TypeKey.True,
__isDefaultExample: false
});
}
// src/parser/bitmark/peg/contentProcessors/TrueFalseChainContentProcessor.ts
function trueFalseChainContentProcessor(context, contentDepth, tagsConfig, content, target, bodyParts) {
if (contentDepth === ContentDepth.Chain) {
trueFalseTagContentProcessor(context, ContentDepth.Chain, content, target);
} else {
buildTrueFalse(context, contentDepth, tagsConfig, content, target, bodyParts);
}
}
function buildTrueFalse(context, _contentDepth, tagsConfig, content, target, bodyParts) {
const { bitType } = context;
const chainContent = [content, ...content.chain ?? []];
const statements = target.statements;
const choices = target.choices;
const responses = target.responses;
if (!statements || !choices || !responses || !bodyParts) return;
if (instance2.isOfBitType(bitType, BitType.trueFalse1)) {
target.statement = buildStatement(context, tagsConfig, chainContent);
} else if (instance2.isOfBitType(bitType, [
BitType.trueFalse,
BitType.multipleChoice,
BitType.multipleChoice1,
BitType.multipleResponse,
BitType.multipleResponse1,
BitType.feedback
])) {
const tf = buildStatementsChoicesResponses(context, tagsConfig, chainContent);
if (tf.statements) statements.push(...tf.statements);
if (tf.choices) choices.push(...tf.choices);
if (tf.responses) responses.push(...tf.responses);
} else if (instance2.isOfBitType(bitType, BitType.highlightText)) {
const highlight = buildHighlight(context, tagsConfig, chainContent);
if (highlight) bodyParts.push(highlight);
} else {
const select = buildSelect(context, tagsConfig, chainContent);
if (select) bodyParts.push(select);
}
}
function buildStatement(context, tagsConfig, trueFalseContent) {
const { bitType } = context;
if (!instance2.isOfBitType(bitType, BitType.trueFalse1)) return void 0;
if (context.DEBUG_CHAIN_CONTENT)
context.debugPrint("trueFalse V1 content (statement)", trueFalseContent);
const { trueFalse, ...tags2 } = context.bitContentProcessor(
ContentDepth.Chain,
tagsConfig,
trueFalseContent
);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("trueFalse V1 tags (statement)", tags2);
let statement;
let firstTrueFalse;
if (trueFalse && trueFalse.length > 0) {
firstTrueFalse = trueFalse[0];
}
if (firstTrueFalse) {
const { statement: _ignore, ...tagsRest } = tags2;
statement = { ...firstTrueFalse, statement: firstTrueFalse.text, ...tagsRest };
}
return statement;
}
function buildStatementsChoicesResponses(context, tagsConfig, trueFalseContent) {
const { bitType } = context;
const insertStatements = instance2.isOfBitType(bitType, BitType.trueFalse);
const insertChoices = instance2.isOfBitType(bitType, [
BitType.multipleChoice,
BitType.multipleChoice1,
BitType.feedback
]);
const insertResponses = instance2.isOfBitType(bitType, [
BitType.multipleResponse,
BitType.multipleResponse1
]);
if (!insertStatements && !insertChoices && !insertResponses) return {};
const statements = [];
const choices = [];
const responses = [];
const trueFalseContents = context.splitBitContent(trueFalseContent, [
TypeKey.True,
TypeKey.False
]);
if (context.DEBUG_CHAIN_CONTENT) {
context.debugPrint("trueFalse V1 content (choices/responses)", trueFalseContents);
}
for (const contents of trueFalseContents) {
const { trueFalse, ...tags2 } = context.bitContentProcessor(
ContentDepth.Chain,
tagsConfig,
contents
);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("trueFalse V1 tags (choices/responses)", tags2);
let firstTrueFalse;
if (trueFalse && trueFalse.length > 0) {
firstTrueFalse = trueFalse[0];
}
if (firstTrueFalse) {
if (insertStatements) {
const { statement: _ignore, ...tagsRest } = tags2;
const statement = {
...firstTrueFalse,
statement: firstTrueFalse.textAst ?? [],
...tagsRest
};
if (statement) statements.push(statement);
} else if (insertChoices) {
const choice = { ...firstTrueFalse, choice: firstTrueFalse.text, ...tags2 };
if (choice) choices.push(choice);
} else if (insertResponses) {
const response = { ...firstTrueFalse, response: firstTrueFalse.text, ...tags2 };
if (response) responses.push(response);
}
}
}
const res = {};
if (insertStatements) {
res.statements = statements;
} else if (insertChoices) {
res.choices = choices;
} else if (insertResponses) {
res.responses = responses;
}
return res;
}
function buildHighlight(context, tagsConfig, highlightContent) {
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("highlight content", highlightContent);
const { trueFalse, ...tags2 } = context.bitContentProcessor(
ContentDepth.Chain,
tagsConfig,
highlightContent
);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("highlight TAGS", { trueFalse, ...tags2 });
const texts = [];
if (trueFalse) {
for (const tf of trueFalse) {
const ht = { ...tf, isHighlighted: false };
if (ht) texts.push(ht);
}
}
const highlight = {
type: BodyBitType.highlight,
texts,
...tags2
};
return highlight;
}
function buildSelect(context, tagsConfig, selectContent) {
if (context.DEBUG_CHAIN_CONTENT) context.debugPrint("select content", selectContent);
const { trueFalse, ...tags2 } = context.bitContentProcessor(
ContentDepth.Chain,
tagsConfig,
selectContent
);
if (context.DEBUG_CHAIN_TAGS) context.debugPrint("select TAGS", { trueFalse, ...tags2 });
const options = [];
if (trueFalse) {
for (const tf of trueFalse) {
const so = tf;
if (so) options.push(so);
}
}
const select = {
type: BodyBitType.select,
options,
...tags2
};
return select;
}
// src/parser/bitmark/peg/BitmarkPegParserProcessor.ts
var ENABLE_DEBUG2 = true;
var DEBUG_DATA2 = true;
var DEBUG_DATA_INCLUDE_PARSER2 = false;
var DEBUG_BIT_RAW = true;
var DEBUG_BIT_CONTENT_RAW = false;
var DEBUG_BIT_CONTENT = true;
var DEBUG_BIT_TAGS = true;
var DEBUG_BODY = true;
var DEBUG_FOOTER = true;
var DEBUG_CHAIN_CONTENT = true;
var DEBUG_CHAIN_TAGS = true;
var DEBUG_CARD_SET_CONTENT = true;
var DEBUG_CARD_SET = true;
var DEBUG_CARD_TAGS = true;
var DEBUG2 = ENABLE_DEBUG2 && process.env.BPG_ENV === "development";
var builder2 = new Builder();
var BitmarkPegParserProcessor = class {
constructor(options) {
__publicField(this, "context");
__publicField(this, "nonFatalWarnings", []);
__publicField(this, "nonFatalErrors", []);
__publicField(this, "parser", {});
__publicField(this, "parse");
__publicField(this, "parserText");
__publicField(this, "parserLocation");
this.parse = options.parse;
this.parserText = options.parserText;
this.parserLocation = options.parserLocation;
this.context = {
bitConfig: instance2.getBitConfig(BitType._error),
bitType: BitType._error,
textFormat: TextFormat.bitmarkText,
DEBUG_BIT_RAW,
DEBUG_BIT_CONTENT_RAW,
DEBUG_BIT_CONTENT,
DEBUG_BIT_TAGS,
DEBUG_BODY,
DEBUG_FOOTER,
DEBUG_CHAIN_CONTENT,
DEBUG_CHAIN_TAGS,
DEBUG_CARD_SET_CONTENT,
DEBUG_CARD_SET,
DEBUG_CARD_TAGS,
parser: this.parser,
parse: this.parse,
bitContentProcessor: this.bitContentProcessor.bind(this),
splitBitContent: this.splitBitContent.bind(this),
addWarning: this.addWarning.bind(this),
addError: this.addError.bind(this),
debugPrint: this.debugPrint.bind(this),
state: {
//
}
};
}
// Build bits
buildBits(rawBits) {
const bits = [];
let errors = [];
for (const rawBit of rawBits) {
if (!rawBit) continue;
const bit = rawBit.value;
if (bit) {
bits.push(bit);
} else {
errors = errors.concat(rawBit.errors ?? []);
}
}
const res = builder2.buildBitmark({
bits,
errors: errors.length > 0 ? errors : void 0
});
return res;
}
// Build bit
buildBit(bitHeader, bitContent) {
const { bitType, textFormat, resourceType, bitLevel, isCommented } = bitHeader;
if (!bitType || instance2.isOfBitType(bitType, BitType._error)) return this.invalidBit();
const bitConfig = instance2.getBitConfig(bitType);
this.context.bitConfig = bitConfig;
this.context.bitType = bitType;
this.context.textFormat = textFormat;
this.context.resourceType = resourceType;
if (DEBUG_BIT_CONTENT_RAW) this.debugPrint("BIT CONTENT RAW", bitContent);
const isTrueFalseV1 = instance2.isOfBitType(bitType, BitType.trueFalse1);
const isMultipleChoiceV1 = instance2.isOfBitType(bitType, BitType.multipleChoice1);
const isMultipleResponseV1 = instance2.isOfBitType(bitType, BitType.multipleResponse1);
if (DEBUG_BIT_CONTENT) this.debugPrint("BIT CONTENT", bitContent);
this.resetParserState();
bitContent = this.squashUnwantedInlineBodyBits(bitContent);
bitContent = instance9.validateBitTags(this.context, bitContent);
const {
body,
footer,
cardSet,
title,
statement,
statements,
choices,
responses,
propertyStyleResources,
resources,
posterImage,
internalComments,
...tags2
} = this.bitContentProcessor(ContentDepth.Bit, bitConfig.tags, bitContent);
if (DEBUG_BIT_TAGS) this.debugPrint("BIT TAGS", tags2);
if (DEBUG_BODY) this.debugPrint("BIT BODY", body);
if (DEBUG_FOOTER) this.debugPrint("BIT FOOTER", footer);
const titles = buildTitles(this.context, bitType, title);
const bitSpecificCards = buildCards(
this.context,
bitType,
textFormat,
cardSet,
statement,
statements,
choices,
responses
);
const filteredResources = buildResources(this.context, resourceType, resources);
const internalComment = [
...internalComments ?? [],
...bitSpecificCards.internalComments ?? []
];
const warnings = this.buildBitLevelWarnings();
const errors = this.buildBitLevelErrors();
if (warnings) this.parser.warnings = warnings;
if (errors) this.parser.errors = errors;
const bit = builder2.buildBit({
bitType,
bitLevel,
isCommented,
textFormat,
resourceType,
...titles,
posterImage,
statement: isTrueFalseV1 ? statement : void 0,
choices: isMultipleChoiceV1 ? choices : void 0,
responses: isMultipleResponseV1 ? responses : void 0,
...tags2,
...propertyStyleResources,
resources: filteredResources,
...bitSpecificCards,
body,
footer,
internalComment,
parser: this.parser
});
return { value: bit };
}
// Build bit for data that cannot be parsed
invalidBit(bitData) {
if (bitData) this.addError(`Invalid bit`);
this.parser.errors = this.buildBitLevelErrors();
const bit = builder2.buildBit({
bitType: instance2.getBitType(BitType._error),
bitLevel: 1,
parser: this.parser
});
return { value: bit };
}
// Build bit header
buildBitHeader(bitTypeBreakscaped, bitLevel, textFormatAndResourceType) {
const bitType = instance3.unbreakscape(bitTypeBreakscaped, {
format: TextFormat.plainText,
location: TextLocation.tag
});
const validBitType = instance2.getBitType(bitType);
const commented = instance2.isBitTypeCommented(bitType);
if (instance2.isOfBitType(validBitType, BitType._error)) {
this.addError(`Invalid bit type: '${bitType}'`);
}
const bitConfig = instance2.getBitConfig(validBitType);
if (bitLevel > instance2.bitLevelMax) {
this.addWarning(
`Bit level of ${bitLevel} too high, setting to max value of ${instance2.bitLevelMax}`
);
bitLevel = instance2.bitLevelMax;
} else if (bitLevel < instance2.bitLevelMin) {
this.addWarning(
`Bit level of ${bitLevel} too low, setting to min value of ${instance2.bitLevelMin}`
);
bitLevel = instance2.bitLevelMin;
}
let textFormat = Enum21(TextFormat).fromValue(textFormatAndResourceType.textFormat);
const isInvalidTextFormat = textFormatAndResourceType.textFormat && !textFormat;
if (isInvalidTextFormat) {
this.addWarning(
`Invalid text format '${textFormatAndResourceType.textFormat}', defaulting to '${BodyTextFormat.bitmarkPlusPlus}'`
);
}
const deprecatedTextFormat = Enum21(DeprecatedTextFormat).fromValue(
textFormatAndResourceType.textFormat
);
if (deprecatedTextFormat) {
textFormat = TextFormat.bitmarkText;
this.addWarning(
`${deprecatedTextFormat} text format is deprecated. Bit will be parsed as '${BodyTextFormat.bitmarkPlusPlus}'`
);
}
textFormat = textFormat ?? bitConfig.textFormatDefault;
const resourceType = Enum21(ResourceType).fromValue(textFormatAndResourceType.resourceType);
if (textFormatAndResourceType.resourceType && !resourceType) {
this.addWarning(
`Invalid resource type '${textFormatAndResourceType.resourceType}', it will be ignored`
);
}
return {
bitType: validBitType,
bitLevel,
textFormat: textFormat ?? bitConfig.textFormatDefault,
resourceType,
isCommented: commented
};
}
// Build text and resource type
buildTextAndResourceType(value1, value2) {
const res = {};
const processValue = (value) => {
if (value) {
const val = instance3.unbreakscape(stringUtils.string(value.value), {
format: TextFormat.plainText,
location: TextLocation.tag
});
if (value.type === TypeKey.TextFormat) {
res.textFormat = val;
} else {
res.resourceType = val;
}
}
};
processValue(value1);
processValue(value2);
return res;
}
/**
* Reset the parser state
*
*/
resetParserState() {
this.context.state = {};
}
/**
* Process Type/Key/Value data, squashing inline body bits back into text for bits / body text formats that
* do not support them, and for after the plain text tag '$$$$'.
*
* @param data the bit content (Type/Key/Value data)
* @returns
*/
squashUnwantedInlineBodyBits(data) {
const result = [];
if (!data) return result;
const { textFormat } = this.context;
const isBitmarkText = textFormat === TextFormat.bitmarkText;
const convertTagToTextRecursive = (tag, str = "") => {
const { chain, parser: parser2 } = tag;
str += parser2.text;
if (chain) {
for (const t of chain) {
str = convertTagToTextRecursive(t, str);
}
}
return str;
};
let inPlainText = false;
for (const content of data) {
const { type, value } = content;
const valueStr = value ?? "";
let atPlainTextDivider = false;
if (!inPlainText) {
if (type === TypeKey.BodyText && !isBitmarkText && valueStr && valueStr.trim()) {
inPlainText = true;
}
if (type === TypeKey.PlainTextDivider) {
inPlainText = true;
atPlainTextDivider = true;
}
}
if (!atPlainTextDivider) {
if (inPlainText) {
if (type === TypeKey.BodyText) {
result.push({ type: TypeKey.BodyTextPlain, value });
} else {
const s = convertTagToTextRecursive(content);
result.push({ type: TypeKey.BodyTextPlain, value: s });
}
} else {
result.push(content);
}
} else {
result.push({ type: TypeKey.BodyText, value: "\n" });
}
}
return result;
}
/**
* Process Type/Key/Value data, building the bit parts as AST nodes.
*
* @param bitType bit type
* @param textFormat bit text format type
* @param contentDepth bit level (in chain, in card etc)
* @param tagsConfig tags configuration at this parser level (pass undef)
* @param data the bit content (Type/Key/Value data)
* @returns
*/
bitContentProcessor(contentDepth, tagsConfig, data) {
const result = {};
if (!data) return result;
const { bitType, textFormat } = this.context;
result.title = [];
result.solutions = [];
result.__solutionsAst = [];
result.statements = [];
result.choices = [];
result.responses = [];
result.propertyStyleResources = {};
result.resources = [];
result.trueFalse = [];
result.markConfig = [];
result.extraProperties = {};
result.internalComments = [];
let inFooter = false;
const bodyParts = [];
let bodyTextPart = instance3.EMPTY_STRING;
let footer = instance3.EMPTY_STRING;
let bodyPlainText = instance3.EMPTY_STRING;
let footerPlainText = instance3.EMPTY_STRING;
const inBit = contentDepth === ContentDepth.Bit;
const inCard = contentDepth === ContentDepth.Card;
const inChain = contentDepth === ContentDepth.Chain;
const addBodyText = () => {
if (bodyTextPart) {
bodyTextPart = instance9.checkBodyPart(
this.context,
contentDepth,
bodyTextPart
);
const bodyText = instance11.buildBodyText(bodyTextPart, false);
bodyParts.push(bodyText);
}
bodyTextPart = instance3.EMPTY_STRING;
};
for (const content of data) {
const { type, value } = content;
switch (type) {
case TypeKey.ItemLead: {
itemLeadChainContentProcessor(this.context, contentDepth, tagsConfig, content, result);
break;
}
case TypeKey.Instruction:
case TypeKey.Hint:
case TypeKey.Anchor:
case TypeKey.SampleSolution:
defaultTagContentProcessor(this.context, contentDepth, tagsConfig, content, result);
break;
case TypeKey.Reference:
referenceTagContentProcessor(this.context, contentDepth, tagsConfig, content, result);
break;
case TypeKey.Title:
titleTagContentProcessor(this.context, contentDepth, tagsConfig, content, result);
break;
case TypeKey.Property:
propertyContentProcessor(this.context, contentDepth, tagsConfig, content, result);
break;
case TypeKey.Gap: {
if (!inChain) addBodyText();
gapChainContentProcessor(
this.context,
contentDepth,
tagsConfig,
content,
result,
bodyParts
);
break;
}
case TypeKey.Mark: {
if (!inChain) addBodyText();
markChainContentProcessor(
this.context,
contentDepth,
tagsConfig,
content,
result,
bodyParts
);
break;
}
case TypeKey.True:
case TypeKey.False: {
if (!inChain) addBodyText();
trueFalseChainContentProcessor(
this.context,
contentDepth,
tagsConfig,
content,
result,
bodyParts
);
break;
}
case TypeKey.Resource:
resourceContentProcessor(this.context, contentDepth, tagsConfig, content, result);
break;
case TypeKey.CardSet: {
result.cardSet = value;
inFooter = true;
break;
}
case TypeKey.BodyText:
case TypeKey.CardText: {
if (inFooter) {
footer = instance3.concatenate(footer, value);
} else {
bodyTextPart = instance3.concatenate(bodyTextPart, value);
}
break;
}
case TypeKey.BodyTextPlain: {
if (inFooter) {
footerPlainText = instance3.concatenate(footerPlainText, value);
} else {
bodyPlainText = instance3.concatenate(bodyPlainText, value);
}
break;
}
case TypeKey.FooterDivider: {
if (inFooter) {
footer = instance3.concatenate(footer, value);
}
inFooter = true;
break;
}
default:
}
}
addBodyText();
const bodyPlainTextNode = bodyPlainText ? instance11.buildBodyText(bodyPlainText, true) : void 0;
if (bodyPlainTextNode) bodyParts.push(bodyPlainTextNode);
if (result.itemLead) {
const l = result.itemLead.length;
if (l > 0) result.item = result.itemLead[0];
if (l > 1) result.lead = result.itemLead[1];
if (l > 2) result.pageNumber = result.itemLead[2];
if (l > 3) result.marginNumber = result.itemLead[l - 1];
}
if (result.__itemLeadString) {
const l = result.__itemLeadString.length;
if (l > 0) result.__itemString = result.__itemLeadString[0];
if (l > 1) result.__leadString = result.__itemLeadString[1];
if (l > 2) result.__pageNumberString = result.__itemLeadString[2];
if (l > 3) result.__marginNumberString = result.__itemLeadString[l - 1];
}
if (inBit) {
result.body = instance11.process(
this.context,
contentDepth,
bitType,
textFormat,
tagsConfig,
result,
bodyParts,
false
);
} else if (inCard) {
result.cardBody = instance11.process(
this.context,
contentDepth,
bitType,
textFormat,
tagsConfig,
result,
bodyParts,
true
);
}
result.footer = instance12.process(
this.context,
contentDepth,
tagsConfig,
result,
footer,
footerPlainText
);
if (Object.keys(result.extraProperties).length === 0) delete result.extraProperties;
if (result.title.length === 0) delete result.title;
if (result.solutions.length === 0) delete result.solutions;
if (result.statements.length === 0) delete result.statements;
if (result.choices.length === 0) delete result.choices;
if (result.responses.length === 0) delete result.responses;
if (result.trueFalse.length === 0) delete result.trueFalse;
if (result.markConfig.length === 0) delete result.markConfig;
if (Object.keys(result.propertyStyleResources).length === 0)
delete result.propertyStyleResources;
if (result.resources.length === 0) delete result.resources;
if (result.internalComments.length === 0) delete result.internalComments;
return result;
}
buildBitLevelWarnings() {
let warnings;
if (this.nonFatalWarnings.length > 0) {
warnings = this.nonFatalWarnings;
this.nonFatalWarnings = [];
}
return warnings;
}
buildBitLevelErrors() {
let errors;
if (this.nonFatalErrors.length > 0) {
errors = this.nonFatalErrors;
this.nonFatalErrors = [];
}
return errors;
}
/**
* Split bit content into parts based on the given type keys
*
* @param bitContent bit content to split
* @param types to split on
*/
splitBitContent(bitContent, types) {
const parts = [];
let part = [];
for (const c of bitContent) {
if (types.includes(c.type)) {
if (part.length > 0) parts.push(part);
part = [];
}
part.push(c);
}
if (part.length > 0) parts.push(part);
return parts;
}
//
// Util functions
//
/**
* Add an warning to the list of non-fatal warnings
* @param message The error message
* @param parserData Parser data - if not set, the current parser data will be used
* @param parserDataOriginal Parser data of the original instance of a duplicate error
*/
addWarning(message, parserData, parserDataOriginal) {
const warning = {
message,
text: parserData?.parser.text ?? this.parserText(),
location: parserData?.parser.location ?? this.parserLocation(),
original: parserDataOriginal?.parser ?? void 0
};
if (!warning.original) delete warning.original;
this.nonFatalWarnings.push(warning);
}
/**
* Add an error to the list of non-fatal errors
* @param message The error message
* @param parserData Parser data - if not set, the current parser data will be used
* @param parserDataOriginal Parser data of the original instance of a duplicate error
*/
addError(message, parserData, parserDataOriginal) {
const error = {
message,
text: parserData?.parser.text ?? this.parserText(),
location: parserData?.parser.location ?? this.parserLocation(),
original: parserDataOriginal?.parser ?? void 0
};
if (!error.original) delete error.original;
this.nonFatalErrors.push(error);
}
/**
* Print out data for debugging
*
* @param header
* @param data
*/
debugPrint(header, data) {
if (DEBUG2) {
if (DEBUG_DATA2) {
if (!DEBUG_DATA_INCLUDE_PARSER2) {
if (data != void 0) {
data = JSON.parse(JSON.stringify(data, (k, v) => k === "parser" ? void 0 : v));
}
}
console.log(`===== START: ${header} =====`);
console.log(JSON.stringify(data, null, 2));
console.log(`===== END: ${header} =====`);
} else {
console.log(`- DEBUG: ${header}`);
}
}
}
};
// src/generated/parser/bitmark/bitmark-peggy-parser.js
var peg$SyntaxError2 = class extends SyntaxError {
constructor(message, expected, found, location) {
super(message);
this.expected = expected;
this.found = found;
this.location = location;
this.name = "SyntaxError";
}
format(sources) {
let str = "Error: " + this.message;
if (this.location) {
let src = null;
const st = sources.find((s2) => s2.source === this.location.source);
if (st) {
src = st.text.split(/\r\n|\n|\r/g);
}
const s = this.location.start;
const offset_s = this.location.source && typeof this.location.source.offset === "function" ? this.location.source.offset(s) : s;
const loc = this.location.source + ":" + offset_s.line + ":" + offset_s.column;
if (src) {
const e = this.location.end;
const filler = "".padEnd(offset_s.line.toString().length, " ");
const line = src[s.line - 1];
const last = s.line === e.line ? e.column : line.length + 1;
const hatLen = last - s.column || 1;
str += "\n --> " + loc + "\n" + filler + " |\n" + offset_s.line + " | " + line + "\n" + filler + " | " + "".padEnd(s.column - 1, " ") + "".padEnd(hatLen, "^");
} else {
str += "\n at " + loc;
}
}
return str;
}
static buildMessage(expected, found) {
function hex(ch) {
return ch.codePointAt(0).toString(16).toUpperCase();
}
const nonPrintable = Object.prototype.hasOwnProperty.call(RegExp.prototype, "unicode") ? new RegExp("[\\p{C}\\p{Mn}\\p{Mc}]", "gu") : null;
function unicodeEscape(s) {
if (nonPrintable) {
return s.replace(nonPrintable, (ch) => "\\u{" + hex(ch) + "}");
}
return s;
}
function literalEscape(s) {
return unicodeEscape(s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\0/g, "\\0").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/[\x00-\x0F]/g, (ch) => "\\x0" + hex(ch)).replace(/[\x10-\x1F\x7F-\x9F]/g, (ch) => "\\x" + hex(ch)));
}
function classEscape(s) {
return unicodeEscape(s.replace(/\\/g, "\\\\").replace(/\]/g, "\\]").replace(/\^/g, "\\^").replace(/-/g, "\\-").replace(/\0/g, "\\0").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/[\x00-\x0F]/g, (ch) => "\\x0" + hex(ch)).replace(/[\x10-\x1F\x7F-\x9F]/g, (ch) => "\\x" + hex(ch)));
}
const DESCRIBE_EXPECTATION_FNS = {
literal(expectation) {
return '"' + literalEscape(expectation.text) + '"';
},
class(expectation) {
const escapedParts = expectation.parts.map(
(part) => Array.isArray(part) ? classEscape(part[0]) + "-" + classEscape(part[1]) : classEscape(part)
);
return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]" + (expectation.unicode ? "u" : "");
},
any() {
return "any character";
},
end() {
return "end of input";
},
other(expectation) {
return expectation.description;
}
};
function describeExpectation(expectation) {
return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
}
function describeExpected(expected2) {
const descriptions = expected2.map(describeExpectation);
descriptions.sort();
if (descriptions.length > 0) {
let j = 1;
for (let i = 1; i < descriptions.length; i++) {
if (descriptions[i - 1] !== descriptions[i]) {
descriptions[j] = descriptions[i];
j++;
}
}
descriptions.length = j;
}
switch (descriptions.length) {
case 1:
return descriptions[0];
case 2:
return descriptions[0] + " or " + descriptions[1];
default:
return descriptions.slice(0, -1).join(", ") + ", or " + descriptions[descriptions.length - 1];
}
}
function describeFound(found2) {
return found2 ? '"' + literalEscape(found2) + '"' : "end of input";
}
return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
}
};
function peg$parse2(input, options) {
options = options !== void 0 ? options : {};
const peg$FAILED = {};
const peg$source = options.grammarSource;
const peg$startRuleFunctions = {
bitmark: peg$parsebitmark,
bit: peg$parsebit,
cardContent: peg$parsecardContent
};
let peg$startRuleFunction = peg$parsebitmark;
const peg$c0 = "[.";
const peg$c1 = "]";
const peg$c2 = "[";
const peg$c3 = ":";
const peg$c4 = "&";
const peg$c5 = "$$$$";
const peg$c6 = "====";
const peg$c7 = "text";
const peg$c8 = "~~~~";
const peg$c9 = "footer";
const peg$c10 = " ";
const peg$c11 = "--";
const peg$c12 = "++";
const peg$c13 = "===";
const peg$c14 = "==";
const peg$c15 = "[@id";
const peg$c16 = "#";
const peg$c17 = "[\u25BC";
const peg$c18 = "[\u25BA";
const peg$c19 = "[@";
const peg$c20 = "[%";
const peg$c21 = "[!";
const peg$c22 = "[?";
const peg$c23 = "[+";
const peg$c24 = "[-";
const peg$c25 = "[$";
const peg$c26 = "[_";
const peg$c27 = "[=";
const peg$c28 = "[&";
const peg$c29 = "^]";
const peg$c30 = "id:";
const peg$c31 = ".";
const peg$c32 = "\n";
const peg$c33 = "\r\n";
const peg$r0 = /^[^\]]/;
const peg$r1 = /^[a-z]/;
const peg$r2 = /^[a-z0-9\-]/;
const peg$r3 = /^[^:\]]/;
const peg$r4 = /^[^&:\]]/;
const peg$r5 = /^[^\n\r\u2028\u2029]/;
const peg$r6 = /^[ \t]/;
const peg$r7 = /^[\r\u2028-\u2029]/;
const peg$r8 = /^[ \t\n\r\u2028\u2029]/;
const peg$e0 = peg$literalExpectation("[.", false);
const peg$e1 = peg$classExpectation(["]"], true, false, false);
const peg$e2 = peg$literalExpectation("]", false);
const peg$e3 = peg$literalExpectation("[", false);
const peg$e4 = peg$literalExpectation(":", false);
const peg$e5 = peg$literalExpectation("&", false);
const peg$e6 = peg$anyExpectation();
const peg$e7 = peg$literalExpectation("$$$$", false);
const peg$e8 = peg$literalExpectation("====", false);
const peg$e9 = peg$literalExpectation("text", false);
const peg$e10 = peg$literalExpectation("~~~~", false);
const peg$e11 = peg$literalExpectation("footer", false);
const peg$e12 = peg$literalExpectation(" ", false);
const peg$e13 = peg$literalExpectation("--", false);
const peg$e14 = peg$literalExpectation("++", false);
const peg$e15 = peg$literalExpectation("===", false);
const peg$e16 = peg$literalExpectation("==", false);
const peg$e17 = peg$classExpectation([["a", "z"]], false, false, false);
const peg$e18 = peg$classExpectation([["a", "z"], ["0", "9"], "-"], false, false, false);
const peg$e19 = peg$literalExpectation("[@id", false);
const peg$e20 = peg$literalExpectation("#", false);
const peg$e21 = peg$literalExpectation("[\u25BC", false);
const peg$e22 = peg$literalExpectation("[\u25BA", false);
const peg$e23 = peg$literalExpectation("[@", false);
const peg$e24 = peg$literalExpectation("[%", false);
const peg$e25 = peg$literalExpectation("[!", false);
const peg$e26 = peg$literalExpectation("[?", false);
const peg$e27 = peg$literalExpectation("[+", false);
const peg$e28 = peg$literalExpectation("[-", false);
const peg$e29 = peg$literalExpectation("[$", false);
const peg$e30 = peg$literalExpectation("[_", false);
const peg$e31 = peg$literalExpectation("[=", false);
const peg$e32 = peg$literalExpectation("[&", false);
const peg$e33 = peg$literalExpectation("^]", false);
const peg$e34 = peg$literalExpectation("id:", false);
const peg$e35 = peg$classExpectation([":", "]"], true, false, false);
const peg$e36 = peg$literalExpectation(".", false);
const peg$e37 = peg$classExpectation(["&", ":", "]"], true, false, false);
const peg$e38 = peg$otherExpectation("Character");
const peg$e39 = peg$classExpectation(["\n", "\r", "\u2028", "\u2029"], true, false, false);
const peg$e40 = peg$otherExpectation("Blank Line");
const peg$e41 = peg$classExpectation([" ", " "], false, false, false);
const peg$e42 = peg$otherExpectation("Line Terminator");
const peg$e43 = peg$literalExpectation("\n", false);
const peg$e44 = peg$literalExpectation("\r\n", false);
const peg$e45 = peg$classExpectation(["\r", ["\u2028", "\u2029"]], false, false, false);
const peg$e46 = peg$otherExpectation("Whitespace, then Line Terminator");
const peg$e47 = peg$otherExpectation("whitespace");
const peg$e48 = peg$classExpectation([" ", " ", "\n", "\r", "\u2028", "\u2029"], false, false, false);
function peg$f0(firstBit, bits) {
return processor.buildBits([firstBit, ...bits]);
}
function peg$f1(bit) {
return helper.handleRawBit(bit);
}
function peg$f2(bit) {
return bit;
}
function peg$f3(bit) {
return helper.handleRawBit(bit);
}
function peg$f4(bitHeader) {
return bitHeader;
}
function peg$f5(bit) {
return bit;
}
function peg$f6(bitHeader, bitContent) {
return processor.buildBit(bitHeader, bitContent);
}
function peg$f7(bit) {
return processor.invalidBit(bit);
}
function peg$f8(bitLevel, bitType, formatAndResource) {
return processor.buildBitHeader(bitType, bitLevel, formatAndResource);
}
function peg$f9(value1, value2) {
return processor.buildTextAndResourceType(value1, value2);
}
function peg$f10(value) {
return helper.handleTextFormat(value);
}
function peg$f11(value) {
return helper.handleResourceType(value);
}
function peg$f12(value) {
return helper.handleBitContent(value);
}
function peg$f13(value) {
return helper.handleTagChain(value);
}
function peg$f14(value) {
return value;
}
function peg$f15(value) {
return helper.handleBitTag(value);
}
function peg$f16(value) {
return { type: TypeKey.BodyChar, value };
}
function peg$f17(value) {
return helper.handlePlainTextDivider(value);
}
function peg$f18(value) {
return helper.handleFooterDivider(value);
}
function peg$f19(value) {
return helper.handleCardSet(value[1].flat());
}
function peg$f20() {
helper.handleCardSetStart();
}
function peg$f21() {
helper.handleCardSetEnd();
}
function peg$f22(value) {
return helper.handleCards(value);
}
function peg$f23(value) {
return helper.handleCardLineOrDivider(value, 2);
}
function peg$f24(qualifier) {
return ["====", qualifier];
}
function peg$f25() {
return ["===="];
}
function peg$f26(qualifier) {
return ["--", qualifier];
}
function peg$f27() {
return ["--"];
}
function peg$f28(qualifier) {
return ["++", qualifier];
}
function peg$f29() {
return ["++"];
}
function peg$f30(value) {
return helper.handleCardLine(value);
}
function peg$f31(value) {
return helper.handleCardSet(value[1].flat());
}
function peg$f32() {
helper.handleCardSetStart();
}
function peg$f33() {
helper.handleCardSetEnd();
}
function peg$f34(value) {
return helper.handleCards(value);
}
function peg$f35(value) {
return helper.handleCardLineOrDivider(value, 1);
}
function peg$f36(value) {
return helper.handleCardLine(value);
}
function peg$f37(value) {
return value;
}
function peg$f38(value) {
return helper.handleCardContent(value);
}
function peg$f39(value) {
return { type: TypeKey.CardChar, value };
}
function peg$f40(value) {
return helper.handlePropertyTag("id", value);
}
function peg$f41(level, title) {
return helper.handleTag(TypeKey.Title, { level, title });
}
function peg$f42(value) {
return helper.handleTag(TypeKey.Anchor, value);
}
function peg$f43(value) {
return helper.handleTag(TypeKey.Reference, value);
}
function peg$f44(key, value) {
return helper.handlePropertyTag(key, value);
}
function peg$f45(value) {
return helper.handleTag(TypeKey.ItemLead, value);
}
function peg$f46(value) {
return helper.handleTag(TypeKey.Instruction, value);
}
function peg$f47(value) {
return helper.handleTag(TypeKey.Hint, value);
}
function peg$f48(value) {
return helper.handleTag(TypeKey.True, value);
}
function peg$f49(value) {
return helper.handleTag(TypeKey.False, value);
}
function peg$f50(value) {
return helper.handleTag(TypeKey.SampleSolution, value);
}
function peg$f51(value) {
return helper.handleTag(TypeKey.Gap, value);
}
function peg$f52(value) {
return helper.handleTag(TypeKey.Mark, value);
}
function peg$f53(key, value) {
return helper.handleResourceTag(key, value);
}
function peg$f54(value) {
return value;
}
function peg$f55(value) {
return value ? value.trim() : "";
}
function peg$f56(value) {
return value.length;
}
function peg$f57(value) {
return value ? value.trim() : null;
}
function peg$f58(value) {
return value ? value.trim() : "";
}
function peg$f59() {
return true;
}
let peg$currPos = options.peg$currPos | 0;
let peg$savedPos = peg$currPos;
const peg$posDetailsCache = [{ line: 1, column: 1 }];
let peg$maxFailPos = peg$currPos;
let peg$maxFailExpected = options.peg$maxFailExpected || [];
let peg$silentFails = options.peg$silentFails | 0;
let peg$result;
if (options.startRule) {
if (!(options.startRule in peg$startRuleFunctions)) {
throw new Error(`Can't start parsing from rule "` + options.startRule + '".');
}
peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
}
function text() {
return input.substring(peg$savedPos, peg$currPos);
}
function offset() {
return peg$savedPos;
}
function range() {
return {
source: peg$source,
start: peg$savedPos,
end: peg$currPos
};
}
function location() {
return peg$computeLocation(peg$savedPos, peg$currPos);
}
function expected(description, location2) {
location2 = location2 !== void 0 ? location2 : peg$computeLocation(peg$savedPos, peg$currPos);
throw peg$buildStructuredError(
[peg$otherExpectation(description)],
input.substring(peg$savedPos, peg$currPos),
location2
);
}
function error(message, location2) {
location2 = location2 !== void 0 ? location2 : peg$computeLocation(peg$savedPos, peg$currPos);
throw peg$buildSimpleError(message, location2);
}
function peg$getUnicode(pos = peg$currPos) {
const cp = input.codePointAt(pos);
if (cp === void 0) {
return "";
}
return String.fromCodePoint(cp);
}
function peg$literalExpectation(text2, ignoreCase) {
return { type: "literal", text: text2, ignoreCase };
}
function peg$classExpectation(parts, inverted, ignoreCase, unicode) {
return { type: "class", parts, inverted, ignoreCase, unicode };
}
function peg$anyExpectation() {
return { type: "any" };
}
function peg$endExpectation() {
return { type: "end" };
}
function peg$otherExpectation(description) {
return { type: "other", description };
}
function peg$computePosDetails(pos) {
let details = peg$posDetailsCache[pos];
let p;
if (details) {
return details;
} else {
if (pos >= peg$posDetailsCache.length) {
p = peg$posDetailsCache.length - 1;
} else {
p = pos;
while (!peg$posDetailsCache[--p]) {
}
}
details = peg$posDetailsCache[p];
details = {
line: details.line,
column: details.column
};
while (p < pos) {
if (input.charCodeAt(p) === 10) {
details.line++;
details.column = 1;
} else {
details.column++;
}
p++;
}
peg$posDetailsCache[pos] = details;
return details;
}
}
function peg$computeLocation(startPos, endPos, offset2) {
const startPosDetails = peg$computePosDetails(startPos);
const endPosDetails = peg$computePosDetails(endPos);
const res = {
source: peg$source,
start: {
offset: startPos,
line: startPosDetails.line,
column: startPosDetails.column
},
end: {
offset: endPos,
line: endPosDetails.line,
column: endPosDetails.column
}
};
if (offset2 && peg$source && typeof peg$source.offset === "function") {
res.start = peg$source.offset(res.start);
res.end = peg$source.offset(res.end);
}
return res;
}
function peg$fail(expected2) {
if (peg$currPos < peg$maxFailPos) {
return;
}
if (peg$currPos > peg$maxFailPos) {
peg$maxFailPos = peg$currPos;
peg$maxFailExpected = [];
}
peg$maxFailExpected.push(expected2);
}
function peg$buildSimpleError(message, location2) {
return new peg$SyntaxError2(message, null, null, location2);
}
function peg$buildStructuredError(expected2, found, location2) {
return new peg$SyntaxError2(
peg$SyntaxError2.buildMessage(expected2, found),
expected2,
found,
location2
);
}
function peg$parsebitmark() {
let s0;
s0 = peg$parseBM_Bitmark();
return s0;
}
function peg$parseBM_Bitmark() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = [];
s2 = peg$parseWS();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseWS();
}
s2 = peg$parseBM_FirstBit();
s3 = peg$parseBM_Bits();
peg$savedPos = s0;
s0 = peg$f0(s2, s3);
return s0;
}
function peg$parseBM_FirstBit() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = [];
s3 = peg$parseBM_BodyLine();
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseBM_BodyLine();
}
s1 = input.substring(s1, peg$currPos);
peg$savedPos = s0;
s1 = peg$f1(s1);
s0 = s1;
return s0;
}
function peg$parseBM_Bits() {
let s0, s1;
s0 = [];
s1 = peg$parseBM_NL_Bit();
while (s1 !== peg$FAILED) {
s0.push(s1);
s1 = peg$parseBM_NL_Bit();
}
return s0;
}
function peg$parseBM_NL_Bit() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = [];
s2 = peg$parseBlankLine();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseBlankLine();
}
s2 = peg$parseNL();
if (s2 === peg$FAILED) {
s2 = null;
}
s3 = peg$parseBM_Bit();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f2(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseBM_Bit() {
let s0, s1, s2, s3, s4, s5;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$currPos;
s3 = peg$parseBM_BitHeader();
if (s3 !== peg$FAILED) {
s4 = [];
s5 = peg$parseBM_BodyLine();
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parseBM_BodyLine();
}
s3 = [s3, s4];
s2 = s3;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s1 = input.substring(s1, peg$currPos);
} else {
s1 = s2;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f3(s1);
}
s0 = s1;
return s0;
}
function peg$parseBM_BitHeader() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c0) {
s1 = peg$c0;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
s3 = input.charAt(peg$currPos);
if (peg$r0.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e1);
}
}
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = input.charAt(peg$currPos);
if (peg$r0.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e1);
}
}
}
if (input.charCodeAt(peg$currPos) === 93) {
s3 = peg$c1;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e2);
}
}
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f4(s1);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseBM_BodyLine() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parseNL();
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
peg$silentFails++;
s3 = peg$parseBitHeader();
peg$silentFails--;
if (s3 === peg$FAILED) {
s2 = void 0;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = [];
s1 = peg$parseChar();
if (s1 !== peg$FAILED) {
while (s1 !== peg$FAILED) {
s0.push(s1);
s1 = peg$parseChar();
}
} else {
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parsebit() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = [];
s2 = peg$parseBlankLine();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseBlankLine();
}
s2 = peg$parseBit();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parseBlankLine();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parseBlankLine();
}
peg$savedPos = s0;
s0 = peg$f5(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseBit() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseBitHeader();
if (s1 !== peg$FAILED) {
s2 = peg$parseBitContent();
peg$savedPos = s0;
s0 = peg$f6(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseAnything();
s1 = input.substring(s1, peg$currPos);
peg$savedPos = s0;
s1 = peg$f7(s1);
s0 = s1;
}
return s0;
}
function peg$parseBitHeader() {
let s0, s1, s2, s3, s4, s5;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 91) {
s1 = peg$c2;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e3);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseBit_Level();
if (s2 !== peg$FAILED) {
s3 = peg$parseBit_Value();
s4 = peg$parseTextFormatAndResourceType();
if (input.charCodeAt(peg$currPos) === 93) {
s5 = peg$c1;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e2);
}
}
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f8(s2, s3, s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseTextFormatAndResourceType() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$parseTextFormat();
if (s1 === peg$FAILED) {
s1 = peg$parseResourceType();
}
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseTextFormat();
if (s2 === peg$FAILED) {
s2 = peg$parseResourceType();
}
if (s2 === peg$FAILED) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f9(s1, s2);
return s0;
}
function peg$parseTextFormat() {
let s0, s1, s2;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 58) {
s1 = peg$c3;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e4);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseBit_Value();
peg$savedPos = s0;
s0 = peg$f10(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseResourceType() {
let s0, s1, s2;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 38) {
s1 = peg$c4;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e5);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseBit_Value();
peg$savedPos = s0;
s0 = peg$f11(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseBitContent() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = [];
s2 = peg$parseDivider();
if (s2 === peg$FAILED) {
s2 = peg$parseCardSet_V2();
if (s2 === peg$FAILED) {
s2 = peg$parseCardSet_V1();
if (s2 === peg$FAILED) {
s2 = peg$parseTagChain();
if (s2 === peg$FAILED) {
s2 = peg$parseBodyChar();
}
}
}
}
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseDivider();
if (s2 === peg$FAILED) {
s2 = peg$parseCardSet_V2();
if (s2 === peg$FAILED) {
s2 = peg$parseCardSet_V1();
if (s2 === peg$FAILED) {
s2 = peg$parseTagChain();
if (s2 === peg$FAILED) {
s2 = peg$parseBodyChar();
}
}
}
}
}
peg$savedPos = s0;
s1 = peg$f12(s1);
s0 = s1;
return s0;
}
function peg$parseTagChain() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseBitTag();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parseChainedBitTag();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parseChainedBitTag();
}
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f13(s1);
}
s0 = s1;
return s0;
}
function peg$parseChainedBitTag() {
let s0, s1;
s0 = peg$currPos;
s1 = peg$parseBitTag();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f14(s1);
}
s0 = s1;
return s0;
}
function peg$parseBitTag() {
let s0, s1;
s0 = peg$currPos;
s1 = peg$parseIDTag();
if (s1 === peg$FAILED) {
s1 = peg$parsePropertyTag();
if (s1 === peg$FAILED) {
s1 = peg$parseTitleTag();
if (s1 === peg$FAILED) {
s1 = peg$parseAnchorTag();
if (s1 === peg$FAILED) {
s1 = peg$parseReferenceTag();
if (s1 === peg$FAILED) {
s1 = peg$parseItemLeadTag();
if (s1 === peg$FAILED) {
s1 = peg$parseInstructionTag();
if (s1 === peg$FAILED) {
s1 = peg$parseHintTag();
if (s1 === peg$FAILED) {
s1 = peg$parseGapTag();
if (s1 === peg$FAILED) {
s1 = peg$parseMarkTag();
if (s1 === peg$FAILED) {
s1 = peg$parseSampleSolutionTag();
if (s1 === peg$FAILED) {
s1 = peg$parseTrueTag();
if (s1 === peg$FAILED) {
s1 = peg$parseFalseTag();
if (s1 === peg$FAILED) {
s1 = peg$parseResourceTag();
}
}
}
}
}
}
}
}
}
}
}
}
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f15(s1);
}
s0 = s1;
return s0;
}
function peg$parseBodyChar() {
let s0, s1;
s0 = peg$currPos;
if (input.length > peg$currPos) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e6);
}
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f16(s1);
}
s0 = s1;
return s0;
}
function peg$parseDivider() {
let s0;
s0 = peg$parseFooterDivider();
if (s0 === peg$FAILED) {
s0 = peg$parsePlainTextDivider();
}
return s0;
}
function peg$parsePlainTextDividerText() {
let s0, s1, s2, s3, s4, s5;
if (input.substr(peg$currPos, 4) === peg$c5) {
s0 = peg$c5;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c6) {
s1 = peg$c6;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseWS();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseWS();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c7) {
s3 = peg$c7;
peg$currPos += 4;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e9);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
s5 = peg$parseWS();
if (s5 !== peg$FAILED) {
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parseWS();
}
} else {
s4 = peg$FAILED;
}
if (s4 !== peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c6) {
s5 = peg$c6;
peg$currPos += 4;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s5 !== peg$FAILED) {
s1 = [s1, s2, s3, s4, s5];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parsePlainTextDivider() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseNL();
if (s2 !== peg$FAILED) {
s3 = peg$parsePlainTextDividerText();
if (s3 !== peg$FAILED) {
s4 = peg$parseWNL();
if (s4 !== peg$FAILED) {
s2 = [s2, s3, s4];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f17(s1);
}
s0 = s1;
return s0;
}
function peg$parseFooterDividerText() {
let s0, s1, s2, s3, s4, s5;
if (input.substr(peg$currPos, 4) === peg$c8) {
s0 = peg$c8;
peg$currPos += 4;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e10);
}
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c6) {
s1 = peg$c6;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseWS();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseWS();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
if (input.substr(peg$currPos, 6) === peg$c9) {
s3 = peg$c9;
peg$currPos += 6;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e11);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
s5 = peg$parseWS();
if (s5 !== peg$FAILED) {
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parseWS();
}
} else {
s4 = peg$FAILED;
}
if (s4 !== peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c6) {
s5 = peg$c6;
peg$currPos += 4;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s5 !== peg$FAILED) {
s1 = [s1, s2, s3, s4, s5];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parseFooterDivider() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseNL();
if (s2 !== peg$FAILED) {
s3 = peg$parseFooterDividerText();
if (s3 !== peg$FAILED) {
s4 = peg$parseWNL();
if (s4 !== peg$FAILED) {
s2 = [s2, s3, s4];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f18(s1);
}
s0 = s1;
return s0;
}
function peg$parseCardSet_V2() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseCardSetStart_V2();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parseCards_V2();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parseCards_V2();
}
s4 = peg$parseCardSetEnd_V2();
if (s4 !== peg$FAILED) {
s2 = [s2, s3, s4];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f19(s1);
}
s0 = s1;
return s0;
}
function peg$parseCardSetStart_V2() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = [];
s2 = peg$parseWNL();
if (s2 === peg$FAILED) {
s2 = peg$parseNL();
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseWNL();
if (s2 === peg$FAILED) {
s2 = peg$parseNL();
}
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
peg$silentFails++;
s3 = peg$parseCardDividerLookahead_V2();
peg$silentFails--;
if (s3 !== peg$FAILED) {
peg$currPos = s2;
s2 = void 0;
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f20();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseCardSetEnd_V2() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseFooterDividerText();
if (s2 === peg$FAILED) {
s2 = null;
}
s3 = peg$currPos;
peg$silentFails++;
s4 = peg$parseWEOL();
peg$silentFails--;
if (s4 !== peg$FAILED) {
peg$currPos = s3;
s3 = void 0;
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f21();
}
s0 = s1;
return s0;
}
function peg$parseCards_V2() {
let s0, s1;
s0 = peg$currPos;
s1 = peg$parseCardLineOrDivider_V2();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f22(s1);
}
s0 = s1;
return s0;
}
function peg$parseCardLineOrDivider_V2() {
let s0, s1;
s0 = peg$currPos;
s1 = peg$parseCardDividerToken_V2();
if (s1 === peg$FAILED) {
s1 = peg$parseCardSideDividerToken_V2();
if (s1 === peg$FAILED) {
s1 = peg$parseCardVariantDividerToken_V2();
if (s1 === peg$FAILED) {
s1 = peg$parseCardLine_V2();
}
}
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f23(s1);
}
s0 = s1;
return s0;
}
function peg$parseCardDividerToken_V2() {
let s0, s1, s2, s3, s4, s5, s6;
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c6) {
s1 = peg$c6;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s1 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 32) {
s2 = peg$c10;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s2 !== peg$FAILED) {
s3 = peg$parseQualifier();
if (s3 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 32) {
s4 = peg$c10;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s4 !== peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c6) {
s5 = peg$c6;
peg$currPos += 4;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s5 !== peg$FAILED) {
s6 = peg$parseWNL();
if (s6 === peg$FAILED) {
s6 = peg$parseWEOL();
}
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f24(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c6) {
s1 = peg$c6;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseWNL();
if (s2 === peg$FAILED) {
s2 = peg$parseWEOL();
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f25();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parseCardSideDividerToken_V2() {
let s0, s1, s2, s3, s4, s5, s6;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c11) {
s1 = peg$c11;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e13);
}
}
if (s1 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 32) {
s2 = peg$c10;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s2 !== peg$FAILED) {
s3 = peg$parseQualifier();
if (s3 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 32) {
s4 = peg$c10;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s4 !== peg$FAILED) {
if (input.substr(peg$currPos, 2) === peg$c11) {
s5 = peg$c11;
peg$currPos += 2;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e13);
}
}
if (s5 !== peg$FAILED) {
s6 = peg$parseWNL();
if (s6 === peg$FAILED) {
s6 = peg$parseWEOL();
}
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f26(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c11) {
s1 = peg$c11;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e13);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseWNL();
if (s2 === peg$FAILED) {
s2 = peg$parseWEOL();
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f27();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parseCardVariantDividerToken_V2() {
let s0, s1, s2, s3, s4, s5, s6;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c12) {
s1 = peg$c12;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e14);
}
}
if (s1 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 32) {
s2 = peg$c10;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s2 !== peg$FAILED) {
s3 = peg$parseQualifier();
if (s3 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 32) {
s4 = peg$c10;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s4 !== peg$FAILED) {
if (input.substr(peg$currPos, 2) === peg$c12) {
s5 = peg$c12;
peg$currPos += 2;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e14);
}
}
if (s5 !== peg$FAILED) {
s6 = peg$parseWNL();
if (s6 === peg$FAILED) {
s6 = peg$parseWEOL();
}
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f28(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c12) {
s1 = peg$c12;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e14);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseWNL();
if (s2 === peg$FAILED) {
s2 = peg$parseWEOL();
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f29();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parseCardDividerLookahead_V2() {
let s0, s1, s2, s3, s4, s5, s6;
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c6) {
s1 = peg$c6;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 32) {
s3 = peg$c10;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s3 !== peg$FAILED) {
s4 = peg$parseQualifier();
if (s4 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 32) {
s5 = peg$c10;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s5 !== peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c6) {
s6 = peg$c6;
peg$currPos += 4;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s6 !== peg$FAILED) {
s3 = [s3, s4, s5, s6];
s2 = s3;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
if (s2 === peg$FAILED) {
s2 = null;
}
s3 = peg$parseWNL();
if (s3 === peg$FAILED) {
s3 = peg$parseWEOL();
}
if (s3 !== peg$FAILED) {
s1 = [s1, s2, s3];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseCardLine_V2() {
let s0, s1, s2, s3, s4, s5;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$currPos;
s3 = peg$parseFooterDividerText();
if (s3 !== peg$FAILED) {
s4 = peg$parseWEOL();
if (s4 !== peg$FAILED) {
s3 = [s3, s4];
s2 = s3;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
peg$silentFails--;
if (s2 === peg$FAILED) {
s1 = void 0;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = peg$currPos;
s4 = peg$parseLine();
s5 = peg$parseNL();
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 === peg$FAILED) {
s3 = peg$currPos;
s4 = [];
s5 = peg$parseChar();
if (s5 !== peg$FAILED) {
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parseChar();
}
} else {
s4 = peg$FAILED;
}
if (s4 !== peg$FAILED) {
s5 = peg$parseEOL();
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
}
if (s3 !== peg$FAILED) {
s2 = input.substring(s2, peg$currPos);
} else {
s2 = s3;
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f30(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseCardSet_V1() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseCardSetStart_V1();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parseCards_V1();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parseCards_V1();
}
s4 = peg$parseCardSetEnd_V1();
if (s4 !== peg$FAILED) {
s2 = [s2, s3, s4];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f31(s1);
}
s0 = s1;
return s0;
}
function peg$parseCardSetStart_V1() {
let s0, s1, s2, s3, s4, s5;
s0 = peg$currPos;
s1 = [];
s2 = peg$parseWNL();
if (s2 === peg$FAILED) {
s2 = peg$parseNL();
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseWNL();
if (s2 === peg$FAILED) {
s2 = peg$parseNL();
}
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
peg$silentFails++;
s3 = peg$currPos;
if (input.substr(peg$currPos, 3) === peg$c13) {
s4 = peg$c13;
peg$currPos += 3;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s4 !== peg$FAILED) {
s5 = peg$parseWNL();
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
peg$silentFails--;
if (s3 !== peg$FAILED) {
peg$currPos = s2;
s2 = void 0;
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f32();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseCardSetEnd_V1() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = peg$parseFooterDividerText();
if (s2 === peg$FAILED) {
s2 = null;
}
s3 = peg$currPos;
peg$silentFails++;
s4 = peg$parseWEOL();
peg$silentFails--;
if (s4 !== peg$FAILED) {
peg$currPos = s3;
s3 = void 0;
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f33();
}
s0 = s1;
return s0;
}
function peg$parseCards_V1() {
let s0, s1;
s0 = peg$currPos;
s1 = peg$parseCardLineOrDivider_V1();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f34(s1);
}
s0 = s1;
return s0;
}
function peg$parseCardLineOrDivider_V1() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$currPos;
if (input.substr(peg$currPos, 3) === peg$c13) {
s2 = peg$c13;
peg$currPos += 3;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s2 !== peg$FAILED) {
s3 = peg$parseWNL();
if (s3 === peg$FAILED) {
s3 = peg$parseWEOL();
}
if (s3 !== peg$FAILED) {
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 === peg$FAILED) {
s1 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c14) {
s2 = peg$c14;
peg$currPos += 2;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
if (s2 !== peg$FAILED) {
s3 = peg$parseWNL();
if (s3 === peg$FAILED) {
s3 = peg$parseWEOL();
}
if (s3 !== peg$FAILED) {
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 === peg$FAILED) {
s1 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c11) {
s2 = peg$c11;
peg$currPos += 2;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e13);
}
}
if (s2 !== peg$FAILED) {
s3 = peg$parseWNL();
if (s3 === peg$FAILED) {
s3 = peg$parseWEOL();
}
if (s3 !== peg$FAILED) {
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 === peg$FAILED) {
s1 = peg$parseCardLine_V1();
}
}
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f35(s1);
}
s0 = s1;
return s0;
}
function peg$parseCardLine_V1() {
let s0, s1, s2, s3, s4, s5;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
s2 = peg$currPos;
s3 = peg$parseFooterDividerText();
if (s3 !== peg$FAILED) {
s4 = peg$parseWEOL();
if (s4 !== peg$FAILED) {
s3 = [s3, s4];
s2 = s3;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
peg$silentFails--;
if (s2 === peg$FAILED) {
s1 = void 0;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = peg$currPos;
s4 = peg$parseLine();
s5 = peg$parseNL();
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 === peg$FAILED) {
s3 = peg$currPos;
s4 = [];
s5 = peg$parseChar();
if (s5 !== peg$FAILED) {
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parseChar();
}
} else {
s4 = peg$FAILED;
}
if (s4 !== peg$FAILED) {
s5 = peg$parseEOL();
if (s5 !== peg$FAILED) {
s4 = [s4, s5];
s3 = s4;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
}
if (s3 !== peg$FAILED) {
s2 = input.substring(s2, peg$currPos);
} else {
s2 = s3;
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f36(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseQualifier() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
if (input.substr(peg$currPos, 6) === peg$c9) {
s2 = peg$c9;
peg$currPos += 6;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e11);
}
}
if (s2 === peg$FAILED) {
if (input.substr(peg$currPos, 4) === peg$c7) {
s2 = peg$c7;
peg$currPos += 4;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e9);
}
}
}
peg$silentFails--;
if (s2 === peg$FAILED) {
s1 = void 0;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$parseQualifierName();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f37(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseQualifierName() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = input.charAt(peg$currPos);
if (peg$r1.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e17);
}
}
if (s2 !== peg$FAILED) {
s3 = [];
s4 = input.charAt(peg$currPos);
if (peg$r2.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e18);
}
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = input.charAt(peg$currPos);
if (peg$r2.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e18);
}
}
}
s2 = [s2, s3];
s1 = s2;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s0 = input.substring(s0, peg$currPos);
} else {
s0 = s1;
}
return s0;
}
function peg$parsecardContent() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = [];
s2 = peg$parseTagChain();
if (s2 === peg$FAILED) {
s2 = peg$parseCardChar();
}
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseTagChain();
if (s2 === peg$FAILED) {
s2 = peg$parseCardChar();
}
}
peg$savedPos = s0;
s1 = peg$f38(s1);
s0 = s1;
return s0;
}
function peg$parseCardChar() {
let s0, s1;
s0 = peg$currPos;
if (input.length > peg$currPos) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e6);
}
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f39(s1);
}
s0 = s1;
return s0;
}
function peg$parseIDTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 4) === peg$c15) {
s1 = peg$c15;
peg$currPos += 4;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e19);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseKeyValueTag_Value();
if (s2 !== peg$FAILED) {
s3 = peg$parseTag_Close();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f40(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseTitleTag() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 91) {
s1 = peg$c2;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e3);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
if (input.charCodeAt(peg$currPos) === 35) {
s3 = peg$c16;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e20);
}
}
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
if (input.charCodeAt(peg$currPos) === 35) {
s3 = peg$c16;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e20);
}
}
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s3 = peg$parseTag_Value();
s4 = peg$parseTag_CloseOrEOF();
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f41(s2, s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseAnchorTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c17) {
s1 = peg$c17;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e21);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
s3 = peg$parseTag_CloseOrEOF();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f42(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseReferenceTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c18) {
s1 = peg$c18;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e22);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
s3 = peg$parseTag_CloseOrEOF();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f43(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parsePropertyTag() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c19) {
s1 = peg$c19;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e23);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseKeyValueTag_Key();
if (s2 !== peg$FAILED) {
s3 = peg$parseKeyValueTag_Value();
if (s3 !== peg$FAILED) {
s4 = peg$parseTag_CloseOrEOF();
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f44(s2, s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseItemLeadTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c20) {
s1 = peg$c20;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e24);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
s3 = peg$parseTag_CloseOrEOF();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f45(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseInstructionTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c21) {
s1 = peg$c21;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e25);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
s3 = peg$parseTag_CloseOrEOF();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f46(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseHintTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c22) {
s1 = peg$c22;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
s3 = peg$parseTag_CloseOrEOF();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f47(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseTrueTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c23) {
s1 = peg$c23;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e27);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
s3 = peg$parseTag_CloseOrEOF();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f48(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseFalseTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c24) {
s1 = peg$c24;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e28);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
s3 = peg$parseTag_CloseOrEOF();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f49(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseSampleSolutionTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c25) {
s1 = peg$c25;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e29);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
s3 = peg$parseTag_CloseOrEOF();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f50(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseGapTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c26) {
s1 = peg$c26;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e30);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
s3 = peg$parseTag_CloseOrEOF();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f51(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseMarkTag() {
let s0, s1, s2, s3;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c27) {
s1 = peg$c27;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e31);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
s3 = peg$parseTag_CloseOrEOF();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f52(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseResourceTag() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c28) {
s1 = peg$c28;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e32);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseKeyValueTag_Key();
if (s2 !== peg$FAILED) {
s3 = peg$parseKeyValueTag_Value();
if (s3 !== peg$FAILED) {
s4 = peg$parseTag_CloseOrEOF();
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f53(s2, s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseTag_Value() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = [];
if (input.substr(peg$currPos, 2) === peg$c29) {
s3 = peg$c29;
peg$currPos += 2;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e33);
}
}
if (s3 === peg$FAILED) {
s3 = input.charAt(peg$currPos);
if (peg$r0.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e1);
}
}
}
while (s3 !== peg$FAILED) {
s2.push(s3);
if (input.substr(peg$currPos, 2) === peg$c29) {
s3 = peg$c29;
peg$currPos += 2;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e33);
}
}
if (s3 === peg$FAILED) {
s3 = input.charAt(peg$currPos);
if (peg$r0.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e1);
}
}
}
}
s1 = input.substring(s1, peg$currPos);
peg$savedPos = s0;
s1 = peg$f54(s1);
s0 = s1;
return s0;
}
function peg$parseKeyValueTag_Key() {
let s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$currPos;
peg$silentFails++;
if (input.substr(peg$currPos, 3) === peg$c30) {
s2 = peg$c30;
peg$currPos += 3;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e34);
}
}
peg$silentFails--;
if (s2 === peg$FAILED) {
s1 = void 0;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
s2 = peg$currPos;
s3 = [];
s4 = input.charAt(peg$currPos);
if (peg$r3.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e35);
}
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = input.charAt(peg$currPos);
if (peg$r3.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e35);
}
}
}
s2 = input.substring(s2, peg$currPos);
peg$savedPos = s0;
s0 = peg$f55(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseTag_Close() {
let s0;
if (input.charCodeAt(peg$currPos) === 93) {
s0 = peg$c1;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e2);
}
}
return s0;
}
function peg$parseTag_CloseOrEOF() {
let s0, s1, s2;
if (input.charCodeAt(peg$currPos) === 93) {
s0 = peg$c1;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e2);
}
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = [];
s2 = peg$parseWS();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parseWS();
}
s2 = peg$parseEOF();
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parseBit_Level() {
let s0, s1, s2;
s0 = peg$currPos;
s1 = [];
if (input.charCodeAt(peg$currPos) === 46) {
s2 = peg$c31;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e36);
}
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
if (input.charCodeAt(peg$currPos) === 46) {
s2 = peg$c31;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e36);
}
}
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f56(s1);
}
s0 = s1;
return s0;
}
function peg$parseBit_Value() {
let s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$currPos;
s2 = [];
s3 = input.charAt(peg$currPos);
if (peg$r4.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e37);
}
}
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = input.charAt(peg$currPos);
if (peg$r4.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e37);
}
}
}
s1 = input.substring(s1, peg$currPos);
peg$savedPos = s0;
s1 = peg$f57(s1);
s0 = s1;
return s0;
}
function peg$parseKeyValueTag_Value() {
let s0, s1, s2;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 58) {
s1 = peg$c3;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e4);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseTag_Value();
peg$savedPos = s0;
s0 = peg$f58(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = "";
peg$savedPos = s0;
s1 = peg$f59();
s0 = s1;
}
return s0;
}
function peg$parseAnything() {
let s0, s1;
peg$silentFails++;
s0 = [];
if (input.length > peg$currPos) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e6);
}
}
while (s1 !== peg$FAILED) {
s0.push(s1);
if (input.length > peg$currPos) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e6);
}
}
}
peg$silentFails--;
return s0;
}
function peg$parseChar() {
let s0, s1;
peg$silentFails++;
s0 = input.charAt(peg$currPos);
if (peg$r5.test(s0)) {
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e39);
}
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e38);
}
}
return s0;
}
function peg$parseLine() {
let s0, s1;
peg$silentFails++;
s0 = [];
s1 = peg$parseChar();
while (s1 !== peg$FAILED) {
s0.push(s1);
s1 = peg$parseChar();
}
peg$silentFails--;
return s0;
}
function peg$parseBlankLine() {
let s0, s1, s2;
peg$silentFails++;
s0 = peg$currPos;
s1 = [];
s2 = input.charAt(peg$currPos);
if (peg$r6.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e41);
}
}
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = input.charAt(peg$currPos);
if (peg$r6.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e41);
}
}
}
s2 = peg$parseNL();
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e40);
}
}
return s0;
}
function peg$parseNL() {
let s0, s1;
peg$silentFails++;
if (input.charCodeAt(peg$currPos) === 10) {
s0 = peg$c32;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e43);
}
}
if (s0 === peg$FAILED) {
if (input.substr(peg$currPos, 2) === peg$c33) {
s0 = peg$c33;
peg$currPos += 2;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e44);
}
}
if (s0 === peg$FAILED) {
s0 = input.charAt(peg$currPos);
if (peg$r7.test(s0)) {
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e45);
}
}
}
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e42);
}
}
return s0;
}
function peg$parseWNL() {
let s0, s1, s2;
peg$silentFails++;
s0 = peg$currPos;
s1 = [];
s2 = input.charAt(peg$currPos);
if (peg$r6.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e41);
}
}
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = input.charAt(peg$currPos);
if (peg$r6.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e41);
}
}
}
s2 = peg$parseNL();
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e46);
}
}
return s0;
}
function peg$parseWS() {
let s0, s1;
peg$silentFails++;
s0 = input.charAt(peg$currPos);
if (peg$r8.test(s0)) {
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e48);
}
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e47);
}
}
return s0;
}
function peg$parseEOL() {
let s0, s1;
s0 = peg$parseNL();
if (s0 === peg$FAILED) {
s0 = peg$currPos;
peg$silentFails++;
if (input.length > peg$currPos) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e6);
}
}
peg$silentFails--;
if (s1 === peg$FAILED) {
s0 = void 0;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parseWEOL() {
let s0, s1;
s0 = peg$parseWNL();
if (s0 === peg$FAILED) {
s0 = peg$currPos;
peg$silentFails++;
if (input.length > peg$currPos) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e6);
}
}
peg$silentFails--;
if (s1 === peg$FAILED) {
s0 = void 0;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
return s0;
}
function peg$parseEOF() {
let s0, s1;
s0 = peg$currPos;
peg$silentFails++;
if (input.length > peg$currPos) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e6);
}
}
peg$silentFails--;
if (s1 === peg$FAILED) {
s0 = void 0;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
const helper = new BitmarkPegParserHelper({
parse: peg$parse2,
parserText: text,
parserLocation
});
const processor = new BitmarkPegParserProcessor({
parse: peg$parse2,
parserText: text,
parserLocation
});
function parserLocation() {
const l = location();
if (!l) return l;
delete l.source;
return l;
}
function peg$computeLocation(startPos, endPos, offset2) {
offset2 = true;
var startPosDetails = peg$computePosDetails(startPos);
var endPosDetails = peg$computePosDetails(endPos);
var res = {
source: peg$source,
start: {
offset: startPos,
line: startPosDetails.line,
column: startPosDetails.column
},
end: {
offset: endPos,
line: endPosDetails.line,
column: endPosDetails.column
}
};
if (offset2 && peg$source && typeof peg$source.offset === "function") {
res.start = peg$source.offset(res.start);
res.end = peg$source.offset(res.end);
}
return res;
}
peg$result = peg$startRuleFunction();
const peg$success = peg$result !== peg$FAILED && peg$currPos === input.length;
function peg$throw() {
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
peg$fail(peg$endExpectation());
}
throw peg$buildStructuredError(
peg$maxFailExpected,
peg$maxFailPos < input.length ? peg$getUnicode(peg$maxFailPos) : null,
peg$maxFailPos < input.length ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
);
}
if (options.peg$library) {
return (
/** @type {any} */
{
peg$result,
peg$currPos,
peg$FAILED,
peg$maxFailExpected,
peg$maxFailPos,
peg$success,
peg$throw: peg$success ? void 0 : peg$throw
}
);
}
if (peg$success) {
return peg$result;
} else {
peg$throw();
}
}
// src/parser/bitmark/peg/BitmarkPegParser.ts
var parse2 = peg$parse2;
// src/parser/bitmark/BitmarkParser.ts
var BitmarkParser = class {
constructor() {
__publicField(this, "jsonParser", new JsonParser());
}
/**
* Convert Bitmark markup to AST.
*
* The Bitmark markup should be a string.
*
* @param json - bitmark JSON as a string or a plain JS object
* @param _options - unused
* @returns bitmark AST
*/
toAst(bitmark, _options) {
return parse2(bitmark);
}
};
// src/parser/html/HtmlDom.ts
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"meta",
"param",
"source",
"track",
"wbr"
]);
var IMPLIED_CLOSE = {
li: /* @__PURE__ */ new Set(["li"]),
td: /* @__PURE__ */ new Set(["td", "th"]),
th: /* @__PURE__ */ new Set(["td", "th"]),
tr: /* @__PURE__ */ new Set(["td", "th", "tr"]),
thead: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
tbody: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
tfoot: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
p: /* @__PURE__ */ new Set(["p"]),
option: /* @__PURE__ */ new Set(["option"])
};
var TAG_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9:-]*)((?:\s+(?:"[^"]*"|'[^']*'|[^<>"'])*)?)(\/)?>/g;
var ATTR_RE = /([a-zA-Z_:][a-zA-Z0-9:._-]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
function parseAttrs(raw) {
const attrs = {};
if (!raw) return attrs;
ATTR_RE.lastIndex = 0;
let m;
while ((m = ATTR_RE.exec(raw)) !== null) {
const name = m[1].toLowerCase();
let value = m[2] ?? "";
if (value.length >= 2 && (value[0] === '"' || value[0] === "'")) {
value = value.slice(1, -1);
}
attrs[name] = value;
}
return attrs;
}
function stripNonContent(html) {
return html.replace(/<!--[\s\S]*?-->/g, "").replace(/<!\[CDATA\[[\s\S]*?\]\]>/g, "").replace(/<!doctype[^>]*>/gi, "").replace(/<script\b[\s\S]*?<\/script>/gi, "").replace(/<style\b[\s\S]*?<\/style>/gi, "");
}
function tokenize(html) {
const tokens = [];
const clean = stripNonContent(html);
TAG_RE.lastIndex = 0;
let lastIndex = 0;
let m;
while ((m = TAG_RE.exec(clean)) !== null) {
if (m.index > lastIndex) {
tokens.push({ kind: "text", text: clean.slice(lastIndex, m.index) });
}
lastIndex = TAG_RE.lastIndex;
const isClose = m[1] === "/";
const name = m[2].toLowerCase();
if (isClose) {
tokens.push({ kind: "close", name });
} else {
tokens.push({
kind: "open",
name,
attrs: parseAttrs(m[3]),
selfClose: m[4] === "/"
});
}
}
if (lastIndex < clean.length) {
tokens.push({ kind: "text", text: clean.slice(lastIndex) });
}
return tokens;
}
function parseHtml(html) {
const root = { type: "element", name: "#root", attrs: {}, children: [] };
const stack = [root];
const top = () => stack[stack.length - 1];
for (const token of tokenize(html)) {
if (token.kind === "text") {
if (token.text) top().children.push({ type: "text", value: token.text });
continue;
}
if (token.kind === "open") {
const name = token.name;
const closes = IMPLIED_CLOSE[name];
if (closes) {
while (stack.length > 1 && closes.has(top().name)) stack.pop();
}
const el = {
type: "element",
name,
attrs: token.attrs ?? {},
children: []
};
top().children.push(el);
if (!token.selfClose && !VOID_ELEMENTS.has(name)) {
stack.push(el);
}
continue;
}
for (let i = stack.length - 1; i > 0; i--) {
if (stack[i].name === token.name) {
stack.length = i;
break;
}
}
}
return root.children;
}
function childElements(el, names) {
return el.children.filter((c) => c.type === "element" && names.has(c.name));
}
// src/parser/html/HtmlTableParser.ts
var MARK_TAGS = {
b: "bold",
strong: "bold",
i: "italic",
em: "italic",
sup: "superscript",
sub: "subscript",
s: "strike",
strike: "strike",
del: "del",
ins: "ins",
u: "underline",
mark: "highlight",
code: "code"
};
var SECTION_TAGS = /* @__PURE__ */ new Set(["thead", "tbody", "tfoot"]);
var ROW_TAGS = /* @__PURE__ */ new Set(["tr"]);
var CELL_TAGS = /* @__PURE__ */ new Set(["td", "th"]);
var LI_TAGS = /* @__PURE__ */ new Set(["li"]);
var VALID_SCOPES = /* @__PURE__ */ new Set(["row", "col", "rowgroup", "colgroup"]);
var NAMED_ENTITIES = {
amp: "&",
lt: "<",
gt: ">",
quot: '"',
apos: "'",
nbsp: "\xA0"
};
function decodeEntities(text) {
return text.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]*);/g, (whole, body) => {
if (body[0] === "#") {
const code = body[1] === "x" || body[1] === "X" ? Number.parseInt(body.slice(2), 16) : Number.parseInt(body.slice(1), 10);
if (Number.isFinite(code) && code >= 0 && code <= 1114111) {
try {
return String.fromCodePoint(code);
} catch {
return whole;
}
}
return whole;
}
return NAMED_ENTITIES[body] ?? whole;
});
}
function intAttr(value) {
if (value == null) return void 0;
const n = Number.parseInt(value, 10);
return Number.isInteger(n) ? n : void 0;
}
function isElement(node) {
return node.type === "element";
}
var HtmlTableParser = class {
constructor() {
__publicField(this, "keepUnknownTags", false);
}
/**
* Parse an HTML string and return one table bit per top-level `<table>`.
*/
parse(html, options) {
this.keepUnknownTags = options?.keepUnknownTags ?? false;
const tableFormat = options?.tableFormat ?? "table-extended";
const onWarning = options?.onWarning;
const tables = this.collectTopLevelTables(parseHtml(html));
const bits = [];
for (const table of tables) {
const bit = this.tableToBit(table, tableFormat, onWarning);
if (bit) bits.push(bit);
}
return bits;
}
// --- table extraction -----------------------------------------------------
/** Collect every `<table>` element, NOT descending into nested tables. */
collectTopLevelTables(nodes) {
const tables = [];
const visit = (node) => {
if (!isElement(node)) return;
if (node.name === "table") {
tables.push(node);
return;
}
node.children.forEach(visit);
};
nodes.forEach(visit);
return tables;
}
tableToBit(table, tableFormat, onWarning) {
const header = [];
const body = [];
const footer = [];
let caption;
for (const child of table.children) {
if (!isElement(child)) continue;
if (child.name === "caption") {
const capContent = this.inlineNodes(child.children, []);
const trimmed = this.trimInline(capContent);
if (trimmed.length > 0) caption = trimmed;
} else if (SECTION_TAGS.has(child.name)) {
const target = child.name === "thead" ? header : child.name === "tfoot" ? footer : body;
for (const tr of childElements(child, ROW_TAGS)) {
target.push(this.rowToCells(tr));
}
} else if (ROW_TAGS.has(child.name)) {
body.push(this.rowToCells(child));
}
}
if (header.length === 0 && body.length === 0 && footer.length === 0) {
return void 0;
}
const tableExtended = {};
if (header.length > 0) tableExtended.header = { rows: header };
if (body.length > 0) tableExtended.body = { rows: body };
if (footer.length > 0) tableExtended.footer = { rows: footer };
let tableData = tableExtended;
if (tableFormat === "table") {
if (this.isLossyAsBasic(tableExtended)) {
onWarning?.(
"Converting an HTML table to [.table] loses rowspan/colspan/scope and multi-section structure; use --tableFormat table-extended to preserve them."
);
}
tableData = convertExtendedToBasicTableFormat(tableExtended);
}
const bit = {
type: tableFormat,
format: "bitmark++",
table: tableData
};
if (caption) bit.caption = caption;
return bit;
}
rowToCells(tr) {
const cells = [];
for (const cell of childElements(tr, CELL_TAGS)) {
cells.push(this.cellToJson(cell));
}
return { cells };
}
cellToJson(cell) {
const json = {
content: this.blockNodes(cell.children)
};
if (cell.name === "th") json.title = true;
const rowspan = intAttr(cell.attrs.rowspan);
if (rowspan != null && rowspan >= 2) json.rowspan = rowspan;
const colspan = intAttr(cell.attrs.colspan);
if (colspan != null && colspan >= 2) json.colspan = colspan;
const scope = cell.attrs.scope?.toLowerCase();
if (scope && VALID_SCOPES.has(scope)) {
json.scope = scope;
}
const width = this.widthFromCell(cell);
if (width != null) json.colwidth = width;
return json;
}
widthFromCell(cell) {
const attrWidth = intAttr(cell.attrs.width);
if (attrWidth != null && attrWidth > 0) return attrWidth;
const style = cell.attrs.style;
if (style) {
const m = style.match(/width\s*:\s*(\d+)\s*px/i);
if (m) {
const n = Number.parseInt(m[1], 10);
if (Number.isInteger(n) && n > 0) return n;
}
}
return void 0;
}
isLossyAsBasic(table) {
if (table.footer) return true;
if (table.header && table.header.rows.length > 1) return true;
const sections = [table.header, table.body, table.footer];
for (const section of sections) {
if (!section) continue;
for (const row of section.rows) {
for (const cell of row.cells) {
if (cell.rowspan || cell.colspan || cell.scope) return true;
}
}
}
return false;
}
// --- content conversion (HTML -> TextAst) ---------------------------------
/**
* Convert a cell/li's child nodes into block-level TextAst nodes (paragraphs, lists, images).
*/
blockNodes(nodes) {
const blocks = [];
let inline = [];
const flush = () => {
const trimmed = this.trimInline(inline);
inline = [];
if (trimmed.length === 0) return;
if (trimmed.length === 1 && trimmed[0].type === "imageInline") {
blocks.push({ ...trimmed[0], type: "image" });
} else {
blocks.push({ type: "paragraph", content: trimmed, attrs: {} });
}
};
for (const node of nodes) {
if (!isElement(node)) {
inline.push(...this.inlineText(node.value, []));
continue;
}
const name = node.name;
if (name === "p") {
flush();
const content = this.trimInline(this.inlineNodes(node.children, []));
if (content.length > 0) {
blocks.push({ type: "paragraph", content, attrs: {} });
}
} else if (this.isListTag(name)) {
flush();
const list = this.listNode(node);
if (list) blocks.push(list);
} else {
inline.push(...this.inlineFromElement(node, []));
}
}
flush();
return blocks;
}
/** Convert child nodes to a flat list of inline TextNodes, applying the given marks. */
inlineNodes(nodes, marks) {
const out = [];
for (const node of nodes) {
if (!isElement(node)) {
out.push(...this.inlineText(node.value, marks));
} else {
out.push(...this.inlineFromElement(node, marks));
}
}
return out;
}
inlineFromElement(el, marks) {
const name = el.name;
if (name === "br") {
return [{ type: "hardBreak" }];
}
if (name === "img") {
return [this.imageNode(el, true)];
}
if (this.isMathTag(name)) {
return [this.latexNode(el)];
}
const markType = MARK_TAGS[name];
if (markType) {
return this.inlineNodes(el.children, [...marks, { type: markType }]);
}
if (name === "a") {
const linkMark = {
type: "link",
attrs: {
href: el.attrs.href ?? "",
...el.attrs.target ? { target: el.attrs.target } : {}
}
};
return this.inlineNodes(el.children, [...marks, linkMark]);
}
if (this.keepUnknownTags) {
const open = this.serializeOpenTag(el);
const close = `</${name}>`;
return [
...this.inlineText(open, marks),
...this.inlineNodes(el.children, marks),
...this.inlineText(close, marks)
];
}
return this.inlineNodes(el.children, marks);
}
inlineText(raw, marks) {
const text = decodeEntities(raw).replace(/\s+/g, " ");
if (text === "") return [];
const node = { text, type: "text" };
if (marks.length > 0) node.marks = marks.map((m) => ({ ...m }));
return [node];
}
serializeOpenTag(el) {
const attrs = Object.entries(el.attrs).map(([k, v]) => v === "" ? ` ${k}` : ` ${k}="${v}"`).join("");
return `<${el.name}${attrs}>`;
}
// --- lists ----------------------------------------------------------------
isListTag(name) {
return name === "ul" || name === "ol";
}
listNode(el) {
const items = childElements(el, LI_TAGS);
if (items.length === 0) return void 0;
const isTask = el.name === "ul" && items.some((li) => {
const firstEl = li.children.find(isElement);
return firstEl?.name === "input" && firstEl.attrs.type?.toLowerCase() === "checkbox";
});
if (isTask) {
return {
type: "taskList",
content: items.map((li) => this.taskItemNode(li))
};
}
const type = this.listType(el);
const node = {
type,
content: items.map((li) => this.listItemNode(li))
};
if (el.name === "ol") {
const start = intAttr(el.attrs.start);
if (start != null) {
node.attrs = { start };
}
}
return node;
}
listType(el) {
if (el.name === "ol") {
switch (el.attrs.type) {
case "i":
return "orderedListRomanLower";
case "I":
return "orderedListRoman";
case "a":
return "letteredListLower";
case "A":
return "letteredList";
default:
return "orderedList";
}
}
const style = el.attrs.style ?? "";
if (/list-style(-type)?\s*:\s*none/i.test(style)) return "noBulletList";
return "bulletList";
}
listItemNode(li) {
let content = this.blockNodes(li.children);
if (content.length === 0) {
content = [{ type: "paragraph", content: [], attrs: {} }];
}
return { type: "listItem", content };
}
taskItemNode(li) {
const checkbox = li.children.find(
(c) => isElement(c) && c.name === "input" && c.attrs.type?.toLowerCase() === "checkbox"
);
const checked = checkbox ? "checked" in checkbox.attrs : false;
const rest = li.children.filter((c) => c !== checkbox);
let content = this.blockNodes(rest);
if (content.length === 0) {
content = [{ type: "paragraph", content: [], attrs: {} }];
}
return { type: "taskItem", attrs: { checked }, content };
}
// --- images ---------------------------------------------------------------
imageNode(el, inline) {
const attrs = { src: el.attrs.src ?? "" };
attrs.alt = el.attrs.alt ?? null;
attrs.title = el.attrs.title ?? null;
const width = intAttr(el.attrs.width);
if (width != null) attrs.width = width;
const height = intAttr(el.attrs.height);
if (height != null) attrs.height = height;
if (el.attrs.class) attrs.class = el.attrs.class;
return { type: inline ? "imageInline" : "image", attrs };
}
// --- formulas -------------------------------------------------------------
isMathTag(name) {
return this.localName(name) === "math";
}
latexNode(el) {
return {
type: "latex",
attrs: {
formula: this.latexFromMath(el)
}
};
}
latexFromMath(el) {
const alt = el.attrs.alttext ?? el.attrs.alt ?? el.attrs.title;
if (alt != null) return decodeEntities(alt);
const annotation = this.findLatexAnnotation(el);
if (annotation) return this.textContent(annotation).trim();
return this.textContent(el).trim();
}
findLatexAnnotation(el) {
for (const child of el.children) {
if (!isElement(child)) continue;
const lowerEncoding = (child.attrs.encoding ?? "").toLowerCase();
if (this.localName(child.name) === "annotation" && (/\btex\b/.test(lowerEncoding) || /\blatex\b/.test(lowerEncoding))) {
return child;
}
const nested = this.findLatexAnnotation(child);
if (nested) return nested;
}
return void 0;
}
textContent(el) {
return el.children.map((child) => isElement(child) ? this.textContent(child) : decodeEntities(child.value)).join("").replace(/\s+/g, " ");
}
localName(name) {
return name.includes(":") ? name.slice(name.indexOf(":") + 1) : name;
}
// --- whitespace -----------------------------------------------------------
/** Trim leading/trailing whitespace from inline content (edges only). */
trimInline(nodes) {
const out = nodes.slice();
const isWs = (n) => n.type === "text" && /^\s*$/.test(n.text ?? "");
while (out.length > 0 && isWs(out[0])) out.shift();
if (out.length > 0 && out[0].type === "text") {
out[0] = { ...out[0], text: (out[0].text ?? "").replace(/^\s+/, "") };
}
while (out.length > 0 && isWs(out[out.length - 1])) out.pop();
if (out.length > 0 && out[out.length - 1].type === "text") {
const last = out.length - 1;
out[last] = { ...out[last], text: (out[last].text ?? "").replace(/\s+$/, "") };
}
return out.filter((n) => !(n.type === "text" && n.text === ""));
}
};
// src/BitmarkParserGenerator.ts
import path4 from "path";
import fs4 from "fs-extra";
// src/ast/writer/FileWriter.ts
import "fs";
import path from "path";
import fs from "fs-extra";
// src/ast/writer/StreamWriter.ts
import os from "os";
var StreamWriter = class {
constructor() {
__publicField(this, "_stream");
__publicField(this, "endOfLineString", os.EOL);
__publicField(this, "lastWrite", "");
}
get isSync() {
return false;
}
/**
* Get the current write stream
*/
get stream() {
return this._stream;
}
/**
* Set the current write stream
*/
set stream(stream) {
this._stream = stream;
}
openSync() {
throw new Error("Sync operation not supported");
}
closeSync() {
throw new Error("Sync operation not supported");
}
writeLine(value) {
if (!this._stream) return this;
if (value != null) {
this._stream.write(value + this.endOfLineString);
this.lastWrite = value + this.endOfLineString;
} else {
this._stream.write(this.endOfLineString);
this.lastWrite = this.endOfLineString;
}
return this;
}
writeLines(values, delimiter) {
if (!this._stream) return this;
if (!values) return this;
let str = "";
for (let i = 0, len = values.length; i < len; i++) {
const value = values[i];
this._stream.write(value);
str += value;
if (delimiter && i < len - 1) {
this._stream.write(delimiter);
str += delimiter;
}
this._stream.write(this.endOfLineString);
str += this.endOfLineString;
}
this.lastWrite = str;
return this;
}
write(value) {
if (!this._stream) return this;
if (value == null) return this;
if (value) this.lastWrite = value;
this._stream.write(value);
return this;
}
writeWhiteSpace() {
this.write(" ");
return this;
}
getLastWrite() {
return this.lastWrite;
}
};
// src/ast/writer/FileWriter.ts
var FileWriter = class extends StreamWriter {
/**
* Create a writer to write to a file.
*
* @param path - path of file to write
* @param options - options for file writing
*/
constructor(path5, options) {
super();
__publicField(this, "_path");
__publicField(this, "_append");
__publicField(this, "_encoding");
const opts = Object.assign({}, options);
this._path = path5;
this._append = !!opts.append;
this._encoding = opts.encoding ?? "utf8";
}
get path() {
return this._path;
}
get append() {
return this._append;
}
get encoding() {
return this._encoding;
}
async open() {
return new Promise((resolve, reject) => {
const flags = this._append ? "a" : "w";
fs.ensureDirSync(path.dirname(this._path.toString()));
const stream = fs.createWriteStream(this._path, {
flags,
encoding: "utf8"
});
this.stream = stream;
if (!stream) reject(new Error("stream undefined"));
stream.once("open", (_fd) => {
resolve();
});
stream.once("error", (e) => {
reject(e);
});
});
}
async close() {
return new Promise((resolve, reject) => {
const stream = this.stream;
if (!stream) reject(new Error("stream not set"));
stream.once("close", (_fd) => {
resolve();
});
stream.once("error", (e) => {
reject(e);
});
stream.end();
});
}
};
// src/generator/bitmark/BitmarkFileGenerator.ts
import "fs";
// src/ast/writer/SyncFileWriter.ts
import "fs";
import path2 from "path";
import fs2 from "fs-extra";
import os2 from "os";
var SyncFileWriter = class {
/**
* Create a writer to write to a file.
*
* @param path - path of file to write
* @param options - options for file writing
*/
constructor(path5, options) {
__publicField(this, "_path");
__publicField(this, "_append");
__publicField(this, "_encoding");
__publicField(this, "_open");
__publicField(this, "endOfLineString", os2.EOL);
__publicField(this, "lastWrite", "");
const opts = Object.assign({}, options);
this._path = path5;
this._append = !!opts.append;
this._encoding = opts.encoding ?? "utf8";
this._open = false;
}
get isSync() {
return true;
}
get path() {
return this._path;
}
get append() {
return this._append;
}
get encoding() {
return this._encoding;
}
async open() {
this.openSync();
return Promise.resolve();
}
async close() {
try {
this.closeSync();
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve();
}
openSync() {
const flag = this._append ? "a" : "w";
fs2.ensureDirSync(path2.dirname(this._path.toString()));
fs2.writeFileSync(this._path, "", {
flag,
encoding: this._encoding
});
this._open = true;
}
closeSync() {
this._open = false;
}
writeLine(value) {
if (!this._open) return this;
let line;
if (value != null) {
line = value + this.endOfLineString;
} else {
line = this.endOfLineString;
}
fs2.appendFileSync(this._path, line, {
encoding: this._encoding
});
this.lastWrite = line;
return this;
}
writeLines(values, delimiter) {
if (!this._open) return this;
if (!values) return this;
let str = "";
for (let i = 0, len = values.length; i < len; i++) {
const value = values[i];
str += value;
if (delimiter && i < len - 1) {
str += delimiter;
}
str += this.endOfLineString;
}
fs2.appendFileSync(this._path, str, {
encoding: this._encoding
});
this.lastWrite = str;
return this;
}
write(value) {
if (!this._open) return this;
if (value == null) return this;
if (value) this.lastWrite = value;
fs2.appendFileSync(this._path, value, {
encoding: this._encoding
});
return this;
}
writeWhiteSpace() {
this.write(" ");
return this;
}
getLastWrite() {
return this.lastWrite;
}
};
// src/generator/bitmark/BitmarkFileGenerator.ts
var BitmarkFileGenerator = class {
/**
* Generate bitmark markup from a bitmark AST as a file
*
* @param path - path of file to generate
* @param options - bitmark generation options
*/
constructor(path5, options) {
__publicField(this, "generator");
__publicField(this, "async");
this.async = options?.async ?? false;
const writer = this.async ? new FileWriter(path5, options?.fileOptions) : new SyncFileWriter(path5, options?.fileOptions);
this.generator = new BitmarkGenerator(writer, options);
}
/**
* Generate bitmark markup from bitmark AST as a file
*
* @param ast bitmark AST
*/
async generate(ast) {
return this.generator.generate(ast);
}
/**
* Generate bitmark markup from bitmark AST as a file synchronously
*
* @param ast bitmark AST
*/
generateSync(_ast) {
if (this.async) {
throw new Error("Sync operation not supported");
}
this.generator.generateSync(_ast);
}
};
// src/generator/json/JsonFileGenerator.ts
import "fs";
var JsonFileGenerator = class {
/**
* Generate bitmark JSON from a bitmark AST as a file
*
* @param path - path of file to generate
* @param bitmarkVersion - bitmark version, use null to use latest version
* @param fileOptions - file options
* @param bitmarkOptions - bitmark generation options
*/
constructor(path5, options) {
__publicField(this, "generator");
__publicField(this, "async");
this.async = options?.async ?? false;
const writer = this.async ? new FileWriter(path5, options?.fileOptions) : new SyncFileWriter(path5, options?.fileOptions);
this.generator = new JsonGenerator(writer, options);
}
/**
* Generate bitmark JSON from bitmark AST as a file
*
* @param ast bitmark AST
*/
async generate(ast) {
return this.generator.generate(ast);
}
/**
* Generate bitmark JSON from bitmark AST as a file synchronously
*
* @param ast bitmark AST
*/
generateSync(_ast) {
if (this.async) {
throw new Error("Sync operation not supported");
}
this.generator.generateSync(_ast);
}
};
// src/generator/plainText/PlainTextGenerator.ts
var TEXT_NODE_TYPES = new Set(Object.values(TextNodeType));
var PlainTextGenerator = class {
/**
* Generate plain text from a string or JSON object.
*
* @param input - A string (plain or JSON-encoded) or a parsed JSON value.
* @returns The extracted plain text.
*/
generate(input) {
let data = input;
if (typeof data === "string") {
try {
data = JSON.parse(data);
} catch (_e) {
return data;
}
}
return this.walk(data).trim();
}
// ---------------------------------------------------------------------------
// Private helpers
// ---------------------------------------------------------------------------
walk(value) {
if (value == null) return "";
if (typeof value === "string") return value;
if (typeof value !== "object") return "";
if (Array.isArray(value)) {
if (value.length === 0) return "";
if (this.isTextAst(value)) {
return this.textAstToPlainText(value);
}
return value.map((v) => this.walk(v)).filter(Boolean).join("\n");
}
const obj = value;
if (this.isTextNode(obj)) {
return this.textNodeToPlainText(obj);
}
if (this.isBitWrapper(obj)) {
return this.walk(obj["bit"]);
}
const parts = [];
for (const val of Object.values(obj)) {
if (val == null || typeof val !== "object") continue;
const text = this.walk(val);
if (text) parts.push(text);
}
return parts.join("\n");
}
// -- Type guards -----------------------------------------------------------
isTextNode(obj) {
return typeof obj["type"] === "string" && TEXT_NODE_TYPES.has(obj["type"]);
}
isTextAst(arr) {
const first = arr[0];
return typeof first === "object" && first !== null && !Array.isArray(first) && this.isTextNode(first);
}
isBitWrapper(obj) {
return "bit" in obj && typeof obj["bit"] === "object" && obj["bit"] !== null && !Array.isArray(obj["bit"]);
}
// -- TextNode extraction ---------------------------------------------------
textAstToPlainText(ast) {
return ast.map((node) => this.textNodeToPlainText(node)).join("\n");
}
textNodeToPlainText(node) {
const { type, text, content } = node;
switch (type) {
case TextNodeType.text:
return this.textWithMarks(node);
case TextNodeType.hardBreak:
return "\n";
// Block elements whose children are joined without extra separator
case TextNodeType.paragraph:
case TextNodeType.heading:
case TextNodeType.section:
case TextNodeType.gap:
case TextNodeType.select:
case TextNodeType.highlight:
case TextNodeType.mark:
case TextNodeType.codeBlock:
return content ? content.map((c) => this.textNodeToPlainText(c)).join("") : text ?? "";
// List items are handled by listToPlainText with indent context
case TextNodeType.listItem:
return content ? content.map((c) => this.textNodeToPlainText(c)).join("") : text ?? "";
// Task item – handled by taskListToPlainText, but fallback if encountered standalone
case TextNodeType.taskItem: {
const checked = node.attrs?.checked ?? false;
const prefix = checked ? "[x] " : "[ ] ";
const itemText = content ? content.map((c) => this.textNodeToPlainText(c)).join("") : text ?? "";
return `${prefix}${itemText}`;
}
// List containers – rendered with indent-aware helper
case TextNodeType.noBulletList:
case TextNodeType.bulletList:
case TextNodeType.orderedList:
case TextNodeType.orderedListRoman:
case TextNodeType.orderedListRomanLower:
case TextNodeType.letteredList:
case TextNodeType.letteredListLower:
return this.listToPlainText(node, 0);
// Task list – rendered with indent-aware helper
case TextNodeType.taskList:
return this.taskListToPlainText(node, 0);
// Images – return alt text when available
case TextNodeType.image:
case TextNodeType.imageInline: {
const attrs = node.attrs;
return attrs?.alt ?? "";
}
// LaTeX – return the formula source
case TextNodeType.latex: {
const latexAttrs = node.attrs;
return latexAttrs?.formula ?? "";
}
default:
return content ? content.map((c) => this.textNodeToPlainText(c)).join("") : text ?? "";
}
}
listToPlainText(node, depth) {
const { type, content } = node;
if (!content || content.length === 0) return "";
const indent = " ".repeat(depth);
const start = node.attrs?.start ?? 1;
const displayStart = start < 1 ? start + 1 : start;
return content.map((child, i) => {
const { inline, nested } = this.splitListItemContent(child, depth);
const prefix = this.listItemPrefix(type, displayStart + i);
const line = `${indent}${prefix}${inline}`;
return nested ? `${line}
${nested}` : line;
}).join("\n");
}
taskListToPlainText(node, depth) {
const { content } = node;
if (!content || content.length === 0) return "";
const indent = " ".repeat(depth);
return content.map((child) => {
const checked = child.attrs?.checked ?? false;
const prefix = checked ? "[x] " : "[ ] ";
const { inline, nested } = this.splitListItemContent(child, depth);
const line = `${indent}${prefix}${inline}`;
return nested ? `${line}
${nested}` : line;
}).join("\n");
}
splitListItemContent(item, depth) {
const children = item.content ?? [];
const inlineParts = [];
const nestedParts = [];
for (const child of children) {
if (this.isListType(child.type)) {
nestedParts.push(this.renderNestedList(child, depth + 1));
} else {
inlineParts.push(this.textNodeToPlainText(child));
}
}
return {
inline: inlineParts.join(""),
nested: nestedParts.join("\n")
};
}
isListType(type) {
return type === TextNodeType.bulletList || type === TextNodeType.orderedList || type === TextNodeType.orderedListRoman || type === TextNodeType.orderedListRomanLower || type === TextNodeType.letteredList || type === TextNodeType.letteredListLower || type === TextNodeType.noBulletList || type === TextNodeType.taskList;
}
renderNestedList(node, depth) {
if (node.type === TextNodeType.taskList) {
return this.taskListToPlainText(node, depth);
}
return this.listToPlainText(node, depth);
}
listItemPrefix(listType, index) {
switch (listType) {
case TextNodeType.bulletList:
return "\u2022 ";
case TextNodeType.orderedList:
return `${index}. `;
case TextNodeType.orderedListRoman:
return `${this.toRoman(index)}. `;
case TextNodeType.orderedListRomanLower:
return `${this.toRoman(index).toLowerCase()}. `;
case TextNodeType.letteredList:
return `${this.toLetter(index)}. `;
case TextNodeType.letteredListLower:
return `${this.toLetter(index).toLowerCase()}. `;
case TextNodeType.noBulletList:
default:
return "";
}
}
toRoman(num) {
const romanNumerals = [
[1e3, "M"],
[900, "CM"],
[500, "D"],
[400, "CD"],
[100, "C"],
[90, "XC"],
[50, "L"],
[40, "XL"],
[10, "X"],
[9, "IX"],
[5, "V"],
[4, "IV"],
[1, "I"]
];
let result = "";
let remaining = num;
for (const [value, numeral] of romanNumerals) {
while (remaining >= value) {
result += numeral;
remaining -= value;
}
}
return result;
}
toLetter(num) {
let result = "";
let remaining = num;
while (remaining > 0) {
remaining--;
result = String.fromCharCode(65 + remaining % 26) + result;
remaining = Math.floor(remaining / 26);
}
return result;
}
textWithMarks(node) {
const { text, marks } = node;
const parts = [];
const linkMark = marks?.find((m) => m.type === "link");
const href = linkMark?.attrs?.href;
if (text && href && text !== href) {
const hrefBare = href.replace(/^https?:\/\//, "");
if (text.includes(hrefBare)) {
parts.push(text.replace(hrefBare, href));
} else if (text.includes(href)) {
parts.push(text);
} else {
parts.push(`${text} ${href}`);
}
} else if (text) {
parts.push(text);
} else if (href) {
parts.push(href);
}
if (marks) {
for (const mark of marks) {
if (mark.type === "footnote") {
const footnote = mark;
if (footnote.attrs?.content) {
const footnoteText = footnote.attrs.content.map((c) => this.textNodeToPlainText(c)).join("");
if (footnoteText) parts.push(footnoteText);
}
}
}
}
return parts.join(" ");
}
};
// src/info/ConfigBuilder.ts
import path3 from "path";
import { Enum as Enum22 } from "@ncoderz/superenum";
import fs3 from "fs-extra";
var normalizeCardKey = (cardSetKey) => stringUtils.camelToKebab(cardSetKey);
var ConfigBuilder = class {
build(options) {
const opts = Object.assign({}, options);
const bitConfigs = [];
const groupConfigs = [];
const bitGroupConfigKeys = /* @__PURE__ */ new Set();
const bitGroupConfigs = [];
for (const bt of Enum22(BitType).values()) {
const bitType = instance2.getBitType(bt);
const _bitConfig = BITS[bitType];
if (_bitConfig?.baseBitType) {
let currentBitType = _bitConfig.baseBitType;
while (currentBitType) {
bitGroupConfigKeys.add(currentBitType);
const parentConfig = BITS[currentBitType];
currentBitType = parentConfig?.baseBitType;
}
}
}
for (const bt of Enum22(BitType).values()) {
const bitType = instance2.getBitType(bt);
const _bitConfig = BITS[bitType];
if (_bitConfig) {
_bitConfig.bitType = bitType;
bitConfigs.push(_bitConfig);
}
}
for (const [k, g] of Object.entries(GROUPS)) {
const g2 = g;
let k2 = k;
if (k.startsWith("group_")) k2 = k2.substring(6);
k2 = /*'_' +*/
stringUtils.camelToKebab(k2);
g2.key = k2;
groupConfigs.push(g2);
}
const outputFolder = opts.outputDir ?? "assets/config";
const outputFolderBits = path3.join(outputFolder, "bits");
const outputFolderGroups = path3.join(outputFolder, "groups");
const outputFolderCards = path3.join(outputFolder, "cards");
fs3.ensureDirSync(outputFolderBits);
fs3.ensureDirSync(outputFolderGroups);
fs3.ensureDirSync(outputFolderCards);
const bitsFiles = fs3.readdirSync(outputFolderBits).filter((f) => f.endsWith(".jsonc"));
for (const file of bitsFiles) {
fs3.removeSync(path3.join(outputFolderBits, file));
}
const partialsFiles = fs3.readdirSync(outputFolderGroups).filter((f) => f.endsWith(".jsonc"));
for (const file of partialsFiles) {
fs3.removeSync(path3.join(outputFolderGroups, file));
}
const cardsFiles = fs3.readdirSync(outputFolderCards).filter((f) => f.endsWith(".jsonc"));
for (const file of cardsFiles) {
fs3.removeSync(path3.join(outputFolderCards, file));
}
const fileWrites = [];
const tagEntriesTypeOrder = [
BitTagConfigKeyType.tag,
BitTagConfigKeyType.property,
BitTagConfigKeyType.resource,
BitTagConfigKeyType.group,
BitTagConfigKeyType.unknown
];
const exportJsonKeyErrors = [];
const resolveGroupReferences = (groupKey) => {
if (squashedGroups.has(groupKey)) {
const replacements = squashedGroups.get(groupKey);
return replacements.flatMap(
(r) => squashedGroups.has(r.key) ? resolveGroupReferences(r.key) : [r]
);
}
return [{ key: groupKey }];
};
const mappingKeysField = (base, json, html) => {
const record = {};
for (const [id, value] of Object.entries(base ?? {})) {
if (value != null) record[id] = value;
}
if (json.present && json.value != null) record.json = json.value;
if (html.present && html.value != null) record.html = html.value;
return Object.keys(record).length > 0 ? { mappingKeys: record } : {};
};
const bareKeyForProperty = (key) => {
if (key.startsWith("@") || key.startsWith("&")) return key.substring(1);
return void 0;
};
const legacyIsTrivialDefault = (tag) => {
const bare = bareKeyForProperty(tag.key);
if (bare == null) return false;
return tag.jsonKey == null || tag.jsonKey === bare;
};
const processTagEntries = (tag, pathStack) => {
const tags2 = [];
let tagName = tag.key;
const tagType = typeFromConfigKey(tag.key);
let format = "";
let chain = void 0;
if (tagType === BitTagConfigKeyType.tag) {
tagName = tag.key;
if (tag.format === TagFormat.plainText) {
format = "string";
} else if (tag.format === TagFormat.boolean) {
format = "boolean";
} else if (tag.format === TagFormat.number) {
format = "number";
} else {
format = "bitmark+";
}
} else if (tagType === BitTagConfigKeyType.property) {
tagName = tag.key;
if (tag.format === TagFormat.plainText) {
format = "string";
} else if (tag.format === TagFormat.boolean) {
format = "boolean";
} else if (tag.format === TagFormat.bitmarkText) {
format = "bitmark+";
} else if (tag.format === TagFormat.number) {
format = "number";
} else if (tag.format === TagFormat.coordinates) {
format = "coordinates";
}
} else if (tagType === BitTagConfigKeyType.resource) {
format = "string";
} else if (tagType === BitTagConfigKeyType.group) {
let k = tag.key;
if (k.startsWith("group_")) k = k.substring(6);
k = stringUtils.camelToKebab(k);
const resolvedGroups = resolveGroupReferences(k);
const hasOuterExport = Object.prototype.hasOwnProperty.call(tag, "exportJsonKey");
const hasOuterHtml = Object.prototype.hasOwnProperty.call(tag, "htmlKey");
if (!hasOuterExport && tag.jsonKey != null) {
const headingPath = [...pathStack, `tags.${tag.key}`].join(" / ");
exportJsonKeyErrors.push(
`Missing exportJsonKey for non-default jsonKey at ${headingPath} (legacy: \`${tag.jsonKey}\`)`
);
}
for (const ref of resolvedGroups) {
const exportJsonKeyChosen = hasOuterExport ? tag.exportJsonKey : ref.exportJsonKey;
const hasExportChosen = hasOuterExport || !!ref.hasExportJsonKey;
const htmlKeyChosen = hasOuterHtml ? tag.htmlKey : ref.htmlKey;
const hasHtmlChosen = hasOuterHtml || !!ref.hasHtmlKey;
const min = tag.minCount != null ? tag.minCount : ref.min;
const max = tag.maxCount != null ? tag.maxCount : ref.max;
const description = tag.description ? tag.description : ref.description;
tags2.push({
type: "group",
key: ref.key,
...mappingKeysField(
{ ...ref.extraKeys ?? {}, ...tag.mappingKeys ?? {} },
{ value: exportJsonKeyChosen, present: hasExportChosen },
{ value: htmlKeyChosen, present: hasHtmlChosen }
),
...min != null ? { min } : {},
...max != null ? { max } : {},
...description ? { description } : {}
});
}
return tags2;
}
if (Array.isArray(tag.chain) && tag.chain.length > 0) {
const chainTags = [];
const childPathStack = [...pathStack, `tags.${tag.key}`];
for (const [, chainTag] of tag.chain.entries()) {
chainTags.push(...processTagEntries(chainTag, childPathStack));
}
chain = chainTags;
}
const hasExport = Object.prototype.hasOwnProperty.call(tag, "exportJsonKey");
if (!hasExport && !legacyIsTrivialDefault(tag)) {
const headingPath = [...pathStack, `tags.${tag.key}`].join(" / ");
exportJsonKeyErrors.push(
`Missing exportJsonKey for non-default jsonKey at ${headingPath}` + (tag.jsonKey ? ` (legacy: \`${tag.jsonKey}\`)` : ` (symbol-mapped tag \`${tag.key}\`)`)
);
}
const hasHtml = Object.prototype.hasOwnProperty.call(tag, "htmlKey");
const t = {
type: "tag",
key: tagName,
...mappingKeysField(
tag.mappingKeys,
{ value: tag.exportJsonKey, present: hasExport },
{ value: tag.htmlKey, present: hasHtml }
),
...format && format !== "string" ? { format } : {},
...tag.defaultValue != null ? { default: tag.defaultValue } : {},
...tag.nullable ? { nullable: true } : {},
...tag.minCount != null && tag.minCount !== 0 ? { min: tag.minCount } : {},
...tag.maxCount != null && tag.maxCount !== 1 ? { max: tag.maxCount } : {},
description: tag.description ?? "",
tags: chain
};
tags2.push(t);
return tags2;
};
const cardMappingKeysField = (container, base, legacyKey, exportKey, htmlExportKey, pathHeading) => {
const c = container;
const hasExport = Object.prototype.hasOwnProperty.call(c, exportKey);
if (!hasExport && c[legacyKey] != null) {
exportJsonKeyErrors.push(
`Missing exportJsonKey for non-default jsonKey at ${pathHeading} (legacy: \`${c[legacyKey]}\`)`
);
}
return mappingKeysField(
base,
{ value: c[exportKey], present: hasExport },
{
value: c[htmlExportKey],
present: Object.prototype.hasOwnProperty.call(c, htmlExportKey)
}
);
};
const serializeCardSet = (cardSetKey) => {
const variantBodyFormat = (variant) => variant.format ?? TextFormat.bitmarkText;
const cardSetConfig = CARDSETS[cardSetKey];
if (!cardSetConfig) return void 0;
const normalizedKey = normalizeCardKey(cardSetKey);
const variantTagEntriesTypeOrder = [
BitTagConfigKeyType.group,
BitTagConfigKeyType.tag,
BitTagConfigKeyType.property,
BitTagConfigKeyType.resource,
BitTagConfigKeyType.unknown
];
const sides = cardSetConfig.sides.map((side, sideIdx) => {
const sidePath = `cardSets.${normalizedKey} / sides.${side.name ?? `[${sideIdx}]`}`;
const variants = side.variants.map((variant, variantIdx) => {
const variantPath = `${sidePath} / variants.[${variantIdx}]`;
const variantTags = [];
const variantTagEntries = Object.entries(variant.tags ?? []).sort((a, b) => {
const tagA = a[1];
const tagB = b[1];
const typeA = typeFromConfigKey(tagA.key);
const typeB = typeFromConfigKey(tagB.key);
const typeOrder = variantTagEntriesTypeOrder.indexOf(typeA) - variantTagEntriesTypeOrder.indexOf(typeB);
return typeOrder;
});
for (const [, variantTag] of variantTagEntries) {
variantTags.push(...processTagEntries(variantTag, [variantPath]));
}
return {
...cardMappingKeysField(
variant,
void 0,
"jsonKey",
"exportJsonKey",
"htmlKey",
variantPath
),
bodyFormat: variantBodyFormat(variant),
tags: variantTags,
...variant.repeatCount != null && variant.repeatCount !== 1 ? { repeatCount: variant.repeatCount } : {},
...variant.bodyRequired ? { bodyRequired: true } : {},
...variant.bodyAllowed === false ? { bodyForbidden: true } : {}
};
});
return {
name: side.name,
...side.repeat ? { repeat: side.repeat } : {},
...cardMappingKeysField(
side,
side.mappingKeys,
"jsonKey",
"exportJsonKey",
"htmlKey",
sidePath
),
variants
};
});
if (cardSetConfig.sections) {
const cards = Object.entries(cardSetConfig.sections).map(([sectionName, section]) => {
const sectionPath = `cardSets.${normalizedKey} / cards.${sectionName}`;
const hasSideExport = Object.prototype.hasOwnProperty.call(section, "sideExportJsonKey");
const hasSideHtml = Object.prototype.hasOwnProperty.call(section, "sideHtmlKey");
let cardSides = sides;
if (hasSideExport || section.sideJsonKey != null || hasSideHtml) {
if (!hasSideExport && section.sideJsonKey != null) {
exportJsonKeyErrors.push(
`Missing exportJsonKey for non-default jsonKey at ${sectionPath} / sides.* (legacy: \`${section.sideJsonKey}\`)`
);
}
cardSides = sides.map((s) => {
const record = { ...s.mappingKeys ?? {} };
if (hasSideExport) {
if (section.sideExportJsonKey != null) record.json = section.sideExportJsonKey;
else delete record.json;
}
if (hasSideHtml) {
if (section.sideHtmlKey != null) record.html = section.sideHtmlKey;
else delete record.html;
}
return {
name: s.name,
...s.repeat ? { repeat: s.repeat } : {},
...Object.keys(record).length > 0 ? { mappingKeys: record } : {},
variants: s.variants
};
});
}
return {
name: sectionName,
...section.isDefault ? { isDefault: true } : {},
...cardMappingKeysField(
section,
section.mappingKeys,
"jsonKey",
"exportJsonKey",
"htmlKey",
sectionPath
),
// PLAN-085: per-section cardinality. Emit only when non-default
// (treat `0` / undefined as "unbounded — omit").
...section.minCount != null && section.minCount !== 0 ? { min: section.minCount } : {},
...section.maxCount != null && section.maxCount !== 0 ? { max: section.maxCount } : {},
sides: cardSides
};
});
return {
name: normalizedKey,
...mappingKeysField(
void 0,
{ present: false },
{
value: cardSetConfig.htmlKey,
present: Object.prototype.hasOwnProperty.call(cardSetConfig, "htmlKey")
}
),
cards
};
}
const cardSetPath = `cardSets.${normalizedKey} / cards.default`;
return {
name: normalizedKey,
cards: [
{
name: "default",
isDefault: true,
...cardMappingKeysField(
cardSetConfig,
cardSetConfig.mappingKeys,
"jsonKey",
"exportJsonKey",
"htmlKey",
cardSetPath
),
sides
}
]
};
};
for (const bt of bitGroupConfigKeys) {
const bitType = instance2.getBitType(bt);
const _bitConfig = BITS[bitType];
if (_bitConfig) {
_bitConfig.bitType = bitType;
if (!bitGroupConfigs.some((c) => c.bitType === bitType)) {
bitGroupConfigs.push(_bitConfig);
}
}
}
const squashedGroups = /* @__PURE__ */ new Map();
for (const b of bitGroupConfigs) {
const groupKey = `group-${b.bitType}`;
const processedTags = [];
for (const [, tag] of Object.entries(b.tags ?? [])) {
processedTags.push(...processTagEntries(tag, []));
}
const allAreGroups = processedTags.every((t) => t.type === "group");
if (allAreGroups && processedTags.length > 0) {
const replacements = [];
if (b.baseBitType) {
replacements.push({ key: `group-${b.baseBitType}` });
}
for (const t of processedTags) {
const ref = {
key: t.key,
...t.min != null ? { min: t.min } : {},
...t.max != null ? { max: t.max } : {},
...t.description ? { description: t.description } : {}
};
const record = t.mappingKeys ?? {};
if (Object.prototype.hasOwnProperty.call(record, "json")) {
ref.exportJsonKey = record.json;
ref.hasExportJsonKey = true;
}
if (Object.prototype.hasOwnProperty.call(record, "html")) {
ref.htmlKey = record.html;
ref.hasHtmlKey = true;
}
const extras = Object.fromEntries(
Object.entries(record).filter(([id]) => id !== "json" && id !== "html")
);
if (Object.keys(extras).length > 0) ref.extraKeys = extras;
replacements.push(ref);
}
squashedGroups.set(groupKey, replacements);
}
}
for (const b of bitConfigs) {
const resolvedBitConfig = instance2.getBitConfig(b.bitType);
const tags2 = [];
const tagEntries = Object.entries(b.tags ?? []).sort((a, b2) => {
const tagA = a[1];
const tagB = b2[1];
const typeA = typeFromConfigKey(tagA.key);
const typeB = typeFromConfigKey(tagB.key);
const typeOrder = tagEntriesTypeOrder.indexOf(typeA) - tagEntriesTypeOrder.indexOf(typeB);
return typeOrder;
});
const isInheritedFrom = bitGroupConfigKeys.has(b.bitType);
const indexByKey = /* @__PURE__ */ new Map();
const pushOrReplace = (entry) => {
const existing = indexByKey.get(entry.key);
if (existing !== void 0) {
tags2[existing] = entry;
} else {
indexByKey.set(entry.key, tags2.length);
tags2.push(entry);
}
};
if (isInheritedFrom) {
const resolvedGroups = resolveGroupReferences(`group-${b.bitType}`);
for (const ref of resolvedGroups) {
pushOrReplace({
type: "group",
key: ref.key,
...mappingKeysField(
ref.extraKeys,
{ value: ref.exportJsonKey, present: !!ref.hasExportJsonKey },
{ value: ref.htmlKey, present: !!ref.hasHtmlKey }
),
...ref.min != null ? { min: ref.min } : {},
...ref.max != null ? { max: ref.max } : {},
...ref.description ? { description: ref.description } : {}
});
}
} else {
if (b.baseBitType) {
const resolvedGroups = resolveGroupReferences(`group-${b.baseBitType}`);
for (const ref of resolvedGroups) {
pushOrReplace({
type: "group",
key: ref.key,
...mappingKeysField(
ref.extraKeys,
{ value: ref.exportJsonKey, present: !!ref.hasExportJsonKey },
{ value: ref.htmlKey, present: !!ref.hasHtmlKey }
),
...ref.min != null ? { min: ref.min } : {},
...ref.max != null ? { max: ref.max } : {},
...ref.description ? { description: ref.description } : {}
});
}
}
for (const [_tagKey, tag] of tagEntries) {
for (const entry of processTagEntries(tag, [])) {
pushOrReplace(entry);
}
}
}
let cardRef;
let current = b;
while (current) {
if (current.cardSet && CARDSETS[current.cardSet]) {
cardRef = normalizeCardKey(current.cardSet);
break;
}
current = current.baseBitType ? BITS[current.baseBitType] : void 0;
}
const bitJson = {
name: b.bitType,
description: b.description ?? "",
since: resolvedBitConfig.since,
deprecated: resolvedBitConfig.deprecated,
bodyFormat: resolvedBitConfig.textFormatDefault ?? "bitmark--",
...resolvedBitConfig.bodyRequired ? { bodyRequired: true } : {},
...resolvedBitConfig.bodyAllowed === false ? { bodyForbidden: true } : {},
...resolvedBitConfig.footerRequired ? { footerRequired: true } : {},
...resolvedBitConfig.footerAllowed === false ? { footerForbidden: true } : {},
...resolvedBitConfig.resourceAttachmentAllowed === false ? { resourceAttachmentAllowed: false } : {},
// Bit-level named key mappings (element keys for markup import),
// carried verbatim from the raw config.
...mappingKeysField(b.mappingKeys, { present: false }, { present: false }),
tags: tags2,
...cardRef ? { cardSet: cardRef } : {}
};
const output = path3.join(outputFolderBits, `${b.bitType}.jsonc`);
const str = JSON.stringify(bitJson, null, 2);
fileWrites.push(fs3.writeFile(output, str));
}
const writeGroupConfigs = (groupConfigs2) => {
for (const g of groupConfigs2) {
const tags2 = [];
const groupKey = stringUtils.camelToKebab(g.key);
const tagEntriesTypeOrder2 = [
BitTagConfigKeyType.tag,
BitTagConfigKeyType.property,
BitTagConfigKeyType.resource,
BitTagConfigKeyType.group
];
const tagEntries = Object.entries(g.tags ?? []).sort((a, b) => {
const tagA = a[1];
const tagB = b[1];
const typeOrder = tagEntriesTypeOrder2.indexOf(tagA.type) - tagEntriesTypeOrder2.indexOf(tagB.type);
return typeOrder;
});
for (const [_tagKey, tag] of tagEntries) {
tags2.push(...processTagEntries(tag, []));
}
const bitJson = {
name: groupKey,
description: g.description ?? "",
deprecated: g.deprecated,
tags: tags2
// cards: [
// {
// card: null,
// side: 1,
// variant: null,
// description: '',
// tags: [],
// },
// {
// card: null,
// side: 2,
// variant: 1,
// description: '',
// tags: [],
// },
// {
// card: null,
// side: 2,
// variant: null,
// description: '',
// tags: [],
// },
// ],
};
const output = path3.join(outputFolderGroups, `${groupKey}.jsonc`);
const str = JSON.stringify(bitJson, null, 2);
fs3.writeFileSync(output, str);
}
};
writeGroupConfigs(groupConfigs);
const writeCardConfigs = () => {
for (const cardSetKey of Object.keys(CARDSETS)) {
const cardJson = serializeCardSet(cardSetKey);
if (!cardJson) continue;
const output = path3.join(outputFolderCards, `${cardJson.name}.jsonc`);
const str = JSON.stringify(cardJson, null, 2);
fileWrites.push(fs3.writeFile(output, str));
}
};
const writeBitsAsGroupConfigs = (bitsAsGroupConfigs) => {
for (const b of bitsAsGroupConfigs) {
const groupKey = `group-${b.bitType}`;
if (squashedGroups.has(groupKey)) {
continue;
}
const tags2 = [];
if (b.baseBitType) {
const resolvedGroups = resolveGroupReferences(`group-${b.baseBitType}`);
for (const ref of resolvedGroups) {
tags2.push({
type: "group",
key: ref.key,
...mappingKeysField(
ref.extraKeys,
{ value: ref.exportJsonKey, present: !!ref.hasExportJsonKey },
{ value: ref.htmlKey, present: !!ref.hasHtmlKey }
),
...ref.min != null ? { min: ref.min } : {},
...ref.max != null ? { max: ref.max } : {},
...ref.description ? { description: ref.description } : {}
});
}
}
const tagEntries = Object.entries(b.tags ?? []).sort((a, b2) => {
const tagA = a[1];
const tagB = b2[1];
const typeA = typeFromConfigKey(tagA.key);
const typeB = typeFromConfigKey(tagB.key);
const typeOrder = tagEntriesTypeOrder.indexOf(typeA) - tagEntriesTypeOrder.indexOf(typeB);
return typeOrder;
});
for (const [_tagKey, tag] of tagEntries) {
tags2.push(...processTagEntries(tag, []));
}
const bitJson = {
name: groupKey,
description: b.description ?? "",
deprecated: b.deprecated,
tags: tags2
};
const output = path3.join(outputFolderGroups, `${groupKey}.jsonc`);
const str = JSON.stringify(bitJson, null, 2);
fs3.writeFileSync(output, str);
}
};
writeBitsAsGroupConfigs(bitGroupConfigs);
writeCardConfigs();
if (exportJsonKeyErrors.length > 0) {
console.error("\n\u26A0\uFE0F exportJsonKey validation errors:");
for (const e of exportJsonKeyErrors) console.error(` - ${e}`);
throw new Error(
`exportJsonKey validation failed: ${exportJsonKeyErrors.length} entr${exportJsonKeyErrors.length === 1 ? "y" : "ies"} with non-default jsonKey but no exportJsonKey companion.`
);
}
Promise.all(fileWrites).then(() => {
const validationErrors = this.validateConfigTree(outputFolder);
if (validationErrors.length > 0) {
console.error("\n\u26A0\uFE0F Config tree validation errors:");
for (const error of validationErrors) {
console.error(` - ${error}`);
}
throw new Error(`Config tree validation failed with ${validationErrors.length} error(s)`);
} else {
console.log("\u2705 Config tree validation passed");
}
}).catch((err) => {
console.error("Error during config build or validation:", err);
throw err;
});
}
/**
* Validate the config tree for missing group references
* @param outputFolder - The folder containing the config files
* @returns Array of validation errors, empty if valid
*/
validateConfigTree(outputFolder) {
const errors = [];
const outputFolderBits = path3.join(outputFolder, "bits");
const outputFolderGroups = path3.join(outputFolder, "groups");
const outputFolderCards = path3.join(outputFolder, "cards");
const availableGroups = /* @__PURE__ */ new Set();
if (fs3.existsSync(outputFolderGroups)) {
const groupFiles = fs3.readdirSync(outputFolderGroups).filter((f) => f.endsWith(".jsonc"));
for (const file of groupFiles) {
const groupName = file.replace(".jsonc", "");
availableGroups.add(groupName);
}
}
const availableCards = /* @__PURE__ */ new Set();
const cardFiles = fs3.existsSync(outputFolderCards) ? fs3.readdirSync(outputFolderCards).filter((f) => f.endsWith(".jsonc")) : [];
for (const file of cardFiles) {
const cardName = file.replace(".jsonc", "");
availableCards.add(cardName);
}
const checkTags = (tags2, filePath, lineOffset) => {
for (let i = 0; i < tags2.length; i++) {
const tag = tags2[i];
if (tag?.type === "group" && tag.key) {
if (!availableGroups.has(tag.key)) {
const line = lineOffset + i + 1;
errors.push(`Missing group reference '${tag.key}' in ${filePath}:${line}`);
}
}
if (Array.isArray(tag?.tags)) {
checkTags(tag.tags, filePath, lineOffset + i + 1);
}
}
};
if (fs3.existsSync(outputFolderBits)) {
const bitFiles = fs3.readdirSync(outputFolderBits).filter((f) => f.endsWith(".jsonc"));
for (const file of bitFiles) {
const filePath = path3.join(outputFolderBits, file);
try {
const content = fs3.readFileSync(filePath, "utf-8");
const config = JSON.parse(content);
if (config.tags && Array.isArray(config.tags)) {
checkTags(config.tags, `bits/${file}`, 15);
}
if (typeof config.card === "string") {
if (!availableCards.has(config.card)) {
errors.push(`Missing card file '${config.card}' referenced in bits/${file}`);
}
} else if (config.card?.sides && Array.isArray(config.card.sides)) {
config.card.sides.forEach((side, sideIdx) => {
const variants = side.variants;
if (Array.isArray(variants)) {
variants.forEach((variant, variantIdx) => {
const variantTags = variant.tags;
if (Array.isArray(variantTags)) {
checkTags(
variantTags,
`bits/${file} (card side ${sideIdx}, variant ${variantIdx})`,
15
);
}
});
}
});
}
} catch (err) {
errors.push(`Failed to parse ${file}: ${err}`);
}
}
}
if (fs3.existsSync(outputFolderGroups)) {
const groupFiles = fs3.readdirSync(outputFolderGroups).filter((f) => f.endsWith(".jsonc"));
for (const file of groupFiles) {
const filePath = path3.join(outputFolderGroups, file);
try {
const content = fs3.readFileSync(filePath, "utf-8");
const config = JSON.parse(content);
if (config.tags && Array.isArray(config.tags)) {
checkTags(config.tags, `groups/${file}`, 15);
}
} catch (err) {
errors.push(`Failed to parse ${file}: ${err}`);
}
}
}
if (cardFiles.length > 0) {
for (const file of cardFiles) {
const filePath = path3.join(outputFolderCards, file);
try {
const content = fs3.readFileSync(filePath, "utf-8");
const config = JSON.parse(content);
const expectedKey = file.replace(".jsonc", "");
if (config.key && config.key !== expectedKey) {
errors.push(`Card key mismatch in cards/${file}: expected '${expectedKey}'`);
}
if (Array.isArray(config.cards)) {
for (const cardNode of config.cards) {
if (cardNode && Array.isArray(cardNode.sides)) {
cardNode.sides.forEach((side, sideIdx) => {
const variants = side.variants;
if (Array.isArray(variants)) {
variants.forEach((variant, variantIdx) => {
const variantTags = variant.tags;
if (Array.isArray(variantTags)) {
checkTags(
variantTags,
`cards/${file} (side ${sideIdx}, variant ${variantIdx})`,
10
);
}
});
}
});
}
}
}
} catch (err) {
errors.push(`Failed to parse cards/${file}: ${err}`);
}
}
}
return errors;
}
};
// src/BitmarkParserGenerator.ts
var Input = {
/**
* Input is as a string
*/
string: "string",
/**
* Input is as a file path
*/
file: "file"
};
var Output = {
/**
* Output bitmark string
*/
bitmark: "bitmark",
/**
* Output JSON as a plain JS object, or a file
*/
json: "json",
/**
* Output AST as a plain JS object, or a file
*/
ast: "ast",
/**
* Output plain text as a string, or a file
*/
text: "text"
};
var InputFormat = {
/**
* Input is bitmark
*/
bitmark: "bitmark",
/**
* Input is bitmarkText
*/
bitmarkText: "bitmarkText",
/**
* Input is plain text
*/
plainText: "plainText",
/**
* Input is HTML (used by convertHtmlTable)
*/
html: "html"
};
var BitmarkParserGenerator = class {
constructor() {
__publicField(this, "ast", new Ast());
__publicField(this, "jsonParser", new JsonParser());
__publicField(this, "bitmarkParser", new BitmarkParser());
__publicField(this, "textParser", new TextParser());
__publicField(this, "textGenerator", new TextGenerator());
/**
* Stringify / prettify a plain JS object to a JSON string, depending on the JSON options
*
* @param json
* @param options
* @param forceStringify
* @returns
*/
__publicField(this, "jsonStringifyPrettify", (json, options, forceStringify) => {
const prettifySpace = options.prettify === true ? 2 : options.prettify || void 0;
const stringify = forceStringify || options.stringify === true || prettifySpace !== void 0;
if (stringify) {
return JSON.stringify(json, null, prettifySpace);
}
return json;
});
}
/**
* Get the version of the bitmark-parser-generator library
*/
version() {
return env.appVersion.full;
}
/**
* Get information about the bitmark-parser-generator library
*/
info(options) {
const opts = Object.assign({}, options);
const builder3 = new InfoBuilder();
let res;
const outputString = !opts.outputFormat || opts.outputFormat === InfoFormat.text;
const outputJson = opts.outputFormat === InfoFormat.json;
const all = opts.type === InfoType.all;
const deprecated = opts.type === InfoType.deprecated;
const includeNonDeprecated = all || !deprecated;
const includeDeprecated = all || deprecated;
if (opts.type === InfoType.bit) {
const bitConfigs = builder3.getSupportedBitConfigs().filter((b) => {
if (!opts.bit) return true;
const bitType = instance2.getBitType(opts.bit);
return bitType === b.bitType;
});
if (outputString) {
res = bitConfigs.map(
(b) => b.toString({
includeChains: true,
includeConfigs: true
})
).join("\n\n--------------\n\n");
} else {
res = bitConfigs;
}
} else {
const supportedBits = builder3.getSupportedBits({
includeNonDeprecated,
includeDeprecated
}).filter((b) => {
if (!opts.bit) return true;
const bitType = instance2.getBitType(opts.bit);
if (b.name === bitType) return true;
});
if (outputString) {
res = this.supportedBitsAsString(supportedBits);
} else {
res = supportedBits;
}
}
if (outputJson) {
const prettifySpace = opts.prettify === true ? 2 : opts.prettify || void 0;
res = JSON.stringify(res, null, prettifySpace);
}
return res;
}
/**
* Generate the new configuration for the bitmark parser.
*/
generateConfig(options) {
const opts = Object.assign({}, options);
const builder3 = new ConfigBuilder();
builder3.build(opts);
}
/**
* Convert bitmark from bitmark to JSON, or JSON to bitmark.
*
* Input type is detected automatically and may be:
* - string: bitmark, JSON, or AST
* - object: JSON or AST
* - file: bitmark, JSON, or AST
*
* Output type is selected automatically based on input type detection:
* - input(JSON/AST) ==> output(bitmark)
* - input(bitmark) ==> output(JSON)
*
* Output type can be overridden to one of the following:
* - bitmark: output bitmark string
* - json: output JSON as a plain JS object, or a file
* - ast: output AST as a plain JS object, or a file
* - text: output plain text as a string, or a file
*
* By default, the result is returned as a string for bitmark, or a plain JS object for JSON/AST.
*
* The options can be used to write the output to a file and to set conversion options or override defaults.
*
* If both the input and output formats are the same, the input will be validated and rewritten.
* This feature be used to upgrade bitmark or JSON to the latest version.
*
* @param input - bitmark or JSON or AST as a string, JSON or AST as plain JS object, or path to a file containing
* bitmark, JSON, or AST.
* @param options - the conversion options
* @returns A string if converting to bitmark, a plain JS object if converting to JSON, or
* void if writing to a file
* @throws Error if any error occurs
*/
convert(input, options) {
let res;
const opts = Object.assign({}, options);
const jsonOptions = Object.assign({}, opts.jsonOptions);
const outputFormat = opts.outputFormat;
const outputBitmark = outputFormat === Output.bitmark;
const outputJson = outputFormat === Output.json;
const outputAst = outputFormat === Output.ast;
const outputText = outputFormat === Output.text;
const bitmarkParserType = BitmarkParserType.peggy;
let inStr = input;
const inputIsString = typeof input === "string";
if (env.isBrowser && opts.outputFile) {
throw new Error("Cannot write to file in browser environment");
}
if (!opts.inputFormat || opts.inputFormat === Input.file) {
if (env.isNode) {
if (inputIsString && fs4.existsSync(inStr)) {
inStr = fs4.readFileSync(inStr, {
encoding: "utf8"
});
}
}
}
let ast = this.ast.preprocessAst(inStr);
const isAst = !!ast;
if (!isAst) {
ast = this.jsonParser.toAst(inStr);
}
const isJson = !!ast?.bits;
const isBitmark = inputIsString && !isJson && !isAst;
const bitmarkToBitmark = (bitmarkStr) => {
bitmarkToAst(bitmarkStr);
astToBitmark(res);
};
const bitmarkToAst = (bitmarkStr) => {
res = this.bitmarkParser.toAst(bitmarkStr, {
parserType: bitmarkParserType
});
};
const bitmarkToJson = (bitmarkStr) => {
if (bitmarkParserType === BitmarkParserType.peggy) {
ast = this.bitmarkParser.toAst(bitmarkStr, {
parserType: bitmarkParserType
});
if (opts.outputFile) {
const generator = new JsonFileGenerator(opts.outputFile, opts);
generator.generateSync(ast);
} else {
const generator = new JsonObjectGenerator(opts);
const json = generator.generateSync(ast);
res = this.jsonStringifyPrettify(json, jsonOptions);
}
}
};
const bitmarkToText = (bitmarkStr) => {
ast = this.bitmarkParser.toAst(bitmarkStr, {
parserType: bitmarkParserType
});
const jsonGenerator = new JsonObjectGenerator(opts);
const json = jsonGenerator.generateSync(ast);
const textGenerator = new PlainTextGenerator();
const str = textGenerator.generate(json);
if (opts.outputFile) {
fs4.writeFileSync(opts.outputFile, str, {
encoding: "utf8"
});
} else {
res = str;
}
};
const astToBitmark = (astJson) => {
if (opts.outputFile) {
const generator = new BitmarkFileGenerator(opts.outputFile, opts);
generator.generateSync(astJson);
} else {
const generator = new BitmarkStringGenerator(opts);
res = generator.generateSync(astJson);
}
};
const astToAst = (astJson) => {
res = this.jsonStringifyPrettify(astJson, jsonOptions);
};
const astToJson = (astJson) => {
if (opts.outputFile) {
const generator = new JsonFileGenerator(opts.outputFile, opts);
generator.generateSync(astJson);
} else {
const generator = new JsonObjectGenerator(opts);
const json = generator.generateSync(astJson);
res = this.jsonStringifyPrettify(json, jsonOptions);
}
};
const astToText = (astJson) => {
const jsonGenerator = new JsonObjectGenerator(opts);
const json = jsonGenerator.generateSync(astJson);
const textGenerator = new PlainTextGenerator();
const str = textGenerator.generate(json);
if (opts.outputFile) {
fs4.writeFileSync(opts.outputFile, str, {
encoding: "utf8"
});
} else {
res = str;
}
};
const jsonToBitmark = (astJson) => {
if (opts.outputFile) {
const generator = new BitmarkFileGenerator(opts.outputFile, opts);
generator.generateSync(astJson);
} else {
const generator = new BitmarkStringGenerator(opts);
res = generator.generateSync(astJson);
}
};
const jsonToAst = (astJson) => {
res = this.jsonStringifyPrettify(astJson, jsonOptions);
};
const jsonToText = (astJson) => {
const jsonGenerator = new JsonObjectGenerator(opts);
const json = jsonGenerator.generateSync(astJson);
const textGenerator = new PlainTextGenerator();
const str = textGenerator.generate(json);
if (opts.outputFile) {
fs4.writeFileSync(opts.outputFile, str, {
encoding: "utf8"
});
} else {
res = str;
}
};
const jsonToJson = (astJson) => {
astToJson(astJson);
};
if (isBitmark) {
if (outputBitmark) {
bitmarkToBitmark(inStr);
} else if (outputAst) {
bitmarkToAst(inStr);
} else if (outputText) {
bitmarkToText(inStr);
} else {
bitmarkToJson(inStr);
}
} else if (isAst) {
ast = ast;
if (outputAst) {
astToAst(ast);
} else if (outputJson) {
astToJson(ast);
} else if (outputText) {
astToText(ast);
} else {
astToBitmark(ast);
}
} else if (isJson) {
ast = ast;
if (outputJson) {
jsonToJson(ast);
} else if (outputAst) {
jsonToAst(ast);
} else if (outputText) {
jsonToText(ast);
} else {
jsonToBitmark(ast);
}
} else {
res = "";
}
return res;
}
/**
* Upgrade bitmark or JSON, upgrading to the latest supported syntax, removing unrecognised data in the process.
*
* THIS FEATURE SHOULD BE USED WITH CAUTION. IT WILL POTENTIALLY DELETE DATA.
*
* Input type is detected automatically and may be:
* - string: bitmark, JSON
* - object: JSON
* - file: bitmark, JSON
*
* Output type is the same as the detected input type
*
* By default, the result is returned as a string for bitmark, or a plain JS object for JSON.
*
* The options can be used to write the output to a file and to set conversion options or override defaults.
*
* If both the input and output formats are the same, the input will be validated and rewritten.
*
* @param input - bitmark or JSON or AST as a string, JSON or AST as plain JS object, or path to a file containing
* bitmark, JSON, or AST.
* @param options - the conversion options
* @returns Promise that resolves to string if upgrading to bitmark, a plain JS object if converting to JSON, or
* void if writing to a file
* @throws Error if any error occurs
*/
upgrade(input, options) {
let res;
const opts = Object.assign({}, options);
const jsonOptions = Object.assign({}, opts.jsonOptions);
const bitmarkParserType = opts.bitmarkParserType;
let inStr = input;
const inputIsString = typeof input === "string";
if (env.isBrowser && opts.outputFile) {
throw new Error("Cannot write to file in browser environment");
}
if (!opts.inputFormat || opts.inputFormat === Input.file) {
if (env.isNode) {
if (inputIsString && fs4.existsSync(inStr)) {
inStr = fs4.readFileSync(inStr, {
encoding: "utf8"
});
}
}
}
let ast = this.jsonParser.toAst(inStr);
const isJson = !!ast?.bits;
const isBitmark = inputIsString && !isJson;
const bitmarkToBitmark = (bitmarkStr) => {
const astJson = this.bitmarkParser.toAst(bitmarkStr, {
parserType: bitmarkParserType
});
if (opts.outputFile) {
const generator = new BitmarkFileGenerator(opts.outputFile, opts);
generator.generateSync(astJson);
} else {
const generator = new BitmarkStringGenerator(opts);
res = generator.generateSync(astJson);
}
};
const jsonToJson = (astJson) => {
if (opts.outputFile) {
const generator = new JsonFileGenerator(opts.outputFile, opts);
generator.generateSync(astJson);
} else {
const generator = new JsonObjectGenerator(opts);
const json = generator.generateSync(astJson);
res = this.jsonStringifyPrettify(json, jsonOptions);
}
};
if (isBitmark) {
bitmarkToBitmark(inStr);
} else if (isJson) {
ast = ast;
jsonToJson(ast);
} else {
return;
}
return res;
}
/**
* Create a bitmark AST (Abstract Syntax Tree) from bitmark or JSON or AST
*
* Input type is detected automatically and may be string, object (JSON or AST), or file
*
* @param input the JSON or bitmark to convert to a bitmark AST
* @param options - the create AST options
* @returns bitmark AST
* @throws Error if any error occurs
*/
createAst(input, options) {
let res;
let inStr = input;
const inputIsString = typeof input === "string";
const opts = Object.assign({}, options);
if (!opts.inputFormat || opts.inputFormat === Input.file) {
if (env.isNode) {
if (inputIsString && fs4.existsSync(inStr)) {
inStr = fs4.readFileSync(inStr, {
encoding: "utf8"
});
}
}
}
let ast = this.ast.preprocessAst(inStr);
const isAst = !!ast;
if (!isAst) {
ast = this.jsonParser.toAst(inStr);
}
const isJson = !!ast?.bits;
const isBitmark = inputIsString && !isJson && !isAst;
if (isBitmark) {
res = this.bitmarkParser.toAst(inStr);
} else if (isJson) {
res = ast;
} else {
res = {};
}
return res;
}
/**
* Convert bitmark text to JSON, or JSON to bitmark text.
*
* Input type is detected automatically and may be:
* - string: bitmark text or JSON
* - object: JSON
* - file: bitmark text or JSON
*
* Output type is selected automatically based on input type detection:
* - input(JSON) ==> output(bitmark text)
* - input(bitmark text) ==> output(JSON)
*
* By default, the result is returned as a string for bitmark text, or a plain JS object for JSON.
*
* The options can be used to write the output to a file and to set conversion options or override defaults.
*
* @param input - bitmark text or JSON as a string, JSON as plain JS object, or path to a file containing
* bitmark text or JSON.
* @param options - the conversion options
* @returns Promise that resolves to string if converting to bitmark text, a plain JS object if converting to JSON, or
* void if writing to a file
* @throws Error if any error occurs
*/
convertText(input, options) {
let res;
let preRes;
const opts = Object.assign({}, options);
const fileOptions = Object.assign({}, opts.fileOptions);
const jsonOptions = Object.assign({}, opts.jsonOptions);
const textFormat = Enum23(TextFormat).fromValue(opts.textFormat) ?? TextFormat.bitmarkText;
const textLocation = opts.textLocation ?? TextLocation.body;
let inStr = input;
const inputIsString = typeof input === "string";
if (env.isBrowser && opts.outputFile) {
throw new Error("Cannot write to file in browser environment");
}
if (!opts.inputFormat || opts.inputFormat === Input.file) {
if (env.isNode) {
if (inputIsString && fs4.existsSync(inStr)) {
inStr = fs4.readFileSync(inStr, {
encoding: "utf8"
});
}
}
}
const ast = this.textParser.preprocessAst(inStr);
const isAst = !!ast;
if (!isAst) {
preRes = this.textParser.toAst(inStr, {
format: textFormat,
location: TextLocation.body
});
} else {
preRes = this.textGenerator.generateSync(ast, textFormat, textLocation);
}
if (opts.outputFile) {
const output = opts.outputFile.toString();
let strRes = preRes;
if (!isAst) {
strRes = this.jsonStringifyPrettify(preRes, jsonOptions, true);
}
const flag = fileOptions.append ? "a" : "w";
fs4.ensureDirSync(path4.dirname(output));
fs4.writeFileSync(output, strRes, {
flag
});
} else {
if (!isAst) {
res = this.jsonStringifyPrettify(preRes, jsonOptions);
} else {
res = preRes;
}
}
return res;
}
extractPlainText(input, options) {
const dataIn = input;
const inputFormat = options?.inputFormat;
const isString2 = typeof input === "string";
let data;
const parseAutomatically = () => {
let dataOut = dataIn;
if (typeof dataIn === "string") {
try {
dataOut = JSON.parse(dataIn);
} catch (_e) {
let isBitmark = false;
const bitmarkData = this.convert(dataIn, {
outputFormat: Output.json
});
if (bitmarkData.length > 0) {
const isError = bitmarkData[0].bit.type === BitType._error;
if (!isError) {
isBitmark = true;
dataOut = bitmarkData;
}
}
if (!isBitmark) {
dataOut = this.convertText(dataIn, {
textFormat: TextFormat.bitmarkText
});
}
}
}
return dataOut;
};
if (inputFormat === InputFormat.bitmark) {
data = this.convert(dataIn, {
outputFormat: Output.json
});
} else if (inputFormat === InputFormat.bitmarkText) {
data = this.convertText(dataIn, {
textFormat: TextFormat.bitmarkText
});
} else if (inputFormat === InputFormat.plainText) {
if (isString2) data = String(input);
} else {
data = parseAutomatically();
}
const generator = new PlainTextGenerator();
const res = generator.generate(data);
return res;
}
/**
* Convert between HTML tables and bitmark tables.
*
* Direction is auto-detected (override with `options.inputFormat`):
* - HTML input: every `<table>` is extracted and emitted as a `[.table-extended]` (or `[.table]`)
* bit; output as bitmark (default), JSON, AST, or HTML (re-emit).
* - bitmark / JSON / AST input: each `table` / `table-extended` bit is rendered as an HTML
* `<table>` fragment.
*
* Cell content is reproduced verbatim except inline conversion to bitmark marks (bold, italic,
* superscript, etc.), with lists and images mapped to their bitmark equivalents.
*
* @param input - HTML, bitmark, JSON or AST string; or a parsed JSON/AST value; or a file path.
* @param options - the conversion options
* @returns the converted output as a string (or object for JSON/AST), or void when writing a file
* @throws Error if any error occurs
*/
convertHtmlTable(input, options) {
const opts = Object.assign({}, options);
if (env.isBrowser && opts.outputFile) {
throw new Error("Cannot write to file in browser environment");
}
let inStr = input;
const inputIsString = typeof input === "string";
if (env.isNode && inputIsString && fs4.existsSync(inStr)) {
inStr = fs4.readFileSync(inStr, { encoding: "utf8" });
}
const isHtmlString = (s) => {
const t = s.replace(/^\s+/, "");
return t.startsWith("<") && /<table/i.test(t);
};
let htmlInput;
if (opts.inputFormat === InputFormat.html) {
htmlInput = true;
} else if (opts.inputFormat != null) {
htmlInput = false;
} else {
htmlInput = inputIsString && isHtmlString(inStr);
}
if (htmlInput) {
const parser2 = new HtmlTableParser();
const bits = parser2.parse(inStr, {
tableFormat: opts.tableFormat ?? "table-extended",
keepUnknownTags: opts.keepUnknownTags,
onWarning: (m) => console.warn(m)
});
const outputFormat2 = opts.outputFormat ?? Output.bitmark;
if (outputFormat2 === "html") {
const json2 = this.convert(bits, { outputFormat: Output.json });
return this.outputHtml(new HtmlTableGenerator().generate(json2), opts);
}
return this.convert(bits, {
outputFormat: Enum23(Output).fromValue(outputFormat2),
outputFile: opts.outputFile,
fileOptions: opts.fileOptions,
jsonOptions: opts.jsonOptions,
bitmarkOptions: {
...opts.bitmarkOptions,
...opts.noBreakscaping ? { noBreakscaping: true } : {}
}
});
}
const outputFormat = opts.outputFormat ?? "html";
if (outputFormat !== "html") {
throw new Error(
"convertHtmlTable: non-HTML input can only be converted to HTML. Use convert() for bitmark/JSON/AST conversions."
);
}
const json = this.convert(inputIsString ? inStr : input, { outputFormat: Output.json });
return this.outputHtml(new HtmlTableGenerator().generate(json), opts);
}
/**
* Write an HTML string to the configured output file, or return it.
*/
outputHtml(html, opts) {
if (opts.outputFile) {
const output = opts.outputFile.toString();
const flag = opts.fileOptions?.append ? "a" : "w";
fs4.ensureDirSync(path4.dirname(output));
fs4.writeFileSync(output, html, { flag });
return;
}
return html;
}
/**
* Breakscape bitmark text.
*
* Input type is detected automatically and may be:
* - string: text
* - file: path to a file containing text
*
* By default, the result is returned as a string.
* By default, the text is breakscaped as if it was bitmark text in the body.
* Use the options to change this behaviour.
*
* The options can be used to write the output to a file.
*
* @param input - text, or path to a file containing text.
* @param options - the conversion options
* @returns breakscaped string, or void if writing to a file
* @throws Error if any error occurs
*/
breakscapeText(input, options) {
if (!input) return input;
const opts = Object.assign({}, options);
const fileOptions = Object.assign({}, opts.fileOptions);
const textFormat = Enum23(TextFormat).fromValue(opts.textFormat) ?? TextFormat.bitmarkText;
const textLocation = opts.textLocation ?? TextLocation.body;
let inStr = input;
const inputIsString = typeof input === "string";
if (env.isBrowser && opts.outputFile) {
throw new Error("Cannot write to file in browser environment");
}
if (!opts.inputFormat || opts.inputFormat === Input.file) {
if (env.isNode) {
if (inputIsString && fs4.existsSync(inStr)) {
inStr = fs4.readFileSync(inStr, {
encoding: "utf8"
});
}
}
}
const res = instance3.breakscape(inStr, {
format: textFormat,
location: textLocation
});
if (opts.outputFile) {
const output = opts.outputFile.toString();
const flag = fileOptions.append ? "a" : "w";
fs4.ensureDirSync(path4.dirname(output));
fs4.writeFileSync(output, res, {
flag
});
} else {
return res;
}
}
/**
* Unescape (unbreakscape) bitmark text
*
* Input type is detected automatically and may be:
* - string: text
* - file: path to a file containing text
*
* By default, the result is returned as a string.
* By default, the text is unbreakscaped as if it was bitmark text in the body.
* Use the options to change this behaviour.
*
* The options can be used to write the output to a file.
*
* @param input - text, or path to a file containing text.
* @param options - the conversion options
* @returns unbreakscaped string, or void if writing to a file
* @throws Error if any error occurs
*/
unbreakscapeText(input, options) {
if (!input) return input;
const opts = Object.assign({}, options);
const fileOptions = Object.assign({}, opts.fileOptions);
const textFormat = Enum23(TextFormat).fromValue(opts.textFormat) ?? TextFormat.bitmarkText;
const textLocation = opts.textLocation ?? TextLocation.body;
let inStr = input;
const inputIsString = typeof input === "string";
if (env.isBrowser && opts.outputFile) {
throw new Error("Cannot write to file in browser environment");
}
if (!opts.inputFormat || opts.inputFormat === Input.file) {
if (env.isNode) {
if (inputIsString && fs4.existsSync(inStr)) {
inStr = fs4.readFileSync(inStr, {
encoding: "utf8"
});
}
}
}
const res = instance3.unbreakscape(inStr, {
format: textFormat,
location: textLocation
});
if (opts.outputFile) {
const output = opts.outputFile.toString();
const flag = fileOptions.append ? "a" : "w";
fs4.ensureDirSync(path4.dirname(output));
fs4.writeFileSync(output, res, {
flag
});
} else {
return res;
}
return;
}
textAstToPlainText(textAst, _options) {
const textGenerator = new TextGenerator(BitmarkVersion.v3, {
//
});
const res = textGenerator.generateSync(textAst, TextFormat.bitmarkText, TextLocation.body, {
noBreakscaping: true,
noMarkup: true
});
return res;
}
/**
* Get the supported bits as a formatted strings
*
* @param supportedBits
* @returns
*/
supportedBitsAsString(supportedBits) {
let res = "";
for (const rootBit of supportedBits) {
res += `${rootBit.name} (since: ${rootBit.since}`;
if (rootBit.deprecated) res += `, deprecated: ${rootBit.deprecated}`;
res += ")\n";
}
return res;
}
};
// src/generator/json/JsonStringGenerator.ts
var JsonStringGenerator = class {
/**
* Generate bitmark JSON from a bitmark AST as a string
*
* @param options - JSON generation options
*/
constructor(options) {
__publicField(this, "generator");
__publicField(this, "writer");
this.writer = new StringWriter();
this.generator = new JsonGenerator(this.writer, options);
}
/**
* Generate bitmark JSON from bitmark AST as a string
*
* @param ast bitmark AST
*/
async generate(ast) {
await this.generator.generate(ast);
return this.writer.getString();
}
/**
* Generate bitmark JSON from bitmark AST as a string synchronously
*
* @param ast bitmark AST
*/
generateSync(ast) {
this.generator.generateSync(ast);
return this.writer.getString();
}
};
// src/utils/BoundingBox.ts
function toBbox(arr) {
if (!Array.isArray(arr) || arr.length !== 4) return null;
if (!arr.every(Number.isFinite)) return null;
return [arr[0], arr[1], arr[2], arr[3]];
}
function parseSourceBB(sourceBB) {
if (sourceBB == null) return [];
if (!Array.isArray(sourceBB) || sourceBB.length === 0) return [];
const isNested = sourceBB.some(Array.isArray);
const candidates = isNested ? sourceBB : [sourceBB];
const out = [];
for (const candidate of candidates) {
const box = toBbox(candidate);
if (box) out.push(box);
}
return out;
}
function boundingBoxIntersects(a, b) {
return a[0] < b[2] && a[2] > b[0] && a[1] < b[3] && a[3] > b[1];
}
function boundingBoxUnion(boxes) {
if (boxes.length === 0) return null;
let [x0, y0, x1, y1] = boxes[0];
for (let i = 1; i < boxes.length; i++) {
const b = boxes[i];
if (b[0] < x0) x0 = b[0];
if (b[1] < y0) y0 = b[1];
if (b[2] > x1) x1 = b[2];
if (b[3] > y1) y1 = b[3];
}
return [x0, y0, x1, y1];
}
var BoundingBoxIndex = class _BoundingBoxIndex {
constructor(entries) {
__publicField(this, "_entries");
__publicField(this, "bitmarkParser", new BitmarkParserGenerator());
this._entries = entries;
}
/**
* Build an index from already-parsed bits. Bits without a parseable
* `sourceBB` are dropped. Order of the input is preserved.
*
* Pass `options.sourceRL` to scope the index to a single source page —
* bits whose `sourceRL` doesn't match (or is absent) are excluded.
*/
static fromBits(wrappers, options) {
const filterSourceRL = options?.sourceRL;
const entries = [];
for (const wrapper of wrappers) {
if (!wrapper?.bit) continue;
if (filterSourceRL !== void 0 && wrapper.bit.sourceRL !== filterSourceRL) {
continue;
}
const boxes = parseSourceBB(wrapper.bit.sourceBB);
if (boxes.length === 0) continue;
entries.push({ bit: wrapper.bit, wrapper, boxes });
}
return new _BoundingBoxIndex(entries);
}
/** All bits with at least one valid `sourceBB`, in input order. */
get entries() {
return this._entries;
}
/**
* Bits whose ANY `sourceBB` box intersects `query` (strict overlap —
* touching-only edges do NOT count as an intersection).
*/
intersecting(query) {
const matches = [];
for (const entry of this._entries) {
if (entry.boxes.some((box) => boundingBoxIntersects(query, box))) {
matches.push(entry);
}
}
return matches;
}
/**
* Flat list of every individual `sourceBB` box that intersects `query`.
* For a multi-box bit, only the boxes that themselves intersected are
* returned.
*/
intersectingBoxes(query) {
const matches = [];
for (const entry of this._entries) {
for (const box of entry.boxes) {
if (boundingBoxIntersects(query, box)) matches.push(box);
}
}
return matches;
}
/**
* Smallest axis-aligned box that fully encloses every individual `sourceBB`
* box that intersected the query. For a multi-box bit, only the boxes that
* themselves intersected contribute to the union — non-intersecting boxes
* from the same bit are ignored. Returns `null` if no box matches.
*/
enclosingBoundingBox(query) {
return boundingBoxUnion(this.intersectingBoxes(query));
}
/** Render selected bits back to bitmark text. Empty input returns `''`. */
toBitmark(entries) {
if (entries.length === 0) return "";
const wrappers = entries.map((e) => e.wrapper);
return this.bitmarkParser.convert(wrappers, {
outputFormat: Output.bitmark,
bitmarkOptions: { prettifyJson: true }
});
}
};
// src/index.ts
init();
export {
Ast,
BitType,
BitmarkFileGenerator,
BitmarkGenerator,
BitmarkParser,
BitmarkParserGenerator,
BitmarkParserType,
BitmarkStringGenerator,
BitmarkVersion,
BodyTextFormat,
BoundingBoxIndex,
Builder,
CardSetVersion,
FileWriter,
HtmlTableGenerator,
HtmlTableParser,
InfoFormat,
InfoType,
Input,
InputFormat,
JsonFileGenerator,
JsonGenerator,
JsonParser,
JsonStringGenerator,
NodeType,
Output,
ResourceBuilder,
StreamWriter,
StringWriter,
TextMarkType,
TextNodeType,
parse as bitmarkTextParse
};
//# sourceMappingURL=index.js.map