diff --git a/.vscode/launch.json b/.vscode/launch.json
index 64570714..bc41173b 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -39,12 +39,12 @@
"internalConsoleOptions": "neverOpen"
},
{
+ "name": "Launch Chromium",
"type": "chrome",
"request": "launch",
- "name": "Launch Chromium Ubuntu (http://localhost:8080)",
- "url": "http://localhost:8080",
+ "cwd": "${workspaceFolder}",
"webRoot": "${workspaceFolder}",
- "runtimeExecutable": "/snap/bin/chromium"
+ "url": "http://localhost:8080"
}
]
}
\ No newline at end of file
diff --git a/i18n/catroid_strings/values-en/strings.xml b/i18n/catroid_strings/values-en/strings.xml
index 8caf0359..adf38b92 100644
--- a/i18n/catroid_strings/values-en/strings.xml
+++ b/i18n/catroid_strings/values-en/strings.xml
@@ -1911,6 +1911,8 @@ needs read and write access to it. You can always change permissions through you
gamepad down pressed
gamepad left pressed
gamepad right pressed
+ stage width
+ stage height
phiro front left sensor
phiro front right sensor
phiro side left sensor
@@ -2176,7 +2178,7 @@ needs read and write access to it. You can always change permissions through you
Delete?
Deleting a variable or list that is still in use can cause
- errors.
+ errors. You can\'t undo this!
Your project gets uploaded to the app\’s sharing site where others
@@ -2268,10 +2270,4 @@ needs read and write access to it. You can always change permissions through you
Undo sort
Sort
checkbox
-
- Cannot undo renaming of variable %1$s. It
- appears another variable has the same name.
- You can no longer restore variable %1$s
-
-
diff --git a/i18n/strings_to_json_mapping.json b/i18n/strings_to_json_mapping.json
index f0facffb..ef779237 100644
--- a/i18n/strings_to_json_mapping.json
+++ b/i18n/strings_to_json_mapping.json
@@ -299,9 +299,9 @@
"LEGOEV3_MOTORSTOP": "${ev3_motor_stop} %1%2",
"LEGOEV3_PLAYTONE": "${ev3_play_tone} ${ev3_tone_duration_for} %1%2 ${second_plural.one} ${nxt_tone_frequency} %3%4 ${nxt_tone_hundred_hz} ${ev3_tone_volume} %5%6 ${ev3_tone_percent}",
"LEGOEV3_SETLED": "${ev3_set_led_status} %1%2",
- "MOTOR_LEFT": "${phiro_motor_left}",
- "MOTOR_RIGHT": "${phiro_motor_right}",
- "MOTOR_BOTH": "${phiro_motor_both}",
+ "PHIRO_MOTOR_LEFT": "${phiro_motor_left}",
+ "PHIRO_MOTOR_RIGHT": "${phiro_motor_right}",
+ "PHIRO_MOTOR_BOTH": "${phiro_motor_both}",
"DRONE_TAKEOFFLAND": "${brick_drone_takeoff_land}",
"DRONE_EMERGENCY": "${brick_drone_emergency}",
"DRONE_MOVEUP": "${brick_drone_move_up} %1%2 ${second_plural.one} ${brick_drone_with.} %3%4 ${ev3_tone_percent} ${brick_drone_power}",
@@ -444,6 +444,12 @@
"EV3_SENSOR_2": "${formula_editor_sensor_lego_ev3_2}",
"EV3_SENSOR_3": "${formula_editor_sensor_lego_ev3_3}",
"EV3_SENSOR_4": "${formula_editor_sensor_lego_ev3_4}",
+ "EV3_MOTOR_A": "${ev3_motor_a}",
+ "EV3_MOTOR_B": "${ev3_motor_b}",
+ "EV3_MOTOR_C": "${ev3_motor_c}",
+ "EV3_MOTOR_D": "${ev3_motor_d}",
+ "EV3_MOTOR_MOTOR_B_C": "${ev3_motor_b_anc_d}",
+ "EV3_MOTOR_ALL_MOTORS": "${ev3_motor_all}",
"COLOR_AT_XY": "${formula_editor_sensor_color_at_x_y}",
"COLOR_TOUCHES_COLOR": "${formula_editor_function_color_touches_color}",
"COLOR_EQUALS_COLOR": "${formula_editor_sensor_color_equals_color}",
@@ -493,14 +499,14 @@
"ALIGNMENTS_0": "${brick_show_variable_aligned_left}",
"ALIGNMENTS_1": "${brick_show_variable_aligned_centered}",
"ALIGNMENTS_2": "${brick_show_variable_aligned_right}",
- "SPINNER_0": "${brick_stop_this_script}",
- "SPINNER_1": "${brick_stop_all_scripts}",
- "SPINNER_2": "${brick_stop_other_scripts}",
+ "STOP_SCRIPT_0": "${brick_stop_this_script}",
+ "STOP_SCRIPT_1": "${brick_stop_all_scripts}",
+ "STOP_SCRIPT_2": "${brick_stop_other_scripts}",
"SPEECH_RECOGNITION_LANGUAGE": "${preference_title_ai_speech_recognition}",
- "CAMSPINNER_0": "${video_brick_camera_off}",
- "CAMSPINNER_1": "${video_brick_camera_on}",
- "CAMCHOOSESPINNER_0": "${choose_camera_back}",
- "CAMCHOOSESPINNER_1": "${choose_camera_front}",
+ "CAMSPINNER_FALSE": "${video_brick_camera_off}",
+ "CAMSPINNER_TRUE": "${video_brick_camera_on}",
+ "CAMCHOOSESPINNER_FALSE": "${choose_camera_back}",
+ "CAMCHOOSESPINNER_TRUE": "${choose_camera_front}",
"FLASHSPINNER_0": "${brick_flash_off}",
"FLASHSPINNER_1": "${brick_flash_on}",
"FADESPINNER_0": "${particle_effects_fade_in}",
@@ -564,27 +570,27 @@
"CLOSE": "${close}",
"SHOW_VARIABLE": "${brick_show_variable}",
"SWITCH_TO_1D": "${switch_to_1d}",
- "GO_TO_TOUCH_POSITION": "${brick_go_to_touch_position}",
- "GO_TO_RANDOM_POSITION": "${brick_go_to_random_position}",
- "MOTOR_A": "${nxt_motor_a}",
- "MOTOR_B": "${nxt_motor_b}",
- "MOTOR_C": "${nxt_motor_c}",
- "MOTOR_B_C": "${nxt_motor_b_and_c}",
- "MOTOR_D": "${ev3_motor_d}",
- "LED_OFF": "${ev3_led_status_off}",
- "LED_GREEN": "${ev3_led_status_green}",
- "LED_RED": "${ev3_led_status_red}",
- "LED_ORANGE": "${ev3_led_status_orange}",
- "LED_GREEN_FLASHING": "${ev3_led_status_green_flashing}",
- "LED_RED_FLASHING": "${ev3_led_status_red_flashing}",
- "LED_ORANGE_FLASHING": "${ev3_led_status_orange_flashing}",
- "LED_GREEN_PULSE": "${ev3_led_status_green_pulse}",
- "LED_RED_PULSE": "${ev3_led_status_red_pulse}",
- "LED_ORANGE_PULSE": "${ev3_led_status_orange_pulse}",
- "DEFAULT": "${sound_default}",
- "MONSTER": "${sound_monster}",
- "INSECT": "${sound_insect}",
- "ROBOT": "${sound_robot}",
+ "GO_TO_POSITION_80": "${brick_go_to_touch_position}",
+ "GO_TO_POSITION_81": "${brick_go_to_random_position}",
+ "NXT_MOTOR_A": "${nxt_motor_a}",
+ "NXT_MOTOR_B": "${nxt_motor_b}",
+ "NXT_MOTOR_C": "${nxt_motor_c}",
+ "NXT_MOTOR_B_C": "${nxt_motor_b_and_c}",
+ "NXT_ALL_MOTORS": "${nxt_motor_all}",
+ "EV3_LED_OFF": "${ev3_led_status_off}",
+ "EV3_LED_GREEN": "${ev3_led_status_green}",
+ "EV3_LED_RED": "${ev3_led_status_red}",
+ "EV3_LED_ORANGE": "${ev3_led_status_orange}",
+ "EV3_LED_GREEN_FLASHING": "${ev3_led_status_green_flashing}",
+ "EV3_LED_RED_FLASHING": "${ev3_led_status_red_flashing}",
+ "EV3_LED_ORANGE_FLASHING": "${ev3_led_status_orange_flashing}",
+ "EV3_LED_GREEN_PULSE": "${ev3_led_status_green_pulse}",
+ "EV3_LED_RED_PULSE": "${ev3_led_status_red_pulse}",
+ "EV3_LED_ORANGE_PULSE": "${ev3_led_status_orange_pulse}",
+ "JUMPING_SUMO_SOUND_DEFAULT": "${sound_default}",
+ "JUMPING_SUMO_SOUND_MONSTER": "${sound_monster}",
+ "JUMPING_SUMO_SOUND_INSECT": "${sound_insect}",
+ "JUMPING_SUMO_SOUND_ROBOT": "${sound_robot}",
"SECONDS_PLURAL": "${second_plural.other}",
"STEPS_PLURAL": "${brick_move_n_step_plural.other}",
"LAYERS_PLURAL": "${brick_go_back_layer_plural.other}",
@@ -610,5 +616,45 @@
"PHIRO_TONE_FA": "${phiro_tone_fa}",
"PHIRO_TONE_SO": "${phiro_tone_so}",
"PHIRO_TONE_LA": "${phiro_tone_la}",
- "PHIRO_TONE_TI": "${phiro_tone_ti}"
+ "PHIRO_TONE_TI": "${phiro_tone_ti}",
+ "INSTRUMENT_ELECTRIC_PIANO": "${electric_piano}",
+ "INSTRUMENT_PIANO": "${piano}",
+ "INSTRUMENT_CELLO": "${cello}",
+ "INSTRUMENT_FLUTE": "${flute}",
+ "INSTRUMENT_VIBRAPHONE": "${vibraphone}",
+ "INSTRUMENT_ORGAN": "${organ}",
+ "INSTRUMENT_GUITAR": "${guitar}",
+ "INSTRUMENT_ELECTRIC_GUITAR": "${electric_guitar}",
+ "INSTRUMENT_BASS": "${bass}",
+ "INSTRUMENT_PIZZICATO": "${pizzicato}",
+ "INSTRUMENT_SYNTH_PAD": "${synth_pad}",
+ "INSTRUMENT_CHOIR": "${choir}",
+ "INSTRUMENT_SYNTH_LEAD": "${synth_lead}",
+ "INSTRUMENT_WOODEN_FLUTE": "${wooden_flute}",
+ "INSTRUMENT_TROMBONE": "${trombone}",
+ "INSTRUMENT_SAXOPHONE": "${saxophone}",
+ "INSTRUMENT_BASSOON": "${bassoon}",
+ "INSTRUMENT_CLARINET": "${clarinet}",
+ "INSTRUMENT_MUSIC_BOX": "${music_box}",
+ "INSTRUMENT_STEEL_DRUM": "${steel_drum}",
+ "INSTRUMENT_MARIMBA": "${marimba}",
+ "DRUM_SNARE_DRUM": "${snare_drum}",
+ "DRUM_BASS_DRUM": "${bass_drum}",
+ "DRUM_SIDE_STICK": "${side_stick}",
+ "DRUM_CRASH_CYMBAL": "${crash_cymbal}",
+ "DRUM_OPEN_HI_HAT": "${open_hi_hat}",
+ "DRUM_CLOSED_HI_HAT": "${closed_hi_hat}",
+ "DRUM_TAMBOURINE": "${tambourine}",
+ "DRUM_HAND_CLAP": "${hand_clap}",
+ "DRUM_CLAVES": "${claves}",
+ "DRUM_WOOD_BLOCK": "${wood_block}",
+ "DRUM_COWBELL": "${cowbell}",
+ "DRUM_TRIANGLE": "${triangle}",
+ "DRUM_BONGO": "${bongo}",
+ "DRUM_CONGA": "${conga}",
+ "DRUM_CABASA": "${cabasa}",
+ "DRUM_GUIRO": "${guiro}",
+ "DRUM_VIBRASLAP": "${vibraslap}",
+ "DRUM_OPEN_CUICA": "${open_cuica}",
+ "WHEN_BOUNCE_OFF_ANYTHING": "${collision_with_anything}"
}
\ No newline at end of file
diff --git a/src/common/js/parser/formula.js b/src/common/js/parser/formula.js
deleted file mode 100644
index fd7887c1..00000000
--- a/src/common/js/parser/formula.js
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * Catblocks formular class for parsing catroid programs
- */
-
-export default class Formula {
- constructor() {
- this.value = '';
- this.operator = '';
- this.left = null;
- this.right = null;
- this.mid = null;
- }
-
- setLeft(leftBlock) {
- if (this.left === null) {
- this.left = leftBlock;
- } else {
- this.left.setLeft(leftBlock);
- }
- }
-
- setRight(rightBlock) {
- if (this.right === null) {
- this.right = rightBlock;
- } else {
- this.right.setRight(rightBlock);
- }
- }
-
- setMid(midBlock) {
- if (this.mid === null) {
- this.mid = midBlock;
- } else {
- this.mid.setMid(midBlock);
- }
- }
-
- static getAllLayouts() {
- return {
- BRACKET: '(%l%r)',
- USER_LIST: '*%v*',
- STRING: "'%v'",
- USER_VARIABLE: '"%v"',
- SIN: '%v(%l)',
- COS: '%v(%l)',
- TAN: '%v(%l)',
- LN: '%v(%l)',
- LOG: '%v(%l)',
- ABS: '%v(%l)',
- ROUND: '%v(%l)',
- ARCSIN: '%v(%l)',
- ARCCOS: '%v(%l)',
- ARCTAN: '%v(%l)',
- FLOOR: '%v(%l)',
- CEIL: '%v(%l)',
- EXP: '%v(%l)',
- SQRT: '%v(%l)',
- MULTI_FINGER_X: '%v(%l)',
- MULTI_FINGER_Y: '%v(%l)',
- MULTI_FINGER_TOUCHED: '%v(%l)',
- TEXT_BLOCK_X: '%v(%l)',
- TEXT_BLOCK_Y: '%v(%l)',
- TEXT_BLOCK_SIZE: '%v(%l)',
- TEXT_BLOCK_LANGUAGE_FROM_CAMERA: '%v(%l)',
- ARDUINOANALOG: '%v(%l)',
- ARDUINODIGITAL: '%v(%l)',
- ARCTAN2: '%v(%l, %r)',
- POWER: '%v(%l, %r)',
- RASPIDIGITAL: '%v(%l)',
- MOD: '%v(%l, %r)',
- RAND: '%v(%l, %r)',
- MAX: '%v(%l, %r)',
- MIN: '%v(%l, %r)',
- IF_THEN_ELSE: '%v(%l,%r,%m)',
- LENGTH: '%v(%l)',
- LETTER: '%v(%l, %r)',
- JOIN: '%v(%l, %r)',
- JOIN3: '%v(%l,%r,%m)',
- FLATTEN: '%v(%l)',
- INDEX_OF_ITEM: '%v(%l, %r)',
- INDEX_CURRENT_TOUCH: '%v(%l)',
- COLOR_AT_XY: '%v(%l, %r)',
- COLOR_TOUCHES_COLOR: '%v(%l, %r)',
- COLOR_EQUALS_COLOR: '%v(%l, %r)',
- COLLIDES_WITH_COLOR: '%v(%l)',
- REGEX: '%v(%l, %r)',
- CONTAINS: '%v(%l, %r)',
- NUMBER_OF_ITEMS: '%v(%l)',
- LIST_ITEM: '%v(%l, %r)',
- DEFAULT: '%l %v %r'
- };
- }
-
- static getOpLayout(op) {
- if (op) {
- const layout = Formula.getAllLayouts()[op];
- if (layout) {
- return layout;
- }
- }
- return Formula.getAllLayouts()['DEFAULT'];
- }
-
- static packValue(layout, key, value) {
- if (['%v', '%l', '%r', '%m'].includes(key)) {
- if (value.length > 0) {
- const result = value.replace(/(\.[0-9]*[1-9])0+$|\.0*$/, '$1');
- return layout.replace(key, `${result}`);
- }
- return layout.replace(key, '');
- }
- return layout;
- }
-
- static packLayout(op, value, left, right, mid) {
- let layout = Formula.getOpLayout(op);
- layout = Formula.packValue(layout, '%v', value);
- layout = Formula.packValue(layout, '%l', left);
- layout = Formula.packValue(layout, '%r', right);
- layout = Formula.packValue(layout, '%m', mid);
- return layout;
- }
-
- static stringify(f) {
- const left = (() => {
- if (f.left) {
- return Formula.stringify(f.left);
- }
- return '';
- })();
- const right = (() => {
- if (f.right) {
- return Formula.stringify(f.right);
- }
- return '';
- })();
- const mid = (() => {
- if (f.mid) {
- return Formula.stringify(f.mid);
- }
- return '';
- })();
-
- const nodeValue = Formula.packLayout(f.operator, f.value, left, right, mid);
- return nodeValue;
- }
-}
diff --git a/src/common/js/parser/parser.js b/src/common/js/parser/parser.js
deleted file mode 100644
index 63ebf196..00000000
--- a/src/common/js/parser/parser.js
+++ /dev/null
@@ -1,1106 +0,0 @@
-import { CatBlocksMsgs } from '../../../library/ts/i18n/CatBlocksMsgs';
-import Formula from './formula';
-
-class Scene {
- constructor(name) {
- this.name = name;
- this.objectList = [];
- }
-}
-
-class Object {
- constructor(name) {
- this.name = name;
- this.lookList = [];
- this.soundList = [];
- this.scriptList = [];
- this.userBricks = null;
- }
-}
-
-class File {
- constructor(name, fileName) {
- this.name = name;
- this.fileName = fileName;
- }
-}
-
-class Script {
- constructor(name, id, posX, posY, commentedOut) {
- this.name = name;
- this.brickList = [];
- this.posX = posX;
- this.posY = posY;
- this.id = id;
- this.commentedOut = commentedOut;
- this.formValues = new Map();
- }
-}
-
-class Brick {
- constructor(name, id) {
- this.name = name;
- if (id) {
- this.id = id;
- }
- this.loopOrIfBrickList = [];
- this.elseBrickList = [];
- this.formValues = new Map();
- this.colorVariation = 0;
- this.userBrickId = undefined;
- this.commentedOut = false;
- this.endBrickList = [];
- }
-}
-
-class UserBrickDefinition {
- constructor(id) {
- this.id = id;
- this.inputTypes = [];
- this.msg = '';
- }
-
- getArgs(fieldNameEqualsContent) {
- const args = [];
- for (let i = 0; i < this.inputTypes.length; ++i) {
- if (this.inputTypes[i].type.toUpperCase() == 'INPUT') {
- args.push({
- type: 'field_catblockstext',
- name: this.inputTypes[i].varName,
- text: fieldNameEqualsContent ? this.inputTypes[i].varName : 'unset'
- });
- args.push({
- type: 'field_image',
- name: `${this.inputTypes[i].varName}_INFO`,
- src: `${document.location.pathname}media/info_icon.svg`,
- height: 24,
- width: 24,
- alt: '(i)',
- flip_rtl: true
- });
- }
- }
- return args;
- }
-
- getJsonDefinition() {
- const args = this.getArgs(false);
- return {
- message0: this.msg,
- args0: args,
- args2: args,
- category: 'user',
- colour: '#3556a2',
- extensions: ['shapeBrick']
- };
- }
-
- getFormValueMapForDefinitionBrick() {
- const args = new Map();
- for (let i = 0; i < this.inputTypes.length; ++i) {
- if (this.inputTypes[i].type.toUpperCase() == 'INPUT') {
- args.set(this.inputTypes[i].varName, this.inputTypes[i].varName);
- }
- }
- return args;
- }
-
- getDefinitionJsonDefinition() {
- const args = this.getArgs(true);
- return {
- message0: this.msg,
- args0: args,
- category: 'user',
- colour: '#3556a2',
- previousStatement: 'userdefinedtemplate',
- nextStatement: 'userdefinedtemplate',
- formValueMap: this.getFormValueMapForDefinitionBrick()
- };
- }
-}
-
-const sceneList = [];
-let xmlDoc = undefined;
-const supportedAppVersion = 0.9994;
-
-// global log enable switch
-const DEBUG = false;
-
-/**
- * Catblocks debug function
- * @param {*} msg
- * @param {*} debug
- */
-const catLog = (msg, debug = DEBUG) => {
- if (debug) {
- console.log(msg);
- }
-};
-
-/**
- * Check if current catroid code version is supported
- * @param {XMLDocument} program to validate
- * @return {boolean} if supported or not
- */
-function isSupported(program) {
- const appVersion = program.getElementsByTagName('catrobatLanguageVersion');
- if (appVersion === undefined || appVersion.length < 1) {
- console.warn('Unsupported program version found, please upgrade programm to newer version via reupload.');
- return false;
- }
- if (appVersion[0].innerHTML < supportedAppVersion) {
- console.warn('Unsupported program version found, please upgrade programm to newer version via reupload.');
- return false;
- }
- return true;
-}
-
-/**
- * Initialize parser for new conversion
- * Clean old parsed values and define xmlDoc for xPath
- * @param {XMLDocument} xml
- */
-function initParser(xml) {
- xmlDoc = xml;
- sceneList.length = 0;
-}
-
-/**
- * Get the xml program as JSON
- * @param {XMLDocument} xml catroid program xml
- * @returns {Object} parsed program
- */
-function getCatroidProgramObject(xml) {
- const scenes = xml.getElementsByTagName('scenes')[0].children;
- for (let i = 0; i < scenes.length; i++) {
- sceneList.push(parseScenes(scenes[i]));
- }
- const name = xml.getElementsByTagName('header')[0].getElementsByTagName('programName')[0].innerHTML;
- return { scenes: sceneList, programName: name };
-}
-/**
- * Flat/dereference xml nodes
- * @param {*} node node
- * @param {*} xml XMLDocument
- */
-function flatReference(node, xml = xmlDoc) {
- const refPath = node.getAttribute('reference');
- if (refPath) {
- return xml.evaluate(refPath, node, null, XPathResult.ANY_TYPE, null).iterateNext();
- }
- return node;
-}
-
-/**
- * Escape not allowed characters in names
- * @param {string} name to escape
- * @returns {string} proper value
- */
-function escapeName(name) {
- return (name || '').replace(/[&]/, '');
-}
-
-function parseScenes(scene) {
- catLog(scene);
-
- const name = escapeName(scene.getElementsByTagName('name')[0].childNodes[0].nodeValue);
- const currentScene = new Scene(name);
- const objectList = scene.getElementsByTagName('objectList')[0].children;
- for (let i = 0; i < objectList.length; i++) {
- currentScene.objectList.push(parseObjects(objectList[i]));
- }
- return currentScene;
-}
-
-function parseObjects(object) {
- object = flatReference(object);
- catLog(object);
-
- const name = escapeName(object.getAttribute('name'));
- if (name !== null) {
- const currentObject = new Object(name);
- const lookList = object.getElementsByTagName('lookList')[0].children;
- const soundList = object.getElementsByTagName('soundList')[0].children;
- const scriptList = object.getElementsByTagName('scriptList')[0].children;
-
- const userDefinedBrickList = object.getElementsByTagName('userDefinedBrickList');
- if (userDefinedBrickList && userDefinedBrickList[0] && userDefinedBrickList[0].children) {
- const userBrickDefinitions = parseUserBrickDefinitions(userDefinedBrickList[0].children);
- currentObject.userBricks = userBrickDefinitions;
- }
-
- for (let i = 0; i < lookList.length; i++) {
- let name = lookList[i].getAttribute('name');
- if (name == null) {
- const xml = lookList[i].getElementsByTagName('name');
- if (xml.length > 0 && xml[0] !== undefined) {
- name = xml[0].textContent;
- }
- }
-
- let fileName = lookList[i].getAttribute('fileName');
- if (fileName == null) {
- const xml = lookList[i].getElementsByTagName('fileName');
- if (xml.length > 0 && xml[0] !== undefined) {
- fileName = xml[0].textContent;
- }
- }
-
- const file = new File(name, fileName);
- currentObject.lookList.push(file);
- }
- for (let i = 0; i < soundList.length; i++) {
- let name = soundList[i].getAttribute('name');
- if (name == null) {
- const xml = soundList[i].getElementsByTagName('name');
- if (xml.length > 0 && xml[0] !== undefined) {
- name = xml[0].textContent;
- }
- }
-
- let fileName = soundList[i].getAttribute('fileName');
- if (fileName == null) {
- const xml = soundList[i].getElementsByTagName('fileName');
- if (xml.length > 0 && xml[0] !== undefined) {
- fileName = xml[0].textContent;
- }
- }
-
- const file = new File(name, fileName);
- currentObject.soundList.push(file);
- }
- for (let i = 0; i < scriptList.length; i++) {
- currentObject.scriptList.push(parseScripts(scriptList[i]));
- }
- return currentObject;
- }
-}
-
-function parseUserBrickDefinitions(userBricks) {
- const userDefinedBrickDefinitions = [];
-
- for (let i = 0; i < userBricks.length; ++i) {
- const brickDefinition = userBricks[i];
- if (!brickDefinition) {
- continue;
- }
-
- //
- let msg = '';
- const inputs = [];
- const brickId = brickDefinition.getElementsByTagName('userDefinedBrickID')[0].innerHTML;
- if (!brickId) {
- continue;
- }
-
- const brickDataDefNode = brickDefinition.getElementsByTagName('userDefinedBrickDataList')[0];
- if (!brickDataDefNode) {
- continue;
- }
- const brickDataDefs = flatReference(brickDataDefNode);
-
- let inputCounter = 1;
- //
- for (let j = 0; j < brickDataDefs.children.length; ++j) {
- const dataDef = brickDataDefs.children[j];
- if (dataDef.nodeName == 'userDefinedBrickLabel') {
- msg += dataDef.getElementsByTagName('label')[0].innerHTML + ' ';
- } else if (dataDef.nodeName == 'userDefinedBrickInput') {
- msg += `%${inputCounter}%${inputCounter + 1} `;
-
- const inputTag = dataDef.getElementsByTagName('input')[0];
- let varName;
- if (inputTag.hasAttribute('reference')) {
- const inputRef = inputTag.getAttribute('reference');
- varName = xmlDoc.evaluate(inputRef, inputTag).iterateNext().getElementsByTagName('input')[0].innerHTML;
- } else {
- varName = inputTag.getElementsByTagName('input')[0].innerHTML;
- }
-
- inputs.push({
- type: dataDef.getElementsByTagName('type')[0].innerHTML,
- varName: varName
- });
- inputCounter += 2;
- } else {
- throw `Unknown user brick data definition: ${dataDef.nodeName}`;
- }
- }
- msg = msg.trimRight();
-
- const userBrick = new UserBrickDefinition(brickId);
- userDefinedBrickDefinitions.push(userBrick);
- userBrick.msg = msg;
- userBrick.inputTypes = inputs;
- }
-
- return userDefinedBrickDefinitions;
-}
-
-function parseScripts(script) {
- catLog(script);
-
- const name = escapeName(script.getAttribute('type'));
- let posX = undefined;
- let posY = undefined;
- if (script.hasAttribute('posX') && script.hasAttribute('posY')) {
- posX = script.getAttribute('posX');
- posY = script.getAttribute('posY');
- }
- const scriptIdTag = script.getElementsByTagName('scriptId');
- let scriptId = undefined;
- if (scriptIdTag.length > 0) {
- scriptId = scriptIdTag[0].innerHTML;
- }
-
- let commentedOut = false;
- const commentedOutTag = script.getElementsByTagName('commentedOut');
- if (commentedOutTag.length > 0) {
- commentedOut = commentedOutTag[0].innerHTML == 'true';
- }
-
- const currentScript = new Script(name, scriptId, posX, posY, commentedOut);
-
- const actionTags = script.getElementsByTagName('action');
- if (actionTags && actionTags.length > 0) {
- currentScript.formValues.set('ACTION', actionTags[0].textContent);
- }
-
- const brickList = script.getElementsByTagName('brickList')[0].children;
- for (let i = 0; i < script.childNodes.length; i++) {
- checkUsage(script.childNodes[i], currentScript);
- }
-
- let positionInScriptBrickList = 0;
- for (let i = 0; i < brickList.length; i++) {
- if (brickList[i].attributes[0].value === 'RepeatBrick' && checkIfNewProgram('RepeatBrick', brickList)) {
- const loopFinished = fillLoopControlBrick(
- brickList,
- currentScript,
- 'RepeatBrick',
- i,
- positionInScriptBrickList,
- null,
- true
- );
- i = loopFinished + 1;
- } else if (
- brickList[i].attributes[0].value === 'IfThenLogicBeginBrick' &&
- checkIfNewProgram('IfThenLogicBeginBrick', brickList)
- ) {
- const ifFinished = fillLoopControlBrick(
- brickList,
- currentScript,
- 'IfThenLogicBeginBrick',
- i,
- positionInScriptBrickList,
- null,
- true
- );
- i = ifFinished + 1;
- } else if (
- brickList[i].attributes[0].value === 'IfLogicBeginBrick' &&
- checkIfNewProgram('IfLogicBeginBrick', brickList)
- ) {
- const ifFinished = fillLoopControlBrick(
- brickList,
- currentScript,
- 'IfLogicBeginBrick',
- i,
- positionInScriptBrickList,
- null,
- true
- );
- i = ifFinished + 1;
- } else {
- currentScript.brickList.push(parseBrick(brickList[i]));
- positionInScriptBrickList++;
- }
- }
- return currentScript;
-}
-
-function checkIfNewProgram(currentBrick, brickList) {
- let endBrick;
- if (currentBrick === 'RepeatBrick') {
- endBrick = 'LoopEndBrick';
- } else if (currentBrick === 'IfLogicBeginBrick') {
- endBrick = 'IfLogicEndBrick';
- } else if (currentBrick === 'IfThenLogicBeginBrick') {
- endBrick = 'IfThenLogicEndBrick';
- }
-
- for (let i = 0; i < brickList.length; i++) {
- if (brickList[i].attributes[0].value === endBrick) {
- return true;
- }
- }
- return false;
-}
-
-function fillLoopControlBrick(
- brickList,
- currentScript,
- currentBrick,
- counter,
- positionInScriptBrickList,
- currentListToFill = null,
- firstCall = false
-) {
- let i = counter;
- let endBrick;
- let elseBrick;
- if (currentBrick === 'RepeatBrick') {
- endBrick = 'LoopEndBrick';
- elseBrick = null;
- } else if (currentBrick === 'IfLogicBeginBrick') {
- endBrick = 'IfLogicEndBrick';
- elseBrick = 'IfLogicElseBrick';
- } else if (currentBrick === 'IfThenLogicBeginBrick') {
- endBrick = 'IfThenLogicEndBrick';
- elseBrick = 'IfThenLogicElseBrick';
- }
- if (firstCall) {
- currentScript.brickList.push(parseBrick(brickList[i]));
- }
-
- positionInScriptBrickList++;
- let position = 0;
- if (positionInScriptBrickList !== 0) {
- position = positionInScriptBrickList - 1;
- }
- i++;
- let list;
- if (currentListToFill === null) {
- list = currentScript.brickList[position];
- } else {
- list = currentListToFill;
- }
-
- let lastIndex = 0;
- let listToFill = null;
-
- while (brickList[i].attributes[0].value !== endBrick) {
- if (brickList[i].attributes[0].value === elseBrick && elseBrick !== null) {
- i++;
- break;
- }
- if (brickList[i].attributes[0].value === 'IfLogicBeginBrick') {
- list.loopOrIfBrickList.push(parseBrick(brickList[i]));
- lastIndex = list.loopOrIfBrickList.length - 1;
- listToFill = list.loopOrIfBrickList[lastIndex];
- const ifFinished = fillLoopControlBrick(
- brickList,
- currentScript,
- 'IfLogicBeginBrick',
- i,
- positionInScriptBrickList,
- listToFill
- );
- i = ifFinished + 1;
- } else if (brickList[i].attributes[0].value === 'IfThenLogicBeginBrick') {
- list.loopOrIfBrickList.push(parseBrick(brickList[i]));
- lastIndex = list.loopOrIfBrickList.length - 1;
- listToFill = list.loopOrIfBrickList[lastIndex];
- const ifFinished = fillLoopControlBrick(
- brickList,
- currentScript,
- 'IfThenLogicBeginBrick',
- i,
- positionInScriptBrickList,
- listToFill
- );
- i = ifFinished + 1;
- } else if (brickList[i].attributes[0].value === 'RepeatBrick') {
- list.loopOrIfBrickList.push(parseBrick(brickList[i]));
- lastIndex = list.loopOrIfBrickList.length - 1;
- listToFill = list.loopOrIfBrickList[lastIndex];
- const loopFinished = fillLoopControlBrick(
- brickList,
- currentScript,
- 'RepeatBrick',
- i,
- positionInScriptBrickList,
- listToFill
- );
- i = loopFinished + 1;
- } else {
- list.loopOrIfBrickList.push(parseBrick(brickList[i]));
- i++;
- }
- }
-
- if (elseBrick !== null) {
- while (brickList[i].attributes[0].value !== endBrick) {
- if (brickList[i].attributes[0].value === 'IfLogicBeginBrick') {
- list.elseBrickList.push(parseBrick(brickList[i]));
- lastIndex = list.elseBrickList.length - 1;
- listToFill = list.elseBrickList[lastIndex];
- const ifFinished = fillLoopControlBrick(
- brickList,
- currentScript,
- 'IfLogicBeginBrick',
- i,
- positionInScriptBrickList,
- listToFill
- );
- i = ifFinished + 1;
- } else if (brickList[i].attributes[0].value === 'IfThenLogicBeginBrick') {
- list.elseBrickList.push(parseBrick(brickList[i]));
- lastIndex = list.elseBrickList.length - 1;
- listToFill = list.elseBrickList[lastIndex];
- const ifFinished = fillLoopControlBrick(
- brickList,
- currentScript,
- 'IfThenLogicBeginBrick',
- i,
- positionInScriptBrickList,
- listToFill
- );
- i = ifFinished + 1;
- } else if (brickList[i].attributes[0].value === 'RepeatBrick') {
- list.elseBrickList.push(parseBrick(brickList[i]));
- lastIndex = list.elseBrickList.length - 1;
- listToFill = list.elseBrickList[lastIndex];
- const loopFinished = fillLoopControlBrick(
- brickList,
- currentScript,
- 'RepeatBrick',
- i,
- positionInScriptBrickList,
- listToFill
- );
- i = loopFinished + 1;
- } else {
- list.elseBrickList.push(parseBrick(brickList[i]));
- i++;
- }
- }
- }
- return i;
-}
-
-function parseBrick(brick) {
- catLog(brick);
-
- const name = (brick.getAttribute('type') || 'emptyBlockName').match(/[a-zA-Z0-9]+/)[0];
-
- let brickId = null;
- const idTag = brick.getElementsByTagName('brickId');
- if (idTag && idTag.length >= 1) {
- brickId = idTag[0].innerHTML;
- if (brickId) {
- brickId = brickId.trim();
- }
- }
-
- const currentBrick = new Brick(name, brickId);
-
- for (let i = 0; i < brick.childNodes.length; i++) {
- checkUsage(brick.childNodes[i], currentBrick);
- }
-
- if (currentBrick.userBrickId) {
- currentBrick.name = currentBrick.userBrickId;
- }
-
- return currentBrick;
-}
-
-/**
- * Return crowdin value or default
- * @param {*} key
- * @param {*} def
- */
-const getMsgValueOrDefault = (key, def = '') => {
- if (key === undefined) {
- return def;
- }
- const msgValue = CatBlocksMsgs.getTranslationForKey(key);
- return msgValue ? msgValue : def;
-};
-
-/**
- * Return node value or default
- * @param {*} node
- * @param {*} def
- */
-const getNodeValueOrDefault = (node, def = '') => {
- if (node === undefined || node.nodeValue === undefined) {
- return def;
- }
- return node.nodeValue;
-};
-
-function checkUsage(list, location) {
- switch (list.nodeName) {
- case 'broadcastMessage':
- case 'spriteToBounceOffName':
- case 'receivedMessage':
- case 'sceneToStart':
- case 'sceneForTransition': {
- location.formValues.set('DROPDOWN', getNodeValueOrDefault(list.childNodes[0]));
- break;
- }
- case 'pointedObject': {
- const brickName = list.getAttribute('name');
- location.formValues.set('DROPDOWN', brickName);
- break;
- }
- case 'eye': {
- const eye = getNodeValueOrDefault(list.childNodes[0]);
- location.formValues.set('eye', getMsgValueOrDefault(`PHIRO_EYE_${eye}`));
- break;
- }
- case 'tone': {
- const tone = getNodeValueOrDefault(list.childNodes[0], 'DO');
- location.formValues.set('tone', getMsgValueOrDefault(`PHIRO_TONE_${tone}`));
- break;
- }
-
- case 'objectToClone': {
- if (list.children[0] != null && list.children[0].children[0]) {
- location.formValues.set('SPINNER', list.children[0].children[0].attributes.name.value);
- } else {
- location.formValues.set('SPINNER', getNodeValueOrDefault(list.childNodes[0]));
- }
- break;
- }
-
- case 'spinnerSelectionFRONT': {
- const key = getNodeValueOrDefault(list.childNodes[0]);
- if (key == 'true') {
- location.formValues.set('SPINNER', getMsgValueOrDefault(`CAMCHOOSESPINNER_1`));
- } else {
- location.formValues.set('SPINNER', getMsgValueOrDefault(`CAMCHOOSESPINNER_0`));
- }
- break;
- }
-
- case 'spinnerSelectionON': {
- const key = getNodeValueOrDefault(list.childNodes[0]);
- if (key == 'true') {
- location.formValues.set('SPINNER', getMsgValueOrDefault(`CAMSPINNER_1`));
- } else {
- location.formValues.set('SPINNER', getMsgValueOrDefault(`CAMSPINNER_0`));
- }
- break;
- }
-
- case 'ledStatus':
- case 'soundName':
- case 'motor': {
- location.formValues.set('DROPDOWN', getMsgValueOrDefault(list.childNodes[0].nodeValue));
- break;
- }
-
- case 'eventValue': {
- const value = getNodeValueOrDefault(list.childNodes[0]);
- if (list.parentElement.getAttribute('type') === 'RaspiInterruptScript') {
- if (value === 'pressed') {
- location.formValues.set('eventValue', getMsgValueOrDefault('RASPI_PRESSED'));
- } else if (value === 'released') {
- location.formValues.set('eventValue', getMsgValueOrDefault('RASPI_RELEASED'));
- }
- } else {
- location.formValues.set('eventValue', getNodeValueOrDefault(value));
- }
- break;
- }
- case 'pin': {
- location.formValues.set('pin', getNodeValueOrDefault(list.childNodes[0]));
- break;
- }
-
- case 'spinnerSelectionID': {
- const brickName = list.parentElement.getAttribute('type');
- const key = getNodeValueOrDefault(list.childNodes[0]);
- if (brickName === 'CameraBrick') {
- location.formValues.set('SPINNER', getMsgValueOrDefault(`CAMSPINNER_${key}`, key));
- } else if (brickName === 'ReadVariableFromFileBrick') {
- location.formValues.set('SPINNER', getMsgValueOrDefault(`READ_VARIABLE_${key}`, key));
- } else {
- location.formValues.set('SPINNER', getMsgValueOrDefault(`FLASHSPINNER_${key}`, key));
- }
- break;
- }
-
- case 'sensorSpinnerPosition': {
- location.formValues.set('DROPDOWN', getMsgValueOrDefault(`SPINNER_PHIRO_${list.childNodes[0].textContent}`));
- break;
- }
-
- case 'nfcTagNdefType': {
- location.formValues.set('DROPDOWN', getMsgValueOrDefault('TNF_' + list.childNodes[0].textContent));
- break;
- }
-
- case 'fadeSpinnerSelectionId': {
- const brickName = list.parentElement.getAttribute('type');
- const key = getNodeValueOrDefault(list.childNodes[0]);
-
- if (brickName === 'FadeParticleEffectBrick') {
- location.formValues.set('brick_fade_particle_effect_spinner', getMsgValueOrDefault(`FADESPINNER_${key}`, key));
- } else if (brickName === 'ParticleEffectAdditivityBrick') {
- location.formValues.set(
- 'brick_additive_particle_effect_spinner',
- getMsgValueOrDefault(`PARTICLESPINNER_${key}`, key)
- );
- }
- break;
- }
-
- case 'type': {
- const key = getNodeValueOrDefault(list.childNodes[0]);
- location.formValues.set('DROPDOWN', getMsgValueOrDefault(`GRAVITY_${key}`, key));
- break;
- }
-
- case 'spinnerSelection': {
- const key = getNodeValueOrDefault(list.childNodes[0]);
- const brickName = list.parentElement.getAttribute('type');
- if (brickName === 'GoToBrick') {
- if (key === '80') {
- location.formValues.set('SPINNER', getMsgValueOrDefault('GO_TO_TOUCH_POSITION', key));
- } else if (key === '81') {
- location.formValues.set('SPINNER', getMsgValueOrDefault('GO_TO_RANDOM_POSITION', key));
- } else if (key === '82') {
- const children = list.parentElement.childNodes;
- for (let j = 0; j < children.length; j++) {
- if (children[j].nodeName === 'destinationSprite') {
- const name = children[j].getAttribute('name');
- location.formValues.set('SPINNER', name);
- break;
- }
- }
- }
- } else {
- location.formValues.set('SPINNER', getMsgValueOrDefault(`SPINNER_${key}`, key));
- }
- break;
- }
-
- case 'alignmentSelection': {
- const key = getNodeValueOrDefault(list.childNodes[0]);
- location.formValues.set('ALIGNMENT', getMsgValueOrDefault(`ALIGNMENTS_${key}`, key));
- break;
- }
-
- case 'ledAnimationName': {
- const key = getNodeValueOrDefault(list.childNodes[0]);
- location.formValues.set('ADRONEANIMATION', getMsgValueOrDefault(key, key));
- break;
- }
-
- case 'animationName': {
- const key = getNodeValueOrDefault(list.childNodes[0]);
- location.formValues.set('ANIMATION', getMsgValueOrDefault(`ANIMATION_${key}`, key));
- break;
- }
-
- case 'selection': {
- const key = getNodeValueOrDefault(list.childNodes[0]);
- location.formValues.set('SPINNER', getMsgValueOrDefault(`POINTTO_${key}`, key));
- break;
- }
-
- case 'formulaMap':
- case 'formulaList': {
- const formulaList = list.children;
- for (let j = 0; j < formulaList.length; j++) {
- const formula = new Formula();
- workFormula(formula, formulaList[j]);
- let attribute;
- if (formulaList[j].hasAttribute('input')) {
- attribute = formulaList[j].getAttribute('input');
- } else {
- attribute = formulaList[j].getAttribute('category');
- }
- location.formValues.set(attribute, Formula.stringify(formula));
- }
- break;
- }
-
- case 'ifBranchBricks':
- case 'loopBricks': {
- const loopOrIfBrickList = list.children;
- for (let j = 0; j < loopOrIfBrickList.length; j++) {
- location.loopOrIfBrickList.push(parseBrick(loopOrIfBrickList[j]));
- }
- break;
- }
-
- case 'elseBranchBricks': {
- const elseBrickList = list.children;
- for (let j = 0; j < elseBrickList.length; j++) {
- location.elseBrickList.push(parseBrick(elseBrickList[j]));
- }
- break;
- }
-
- case 'sound':
- case 'look': {
- const node = flatReference(list);
- const name = node.getAttribute('name');
- location.formValues.set(list.nodeName, name);
- break;
- }
-
- case 'userVariable':
- case 'userList': {
- const node = flatReference(list);
- const nodeName = node.querySelector(`${list.nodeName} name`)
- ? node.querySelector(`${list.nodeName} name`).textContent
- : 'node';
- location.formValues.set('DROPDOWN', nodeName);
- break;
- }
-
- case 'userLists': {
- if (list.parentElement.getAttribute('type') === 'ParameterizedBrick') {
- const lengthOfList = list.children.length;
- let message =
- lengthOfList === 1
- ? getMsgValueOrDefault(`ASSERTION_PARAMETERIZED_LIST_ONE`, 'list')
- : getMsgValueOrDefault(`ASSERTION_PARAMETERIZED_LIST_OTHER`, 'lists');
- message = message.replaceAll('%d', lengthOfList);
- location.formValues.set('CATBLOCKS_ASSERT_LISTS_SELECTED', message);
- }
- break;
- }
-
- case 'userDataList': {
- const userDataList = list.children;
- for (let j = 0; j < userDataList.length; j++) {
- const userDataElement = flatReference(userDataList[j]);
- const userDataCategory = userDataList[j].getAttribute('category');
- let userDataName = null;
- if (userDataElement.getElementsByTagName('name').length != 0) {
- userDataName = userDataElement.getElementsByTagName('name')[0].innerHTML;
- }
- location.formValues.set(userDataCategory, userDataName);
- }
- break;
- }
-
- case 'userDefinedBrickID': {
- location.userBrickId = list.innerHTML;
- break;
- }
-
- case 'commentedOut': {
- location.commentedOut = list.innerHTML == 'true';
- break;
- }
-
- case 'instrumentSelection':
- case 'drumSelection': {
- const key = getNodeValueOrDefault(list.childNodes[0]);
- const value = key.toLowerCase().replaceAll('_', ' ');
- location.formValues.set('DROPDOWN', value);
- break;
- }
-
- case 'endBrick': {
- if (list.parentElement.getAttribute('type') === 'ParameterizedBrick') {
- const children = list.children;
- for (let j = 0; j < children.length; j++) {
- if (children[j].nodeName === 'formulaList') {
- const formulaList = children[j].children;
- for (let j = 0; j < formulaList.length; j++) {
- const formula = new Formula();
- workFormula(formula, formulaList[j]);
- const attribute = formulaList[j].getAttribute('category');
- location.formValues.set(attribute, Formula.stringify(formula));
- }
- } else if (children[j].nodeName === 'userList') {
- const node = flatReference(children[j]);
- const name = node.children[2].innerHTML;
- location.formValues.set('LIST_SELECTED', name);
- }
- }
- }
- break;
- }
-
- case 'screenRefresh': {
- const key = getNodeValueOrDefault(list.childNodes[0]).toUpperCase();
- location.formValues.set('UDB_SCREEN_REFRESH', getMsgValueOrDefault(`UDB_SCREEN_REFRESH_${key}`, key));
- break;
- }
-
- default:
- }
-}
-
-function workFormula(formula, input) {
- for (let i = 0; i < input.childNodes.length; i++) {
- if (input.childNodes[i].nodeName === 'additionalChildren') {
- if (input.childNodes[i].hasChildNodes()) {
- if (input.childNodes[i].childNodes[1].nodeName === 'org.catrobat.catroid.formulaeditor.FormulaElement') {
- const newFormula = new Formula();
- formula.setMid(newFormula);
- workFormula(newFormula, input.childNodes[i].childNodes[1]);
- }
- }
- }
- if (input.childNodes[i].nodeName === 'leftChild') {
- const newFormula = new Formula();
- formula.setLeft(newFormula);
- workFormula(newFormula, input.childNodes[i]);
- }
- if (input.childNodes[i].nodeName === 'rightChild') {
- const newFormula = new Formula();
- formula.setRight(newFormula);
- workFormula(newFormula, input.childNodes[i]);
- }
-
- if (input.childNodes[i].nodeName === 'type') {
- const typeValue = input.childNodes[i].innerHTML;
- if (
- typeValue === 'BRACKET' ||
- typeValue === 'USER_LIST' ||
- typeValue === 'STRING' ||
- typeValue === 'NUMBER' ||
- typeValue === 'USER_VARIABLE' ||
- typeValue === 'USER_DEFINED_BRICK_INPUT'
- ) {
- formula.operator = typeValue;
- }
- }
- if (input.childNodes[i].nodeName === 'value') {
- const operatorKey = getNodeValueOrDefault(input.childNodes[i].childNodes[0]);
- if (
- formula.operator !== 'USER_LIST' &&
- formula.operator !== 'STRING' &&
- formula.operator !== 'NUMBER' &&
- formula.operator !== 'USER_VARIABLE' &&
- formula.optor !== 'USER_DEFINED_BRICK_INPUT'
- ) {
- formula.operator = operatorKey;
- }
- formula.value = getMsgValueOrDefault(operatorKey, operatorKey);
- }
- }
-}
-
-/**
- * Export Parser class
- * Only those methods are visible outside this module
- */
-export class Parser {
- /**
- * For performance reasons only the requested object is parsed.
- * The xml is filtered the the selected object is parsed.
- *
- * @static
- * @param {string} xmlString code.xml as string
- * @param {*} sceneName name of the scene containing the object to render
- * @param {*} objectName name of the object to render
- * @memberof Parser
- */
- static convertObjectToJSON(xmlString, sceneName, objectName) {
- if (typeof xmlString === 'string') {
- try {
- const xml = new window.DOMParser().parseFromString(xmlString.trim(), 'text/xml');
- if (!isSupported(xml)) {
- return undefined;
- }
-
- const xpath = `/program/scenes/scene[name='${sceneName}']/objectList`;
- const xpathResult = xml.evaluate(xpath, xml, null, XPathResult.ANY_TYPE, null);
- if (!xpathResult) {
- return undefined;
- }
-
- const objectListTag = xpathResult.iterateNext();
- if (!objectListTag) {
- return undefined;
- }
-
- let objectTag = null;
-
- const objectTagResult = xml.evaluate('object', objectListTag);
- let currentObjectNode = objectTagResult.iterateNext();
- while (currentObjectNode) {
- const flatObjectNode = flatReference(currentObjectNode, xml);
- if (flatObjectNode.hasAttribute('name')) {
- if (flatObjectNode.getAttribute('name') == objectName) {
- objectTag = flatObjectNode;
- break;
- }
- }
- currentObjectNode = objectTagResult.iterateNext();
- }
-
- if (!objectTag) {
- return undefined;
- }
-
- initParser(xml);
- return parseObjects(objectTag);
- } catch (e) {
- catLog(e);
- console.error(
- `Failed to convert catroid program given as string into a XMLDocument, please verify that the string is a valid program`
- );
- return undefined;
- }
- }
- }
-
- /**
- * Convert given XML to JSON object
- * @static
- * @param {Element} xmlString code.xml file
- * @returns {Object}
- */
- static convertProgramToJSON(xmlString) {
- if (typeof xmlString === 'string') {
- try {
- const xml = new window.DOMParser().parseFromString(xmlString, 'text/xml');
- if (!isSupported(xml)) {
- return undefined;
- }
-
- initParser(xml);
- return getCatroidProgramObject(xml);
- } catch (e) {
- catLog(e);
- console.error(
- `Failed to convert catroid program given as string into a XMLDocument, please verify that the string is a valid program`
- );
- return undefined;
- }
- }
- return getCatroidProgramObject(xmlString);
- }
-
- /**
- * Convert given XML to JSON object and return every error
- * @static
- * @param {XMLDocument | string} xmlString code.xml file
- * @returns {Object}
- */
- static convertProgramToJSONDebug(xmlString) {
- const obj = Parser.convertProgramToJSON(xmlString);
-
- if (obj === undefined) {
- const xml = new window.DOMParser().parseFromString(xmlString, 'text/xml');
-
- const appVersion = xml.getElementsByTagName('catrobatLanguageVersion');
- if (appVersion === undefined || appVersion.length < 1) {
- throw new Error(`Found program version "${appVersion}", minimum supported is ${supportedAppVersion}`);
- } else if (appVersion[0].innerHTML < supportedAppVersion) {
- throw new Error(
- `Found program version ${appVersion[0].innerHTML}, minimum supported is ${supportedAppVersion}`
- );
- }
-
- initParser(xml);
- return getCatroidProgramObject(xml);
- }
-
- return obj;
- }
-}
diff --git a/src/common/ts/parser/CatblocksBrick.ts b/src/common/ts/parser/CatblocksBrick.ts
new file mode 100644
index 00000000..b3aff394
--- /dev/null
+++ b/src/common/ts/parser/CatblocksBrick.ts
@@ -0,0 +1,15 @@
+export class CatblocksBrick {
+ name: string;
+ id: string | null = null;
+ loopOrIfBrickList: CatblocksBrick[] = new Array();
+ elseBrickList: CatblocksBrick[] = new Array();
+ colorVariation = 0;
+ commentedOut = false;
+ userDefinedBrickID: string | null = null;
+ formValues: Map = new Map();
+
+ constructor(name: string, id: string | null) {
+ this.name = name;
+ this.id = id;
+ }
+}
diff --git a/src/common/ts/parser/CatblocksFile.ts b/src/common/ts/parser/CatblocksFile.ts
new file mode 100644
index 00000000..ca6be86c
--- /dev/null
+++ b/src/common/ts/parser/CatblocksFile.ts
@@ -0,0 +1,9 @@
+export class CatblocksFile {
+ name: string;
+ path: string;
+
+ constructor(name: string, path: string) {
+ this.name = name;
+ this.path = path;
+ }
+}
diff --git a/src/common/ts/parser/CatblocksObject.ts b/src/common/ts/parser/CatblocksObject.ts
new file mode 100644
index 00000000..fc1e3f08
--- /dev/null
+++ b/src/common/ts/parser/CatblocksObject.ts
@@ -0,0 +1,15 @@
+import { CatblocksFile } from './CatblocksFile';
+import { CatblocksScript } from './CatblocksScript';
+import { UserDefinedBrickDefinition } from './UserDefinedBrick';
+
+export class CatblocksObject {
+ name: string;
+ scriptList: CatblocksScript[] = new Array();
+ lookList: CatblocksFile[] = new Array();
+ soundList: CatblocksFile[] = new Array();
+ userDefinedBricks: UserDefinedBrickDefinition[] = new Array();
+
+ constructor(name: string) {
+ this.name = name;
+ }
+}
diff --git a/src/common/ts/parser/CatblocksProject.ts b/src/common/ts/parser/CatblocksProject.ts
new file mode 100644
index 00000000..dcd20d49
--- /dev/null
+++ b/src/common/ts/parser/CatblocksProject.ts
@@ -0,0 +1,10 @@
+import { CatblocksScene } from './CatblocksScene';
+
+export class CatblocksProject {
+ scenes: CatblocksScene[] = new Array();
+ programName = '';
+
+ constructor(programName: string) {
+ this.programName = programName;
+ }
+}
diff --git a/src/common/ts/parser/CatblocksScene.ts b/src/common/ts/parser/CatblocksScene.ts
new file mode 100644
index 00000000..84328cba
--- /dev/null
+++ b/src/common/ts/parser/CatblocksScene.ts
@@ -0,0 +1,10 @@
+import { CatblocksObject } from './CatblocksObject';
+
+export class CatblocksScene {
+ name: string;
+ objectList: CatblocksObject[] = new Array();
+
+ constructor(name: string) {
+ this.name = name;
+ }
+}
diff --git a/src/common/ts/parser/CatblocksScript.ts b/src/common/ts/parser/CatblocksScript.ts
new file mode 100644
index 00000000..fb3b1e4b
--- /dev/null
+++ b/src/common/ts/parser/CatblocksScript.ts
@@ -0,0 +1,11 @@
+import { CatblocksBrick } from './CatblocksBrick';
+
+export class CatblocksScript extends CatblocksBrick {
+ posX: number | null = null;
+ posY: number | null = null;
+ brickList: CatblocksBrick[] = new Array();
+
+ constructor(brickType: string, scriptId: string | null) {
+ super(brickType, scriptId);
+ }
+}
diff --git a/src/common/ts/parser/CatblocksSpinnerProperties.ts b/src/common/ts/parser/CatblocksSpinnerProperties.ts
new file mode 100644
index 00000000..faf48453
--- /dev/null
+++ b/src/common/ts/parser/CatblocksSpinnerProperties.ts
@@ -0,0 +1,11 @@
+export class CatblocksSpinnerProperties {
+ name: string;
+ valueXPath: string[];
+ messageFormat: string;
+
+ constructor(name: string, valueXPath: string[], messageFormat: string) {
+ this.name = name;
+ this.valueXPath = valueXPath;
+ this.messageFormat = messageFormat;
+ }
+}
diff --git a/src/common/ts/parser/Formula.ts b/src/common/ts/parser/Formula.ts
new file mode 100644
index 00000000..036cbacb
--- /dev/null
+++ b/src/common/ts/parser/Formula.ts
@@ -0,0 +1,205 @@
+import { ParserUtils } from './ParserUtils';
+
+export abstract class Formula {
+ protected static simpleFormulaTypes: readonly string[] = [
+ 'NUMBER',
+ 'STRING',
+ 'SENSOR',
+ 'USER_LIST',
+ 'USER_VARIABLE',
+ 'USER_DEFINED_BRICK_INPUT'
+ ];
+
+ protected type: string;
+ protected value: string;
+
+ constructor(type: string, value: string) {
+ this.type = type;
+ this.value = value;
+ }
+
+ public static parse(formulaNode: Element, codeXml: Document): Formula {
+ let value = '';
+ const valueNode = ParserUtils.getElementByXPath(codeXml, 'value', formulaNode);
+ if (valueNode) {
+ value = valueNode.textContent ?? '';
+ }
+
+ const typeNode = ParserUtils.getElementByXPath(codeXml, 'type', formulaNode);
+ if (typeNode) {
+ const strType = ParserUtils.trimTextContent(typeNode.textContent);
+ if (Formula.simpleFormulaTypes.includes(strType)) {
+ return SimpleFormula.parseSimpleFormula(strType, value);
+ } else {
+ return ComplexFormula.parseComplexFormula(value, strType, formulaNode, codeXml);
+ }
+ }
+ throw new Error('No valid forumla type found.');
+ }
+
+ public abstract toFormulaString(): string;
+}
+
+class SimpleFormula extends Formula {
+ private static readonly defaultSimpleFormulaFormat: string = '%v';
+ private static readonly simpleFormulaFormats: Map = new Map([
+ ['USER_LIST', '*%v*'],
+ ['STRING', "'%v'"],
+ ['USER_VARIABLE', '"%v"'],
+ ['USER_DEFINED_BRICK_INPUT', '[%v]']
+ ]);
+
+ constructor(type: string, value: string) {
+ super(type, value);
+ }
+
+ public static parseSimpleFormula(type: string, value: string): SimpleFormula {
+ return new SimpleFormula(type, value);
+ }
+
+ public override toFormulaString(): string {
+ const formulaFormat: string =
+ SimpleFormula.simpleFormulaFormats.get(this.type) ?? SimpleFormula.defaultSimpleFormulaFormat;
+ const messageValue = ParserUtils.getMessage(this.value, this.value);
+ return formulaFormat.replace('%v', messageValue);
+ }
+}
+
+class ComplexFormula extends Formula {
+ private static readonly defaultComplexFormulaFormat: string = '%l %v %r';
+ private static readonly complexFormulaFormats: Map = new Map([
+ ['BRACKET', '(%l%r)'],
+ ['SIN', '%v(%l)'],
+ ['COS', '%v(%l)'],
+ ['TAN', '%v(%l)'],
+ ['LN', '%v(%l)'],
+ ['LOG', '%v(%l)'],
+ ['ABS', '%v(%l)'],
+ ['ROUND', '%v(%l)'],
+ ['ARCSIN', '%v(%l)'],
+ ['ARCCOS', '%v(%l)'],
+ ['ARCTAN', '%v(%l)'],
+ ['FLOOR', '%v(%l)'],
+ ['CEIL', '%v(%l)'],
+ ['EXP', '%v(%l)'],
+ ['SQRT', '%v(%l)'],
+ ['MULTI_FINGER_X', '%v(%l)'],
+ ['MULTI_FINGER_Y', '%v(%l)'],
+ ['MULTI_FINGER_TOUCHED', '%v(%l)'],
+ ['TEXT_BLOCK_X', '%v(%l)'],
+ ['TEXT_BLOCK_Y', '%v(%l)'],
+ ['TEXT_BLOCK_SIZE', '%v(%l)'],
+ ['TEXT_BLOCK_LANGUAGE_FROM_CAMERA', '%v(%l)'],
+ ['ARDUINOANALOG', '%v(%l)'],
+ ['ARDUINODIGITAL', '%v(%l)'],
+ ['ARCTAN2', '%v(%l, %r)'],
+ ['POWER', '%v(%l, %r)'],
+ ['RASPIDIGITAL', '%v(%l)'],
+ ['MOD', '%v(%l, %r)'],
+ ['RAND', '%v(%l, %r)'],
+ ['MAX', '%v(%l, %r)'],
+ ['MIN', '%v(%l, %r)'],
+ ['IF_THEN_ELSE', '%v(%l, %r, %m)'],
+ ['LENGTH', '%v(%l)'],
+ ['LETTER', '%v(%l, %r)'],
+ ['JOIN', '%v(%l, %r)'],
+ ['JOIN3', '%v(%l, %r, %m)'],
+ ['FLATTEN', '%v(%l)'],
+ ['INDEX_OF_ITEM', '%v(%l, %r)'],
+ ['INDEX_CURRENT_TOUCH', '%v(%l)'],
+ ['COLOR_AT_XY', '%v(%l, %r)'],
+ ['COLOR_TOUCHES_COLOR', '%v(%l, %r)'],
+ ['COLOR_EQUALS_COLOR', '%v(%l, %r)'],
+ ['COLLIDES_WITH_COLOR', '%v(%l)'],
+ ['REGEX', '%v(%l, %r)'],
+ ['CONTAINS', '%v(%l, %r)'],
+ ['NUMBER_OF_ITEMS', '%v(%l)'],
+ ['LIST_ITEM', '%v(%l, %r)']
+ ]);
+
+ leftChild: Formula | null = null;
+ rightChild: Formula | null = null;
+ middleChild: Formula | null = null;
+
+ constructor(
+ type: string,
+ value: string,
+ leftChild: Formula | null,
+ rightChild: Formula | null = null,
+ middleChild: Formula | null = null
+ ) {
+ super(type, value);
+ this.leftChild = leftChild;
+ this.rightChild = rightChild;
+ this.middleChild = middleChild;
+ }
+
+ public static parseComplexFormula(
+ type: string,
+ value: string,
+ formulaNode: Element,
+ codeXml: Document
+ ): ComplexFormula {
+ const leftChildNode = ParserUtils.getElementByXPath(codeXml, 'leftChild', formulaNode);
+ const rightChildNode = ParserUtils.getElementByXPath(codeXml, 'rightChild', formulaNode);
+ const additionalChildrenNode = ParserUtils.getElementByXPath(codeXml, 'additionalChildren', formulaNode);
+
+ let leftFormula: Formula | null = null;
+ let rightFormula: Formula | null = null;
+ let middleFormula: Formula | null = null;
+
+ if (leftChildNode) {
+ leftFormula = Formula.parse(leftChildNode, codeXml);
+ }
+ if (rightChildNode) {
+ rightFormula = Formula.parse(rightChildNode, codeXml);
+ }
+ if (additionalChildrenNode) {
+ const additionalChild = ParserUtils.getElementByXPath(
+ codeXml,
+ 'org.catrobat.catroid.formulaeditor.FormulaElement',
+ additionalChildrenNode
+ );
+ if (additionalChild) {
+ middleFormula = Formula.parse(additionalChild, codeXml);
+ }
+ }
+ return new ComplexFormula(value, type, leftFormula, rightFormula, middleFormula);
+ }
+
+ public override toFormulaString(): string {
+ const valueString: string = ParserUtils.getMessage(this.value, this.value);
+
+ let leftString = '';
+ let middleString = '';
+ let rightString = '';
+ if (this.leftChild) {
+ leftString = this.leftChild.toFormulaString();
+ }
+ if (this.rightChild) {
+ rightString = this.rightChild.toFormulaString();
+ }
+ if (this.middleChild) {
+ middleString = this.middleChild.toFormulaString();
+ }
+
+ let formulaFormat = ComplexFormula.complexFormulaFormats.get(this.type);
+ if (!formulaFormat) {
+ formulaFormat =
+ ComplexFormula.complexFormulaFormats.get(this.value) ?? ComplexFormula.defaultComplexFormulaFormat;
+ }
+ if (formulaFormat === ComplexFormula.defaultComplexFormulaFormat) {
+ if (!leftString) {
+ formulaFormat = formulaFormat.replace('%l ', '');
+ }
+ if (!rightString) {
+ formulaFormat = formulaFormat.replace(' %r', '');
+ }
+ }
+ return formulaFormat
+ .replace('%v', valueString)
+ .replace('%l', leftString)
+ .replace('%r', rightString)
+ .replace('%m', middleString);
+ }
+}
diff --git a/src/common/ts/parser/Parser.ts b/src/common/ts/parser/Parser.ts
new file mode 100644
index 00000000..e4d682c4
--- /dev/null
+++ b/src/common/ts/parser/Parser.ts
@@ -0,0 +1,536 @@
+import { ParserUtils } from './ParserUtils';
+import { Formula } from './Formula';
+import { getBrickSpinnerProperties } from '../../../library/js/blocks/bricks';
+import { CatblocksBrick } from './CatblocksBrick';
+import { CatblocksFile } from './CatblocksFile';
+import { CatblocksObject } from './CatblocksObject';
+import { CatblocksProject } from './CatblocksProject';
+import { CatblocksScene } from './CatblocksScene';
+import { CatblocksScript } from './CatblocksScript';
+import { UserDefinedBrickDefinition } from './UserDefinedBrick';
+import { UserDefinedBrickInputType } from './UserDefinedBrickInputType';
+import { CatblocksSpinnerProperties } from './CatblocksSpinnerProperties';
+
+export class CatblocksParser {
+ private strCodeXml: string;
+ private codeXml: Document;
+
+ private isOldMultiPartBrickFormat = false;
+
+ constructor(strCodeXml = '') {
+ this.strCodeXml = strCodeXml;
+ this.codeXml = new Document();
+ if (this.strCodeXml) {
+ this.parseXmlString(strCodeXml);
+ }
+ }
+
+ public parseXmlString(strCodeXml: string): void {
+ this.strCodeXml = strCodeXml;
+ if (this.strCodeXml) {
+ const parser = new DOMParser();
+ this.codeXml = parser.parseFromString(strCodeXml, 'application/xml');
+ }
+ }
+
+ public xmlToCatblocksProject(): CatblocksProject {
+ if (!this.strCodeXml) {
+ throw new Error('Please initialize the program xml first!');
+ }
+
+ this.assureProgramVersionSupported();
+
+ const programNameNode = ParserUtils.getElementByXPath(this.codeXml, '/program/header/programName', this.codeXml);
+ if (!programNameNode) {
+ throw new Error('Program name not found');
+ }
+
+ const programName = programNameNode.textContent ?? '';
+ const catblocksProject = new CatblocksProject(programName);
+
+ ParserUtils.foreachElementByXPath(this.codeXml, '/program/scenes/scene', this.codeXml, (sceneNode: Element) => {
+ const scene = this.parseScene(sceneNode);
+ if (scene) {
+ catblocksProject.scenes.push(scene);
+ }
+ });
+
+ return catblocksProject;
+ }
+
+ public xmlToCatblocksObject(sceneNameToParse: string, objectNameToParse: string): CatblocksObject | null {
+ if (!this.strCodeXml) {
+ throw new Error('Please initialize the program xml first!');
+ }
+ this.assureProgramVersionSupported();
+
+ let parsedObject: CatblocksObject | null = null;
+ ParserUtils.foreachElementByXPath(this.codeXml, '/program/scenes/scene', this.codeXml, (sceneNode: Element) => {
+ const sceneName = ParserUtils.getElementByXPath(this.codeXml, 'name', sceneNode)?.textContent ?? '';
+ if (sceneName === sceneNameToParse) {
+ ParserUtils.foreachElementByXPath(this.codeXml, 'objectList/object', sceneNode, (objectNode: Element) => {
+ const objectName = ParserUtils.getAttrByXPath(this.codeXml, '@name', objectNode)?.textContent;
+ if (objectName === objectNameToParse) {
+ parsedObject = this.parseObject(objectNode);
+ return;
+ }
+ });
+ if (parsedObject) {
+ return;
+ }
+ }
+ });
+ if (!parsedObject) {
+ throw new Error(`The object ${objectNameToParse} was not found in scene ${sceneNameToParse}`);
+ }
+ return parsedObject;
+ }
+
+ private assureProgramVersionSupported() {
+ const programVersionNode = ParserUtils.getElementByXPath(
+ this.codeXml,
+ '/program/header/catrobatLanguageVersion',
+ this.codeXml
+ );
+ if (!programVersionNode) {
+ throw new Error('Program version not found');
+ }
+ const strProgramVersion = ParserUtils.trimTextContent(programVersionNode.textContent);
+ if (!strProgramVersion) {
+ throw new Error('Program version is empty');
+ }
+ const programVersion = parseFloat(strProgramVersion);
+ if (programVersion < 0.9994) {
+ throw new Error(`Found program version ${programVersion}, minimum supported is 0.9994`);
+ }
+ }
+
+ private parseScene(sceneNode: Element): CatblocksScene | null {
+ const sceneName = ParserUtils.getElementByXPath(this.codeXml, 'name', sceneNode)?.textContent ?? '';
+ if (!sceneName) {
+ return null;
+ }
+ const scene = new CatblocksScene(sceneName);
+
+ ParserUtils.foreachElementByXPath(this.codeXml, 'objectList/object', sceneNode, (objectNode: Element) => {
+ const object = this.parseObject(objectNode);
+ if (object) {
+ scene.objectList.push(object);
+ }
+ });
+
+ return scene;
+ }
+
+ private parseObject(objectNode: Element): CatblocksObject | null {
+ const objectName = objectNode.getAttribute('name');
+ if (!objectName) {
+ return null;
+ }
+ const object = new CatblocksObject(objectName);
+
+ ParserUtils.foreachElementByXPath(this.codeXml, 'lookList/look', objectNode, (lookNode: Element) => {
+ const look = this.parseCatblocksFile(lookNode);
+ if (look) {
+ object.lookList.push(look);
+ }
+ });
+
+ ParserUtils.foreachElementByXPath(this.codeXml, 'soundList/sound', objectNode, (soundNode: Element) => {
+ const sound = this.parseCatblocksFile(soundNode);
+ if (sound) {
+ object.soundList.push(sound);
+ }
+ });
+
+ ParserUtils.foreachElementByXPath(
+ this.codeXml,
+ 'userDefinedBrickList/brick',
+ objectNode,
+ (userBrickNode: Element) => {
+ const userDefinedBrick = this.parseUserDefinedBrickDefinitions(userBrickNode);
+ if (userDefinedBrick) {
+ object.userDefinedBricks.push(userDefinedBrick);
+ }
+ }
+ );
+
+ ParserUtils.foreachElementByXPath(this.codeXml, 'scriptList/script', objectNode, (scriptNode: Element) => {
+ const script = this.parseScript(scriptNode);
+ if (!script) {
+ return;
+ }
+ object.scriptList.push(script);
+ });
+
+ return object;
+ }
+
+ private parseCatblocksFile(fileNode: Element): CatblocksFile | null {
+ const filename = fileNode.getAttribute('fileName');
+ const name = fileNode.getAttribute('name');
+ if (name && filename) {
+ return new CatblocksFile(name, filename);
+ }
+ return null;
+ }
+
+ private parseUserDefinedBrickDefinitions(brickElement: Element): UserDefinedBrickDefinition | null {
+ const userDefinedBrickID = ParserUtils.trimTextContent(
+ ParserUtils.getElementByXPath(this.codeXml, 'userDefinedBrickID', brickElement)?.textContent
+ );
+ if (!userDefinedBrickID) {
+ return null;
+ }
+ const udbDefinition = new UserDefinedBrickDefinition(userDefinedBrickID);
+
+ const userBrickDataListNode = ParserUtils.getElementByXPath(this.codeXml, 'userDefinedBrickDataList', brickElement);
+ if (!userBrickDataListNode) {
+ return null;
+ }
+
+ let inputCounter = 1;
+ if (!userBrickDataListNode?.children) {
+ return null;
+ }
+ for (const childNode of userBrickDataListNode.children) {
+ const dataNode = ParserUtils.flattenElementReference(this.codeXml, childNode);
+ const dataType = ParserUtils.trimTextContent(
+ ParserUtils.getElementByXPath(this.codeXml, 'type', dataNode)?.textContent
+ );
+ if (dataType?.toUpperCase() === 'LABEL') {
+ const labelText = ParserUtils.trimTextContent(
+ ParserUtils.getElementByXPath(this.codeXml, 'label', dataNode)?.textContent
+ );
+ udbDefinition.message += `${labelText} `;
+ } else if (dataType?.toUpperCase() === 'INPUT') {
+ const inputName = ParserUtils.trimTextContent(
+ ParserUtils.getElementByXPath(this.codeXml, 'input/input', dataNode)?.textContent
+ );
+ if (inputName) {
+ udbDefinition.inputTypes.push(new UserDefinedBrickInputType(dataType, inputName));
+ udbDefinition.message += `%${inputCounter++}%${inputCounter++} `;
+ }
+ }
+ }
+ udbDefinition.message = udbDefinition.message.trim();
+
+ return udbDefinition;
+ }
+
+ private parseScript(scriptNode: Element): CatblocksScript | null {
+ const scriptType = scriptNode.getAttribute('type');
+ const strScriptPosX = scriptNode.getAttribute('posX');
+ const strScriptPosY = scriptNode.getAttribute('posY');
+
+ if (!scriptType) {
+ return null;
+ }
+
+ const scriptIdNode = ParserUtils.getElementByXPath(this.codeXml, 'scriptId', scriptNode);
+ let scriptId: string | null = null;
+ if (scriptIdNode) {
+ scriptId = ParserUtils.trimTextContent(scriptIdNode.textContent);
+ }
+
+ const script = new CatblocksScript(scriptType, scriptId);
+ if (strScriptPosX && strScriptPosY) {
+ script.posX = parseFloat(strScriptPosX);
+ script.posY = parseFloat(strScriptPosY);
+ }
+ script.commentedOut = false;
+ const commentedOutNode = ParserUtils.getElementByXPath(this.codeXml, 'commentedOut', scriptNode);
+ if (commentedOutNode) {
+ if (ParserUtils.trimTextContent(commentedOutNode.textContent).toLowerCase() === 'true') {
+ script.commentedOut = true;
+ }
+ }
+
+ const userDefinedBrickIDNode = ParserUtils.getElementByXPath(this.codeXml, 'userDefinedBrickID', scriptNode);
+ if (userDefinedBrickIDNode) {
+ script.userDefinedBrickID = ParserUtils.trimTextContent(userDefinedBrickIDNode.textContent);
+ }
+
+ this.parseFormulas(scriptNode, script);
+ this.parseSpinners(scriptNode, script);
+
+ ParserUtils.foreachElementByXPath(this.codeXml, 'brickList/brick', scriptNode, (brickNode: Element) => {
+ const brick = this.parseBrick(brickNode);
+ if (brick) {
+ script.brickList.push(brick);
+ }
+ });
+
+ this.fixBrickHierarchy(script);
+
+ return script;
+ }
+
+ private fixBrickHierarchy(script: CatblocksScript) {
+ if (!this.isOldMultiPartBrickFormat) {
+ return;
+ }
+
+ const mainBricks: Array = [];
+ const brickStack: Array> = [];
+
+ for (let i = 0; i < script.brickList.length; i++) {
+ const brick = script.brickList[i];
+ if (
+ brick.name === 'RepeatBrick' ||
+ brick.name === 'IfLogicBeginBrick' ||
+ brick.name === 'IfThenLogicBeginBrick'
+ ) {
+ if (mainBricks.length > 0) {
+ script.brickList.splice(i, 1);
+ i--;
+ }
+ mainBricks.push(brick);
+ brickStack.push([]);
+ continue;
+ }
+
+ if (brick.name === 'LoopEndBrick' || brick.name === 'IfLogicElseBrick' || brick.name === 'IfThenLogicElseBrick') {
+ const topBrick = mainBricks.pop();
+ if (topBrick) {
+ topBrick.loopOrIfBrickList = brickStack.pop() || [];
+ if (brick.name === 'IfLogicElseBrick' || brick.name === 'IfThenLogicElseBrick') {
+ mainBricks.push(topBrick);
+ brickStack.push([]);
+ } else {
+ const newParent = brickStack.pop();
+ if (newParent) {
+ newParent.push(topBrick);
+ brickStack.push(newParent);
+ }
+ }
+ }
+ script.brickList.splice(i, 1);
+ i--;
+ } else if (brick.name === 'IfLogicEndBrick' || brick.name === 'IfThenLogicEndBrick') {
+ const topBrick = mainBricks.pop();
+ if (topBrick) {
+ topBrick.elseBrickList = brickStack.pop() || [];
+ const newParent = brickStack.pop();
+ if (newParent) {
+ newParent.push(topBrick);
+ brickStack.push(newParent);
+ }
+ }
+ script.brickList.splice(i, 1);
+ i--;
+ } else {
+ const currentStack = brickStack.pop();
+ if (currentStack) {
+ brickStack.push(currentStack);
+ currentStack.push(script.brickList[i]);
+ script.brickList.splice(i, 1);
+ i--;
+ }
+ }
+ }
+ }
+
+ private parseBrick(brickNode: Element): CatblocksBrick | null {
+ let brickType = brickNode.getAttribute('type');
+ if (!brickType) {
+ return null;
+ }
+
+ if (brickType === 'LoopEndBrick' || brickType === 'IfLogicEndBrick' || brickType === 'IfThenLogicEndBrick') {
+ this.isOldMultiPartBrickFormat = true;
+ }
+
+ let brickId: string | null = null;
+ const brickIdNode = ParserUtils.getElementByXPath(this.codeXml, 'brickId', brickNode);
+ if (brickIdNode) {
+ brickId = ParserUtils.trimTextContent(brickIdNode.textContent);
+ }
+
+ let userDefinedBrickID: string | null = null;
+ if (brickType === 'UserDefinedBrick') {
+ const userBrickIdNode = ParserUtils.getElementByXPath(this.codeXml, 'userDefinedBrickID', brickNode);
+ if (userBrickIdNode) {
+ userDefinedBrickID = ParserUtils.trimTextContent(userBrickIdNode.textContent);
+ brickType = userDefinedBrickID;
+ } else {
+ throw new Error('UserDefinedBrick brick without userDefinedBrickID');
+ }
+ }
+
+ const brick = new CatblocksBrick(brickType, brickId);
+ brick.userDefinedBrickID = userDefinedBrickID;
+
+ brick.commentedOut = false;
+ const commentedOutNode = ParserUtils.getElementByXPath(this.codeXml, 'commentedOut', brickNode);
+ if (commentedOutNode) {
+ if (ParserUtils.trimTextContent(commentedOutNode.textContent).toLowerCase() === 'true') {
+ brick.commentedOut = true;
+ }
+ }
+
+ this.parseFormulas(brickNode, brick);
+ this.parseSpinners(brickNode, brick);
+
+ ParserUtils.foreachElementByXPath(
+ this.codeXml,
+ '*[self::ifBranchBricks or self::loopBricks]/brick',
+ brickNode,
+ (subBrickNode: Element) => {
+ const subBricks = this.parseBrick(subBrickNode);
+ if (subBricks) {
+ brick.loopOrIfBrickList.push(subBricks);
+ }
+ }
+ );
+
+ ParserUtils.foreachElementByXPath(
+ this.codeXml,
+ '*[self::elseBranchBricks]/brick',
+ brickNode,
+ (subBrickNode: Element) => {
+ const subBricks = this.parseBrick(subBrickNode);
+ if (subBricks) {
+ brick.elseBrickList.push(subBricks);
+ }
+ }
+ );
+ return brick;
+ }
+
+ private parseFormulas(brickNode: Element, brick: CatblocksBrick): void {
+ ParserUtils.foreachElementByXPath(
+ this.codeXml,
+ '*[self::formulaList or self::formulaMap]/formula',
+ brickNode,
+ (formulaNode: Element) => {
+ this.parseSingleFormula(formulaNode, brick);
+ }
+ );
+
+ this.parseSpecialFormulas(brickNode, brick);
+ }
+
+ private parseSpecialFormulas(brickNode: Element, brick: CatblocksBrick): void {
+ if (brick.name === 'ParameterizedBrick') {
+ const formulaList = ParserUtils.getElementByXPath(this.codeXml, 'endBrick/formulaList', brickNode);
+ if (formulaList) {
+ const formulaNode = ParserUtils.getElementByXPath(this.codeXml, 'formula', formulaList);
+ if (formulaNode) {
+ this.parseSingleFormula(formulaNode, brick);
+ }
+ }
+
+ let userListCounter = 0;
+ ParserUtils.foreachElementByXPath(this.codeXml, 'userLists/userList', brickNode, (formulaNode: Element) => {
+ if (formulaNode) {
+ userListCounter++;
+ }
+ });
+
+ let message =
+ userListCounter === 1
+ ? ParserUtils.getMessage(`ASSERTION_PARAMETERIZED_LIST_ONE`, 'list')
+ : ParserUtils.getMessage(`ASSERTION_PARAMETERIZED_LIST_OTHER`, 'lists');
+ message = message.replace('%d', userListCounter.toString());
+ brick.formValues.set('CATBLOCKS_ASSERT_LISTS_SELECTED', message);
+ }
+ }
+
+ private parseSingleFormula(formulaNode: Element, brick: CatblocksBrick): void {
+ const formulaCategoryAttr = ParserUtils.getAttrByXPath(this.codeXml, '@category | @input', formulaNode);
+ if (formulaCategoryAttr) {
+ const formulaCategory = formulaCategoryAttr.textContent;
+ if (!formulaCategory) {
+ throw new Error('Formula category is empty');
+ }
+ const formula = Formula.parse(formulaNode, this.codeXml);
+ if (formula) {
+ brick.formValues.set(formulaCategory, formula.toFormulaString());
+ }
+ }
+ }
+
+ private parseSpinners(brickNode: Element, brick: CatblocksBrick) {
+ const spinners: Array = getBrickSpinnerProperties(brick.name);
+ if (spinners) {
+ for (const spinnerProperties of spinners) {
+ try {
+ this.parseSpinner(brickNode, spinnerProperties, brick);
+ } catch (error) {
+ throw new Error(
+ `Spinner value node not found for brick '${brick.name}' spinner '${spinnerProperties.name}': ${error}`
+ );
+ }
+ }
+ }
+ }
+
+ private parseSpinner(brickNode: Element, spinnerProperties: CatblocksSpinnerProperties, brick: CatblocksBrick): void {
+ const inputName: string = spinnerProperties.name;
+ const messageFormat: string = spinnerProperties.messageFormat;
+
+ let referenceNode: Node = brickNode;
+ for (let i = 0; i < spinnerProperties.valueXPath.length; i++) {
+ const xPath = spinnerProperties.valueXPath[i];
+ const valueNode = ParserUtils.getFlatNodeByXPath(this.codeXml, xPath, referenceNode);
+ if (valueNode) {
+ if (i < spinnerProperties.valueXPath.length - 1) {
+ referenceNode = valueNode;
+ continue;
+ }
+ let message: string | null = null;
+ if (valueNode instanceof Element) {
+ const value = valueNode.textContent;
+ message = value;
+ } else if (valueNode instanceof Attr) {
+ const value = valueNode.textContent;
+ if (value) {
+ message = value;
+ }
+ }
+ if (message !== null && messageFormat !== undefined) {
+ if (messageFormat) {
+ message = messageFormat.replace('%v', message);
+ }
+
+ brick.formValues.set(inputName, ParserUtils.getMessage(message, message));
+ break;
+ } else {
+ throw new Error(`Spinner value node not found for brick ${brick.name} spinner ${spinnerProperties.name} (1)`);
+ }
+ } else {
+ const message = this.handleSpecialBrickSpinner(brick, spinnerProperties.name, brickNode);
+ if (message !== null && messageFormat !== undefined) {
+ brick.formValues.set(inputName, ParserUtils.getMessage(message, message));
+ break;
+ } else {
+ brick.formValues.set(inputName, '');
+ console.warn(`Spinner value node not found for brick ${brick.name} spinner ${spinnerProperties.name} (2)`);
+ }
+ }
+ }
+ }
+
+ private handleSpecialBrickSpinner(brick: CatblocksBrick, fieldName: string, brickNode: Element): string | null {
+ if (brick.name === 'CloneBrick') {
+ return 'CONTROL_OFYOURSELF';
+ }
+ if (brick.name === 'GoToBrick') {
+ const spinnerSelection = ParserUtils.getElementByXPath(this.codeXml, 'spinnerSelection', brickNode);
+ if (spinnerSelection) {
+ const selection = ParserUtils.trimTextContent(spinnerSelection.textContent);
+ if (selection) {
+ return `GO_TO_POSITION_${selection}`;
+ }
+ }
+ }
+ if (brick.name === 'WhenBounceOffScript') {
+ return 'WHEN_BOUNCE_OFF_ANYTHING';
+ }
+ if (brick.name === 'SetListeningLanguageBrick') {
+ return '';
+ }
+ return null;
+ }
+}
diff --git a/src/common/ts/parser/ParserUtils.ts b/src/common/ts/parser/ParserUtils.ts
new file mode 100644
index 00000000..d08f05f3
--- /dev/null
+++ b/src/common/ts/parser/ParserUtils.ts
@@ -0,0 +1,81 @@
+import { CatBlocksMsgs } from '../../../library/ts/i18n/CatBlocksMsgs';
+
+export class ParserUtils {
+ public static getAttrByXPath(codeXml: Document, xpath: string, contextNode: Node): Attr | null {
+ const node = ParserUtils.getNodeByXPath(codeXml, xpath, contextNode);
+ if (node) {
+ return node;
+ }
+ return null;
+ }
+
+ public static getElementByXPath(codeXml: Document, xpath: string, contextNode: Node): Element | null {
+ const node = ParserUtils.getNodeByXPath(codeXml, xpath, contextNode);
+ if (node) {
+ return ParserUtils.flattenElementReference(codeXml, node);
+ }
+ return null;
+ }
+
+ public static getNodeByXPath(codeXml: Document, xpath: string, contextNode: Node): Node | null {
+ const iterator = codeXml.evaluate(xpath, contextNode);
+ const node = iterator?.iterateNext();
+ return node;
+ }
+
+ public static getFlatNodeByXPath(codeXml: Document, xpath: string, contextNode: Node): Node | null {
+ const iterator = codeXml.evaluate(xpath, contextNode);
+ const node = iterator?.iterateNext();
+ if (node instanceof Element) {
+ return ParserUtils.flattenElementReference(codeXml, node);
+ }
+ return node;
+ }
+
+ public static foreachElementByXPath(
+ codeXml: Document,
+ xpath: string,
+ contextNode: Node,
+ nodeCallback: (node: Element) => void
+ ): void {
+ const iterator = codeXml.evaluate(xpath, contextNode);
+ let currentNode: Node | null | undefined = null;
+ while ((currentNode = iterator?.iterateNext())) {
+ nodeCallback(ParserUtils.flattenElementReference(codeXml, currentNode));
+ }
+ }
+
+ public static flattenElementReference(codeXml: Document, node: Element): Element {
+ if (node.nodeType == Node.ELEMENT_NODE) {
+ const elementNode = node;
+ if (elementNode.hasAttribute('reference')) {
+ const referenceXPath = elementNode.getAttribute('reference');
+ if (referenceXPath) {
+ const referencedElement = codeXml?.evaluate(referenceXPath, node).iterateNext();
+ if (referencedElement) {
+ return referencedElement;
+ }
+ }
+ }
+ }
+ return node;
+ }
+
+ public static getMessage(key: string, defaultMessage: string): string {
+ const message = CatBlocksMsgs.getTranslationForKey(key.toUpperCase());
+ if (message) {
+ return message;
+ }
+ if (defaultMessage) {
+ return defaultMessage;
+ }
+ return key;
+ }
+
+ public static trimTextContent(textContent: string | null | undefined): string {
+ if (textContent) {
+ return textContent.trim();
+ }
+ return '';
+ }
+}
diff --git a/src/common/ts/parser/UserDefinedBrick.ts b/src/common/ts/parser/UserDefinedBrick.ts
new file mode 100644
index 00000000..813c32d6
--- /dev/null
+++ b/src/common/ts/parser/UserDefinedBrick.ts
@@ -0,0 +1,66 @@
+import { BlockDefinition } from 'blockly/core/blocks';
+import { UserDefinedBrickInputType } from './UserDefinedBrickInputType';
+
+export class UserDefinedBrickDefinition {
+ userDefinedBrickID: string;
+ inputTypes: UserDefinedBrickInputType[] = new Array();
+ message = '';
+
+ constructor(userDefinedBrickID: string) {
+ this.userDefinedBrickID = userDefinedBrickID;
+ }
+
+ private getArgs(fieldNameEqualsContent: boolean): Partial[] {
+ const args: Partial[] = [];
+ for (let i = 0; i < this.inputTypes.length; ++i) {
+ if (this.inputTypes[i].type.toUpperCase() == 'INPUT') {
+ args.push({
+ type: 'field_catblockstext',
+ name: this.inputTypes[i].name,
+ text: fieldNameEqualsContent ? this.inputTypes[i].name : 'unset'
+ });
+ args.push({
+ type: 'field_image',
+ name: `${this.inputTypes[i].name}_INFO`,
+ src: `${document.location.pathname}media/info_icon.svg`,
+ height: 24,
+ width: 24,
+ alt: '(i)',
+ flip_rtl: true
+ });
+ }
+ }
+ return args;
+ }
+
+ public getJson(): BlockDefinition {
+ const args = this.getArgs(false);
+ return {
+ message0: this.message,
+ args0: args,
+ args2: args,
+ category: 'user',
+ colour: '#3556a2',
+ extensions: ['shapeBrick']
+ };
+ }
+
+ public getJsonForDefinitionBrick(): BlockDefinition {
+ const args = this.getArgs(true);
+ const argValues = new Map();
+ for (let i = 0; i < this.inputTypes.length; ++i) {
+ if (this.inputTypes[i].type.toUpperCase() == 'INPUT') {
+ argValues.set(this.inputTypes[i].name, this.inputTypes[i].name);
+ }
+ }
+ return {
+ message0: this.message,
+ args0: args,
+ category: 'user',
+ colour: '#3556a2',
+ previousStatement: 'userdefinedtemplate',
+ nextStatement: 'userdefinedtemplate',
+ formValueMap: argValues
+ };
+ }
+}
diff --git a/src/common/ts/parser/UserDefinedBrickInputType.ts b/src/common/ts/parser/UserDefinedBrickInputType.ts
new file mode 100644
index 00000000..81bfe6a3
--- /dev/null
+++ b/src/common/ts/parser/UserDefinedBrickInputType.ts
@@ -0,0 +1,9 @@
+export class UserDefinedBrickInputType {
+ type: string;
+ name: string;
+
+ constructor(type: string, name: string) {
+ this.type = type;
+ this.name = name;
+ }
+}
diff --git a/src/intern/js/playground/playground.js b/src/intern/js/playground/playground.js
index fddde37b..730a41dc 100644
--- a/src/intern/js/playground/playground.js
+++ b/src/intern/js/playground/playground.js
@@ -1,6 +1,6 @@
import Blockly from 'blockly';
import { jsonDomToWorkspace, zebraChangeColor, RenderSource_Share } from '../../../library/js/integration/utils';
-import { Parser } from '../../../common/js/parser/parser';
+import { CatblocksParser } from '../../../common/ts/parser/Parser';
import { CatBlocksMsgs } from '../../../library/ts/i18n/CatBlocksMsgs';
import { initBricks } from '../../../library/js/blocks/bricks';
@@ -8,7 +8,6 @@ export class Playground {
constructor() {
// for debugging
this.Blockly = Blockly;
- this.Parser = Parser;
this.toolbox = undefined;
this.workspace = undefined;
@@ -255,7 +254,8 @@ export class Playground {
try {
const input = document.getElementById('importExport');
const xmlString = beforeScript + input.value + afterScript;
- const blocksJSON = this.Parser.convertProgramToJSONDebug(xmlString);
+ const parser = new CatblocksParser(xmlString);
+ const blocksJSON = parser.xmlToCatblocksProject();
if (blocksJSON !== undefined) {
const scenes = blocksJSON.scenes;
if (scenes !== undefined && scenes.length > 0) {
diff --git a/src/intern/ts/Playground.ts b/src/intern/ts/Playground.ts
new file mode 100644
index 00000000..f25f0f32
--- /dev/null
+++ b/src/intern/ts/Playground.ts
@@ -0,0 +1,31 @@
+import '../scss/style.scss';
+
+import { Playground } from '../js/playground/playground';
+import Blockly from 'blockly';
+import { CatBlocksMsgs } from '../../library/ts/i18n/CatBlocksMsgs';
+import { CatBlocksConfig } from '../../library/ts/config/CatBlocksConfig';
+import { ConfigValidator } from '../../library/ts/config/ConfigValidator';
+
+declare global {
+ interface Window {
+ Catblocks: Playground;
+ Blockly: typeof Blockly;
+ }
+}
+
+(async function () {
+ const config: Partial = {
+ language: process.env.DISPLAY_LANGUAGE,
+ rtl: process.env.DISPLAY_RTL?.toLowerCase() === 'true',
+ container: 'catblocks-workspace-container',
+ shareRoot: '/'
+ };
+ const validatedConfig = ConfigValidator.parseOptions(config);
+
+ CatBlocksMsgs.init(validatedConfig.i18n);
+ await CatBlocksMsgs.setLocale('en');
+ window.Blockly = Blockly;
+ const playground = new Playground();
+ playground.init();
+ window.Catblocks = playground;
+})();
diff --git a/src/intern/ts/Testing.ts b/src/intern/ts/Testing.ts
index 273e048d..126324ec 100644
--- a/src/intern/ts/Testing.ts
+++ b/src/intern/ts/Testing.ts
@@ -7,7 +7,7 @@ import { Playground } from '../js/playground/playground';
import Blockly from 'blockly';
import { CatBlocksShare } from '../../library/ts/CatBlocksShare';
import * as shareUtils from '../../library/js/integration/utils';
-import { Parser } from '../../common/js/parser/parser';
+import { CatblocksParser } from '../../common/ts/parser/Parser';
import { CatBlocksCatroid } from '../../library/ts/CatBlocksCatroid';
import { IAndroid } from '../../library/ts/IAndroid';
@@ -60,7 +60,7 @@ class Test {
CatBlocksMsgs: typeof CatBlocksMsgs;
CatBlocks: typeof CatBlocksShare;
ShareUtils: typeof shareUtils;
- Parser: typeof Parser;
+ Parser: CatblocksParser;
CatroidCatBlocks: typeof CatBlocksCatroid;
constructor() {
@@ -69,8 +69,8 @@ class Test {
this.CatBlocksMsgs = CatBlocksMsgs;
this.CatBlocks = CatBlocksShare;
this.ShareUtils = shareUtils;
- this.Parser = Parser;
this.CatroidCatBlocks = CatBlocksCatroid;
+ this.Parser = new CatblocksParser();
}
}
diff --git a/src/intern/ts/render/FileHandlerBase.ts b/src/intern/ts/render/FileHandlerBase.ts
index fb245996..8678cee6 100644
--- a/src/intern/ts/render/FileHandlerBase.ts
+++ b/src/intern/ts/render/FileHandlerBase.ts
@@ -1,8 +1,8 @@
import JSZip from 'jszip';
import { MessageBox } from './MessageBox';
-import { Parser } from '../../../common/js/parser/parser';
import { CatBlocksShare } from '../../../library/ts/CatBlocksShare';
import { generateNewDOM } from '../../../library/js/integration/utils';
+import { CatblocksParser } from '../../../common/ts/parser/Parser';
enum FileType {
IMAGE,
@@ -202,8 +202,8 @@ class FileHandlerBase {
counter: number,
fileMap: Record
) {
- // inject code
- const programJSON = Parser.convertProgramToJSONDebug(codeXML);
+ const parser = new CatblocksParser(codeXML);
+ const projectJson = parser.xmlToCatblocksProject();
// prepare container for program injection
const programContainer = this.createProgramContainer(container, name, counter);
@@ -212,7 +212,7 @@ class FileHandlerBase {
CatBlocksShare.controller.renderProgramJSON(
programID,
programContainer,
- programJSON,
+ projectJson,
{
object: {
fileMap: fileMap
diff --git a/src/library/js/blocks/bricks.js b/src/library/js/blocks/bricks.js
index 6f12d84d..743e5d9a 100644
--- a/src/library/js/blocks/bricks.js
+++ b/src/library/js/blocks/bricks.js
@@ -5,6 +5,7 @@
import Blockly from 'blockly';
import categories from './categories';
import { initCatblocksColours } from './colours';
+import { CatblocksSpinnerProperties } from '../../../common/ts/parser/CatblocksSpinnerProperties';
export const getBrickScriptMapping = () => {
return new Map()
@@ -116,6 +117,41 @@ const loadBricks = (cats = categories, blockly = Blockly, advancedMode = false)
}
};
+function getAllBricks() {
+ const bricks = {};
+ for (const catName in categories) {
+ const cat = categories[catName];
+ for (const brickName in cat) {
+ bricks[`${brickName}`] = cat[brickName];
+ }
+ }
+ return bricks;
+}
+
+export function getBrickSpinnerProperties(brickType) {
+ const allBricks = getAllBricks();
+ const spinners = [];
+
+ if (Object.prototype.hasOwnProperty.call(allBricks, brickType)) {
+ const brick = allBricks[brickType];
+ for (let i = 0; i < 5; i++) {
+ const argName = `args${i}`;
+ if (Object.prototype.hasOwnProperty.call(brick, argName)) {
+ const args = brick[argName];
+ for (const arg of args) {
+ if (arg['type'] === 'field_catblocksspinner') {
+ const name = arg['name'];
+ const valueXpaths = arg['value_xpath'];
+ const messageFormat = arg['message_format'];
+ spinners.push(new CatblocksSpinnerProperties(name, valueXpaths, messageFormat));
+ }
+ }
+ }
+ }
+ }
+ return spinners;
+}
+
/**
* @param {boolean} advancedMode
*/
diff --git a/src/library/js/blocks/categories/control.js b/src/library/js/blocks/categories/control.js
index c59fdec0..590131a0 100644
--- a/src/library/js/blocks/categories/control.js
+++ b/src/library/js/blocks/categories/control.js
@@ -288,6 +288,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_scene_transition_spinner',
+ value_xpath: ['sceneForTransition'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -307,6 +309,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_scene_start_spinner',
+ value_xpath: ['sceneToStart'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -326,6 +330,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_stop_script_spinner',
+ value_xpath: ['spinnerSelection'],
+ message_format: 'STOP_SCRIPT_%v',
name: 'SPINNER'
},
{
@@ -345,6 +351,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_clone_spinner',
+ value_xpath: ['objectToClone', '@name'],
+ message_format: '%v',
name: 'SPINNER'
},
{
@@ -404,6 +412,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_for_variable_spinner',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -450,6 +460,8 @@ export default {
{
type: 'field_catblocksspinner',
name: 'FOR_ITEM_IN_USERLIST_LIST',
+ value_xpath: ['userDataList', 'userData[@category="FOR_ITEM_IN_USERLIST_LIST"]', 'name'],
+ message_format: '%v',
catroid_field_id: 'for_item_in_userlist_list_spinner'
},
{
@@ -464,6 +476,14 @@ export default {
{
type: 'field_catblocksspinner',
name: 'FOR_ITEM_IN_USERLIST_VARIABLE',
+ value_xpath: [
+ 'userDataList',
+ 'userData[@category="FOR_ITEM_IN_USERLIST_VARIABLE"]',
+ 'userVariable',
+ 'default',
+ 'name'
+ ],
+ message_format: '%v',
catroid_field_id: 'for_item_in_userlist_variable_spinner'
},
{
@@ -507,6 +527,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_broadcast_spinner',
+ value_xpath: ['broadcastMessage'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -526,6 +548,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_broadcast_spinner',
+ value_xpath: ['broadcastMessage'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -606,6 +630,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_phiro_sensor_action_spinner',
+ value_xpath: ['sensorSpinnerPosition'],
+ message_format: 'SPINNER_PHIRO_%v',
name: 'DROPDOWN'
},
{
diff --git a/src/library/js/blocks/categories/device.js b/src/library/js/blocks/categories/device.js
index d04225e4..4ad8ca63 100644
--- a/src/library/js/blocks/categories/device.js
+++ b/src/library/js/blocks/categories/device.js
@@ -7,6 +7,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_when_nfc_spinner',
+ value_xpath: ['nfcTag', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -40,6 +42,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_set_nfc_tag_ndef_record_spinner',
+ value_xpath: ['nfcTagNdefType'],
+ message_format: 'TNF_%v',
name: 'DROPDOWN'
},
{
diff --git a/src/library/js/blocks/categories/event.js b/src/library/js/blocks/categories/event.js
index 93834866..c7a74349 100644
--- a/src/library/js/blocks/categories/event.js
+++ b/src/library/js/blocks/categories/event.js
@@ -19,6 +19,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_broadcast_spinner',
+ value_xpath: ['receivedMessage'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -57,6 +59,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_when_background_spinner',
+ value_xpath: ['look', '@name'],
+ message_format: '%v',
name: 'look'
},
{
diff --git a/src/library/js/blocks/categories/lego.js b/src/library/js/blocks/categories/lego.js
index 3bfa0c2d..f36ed4cc 100644
--- a/src/library/js/blocks/categories/lego.js
+++ b/src/library/js/blocks/categories/lego.js
@@ -7,6 +7,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'lego_ev3_motor_turn_angle_spinner',
+ value_xpath: ['motor'],
+ message_format: 'EV3_%v',
name: 'DROPDOWN'
},
{
@@ -40,6 +42,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_ev3_motor_move_spinner',
+ value_xpath: ['motor'],
+ message_format: 'EV3_%v',
name: 'DROPDOWN'
},
{
@@ -73,6 +77,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'ev3_stop_motor_spinner',
+ value_xpath: ['motor'],
+ message_format: 'EV3_%v',
name: 'DROPDOWN'
},
{
@@ -139,6 +145,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_ev3_set_led_spinner',
+ value_xpath: ['ledStatus'],
+ message_format: 'EV3_%v',
name: 'DROPDOWN'
},
{
@@ -158,6 +166,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'lego_motor_turn_angle_spinner',
+ value_xpath: ['motor'],
+ message_format: 'NXT_%v',
name: 'DROPDOWN'
},
{
@@ -191,6 +201,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'stop_motor_spinner',
+ value_xpath: ['motor'],
+ message_format: 'NXT_%v',
name: 'DROPDOWN'
},
{
@@ -210,6 +222,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'lego_motor_action_spinner',
+ value_xpath: ['motor'],
+ message_format: 'NXT_%v',
name: 'DROPDOWN'
},
{
diff --git a/src/library/js/blocks/categories/look.js b/src/library/js/blocks/categories/look.js
index 92a962ad..8aeb129a 100644
--- a/src/library/js/blocks/categories/look.js
+++ b/src/library/js/blocks/categories/look.js
@@ -7,6 +7,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_set_look_spinner',
+ value_xpath: ['look', '@name'],
+ message_format: '%v',
name: 'look'
},
{
@@ -90,6 +92,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_ask_spinner',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -349,6 +353,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_set_background_spinner',
+ value_xpath: ['look', '@name'],
+ message_format: '%v',
name: 'look'
},
{
@@ -387,6 +393,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_set_background_spinner',
+ value_xpath: ['look', '@name'],
+ message_format: '%v',
name: 'look'
},
{
@@ -425,6 +433,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_video_spinner',
+ value_xpath: ['spinnerSelectionON'],
+ message_format: 'CAMSPINNER_%v',
name: 'SPINNER'
},
{
@@ -444,6 +454,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_choose_camera_spinner',
+ value_xpath: ['spinnerSelectionFRONT'],
+ message_format: 'CAMCHOOSESPINNER_%v',
name: 'SPINNER'
},
{
@@ -463,6 +475,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_flash_spinner',
+ value_xpath: ['spinnerSelectionID'],
+ message_format: 'FLASHSPINNER_%v',
name: 'SPINNER'
},
{
@@ -567,6 +581,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_phiro_rgb_light_spinner',
+ value_xpath: ['eye'],
+ message_format: 'PHIRO_EYE_%v',
name: 'eye'
},
{
@@ -627,6 +643,8 @@ export default {
args0: [
{
type: 'field_catblocksspinner',
+ value_xpath: ['fadeSpinnerSelectionId'],
+ message_format: 'FADESPINNER_%v',
name: 'brick_fade_particle_effect_spinner'
},
{
@@ -645,6 +663,8 @@ export default {
args0: [
{
type: 'field_catblocksspinner',
+ value_xpath: ['fadeSpinnerSelectionId'],
+ message_format: 'PARTICLESPINNER_%v',
name: 'brick_additive_particle_effect_spinner'
},
{
diff --git a/src/library/js/blocks/categories/motion.js b/src/library/js/blocks/categories/motion.js
index 9d771352..a0f7f73d 100644
--- a/src/library/js/blocks/categories/motion.js
+++ b/src/library/js/blocks/categories/motion.js
@@ -116,6 +116,8 @@ export default {
{
type: 'field_catblocksspinner',
name: 'SPINNER',
+ value_xpath: ['destinationSprite', '@name'],
+ message_format: '%v',
catroid_field_id: 'brick_go_to_spinner'
},
{
@@ -214,6 +216,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_point_to_spinner',
+ value_xpath: ['pointedObject', '@name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -233,6 +237,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_set_rotation_style_spinner',
+ value_xpath: ['selection'],
+ message_format: 'POINTTO_%v',
name: 'SPINNER'
},
{
@@ -340,6 +346,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_set_physics_object_type_spinner',
+ value_xpath: ['type'],
+ message_format: 'GRAVITY_%v',
name: 'DROPDOWN'
},
{
@@ -793,6 +801,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_drone_play_led_animation_spinner',
+ value_xpath: ['ledAnimationName'],
+ message_format: '%v',
name: 'ADRONEANIMATION',
text: 'unset'
},
@@ -813,6 +823,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_jumping_sumo_animation_spinner',
+ value_xpath: ['animationName'],
+ message_format: 'ANIMATION_%v',
name: 'ANIMATION'
},
{
@@ -948,6 +960,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_when_bounce_off_spinner',
+ value_xpath: ['spriteToBounceOffName'], // empty if all
+ message_format: '%v',
name: 'DROPDOWN'
},
{
diff --git a/src/library/js/blocks/categories/phiro.js b/src/library/js/blocks/categories/phiro.js
index d79388ce..43e2851b 100644
--- a/src/library/js/blocks/categories/phiro.js
+++ b/src/library/js/blocks/categories/phiro.js
@@ -7,6 +7,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_phiro_motor_forward_action_spinner',
+ value_xpath: ['motor'],
+ message_format: 'PHIRO_%v',
name: 'DROPDOWN'
},
{
@@ -40,6 +42,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_phiro_motor_backward_action_spinner',
+ value_xpath: ['motor'],
+ message_format: 'PHIRO_%v',
name: 'DROPDOWN'
},
{
@@ -73,6 +77,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_phiro_stop_motor_spinner',
+ value_xpath: ['motor'],
+ message_format: 'PHIRO_%v',
name: 'DROPDOWN'
},
{
diff --git a/src/library/js/blocks/categories/script.js b/src/library/js/blocks/categories/script.js
index 088b1c5e..d3cbb380 100644
--- a/src/library/js/blocks/categories/script.js
+++ b/src/library/js/blocks/categories/script.js
@@ -7,6 +7,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_when_gamepad_button_spinner',
+ value_xpath: ['action'],
+ message_format: '%v',
name: 'ACTION'
},
{
@@ -26,6 +28,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_raspi_when_pinspinner',
+ value_xpath: ['pin'],
+ message_format: '%v',
name: 'pin'
},
{
@@ -40,6 +44,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_raspi_when_valuespinner',
+ value_xpath: ['eventValue'],
+ message_format: 'RASPI_%v',
name: 'eventValue'
},
{
diff --git a/src/library/js/blocks/categories/sound.js b/src/library/js/blocks/categories/sound.js
index 3884d4f1..94ca7642 100644
--- a/src/library/js/blocks/categories/sound.js
+++ b/src/library/js/blocks/categories/sound.js
@@ -7,6 +7,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_play_sound_spinner',
+ value_xpath: ['sound', '@name'],
+ message_format: '%v',
name: 'sound'
},
{
@@ -26,6 +28,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_play_sound_spinner',
+ value_xpath: ['sound', '@name'],
+ message_format: '%v',
name: 'sound'
},
{
@@ -138,6 +142,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_ask_speech_spinner',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -157,6 +163,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_stop_sound_spinner',
+ value_xpath: ['sound', '@name'],
+ message_format: '%v',
name: 'sound'
},
{
@@ -176,6 +184,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_start_listening_spinner',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'LISTEN'
},
{
@@ -195,6 +205,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'set_instrument_spinner',
+ value_xpath: ['instrumentSelection'],
+ message_format: 'INSTRUMENT_%v',
name: 'DROPDOWN'
},
{
@@ -233,6 +245,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_set_listening_language_spinner',
+ value_xpath: ['languageObject'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -290,6 +304,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'play_drum_for_beats_spinner',
+ value_xpath: ['drumSelection'],
+ message_format: 'DRUM_%v',
name: 'DROPDOWN'
},
{
@@ -356,6 +372,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_jumping_sumo_sound_spinner',
+ value_xpath: ['soundName'],
+ message_format: 'JUMPING_SUMO_SOUND_%v',
name: 'DROPDOWN'
},
{
@@ -392,6 +410,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_phiro_select_tone_spinner',
+ value_xpath: ['tone'],
+ message_format: 'PHIRO_TONE_%v',
name: 'tone'
},
{
@@ -426,6 +446,8 @@ export default {
type: 'field_catblocksspinner',
catroid_field_id: 'brick_play_sound_at_spinner',
name: 'sound',
+ value_xpath: ['sound', '@name'],
+ message_format: '%v',
text: 'unset'
},
{
diff --git a/src/library/js/blocks/categories/test.js b/src/library/js/blocks/categories/test.js
index b32bb98b..4b739a72 100644
--- a/src/library/js/blocks/categories/test.js
+++ b/src/library/js/blocks/categories/test.js
@@ -43,6 +43,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_assert_lists_actual',
+ value_xpath: ['userDataList', 'userData[@category="ASSERT_LISTS_ACTUAL"]', 'name'],
+ message_format: '%v',
name: 'ASSERT_LISTS_ACTUAL'
},
{
@@ -57,6 +59,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_assert_lists_expected',
+ value_xpath: ['userDataList', 'userData[@category="ASSERT_LISTS_EXPECTED"]', 'name'],
+ message_format: '%v',
name: 'ASSERT_LISTS_EXPECTED'
},
{
@@ -114,6 +118,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_param_expected_list',
+ value_xpath: ['endBrick', 'userList', 'name'],
+ message_format: '%v',
name: 'LIST_SELECTED'
},
{
diff --git a/src/library/js/blocks/categories/user.js b/src/library/js/blocks/categories/user.js
index a64a6062..c3045a67 100644
--- a/src/library/js/blocks/categories/user.js
+++ b/src/library/js/blocks/categories/user.js
@@ -24,6 +24,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_set_screen_refresh_spinner',
+ value_xpath: ['screenRefresh'],
+ message_format: 'UDB_SCREEN_REFRESH_%v',
name: 'UDB_SCREEN_REFRESH'
},
{
diff --git a/src/library/js/blocks/categories/userlist.js b/src/library/js/blocks/categories/userlist.js
index 59dc3139..253c5cfc 100644
--- a/src/library/js/blocks/categories/userlist.js
+++ b/src/library/js/blocks/categories/userlist.js
@@ -7,6 +7,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'read_list_from_device_spinner',
+ value_xpath: ['userList', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -26,6 +28,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'write_list_spinner',
+ value_xpath: ['userList', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
diff --git a/src/library/js/blocks/categories/uservariables.js b/src/library/js/blocks/categories/uservariables.js
index d81e92df..4b1e5b4c 100644
--- a/src/library/js/blocks/categories/uservariables.js
+++ b/src/library/js/blocks/categories/uservariables.js
@@ -7,6 +7,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'set_variable_spinner',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -40,6 +42,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'change_variable_spinner',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -73,6 +77,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'show_variable_spinner',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'DROPDOWN',
text: 'unset'
},
@@ -121,6 +127,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'show_variable_color_size_spinner',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -191,6 +199,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_show_variable_color_size_align_spinner',
+ value_xpath: ['alignmentSelection'],
+ message_format: 'ALIGNMENTS_%v',
name: 'ALIGNMENT'
},
{
@@ -210,6 +220,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'delete_item_of_userlist_spinner',
+ value_xpath: ['userList', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -256,7 +268,10 @@ export default {
},
{
type: 'field_catblocksspinner',
+ value_xpath: ['userList', 'name'],
+ message_format: '%v',
catroid_field_id: 'add_item_to_userlist_spinner',
+
name: 'DROPDOWN'
},
{
@@ -290,6 +305,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'insert_item_into_userlist_spinner',
+ value_xpath: ['userList', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -323,6 +340,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'replace_item_in_userlist_spinner',
+ value_xpath: ['userList', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -370,6 +389,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'hide_variable_spinner',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -390,6 +411,8 @@ export default {
type: 'field_catblocksspinner',
catroid_field_id: 'read_variable_from_device_spinner',
name: 'DROPDOWN',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
text: 'unset'
},
{
@@ -410,6 +433,8 @@ export default {
type: 'field_catblocksspinner',
catroid_field_id: 'write_variable_spinner',
name: 'DROPDOWN',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
text: 'unset'
},
{
@@ -457,6 +482,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_store_csv_into_userlist_spinner',
+ value_xpath: ['userList', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -476,6 +503,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'clear_userlist_spinner',
+ value_xpath: ['userList', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -510,6 +539,8 @@ export default {
type: 'field_catblocksspinner',
catroid_field_id: 'web_request_spinner',
name: 'DROPDOWN',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
text: 'unset'
},
{
@@ -529,6 +560,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_read_variable_from_file_spinner_variable',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
@@ -557,6 +590,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_read_variable_from_file_spinner_mode',
+ value_xpath: ['spinnerSelectionID'],
+ message_format: 'READ_VARIABLE_%v',
name: 'SPINNER'
},
{
@@ -576,6 +611,8 @@ export default {
{
type: 'field_catblocksspinner',
catroid_field_id: 'brick_write_variable_to_file_spinner',
+ value_xpath: ['userVariable', 'userVariable', 'default', 'name'],
+ message_format: '%v',
name: 'DROPDOWN'
},
{
diff --git a/src/library/js/integration/catroid.js b/src/library/js/integration/catroid.js
index 8872c710..e76fcd12 100644
--- a/src/library/js/integration/catroid.js
+++ b/src/library/js/integration/catroid.js
@@ -3,7 +3,7 @@ import '../../scss/catroid.scss';
import { getBrickScriptMapping } from '../blocks/bricks';
import Blockly from 'blockly';
-import { Parser } from '../../../common/js/parser/parser';
+import { CatblocksParser } from '../../../common/ts/parser/Parser';
import {
defaultOptions,
generateFormulaModal,
@@ -86,7 +86,8 @@ export class Catroid {
const newId = Android.duplicateBrick(scope.block.id);
const codeXML = Android.getCurrentProject();
- const objectJSON = Parser.convertObjectToJSON(codeXML, this.scene, this.object);
+ const parser = new CatblocksParser(codeXML);
+ const objectJSON = parser.xmlToCatblocksObject(this.scene, this.object);
const clone = objectJSON.scriptList.filter(x => x.id.toLowerCase() == newId.toLowerCase());
if (clone && clone.length) {
@@ -455,7 +456,8 @@ export class Catroid {
const newScriptId = bricksToAdd[0].brickId.toLowerCase();
const codeXML = Android.getCurrentProject();
- const objectJSON = Parser.convertObjectToJSON(codeXML, this.scene, this.object);
+ const parser = new CatblocksParser(codeXML);
+ const objectJSON = parser.xmlToCatblocksObject(this.scene, this.object);
const newScript = objectJSON.scriptList.filter(x => x.id.toLowerCase() == newScriptId);
if (newScript && newScript.length) {
diff --git a/src/library/js/integration/share.js b/src/library/js/integration/share.js
index 150dea1b..eb0e6e29 100644
--- a/src/library/js/integration/share.js
+++ b/src/library/js/integration/share.js
@@ -431,11 +431,11 @@ export class Share {
if (object.lookList) {
for (const look of object.lookList) {
- if (!options.sceneName || !look.fileName) {
+ if (!options.sceneName || !look.name) {
continue;
}
- const imgPath = `${options.sceneName}/images/${look.fileName}`;
+ const imgPath = `${options.sceneName}/images/${look.path}`;
src = escapeURI(`${this.config.shareRoot}${options.programRoot}${imgPath}`);
if (options.programRoot.startsWith('http')) {
@@ -568,12 +568,12 @@ export class Share {
class: 'col'
});
- if (!options.sceneName || !sound.fileName) {
+ if (!options.sceneName || !sound.name) {
failed++;
continue;
}
- const soundPath = `${options.sceneName}/sounds/${sound.fileName}`;
+ const soundPath = `${options.sceneName}/sounds/${sound.path}`;
let src = escapeURI(`${this.config.shareRoot}${options.programRoot}${soundPath}`);
if (options.programRoot.startsWith('http')) {
@@ -586,7 +586,7 @@ export class Share {
let displaySoundName = sound.name;
if (!displaySoundName) {
- displaySoundName = sound.fileName;
+ displaySoundName = sound.name;
}
const audioContainer = generateNewDOM(col, 'audio', {
@@ -670,12 +670,12 @@ export class Share {
class: 'col-12 col-sm-6 d-flex align-items-center'
});
- if (!options.sceneName || !look.fileName) {
+ if (!options.sceneName || !look.name) {
failed++;
continue;
}
- const imgPath = `${options.sceneName}/images/${look.fileName}`;
+ const imgPath = `${options.sceneName}/images/${look.path}`;
let src = escapeURI(`${this.config.shareRoot}${options.programRoot}${imgPath}`);
// renderProgram got a full link
@@ -693,7 +693,7 @@ export class Share {
let displayLookName = look.name;
if (!displayLookName) {
- displayLookName = look.fileName;
+ displayLookName = look.name;
}
const imgID = generateID(`${objectID}-${displayLookName}`) + '-imgID';
diff --git a/src/library/js/integration/utils.js b/src/library/js/integration/utils.js
index be4394a1..8ff60992 100644
--- a/src/library/js/integration/utils.js
+++ b/src/library/js/integration/utils.js
@@ -306,10 +306,8 @@ export const renderAndConnectBlocksInList = (parentBrick, brickList, brickListTy
}
}
- if (parentBrick === null && brickList[i].userBrickId !== undefined) {
- // When there is no parentBrick but the userBrickId is set
- // ChildBrick is a UserDefinedScript and we need to add the UserDefinedBrick definition
- const definitionBrickName = brickList[i].userBrickId + '_UDB_CATBLOCKS_DEF';
+ if (brickList[i].name === 'UserDefinedScript') {
+ const definitionBrickName = brickList[i].userDefinedBrickID + '_UDB_CATBLOCKS_DEF';
const definitionBrick = Blockly.Bricks[definitionBrickName];
const definitionBrickToRender = {
name: definitionBrickName,
@@ -322,7 +320,7 @@ export const renderAndConnectBlocksInList = (parentBrick, brickList, brickListTy
}
if (brickList[i].brickList !== undefined && brickList[i].brickList.length > 0) {
- if (brickList[i].userBrickId !== undefined) {
+ if (brickList[i].userDefinedBrickID !== undefined) {
// if there are bricks in the brickList and the userBrickId is set, it is a UserDefinedScript
renderAndConnectBlocksInList(
childBrick,
@@ -381,9 +379,7 @@ export const renderBrick = (parentBrick, jsonBrick, brickListType, workspace, re
let catblocksDomBrickID;
const brickIDGenerator = new BrickIDGenerator();
if (childBrick.type === 'UserDefinedScript') {
- catblocksDomBrickID = brickIDGenerator.createBrickIDForUserDefinedScript(childBrick, jsonBrick.userBrickId);
- } else if (childBrick.type !== 'UserDefinedScript' && jsonBrick.userBrickId) {
- catblocksDomBrickID = brickIDGenerator.createBrickIDForUserDefinedScriptCall(childBrick, jsonBrick.userBrickId);
+ catblocksDomBrickID = brickIDGenerator.createBrickIDForUserDefinedScript(childBrick, jsonBrick.userDefinedBrickID);
} else {
catblocksDomBrickID = brickIDGenerator.createBrickID(childBrick);
}
@@ -625,13 +621,13 @@ export const lazyLoadImage = (event, eventRoot, callback) => {
export const buildUserDefinedBrick = (object, advancedMode = false) => {
const createdBricks = [];
- if (!object.userBricks) {
+ if (!object.userDefinedBricks) {
return createdBricks;
}
- for (let i = 0; i < object.userBricks.length; ++i) {
- const jsonDef = object.userBricks[i].getJsonDefinition();
- const brickName = object.userBricks[i].id;
+ for (let i = 0; i < object.userDefinedBricks.length; ++i) {
+ const jsonDef = object.userDefinedBricks[i].getJson();
+ const brickName = object.userDefinedBricks[i].userDefinedBrickID;
Blockly.Bricks[brickName] = jsonDef;
Blockly.Blocks[brickName] = {
init: function () {
@@ -645,8 +641,8 @@ export const buildUserDefinedBrick = (object, advancedMode = false) => {
};
createdBricks.push(brickName);
- const definitionJsonDef = object.userBricks[i].getDefinitionJsonDefinition();
- const definitionBrickName = object.userBricks[i].id + '_UDB_CATBLOCKS_DEF';
+ const definitionJsonDef = object.userDefinedBricks[i].getJsonForDefinitionBrick();
+ const definitionBrickName = object.userDefinedBricks[i].userDefinedBrickID + '_UDB_CATBLOCKS_DEF';
Blockly.Bricks[definitionBrickName] = definitionJsonDef;
Blockly.Blocks[definitionBrickName] = {
init: function () {
diff --git a/src/library/ts/CatBlocksCatroid.ts b/src/library/ts/CatBlocksCatroid.ts
index d1120219..ed71a0cd 100644
--- a/src/library/ts/CatBlocksCatroid.ts
+++ b/src/library/ts/CatBlocksCatroid.ts
@@ -1,5 +1,5 @@
import { Modal } from 'bootstrap';
-import { Parser } from '../../common/js/parser/parser';
+import { CatblocksParser } from '../../common/ts/parser/Parser';
import { CatBlocksConfig } from './config/CatBlocksConfig';
import { Catroid } from '../js/integration/catroid';
import { CatBlocksBase } from './CatBlocksBase';
@@ -24,6 +24,10 @@ export class CatBlocksCatroid extends CatBlocksBase {
}
public static render(codeXML: string, showScene?: string, showObject?: string, brickIDToFocus?: string) {
+ if (!showObject || !showScene) {
+ throw new Error('Invalid object or scene. Object and scene must be selected');
+ }
+
return new Promise((resolve, reject) => {
const spinnerElement = document.getElementById('spinnerModal');
if (!spinnerElement) {
@@ -35,7 +39,8 @@ export class CatBlocksCatroid extends CatBlocksBase {
const eventListener = () => {
spinnerElement.removeEventListener('shown.bs.modal', eventListener, false);
try {
- const objectJSON = Parser.convertObjectToJSON(codeXML, showScene, showObject);
+ const parser = new CatblocksParser(codeXML);
+ const objectJSON = parser.xmlToCatblocksObject(showScene, showObject);
console.log(objectJSON);
this.controller.scene = showScene;
this.controller.object = showObject;
diff --git a/src/library/ts/CatBlocksShare.ts b/src/library/ts/CatBlocksShare.ts
index 60594172..03bb277d 100644
--- a/src/library/ts/CatBlocksShare.ts
+++ b/src/library/ts/CatBlocksShare.ts
@@ -1,4 +1,4 @@
-import { Parser } from '../../common/js/parser/parser';
+import { CatblocksParser } from '../../common/ts/parser/Parser';
import { CatBlocksConfig } from './config/CatBlocksConfig';
import { Share } from '../js/integration/share';
import { CatBlocksBase } from './CatBlocksBase';
@@ -51,7 +51,8 @@ export class CatBlocksShare extends CatBlocksBase {
const response = await fetch(`${path}${name}/code.xml`);
const codeXML = await response.text();
- const programJSON = Parser.convertProgramToJSONDebug(codeXML);
+ const parser = new CatblocksParser(codeXML);
+ const programJSON = parser.xmlToCatblocksProject();
const programID = `catblocks-program-${name}`;
diff --git a/test/jsunit/block/block.test.js b/test/jsunit/block/block.test.js
index e49b037a..c9f724e3 100644
--- a/test/jsunit/block/block.test.js
+++ b/test/jsunit/block/block.test.js
@@ -537,13 +537,13 @@ describe('Catroid Block IDs', () => {
};
const firstQueryBase = '#UserDefinedScript-0 #IfLogicBeginBrick-0 ';
- await checkQuerySelectorExistence(firstQueryBase + '#UserDefinedScript-0-Call-0');
- await checkQuerySelectorExistence(firstQueryBase + '#UserDefinedScript-0-Call-1');
- await checkQuerySelectorExistence(firstQueryBase + '#UserDefinedScript-1-Call-0');
+ await checkQuerySelectorExistence(firstQueryBase + '#e8535f0b-477f-42da-b2cf-ed1168eafda9-0');
+ await checkQuerySelectorExistence(firstQueryBase + '#e8535f0b-477f-42da-b2cf-ed1168eafda9-1');
+ await checkQuerySelectorExistence(firstQueryBase + '#f3bbf4a4-15bf-4eea-b622-82548f172ade-0');
const secondQueryBase = '#UserDefinedScript-1 #IfLogicBeginBrick-1 ';
- await checkQuerySelectorExistence(secondQueryBase + '#UserDefinedScript-1-Call-1');
- await checkQuerySelectorExistence(secondQueryBase + '#UserDefinedScript-1-Call-2');
- await checkQuerySelectorExistence(secondQueryBase + '#UserDefinedScript-0-Call-2');
+ await checkQuerySelectorExistence(secondQueryBase + '#f3bbf4a4-15bf-4eea-b622-82548f172ade-1');
+ await checkQuerySelectorExistence(secondQueryBase + '#f3bbf4a4-15bf-4eea-b622-82548f172ade-2');
+ await checkQuerySelectorExistence(secondQueryBase + '#e8535f0b-477f-42da-b2cf-ed1168eafda9-2');
}, 99999);
});
diff --git a/test/jsunit/parser/parser.test.js b/test/jsunit/parser/parser.test.js
index 5e23ed5f..e5285a52 100644
--- a/test/jsunit/parser/parser.test.js
+++ b/test/jsunit/parser/parser.test.js
@@ -55,7 +55,8 @@ describe('Parser catroid program tests', () => {
const result = await page.evaluate(pXML => {
try {
- Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ Test.Parser.xmlToCatblocksProject();
} catch (e) {
return e.message;
}
@@ -80,7 +81,8 @@ describe('Parser catroid program tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -106,7 +108,8 @@ describe('Parser catroid program tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -133,7 +136,8 @@ describe('Parser catroid program tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -170,7 +174,8 @@ describe('Parser catroid program tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -214,7 +219,8 @@ describe('Parser catroid program tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -264,7 +270,8 @@ describe('Parser catroid program tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -324,7 +331,8 @@ describe('Parser catroid program tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -382,7 +390,8 @@ describe('Parser catroid program tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -455,12 +464,13 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const formula = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
return formulaMap.entries().next().value.toString();
}, xmlString);
- expect(formula).toMatch('SIZE, 60& ');
+ expect(formula).toMatch('SIZE,60&.0');
});
test('LookList reference not within the same object', async () => {
@@ -505,7 +515,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -517,7 +528,7 @@ describe('Catroid to Catblocks parser tests', () => {
lookList: expect.arrayContaining([
expect.objectContaining({
name: lookName,
- fileName: lookFileName
+ path: lookFileName
})
])
})
@@ -570,7 +581,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -583,7 +595,7 @@ describe('Catroid to Catblocks parser tests', () => {
soundList: expect.arrayContaining([
expect.objectContaining({
name: soundName,
- fileName: soundFileName
+ path: soundFileName
})
])
})
@@ -624,7 +636,7 @@ describe('Catroid to Catblocks parser tests', () => {
false
-
+
NUMBER
${val1}
@@ -649,7 +661,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, formulaString] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
return [
programJSON,
programJSON.scenes[0].objectList[1].scriptList[0].brickList[0].formValues.entries().next().value.toString()
@@ -657,7 +670,7 @@ describe('Catroid to Catblocks parser tests', () => {
}, xmlString);
expect(programJSON.scenes[0].objectList[1].scriptList[0].brickList[0].name).toBe(brickName);
- expect(formulaString).toMatch(`${val1} ${val2}`);
+ expect(formulaString).toMatch(`${val1} ${val2}`);
});
test('parser converts catroid script properly', async () => {
@@ -703,7 +716,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -773,7 +787,7 @@ describe('Catroid to Catblocks parser tests', () => {
- false
+ false
false
@@ -787,7 +801,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const programJSON = await page.evaluate(pXML => {
- return Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ return Test.Parser.xmlToCatblocksProject();
}, xmlString);
expect(programJSON).toEqual(
@@ -862,7 +877,8 @@ describe('Catroid to Catblocks parser tests', () => {
const langObj = JSON.parse(utils.readFileSync(`${utils.PATHS.CATBLOCKS_MSGS}${lang}.json`));
const [programJSON, formulaString] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
return [
programJSON,
programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues.entries().next().value.toString()
@@ -910,7 +926,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, formulaSize, formulaString] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
return [
programJSON,
programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues.size,
@@ -976,7 +993,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, formula] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
return [
programJSON,
programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues.entries().next().value
@@ -1005,7 +1023,8 @@ describe('Catroid to Catblocks parser tests', () => {
})
);
- expect(formula).toBeNull();
+ expect(formula[0]).toBe('ADRONEANIMATION');
+ expect(formula[1]).toBe('');
});
});
@@ -1061,7 +1080,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
const mapKeys = [];
@@ -1097,7 +1117,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([variableName, 'DROPDOWN']);
- expect(mapValues).toEqual([' ' + firstValue + ' ', userVarialbe]);
+ expect(mapValues).toEqual([firstValue, userVarialbe]);
});
test('local empty name uservariable parsing', async () => {
@@ -1150,7 +1170,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
const mapKeys = [];
@@ -1186,7 +1207,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([variableName, 'DROPDOWN']);
- expect(mapValues).toEqual([' ' + firstValue + ' ', '']);
+ expect(mapValues).toEqual([firstValue, '']);
});
test('local uservariable parsing without name tag', async () => {
@@ -1239,7 +1260,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
const mapKeys = [];
@@ -1275,7 +1297,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([variableName, 'DROPDOWN']);
- expect(mapValues).toEqual([' ' + firstValue + ' ', '']);
+ expect(mapValues).toEqual([firstValue, '']);
});
test('remote uservariable parsing', async () => {
@@ -1339,7 +1361,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -1376,7 +1399,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([variableName, 'DROPDOWN']);
- expect(mapValues).toEqual([' ' + firstValue + ' ', userVariable]);
+ expect(mapValues).toEqual([firstValue, userVariable]);
});
test('remote empty name uservariable parsing', async () => {
@@ -1439,7 +1462,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -1476,7 +1500,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([variableName, 'DROPDOWN']);
- expect(mapValues).toEqual([' ' + firstValue + ' ', '']);
+ expect(mapValues).toEqual([firstValue, '']);
});
test('remote uservariable parsing without name tag', async () => {
@@ -1539,7 +1563,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -1576,7 +1601,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([variableName, 'DROPDOWN']);
- expect(mapValues).toEqual([' ' + firstValue + ' ', '']);
+ expect(mapValues).toEqual([firstValue, '']);
});
test('parser handles formula operator properly', async () => {
@@ -1630,7 +1655,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -1666,8 +1692,8 @@ describe('Catroid to Catblocks parser tests', () => {
})
);
- expect(mapKeys).toEqual([variableName]);
- expect(mapValues).toEqual([` ${firstValue} = ${secondValue} `]);
+ expect(mapKeys).toEqual([variableName, 'DROPDOWN']);
+ expect(mapValues).toEqual([`${firstValue} = ${secondValue}`, '']);
});
});
@@ -1747,7 +1773,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -1784,7 +1811,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([categoryName]);
- expect(mapValues).toEqual([` ${first} × ( ${second} ÷ ( ${third} + ${fourth} ))`]);
+ expect(mapValues).toEqual([`${first} × (${second} ÷ (${third} + ${fourth}))`]);
});
test('Formula with left sided brackets', async () => {
@@ -1862,7 +1889,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -1899,7 +1927,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([categoryName]);
- expect(mapValues).toEqual([`(( ${first} + ${second} ) × ${third} ) ÷ ${fourth} `]);
+ expect(mapValues).toEqual([`((${first} + ${second}) × ${third}) ÷ ${fourth}`]);
});
test('Formula with both sided brackets', async () => {
@@ -1977,7 +2005,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -2014,7 +2043,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([categoryName]);
- expect(mapValues).toEqual([`( ${first} × ${second} ) + ( ${third} × ${fourth} )`]);
+ expect(mapValues).toEqual([`(${first} × ${second}) + (${third} × ${fourth})`]);
});
});
@@ -2074,7 +2103,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -2111,7 +2141,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([categoryName]);
- expect(mapValues).toEqual([`square root( ${first} ) × ${second} `]);
+ expect(mapValues).toEqual([`square root(${first}) × ${second}`]);
});
test('Single value like sin function with logic', async () => {
@@ -2169,7 +2199,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -2206,7 +2237,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([categoryName]);
- expect(mapValues).toEqual([`sine( ${first} ) > ${second} `]);
+ expect(mapValues).toEqual([`sine(${first}) > ${second}`]);
});
test('Two single values like sin plus cos', async () => {
@@ -2268,7 +2299,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -2305,7 +2337,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([categoryName]);
- expect(mapValues).toEqual([`cosine( ${first} ) + sine( ${second} )`]);
+ expect(mapValues).toEqual([`cosine(${first}) + sine(${second})`]);
});
test('Double value like contains', async () => {
@@ -2359,7 +2391,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -2396,7 +2429,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([categoryName]);
- expect(mapValues).toEqual([`contains( ${first} , ${second} )`]);
+ expect(mapValues).toEqual([`contains(${first}, ${second})`]);
});
test('Sensor action in formula', async () => {
@@ -2448,7 +2481,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -2485,7 +2519,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([categoryName]);
- expect(mapValues).toEqual([` touches finger + true `]);
+ expect(mapValues).toEqual([`touches finger + true`]);
});
test('UserList in formula', async () => {
@@ -2529,7 +2563,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -2610,7 +2645,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -2701,7 +2737,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -2803,7 +2840,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -2904,7 +2942,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
const mapKeys = [];
@@ -2940,7 +2979,7 @@ describe('Catroid to Catblocks parser tests', () => {
);
expect(mapKeys).toEqual([categoryName]);
- expect(mapValues).toEqual([`join( ${first} , ${second} , ${third} )`]);
+ expect(mapValues).toEqual([`join(${first}, ${second}, ${third})`]);
});
test('multiple formula', async () => {
@@ -2976,18 +3015,18 @@ describe('Catroid to Catblocks parser tests', () => {
const second = ['2', ' world', 'flatten', 'INDEX_CURRENT_TOUCH'];
const third = ['3', '!', 'INDEX_OF_ITEM'];
const output = [
- `if then else( ${first[0].toLowerCase()} , ${second[0]} , ${third[0]} )`,
+ `if then else(${first[0].toLowerCase()}, ${second[0]}, ${third[0]})`,
`length('${first[1]}')`,
- `join('${first[2]}','${second[1]}','${third[1]}')`,
+ `join('${first[2]}', '${second[1]}', '${third[1]}')`,
`flatten(*${first[3]}*)`,
- `item's index( ${first[4]} , *${second[2]}*)`,
- ` ${first[5].toLowerCase().replaceAll('_', ' ')} `,
- ` number of looks `,
- ` ${first[7].toLowerCase().replaceAll('_', ' ')} `,
- ` ${first[8].toLowerCase().replaceAll('_', ' ')} `,
- ` number of current touches `,
- `index of current touch( ${first[10]} )`,
- ` ${first[11].toLowerCase()} `
+ `item's index(${first[4]}, *${second[2]}*)`,
+ `${first[5].toLowerCase().replaceAll('_', ' ')}`,
+ `number of looks`,
+ `${first[7].toLowerCase().replaceAll('_', ' ')}`,
+ `${first[8].toLowerCase().replaceAll('_', ' ')}`,
+ `number of current touches`,
+ `index of current touch(${first[10]})`,
+ `${first[11].toLowerCase()}`
];
const xmlString = `
@@ -3211,7 +3250,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = [];
const mapKeys = [];
const mapValues = [];
@@ -3289,7 +3329,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -3335,10 +3376,7 @@ describe('Catroid to Catblocks parser tests', () => {
const first = ['200', '#ff0000'];
const second = ['600', '#fe0000'];
const third = '1';
- const expectedOutput = [
- 'color at x y( 200 , 600 )',
- "color equals color with % tolerance('#ff0000', '#fe0000')"
- ];
+ const expectedOutput = ['color at x y(200, 600)', "color equals color with % tolerance('#ff0000', '#fe0000')"];
const xmlString = `
@@ -3409,7 +3447,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
@@ -3452,12 +3491,12 @@ describe('Catroid to Catblocks parser tests', () => {
test('Values in endBrick', async () => {
const blockName = 'ParameterizedBrick';
const blockNameEnd = 'ParameterizedEndBrick';
- const categoryName = ['CATBLOCKS_ASSERT_LISTS_SELECTED', 'ASSERT_LOOP_ACTUAL', 'LIST_SELECTED'];
+ const categoryName = ['ASSERT_LOOP_ACTUAL', 'CATBLOCKS_ASSERT_LISTS_SELECTED', 'LIST_SELECTED'];
const first = 'List (1)';
const second = 'List (2)';
const third = '0';
const fourth = 'List (3)';
- const expectedOutput = ['2 lists selected', ' 0 ', 'List (3)'];
+ const expectedOutput = [third, '2 lists selected', fourth];
const xmlString = `
@@ -3496,7 +3535,7 @@ describe('Catroid to Catblocks parser tests', () => {
5872c621-bb05-4bfb-9150-77e7021acc88
false
-
+
NUMBER
${third}
@@ -3520,7 +3559,8 @@ describe('Catroid to Catblocks parser tests', () => {
`;
const [programJSON, mapKeys, mapValues] = await page.evaluate(pXML => {
- const programJSON = Test.Parser.convertProgramToJSONDebug(pXML);
+ Test.Parser.parseXmlString(pXML);
+ const programJSON = Test.Parser.xmlToCatblocksProject();
const formulaMap = programJSON.scenes[0].objectList[0].scriptList[0].brickList[0].formValues;
diff --git a/webpack.release.build.config.js b/webpack.release.build.config.js
index d13c197d..eeb2ea31 100644
--- a/webpack.release.build.config.js
+++ b/webpack.release.build.config.js
@@ -17,7 +17,7 @@ const releaseFolder = 'release' + (integrationTarget === 'share' ? '' : '_catroi
module.exports = async function () {
let versionInformation = '*** no version found ***';
- try {
+ /*try {
await git.pull('--tags', '--ff-only');
const loadedTags = await git.tags({ '--points-at': 'HEAD' });
console.log('Loaded Tags:', loadedTags);
@@ -26,7 +26,7 @@ module.exports = async function () {
}
} catch (error) {
console.error('Error loading git tags.', error);
- }
+ }*/
const configuration = {
mode: 'production',
diff --git a/yarn.lock b/yarn.lock
index 07ce4b67..3edf7fae 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1982,6 +1982,14 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
mime-types "~2.1.34"
negotiator "0.6.3"
+acorn-globals@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
+ integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==
+ dependencies:
+ acorn "^7.1.1"
+ acorn-walk "^7.1.1"
+
acorn-globals@^7.0.0:
version "7.0.1"
resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz"
@@ -2000,11 +2008,21 @@ acorn-jsx@^5.3.2:
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+acorn-walk@^7.1.1:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
+ integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
+
acorn-walk@^8.0.2:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
+acorn@^7.1.1:
+ version "7.4.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
+ integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+
acorn@^8.1.0, acorn@^8.8.1:
version "8.8.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
@@ -2352,6 +2370,11 @@ braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
+browser-process-hrtime@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
+ integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
+
browserslist@^4.14.5, browserslist@^4.21.3, browserslist@^4.21.4:
version "4.21.4"
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz"
@@ -2825,7 +2848,7 @@ debug@4.3.4, debug@^4.3.4:
dependencies:
ms "2.1.2"
-decimal.js@^10.4.2:
+decimal.js@^10.3.1, decimal.js@^10.4.2:
version "10.4.3"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
@@ -5137,6 +5160,11 @@ nth-check@^2.0.1:
dependencies:
boolbase "^1.0.0"
+nwsapi@^2.2.0:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5"
+ integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==
+
nwsapi@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0"
@@ -6326,7 +6354,7 @@ toidentifier@1.0.1:
resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
-tough-cookie@^4.1.2:
+tough-cookie@^4.0.0, tough-cookie@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874"
integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==
@@ -6525,6 +6553,20 @@ vary@~1.1.2:
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
+w3c-hr-time@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
+ integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
+ dependencies:
+ browser-process-hrtime "^1.0.0"
+
+w3c-xmlserializer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923"
+ integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==
+ dependencies:
+ xml-name-validator "^4.0.0"
+
w3c-xmlserializer@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073"
@@ -6776,7 +6818,7 @@ ws@8.10.0:
resolved "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz"
integrity sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==
-ws@^8.11.0:
+ws@^8.11.0, ws@^8.8.0:
version "8.13.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==