UNPKG

bitmark-grammar

Version:
1,428 lines (1,287 loc) 122 kB
/* * bitmark-listener.js * * last update * Nov 9, 2023 */ const R = require('ramda'); const Stack = require('./stack').Stack; const but = require('./bit-utils'); const { debug } = require('console'); const JSON_BIT_TEMPLATES = require('./bit-template.js').JSON_BIT_TEMPLATES; const webpack = false; const log = {}; const logfun = function (text) { console.log(text); } log.info = logfun; log.debug = logfun; log.error = logfun; // kebabcase to camelcase const camelize = s => s.replace(/-./g, x => x[1].toUpperCase()) // This class defines a complete listener for a parse tree produced by bitmarkParser. let BitmarkListener = function (error_listener, source, parser) { this.error_listener = error_listener; this.source = source; this.parser = parser; // New 10/9/2021 this.stk = new Stack(); this.curr_bit_stk = new Stack(); this.logo_stack = null; this.but = new but.BitUtil(source.trim()); this.format = ""; // &image, &audio, &video etc this.resformat = ""; // type starts with one of these}; this.resselfdesc = { 'image-link': 'image-link', 'audio-link': 'audio-link', 'video-link': 'video-link', 'image': 'image', 'audio': 'audio', 'video': 'video', 'still-image-film-link': 'still-image-film-link', 'still-image-film': 'still-image-film', // !!must come after -link 'image-mood': 'image', 'image-figure': 'image', 'image-banner': 'image', 'image-styled': 'image', 'image-screenshot': 'image', 'app-get-screenshot': 'image', 'app-create-bits-from-image': 'image', 'life-skill-sticker': 'image', 'details-image': 'image', 'images-logo-grave': 'image', 'page-banner': 'image', 'page-buy-button': 'image', 'image-render-svg': 'image', } // this.resformat is the key this.RESOURCE_MAP = { '&image': 'image', '&still-image-film': 'image', '&still-image-film-link': 'videoLink', '&image-link': 'imageLink', '&audio-link': 'audioLink', '&video-link': 'videoLink', '&article-link': 'articleLink', '&app-link': 'appLink', '&website-link': 'websiteLink', '&document-link': 'documentLink', '&document-download': 'documentDownload', }; this.resimagegrp = ['screenshot', 'focus-image', 'photo', 'browser-image']; // implicit image group this.reslist = ['&image', '&audio', '&video', '&document', '&app', '&website', '&still-image-film', '&pdf']; this.fmtlist = ['prosemirror', 'placeholder', 'text']; this.atdef_str = ['date', 'location', 'book', 'duration', 'action', 'deepLink', 'botAnnounceAt', 'botSaveAt', 'botSendAt', 'botRemindAt', 'externalLink', 'videoCallLink', 'externalLinkText', 'textReference', 'quotedPerson', 'kind', 'collection', 'book', 'padletId', 'scormSource', 'posterImage', 'computerLanguage', 'icon', 'iconChar', 'releaseDate', 'releaseVersion', 'content2Buy', 'resolvedDate', 'resolvedBy', 'publisher', 'theme' ]; this.atdef_num = ['focusX', 'focusY', 'numberOfStars', 'jupyter-execution_count', 'jupyter-id', 'reasonableNumOfChars', 'maxCreatedBits', 'maxDisplayLevel' ]; this.atdef_bool = ['aiGenerated', 'resolved', 'zoomDisabled']; this.bot_action_rating = []; // for storing bot-action-rating at exitHint() this.body_key = 'body'; this.num_angleref = 0; return this; } BitmarkListener.prototype = Object(); /*.create(clozeParserListener)*/ BitmarkListener.prototype.constructor = BitmarkListener; BitmarkListener.prototype.get_json = function () { return JSON.stringify(this.stk.bucket, null, 4); }; BitmarkListener.prototype.get_result = function () { while (0 < this.curr_bit_stk.size) delete this.curr_bit_stk.pop(); return this.stk.bucket; }; // BitmarkListener.prototype.push_bit2 = function (code, type, fmt_regex) { let vals = this.but.get_bit_value_colonsep(fmt_regex, code); let b = JSON_BIT_TEMPLATES.Regular_bit; b.bitmark = code; b.bit.type = type; this.stk.push(b); return vals; }; // Call on enter BitmarkListener.prototype.push_tmpl = function (ctx, type, template = R.clone(JSON_BIT_TEMPLATES.Regular_bit)) { let b = template; b.bit.type = type; // bit_type is like "[.video]" let bit_type = this.source.match(/\s*\[([^\]]+)\]/)[0]; let body = this.source.replace(bit_type, ''); let code = this.but.getcode(ctx).trim(); let res = this.but.get_bit_resource(code); // closing ] may be there let bitfmt = res.length === 0 ? 'bitmark--' : res[0]; b.bit['body'] = body; // If arg type is one of [image, audio, video], then set the resformat as the bit name let found = false; for (let t of Object.keys(this.resselfdesc)) { // ['image', 'audio', 'video', 'still-image-film'] if (type.startsWith(t)) { this.resformat = '&' + this.resselfdesc[t]; // image audio video found = true; break; } } if (!found && -1 < this.resimagegrp.indexOf(type)) this.resformat = '&image'; // image audio video if (-1 < ['bitmark++', 'bitmark--', 'text', 'json'].indexOf(bitfmt)) b.bit.format = bitfmt; this.stk.push(b); return res; }; // // BitmarkListener.prototype.set_value_based_on_curr_bit_stk = function (val1, sub1, val2, sub2, tmpl = null) { let subscript = this.curr_bit_stk.top(); let key_obj = null; if (subscript.startsWith('{')) key_obj = subscript.split('$'); // like "{Diaz:0}:marks" if (key_obj.length === 2) { if (tmpl && !(key_obj[0] in this.stk.top().bit[key_obj[1]])) (this.stk.top()).bit[key_obj[1]][key_obj[0]] = R.clone(tmpl); if (key_obj[0] in this.stk.top().bit[key_obj[1]]) { // e.g. bit.gaps['{0}'].example = true; (this.stk.top()).bit[key_obj[1]][key_obj[0]][sub1] = val1; if (sub2 != null) (this.stk.top()).bit[key_obj[1]][key_obj[0]][sub2] = val2; } } } BitmarkListener.prototype.save_blocktaggedtext = function (what, code) { if (0 < this.stk.size) { if (!(what in this.stk.top())) this.stk.top()[what] = []; this.stk.top()[what].push(code); // *** what *** } else log.error("save_blocktaggedtext stack is empty"); }; BitmarkListener.prototype.bitmark_enterRule = function (fn_name) { log.debug(`===>>${fn_name}`); }; BitmarkListener.prototype.bitmark_exitRule = function (fn_name) { log.debug(`<<===${fn_name}`); }; BitmarkListener.prototype.enterBook = function (ctx) { this.push_tmpl(ctx, 'book'); }; // Exit a parse tree produced by bitmarkParser#bitElem. //BitmarkListener.prototype.exitBook = function(ctx) {}; // Enter a parse tree produced by bitmarkParser#chapter. BitmarkListener.prototype.enterChapter = function (ctx) { this.push_tmpl(ctx, 'chapter'); // Added below 10/1/2020 set defaults const bit = this.stk.top().bit; bit['level'] = 1; bit['progress'] = true; bit['toc'] = true; }; // Exit a parse tree produced by bitmarkParser#chapter. //BitmarkListener.prototype.exitChapter = function(ctx) {}; // Enter a parse tree produced by bitmarkParser#progress. BitmarkListener.prototype.exitProgress = function (ctx) { let code = this.but.getcode(ctx); let vals = this.but.get_at_value(code); (this.stk.top()).bit['progress'] = vals[1]; // Remove it from body (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; BitmarkListener.prototype.enterToc = function (ctx) { this.push_tmpl(ctx, 'toc'); } // Enter a parse tree produced by bitmarkParser#toc. BitmarkListener.prototype.exitToc = function (ctx) { let code = this.but.getcode(ctx); let vals = this.but.get_at_value(code); if (this.stk.size === 0) { //this.push_bit(ctx, 'toc'); // toc can't exist by itself. } (this.stk.top()).bit['toc'] = vals[1]; // Remove it from body (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; // Enter a parse tree produced by bitmarkParser#summary. BitmarkListener.prototype.enterSummary = function (ctx) { this.push_tmpl(ctx, 'summary'); }; // Exit a parse tree produced by bitmarkParser#summary. //BitmarkListener.prototype.exitSummary = function(ctx) {}; // Enter a parse tree produced by bitmarkParser#bit. BitmarkListener.prototype.enterBit = function (ctx) { let code = this.but.getcode(ctx); this.but.set_currentbit(code); }; // Exit a parse tree produced by bitmarkParser#bit. BitmarkListener.prototype.exitBit = function (ctx) { // Add an empty resource data if format is defined. if (this.resformat != '' && this.stk.top().bit.resource === undefined && 0 <= this.reslist.indexOf(this.resformat)) { let res = this.resformat; if (this.resformat.startsWith('&')) res = this.resformat.substr(1); /*this.stk.top().bit['resource'] = { 'type': res, [res]: {} }*/ } this.stk.top().bit.body = this.stk.top().bit.body.trim(); }; BitmarkListener.prototype.exitBitmark_ = function (ctx) { this.stk.top().bit.body = this.stk.top().bit.body.trim(); }; // Enter a parse tree produced by bitmarkParser#group_open. BitmarkListener.prototype.enterGroup_born = function (ctx) { this.push_tmpl(ctx, 'group*'); }; // Exit a parse tree produced by bitmarkParser#group_open. //BitmarkListener.prototype.exitGroup_born = function(ctx) {}; // Enter a parse tree produced by bitmarkParser#group_close. BitmarkListener.prototype.enterGroup_died = function (ctx) { this.push_tmpl(ctx, 'group†'); }; // Exit a parse tree produced by bitmarkParser#group_close. //BitmarkListener.prototype.exitGroup_died = function(ctx) {}; BitmarkListener.prototype.enterCloze = function (ctx) { this.push_tmpl(ctx, 'cloze'); // default template }; // Enter a parse tree produced by bitmarkParser#cloze_instruction_grouped. BitmarkListener.prototype.enterCloze_instruction_grouped = function (ctx) { this.push_tmpl(ctx, 'cloze-instruction-grouped'); }; // Enter a parse tree produced by bitmarkParser#cloze_solution_grouped. BitmarkListener.prototype.enterCloze_solution_grouped = function (ctx) { this.push_tmpl(ctx, 'cloze-solution-grouped'); }; BitmarkListener.prototype.enterGap_text = function (ctx) { this.push_tmpl(ctx, 'gap-text'); // default template }; // Enter a parse tree produced by bitmarkParser#cloze_instruction_grouped. BitmarkListener.prototype.enterGap_text_instruction_grouped = function (ctx) { this.push_tmpl(ctx, 'gap-text-instruction-grouped'); }; // Exit a parse tree produced by bitmarkParser#cloze_solution_grouped. //BitmarkListener.prototype.exitCloze_solution_grouped = function(ctx) {}; // This is INTENTIONALLY enter!! BitmarkListener.prototype.enterGap = function (ctx) { let code = this.but.getcode(ctx); if (0 < this.stk.size) { if (!('placeholders' in this.stk.top().bit)) (this.stk.top()).bit['placeholders'] = {}; let n = Object.keys(this.stk.top().bit['placeholders']).length; let key = `{${n}}`; this.stk.top().bit['placeholders'][key] = R.clone(JSON_BIT_TEMPLATES.Gap_bit); this.curr_bit_stk.push(key + '$placeholders'); // let children know the key } else log.error('enterGap no bit on stack'); } BitmarkListener.prototype.exitGap = function (ctx) { let code = this.but.getcode(ctx); let kp = this.curr_bit_stk.pop(); //(key+'$placeholders'); let keypl = kp.split('$'); code = code.replace(/\[[^_][^\]]+\]/g, ''); this.stk.top().bit.body = this.stk.top().bit.body.replace(code, keypl[0]); }; BitmarkListener.prototype.exitSingle_gap = function (ctx) { let code = this.but.getcode(ctx); let type = this.stk.top().bit.type; if (type.startsWith('cloze') || type.startsWith('article')) { let val = code.match(/[^\[]*(\[.*\])([^\]]|[\s])*/s); // accept NEWLINE in the text let n = Object.keys(this.stk.top().bit['placeholders']).length - 1; let key = `{${n}}`; let vals = code.match(/\[_([^\]]+)\]/g); // could be more than 1 // vals can be null when it is [_] if (vals) { for (let g of vals) { let v = this.but.get_bit_value(/\[_([^\]]*)\]/, g); this.stk.top().bit['placeholders'][key].solutions.push(v); } } } }; // Enter a parse tree produced by bitmarkParser#instruction. BitmarkListener.prototype.exitInstruction = function (ctx) { let code = this.but.getcode2(ctx); let re = /\[!(([^\]]|[\s])*)\]/s; // accepts newline let val = this.but.get_bit_value(re, code); const bit = this.stk.top().bit; if (!val) { // There can be [!] return; } if (0 < this.stk.size) { const what = this.curr_bit_stk.top(); if (['cplus', 'cminus', 'pair', 'interview_qanda', 'essay', 'mcrmisc', 'panswer'] .indexOf(what) >= 0) { (this.curr_bit_stk.bottom()).instruction = val; // was second } else if (what === 'mpanswer') { (this.curr_bit_stk.second()).instruction = val; // was second } else if (what === 'bot_action') { // For bot_action_rating_number if (val.match(/^[0-9]+$/)) val = parseInt(val); let l = bit.responses.length; bit.responses[l - 1].response = val; this.bot_action_rating.push(val); } else if (what === 'menu') { this.curr_bit_stk.third()['instruction'] = val; } else if (what != null) { let key_obj = ''; if (typeof what === 'string' && what.startsWith('{')) key_obj = what.split('$'); if (typeof what === 'string' && key_obj.length === 2) { bit[key_obj[1]][key_obj[0]]['instruction'] = val; } else bit['instruction'] = val; } else { // no key bit['instruction'] = val; } // Remove it from body if (what !== 'pair_multival') { bit.body = bit.body.replace(code, ''); } } else log.error("exitInstruction stack is empty"); }; // Enter a parse tree produced by bitmarkParser#hint. BitmarkListener.prototype.exitHint = function (ctx) { let code = this.but.getcode(ctx); let re = /\[\?([^\]]*)\]/; let val = this.but.get_bit_value(re, code); let what = this.curr_bit_stk.top(); const bit = this.stk.top().bit; if (what == 'footer') return; if (0 < this.stk.size && 0 < this.curr_bit_stk.size) { const what = this.curr_bit_stk.top(); let key_obj = what; if (what === 'cplus' || what === 'cminus' || what == 'pair') { (this.curr_bit_stk.second()).hint = val; } else if (what === 'bot_action') { let l = bit.responses.length; bit.responses[l - 1].hint = val; } else if (what === 'menu') { this.curr_bit_stk.third()['hint'] = val; } else { if (typeof what === 'string' && what.startsWith('{')) key_obj = what.split('$'); if (typeof what === 'string' && Array.isArray(key_obj) && key_obj.length === 2) { // bit.gaps.{1}.hint = val bit[key_obj[1]][key_obj[0]]['hint'] = val; } else if (what === 'panswer' || what === 'interview_qanda') (this.curr_bit_stk.bottom()).hint = val; else if (typeof what === 'object') what.hint = val; else bit['hint'] = val; } // TODO key_obj.length < 2??? } else { bit['hint'] = val; } bit.body = bit.body.replace(code, ''); }; BitmarkListener.prototype.enterHeaded_choices = function (ctx) { let p = R.clone(JSON_BIT_TEMPLATES.Mct_placeholder); let n = 0; if (bit['placeholders'] === undefined) bit['placeholders'] = []; else n = Object.keys(bit['placeholders']).length; let key = `{${n}}`; bit['placeholders'][key] = p; this.curr_bit_stk.push(key); // let children know the key }; BitmarkListener.prototype.enterMcrsep = function (ctx) { const bit = this.stk.top().bit; if (bit.type.startsWith('multiple-choice')) { let quiz = R.clone(JSON_BIT_TEMPLATES.Mc_quiz); bit['quizzes'].push(quiz); // Push quiz to store group-wise inst, item, hint etc this.curr_bit_stk.push(quiz); } else if (bit.type.startsWith('multiple-response')) { let resp = R.clone(JSON_BIT_TEMPLATES.Mr_response); bit['quizzes'].push(resp); // Push resp to store group-wise inst, item, hint etc this.curr_bit_stk.push(resp); } else if (bit.type.startsWith('true-false')) { let stmt = R.clone(JSON_BIT_TEMPLATES.True_false_stmt); bit['statements'].push(stmt); // Push resp to store group-wise inst, item, hint etc this.curr_bit_stk.push(stmt); } // Remove all === from body bit.body = bit.body.replace(/\n?===\n?/g, ''); }; BitmarkListener.prototype.enterMcrmisc = function (ctx) { this.curr_bit_stk.push('mcrmisc'); }; BitmarkListener.prototype.exitMcrmisc = function (ctx) { this.curr_bit_stk.pop(); // obj this.curr_bit_stk.pop(); // "mcrmisc" }; BitmarkListener.prototype.get_multi_choice_keys = function (bit_type) { const mul_choice_list_key = { 'multiple-choice-1': 'choices-choice', 'multiple-choice': 'quizzes-choices-choice', 'multiple-response-1': 'responses-response', 'multiple-response': 'quizzes-responses-response', 'true-false-1': 'statement', 'true-false': 'statements-statement', }; const mul_choice_tmpl_class = { 'multiple-choice-1': JSON_BIT_TEMPLATES.Mc_quiz_choice, 'multiple-choice': JSON_BIT_TEMPLATES.Mc_quiz_choice, 'multiple-response-1': JSON_BIT_TEMPLATES.Mr_resp_resp, 'multiple-response': JSON_BIT_TEMPLATES.Mr_resp_resp, 'true-false-1': null, 'true-false': JSON_BIT_TEMPLATES.True_false_stmt, }; let list_key = mul_choice_list_key[bit_type]; if (list_key) { let spl = list_key.split('-'); spl.unshift(spl.length); // Get element template let tmpl = mul_choice_tmpl_class[bit_type]; spl.push(tmpl); return spl; // [len, key1, key2?, key3?, tmpl] } return []; }; // BitmarkListener.prototype.exitCplus = function (ctx) { this.curr_bit_stk.pop(); // cplus this.curr_bit_stk.pop(); // cplus obj }; BitmarkListener.prototype.exitCminus = function (ctx) { this.curr_bit_stk.pop(); // cminus this.curr_bit_stk.pop(); // cminus obj }; // Enter a parse tree produced by bitmarkParser#choice_plus. BitmarkListener.prototype.exitChoice_plus = function (ctx) { let code = this.but.getcode(ctx); let bit_type = (this.stk.top()).bit.type; const re = /\[\+([^\]]*)\]/; let val = this.but.get_bit_value(re, code); let listname = bit_type.startsWith('highlight') ? 'texts' : 'options'; const bit = this.stk.top().bit; if (bit_type === 'bot-interview') return; // TODO fix once JSON is defined let what = this.curr_bit_stk.top(); if (what === 'bot_action') { let l = bit.responses.length; bit.responses[l - 1].response = val; bit.responses[l - 1]['isCorrect'] = true; bit.body = bit.body.replace(code, ''); } else if ('placeholders' in bit) { // multi_choice_text inline choices let key = this.curr_bit_stk.top() === null ? '{0}' : this.curr_bit_stk.top(); let opt; opt = bit_type !== 'highlight-text' ? R.clone(JSON_BIT_TEMPLATES.Mct_opt) : R.clone(JSON_BIT_TEMPLATES.Highlight_text_text); opt.text = val; opt.isCorrect = true; // because plus bit['placeholders'][key][listname].push(opt); } else if (bit_type === 'true-false') { if (this.curr_bit_stk.size === 0) { console.error('True_false_stmt slot does not exist'); return; } this.curr_bit_stk.top().statement = val; this.curr_bit_stk.top().isCorrect = true; bit.body = bit.body.replace(code, ''); } else { // multiple-choice+ let spl = this.get_multi_choice_keys(bit_type); if (1 < spl[0]) { let sz1 = bit[spl[1]].length; let choice_obj; sz1 = bit[spl[1]].length; // was 1 let sz2 = 0; if (bit_type.endsWith('-1')) // multi-choice-1, multi-response-1 sz2 = bit[spl[1]].length == 0 ? 0 : bit[spl[1]].length; else sz2 = bit[spl[1]][sz1 == 0 ? 0 : sz1 - 1][spl[2]].length; if (4 < spl.length) { choice_obj = R.clone(spl[spl.length - 1]); // clone (bit[spl[1]][sz1 - 1])[spl[2]].push(choice_obj); sz2 = (bit[spl[1]][sz1 - 1])[spl[2]].length; } else { choice_obj = R.clone(spl[spl.length - 1]); // clone bit[spl[1]].push(choice_obj); sz1 += 1; } if (2 < spl[0]) { ((bit[spl[1]][sz1 - 1])[spl[2]][sz2 - 1])[spl[3]] = val; } else { (bit[spl[1]][sz1 - 1])[spl[2]] = val; (bit[spl[1]][sz1 - 1]).isCorrect = true; // bcz this is plus } bit.body = bit.body.replace(code, ''); this.curr_bit_stk.push(choice_obj); this.curr_bit_stk.push('cplus'); } else { bit[spl[1]] = val; bit.body = bit.body.replace(code, ''); } } }; // Enter a parse tree produced by bitmarkParser#choice_minus. BitmarkListener.prototype.exitChoice_minus = function (ctx) { let code = this.but.getcode(ctx); let bit_type = (this.stk.top()).bit.type; const re = /\[\-([^\]]*)\]/; let val = this.but.get_bit_value(re, code); let listname = bit_type.startsWith('highlight') ? 'texts' : 'options'; const bit = this.stk.top().bit; if (bit_type === 'bot-interview') return; // TODO fix once JSON is defined let what = this.curr_bit_stk.top(); if (what === 'bot_action') { let l = bit.responses.length; bit.responses[l - 1].response = val; bit.responses[l - 1]['isCorrect'] = false; bit.body = bit.body.replace(code, ''); } else if ('placeholders' in bit) { // multi_choice_text inline choices let key = this.curr_bit_stk.top() === null ? '{0}' : this.curr_bit_stk.top(); let opt; if (-1 < key.indexOf('$')) { key = key.split('$')[0]; } opt = bit_type !== 'highlight-text' ? R.clone(JSON_BIT_TEMPLATES.Mct_opt) : R.clone(JSON_BIT_TEMPLATES.Highlight_text_text); opt.text = val; opt.isCorrect = false; // because minus bit['placeholders'][key][listname].push(opt); } else if (bit_type === 'true-false') { if (this.curr_bit_stk.size === 0) { console.error('True_false_stmt slot does not exist'); return; } this.curr_bit_stk.top().statement = val; this.curr_bit_stk.top().isCorrect = false; bit.body = bit.body.replace(code, ''); } else { // multiple-choice- let spl = this.get_multi_choice_keys(bit_type); if (1 < spl[0]) { let sz1 = bit[spl[1]].length; let choice_obj; sz1 = bit[spl[1]].length; let sz2; if (bit_type.endsWith('-1')) sz2 = bit[spl[1]].length == 0 ? 0 : bit[spl[1]].length; else sz2 = bit[spl[1]][sz1 == 0 ? 0 : sz1 - 1][spl[2]].length; if (4 < spl.length) { choice_obj = R.clone(spl[spl.length - 1]); (bit[spl[1]][sz1 - 1])[spl[2]].push(choice_obj); sz2 = (bit[spl[1]][sz1 - 1])[spl[2]].length; } else { choice_obj = R.clone(spl[spl.length - 1]); // clone bit[spl[1]].push(choice_obj); sz1 += 1; } if (2 < spl[0]) { ((bit[spl[1]][sz1 - 1])[spl[2]][sz2 - 1])[spl[3]] = val; ((bit[spl[1]][sz1 - 1])[spl[2]][sz2 - 1]).isCorrect = false; } else { (bit[spl[1]][sz1 - 1])[spl[2]] = val; (bit[spl[1]][sz1 - 1]).isCorrect = false; // bcz this is minus } (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); this.curr_bit_stk.push(choice_obj); this.curr_bit_stk.push('cminus'); } else { // (spl[0]===1) bit[spl[1]] = val; } } }; BitmarkListener.prototype.exitIl_choice_plus = function (ctx) { this.exitChoice_plus(ctx); }; BitmarkListener.prototype.exitIl_choice_minus = function (ctx) { this.exitChoice_minus(ctx); }; // Enter a parse tree produced by bitmarkParser#multiple_choice_1. BitmarkListener.prototype.enterMultiple_choice_1 = function (ctx) { this.push_tmpl(ctx, 'multiple-choice-1', R.clone(JSON_BIT_TEMPLATES.Multi_choice_1)); }; // Exit a parse tree produced by bitmarkParser#multiple_choice_1. //BitmarkListener.prototype.exitMultiple_choice_1 = function(ctx) {}; // Enter a parse tree produced by bitmarkParser#multiple_choice. BitmarkListener.prototype.enterMultiple_choice = function (ctx) { this.push_tmpl(ctx, 'multiple-choice', R.clone(JSON_BIT_TEMPLATES.Multi_choice)); }; // Exit a parse tree produced by bitmarkParser#multiple_choice. //BitmarkListener.prototype.exitMultiple_choice = function(ctx) {}; // Enter a parse tree produced by bitmarkParser#multiple_response_1. BitmarkListener.prototype.enterMultiple_response_1 = function (ctx) { this.push_tmpl(ctx, 'multiple-response-1', R.clone(JSON_BIT_TEMPLATES.Multi_response_1)); }; // Exit a parse tree produced by bitmarkParser#multiple_response_1. //BitmarkListener.prototype.exitMultiple_response_1 = function(ctx) {}; // Enter a parse tree produced by bitmarkParser#multiple_response. BitmarkListener.prototype.enterMultiple_response = function (ctx) { this.push_tmpl(ctx, 'multiple-response', R.clone(JSON_BIT_TEMPLATES.Multi_response)); }; // Exit a parse tree produced by bitmarkParser#multiple_response. BitmarkListener.prototype.exitMultiple_response = function (ctx) { }; // Enter a parse tree produced by bitmarkParser#multiple_choice_text. BitmarkListener.prototype.enterMultiple_choice_text = function (ctx) { this.push_tmpl(ctx, 'multiple-choice-text', R.clone(JSON_BIT_TEMPLATES.Multi_choice_text)); }; //BitmarkListener.prototype.exitMultiple_choice_text = function(ctx) {}; BitmarkListener.prototype.exitMultiple_il_choice = function (ctx) { this.curr_bit_stk.pop(); }; BitmarkListener.prototype.enterHighlight_text = function (ctx) { this.push_tmpl(ctx, 'highlight-text', R.clone(JSON_BIT_TEMPLATES.Highlight_text)); }; //BitmarkListener.prototype.exitHighlight_text = function(ctx) {}; // BitmarkListener.prototype.enterHeaded_inline_choices = function (ctx) { let code = this.but.getcode(ctx); let p = R.clone(JSON_BIT_TEMPLATES.Mct_placeholder); let n = Object.keys(this.stk.top().bit['placeholders']).length; let key = `{${n}}`; this.stk.top().bit['placeholders'][key] = p; this.curr_bit_stk.push(p); // let children know the key this.curr_bit_stk.push(key); // let children know the key }; BitmarkListener.prototype.exitHeaded_inline_choices = function (ctx) { let code = this.but.getcode(ctx); let key = this.curr_bit_stk.pop(); // replace the choices with the placeholder seq num code = code.replace(/\[[^\+\-][^\]]+\]/g, ''); this.stk.top().bit['body'] = this.stk.top().bit['body'].replace(code, key); this.curr_bit_stk.pop(); // discard p }; // BitmarkListener.prototype.enterHighlight_inline_choices = function (ctx) { let code = this.but.getcode(ctx); let p = R.clone(JSON_BIT_TEMPLATES.Highlight_placeholder); let n = Object.keys(this.stk.top().bit['placeholders']).length; let key = `{${n}}`; this.stk.top().bit['placeholders'][key] = p; // let children know the key e.g. {0} this.curr_bit_stk.push(p); // let children know the key this.curr_bit_stk.push(key); }; BitmarkListener.prototype.exitHighlight_inline_choices = function (ctx) { let code = this.but.getcode(ctx); let key = this.curr_bit_stk.pop(); // replace the choices with the placeholder seq num code = code.replace(/\[[^\+\-][^\]]+\]/g, ''); this.stk.top().bit['body'] = this.stk.top().bit['body'].replace(code, key); this.curr_bit_stk.pop(); // discard p }; // Enter a parse tree produced by bitmarkParser#choice_head. BitmarkListener.prototype.exitChoice_head = function (ctx) { let code = this.but.getcode(ctx); const key = this.curr_bit_stk.top(); // let children know the key const re = /\[\'([^\]]*)\]/; let val = this.but.get_bit_value(re, code); const bit = this.stk.top().bit; bit['placeholders'][key].prefix = val; bit.body = bit.body.replace(code, ''); } // Enter a parse tree produced by bitmarkParser#cloze_and_multiple_choice_text. BitmarkListener.prototype.enterCloze_and_multiple_choice_text = function (ctx) { this.push_tmpl(ctx, 'cloze-and-multiple-choice-text', R.clone(JSON_BIT_TEMPLATES.Multi_choice_text)); }; // Exit a parse tree produced by bitmarkParser#cloze_and_multiple_choice_text. //BitmarkListener.prototype.exitCloze_and_multiple_choice_text = function(ctx) {}; // Enter a parse tree produced by bitmarkParser#essay. BitmarkListener.prototype.enterEssay = function (ctx) { this.push_tmpl(ctx, 'essay', R.clone(JSON_BIT_TEMPLATES.Essay_bit)); this.curr_bit_stk.push(this.stk.top().bit); this.curr_bit_stk.push('essay'); } // Exit a parse tree produced by bitmarkParser#essay. //BitmarkListener.prototype.exitEssay = function(ctx) {}; // Enter a parse tree produced by bitmarkParser#ml_example. // For multiple-choice-text and hightlight-text BitmarkListener.prototype.enterIl_follow = function (ctx) { let code = this.but.getcode(ctx); let is_example = false; let key, bool_key = null; let re; //console.error('****enterIl_follow*** '+code); if (-1 < code.indexOf('[@exa')) { is_example = true; re = /\[@example:([^\]]*)\]/; key = 'example'; bool_key = 'isExample'; } else { is_example = false; // HINT re = /\[\?([^\]]*)\]/; key = 'hint'; bool_key = null; } let val = this.but.get_bit_value(re, code); if (val === null) val = ''; let cbit = this.curr_bit_stk.top(); if (cbit && cbit.startsWith('{')) { if (bool_key) this.stk.top().bit.placeholders[cbit][bool_key] = true; this.stk.top().bit.placeholders[cbit][key] = val; // Remove it from body (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); } }; // Exit a parse tree produced by bitmarkParser#ml_example. BitmarkListener.prototype.exitIl_follow = function (ctx) { let code = this.but.getcode(ctx); // Remove it from body (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; // Enter a parse tree produced by bitmarkParser#example. BitmarkListener.prototype.exitExample = function (ctx) { let code = this.but.getcode(ctx); const re = /\[@example:([^\]]*)\]/; let val = this.but.get_bit_value(re, code); if (val === null) val = ''; let cbstk = this.curr_bit_stk; let cbit = this.curr_bit_stk.top(); let type = this.stk.top().bit.type; let remove_from_body = false; const bit = this.stk.top().bit; if (cbit === 'pair' || cbit === 'pquery' || cbit === 'panswer') { let lastpair = this.stk.top().bit['pairs'].length - 1; bit['pairs'][lastpair].isExample = true; bit['pairs'][lastpair].example = val.trim(); // Remove it from body remove_from_body = true; } else if (cbit && typeof cbit === 'string' && cbit.startsWith('{')) { this.set_value_based_on_curr_bit_stk(val, 'example', true, 'isExample', null); remove_from_body = true; } else if (cbit === 'interview_qanda' || cbit === 'essay') { (this.curr_bit_stk.second()).example = val; (this.curr_bit_stk.second()).isExample = true; remove_from_body = true; } else if (type === 'match-matrix') { let cell_len = this.curr_bit_stk.bottom().cells.length; this.curr_bit_stk.bottom().cells[cell_len - 1].example = val; this.curr_bit_stk.bottom().cells[cell_len - 1].isExample = true; remove_from_body = true; } else if (type === 'true-false' && (cbit === 'cplus' || cbit === 'cminus')) { // Interview JSON not defined yet this.curr_bit_stk.bottom()['example'] = val; // ((((( this.curr_bit_stk.bottom().isExample = true; remove_from_body = true; } else if (typeof cbit === 'object' && !(this.stk.top().bit.type.startsWith('interview')) // Interview JSON undef! && !(this.stk.top().bit.type.startsWith('cloze')) // Need this ) { // Interview JSON not defined yet if (this.curr_bit_stk.size === 1) { this.curr_bit_stk.top().example = val; this.curr_bit_stk.top().isExample = true; remove_from_body = true; } else if (2 <= this.curr_bit_stk.size) { this.curr_bit_stk.second().example = val; this.curr_bit_stk.second().isExample = true; remove_from_body = true; } else { this.stk.top().bit['example'] = val; this.stk.top().bit['isExample'] = true; remove_from_body = true; } } else { this.stk.top()['example'] = val.trim(); remove_from_body = true; } if (remove_from_body) { bit.body = bit.body.replace(code, ''); } }; // Enter a parse tree produced by bitmarkParser#interview. BitmarkListener.prototype.enterInterview = function (ctx) { this.push_tmpl(ctx, 'interview', R.clone(JSON_BIT_TEMPLATES.Interview_bit)); }; BitmarkListener.prototype.exitInterview = function (ctx) { let bit = this.stk.top().bit; if (this.resformat != '' && bit.resource === undefined) { if (0 <= this.reslist.indexOf(this.resformat)) { // Line number is the num lines //let lines = (this.stk.top().bitmark.match(/\n/g)||[]).length; this.error_listener.manualError(ctx, ctx._start.line - 1, 0, 'Missing resource(s) for ' + this.resformat); } } }; BitmarkListener.prototype.enterInterview_qanda = function (ctx) { let qanda = R.clone(JSON_BIT_TEMPLATES.Interview_question); this.stk.top().bit['questions'].push(qanda); this.curr_bit_stk.push(qanda); this.curr_bit_stk.push('interview_qanda'); }; BitmarkListener.prototype.exitInterview_qanda = function (ctx) { // OK this.curr_bit_stk.pop(); this.curr_bit_stk.pop(); }; BitmarkListener.prototype.exitEmphasis_ = function (ctx) { let code = this.but.getcode(ctx); let re = /\[\$(([^\]]|[\s])*)\]/s; // accepts newline if (this.curr_bit_stk.top() === 'interview_qanda') { let val = this.but.get_bit_value(re, code); (this.curr_bit_stk.second()).question = code; // Move this outer if there are multiple (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(this.but.getcode(ctx), ''); } }; BitmarkListener.prototype.exitDollarans = function (ctx) { let code = this.but.getcode(ctx); const re = /\[\$(([^\]]|[\s])*)\]/s; // accepts newline if (this.curr_bit_stk.top() === 'interview_qanda') { let val = this.but.get_bit_value(re, code); (this.curr_bit_stk.second()).sampleSolution = val; // Move this outer if there are multiple (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(this.but.getcode(ctx), ''); } }; BitmarkListener.prototype.exitPartans = function (ctx) { let code = this.but.getcode(ctx); let re = /\[@partialAnswer:?(([^\]]|[\s])*)\]/s; // accepts newline const cbit = this.curr_bit_stk.top() if (cbit === 'interview_qanda' || cbit === 'essay') { let val = this.but.get_bit_value(re, code); (this.curr_bit_stk.second()).partialAnswer = val; // Move this outer if there are multiple (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(this.but.getcode(ctx), ''); } }; // Enter a parse tree produced by bitmarkParser#longans. // [@longAnswer] BitmarkListener.prototype.exitLongans = function (ctx) { let cbit = this.curr_bit_stk.top(); if (cbit === 'Panswer') { let lastpair = this.stk.top().bit['pairs'].length - 1; this.stk.top().bit['pairs'][lastpair].isLongAnswer = true; } else if (cbit === 'interview_qanda') (this.curr_bit_stk.second()).isShortAnswer = false; (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(this.but.getcode(ctx), ''); } BitmarkListener.prototype.exitShortans = function (ctx) { let cbit = this.curr_bit_stk.top(); if (cbit === 'interview_qanda') (this.curr_bit_stk.second()).isShortAnswer = true; (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(this.but.getcode(ctx), ''); }; BitmarkListener.prototype.exitSample_soln_prop = function (ctx) { let cbit = this.curr_bit_stk.top(); if (cbit === 'interview_qanda') { let code = this.but.getcode2(ctx).trim(); const re = /\[@sampleSolution:([^\]]+)\]/; let val = this.but.get_bit_value(re, code); (this.curr_bit_stk.second()).sampleSolution = val; } (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(this.but.getcode(ctx), ''); }; // BitmarkListener.prototype.exitInterview_text = function (ctx) { let cbit = this.curr_bit_stk.top(); let code = this.but.getcode2(ctx).trim(); // was but.getcode() if (cbit === 'interview_qanda') (this.curr_bit_stk.second()).question = code; (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(this.but.getcode(ctx), ''); }; BitmarkListener.prototype.exitInterview_sometext = function (ctx) { let cbit = this.curr_bit_stk.top(); let code = this.but.getcode2(ctx).trim(); // was but.getcode() if (cbit === 'interview_qanda') (this.curr_bit_stk.second()).question += code; (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(this.but.getcode(ctx), ''); }; BitmarkListener.prototype.enterInterview_instruction_grouped = function (ctx) { this.push_tmpl(ctx, 'interview-instruction-grouped', R.clone(JSON_BIT_TEMPLATES.Interview_bit)); }; // Enter a parse tree produced by bitmarkParser#interview_instruction_grouped. //BitmarkListener.prototype.exitInterview_instruction_grouped = function(ctx) {}; BitmarkListener.prototype.exitFooter_resource = function (ctx) { let code = this.but.getcode2(ctx); // was but.getcode() (this.stk.top()).bit.footer = (this.stk.top()).bit.footer + code.trim(); (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; BitmarkListener.prototype.enterInterview_footer = function (ctx) { this.curr_bit_stk.push('footer'); }; BitmarkListener.prototype.exitInterview_footer = function (ctx) { this.curr_bit_stk.pop(); }; // Enter a parse tree produced by bitmarkParser#title. BitmarkListener.prototype.exitTitlestar_ = function (ctx) { let code = this.but.getcode(ctx); this.save_blocktaggedtext('emphasized', code); } // Enter a parse tree produced by bitmarkParser#title. BitmarkListener.prototype.exitTitle = function (ctx) { let code = this.but.getcode(ctx); let val = this.but.get_title(code); let level = this.but.get_numhash(code); let bit = this.stk.top().bit; if (0 < this.stk.size) { if (0 < level && bit.type == 'chapter') { this.stk.top().bit['title'] = val; this.stk.top().bit['level'] = level; } else { //Book if (level < 2) this.stk.top().bit['title'] = val; else this.stk.top().bit['subtitle'] = val; } // Remove it from body (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); } else log.error("enterTitle stack is empty"); }; BitmarkListener.prototype.remove_separators_from_body = function () { // Remove all === from body (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(/\n?==(=)?\n?/g, ''); }; // Enter a parse tree produced by bitmarkParser#match_. BitmarkListener.prototype.enterMatch_ = function (ctx) { this.push_tmpl(ctx, 'match', R.clone(JSON_BIT_TEMPLATES.Match_bit)); } // Exit a parse tree produced by bitmarkParser#match_. BitmarkListener.prototype.exitMatch_ = function (ctx) { this.remove_separators_from_body(); // Remove all === from body } BitmarkListener.prototype.enterMatch_all = function (ctx) { this.push_tmpl(ctx, 'match-all', R.clone(JSON_BIT_TEMPLATES.Match_bit)); } BitmarkListener.prototype.exitMatch_all = function (ctx) { this.remove_separators_from_body(); // Remove all === from body } BitmarkListener.prototype.enterMatch_all_reverse = function (ctx) { this.push_tmpl(ctx, 'match-all-reverse', R.clone(JSON_BIT_TEMPLATES.Match_bit)); }; BitmarkListener.prototype.exitMatch_all_reverse = function (ctx) { this.remove_separators_from_body(); // Remove all === from body }; BitmarkListener.prototype.enterMatch_reverse = function (ctx) { this.push_tmpl(ctx, 'match-reverse', R.clone(JSON_BIT_TEMPLATES.Match_bit)); } BitmarkListener.prototype.exitMatch_reverse = function (ctx) { this.remove_separators_from_body(); // Remove all === from body }; BitmarkListener.prototype.enterMatch_picture = function (ctx) { this.push_tmpl(ctx, 'match-picture', R.clone(JSON_BIT_TEMPLATES.Match_bit)); }; BitmarkListener.prototype.exitMatch_picture = function (ctx) { this.remove_separators_from_body(); // Remove all === from body }; BitmarkListener.prototype.enterMatch_audio = function (ctx) { this.push_tmpl(ctx, 'match-audio', R.clone(JSON_BIT_TEMPLATES.Match_bit)); }; BitmarkListener.prototype.exitMatch_audio = function (ctx) { this.remove_separators_from_body(); // Remove all === from body }; BitmarkListener.prototype.enterMatch_matrix = function (ctx) { this.push_tmpl(ctx, 'match-matrix', R.clone(JSON_BIT_TEMPLATES.MatchMatrix_bit)); }; BitmarkListener.prototype.exitMatch_matrix = function (ctx) { this.remove_separators_from_body(); // Remove all === from body }; BitmarkListener.prototype.enterPair_heading = function (ctx) { this.stk.top().bit['heading'] = { 'forKeys': '', 'forValues': '' }; this.curr_bit_stk.push('pair_heading'); }; BitmarkListener.prototype.exitPair_heading = function (ctx) { // Remove it from body let code = this.but.getcode(ctx); (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); this.curr_bit_stk.pop(); }; // rule=heading_multi: key_title eq value_title ( eq value_title )* ; BitmarkListener.prototype.enterPair_heading_multi = function (ctx) { this.stk.top().bit['heading'] = { 'forKeys': '', 'forValues': [] }; }; BitmarkListener.prototype.exitPair_heading_multi = function (ctx) { let code = this.but.getcode(ctx); (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; // For each pair create a pair object and push BitmarkListener.prototype.enterPqpair = function (ctx) { let p = R.clone(JSON_BIT_TEMPLATES.Match_pair); delete (p.keyImage); delete (p.keyAudio); this.stk.top().bit['pairs'].push(p); this.curr_bit_stk.push(p); this.curr_bit_stk.push('pair'); }; BitmarkListener.prototype.exitPqpair = function (ctx) { this.curr_bit_stk.pop(); this.curr_bit_stk.pop(); }; BitmarkListener.prototype.exitOr_ = function (ctx) { let code = this.but.getcode(ctx); (this.stk.top()).bit['body'] = (this.stk.top()).bit['body'].replace(code, ''); } // For each pair create a pair object and push BitmarkListener.prototype.enterPair_multival = function (ctx) { let p = R.clone(JSON_BIT_TEMPLATES.MatchMatrix_matrixelem); delete (p.keyImage); delete (p.keyAudio); this.stk.top().bit['matrix'].push(p); this.curr_bit_stk.push(p); this.curr_bit_stk.push('pair_multival'); }; BitmarkListener.prototype.enterPair_multivals = function (ctx) { //console.error('enterPair_multivals'); }; BitmarkListener.prototype.exitPair_multival = function (ctx) { // Remove it from body let code = this.but.getcode(ctx); // Remove these from body (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; // For each pair create a pair object and push BitmarkListener.prototype.enterPair = function (ctx) { let p = R.clone(JSON_BIT_TEMPLATES.Match_pair); delete (p.keyImage); delete (p.keyAudio); this.stk.top().bit['pairs'].push(p); }; // // For each pair create a pair object and push BitmarkListener.prototype.enterPair_image = function (ctx) { let p = R.clone(JSON_BIT_TEMPLATES.Match_pair); delete (p.key); delete (p.keyAudio); this.stk.top().bit['pairs'].push(p); }; //BitmarkListener.prototype.enterPair_audios = function(ctx) {}; // For each pair create a pair object and push BitmarkListener.prototype.enterPair_audio = function (ctx) { let p = R.clone(JSON_BIT_TEMPLATES.Match_pair); delete (p.key); delete (p.keyImage); this.stk.top().bit['pairs'].push(p); }; // Audio bit BitmarkListener.prototype.exitPaudiobit = function (ctx) { let code = this.but.getcode(ctx); let [what, url] = this.but.get_url(code); let lastpair = this.stk.top().bit['pairs'].length - 1; this.stk.top().bit['pairs'][lastpair].keyAudio.src = url; this.stk.top().bit['pairs'][lastpair].keyAudio.format = url.split('.').pop(); // Remove it from body (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; // Image bit BitmarkListener.prototype.exitPimagebit = function (ctx) { let code = this.but.getcode(ctx); let [what, url] = this.but.get_url(code); // get_bracket_content(code); let lastpair = this.stk.top().bit['pairs'].length - 1; this.stk.top().bit['pairs'][lastpair].keyImage['src'] = url; //.split('::').pop(); // Remove it from body (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; BitmarkListener.prototype.exitResource_chained = function (ctx) { let code = this.but.getcode(ctx); // e.g. code=[@src2x::https://cdn.bitbook.education/avatars/cat_70x70@2.jpg][ let [key, url] = this.but.get_url(code); key = key.substring(1); let parent = 'resource'; let child = this.curr_bit_stk.top(); // video, audio, image, image-link etc let obname = child; let type = ''; const bit = this.stk.top().bit; if (!this.resformat.toLowerCase().match(new RegExp(child.toLowerCase()), "i")) { parent = 'parser'; child = 'excessResources'; type = key; } else if (code[1] === '&') { child = this.RESOURCE_MAP['&' + child]; if (!bit[parent]) bit[parent] = {}; if (!bit[parent][child]) bit[parent][child] = []; } else { // Probably @def child = this.curr_bit_stk.top(); if (this.RESOURCE_MAP['&' + child] !== undefined) child = this.RESOURCE_MAP['&' + child]; else child = camelize(child); } let re = /\[@([^:]*)\s*:\s*([^\]]*)\s*\]/g; let vals = this.but.get_bit_value_colonsep(re, code); // @defs == width, height, duration, etc if (vals) { if (key === 'posterImage' && key in bit[parent][child]) { bit[parent][child][key].src = vals[1]; bit[parent][child][key].format = vals[1].split('.').pop(); } else if (child === 'videoLink' && key.startsWith('src')) { key = 'thumbnails'; // objects in array if (!(key in bit[parent][child])) bit[parent][child][key] = []; let thumb = R.clone({ format: '', width: null, height: null }); thumb[vals[0]] = url; thumb.format = url.split('.').pop(); bit[parent][child].thumbnails.push(thumb); } else if (type) { if (!this.stk.top()[parent]) this.stk.top()[parent] = {}; if (!this.stk.top()[parent][child]) this.stk.top()[parent][child] = []; let len = this.stk.top()[parent][child].length; if (len == 0) { this.stk.top()[parent][child].push({ 'type': obname }); len++; } this.stk.top()[parent][child][len - 1][vals[0]] = vals[1]; } else bit[parent][child][vals[0]] = vals[1]; } (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; BitmarkListener.prototype.exitImage_chained4match = function (ctx) { let code = this.but.getcode(ctx); // e.g. code=[@src2x::https://cdn.bitbook.education/avatars/cat_70x70@2.jpg][ let [key, url] = this.but.get_url(code); // bracket_content(code); let lastpair = this.stk.top().bit['pairs'].length - 1; key = key.substring(1); this.stk.top().bit['pairs'][lastpair].keyImage[key] = url; (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; BitmarkListener.prototype.exitImage_chained = function (ctx) { let code = this.but.getcode(ctx); // e.g. code=[@src2x::https://cdn.bitbook.education/avatars/cat_70x70@2.jpg][ let [key, url] = this.but.get_url(code); let parent = 'resource'; //this.curr_bit_stk.top(); let child = this.curr_bit_stk.top().trim(); key = key.substring(1).trim(); url = url.trim(); if (1 < this.curr_bit_stk.size) { let second = this.curr_bit_stk.second(); if (second === 'initiator' || second === 'partner') { parent = second; child = 'avatarImage'; } if (this.stk.top().bit[parent] === undefined) this.stk.top().bit[parent] = {}; if (this.stk.top().bit[parent][child] === undefined) this.stk.top().bit[parent][child] = {}; this.stk.top().bit[parent][child][key] = url; } else { if (!this.stk.top().bit[parent]) this.stk.top().bit[parent] = R.clone(JSON_BIT_TEMPLATES.Imageresource_element); child = camelize(child); // image-link => imageLink this.stk.top().bit[parent][child][key] = url; } (this.stk.top()).bit.body = (this.stk.top()).bit.body.replace(code, ''); }; BitmarkListener.prototype.enterKey_title = function (ctx) { let code = this.but.getcode(ctx); let val = this.but.get_title(code); if (0 < this.stk.size) { this.stk.top().bit.heading['forKeys'] = val; } else log.error("enterKey_title stack is empty"); }; BitmarkListener.prototype.enterValue_title = function (ctx) { let code = this.but.getcode(ctx); let val = this.but.get_title(code); if (0 < this.stk.size) { this.stk.top().bit.heading['forValues'] = val; } else log.error("enterValue_title stack is empty"); }; BitmarkListener.prototype.enterValue_title_multi = function (ctx) { let code = this.but.getcode(ctx); let val = this.but.get_title(code); if (0 < this.stk.size) this.stk.top().bit.heading['forValues'].push(val); else log.error("enterValue_title stack is empty"); }; BitmarkListener.prototype.enterPquery = function (ctx) { let code = this.but.getcode(ctx); code = this.but.remove_tail(code, '\n=+\n?'); if (!code) return; this.curr_bit_stk.push('pquery'); // this is the marker }; // BitmarkListener.prototype.exitPquery = function (ctx) { this.curr_bit_stk.pop(); }; BitmarkListener.prototype.exitPquery__ = function (ctx) { let code = this.but.getcode(ctx); let val = code; let topkey = 'pairs'; let lastpairidx = this.stk.top().bit[topkey].length - 1; if (0 < this.stk.size) { // Check if bracketted string e.g. [%11] is inside ** ** // If true, then we don't remove them as those are not processed. //if (!this.but.is_brackets_inside_stars(val)) { const re = /\[[^\]]+\]/g; ///\[[^\]]+\](.*$)/; val = val.replace(re, '').trim(); // Remove all [] enclosed thingy //} if (!val.startsWith('==')) this.stk.top().bit[topkey][lastpairidx]['key'] += val; (this.stk.top()).bit.body = (this.stk.top()).