pxt-ev3
Version:
LEGO MINDSTORMS EV3 for Microsoft MakeCode
784 lines (777 loc) • 37 kB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
/// <reference path="../node_modules/pxt-core/localtypings/pxteditor.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
Object.defineProperty(exports, "__esModule", { value: true });
const field_ports_1 = require("./field_ports");
const field_motors_1 = require("./field_motors");
const field_brickbuttons_1 = require("./field_brickbuttons");
const field_color_1 = require("./field_color");
const field_music_1 = require("./field_music");
pxt.editor.initFieldExtensionsAsync = function (opts) {
pxt.debug('loading pxt-ev3 target extensions...');
updateBlocklyShape();
const res = {
fieldEditors: [{
selector: "ports",
editor: field_ports_1.FieldPorts
}, {
selector: "motors",
editor: field_motors_1.FieldMotors
}, {
selector: "brickbuttons",
editor: field_brickbuttons_1.FieldBrickButtons
}, {
selector: "colorenum",
editor: field_color_1.FieldColorEnum
}, {
selector: "music",
editor: field_music_1.FieldMusic
}]
};
return Promise.resolve(res);
};
/**
* Update the shape of Blockly blocks with square corners
*/
function updateBlocklyShape() {
/**
* Rounded corner radius.
* @const
*/
Blockly.BlockSvg.CORNER_RADIUS = 0 * Blockly.BlockSvg.GRID_UNIT;
/**
* Inner space between edge of statement input and notch.
* @const
*/
Blockly.BlockSvg.STATEMENT_INPUT_INNER_SPACE = 3 * Blockly.BlockSvg.GRID_UNIT;
/**
* SVG path for drawing next/previous notch from left to right.
* @const
*/
Blockly.BlockSvg.NOTCH_PATH_LEFT = ('l 8,8 ' +
'h 16 ' +
'l 8,-8 ');
/**
* SVG path for drawing next/previous notch from right to left.
* @const
*/
Blockly.BlockSvg.NOTCH_PATH_RIGHT = ('l -8,8 ' +
'h -16 ' +
'l -8,-8 ');
/**
* SVG start point for drawing the top-left corner.
* @const
*/
Blockly.BlockSvg.TOP_LEFT_CORNER_START =
'm 0,' + 0;
/**
* SVG path for drawing the rounded top-left corner.
* @const
*/
Blockly.BlockSvg.TOP_LEFT_CORNER =
'l ' + Blockly.BlockSvg.CORNER_RADIUS + ',0 ';
/**
* SVG path for drawing the rounded top-right corner.
* @const
*/
Blockly.BlockSvg.TOP_RIGHT_CORNER =
'l ' + 0 + ',' + Blockly.BlockSvg.CORNER_RADIUS;
/**
* SVG path for drawing the rounded bottom-right corner.
* @const
*/
Blockly.BlockSvg.BOTTOM_RIGHT_CORNER =
'l 0,' + Blockly.BlockSvg.CORNER_RADIUS;
/**
* SVG path for drawing the rounded bottom-left corner.
* @const
*/
Blockly.BlockSvg.BOTTOM_LEFT_CORNER =
'l -' + Blockly.BlockSvg.CORNER_RADIUS + ',0';
/**
* SVG path for drawing the top-left corner of a statement input.
* @const
*/
Blockly.BlockSvg.INNER_TOP_LEFT_CORNER =
'l ' + Blockly.BlockSvg.CORNER_RADIUS + ',-' + 0;
/**
* SVG path for drawing the bottom-left corner of a statement input.
* Includes the rounded inside corner.
* @const
*/
Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER =
'l ' + 0 + ',' + Blockly.BlockSvg.CORNER_RADIUS * 2 +
'l ' + Blockly.BlockSvg.CORNER_RADIUS + ',' + 0;
/**
* Corner radius of the flyout background.
* @type {number}
* @const
*/
Blockly.Flyout.prototype.CORNER_RADIUS = 0;
/**
* Margin around the edges of the blocks in the flyout.
* @type {number}
* @const
*/
Blockly.Flyout.prototype.MARGIN = 8;
}
// When require()d from node, bind the global pxt namespace
// namespace pxt {
// export const dummyExport = 1;
// }
// eval("if (typeof process === 'object' && process + '' === '[object process]') pxt = global.pxt")
},{"./field_brickbuttons":2,"./field_color":3,"./field_motors":4,"./field_music":5,"./field_ports":6}],2:[function(require,module,exports){
"use strict";
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
Object.defineProperty(exports, "__esModule", { value: true });
exports.FieldBrickButtons = void 0;
class FieldBrickButtons extends Blockly.FieldDropdown {
constructor(text, options, validator) {
super(options.data);
this.isFieldCustom_ = true;
/**
* Callback for when a button is clicked inside the drop-down.
* Should be bound to the FieldIconMenu.
* @param {Event} e DOM event for the click/touch
* @private
*/
this.buttonClick_ = function (e) {
let value = e.target.getAttribute('data-value');
this.setValue(value);
Blockly.DropDownDiv.hide();
};
/**
* Callback for when the drop-down is hidden.
*/
this.onHide_ = function () {
const content = Blockly.DropDownDiv.getContentDiv();
content.removeAttribute('role');
content.removeAttribute('aria-haspopup');
content.removeAttribute('aria-activedescendant');
content.style.width = '';
// Update color (deselect) on dropdown hide
let source = this.sourceBlock_;
if (source === null || source === void 0 ? void 0 : source.isShadow()) {
source.setColour(this.savedPrimary_);
}
else if (this.borderRect_) {
this.borderRect_.setAttribute('fill', this.savedPrimary_);
}
};
this.columns_ = parseInt(options.columns) || 4;
this.width_ = parseInt(options.width) || 150;
}
/**
* Create a dropdown menu under the text.
* @private
*/
showEditor_() {
// If there is an existing drop-down we own, this is a request to hide the drop-down.
if (Blockly.DropDownDiv.hideIfOwner(this)) {
return;
}
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
Blockly.DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.clearContent();
// Populate the drop-down with the icons for this field.
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
let contentDiv = document.createElement('div');
// Accessibility properties
contentDiv.setAttribute('role', 'menu');
contentDiv.setAttribute('aria-haspopup', 'true');
const buttonsSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
pxsim.svg.hydrate(buttonsSVG, {
viewBox: "0 0 256.68237 256.68237",
width: this.width_,
height: this.width_
});
contentDiv.appendChild(buttonsSVG);
const gWrapper = pxsim.svg.child(buttonsSVG, 'g', { 'transform': 'translate(-4.695057,58.29823)' });
const gInnerWrapper = pxsim.svg.child(gWrapper, 'g', { 'transform': 'translate(3.9780427e-6,-32.677281)' });
const back = pxsim.svg.child(gInnerWrapper, 'path', {
style: 'fill:#6a6a6a;stroke-width:3.91719985',
d: 'M 106.30882,198.38022 C 84.431262,177.26258 50.453467,142.52878 50.453467,142.52878 v -7.12931 H 37.087971 a 32.381533,32.381533 0 1 1 0,-64.763062 H 50.457376 V 63.503186 L 105.71731,7.0563355 h 55.25604 c 25.02699,25.5048885 55.25994,55.2599395 55.25994,55.2599395 v 8.320133 h 12.77398 a 32.381533,32.381533 0 0 1 0,64.763062 h -12.77398 v 7.13323 c -29.43384,30.27603 -54.66454,55.85144 -54.66454,55.85144 z'
});
const buttonLeft = pxsim.svg.child(gInnerWrapper, 'path', {
style: 'fill:#a8a9a8;stroke-width:3.91719985',
d: 'm 36.492567,78.357208 h 40.69971 V 126.48393 H 36.492567 A 24.063359,24.063359 0 0 1 12.429199,102.42057 v 0 A 24.063359,24.063359 0 0 1 36.492567,78.357208 Z'
});
const buttonRight = pxsim.svg.child(gInnerWrapper, 'path', {
style: 'fill:#a8a9a8;stroke-width:3.91719985',
d: 'M 229.00727,126.48784 H 188.30756 V 78.361126 h 40.69971 a 24.063359,24.063359 0 0 1 24.06335,24.063354 v 0 a 24.063359,24.063359 0 0 1 -24.06335,24.06336 z'
});
const buttonEnter = pxsim.svg.child(gInnerWrapper, 'path', {
style: 'fill:#3c3c3c;stroke-width:3.91719985',
d: 'm 109.27806,78.357208 h 46.9398 a 1.782326,1.782326 0 0 1 1.78233,1.782326 V 124.7016 a 1.782326,1.782326 0 0 1 -1.78233,1.78233 h -46.9398 a 1.782326,1.782326 0 0 1 -1.78233,-1.78233 V 80.139534 a 1.782326,1.782326 0 0 1 1.78233,-1.782326 z'
});
const buttonTop = pxsim.svg.child(gInnerWrapper, 'path', {
style: 'fill:#a8a9a8;stroke-width:3.91719985',
d: 'm 108.09114,15.967966 49.90905,-0.59542 37.43276,38.619675 -15.44943,15.449437 V 97.367379 H 165.7249 V 81.306861 A 11.978797,11.978797 0 0 0 153.84012,69.422075 c -11.59883,-0.184102 -43.37516,0 -43.37516,0 A 9.6676495,9.6676495 0 0 0 100.36251,79.520618 V 97.347793 H 86.103905 V 69.422075 L 70.654464,53.97264 Z'
});
const buttonBottom = pxsim.svg.child(gInnerWrapper, 'path', {
style: 'fill:#a8a9a8;stroke-width:3.91719985',
d: 'M 157.78865,189.01028 108.18908,189.38233 70.654464,150.794 86.323259,135.4895 v -28.08625 h 14.101921 v 16.11144 a 12.006218,12.006218 0 0 0 11.85346,11.9788 c 11.59882,0.1841 43.13227,0 43.13227,0 a 10.18472,10.18472 0 0 0 10.38059,-10.38058 v -17.70966 h 14.39179 v 28.08632 l 15.3045,15.3045 z'
});
const buttons = [buttonEnter, buttonLeft, buttonRight, buttonTop, buttonBottom];
const options = this.getOptions();
for (let i = 0, option; option = options[i]; i++) {
let content = options[i][0]; // Human-readable text or image.
const value = options[i][1]; // Language-neutral value.
const button = buttons[i];
button.setAttribute('id', ':' + i); // For aria-activedescendant
button.setAttribute('role', 'menuitem');
button.setAttribute('cursor', 'pointer');
const title = pxsim.svg.child(button, 'title');
title.textContent = content;
Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
// These are applied manually instead of using the :hover pseudoclass
// because Android has a bad long press "helper" menu and green highlight
// that we must prevent with ontouchstart preventDefault
Blockly.bindEvent_(button, 'mousedown', button, function (e) {
this.setAttribute('stroke', '#ffffff');
e.preventDefault();
});
Blockly.bindEvent_(button, 'mouseover', button, function () {
this.setAttribute('stroke', '#ffffff');
});
Blockly.bindEvent_(button, 'mouseout', button, function () {
this.setAttribute('stroke', 'transparent');
});
button.setAttribute('data-value', value);
}
contentDiv.style.width = this.width_ + 'px';
dropdownDiv.appendChild(contentDiv);
Blockly.DropDownDiv.setColour('#ffffff', '#dddddd');
// Position based on the field position.
Blockly.DropDownDiv.showPositionedByField(this, this.onHide_.bind(this));
// Update colour to look selected.
let source = this.sourceBlock_;
this.savedPrimary_ = source === null || source === void 0 ? void 0 : source.getColour();
if (source === null || source === void 0 ? void 0 : source.isShadow()) {
source.setColour(source.getColourTertiary());
}
else if (this.borderRect_) {
this.borderRect_.setAttribute('fill', this.sourceBlock_.getColourTertiary());
}
}
}
exports.FieldBrickButtons = FieldBrickButtons;
},{}],3:[function(require,module,exports){
"use strict";
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
Object.defineProperty(exports, "__esModule", { value: true });
exports.FieldColorEnum = void 0;
class FieldColorEnum extends pxtblockly.FieldColorNumber {
constructor(text, params, opt_validator) {
super(text, params, opt_validator);
this.isFieldCustom_ = true;
this.paramsData = params["data"];
}
mapColour(enumString) {
switch (enumString) {
case '#000000': return 'ColorSensorColor.Black';
case '#006db3': return 'ColorSensorColor.Blue';
case '#00934b': return 'ColorSensorColor.Green';
case '#ffd01b': return 'ColorSensorColor.Yellow';
case '#f12a21': return 'ColorSensorColor.Red';
case '#ffffff': return 'ColorSensorColor.White';
case '#6c2d00': return 'ColorSensorColor.Brown';
default: return 'ColorSensorColor.None';
}
}
mapEnum(colorString) {
switch (colorString) {
case 'ColorSensorColor.Black': return '#000000';
case 'ColorSensorColor.Blue': return '#006db3';
case 'ColorSensorColor.Green': return '#00934b';
case 'ColorSensorColor.Yellow': return '#ffd01b';
case 'ColorSensorColor.Red': return '#f12a21';
case 'ColorSensorColor.White': return '#ffffff';
case 'ColorSensorColor.Brown': return '#6c2d00';
case 'ColorSensorColor.None': return '#dfe6e9';
default: return colorString;
}
}
showEditor_() {
super.showEditor_();
const colorCells = document.querySelectorAll('.legoColorPicker td');
colorCells.forEach((cell) => {
const titleName = this.mapColour(cell.getAttribute("title"));
const index = this.paramsData.findIndex(item => item[1] === titleName);
cell.setAttribute("title", this.paramsData[index][0]);
});
}
/**
* Return the current colour.
* @param {boolean} opt_asHex optional field if the returned value should be a hex
* @return {string} Current colour in '#rrggbb' format.
*/
getValue(opt_asHex) {
const colour = this.mapColour(this.value_);
if (!opt_asHex && colour.indexOf('#') > -1) {
return `0x${colour.replace(/^#/, '')}`;
}
return colour;
}
/**
* Set the colour.
* @param {string} colour The new colour in '#rrggbb' format.
*/
setValue(colorStr) {
let colour = this.mapEnum(colorStr);
if (this.sourceBlock_ && Blockly.Events.isEnabled() &&
this.value_ != colour) {
Blockly.Events.fire(new Blockly.Events.BlockChange(this.sourceBlock_, 'field', this.name, this.value_, colour));
}
this.value_ = colour;
if (this.sourceBlock_) {
this.sourceBlock_.setColour(colour);
}
}
}
exports.FieldColorEnum = FieldColorEnum;
},{}],4:[function(require,module,exports){
"use strict";
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtblocks.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
Object.defineProperty(exports, "__esModule", { value: true });
exports.FieldMotors = void 0;
class FieldMotors extends pxtblockly.FieldImages {
//public shouldSort_: boolean;
constructor(text, options, validator) {
super(text, options, validator);
this.isFieldCustom_ = true;
this.buttonClick_ = function (e) {
let value = e.target.getAttribute('data-value');
this.setValue(value);
Blockly.DropDownDiv.hide();
};
this.columns_ = parseInt(options.columns) || 4;
this.width_ = parseInt(options.width) || 400;
//this.shouldSort_ = options.sort;
this.addLabel_ = true;
this.renderSelectedImage_ = Blockly.FieldDropdown.prototype.renderSelectedText_;
this.updateSize_ = Blockly.Field.prototype.updateSize_;
}
/**
* Create a dropdown menu under the text.
* @private
*/
showEditor_() {
// If there is an existing drop-down we own, this is a request to hide the drop-down.
if (Blockly.DropDownDiv.hideIfOwner(this)) {
return;
}
let sourceBlock = this.sourceBlock_;
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
Blockly.DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.clearContent();
// Populate the drop-down with the icons for this field.
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
let contentDiv = document.createElement('div');
// Accessibility properties
contentDiv.setAttribute('role', 'menu');
contentDiv.setAttribute('aria-haspopup', 'true');
const options = this.getOptions();
//if (this.shouldSort_) options.sort();
for (let i = 0; i < options.length; i++) {
const content = options[i][0]; // Human-readable text or image.
const value = options[i][1]; // Language-neutral value.
// Icons with the type property placeholder take up space but don't have any functionality
// Use for special-case layouts
if (content.type == 'placeholder') {
let placeholder = document.createElement('span');
placeholder.setAttribute('class', 'blocklyDropDownPlaceholder');
placeholder.style.width = content.width + 'px';
placeholder.style.height = content.height + 'px';
contentDiv.appendChild(placeholder);
continue;
}
let button = document.createElement('button');
button.setAttribute('id', ':' + i); // For aria-activedescendant
button.setAttribute('role', 'menuitem');
button.setAttribute('class', 'blocklyDropDownButton');
button.title = content.alt;
if (this.columns_) {
button.style.width = ((this.width_ / this.columns_) - 8) + 'px';
//button.style.height = ((this.width_ / this.columns_) - 8) + 'px';
}
else {
button.style.width = content.width + 'px';
button.style.height = content.height + 'px';
}
let backgroundColor = sourceBlock.getColour();
if (value == this.getValue()) {
// This icon is selected, show it in a different colour
backgroundColor = sourceBlock.getColourTertiary();
button.setAttribute('aria-selected', 'true');
}
button.style.backgroundColor = backgroundColor;
button.style.borderColor = sourceBlock.getColourTertiary();
Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
Blockly.bindEvent_(button, 'mouseover', button, function () {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
contentDiv.setAttribute('aria-activedescendant', this.id);
});
Blockly.bindEvent_(button, 'mouseout', button, function () {
this.setAttribute('class', 'blocklyDropDownButton');
contentDiv.removeAttribute('aria-activedescendant');
});
let buttonImg = document.createElement('img');
buttonImg.src = content.src;
//buttonImg.alt = icon.alt;
// Upon click/touch, we will be able to get the clicked element as e.target
// Store a data attribute on all possible click targets so we can match it to the icon.
button.setAttribute('data-value', value);
buttonImg.setAttribute('data-value', value);
buttonImg.style.height = 'auto';
button.appendChild(buttonImg);
if (this.addLabel_) {
const buttonText = this.createTextNode_(content.alt);
buttonText.setAttribute('data-value', value);
buttonText.style.whiteSpace = 'inherit';
buttonText.style.width = 'auto';
buttonText.style.padding = '0 10px';
button.appendChild(buttonText);
}
contentDiv.appendChild(button);
}
contentDiv.style.width = this.width_ + 'px';
contentDiv.style.display = 'flex';
contentDiv.style.alignItems = 'stretch';
dropdownDiv.appendChild(contentDiv);
Blockly.DropDownDiv.setColour(sourceBlock.getColour(), sourceBlock.getColourTertiary());
// Position based on the field position.
Blockly.DropDownDiv.showPositionedByField(this, this.onHideCallback.bind(this));
// Update colour to look selected.
this.savedPrimary_ = sourceBlock === null || sourceBlock === void 0 ? void 0 : sourceBlock.getColour();
if (sourceBlock === null || sourceBlock === void 0 ? void 0 : sourceBlock.isShadow()) {
sourceBlock.setColour(sourceBlock.style.colourTertiary);
}
else if (this.borderRect_) {
this.borderRect_.setAttribute('fill', sourceBlock.style.colourTertiary);
}
}
trimOptions_() {
}
}
exports.FieldMotors = FieldMotors;
},{}],5:[function(require,module,exports){
"use strict";
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtblocks.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
Object.defineProperty(exports, "__esModule", { value: true });
exports.FieldMusic = void 0;
let soundCache;
let soundIconCache;
let soundIconCacheArray;
class FieldMusic extends pxtblockly.FieldImages {
constructor(text, options, validator) {
super(text, { blocksInfo: options.blocksInfo, sort: true, data: options.data }, validator);
this.isFieldCustom_ = true;
this.buttonClick_ = function (e) {
let value = e.target.getAttribute('data-value');
this.setValue(value);
Blockly.DropDownDiv.hide();
};
this.categoryClick_ = function (e) {
let value = e.target.getAttribute('data-value');
this.setSelectedCategory(value);
const options = this.getOptions();
options.sort();
const categories = this.getCategories(options);
const dropdownDiv = Blockly.DropDownDiv.getContentDiv();
const categoriesDiv = dropdownDiv.childNodes[0];
const contentDiv = dropdownDiv.childNodes[1];
categoriesDiv.innerHTML = '';
contentDiv.innerHTML = '';
this.refreshCategories(categoriesDiv, categories);
this.refreshOptions(contentDiv, options);
this.stopSounds();
};
/**
* Callback for when a button is hovered over inside the drop-down.
* Should be bound to the FieldIconMenu.
* @param {Event} e DOM event for the mouseover
* @private
*/
this.buttonEnter_ = function (value) {
if (soundCache) {
const jresValue = value.substring(value.lastIndexOf('.') + 1);
const buf = soundCache[jresValue];
if (buf) {
const refBuf = {
data: pxt.U.stringToUint8Array(atob(buf))
};
pxsim.AudioContextManager.playBufferAsync(refBuf);
}
}
};
this.buttonLeave_ = function () {
this.stopSounds();
};
this.columns_ = parseInt(options.columns) || 4;
this.width_ = parseInt(options.width) || 450;
//this.setText = Blockly.FieldDropdown.prototype.setText;
this.updateSize_ = Blockly.Field.prototype.updateSize_;
if (!pxt.BrowserUtils.isIE() && !soundCache) {
soundCache = JSON.parse(pxtTargetBundle.bundledpkgs['music']['sounds.jres']);
}
if (!soundIconCache) {
soundIconCache = JSON.parse(pxtTargetBundle.bundledpkgs['music']['icons.jres']);
soundIconCacheArray = Object.entries(soundIconCache).filter(el => el[0] !== "*");
}
}
/**
* Create a dropdown menu under the text.
* @private
*/
showEditor_() {
// If there is an existing drop-down we own, this is a request to hide the drop-down.
if (Blockly.DropDownDiv.hideIfOwner(this)) {
return;
}
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
Blockly.DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.clearContent();
// Populate the drop-down with the icons for this field.
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
let contentDiv = document.createElement('div');
// Accessibility properties
contentDiv.setAttribute('role', 'menu');
contentDiv.setAttribute('aria-haspopup', 'true');
contentDiv.className = 'blocklyMusicFieldOptions';
contentDiv.style.display = "flex";
contentDiv.style.flexWrap = "wrap";
contentDiv.style.float = "none";
const options = this.getOptions();
//options.sort(); // Do not need to use to not apply sorting in different languages
// Create categoies
const categories = this.getCategories(options);
const selectedCategory = this.parseCategory(this.getText());
this.selectedCategory_ = selectedCategory || categories[0];
let categoriesDiv = document.createElement('div');
// Accessibility properties
categoriesDiv.setAttribute('role', 'menu');
categoriesDiv.setAttribute('aria-haspopup', 'true');
categoriesDiv.style.backgroundColor = this.sourceBlock_.getColourTertiary();
categoriesDiv.className = 'blocklyMusicFieldCategories';
this.refreshCategories(categoriesDiv, categories);
this.refreshOptions(contentDiv, options);
contentDiv.style.width = this.width_ + 'px';
contentDiv.style.cssFloat = 'left';
dropdownDiv.style.maxHeight = `410px`;
dropdownDiv.appendChild(categoriesDiv);
dropdownDiv.appendChild(contentDiv);
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
// Position based on the field position.
Blockly.DropDownDiv.showPositionedByField(this, this.onHide_.bind(this));
// Update colour to look selected.
let source = this.sourceBlock_;
this.savedPrimary_ = source === null || source === void 0 ? void 0 : source.getColour();
if (source === null || source === void 0 ? void 0 : source.isShadow()) {
source.setColour(source.getColourTertiary());
}
else if (this.borderRect_) {
this.borderRect_.setAttribute('fill', this.sourceBlock_.getColourTertiary());
}
}
getCategories(options) {
if (this.categoriesCache_)
return this.categoriesCache_;
let categoryMap = {};
for (let i = 0, option; option = options[i]; i++) {
const content = options[i][0]; // Human-readable text or image.
const category = this.parseCategory(content);
categoryMap[category] = true;
}
this.categoriesCache_ = Object.keys(categoryMap);
return this.categoriesCache_;
}
refreshCategories(categoriesDiv, categories) {
// Show category dropdown.
for (let i = 0; i < categories.length; i++) {
const category = categories[i];
let button = document.createElement('button');
button.setAttribute('id', ':' + i); // For aria-activedescendant
button.setAttribute('role', 'menuitem');
button.setAttribute('class', 'blocklyDropdownTag');
button.setAttribute('data-value', category);
let backgroundColor = '#1A9DBC';
if (category == this.selectedCategory_) {
// This icon is selected, show it in a different colour
backgroundColor = '#0c4e5e';
button.setAttribute('aria-selected', 'true');
}
button.style.padding = "2px 6px";
button.style.backgroundColor = backgroundColor;
button.style.borderColor = backgroundColor;
Blockly.bindEvent_(button, 'click', this, this.categoryClick_);
Blockly.bindEvent_(button, 'mouseup', this, this.categoryClick_);
const textNode = this.createTextNode_(category);
textNode.setAttribute('data-value', category);
button.appendChild(textNode);
categoriesDiv.appendChild(button);
}
}
refreshOptions(contentDiv, options) {
const categories = this.getCategories(options);
// Show options
for (let i = 0, option; option = options[i]; i++) {
let content = options[i][0]; // Human-readable text or image.
const value = options[i][1]; // Language-neutral value.
// Filter for options in selected category
const category = this.parseCategory(content);
if (this.selectedCategory_ != category)
continue;
// Icons with the type property placeholder take up space but don't have any functionality
// Use for special-case layouts
if (content.type == 'placeholder') {
let placeholder = document.createElement('span');
placeholder.setAttribute('class', 'blocklyDropDownPlaceholder');
placeholder.style.width = content.width + 'px';
placeholder.style.height = content.height + 'px';
contentDiv.appendChild(placeholder);
continue;
}
let button = document.createElement('button');
button.setAttribute('id', ':' + i); // For aria-activedescendant
button.setAttribute('role', 'menuitem');
button.setAttribute('class', 'blocklyDropDownButton');
button.title = content;
if (this.columns_) {
button.style.width = ((this.width_ / this.columns_) - 8) + 'px';
//button.style.height = ((this.width_ / this.columns_) - 8) + 'px';
}
else {
button.style.width = content.width + 'px';
button.style.height = content.height + 'px';
}
let backgroundColor = this.savedPrimary_ || this.sourceBlock_.getColour();
if (value == this.getValue()) {
// This icon is selected, show it in a different colour
backgroundColor = this.sourceBlock_.getColourTertiary();
button.setAttribute('aria-selected', 'true');
}
button.style.backgroundColor = backgroundColor;
button.style.borderColor = this.sourceBlock_.getColourTertiary();
Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
// These are applied manually instead of using the :hover pseudoclass
// because Android has a bad long press "helper" menu and green highlight
// that we must prevent with ontouchstart preventDefault
let that = this;
Blockly.bindEvent_(button, 'mousedown', button, function (e) {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
e.preventDefault();
});
Blockly.bindEvent_(button, 'mouseenter', button, function () {
that.buttonEnter_(value);
});
Blockly.bindEvent_(button, 'mouseleave', button, function () {
that.buttonLeave_();
});
Blockly.bindEvent_(button, 'mouseover', button, function () {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
contentDiv.setAttribute('aria-activedescendant', this.id);
});
Blockly.bindEvent_(button, 'mouseout', button, function () {
this.setAttribute('class', 'blocklyDropDownButton');
contentDiv.removeAttribute('aria-activedescendant');
});
// Find index in array by category name
const categoryIndex = categories.indexOf(category);
let buttonImg = document.createElement('img');
buttonImg.src = this.getSoundIcon(categoryIndex);
// Upon click/touch, we will be able to get the clicked element as e.target
// Store a data attribute on all possible click targets so we can match it to the icon.
const textNode = this.createTextNode_(content);
button.setAttribute('data-value', value);
buttonImg.setAttribute('data-value', value);
buttonImg.style.height = "auto";
textNode.setAttribute('data-value', value);
if (pxt.Util.userLanguage() !== "en")
textNode.setAttribute('lang', pxt.Util.userLanguage()); // for hyphens, here you need to set the correct abbreviation of the selected language
textNode.style.display = "block";
textNode.style.lineHeight = "1rem";
textNode.style.marginBottom = "5%";
textNode.style.padding = "0px 8px";
textNode.style.wordBreak = "break-word";
textNode.style.hyphens = "auto";
button.appendChild(buttonImg);
button.appendChild(textNode);
contentDiv.appendChild(button);
}
}
trimOptions_() {
}
onHide_() {
super.onHide_();
Blockly.DropDownDiv.getContentDiv().style.maxHeight = '';
this.stopSounds();
// Update color (deselect) on dropdown hide
let source = this.sourceBlock_;
if (source === null || source === void 0 ? void 0 : source.isShadow()) {
source.setColour(this.savedPrimary_);
}
else if (this.borderRect_) {
this.borderRect_.setAttribute('fill', this.savedPrimary_);
}
}
createTextNode_(content) {
const category = this.parseCategory(content);
let text = content.substr(content.indexOf(' ') + 1);
const textSpan = document.createElement('span');
textSpan.setAttribute('class', 'blocklyDropdownText');
textSpan.textContent = text;
return textSpan;
}
parseCategory(content) {
return content.substr(0, content.indexOf(' '));
}
setSelectedCategory(value) {
this.selectedCategory_ = value;
}
stopSounds() {
pxsim.AudioContextManager.stop();
}
getSoundIcon(indexCategory) {
if (soundIconCacheArray && soundIconCacheArray[indexCategory]) {
return soundIconCacheArray[indexCategory][1].icon;
}
return undefined;
}
}
exports.FieldMusic = FieldMusic;
},{}],6:[function(require,module,exports){
"use strict";
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtblocks.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
Object.defineProperty(exports, "__esModule", { value: true });
exports.FieldPorts = void 0;
class FieldPorts extends pxtblockly.FieldImages {
constructor(text, options, validator) {
super(text, { blocksInfo: options.blocksInfo, sort: true, data: options.data }, validator);
this.isFieldCustom_ = true;
this.buttonClick_ = function (e) {
let value = e.target.getAttribute('data-value');
this.setValue(value);
Blockly.DropDownDiv.hide();
};
this.columns_ = parseInt(options.columns) || 4;
this.width_ = parseInt(options.width) || 300;
//this.setText = Blockly.FieldDropdown.prototype.setText;
this.updateSize_ = Blockly.Field.prototype.updateSize_;
}
trimOptions_() {
}
}
exports.FieldPorts = FieldPorts;
},{}]},{},[1,2,3,4,5,6]);