Skip to content

Instantly share code, notes, and snippets.

@towerofnix
Created October 5, 2018 02:09
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save towerofnix/2cee388117d378a81c3d4482b7c8e304 to your computer and use it in GitHub Desktop.
Save towerofnix/2cee388117d378a81c3d4482b7c8e304 to your computer and use it in GitHub Desktop.
Scratch 3.0 "Text Tools" source code dump
const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type');
const Clone = require('../../util/clone');
const Cast = require('../../util/cast');
const formatMessage = require('format-message');
const MathUtil = require('../../util/math-util');
const Timer = require('../../util/timer');
/**
* Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
* @type {string}
*/
// eslint-disable-next-line max-len
const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTc5MiIgd2lkdGg9IjE3OTIiPjxwYXRoIGQ9Ik0xMjE2IDEyOHEtMzIwIDAtMzIwIDIyNHY0MTZoMTI4djEyOEg4OTZ2NTQ0cTAgMjI0IDMyMCAyMjRoNjR2MTI4aC02NHEtMjcyIDAtMzg0LTE0Ni0xMTIgMTQ2LTM4NCAxNDZoLTY0di0xMjhoNjRxMzIwIDAgMzIwLTIyNFY4OTZINjQwVjc2OGgxMjhWMzUycTAtMjI0LTMyMC0yMjRoLTY0VjBoNjRxMjcyIDAgMzg0IDE0NlE5NDQgMCAxMjE2IDBoNjR2MTI4aC02NHoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=';
// https://www.iconfinder.com/icons/1608366/cursor_i_icon
/**
* Icon svg to be displayed in the category menu, encoded as a data URI.
* @type {string}
*/
// eslint-disable-next-line max-len
const menuIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2LjA5IDEyLjkzN2MuMjI4IDEuMTQxLS44MzMgMi4wNjMtMi4zNzMgMi4wNjMtMS41MzUgMC0yLjk2Mi0uOTIyLTMuMTg2LTIuMDYzLS4yMy0xLjE0Mi44MzMtMi4wNjggMi4zNzItMi4wNjguMzIzIDAgLjY0MS4wNDIuOTQ1LjExN2EzLjUgMy41IDAgMCAxIC40NjguMTUxYy40MzUtLjAxLS4wNTItMS4xNDctLjkxNy02LjExNC0xLjA2Ny02LjE1MiAxLjUzLS45MzUgNC4zODQtMS4zNzcgMi44NTQtLjQ0Mi4wMzggMi40MS0xLjgyNSAxLjkyMi0xLjg2Mi0uNDkzLTIuMzI1LTMuNTc3LjEzMiA3LjM3ek03LjQ2IDguNTYzYy0xLjg2Mi0uNDkzLTIuMzI1LTMuNTc2LjEzIDcuMzdDNy44MTYgMTcuMDczIDYuNzU0IDE4IDUuMjIgMThjLTEuNTM1IDAtMi45NjEtLjkyNi0zLjE5LTIuMDY4LS4yMjQtMS4xNDIuODM3LTIuMDY3IDIuMzc1LTIuMDY3LjUwMSAwIC45ODcuMDk4IDEuNDI3LjI3Mi40MTItLjAyOC0uMDc0LTEuMTg5LS45My02LjExNEMzLjgzNCAxLjg3IDYuNDMgNy4wODcgOS4yODIgNi42NDZjMi44NTQtLjQ0Ny4wMzggMi40MS0xLjgyMyAxLjkxN3oiIGZpbGw9IiM1NzVFNzUiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==';
/**
* Class for the "Text Tools" extension's blocks in Scratch 3.0
* @param {Runtime} runtime - the runtime instantiating this block package.
* @constructor
*/
class Scratch3TextToolsBlocks {
constructor (runtime) {
/**
* The runtime instantiating this block package.
* @type {Runtime}
*/
this.runtime = runtime;
}
/**
* @returns {object} metadata for this extension and its blocks.
*/
getInfo () {
return {
id: 'textTools',
name: formatMessage({
id: 'textTools.categoryName',
default: 'Text Tools',
description: 'Label for the Text Tools extension category'
}),
menuIconURI: menuIconURI,
blockIconURI: blockIconURI,
blocks: [
{
opcode: 'replaceSubstring',
blockType: BlockType.REPORTER,
text: formatMessage({
id: 'textTools.replaceSubstring',
default: 'replace [WHICH] [BEFORE] with [AFTER] in [STRING]',
description: 'replace text in a string'
}),
arguments: {
WHICH: {
type: ArgumentType.STRING,
menu: 'REPLACE_WHICH',
defaultValue: 'every'
},
BEFORE: {
type: ArgumentType.STRING,
defaultValue: 'world'
},
AFTER: {
type: ArgumentType.STRING,
defaultValue: 'friend'
},
STRING: {
type: ArgumentType.STRING,
defaultValue: 'hello world'
}
}
},
{
opcode: 'getSubstring',
blockType: BlockType.REPORTER,
text: formatMessage({
id: 'textTools.getSubstring',
default: 'letters [START] to [END] of [STRING]',
description: 'get letters from a range of a string'
}),
arguments: {
START: {
type: ArgumentType.NUMBER,
defaultValue: 1
},
END: {
type: ArgumentType.NUMBER,
defaultValue: 5
},
STRING: {
type: ArgumentType.STRING,
defaultValue: 'octopus'
}
}
},
{
opcode: 'locationOfSubstring',
blockType: BlockType.REPORTER,
text: formatMessage({
id: 'textTools.locationOfSubstring',
default: 'location of [SUBSTRING] in [STRING]',
description: 'get location (index) of one string in another'
}),
arguments: {
SUBSTRING: {
type: ArgumentType.STRING,
defaultValue: 'eat'
},
STRING: {
type: ArgumentType.STRING,
defaultValue: 'wheat'
}
}
},
{
opcode: 'caseSensitiveEqual',
blockType: BlockType.BOOLEAN,
text: formatMessage({
id: 'textTools.caseSensitiveEqual',
default: '[A] = [B] and is the same case',
description: 'check if two strings are equal and capitalized the same way'
}),
arguments: {
A: {
type: ArgumentType.STRING,
defaultValue: 'library'
},
B: {
type: ArgumentType.STRING,
defaultValue: 'liBRARY'
}
}
}
],
menus: {
REPLACE_WHICH: [
{text: 'every', value: 'every'},
{text: 'first', value: 'first'},
{text: 'last', value: 'last'}
]
}
};
}
replaceSubstring(args) {
const which = Cast.toString(args.WHICH);
const before = Cast.toString(args.BEFORE);
const after = Cast.toString(args.AFTER);
const string = Cast.toString(args.STRING);
const split = string.split(before);
if (split.length === 1) {
// The "before" string doesn't show up at all, so don't do anything.
return string;
} else if (which === 'every') {
return split.join(after);
} else if (which === 'first') {
return split[0] + after + split.slice(1).join(before);
} else if (which === 'last') {
return split.slice(0, -1).join(before) + after + split[split.length - 1];
} else {
// We got passed an invalid menu option, so don't do anything to the string.
return string;
}
}
getSubstring(args) {
let start = Cast.toNumber(args.START);
let end = Cast.toNumber(args.END);
const string = Cast.toString(args.STRING);
// Slice expects 0-indexed, but Scratch is 1-indexed. So first we subtract 1 from start and end:
// * start = start - 1
// * end = end - 1
// Also, we want to return the range INCLUSIVE of both the start and the end (octopus [0 .. 5] = octop), but
// slice is only inclusive of the start. So we add one to the end:
// * start = start - 1
// * end = end
// So we don't change end.
// Only decrement start if it is positive. Negative start already behaves like we want it to.
// ("start = -N" means 'starting from the Nth character from the end", where -1 = last character.)
if (start > 0) {
start = start - 1;
}
// If end is negative, we have to increment it so that slice is inclusive of the character at the end index.
// In this block, "end = -1" means "until and including the last character", but once we increment -1, we have
// 0, and "slice(N, 0)" means "until but not including the last character", so in that case we have to change
// end to be the (positive) length of the string.
if (end < 0) {
end = end + 1;
if (end === 0) {
end = string.length;
}
}
return string.slice(start, end);
}
locationOfSubstring(args) {
const substring = Cast.toString(args.SUBSTRING);
const string = Cast.toString(args.STRING);
return string.indexOf(substring) + 1;
}
caseSensitiveEqual(args) {
const a = Cast.toString(args.A);
const b = Cast.toString(args.B);
return a === b;
}
}
module.exports = Scratch3TextToolsBlocks;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment