pxt-core 9.1.1 → 9.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/built/pxt.js +85 -29
- package/built/pxtblockly.js +69 -8
- package/built/pxtblocks.js +69 -8
- package/built/pxteditor.d.ts +7 -2
- package/built/pxteditor.js +6 -0
- package/built/pxtlib.d.ts +0 -1
- package/built/pxtlib.js +57 -22
- package/built/pxtsim.d.ts +8 -0
- package/built/pxtsim.js +28 -7
- package/built/target.js +1 -1
- package/built/web/main.js +1 -1
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtblockly.js +1 -1
- package/built/web/pxtblocks.js +1 -1
- package/built/web/pxteditor.js +1 -1
- package/built/web/pxtembed.js +2 -2
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtsim.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/built/web/react-common-authcode.css +1 -1
- package/built/web/react-common-multiplayer.css +1 -1
- package/built/web/react-common-skillmap.css +1 -1
- package/built/web/rtlreact-common-authcode.css +1 -1
- package/built/web/rtlreact-common-multiplayer.css +1 -1
- package/built/web/rtlreact-common-skillmap.css +1 -1
- package/built/web/rtlsemantic.css +2 -2
- package/built/web/semantic.css +2 -2
- package/built/web/skillmap/js/{main.b6e4b712.js → main.a9bdebe3.js} +2 -2
- package/common-docs/identity/sign-in.md +7 -15
- package/localtypings/pxtarget.d.ts +2 -0
- package/package.json +1 -1
- package/react-common/components/profile/Profile.tsx +15 -0
- package/react-common/styles/profile/profile.less +12 -0
- package/theme/common.less +9 -6
- package/theme/themes/pxt/globals/site.variables +1 -1
- package/theme/tutorial-sidebar.less +2 -2
- package/webapp/public/skillmap.html +1 -1
package/built/pxt.js
CHANGED
|
@@ -97876,6 +97876,7 @@ var pxt;
|
|
|
97876
97876
|
const AUTH_LOGIN_STATE_KEY = "login-state"; // stored in local storage.
|
|
97877
97877
|
const AUTH_USER_STATE_KEY = "user-state"; // stored in local storage.
|
|
97878
97878
|
const X_PXT_TARGET = "x-pxt-target"; // header passed in auth rest calls.
|
|
97879
|
+
const INTERACTIVE_LOGIN_UNTIL = "interactive-login-until"; // hint whether to prompt user or try SSO first.
|
|
97879
97880
|
let authDisabled = false;
|
|
97880
97881
|
auth.DEFAULT_USER_PREFERENCES = () => ({
|
|
97881
97882
|
highContrast: false,
|
|
@@ -97901,19 +97902,29 @@ var pxt;
|
|
|
97901
97902
|
// Last known auth token state. This is provided as a convenience for legacy methods that cannot be made async.
|
|
97902
97903
|
// Preference hasAuthTokenAsync() over taking a dependency on this cached value.
|
|
97903
97904
|
auth.cachedHasAuthToken = false;
|
|
97904
|
-
async function
|
|
97905
|
-
|
|
97905
|
+
async function setLocalStorageValueAsync(key, value) {
|
|
97906
|
+
if (!!value)
|
|
97907
|
+
return await pxt.storage.shared.setAsync(AUTH_CONTAINER, key, value);
|
|
97908
|
+
else
|
|
97909
|
+
return await pxt.storage.shared.delAsync(AUTH_CONTAINER, key);
|
|
97910
|
+
}
|
|
97911
|
+
async function getLocalStorageValueAsync(key) {
|
|
97906
97912
|
try {
|
|
97907
|
-
|
|
97913
|
+
return await pxt.storage.shared.getAsync(AUTH_CONTAINER, key);
|
|
97908
97914
|
}
|
|
97909
|
-
catch (_a) {
|
|
97915
|
+
catch (_a) {
|
|
97916
|
+
return undefined;
|
|
97917
|
+
}
|
|
97918
|
+
}
|
|
97919
|
+
async function getAuthTokenAsync() {
|
|
97920
|
+
const token = await getLocalStorageValueAsync(CSRF_TOKEN_KEY);
|
|
97910
97921
|
auth.cachedHasAuthToken = !!token;
|
|
97911
97922
|
return token;
|
|
97912
97923
|
}
|
|
97913
97924
|
auth.getAuthTokenAsync = getAuthTokenAsync;
|
|
97914
97925
|
async function setAuthTokenAsync(token) {
|
|
97915
97926
|
auth.cachedHasAuthToken = !!token;
|
|
97916
|
-
return await
|
|
97927
|
+
return await setLocalStorageValueAsync(CSRF_TOKEN_KEY, token);
|
|
97917
97928
|
}
|
|
97918
97929
|
async function hasAuthTokenAsync() {
|
|
97919
97930
|
return !!(await getAuthTokenAsync());
|
|
@@ -97921,7 +97932,7 @@ var pxt;
|
|
|
97921
97932
|
auth.hasAuthTokenAsync = hasAuthTokenAsync;
|
|
97922
97933
|
async function delAuthTokenAsync() {
|
|
97923
97934
|
auth.cachedHasAuthToken = false;
|
|
97924
|
-
return await
|
|
97935
|
+
return await setLocalStorageValueAsync(CSRF_TOKEN_KEY, undefined);
|
|
97925
97936
|
}
|
|
97926
97937
|
async function getUserStateAsync() {
|
|
97927
97938
|
let userState;
|
|
@@ -97984,16 +97995,20 @@ var pxt;
|
|
|
97984
97995
|
idp,
|
|
97985
97996
|
persistent
|
|
97986
97997
|
};
|
|
97998
|
+
// Should the user be prompted to interactively login, or can we try to silently login?
|
|
97999
|
+
const interactiveUntil = parseInt(await getLocalStorageValueAsync(INTERACTIVE_LOGIN_UNTIL));
|
|
98000
|
+
const interactiveLogin = (interactiveUntil || 0) > Date.now();
|
|
97987
98001
|
// Redirect to the login endpoint.
|
|
97988
98002
|
const loginUrl = pxt.Util.stringifyQueryString('/api/auth/login', {
|
|
97989
98003
|
response_type: "token",
|
|
97990
98004
|
provider: idp,
|
|
97991
98005
|
persistent,
|
|
97992
|
-
redirect_uri: `${window.location.origin}${window.location.pathname}?authcallback=1&state=${loginState.key}
|
|
98006
|
+
redirect_uri: `${window.location.origin}${window.location.pathname}?authcallback=1&state=${loginState.key}`,
|
|
98007
|
+
prompt: interactiveLogin ? "select_account" : "silent"
|
|
97993
98008
|
});
|
|
97994
98009
|
const apiResult = await this.apiAsync(loginUrl);
|
|
97995
98010
|
if (apiResult.success) {
|
|
97996
|
-
loginState.authCodeVerifier = apiResult.resp.authCodeVerifier;
|
|
98011
|
+
loginState.authCodeVerifier = apiResult.resp.authCodeVerifier; // will be undefined unless configured for the target
|
|
97997
98012
|
await pxt.storage.shared.setAsync(AUTH_CONTAINER, AUTH_LOGIN_STATE_KEY, loginState);
|
|
97998
98013
|
pxt.tickEvent('auth.login.start', { 'provider': idp });
|
|
97999
98014
|
window.location.href = apiResult.resp.loginUrl;
|
|
@@ -98012,13 +98027,15 @@ var pxt;
|
|
|
98012
98027
|
if (!hasIdentity()) {
|
|
98013
98028
|
return;
|
|
98014
98029
|
}
|
|
98015
|
-
// Clear local auth state so we can no longer make authenticated requests.
|
|
98016
|
-
await this.clearAuthStateAsync();
|
|
98017
98030
|
await AuthClient.staticLogoutAsync(continuationHash);
|
|
98018
98031
|
try {
|
|
98019
|
-
await this.
|
|
98032
|
+
await this.onStateCleared();
|
|
98020
98033
|
}
|
|
98021
98034
|
catch (_a) { }
|
|
98035
|
+
try {
|
|
98036
|
+
await this.onSignedOut();
|
|
98037
|
+
}
|
|
98038
|
+
catch (_b) { }
|
|
98022
98039
|
}
|
|
98023
98040
|
/**
|
|
98024
98041
|
* Sign out the user and clear the auth token cookie.
|
|
@@ -98028,19 +98045,39 @@ var pxt;
|
|
|
98028
98045
|
return;
|
|
98029
98046
|
}
|
|
98030
98047
|
pxt.tickEvent('auth.logout');
|
|
98031
|
-
//
|
|
98048
|
+
// Indicate that for the next minute, signin should be interactive.
|
|
98049
|
+
// Use case: SSO signed in with the wrong account. User wants to sign in with a different account.
|
|
98050
|
+
await setLocalStorageValueAsync(INTERACTIVE_LOGIN_UNTIL, (Date.now() + 60000).toString());
|
|
98051
|
+
continuationHash = continuationHash ? continuationHash.startsWith('#') ? continuationHash : `#${continuationHash}` : "";
|
|
98052
|
+
const clientRedirect = `${window.location.origin}${window.location.pathname}${window.location.search}${continuationHash}`;
|
|
98053
|
+
// Tell backend to clear the http-only auth cookie.
|
|
98054
|
+
let logoutUri = "";
|
|
98032
98055
|
try {
|
|
98033
|
-
|
|
98056
|
+
const uri = pxt.Util.stringifyQueryString('/api/auth/logout', {
|
|
98057
|
+
redirect_uri: clientRedirect,
|
|
98058
|
+
authcallback: '1'
|
|
98059
|
+
});
|
|
98060
|
+
const apiResult = await AuthClient.staticApiAsync(uri);
|
|
98061
|
+
if (apiResult.success) {
|
|
98062
|
+
logoutUri = apiResult.resp.logoutUrl;
|
|
98063
|
+
}
|
|
98064
|
+
}
|
|
98065
|
+
catch (_a) {
|
|
98066
|
+
// Ignore errors.
|
|
98034
98067
|
}
|
|
98035
|
-
catch (_a) { }
|
|
98036
98068
|
// Clear local auth state so we can no longer make authenticated requests.
|
|
98037
98069
|
await delAuthTokenAsync();
|
|
98038
98070
|
await delUserStateAsync();
|
|
98039
|
-
// Redirect to home screen
|
|
98040
98071
|
if (pxt.BrowserUtils.hasWindow()) {
|
|
98041
|
-
|
|
98042
|
-
|
|
98043
|
-
|
|
98072
|
+
if (logoutUri) {
|
|
98073
|
+
// Redirect to logout endpoint
|
|
98074
|
+
window.location.href = logoutUri;
|
|
98075
|
+
}
|
|
98076
|
+
else {
|
|
98077
|
+
// Redirect to home screen
|
|
98078
|
+
window.location.href = clientRedirect;
|
|
98079
|
+
location.reload();
|
|
98080
|
+
}
|
|
98044
98081
|
}
|
|
98045
98082
|
}
|
|
98046
98083
|
async deleteProfileAsync() {
|
|
@@ -98436,6 +98473,8 @@ var pxt;
|
|
|
98436
98473
|
// without its cookie-based counterpart. When "Remember me" is false,
|
|
98437
98474
|
// the cookie is not persisted.
|
|
98438
98475
|
await setAuthTokenAsync(authToken);
|
|
98476
|
+
// Clear interactive login flag. Next auth request will try silent SSO first.
|
|
98477
|
+
await setLocalStorageValueAsync(INTERACTIVE_LOGIN_UNTIL, undefined);
|
|
98439
98478
|
pxt.tickEvent('auth.login.success', { 'provider': loginState.idp });
|
|
98440
98479
|
} while (false);
|
|
98441
98480
|
// Clear url parameters and redirect to the callback location.
|
|
@@ -102460,10 +102499,6 @@ var pxt;
|
|
|
102460
102499
|
return typeof window != "undefined" && !!window.PointerEvent;
|
|
102461
102500
|
}
|
|
102462
102501
|
BrowserUtils.hasPointerEvents = hasPointerEvents;
|
|
102463
|
-
function hasSaveAs() {
|
|
102464
|
-
return isEdge() || isIE() || isFirefox();
|
|
102465
|
-
}
|
|
102466
|
-
BrowserUtils.hasSaveAs = hasSaveAs;
|
|
102467
102502
|
function os() {
|
|
102468
102503
|
if (isWindows())
|
|
102469
102504
|
return "windows";
|
|
@@ -155238,6 +155273,13 @@ var pxsim;
|
|
|
155238
155273
|
}
|
|
155239
155274
|
}
|
|
155240
155275
|
pxsim.Runtime = Runtime;
|
|
155276
|
+
function setParentMuteState(state) {
|
|
155277
|
+
Runtime.postMessage({
|
|
155278
|
+
type: "setmutebuttonstate",
|
|
155279
|
+
state
|
|
155280
|
+
});
|
|
155281
|
+
}
|
|
155282
|
+
pxsim.setParentMuteState = setParentMuteState;
|
|
155241
155283
|
class PerfCounter {
|
|
155242
155284
|
constructor(name) {
|
|
155243
155285
|
this.name = name;
|
|
@@ -155898,8 +155940,19 @@ var pxsim;
|
|
|
155898
155940
|
this.setFrameState(frame);
|
|
155899
155941
|
return true;
|
|
155900
155942
|
}
|
|
155943
|
+
handleDeferredMessages(frame) {
|
|
155944
|
+
var _a, _b, _c;
|
|
155945
|
+
if (frame.dataset["loading"]) {
|
|
155946
|
+
delete frame.dataset["loading"];
|
|
155947
|
+
(_b = (_a = this.deferredMessages) === null || _a === void 0 ? void 0 : _a.filter(defMsg => defMsg[0] === frame)) === null || _b === void 0 ? void 0 : _b.forEach(defMsg => {
|
|
155948
|
+
const [_, msg] = defMsg;
|
|
155949
|
+
this.postMessageCore(frame, msg);
|
|
155950
|
+
});
|
|
155951
|
+
this.deferredMessages = (_c = this.deferredMessages) === null || _c === void 0 ? void 0 : _c.filter(defMsg => defMsg[0] !== frame);
|
|
155952
|
+
}
|
|
155953
|
+
}
|
|
155901
155954
|
handleMessage(msg, source) {
|
|
155902
|
-
var _a, _b, _c
|
|
155955
|
+
var _a, _b, _c;
|
|
155903
155956
|
switch (msg.type || '') {
|
|
155904
155957
|
case 'ready': {
|
|
155905
155958
|
const frameid = msg.frameid;
|
|
@@ -155910,12 +155963,7 @@ var pxsim;
|
|
|
155910
155963
|
this.startFrame(frame);
|
|
155911
155964
|
if (this.options.revealElement)
|
|
155912
155965
|
this.options.revealElement(frame);
|
|
155913
|
-
|
|
155914
|
-
(_c = (_b = this.deferredMessages) === null || _b === void 0 ? void 0 : _b.filter(defMsg => defMsg[0] === frame)) === null || _c === void 0 ? void 0 : _c.forEach(defMsg => {
|
|
155915
|
-
const [_, msg] = defMsg;
|
|
155916
|
-
this.postMessageCore(frame, msg);
|
|
155917
|
-
});
|
|
155918
|
-
this.deferredMessages = (_d = this.deferredMessages) === null || _d === void 0 ? void 0 : _d.filter(defMsg => defMsg[0] !== frame);
|
|
155966
|
+
this.handleDeferredMessages(frame);
|
|
155919
155967
|
}
|
|
155920
155968
|
if (this.options.onSimulatorReady)
|
|
155921
155969
|
this.options.onSimulatorReady();
|
|
@@ -155930,6 +155978,7 @@ var pxsim;
|
|
|
155930
155978
|
switch (stmsg.state) {
|
|
155931
155979
|
case "running":
|
|
155932
155980
|
this.setState(SimulatorState.Running);
|
|
155981
|
+
this.handleDeferredMessages(frame);
|
|
155933
155982
|
break;
|
|
155934
155983
|
case "killed":
|
|
155935
155984
|
this.setState(SimulatorState.Stopped);
|
|
@@ -155966,6 +156015,9 @@ var pxsim;
|
|
|
155966
156015
|
if (this.options.onTopLevelCodeEnd)
|
|
155967
156016
|
this.options.onTopLevelCodeEnd();
|
|
155968
156017
|
break;
|
|
156018
|
+
case 'setmutebuttonstate':
|
|
156019
|
+
(_c = (_b = this.options).onMuteButtonStateChange) === null || _c === void 0 ? void 0 : _c.call(_b, msg.state);
|
|
156020
|
+
break;
|
|
155969
156021
|
default:
|
|
155970
156022
|
this.postMessage(msg, source);
|
|
155971
156023
|
break;
|
|
@@ -156370,6 +156422,10 @@ var pxsim;
|
|
|
156370
156422
|
ctx.resume();
|
|
156371
156423
|
}
|
|
156372
156424
|
AudioContextManager.mute = mute;
|
|
156425
|
+
function isMuted() {
|
|
156426
|
+
return _mute;
|
|
156427
|
+
}
|
|
156428
|
+
AudioContextManager.isMuted = isMuted;
|
|
156373
156429
|
function stopTone() {
|
|
156374
156430
|
setCurrentToneGain(0);
|
|
156375
156431
|
_frequency = 0;
|
package/built/pxtblockly.js
CHANGED
|
@@ -13656,6 +13656,51 @@ var pxtblockly;
|
|
|
13656
13656
|
if (value === "CREATE") {
|
|
13657
13657
|
promptAndCreateKind(this.sourceBlock_.workspace, this.opts, lf("New {0}:", this.opts.memberName), newName => newName && this.setValue(newName));
|
|
13658
13658
|
}
|
|
13659
|
+
else if (value === "RENAME") {
|
|
13660
|
+
const ws = this.sourceBlock_.workspace;
|
|
13661
|
+
const toRename = ws.getVariable(this.value_, kindType(this.opts.name));
|
|
13662
|
+
const oldName = toRename.name;
|
|
13663
|
+
if (this.opts.initialMembers.indexOf(oldName) !== -1) {
|
|
13664
|
+
Blockly.alert(lf("The built-in {0} '{1}' cannot be renamed. Try creating a new kind instead!", this.opts.memberName, oldName));
|
|
13665
|
+
return;
|
|
13666
|
+
}
|
|
13667
|
+
promptAndRenameKind(ws, Object.assign(Object.assign({}, this.opts), { toRename }), lf("Rename '{0}':", oldName), newName => {
|
|
13668
|
+
// Update the values of all existing field instances
|
|
13669
|
+
const allFields = pxtblockly.getAllFields(ws, field => field instanceof FieldKind);
|
|
13670
|
+
for (const field of allFields) {
|
|
13671
|
+
if (field.ref.getValue() === oldName) {
|
|
13672
|
+
field.ref.setValue(newName);
|
|
13673
|
+
}
|
|
13674
|
+
}
|
|
13675
|
+
});
|
|
13676
|
+
}
|
|
13677
|
+
else if (value === "DELETE") {
|
|
13678
|
+
const ws = this.sourceBlock_.workspace;
|
|
13679
|
+
const toDelete = ws.getVariable(this.value_, kindType(this.opts.name));
|
|
13680
|
+
const varName = toDelete.name;
|
|
13681
|
+
if (this.opts.initialMembers.indexOf(varName) !== -1) {
|
|
13682
|
+
Blockly.alert(lf("The built-in {0} '{1}' cannot be deleted.", this.opts.memberName, varName));
|
|
13683
|
+
return;
|
|
13684
|
+
}
|
|
13685
|
+
const uses = pxtblockly.getAllFields(ws, field => field instanceof FieldKind && field.getValue() === varName);
|
|
13686
|
+
if (uses.length > 1) {
|
|
13687
|
+
Blockly.confirm(lf("Delete {0} uses of the \"{1}\" {2}?", uses.length, varName, this.opts.memberName), response => {
|
|
13688
|
+
if (!response)
|
|
13689
|
+
return;
|
|
13690
|
+
Blockly.Events.setGroup(true);
|
|
13691
|
+
for (const use of uses) {
|
|
13692
|
+
use.block.dispose(true);
|
|
13693
|
+
}
|
|
13694
|
+
ws.deleteVariableById(toDelete.getId());
|
|
13695
|
+
this.setValue(this.opts.initialMembers[0]);
|
|
13696
|
+
Blockly.Events.setGroup(false);
|
|
13697
|
+
});
|
|
13698
|
+
}
|
|
13699
|
+
else {
|
|
13700
|
+
ws.deleteVariableById(toDelete.getId());
|
|
13701
|
+
this.setValue(this.opts.initialMembers[0]);
|
|
13702
|
+
}
|
|
13703
|
+
}
|
|
13659
13704
|
else {
|
|
13660
13705
|
super.onItemSelected_(menu, menuItem);
|
|
13661
13706
|
}
|
|
@@ -13680,7 +13725,7 @@ var pxtblockly;
|
|
|
13680
13725
|
createVariableForKind(ws, this.opts, memberName);
|
|
13681
13726
|
}
|
|
13682
13727
|
});
|
|
13683
|
-
if (this.getValue() === "CREATE") {
|
|
13728
|
+
if (this.getValue() === "CREATE" || this.getValue() === "RENAME" || this.getValue() === "DELETE") {
|
|
13684
13729
|
if (this.opts.initialMembers.length) {
|
|
13685
13730
|
this.setValue(this.opts.initialMembers[0]);
|
|
13686
13731
|
}
|
|
@@ -13704,10 +13749,13 @@ var pxtblockly;
|
|
|
13704
13749
|
opts.initialMembers.forEach((e) => res.push([e, e]));
|
|
13705
13750
|
}
|
|
13706
13751
|
res.push([lf("Add a new {0}...", opts.memberName), "CREATE"]);
|
|
13752
|
+
res.push([undefined, "SEPARATOR"]);
|
|
13753
|
+
res.push([lf("Rename {0}...", opts.memberName), "RENAME"]);
|
|
13754
|
+
res.push([lf("Delete {0}...", opts.memberName), "DELETE"]);
|
|
13707
13755
|
return res;
|
|
13708
13756
|
};
|
|
13709
13757
|
}
|
|
13710
|
-
function
|
|
13758
|
+
function promptForName(ws, opts, message, cb, prompt) {
|
|
13711
13759
|
Blockly.prompt(message, null, response => {
|
|
13712
13760
|
if (response) {
|
|
13713
13761
|
let nameIsValid = false;
|
|
@@ -13720,28 +13768,41 @@ var pxtblockly;
|
|
|
13720
13768
|
}
|
|
13721
13769
|
}
|
|
13722
13770
|
if (!nameIsValid) {
|
|
13723
|
-
Blockly.alert(lf("Names must start with a letter and can only contain letters, numbers, '$', and '_'."), () =>
|
|
13771
|
+
Blockly.alert(lf("Names must start with a letter and can only contain letters, numbers, '$', and '_'."), () => promptForName(ws, opts, message, cb, prompt));
|
|
13724
13772
|
return;
|
|
13725
13773
|
}
|
|
13726
|
-
if (pxt.blocks.isReservedWord(response)) {
|
|
13727
|
-
Blockly.alert(lf("'{0}' is a reserved word and cannot be used.", response), () =>
|
|
13774
|
+
if (pxt.blocks.isReservedWord(response) || response === "CREATE" || response === "RENAME" || response === "DELETE") {
|
|
13775
|
+
Blockly.alert(lf("'{0}' is a reserved word and cannot be used.", response), () => promptForName(ws, opts, message, cb, prompt));
|
|
13728
13776
|
return;
|
|
13729
13777
|
}
|
|
13730
13778
|
const existing = getExistingKindMembers(ws, opts.name);
|
|
13731
13779
|
for (let i = 0; i < existing.length; i++) {
|
|
13732
13780
|
const name = existing[i];
|
|
13733
13781
|
if (name === response) {
|
|
13734
|
-
Blockly.alert(lf("A {0} named '{1}' already exists.", opts.memberName, response), () =>
|
|
13782
|
+
Blockly.alert(lf("A {0} named '{1}' already exists.", opts.memberName, response), () => promptForName(ws, opts, message, cb, prompt));
|
|
13735
13783
|
return;
|
|
13736
13784
|
}
|
|
13737
13785
|
}
|
|
13738
13786
|
if (response === opts.createFunctionName) {
|
|
13739
|
-
Blockly.alert(lf("'{0}' is a reserved name.", opts.createFunctionName), () =>
|
|
13787
|
+
Blockly.alert(lf("'{0}' is a reserved name.", opts.createFunctionName), () => promptForName(ws, opts, message, cb, prompt));
|
|
13740
13788
|
}
|
|
13741
|
-
cb(
|
|
13789
|
+
cb(response);
|
|
13742
13790
|
}
|
|
13743
13791
|
}, { placeholder: opts.promptHint });
|
|
13744
13792
|
}
|
|
13793
|
+
function promptAndCreateKind(ws, opts, message, cb) {
|
|
13794
|
+
const responseHandler = (response) => {
|
|
13795
|
+
cb(createVariableForKind(ws, opts, response));
|
|
13796
|
+
};
|
|
13797
|
+
promptForName(ws, opts, message, responseHandler, promptAndCreateKind);
|
|
13798
|
+
}
|
|
13799
|
+
function promptAndRenameKind(ws, opts, message, cb) {
|
|
13800
|
+
const responseHandler = (response) => {
|
|
13801
|
+
ws.getVariableMap().renameVariable(opts.toRename, response);
|
|
13802
|
+
cb(response);
|
|
13803
|
+
};
|
|
13804
|
+
promptForName(ws, opts, message, responseHandler, promptAndRenameKind);
|
|
13805
|
+
}
|
|
13745
13806
|
function getExistingKindMembers(ws, kindName) {
|
|
13746
13807
|
const existing = ws.getVariablesOfType(kindType(kindName));
|
|
13747
13808
|
if (existing && existing.length) {
|
package/built/pxtblocks.js
CHANGED
|
@@ -10094,6 +10094,51 @@ var pxtblockly;
|
|
|
10094
10094
|
if (value === "CREATE") {
|
|
10095
10095
|
promptAndCreateKind(this.sourceBlock_.workspace, this.opts, lf("New {0}:", this.opts.memberName), newName => newName && this.setValue(newName));
|
|
10096
10096
|
}
|
|
10097
|
+
else if (value === "RENAME") {
|
|
10098
|
+
const ws = this.sourceBlock_.workspace;
|
|
10099
|
+
const toRename = ws.getVariable(this.value_, kindType(this.opts.name));
|
|
10100
|
+
const oldName = toRename.name;
|
|
10101
|
+
if (this.opts.initialMembers.indexOf(oldName) !== -1) {
|
|
10102
|
+
Blockly.alert(lf("The built-in {0} '{1}' cannot be renamed. Try creating a new kind instead!", this.opts.memberName, oldName));
|
|
10103
|
+
return;
|
|
10104
|
+
}
|
|
10105
|
+
promptAndRenameKind(ws, Object.assign(Object.assign({}, this.opts), { toRename }), lf("Rename '{0}':", oldName), newName => {
|
|
10106
|
+
// Update the values of all existing field instances
|
|
10107
|
+
const allFields = pxtblockly.getAllFields(ws, field => field instanceof FieldKind);
|
|
10108
|
+
for (const field of allFields) {
|
|
10109
|
+
if (field.ref.getValue() === oldName) {
|
|
10110
|
+
field.ref.setValue(newName);
|
|
10111
|
+
}
|
|
10112
|
+
}
|
|
10113
|
+
});
|
|
10114
|
+
}
|
|
10115
|
+
else if (value === "DELETE") {
|
|
10116
|
+
const ws = this.sourceBlock_.workspace;
|
|
10117
|
+
const toDelete = ws.getVariable(this.value_, kindType(this.opts.name));
|
|
10118
|
+
const varName = toDelete.name;
|
|
10119
|
+
if (this.opts.initialMembers.indexOf(varName) !== -1) {
|
|
10120
|
+
Blockly.alert(lf("The built-in {0} '{1}' cannot be deleted.", this.opts.memberName, varName));
|
|
10121
|
+
return;
|
|
10122
|
+
}
|
|
10123
|
+
const uses = pxtblockly.getAllFields(ws, field => field instanceof FieldKind && field.getValue() === varName);
|
|
10124
|
+
if (uses.length > 1) {
|
|
10125
|
+
Blockly.confirm(lf("Delete {0} uses of the \"{1}\" {2}?", uses.length, varName, this.opts.memberName), response => {
|
|
10126
|
+
if (!response)
|
|
10127
|
+
return;
|
|
10128
|
+
Blockly.Events.setGroup(true);
|
|
10129
|
+
for (const use of uses) {
|
|
10130
|
+
use.block.dispose(true);
|
|
10131
|
+
}
|
|
10132
|
+
ws.deleteVariableById(toDelete.getId());
|
|
10133
|
+
this.setValue(this.opts.initialMembers[0]);
|
|
10134
|
+
Blockly.Events.setGroup(false);
|
|
10135
|
+
});
|
|
10136
|
+
}
|
|
10137
|
+
else {
|
|
10138
|
+
ws.deleteVariableById(toDelete.getId());
|
|
10139
|
+
this.setValue(this.opts.initialMembers[0]);
|
|
10140
|
+
}
|
|
10141
|
+
}
|
|
10097
10142
|
else {
|
|
10098
10143
|
super.onItemSelected_(menu, menuItem);
|
|
10099
10144
|
}
|
|
@@ -10118,7 +10163,7 @@ var pxtblockly;
|
|
|
10118
10163
|
createVariableForKind(ws, this.opts, memberName);
|
|
10119
10164
|
}
|
|
10120
10165
|
});
|
|
10121
|
-
if (this.getValue() === "CREATE") {
|
|
10166
|
+
if (this.getValue() === "CREATE" || this.getValue() === "RENAME" || this.getValue() === "DELETE") {
|
|
10122
10167
|
if (this.opts.initialMembers.length) {
|
|
10123
10168
|
this.setValue(this.opts.initialMembers[0]);
|
|
10124
10169
|
}
|
|
@@ -10142,10 +10187,13 @@ var pxtblockly;
|
|
|
10142
10187
|
opts.initialMembers.forEach((e) => res.push([e, e]));
|
|
10143
10188
|
}
|
|
10144
10189
|
res.push([lf("Add a new {0}...", opts.memberName), "CREATE"]);
|
|
10190
|
+
res.push([undefined, "SEPARATOR"]);
|
|
10191
|
+
res.push([lf("Rename {0}...", opts.memberName), "RENAME"]);
|
|
10192
|
+
res.push([lf("Delete {0}...", opts.memberName), "DELETE"]);
|
|
10145
10193
|
return res;
|
|
10146
10194
|
};
|
|
10147
10195
|
}
|
|
10148
|
-
function
|
|
10196
|
+
function promptForName(ws, opts, message, cb, prompt) {
|
|
10149
10197
|
Blockly.prompt(message, null, response => {
|
|
10150
10198
|
if (response) {
|
|
10151
10199
|
let nameIsValid = false;
|
|
@@ -10158,28 +10206,41 @@ var pxtblockly;
|
|
|
10158
10206
|
}
|
|
10159
10207
|
}
|
|
10160
10208
|
if (!nameIsValid) {
|
|
10161
|
-
Blockly.alert(lf("Names must start with a letter and can only contain letters, numbers, '$', and '_'."), () =>
|
|
10209
|
+
Blockly.alert(lf("Names must start with a letter and can only contain letters, numbers, '$', and '_'."), () => promptForName(ws, opts, message, cb, prompt));
|
|
10162
10210
|
return;
|
|
10163
10211
|
}
|
|
10164
|
-
if (pxt.blocks.isReservedWord(response)) {
|
|
10165
|
-
Blockly.alert(lf("'{0}' is a reserved word and cannot be used.", response), () =>
|
|
10212
|
+
if (pxt.blocks.isReservedWord(response) || response === "CREATE" || response === "RENAME" || response === "DELETE") {
|
|
10213
|
+
Blockly.alert(lf("'{0}' is a reserved word and cannot be used.", response), () => promptForName(ws, opts, message, cb, prompt));
|
|
10166
10214
|
return;
|
|
10167
10215
|
}
|
|
10168
10216
|
const existing = getExistingKindMembers(ws, opts.name);
|
|
10169
10217
|
for (let i = 0; i < existing.length; i++) {
|
|
10170
10218
|
const name = existing[i];
|
|
10171
10219
|
if (name === response) {
|
|
10172
|
-
Blockly.alert(lf("A {0} named '{1}' already exists.", opts.memberName, response), () =>
|
|
10220
|
+
Blockly.alert(lf("A {0} named '{1}' already exists.", opts.memberName, response), () => promptForName(ws, opts, message, cb, prompt));
|
|
10173
10221
|
return;
|
|
10174
10222
|
}
|
|
10175
10223
|
}
|
|
10176
10224
|
if (response === opts.createFunctionName) {
|
|
10177
|
-
Blockly.alert(lf("'{0}' is a reserved name.", opts.createFunctionName), () =>
|
|
10225
|
+
Blockly.alert(lf("'{0}' is a reserved name.", opts.createFunctionName), () => promptForName(ws, opts, message, cb, prompt));
|
|
10178
10226
|
}
|
|
10179
|
-
cb(
|
|
10227
|
+
cb(response);
|
|
10180
10228
|
}
|
|
10181
10229
|
}, { placeholder: opts.promptHint });
|
|
10182
10230
|
}
|
|
10231
|
+
function promptAndCreateKind(ws, opts, message, cb) {
|
|
10232
|
+
const responseHandler = (response) => {
|
|
10233
|
+
cb(createVariableForKind(ws, opts, response));
|
|
10234
|
+
};
|
|
10235
|
+
promptForName(ws, opts, message, responseHandler, promptAndCreateKind);
|
|
10236
|
+
}
|
|
10237
|
+
function promptAndRenameKind(ws, opts, message, cb) {
|
|
10238
|
+
const responseHandler = (response) => {
|
|
10239
|
+
ws.getVariableMap().renameVariable(opts.toRename, response);
|
|
10240
|
+
cb(response);
|
|
10241
|
+
};
|
|
10242
|
+
promptForName(ws, opts, message, responseHandler, promptAndRenameKind);
|
|
10243
|
+
}
|
|
10183
10244
|
function getExistingKindMembers(ws, kindName) {
|
|
10184
10245
|
const existing = ws.getVariablesOfType(kindType(kindName));
|
|
10185
10246
|
if (existing && existing.length) {
|
package/built/pxteditor.d.ts
CHANGED
|
@@ -36,6 +36,11 @@ declare namespace pxt.editor {
|
|
|
36
36
|
HeaderOnly = "errorListHeader",
|
|
37
37
|
Expanded = "errorListExpanded"
|
|
38
38
|
}
|
|
39
|
+
enum MuteState {
|
|
40
|
+
Muted = "muted",
|
|
41
|
+
Unmuted = "unmuted",
|
|
42
|
+
Disabled = "disabled"
|
|
43
|
+
}
|
|
39
44
|
interface IAppProps {
|
|
40
45
|
}
|
|
41
46
|
interface IAppState {
|
|
@@ -64,7 +69,7 @@ declare namespace pxt.editor {
|
|
|
64
69
|
showParts?: boolean;
|
|
65
70
|
fullscreen?: boolean;
|
|
66
71
|
showMiniSim?: boolean;
|
|
67
|
-
mute?:
|
|
72
|
+
mute?: MuteState;
|
|
68
73
|
embedSimView?: boolean;
|
|
69
74
|
editorPosition?: {
|
|
70
75
|
lineNumber: number;
|
|
@@ -275,7 +280,7 @@ declare namespace pxt.editor {
|
|
|
275
280
|
toggleTrace(intervalSpeed?: number): void;
|
|
276
281
|
setTrace(enabled: boolean, intervalSpeed?: number): void;
|
|
277
282
|
toggleMute(): void;
|
|
278
|
-
setMute(
|
|
283
|
+
setMute(state: MuteState): void;
|
|
279
284
|
openInstructions(): void;
|
|
280
285
|
closeFlyout(): void;
|
|
281
286
|
printCode(): void;
|
package/built/pxteditor.js
CHANGED
|
@@ -19,6 +19,12 @@ var pxt;
|
|
|
19
19
|
ErrorListState["HeaderOnly"] = "errorListHeader";
|
|
20
20
|
ErrorListState["Expanded"] = "errorListExpanded";
|
|
21
21
|
})(ErrorListState = editor.ErrorListState || (editor.ErrorListState = {}));
|
|
22
|
+
let MuteState;
|
|
23
|
+
(function (MuteState) {
|
|
24
|
+
MuteState["Muted"] = "muted";
|
|
25
|
+
MuteState["Unmuted"] = "unmuted";
|
|
26
|
+
MuteState["Disabled"] = "disabled";
|
|
27
|
+
})(MuteState = editor.MuteState || (editor.MuteState = {}));
|
|
22
28
|
let FilterState;
|
|
23
29
|
(function (FilterState) {
|
|
24
30
|
FilterState[FilterState["Hidden"] = 0] = "Hidden";
|
package/built/pxtlib.d.ts
CHANGED
|
@@ -727,7 +727,6 @@ declare namespace pxt.BrowserUtils {
|
|
|
727
727
|
export function noSharedLocalStorage(): boolean;
|
|
728
728
|
export function useOldTutorialLayout(): boolean;
|
|
729
729
|
export function hasPointerEvents(): boolean;
|
|
730
|
-
export function hasSaveAs(): boolean;
|
|
731
730
|
export function os(): string;
|
|
732
731
|
export function browser(): string;
|
|
733
732
|
export function browserVersion(): string;
|