bitmark-grammar
Version:
1,435 lines (1,289 loc) • 120 kB
JavaScript
/*
* bitmark-listener.js
*
* last update
* July 25, 2023
*/
import R_clone from 'ramda/es/clone.js';
import R_slice from 'ramda/es/slice.js';
import { Stack } from './stack.mjs';
import { BitUtil } from './bit-utils.mjs';
const { debug } = require('console');
import { JSON_BIT_TEMPLATES } from './bit-template.mjs';
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 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',
];
this.atdef_num = ['focusX', 'focusY', 'numberOfStars',
'jupyter-execution_count', 'jupyter-id', 'reasonableNumOfChars',
'maxCreatedBits'
];
this.atdef_bool = ['aiGenerated'];
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
};
// Exit a parse tree produced by bitmarkParser#cloze.
//BitmarkListener.prototype.exitCloze = function(ctx) {};
// Enter a parse tree produced by bitmarkParser#cloze_instruction_grouped.
BitmarkListener.prototype.enterCloze_instruction_grouped = function (ctx) {
this.push_tmpl(ctx, 'cloze-instruction-grouped');
};
// Exit a parse tree produced by bitmarkParser#cloze_instruction_grouped.
//BitmarkListener.prototype.exitCloze_instruction_grouped = function(ctx) {};
// Enter a parse tree produced by bitmarkParser#cloze_solution_grouped.
BitmarkListener.prototype.enterCloze_solution_grouped = function (ctx) {
this.push_tmpl(ctx, 'cloze-solution-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()).bit.body.replace(val, '');
}
};
//
BitmarkListener.prototype.enterMpquery = function (ctx) {
this.curr_bit_stk.push('mpquery'