blockly-proceds
Advanced tools
Comparing version 1.0.6 to 1.0.7
{ | ||
"name": "blockly-proceds", | ||
"version": "1.0.6", | ||
"version": "1.0.7", | ||
"description": "A Blockly plugin.", | ||
@@ -5,0 +5,0 @@ "scripts": { |
@@ -1,51 +0,21 @@ | ||
# blockly-my-plugin [![Built on Blockly](https://tinyurl.com/built-on-blockly)](https://github.com/google/blockly) | ||
¡Hola! :vulcan_salute: Este es un proyecto relacionado a [Pilas Bloques](https://pilasbloques.program.ar) :heart:. En el repositorio de ese proyecto encontrarás las guías sobre [cómo contribuir](https://github.com/Program-AR/pilas-bloques-app/blob/develop/CONTRIBUTING.md) y el [código de conducta](https://github.com/Program-AR/pilas-bloques-app/blob/develop/CODE_OF_CONDUCT.md), que son guías que aplican también a este proyecto. | ||
<!-- | ||
- TODO: Edit plugin description. | ||
--> | ||
Hi! :vulcan_salute: This is a project related to [Pilas Bloques](https://pilasbloques.program.ar) :heart:. In that project's repository you'll find the [contribution guidelines](https://github.com/Program-AR/pilas-bloques-app/blob/develop/CONTRIBUTING_en.md) and the [code of conduct](https://github.com/Program-AR/pilas-bloques-app/blob/develop/CODE_OF_CONDUCT_en.md) which also apply to this project. | ||
A [Blockly](https://www.npmjs.com/package/blockly) plugin that ... | ||
# Blockly Proceds | ||
## Installation | ||
This Blockly plugin installs new custom procedures for blockly, with custom modifications: | ||
- The parameters now can be easily added and removed with buttons, replacing the mutator popup. | ||
- The arguments (`variables_get`) are associated with the procedure. All this blocks now have a `$parent` field with the id of the procedure where they belong. **For this to work, you must save this `$parent` in the mutation of `variables_get`**. | ||
- The 'help' option was removed from the context menu. | ||
- Procedure descriptions are disabled by default. | ||
### Yarn | ||
``` | ||
yarn add blockly-my-plugin | ||
``` | ||
### npm | ||
``` | ||
npm install blockly-my-plugin --save | ||
``` | ||
## Usage | ||
<!-- | ||
- TODO: Update usage. | ||
--> | ||
If you desire to use a different language than Spanish, you can define your own translations for the needed blocks using `Blockly.Msg`, for example: | ||
```js | ||
import * as Blockly from 'blockly'; | ||
import {Plugin} from 'blockly-my-plugin'; | ||
// Inject Blockly. | ||
const workspace = Blockly.inject('blocklyDiv', { | ||
toolbox: toolboxCategories, | ||
}); | ||
// Initialize plugin. | ||
const plugin = new Plugin(workspace); | ||
plugin.init(); | ||
```javascript | ||
Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT = 'your translation'; | ||
``` | ||
## API | ||
<!-- | ||
- TODO: describe the API. | ||
--> | ||
## License | ||
Apache 2.0 | ||
Then proceed to call `ProcedsBlocklyInit()` to load these new translations. Otherwise, you can use the default language by calling `setDefacultLocale()`: |
declare module 'blockly-proceds' { | ||
export function ProcedsBlocklyInit(blockly: any): void; | ||
export function setDefaultLocale(): void; | ||
} |
344
src/index.js
import 'blockly/blocks'; | ||
import { makeProcedureInit, makeProcedureCustomMenu, makeProcedureDomToMutation, disableContextMenuOptions } from './procedures'; | ||
const PLUS = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAQAAAD2e2DtAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAHdElNRQfhDAUCCjFLV0NqAAAC60lEQVR42u3dQW7aQABA0Wl7MMjJICeDnIwuqm4qVQrYjMH/Pa/jsfFnTJDwjAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBUP7Y+gClOY4zznX9zHmN8bn3gLHcYtwXbYevDZ5nLost/G7dx2foUeNzyyy+BN7Zs8ncjeHvrvP/NAW9qvff/rueAn1sfwNMcX3hvL2S/3wPcVt7fTl+p/c4AfIsA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4mYHcBinVRdz+v+2tjlHfdrv8lRjHFZcyG3P22VmBPOWQrrsd+WtJ7iOjzkDzQrA5b/XpATmBHAY1ynj7MtxfD1/kDkBrP+RrGHC1ZnxX8Bpwhj7NOGV8z1A3IxbgBvA455+fcwAcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcT9mjLKcevTfFPn5/860AwQ58ehr2wnPw51C3jMccYgcwL48nyAu11nPB3AI2Je1bRHxMz7EPgxjuaBb7mO46zLP3MG+OMwjuM8ecx3cp419f81O4B51v7PY6evlO8B4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQN2fp2G0cV9zXhEVct7HfGeD6wntjisu4rbRdtj4VHnFYLYDD1qfCY9aZA7z/39jyBFz+N7fsRrD7yX+n62H+4zTG3QvWnscYn1sfOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALvzG8Ijm7EmMQYoAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTEyLTA1VDAyOjEwOjQ5LTA1OjAwJa2zowAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMi0wNVQwMjoxMDo0OS0wNTowMFTwCx8AAAAASUVORK5CYII=" | ||
const MINUS = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAQAAAD2e2DtAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAHdElNRQfhDAUCCi+xWH4JAAABcUlEQVR42u3c7ZGCMBSG0etuYcTKls7AyrSEVWd4+bjnUECMeSbhD6kCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBzu4XHm2rUvPekD2yutR57/4itTLXU0/Pvs9SUW5TcDrDUyE3r9Na6ZwZKBWD5PxVKIBPAVGtknGsZibeBTADPyCjXE1idn8A0/gJjXFPgn0sEwIEljgAHwPc2Xx87QHMCaE4AzQmgOQE0J4DmBNCcAJoTQHMCaE4AzQmgOQE0J4DmBNCcAJoTQHMCaE4AzQmgOQE0J4DmBNDcb2SUsfc0T2re/utAO0BzPg49sot8HOoI+M5IDJIJ4OF+gI+F7gpyRcwxxa6Iyb0E3mvYB96y1kgtv2vijubS18QBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAWXq7xrTQhKAi3AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTEyLTA1VDAyOjEwOjQ3LTA1OjAwdZLI/gAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMi0wNVQwMjoxMDo0Ny0wNTowMATPcEIAAAAASUVORK5CYII=" | ||
const HAND = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfiBAUBKBeKSgeBAAABTElEQVQoz22QzyvDcRjHX5/vvrMyStI2uZgftdVCSpJCyW1y00oUF+Xg4OIkx5VyUyJOsgv/gnJw4YCSSFMyB5pGbLJ99/k8LltreB2f9+v50eOigoc5FvGTJF8pugCwaKaNaE98uf9zMBXwjtNpbvmuaH2B0+HXuuyMEbOlQ4U1M5ZVcYbowg02YfZW5cnE9JIROdI7jsiOacoMPLZdEYPR1ouQkxCRG+feESlIXkQOzLR+NhvCpc3UbPcHAoRsADcAQ0Twq0ZosQgEVY0SqvGpkAUCxkIUYRP4bZRRNi9pvaD+TwXyFseHuYzy/Kt8CWfQ5Ems6C/5y7uZyDEH0Nt8vq0dUx0XzaapPyIIoIi23+47+SrlTvrTjLhKy2wmO95OqiacS+QBH9gAFDlOZnYbrimWOixSZCwUlK+vZd7bXiPldyi0yqX1OtkfCBS/9XAtDKAAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTgtMDQtMDVUMDQ6NDQ6NDItMDM6MDD+uUN1AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTA0LTA1VDA0OjQwOjIzLTAzOjAw5hdZgAAAAABJRU5ErkJggg==" | ||
// ----------------------- | ||
// [!] Custom context menu | ||
// ----------------------- | ||
const makeProcedureCustomMenu = () => { | ||
return function (options) { | ||
// Add options to create getters for each parameter. | ||
if (!this.isCollapsed()) { | ||
for (var i = this.arguments_.length - 1; i >= 0; i--) { | ||
var option = { enabled: true }; | ||
var name = this.arguments_[i]; | ||
option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name); | ||
option.callback = createParameterCaller(this, name); | ||
options.unshift(option); | ||
} | ||
} | ||
// Add option to create caller. | ||
var option = { enabled: true }; | ||
var name = this.getFieldValue('NAME'); | ||
option.text = Blockly.Msg.PROCEDURES_CREATE_DO.replace('%1', name); | ||
var xmlMutation = document.createElement('mutation'); | ||
xmlMutation.setAttribute('name', name); | ||
for (var i = 0; i < this.arguments_.length; i++) { | ||
var xmlArg = document.createElement('arg'); | ||
xmlArg.setAttribute('name', this.arguments_[i]); | ||
xmlMutation.appendChild(xmlArg); | ||
} | ||
var xmlBlock = document.createElement('block', null, xmlMutation); | ||
xmlBlock.setAttribute('type', this.callType_); | ||
option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); | ||
options.unshift(option); // [!] | ||
options.pop(); // [!] Remove help | ||
}; | ||
}; | ||
const makeProcedureDomToMutation = () => { | ||
return function (xmlElement) { | ||
this.arguments_ = []; | ||
for (var i = 0, childNode; childNode = xmlElement.childNodes[i]; i++) { | ||
if (childNode.nodeName.toLowerCase() == 'arg') { | ||
this.arguments_.push(childNode.getAttribute('name')); | ||
} | ||
} | ||
this.updateParams_(); | ||
Blockly.Procedures.mutateCallers(this); | ||
// Show or hide the statement input. | ||
this.setStatements_(xmlElement.getAttribute('statements') !== 'false'); | ||
this.arguments_.forEach(function (name, i) { // [!] | ||
addParameter(this, i, name); | ||
}.bind(this)); | ||
}; | ||
} | ||
export const ProcedsBlocklyInit = (Blockly) => { | ||
@@ -114,262 +48,18 @@ | ||
const disableContextMenuOptions = (Blockly) => { | ||
const helpOption = Blockly.ContextMenuRegistry.registry.getItem('blockHelp') | ||
const duplicateOption = Blockly.ContextMenuRegistry.registry.getItem('blockDuplicate') | ||
const disableOption = Blockly.ContextMenuRegistry.registry.getItem('blockDisable') | ||
disableOption.preconditionFn = () => 'disable' | ||
duplicateOption.preconditionFn = () => 'disable' | ||
helpOption.preconditionFn = () => 'disabled' | ||
} | ||
export const allProcedures = (workspace) => workspace.getAllBlocks().filter(isProcedure) | ||
const getName = (procedureBlock) => procedureBlock.getFieldValue('NAME') | ||
const isProcedure = (block) => block.type === 'procedures_defnoreturn' | ||
export const allProcedureNames = (workspace) => allProcedures(workspace).map(getName) | ||
export const findLegalName = (name, block, index, workspace) => { | ||
const newName = index === 0 ? name : (name + index) | ||
return allProcedureNames(workspace).includes(newName) ? findLegalName(name, block, index + 1, workspace) : newName; | ||
} | ||
const makeProcedureInit = ( | ||
Blockly, | ||
block, | ||
withParameters = false, | ||
defaultName, | ||
title, | ||
comment, | ||
tooltip, | ||
helpUrl, | ||
) => { | ||
var defaultLegalName = Blockly.Procedures.findLegalName(defaultName, block); | ||
var nameField = new Blockly.FieldTextInput(defaultLegalName, Blockly.Procedures.rename); | ||
nameField.setSpellcheck(false); | ||
// [!] | ||
var addParameterButton = new Blockly.FieldImage( | ||
PLUS, | ||
16, | ||
16, | ||
Blockly.Msg.PROCEDURES_ADD_PARAMETER, | ||
() => addParameter(block, Blockly, undefined) | ||
) | ||
var input = block.appendDummyInput() | ||
.appendField(title) | ||
.appendField(nameField, 'NAME') | ||
.appendField('', 'PARAMS') | ||
block.appendStatementInput("STACK").setCheck(null) | ||
if (withParameters) | ||
input.appendField(addParameterButton); | ||
if ((block.workspace.options.comments || | ||
(block.workspace.options.parentWorkspace && | ||
block.workspace.options.parentWorkspace.options.comments)) && | ||
comment) { | ||
block.setCommentText(comment); | ||
} | ||
block.setCommentText(null); | ||
block.setColour(290); | ||
block.setTooltip(tooltip); | ||
block.setHelpUrl(helpUrl); | ||
block.arguments_ = []; | ||
block.statementConnection_ = null; | ||
// [!] adding create call button | ||
var createCallButton = new Blockly.FieldImage( | ||
HAND, | ||
16, | ||
16, | ||
"", | ||
() => createCall(block, Blockly)) | ||
input.appendField(createCallButton); | ||
}; | ||
const getAvailableName = (block, name) => { | ||
const isTaken = block.arguments_.some(arg => arg === name); | ||
return isTaken ? getAvailableName(block, name + "_") : name | ||
} | ||
const addParameter = (self, Blockly, argName) => { | ||
const argsAmount = self.arguments_.length | ||
const defaultName = argName || Blockly.Msg.PROCEDURES_PARAMETER + " " + (argsAmount + 1); | ||
const name = getAvailableName(self, defaultName); | ||
const id = "INPUTARG" + argsAmount; | ||
self.arguments_.push(name); | ||
self.updateParams_(); | ||
var blocks = self.workspace.getAllBlocks(); | ||
blocks.forEach(block => { | ||
if (block.type === self.callType_ && block.getProcedureCall() === self.getProcedureDef()[0]) { | ||
block.arguments_.push(name); | ||
block.updateShape_(); | ||
} | ||
}) | ||
const callers = () => Blockly.Procedures.getCallers(self.getFieldValue('NAME'), self.workspace); | ||
callers().forEach(caller => { | ||
caller.arguments_.push(name); | ||
caller.updateShape_() | ||
}) | ||
const createCallButton = new Blockly.FieldImage( | ||
HAND, | ||
16, | ||
16, | ||
Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name), | ||
() => createParameterCaller(self, self.arguments_[argsAmount], Blockly) | ||
) | ||
const removeParameterButton = new Blockly.FieldImage( | ||
MINUS, | ||
16, | ||
16, | ||
Blockly.Msg.PROCEDURES_REMOVE_PARAMETER, | ||
() => removeParameter(self, argsAmount, Blockly) | ||
) | ||
const nameField = new Blockly.FieldTextInput(name, function (newName) { | ||
const oldName = self.arguments_[argsAmount]; | ||
if (oldName !== newName) | ||
newName = getAvailableName(self, newName); | ||
self.arguments_[argsAmount] = newName; | ||
callers().forEach(caller => { | ||
caller.arguments_ = caller.arguments_.map(argName => argName === oldName ? newName : argName) | ||
caller.updateShape_() | ||
}) | ||
const varBlocks = self.workspace.getAllBlocks().filter(block => block.type === "variables_get" && block.$parent === self.id) | ||
varBlocks.forEach(varBlock => { | ||
var varField = varBlock.getField("VAR"); | ||
if (varField.getValue() === oldName) { | ||
varField.setValue(newName) | ||
} | ||
}) | ||
return newName; | ||
}); | ||
self | ||
.appendDummyInput(id) | ||
.appendField(Blockly.Msg.PROCEDURES_BEFORE_PARAMS) | ||
.appendField(nameField, 'ARG' + argsAmount) | ||
.appendField(createCallButton) | ||
.appendField(removeParameterButton); | ||
self.moveInputBefore(id, 'STACK'); | ||
} | ||
const removeParameter = (self, argsAmount, Blockly) => { | ||
let arguments_ = self.arguments_ | ||
self.arguments_.forEach((_, i) => self.removeInput("INPUTARG" + i)) | ||
self.arguments_ = [] | ||
arguments_.splice(argsAmount, 1) | ||
const callers = Blockly.Procedures.getCallers(self.getFieldValue('NAME'), self.workspace); | ||
callers.forEach(block => { | ||
block.arguments_ = [] | ||
block.updateShape_(); | ||
}) | ||
arguments_.forEach(arg => addParameter(self, Blockly, arg)) | ||
} | ||
const createCallerXml = (block) => { | ||
var name = block.getFieldValue('NAME'); | ||
var xmlMutation = document.createElement('mutation'); | ||
xmlMutation.setAttribute('name', name); | ||
block.arguments_.forEach((_, i) => { | ||
var xmlArg = document.createElement('arg'); | ||
xmlArg.setAttribute('name', block.arguments_[i]); | ||
xmlMutation.appendChild(xmlArg); | ||
}) | ||
var xmlBlock = document.createElement('block'); | ||
xmlBlock.appendChild(xmlMutation); | ||
xmlBlock.setAttribute('type', 'procedures_callnoreturn'); | ||
return xmlBlock | ||
} | ||
const createCall = (self, Blockly) => { | ||
const block = callbackFactory(self, createCallerXml(self), Blockly) | ||
try { | ||
const procedureBlock = self; | ||
Blockly.Events.disabled_ = 1; | ||
const posParent = procedureBlock.getRelativeToSurfaceXY(); | ||
const pos = block.getRelativeToSurfaceXY(); | ||
let width = procedureBlock.width; | ||
block.moveBy(posParent.x - pos.x + width + 16, posParent.y - pos.y + 6); | ||
} finally { | ||
Blockly.Events.disabled_ = 0; | ||
} | ||
} | ||
const callbackFactory = (block, xml, Blockly) => { | ||
Blockly.Events.disable(); | ||
try { | ||
var newBlock = Blockly.Xml.domToBlock(xml, block.workspace); | ||
// Move the new block next to the old block. | ||
var xy = block.getRelativeToSurfaceXY(); | ||
if (block.RTL) { | ||
xy.x -= Blockly.SNAP_RADIUS; | ||
} else { | ||
xy.x += Blockly.SNAP_RADIUS; | ||
} | ||
xy.y += Blockly.SNAP_RADIUS * 2; | ||
newBlock.moveBy(xy.x, xy.y); | ||
} finally { | ||
Blockly.Events.enable(); | ||
} | ||
newBlock.select(); | ||
return newBlock; | ||
}; | ||
const createParameterCaller = (procedureBlock, name, Blockly) => { | ||
var xmlField = document.createElement('field') | ||
xmlField.textContent = name; | ||
xmlField.setAttribute('name', 'VAR') | ||
var xmlBlock = document.createElement('block') | ||
xmlBlock.appendChild(xmlField) | ||
xmlBlock.setAttribute('type', 'variables_get') | ||
var block = callbackFactory(procedureBlock, xmlBlock, Blockly); | ||
block.$parent = procedureBlock.id; | ||
try { | ||
Blockly.Events.disabled_ = 1; | ||
const posParent = procedureBlock.getRelativeToSurfaceXY(); | ||
const pos = block.getRelativeToSurfaceXY(); | ||
let width = procedureBlock.width; | ||
block.moveBy(posParent.x - pos.x + width + 16, posParent.y - pos.y + 6); | ||
} finally { | ||
Blockly.Events.disabled_ = 0; | ||
} | ||
export const setDefaultLocale = () => { | ||
Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT = 'Describe el procedimiento...'; | ||
Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE = "Hacer algo"; | ||
Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE = "Definir"; | ||
Blockly.Msg.PROCEDURES_DEFNORETURN_NOPARAMS = ""; | ||
Blockly.Msg.PROCEDURES_DEFRETURN_NOPARAMS = ""; | ||
Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT = 'Describe la función...'; | ||
Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE = "devolver algo"; | ||
Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = "Definir"; | ||
Blockly.Msg.PROCEDURES_BEFORE_PARAMS = "con"; | ||
Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP = "Crea un procedimiento."; | ||
Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP = "Crea una función."; | ||
Blockly.Msg.PROCEDURES_ADD_PARAMETER = "Agregar parámetro"; | ||
Blockly.Msg.PROCEDURES_ADD_PARAMETER_PROMPT = "Ingresa el nombre del parámetro"; | ||
Blockly.Msg.PROCEDURES_REMOVE_PARAMETER = "Quitar parámetro"; | ||
Blockly.Msg.PROCEDURES_PARAMETER = "parámetro"; | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
19204
7
328
21