import ScratchBlocks from 'scratch-blocks'; import {defaultColors} from './themes'; const categorySeparator = ''; const blockSeparator = ''; // At default scale, about 28px /* eslint-disable no-unused-vars */ const motion = function (isInitialSetup, isStage, targetId, colors) { const stageSelected = ScratchBlocks.ScratchMsgs.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 ` ${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 (isInitialSetup, isStage, targetId, costumeName, backdropName, colors) { const hello = ScratchBlocks.ScratchMsgs.translate('LOOKS_HELLO', 'Hello!'); const hmm = ScratchBlocks.ScratchMsgs.translate('LOOKS_HMM', 'Hmm...'); // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` ${hello} ${blockSeparator} ${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 (isInitialSetup, isStage, targetId, soundName, colors) { // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` ${soundName} ${soundName} ${blockSeparator} 10 100 ${blockSeparator} -10 100 ${categorySeparator} `; }; 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 ` ${isStage ? ` ` : ` `} ${blockSeparator} 10 ${blockSeparator} ${categorySeparator} `; }; 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 ` 1 ${blockSeparator} 10 ${blockSeparator} ${blockSeparator} ${blockSeparator} ${isStage ? ` ` : ` `} ${categorySeparator} `; }; const sensing = function (isInitialSetup, isStage, targetId, colors) { const name = ScratchBlocks.ScratchMsgs.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 ` ${isStage ? '' : ` ${blockSeparator} `} ${isInitialSetup ? '' : ` ${name} `} ${blockSeparator} ${isStage ? '' : ` ${blockSeparator} ''+ ${blockSeparator} `} ${blockSeparator} ${blockSeparator} ${blockSeparator} ${blockSeparator} ${blockSeparator} ${categorySeparator} `; }; const operators = function (isInitialSetup, isStage, targetId, colors) { 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'); // Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color. return ` ${blockSeparator} 1 10 ${blockSeparator} 50 50 50 ${blockSeparator} ${blockSeparator} ${isInitialSetup ? '' : ` ${apple} ${banana} 1 ${apple} ${apple} ${apple} ${letter} `} ${blockSeparator} ${blockSeparator} ${categorySeparator} `; }; 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 ` `; }; 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 ` `; }; /* eslint-enable no-unused-vars */ const xmlOpen = ''; const xmlClose = ''; /** * @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.} 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. * @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 = defaultColors) { 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); 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;