import ScratchBlocks from 'scratch-blocks'; const categorySeparator = ''; const blockSeparator = ''; // At default scale, about 28px const motion = function (isStage, targetId) { const stageSelected = ScratchBlocks.ScratchMsgs.translate( 'MOTION_STAGE_SELECTED', 'Stage selected: no motion blocks' ); return ` ${isStage ? ` ` : ` 10 15 15 ${blockSeparator} 0 0 1 1 0 0 ${blockSeparator} 90 ${blockSeparator} 10 0 10 0 ${blockSeparator} ${blockSeparator} ${blockSeparator} `} ${categorySeparator} `; }; const xmlEscape = function (unsafe) { return unsafe.replace(/[<>&'"]/g, c => { switch (c) { case '<': return '<'; case '>': return '>'; case '&': return '&'; case '\'': return '''; case '"': return '"'; } }); }; const looks = function (isStage, targetId, costumeName, backdropName) { const hello = ScratchBlocks.ScratchMsgs.translate('LOOKS_HELLO', 'Hello!'); const hmm = ScratchBlocks.ScratchMsgs.translate('LOOKS_HMM', 'Hmm...'); return ` ${isStage ? '' : ` ${hello} 2 ${hello} ${hmm} 2 ${hmm} ${blockSeparator} `} ${isStage ? ` ${backdropName} ${backdropName} ` : ` ${costumeName} ${backdropName} ${blockSeparator} 10 100 `} ${blockSeparator} 25 0 ${blockSeparator} ${isStage ? '' : ` ${blockSeparator} 1 `} ${isStage ? ` ` : ` `} ${categorySeparator} `; }; const sound = function (isStage, targetId, soundName) { return ` ${soundName} ${soundName} ${blockSeparator} 10 100 ${blockSeparator} -10 100 ${categorySeparator} `; }; const events = function (isStage) { return ` ${isStage ? ` ` : ` `} ${blockSeparator} 10 ${blockSeparator} ${categorySeparator} `; }; const control = function (isStage) { return ` 1 ${blockSeparator} 10 ${blockSeparator} ${blockSeparator} ${blockSeparator} ${isStage ? ` ` : ` `} ${categorySeparator} `; }; const sensing = function (isStage) { const name = ScratchBlocks.ScratchMsgs.translate('SENSING_ASK_TEXT', 'What\'s your name?'); return ` ${isStage ? '' : ` ${blockSeparator} `} ${name} ${blockSeparator} ${isStage ? '' : ` ${blockSeparator} ''+ ${blockSeparator} `} ${blockSeparator} ${blockSeparator} ${blockSeparator} ${blockSeparator} ${blockSeparator} ${categorySeparator} `; }; const operators = function () { const apple = ScratchBlocks.ScratchMsgs.translate('OPERATORS_JOIN_APPLE', 'apple'); const banana = ScratchBlocks.ScratchMsgs.translate('OPERATORS_JOIN_BANANA', 'banana'); const letter = ScratchBlocks.ScratchMsgs.translate('OPERATORS_LETTEROF_APPLE', 'a'); return ` ${blockSeparator} 1 10 ${blockSeparator} 50 50 50 ${blockSeparator} ${blockSeparator} ${apple} ${banana} 1 ${apple} ${apple} ${apple} ${letter} ${blockSeparator} ${blockSeparator} ${categorySeparator} `; }; const variables = function () { return ` `; }; const myBlocks = function () { return ` `; }; const xmlOpen = ''; const xmlClose = ''; /** * @param {!boolean} isStage - Whether the toolbox is for a stage-type target. * @param {?string} targetId - The current editing target * @param {?Array.} 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 `...` 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. * @returns {string} - a ScratchBlocks-style XML document for the contents of the toolbox. */ const makeToolboxXML = function (isStage, targetId, categoriesXML = [], costumeName = '', backdropName = '', soundName = '') { 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(isStage, targetId); const looksXML = moveCategory('looks') || looks(isStage, targetId, costumeName, backdropName); const soundXML = moveCategory('sound') || sound(isStage, targetId, soundName); const eventsXML = moveCategory('event') || events(isStage, targetId); const controlXML = moveCategory('control') || control(isStage, targetId); const sensingXML = moveCategory('sensing') || sensing(isStage, targetId); const operatorsXML = moveCategory('operators') || operators(isStage, targetId); const variablesXML = moveCategory('data') || variables(isStage, targetId); const myBlocksXML = moveCategory('procedures') || myBlocks(isStage, targetId); const everything = [ xmlOpen, motionXML, gap, looksXML, gap, soundXML, gap, eventsXML, gap, controlXML, gap, sensingXML, gap, operatorsXML, gap, variablesXML, gap, myBlocksXML ]; for (const extensionCategory of categoriesXML) { everything.push(gap, extensionCategory.xml); } everything.push(xmlClose); return everything.join('\n'); }; export default makeToolboxXML;