scratch-gui / src /lib /make-toolbox-xml.js
soiz1's picture
Upload folder using huggingface_hub
8fd7a1d verified
import LazyScratchBlocks from './tw-lazy-scratch-blocks';
import {defaultBlockColors} from './themes';
const categorySeparator = '<sep gap="36"/>';
const blockSeparator = '<sep gap="36"/>'; // At default scale, about 28px
const translate = (id, english) => {
if (LazyScratchBlocks.isLoaded()) {
return LazyScratchBlocks.get().ScratchMsgs.translate(id, english);
}
return english;
};
/* eslint-disable no-unused-vars */
const motion = function (isInitialSetup, isStage, targetId, colors) {
const stageSelected = translate(
'MOTION_STAGE_SELECTED',
'Stage selected: no motion blocks'
);
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category name="%{BKY_CATEGORY_MOTION}" id="motion" colour="${colors.primary}" secondaryColour="${colors.tertiary}">
${isStage ? `
<label text="${stageSelected}"></label>
` : `
<block type="motion_movesteps">
<value name="STEPS">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="motion_turnright">
<value name="DEGREES">
<shadow type="math_number">
<field name="NUM">15</field>
</shadow>
</value>
</block>
<block type="motion_turnleft">
<value name="DEGREES">
<shadow type="math_number">
<field name="NUM">15</field>
</shadow>
</value>
</block>
${blockSeparator}
<block type="motion_goto">
<value name="TO">
<shadow type="motion_goto_menu">
</shadow>
</value>
</block>
<block type="motion_gotoxy">
<value name="X">
<shadow id="movex" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
<value name="Y">
<shadow id="movey" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="motion_glideto" id="motion_glideto">
<value name="SECS">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="motion_glideto_menu">
</shadow>
</value>
</block>
<block type="motion_glidesecstoxy">
<value name="SECS">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="X">
<shadow id="glidex" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
<value name="Y">
<shadow id="glidey" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
${blockSeparator}
<block type="motion_pointindirection">
<value name="DIRECTION">
<shadow type="math_angle">
<field name="NUM">90</field>
</shadow>
</value>
</block>
<block type="motion_pointtowards">
<value name="TOWARDS">
<shadow type="motion_pointtowards_menu">
</shadow>
</value>
</block>
<block type="motion_pointtowards_xy">
<value name="X">
<shadow id="pointx" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
<value name="Y">
<shadow id="pointy" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="motion_pointtowards_xyfrom">
<value name="X">
<shadow id="pointx" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
<value name="Y">
<shadow id="pointy" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
<value name="FROMX">
<shadow id="pointx" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
<value name="FROMY">
<shadow id="pointy" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
${blockSeparator}
<block type="motion_changexby">
<value name="DX">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="motion_setx">
<value name="X">
<shadow id="setx" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="motion_changeyby">
<value name="DY">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="motion_sety">
<value name="Y">
<shadow id="sety" type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
${blockSeparator}
<block type="motion_ifonedgebounce"/>
${blockSeparator}
<block type="motion_setrotationstyle"/>
${blockSeparator}
<block id="${targetId}_xposition" type="motion_xposition"/>
<block id="${targetId}_yposition" type="motion_yposition"/>
<block id="${targetId}_direction" type="motion_direction"/>`}
${categorySeparator}
</category>
`;
};
const xmlEscape = function (unsafe) {
return unsafe.replace(/[<>&'"]/g, c => {
switch (c) {
case '<': return '&lt;';
case '>': return '&gt;';
case '&': return '&amp;';
case '\'': return '&apos;';
case '"': return '&quot;';
}
});
};
const looks = function (isInitialSetup, isStage, targetId, costumeName, backdropName, colors) {
const hello = translate('LOOKS_HELLO', 'Hello!');
const hmm = translate('LOOKS_HMM', 'Hmm...');
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category name="%{BKY_CATEGORY_LOOKS}" id="looks" colour="${colors.primary}" secondaryColour="${colors.tertiary}">
${isStage ? '' : `
<block type="looks_sayforsecs">
<value name="MESSAGE">
<shadow type="text">
<field name="TEXT">${hello}</field>
</shadow>
</value>
<value name="SECS">
<shadow type="math_number">
<field name="NUM">2</field>
</shadow>
</value>
</block>
<block type="looks_say">
<value name="MESSAGE">
<shadow type="text">
<field name="TEXT">${hello}</field>
</shadow>
</value>
</block>
<block type="looks_thinkforsecs">
<value name="MESSAGE">
<shadow type="text">
<field name="TEXT">${hmm}</field>
</shadow>
</value>
<value name="SECS">
<shadow type="math_number">
<field name="NUM">2</field>
</shadow>
</value>
</block>
<block type="looks_think">
<value name="MESSAGE">
<shadow type="text">
<field name="TEXT">${hmm}</field>
</shadow>
</value>
</block>
${blockSeparator}
`}
${isStage ? `
<block type="looks_switchbackdropto">
<value name="BACKDROP">
<shadow type="looks_backdrops">
<field name="BACKDROP">${backdropName}</field>
</shadow>
</value>
</block>
<block type="looks_switchbackdroptoandwait">
<value name="BACKDROP">
<shadow type="looks_backdrops">
<field name="BACKDROP">${backdropName}</field>
</shadow>
</value>
</block>
<block type="looks_nextbackdrop"/>
` : `
<block id="${targetId}_switchcostumeto" type="looks_switchcostumeto">
<value name="COSTUME">
<shadow type="looks_costume">
<field name="COSTUME">${costumeName}</field>
</shadow>
</value>
</block>
<block type="looks_nextcostume"/>
<block type="looks_switchbackdropto">
<value name="BACKDROP">
<shadow type="looks_backdrops">
<field name="BACKDROP">${backdropName}</field>
</shadow>
</value>
</block>
<block type="looks_nextbackdrop"/>
${blockSeparator}
<block type="looks_changesizeby">
<value name="CHANGE">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="looks_setsizeto">
<value name="SIZE">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
`}
${blockSeparator}
<block type="looks_changeeffectby">
<value name="CHANGE">
<shadow type="math_number">
<field name="NUM">25</field>
</shadow>
</value>
</block>
<block type="looks_seteffectto">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="looks_cleargraphiceffects"/>
${blockSeparator}
${isStage ? '' : `
<block type="looks_show"/>
<block type="looks_hide"/>
${blockSeparator}
<block type="looks_gotofrontback"/>
<block type="looks_goforwardbackwardlayers">
<value name="NUM">
<shadow type="math_integer">
<field name="NUM">1</field>
</shadow>
</value>
</block>
`}
${isStage ? `
<block id="backdropnumbername" type="looks_backdropnumbername"/>
` : `
<block id="${targetId}_costumenumbername" type="looks_costumenumbername"/>
<block id="backdropnumbername" type="looks_backdropnumbername"/>
<block id="${targetId}_size" type="looks_size"/>
<block id="${targetId}_costumes" type="looks_costumes"/>
`}
${categorySeparator}
</category>
`;
};
const sound = function (isInitialSetup, isStage, targetId, soundName, colors) {
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category name="%{BKY_CATEGORY_SOUND}" id="sound" colour="${colors.primary}" secondaryColour="${colors.tertiary}">
<block id="${targetId}_sound_playuntildone" type="sound_playuntildone">
<value name="SOUND_MENU">
<shadow type="sound_sounds_menu">
<field name="SOUND_MENU">${soundName}</field>
</shadow>
</value>
</block>
<block id="${targetId}_sound_play" type="sound_play">
<value name="SOUND_MENU">
<shadow type="sound_sounds_menu">
<field name="SOUND_MENU">${soundName}</field>
</shadow>
</value>
</block>
<block type="sound_stopallsounds"/>
${blockSeparator}
<block type="sound_changeeffectby">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="sound_seteffectto">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="sound_cleareffects"/>
${blockSeparator}
<block type="sound_changevolumeby">
<value name="VOLUME">
<shadow type="math_number">
<field name="NUM">-10</field>
</shadow>
</value>
</block>
<block type="sound_setvolumeto">
<value name="VOLUME">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block id="${targetId}_volume" type="sound_volume"/>
${categorySeparator}
</category>
`;
};
const events = function (isInitialSetup, isStage, targetId, colors) {
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category name="%{BKY_CATEGORY_EVENTS}" id="events" colour="${colors.primary}" secondaryColour="${colors.tertiary}">
<block type="event_whenflagclicked"/>
<block type="event_whenkeypressed">
</block>
${isStage ? `
<block type="event_whenstageclicked"/>
` : `
<block type="event_whenthisspriteclicked"/>
`}
<block type="event_whenbackdropswitchesto">
</block>
${blockSeparator}
<block type="event_whengreaterthan">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
${blockSeparator}
<block type="event_whenbroadcastreceived">
</block>
<block type="event_broadcast">
<value name="BROADCAST_INPUT">
<shadow type="event_broadcast_menu"></shadow>
</value>
</block>
<block type="event_broadcastandwait">
<value name="BROADCAST_INPUT">
<shadow type="event_broadcast_menu"></shadow>
</value>
</block>
${categorySeparator}
</category>
`;
};
const control = function (isInitialSetup, isStage, targetId, colors) {
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category
name="%{BKY_CATEGORY_CONTROL}"
id="control"
colour="${colors.primary}"
secondaryColour="${colors.tertiary}">
<block type="control_wait">
<value name="DURATION">
<shadow type="math_positive_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
${blockSeparator}
<block type="control_repeat">
<value name="TIMES">
<shadow type="math_whole_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block id="forever" type="control_forever"/>
${blockSeparator}
<block type="control_if"/>
<block type="control_if_else"/>
<block id="wait_until" type="control_wait_until"/>
<block id="repeat_until" type="control_repeat_until"/>
<block id="for_each" type="control_for_each">
<value name="VARIABLE">
<shadow type="variables_get">
<field name="VAR">i</field>
</shadow>
</value>
<value name="VALUE">
<shadow type="math_whole_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
${blockSeparator}
<block type="control_switch">
<value name="VALUE">
<shadow type="text">
<field name="TEXT">value</field>
</shadow>
</value>
</block>
<block type="control_case">
<value name="VALUE">
<shadow type="text">
<field name="TEXT">case</field>
</shadow>
</value>
</block>
<block type="control_case_fallthrough">
<value name="VALUE">
<shadow type="text">
<field name="TEXT">case</field>
</shadow>
</value>
</block>
<block type="control_default"/>
<block type="control_break"/>
${blockSeparator}
<block type="control_stop"/>
${blockSeparator}
${isStage ? `
<block type="control_create_clone_of">
<value name="CLONE_OPTION">
<shadow type="control_create_clone_of_menu"/>
</value>
</block>
` : `
<block type="control_start_as_clone"/>
<block type="control_create_clone_of">
<value name="CLONE_OPTION">
<shadow type="control_create_clone_of_menu"/>
</value>
</block>
<block type="control_delete_this_clone"/>
`}
${categorySeparator}
</category>
`;
};
const sensing = function (isInitialSetup, isStage, targetId, colors) {
const name = translate('SENSING_ASK_TEXT', 'What\'s your name?');
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category
name="%{BKY_CATEGORY_SENSING}"
id="sensing"
colour="${colors.primary}"
secondaryColour="${colors.tertiary}">
${isStage ? '' : `
<block type="sensing_touchingobject">
<value name="TOUCHINGOBJECTMENU">
<shadow type="sensing_touchingobjectmenu"/>
</value>
</block>
<block type="sensing_touchingcolor">
<value name="COLOR">
<shadow type="colour_picker"/>
</value>
</block>
<block type="sensing_coloristouchingcolor">
<value name="COLOR">
<shadow type="colour_picker"/>
</value>
<value name="COLOR2">
<shadow type="colour_picker"/>
</value>
</block>
<block type="sensing_distanceto">
<value name="DISTANCETOMENU">
<shadow type="sensing_distancetomenu"/>
</value>
</block>
${blockSeparator}
`}
${isInitialSetup ? '' : `
<block id="askandwait" type="sensing_askandwait">
<value name="QUESTION">
<shadow type="text">
<field name="TEXT">${name}</field>
</shadow>
</value>
</block>
`}
<block id="answer" type="sensing_answer"/>
${blockSeparator}
<block type="sensing_keypressed">
<value name="KEY_OPTION">
<shadow type="sensing_keyoptions"/>
</value>
</block>
<block type="sensing_mousedown"/>
<block type="sensing_mousex"/>
<block type="sensing_mousey"/>
${isStage ? '' : `
${blockSeparator}
'<block type="sensing_setdragmode" id="sensing_setdragmode"></block>'+
${blockSeparator}
`}
${blockSeparator}
<block id="loudness" type="sensing_loudness"/>
${blockSeparator}
<block id="timer" type="sensing_timer"/>
<block type="sensing_resettimer"/>
${blockSeparator}
<block id="of" type="sensing_of">
<value name="OBJECT">
<shadow id="sensing_of_object_menu" type="sensing_of_object_menu"/>
</value>
</block>
${blockSeparator}
<block id="current" type="sensing_current"/>
<block type="sensing_dayssince2000"/>
${blockSeparator}
<block type="sensing_username"/>
${categorySeparator}
</category>
`;
};
const operators = function (isInitialSetup, isStage, targetId, colors) {
const apple = translate('OPERATORS_JOIN_APPLE', 'apple');
const banana = translate('OPERATORS_JOIN_BANANA', 'banana');
const letter = translate('OPERATORS_LETTEROF_APPLE', 'a');
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category
name="%{BKY_CATEGORY_OPERATORS}"
id="operators"
colour="${colors.primary}"
secondaryColour="${colors.tertiary}">
<block type="operator_add">
<value name="NUM1">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
<value name="NUM2">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
</block>
<block type="operator_subtract">
<value name="NUM1">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
<value name="NUM2">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
</block>
<block type="operator_multiply">
<value name="NUM1">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
<value name="NUM2">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
</block>
<block type="operator_divide">
<value name="NUM1">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
<value name="NUM2">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
</block>
${blockSeparator}
<block type="operator_random">
<value name="FROM">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
${blockSeparator}
<block type="operator_gt">
<value name="OPERAND1">
<shadow type="text">
<field name="TEXT"/>
</shadow>
</value>
<value name="OPERAND2">
<shadow type="text">
<field name="TEXT">50</field>
</shadow>
</value>
</block>
<block type="operator_lt">
<value name="OPERAND1">
<shadow type="text">
<field name="TEXT"/>
</shadow>
</value>
<value name="OPERAND2">
<shadow type="text">
<field name="TEXT">50</field>
</shadow>
</value>
</block>
<block type="operator_equals">
<value name="OPERAND1">
<shadow type="text">
<field name="TEXT"/>
</shadow>
</value>
<value name="OPERAND2">
<shadow type="text">
<field name="TEXT">50</field>
</shadow>
</value>
</block>
${blockSeparator}
<block type="operator_and"/>
<block type="operator_or"/>
<block type="operator_not"/>
${blockSeparator}
${isInitialSetup ? '' : `
<block type="operator_join">
<value name="STRING1">
<shadow type="text">
<field name="TEXT">${apple} </field>
</shadow>
</value>
<value name="STRING2">
<shadow type="text">
<field name="TEXT">${banana}</field>
</shadow>
</value>
</block>
<block type="operator_letter_of">
<value name="LETTER">
<shadow type="math_whole_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="STRING">
<shadow type="text">
<field name="TEXT">${apple}</field>
</shadow>
</value>
</block>
<block type="operator_length">
<value name="STRING">
<shadow type="text">
<field name="TEXT">${apple}</field>
</shadow>
</value>
</block>
<block type="operator_contains" id="operator_contains">
<value name="STRING1">
<shadow type="text">
<field name="TEXT">${apple}</field>
</shadow>
</value>
<value name="STRING2">
<shadow type="text">
<field name="TEXT">${letter}</field>
</shadow>
</value>
</block>
`}
${blockSeparator}
<block type="operator_mod">
<value name="NUM1">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
<value name="NUM2">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
</block>
<block type="operator_round">
<value name="NUM">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
</block>
${blockSeparator}
<block type="operator_mathop">
<value name="NUM">
<shadow type="math_number">
<field name="NUM"/>
</shadow>
</value>
</block>
${categorySeparator}
<block type="operator_pi"/>
<block type="operator_newline"/>
</category>
`;
};
const variables = function (isInitialSetup, isStage, targetId, colors) {
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category
name="%{BKY_CATEGORY_VARIABLES}"
id="variables"
colour="${colors.primary}"
secondaryColour="${colors.tertiary}"
custom="VARIABLE">
</category>
`;
};
const myBlocks = function (isInitialSetup, isStage, targetId, colors) {
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category
name="%{BKY_CATEGORY_MYBLOCKS}"
id="myBlocks"
colour="${colors.primary}"
secondaryColour="${colors.tertiary}"
custom="PROCEDURE">
</category>
`;
};
// eslint-disable-next-line max-len
const extraTurboWarpBlocks = `
<block type="argument_reporter_boolean"><field name="VALUE">is compiled?</field></block>
<block type="argument_reporter_boolean"><field name="VALUE">is TurboWarp?</field></block>
`;
/* eslint-enable no-unused-vars */
const xmlOpen = '<xml style="display: none">';
const xmlClose = '</xml>';
/**
* @param {!boolean} isInitialSetup - Whether the toolbox is for initial setup. If the mode is "initial setup",
* blocks with localized default parameters (e.g. ask and wait) should not be loaded. (LLK/scratch-gui#5445)
* @param {?boolean} isStage - Whether the toolbox is for a stage-type target. This is always set to true
* when isInitialSetup is true.
* @param {?string} targetId - The current editing target
* @param {?Array.<object>} categoriesXML - optional array of `{id,xml}` for categories. This can include both core
* and other extensions: core extensions will be placed in the normal Scratch order; others will go at the bottom.
* @property {string} id - the extension / category ID.
* @property {string} xml - the `<category>...</category>` XML for this extension / category.
* @param {?string} costumeName - The name of the default selected costume dropdown.
* @param {?string} backdropName - The name of the default selected backdrop dropdown.
* @param {?string} soundName - The name of the default selected sound dropdown.
* @param {?object} colors - The colors for the theme.
* @returns {string} - a ScratchBlocks-style XML document for the contents of the toolbox.
*/
const makeToolboxXML = function (isInitialSetup, isStage = true, targetId, categoriesXML = [],
costumeName = '', backdropName = '', soundName = '', colors = defaultBlockColors) {
isStage = isInitialSetup || isStage;
const gap = [categorySeparator];
costumeName = xmlEscape(costumeName);
backdropName = xmlEscape(backdropName);
soundName = xmlEscape(soundName);
categoriesXML = categoriesXML.slice();
const moveCategory = categoryId => {
const index = categoriesXML.findIndex(categoryInfo => categoryInfo.id === categoryId);
if (index >= 0) {
// remove the category from categoriesXML and return its XML
const [categoryInfo] = categoriesXML.splice(index, 1);
return categoryInfo.xml;
}
// return `undefined`
};
const motionXML = moveCategory('motion') || motion(isInitialSetup, isStage, targetId, colors.motion);
const looksXML = moveCategory('looks') ||
looks(isInitialSetup, isStage, targetId, costumeName, backdropName, colors.looks);
const soundXML = moveCategory('sound') || sound(isInitialSetup, isStage, targetId, soundName, colors.sounds);
const eventsXML = moveCategory('event') || events(isInitialSetup, isStage, targetId, colors.event);
const controlXML = moveCategory('control') || control(isInitialSetup, isStage, targetId, colors.control);
const sensingXML = moveCategory('sensing') || sensing(isInitialSetup, isStage, targetId, colors.sensing);
const operatorsXML = moveCategory('operators') || operators(isInitialSetup, isStage, targetId, colors.operators);
const variablesXML = moveCategory('data') || variables(isInitialSetup, isStage, targetId, colors.data);
const myBlocksXML = moveCategory('procedures') || myBlocks(isInitialSetup, isStage, targetId, colors.more);
// Always display TurboWarp blocks as the first extension, if it exists,
// and also add an "is compiled?" block to the top.
let turbowarpXML = moveCategory('tw');
if (turbowarpXML && !turbowarpXML.includes(extraTurboWarpBlocks)) {
turbowarpXML = turbowarpXML.replace('<block', `${extraTurboWarpBlocks}<block`);
}
const everything = [
xmlOpen,
motionXML, gap,
looksXML, gap,
soundXML, gap,
eventsXML, gap,
controlXML, gap,
sensingXML, gap,
operatorsXML, gap,
variablesXML, gap,
myBlocksXML
];
if (turbowarpXML) {
everything.push(gap, turbowarpXML);
}
for (const extensionCategory of categoriesXML) {
everything.push(gap, extensionCategory.xml);
}
everything.push(xmlClose);
return everything.join('\n');
};
export default makeToolboxXML;