pxt-core 9.3.13 → 9.3.15
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/README.md +11 -0
- package/built/cli.js +7 -2
- package/built/pxt.js +57 -3
- package/built/pxtblockly.js +97 -57
- package/built/pxtblocks.d.ts +59 -21
- package/built/pxtblocks.js +97 -57
- package/built/pxtlib.d.ts +22 -0
- package/built/pxtlib.js +50 -1
- package/built/target.js +1 -1
- package/built/tests/blocksrunner.js +1 -1
- package/built/web/main.js +1 -1
- package/built/web/multiplayer/js/{main.78cecdcb.js → main.75ca8c58.js} +2 -2
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtasseteditor.js +1 -1
- package/built/web/pxtblockly.js +2 -2
- package/built/web/pxtblocks.js +1 -1
- package/built/web/pxtembed.js +2 -2
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/built/web/rtlsemantic.css +1 -1
- package/built/web/runnerembed.js +1 -0
- package/built/web/semantic.css +1 -1
- package/built/web/skillmap/js/{main.8222bb34.js → main.236bd49e.js} +2 -2
- package/built/web/teachertool/css/main.e9386f28.css +1 -0
- package/built/web/teachertool/js/{main.3a94a341.js → main.8aa6604c.js} +2 -2
- package/localtypings/projectheader.d.ts +21 -0
- package/{built → localtypings}/pxteditor.d.ts +1095 -1090
- package/package.json +1 -1
- package/react-common/components/controls/MenuDropdown.tsx +5 -2
- package/react-common/components/util.tsx +1 -1
- package/theme/pxt.less +1 -0
- package/theme/themepacks.less +41 -0
- package/webapp/public/embed.js +1 -1
- package/webapp/public/multiplayer.html +1 -1
- package/webapp/public/skillmap.html +1 -1
- package/webapp/public/teachertool.html +1 -3
- package/built/pxteditor.js +0 -1834
- package/built/pxtrunner.d.ts +0 -151
- package/built/pxtrunner.js +0 -2626
- package/built/web/pxteditor.js +0 -1
- package/built/web/pxtrunner.js +0 -1
- package/built/web/teachertool/css/main.59776cd1.css +0 -1
package/built/pxteditor.js
DELETED
|
@@ -1,1834 +0,0 @@
|
|
|
1
|
-
var pxt;
|
|
2
|
-
(function (pxt) {
|
|
3
|
-
var editor;
|
|
4
|
-
(function (editor) {
|
|
5
|
-
let SimState;
|
|
6
|
-
(function (SimState) {
|
|
7
|
-
SimState[SimState["Stopped"] = 0] = "Stopped";
|
|
8
|
-
// waiting to be started
|
|
9
|
-
SimState[SimState["Pending"] = 1] = "Pending";
|
|
10
|
-
SimState[SimState["Starting"] = 2] = "Starting";
|
|
11
|
-
SimState[SimState["Running"] = 3] = "Running";
|
|
12
|
-
})(SimState = editor.SimState || (editor.SimState = {}));
|
|
13
|
-
function isBlocks(f) {
|
|
14
|
-
return pxt.U.endsWith(f.name, ".blocks");
|
|
15
|
-
}
|
|
16
|
-
editor.isBlocks = isBlocks;
|
|
17
|
-
let ErrorListState;
|
|
18
|
-
(function (ErrorListState) {
|
|
19
|
-
ErrorListState["HeaderOnly"] = "errorListHeader";
|
|
20
|
-
ErrorListState["Expanded"] = "errorListExpanded";
|
|
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 = {}));
|
|
28
|
-
let FilterState;
|
|
29
|
-
(function (FilterState) {
|
|
30
|
-
FilterState[FilterState["Hidden"] = 0] = "Hidden";
|
|
31
|
-
FilterState[FilterState["Visible"] = 1] = "Visible";
|
|
32
|
-
FilterState[FilterState["Disabled"] = 2] = "Disabled";
|
|
33
|
-
})(FilterState = editor.FilterState || (editor.FilterState = {}));
|
|
34
|
-
editor.initExtensionsAsync = opts => Promise.resolve({});
|
|
35
|
-
editor.initFieldExtensionsAsync = opts => Promise.resolve({});
|
|
36
|
-
editor.HELP_IMAGE_URI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjYiIGhlaWdodD0iMjYiIHZpZXdCb3g9IjAgMCAyNiAyNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGNpcmNsZSBjeD0iMTMiIGN5PSIxMyIgcj0iMTMiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xNy45NTIgOS4xODQwMkMxNy45NTIgMTAuMjU2IDE3LjgxNiAxMS4wNzIgMTcuNTQ0IDExLjYzMkMxNy4yODggMTIuMTkyIDE2Ljc1MiAxMi43OTIgMTUuOTM2IDEzLjQzMkMxNS4xMiAxNC4wNzIgMTQuNTc2IDE0LjU4NCAxNC4zMDQgMTQuOTY4QzE0LjA0OCAxNS4zMzYgMTMuOTIgMTUuNzM2IDEzLjkyIDE2LjE2OFYxNi45NkgxMS44MDhDMTEuNDI0IDE2LjQ2NCAxMS4yMzIgMTUuODQgMTEuMjMyIDE1LjA4OEMxMS4yMzIgMTQuNjg4IDExLjM4NCAxNC4yODggMTEuNjg4IDEzLjg4OEMxMS45OTIgMTMuNDg4IDEyLjUzNiAxMi45NjggMTMuMzIgMTIuMzI4QzE0LjEwNCAxMS42NzIgMTQuNjI0IDExLjE2OCAxNC44OCAxMC44MTZDMTUuMTM2IDEwLjQ0OCAxNS4yNjQgOS45NjgwMiAxNS4yNjQgOS4zNzYwMkMxNS4yNjQgOC4yMDgwMiAxNC40MTYgNy42MjQwMiAxMi43MiA3LjYyNDAyQzExLjc2IDcuNjI0MDIgMTAuNzUyIDcuNzM2MDIgOS42OTYgNy45NjAwMkw5LjE0NCA4LjA4MDAyTDkgNi4wODgwMkMxMC40ODggNS41NjAwMiAxMS44NCA1LjI5NjAyIDEzLjA1NiA1LjI5NjAyQzE0LjczNiA1LjI5NjAyIDE1Ljk2OCA1LjYwODAyIDE2Ljc1MiA2LjIzMjAyQzE3LjU1MiA2Ljg0MDAyIDE3Ljk1MiA3LjgyNDAyIDE3Ljk1MiA5LjE4NDAyWk0xMS40IDIyVjE4LjY0SDE0LjE4NFYyMkgxMS40WiIgZmlsbD0iIzU5NUU3NCIvPgo8L3N2Zz4K';
|
|
37
|
-
let _initEditorExtensionsPromise;
|
|
38
|
-
function initEditorExtensionsAsync() {
|
|
39
|
-
if (!_initEditorExtensionsPromise) {
|
|
40
|
-
_initEditorExtensionsPromise = Promise.resolve();
|
|
41
|
-
if (pxt.appTarget && pxt.appTarget.appTheme && pxt.appTarget.appTheme.extendFieldEditors) {
|
|
42
|
-
const opts = {};
|
|
43
|
-
_initEditorExtensionsPromise = _initEditorExtensionsPromise
|
|
44
|
-
.then(() => pxt.BrowserUtils.loadBlocklyAsync())
|
|
45
|
-
.then(() => pxt.BrowserUtils.loadScriptAsync("fieldeditors.js"))
|
|
46
|
-
.then(() => pxt.editor.initFieldExtensionsAsync(opts))
|
|
47
|
-
.then(res => {
|
|
48
|
-
if (res.fieldEditors)
|
|
49
|
-
res.fieldEditors.forEach(fi => {
|
|
50
|
-
pxt.blocks.registerFieldEditor(fi.selector, fi.editor, fi.validator);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return _initEditorExtensionsPromise;
|
|
56
|
-
}
|
|
57
|
-
editor.initEditorExtensionsAsync = initEditorExtensionsAsync;
|
|
58
|
-
})(editor = pxt.editor || (pxt.editor = {}));
|
|
59
|
-
})(pxt || (pxt = {}));
|
|
60
|
-
var pxt;
|
|
61
|
-
(function (pxt) {
|
|
62
|
-
var editor;
|
|
63
|
-
(function (editor_1) {
|
|
64
|
-
const pendingRequests = {};
|
|
65
|
-
/**
|
|
66
|
-
* Binds incoming window messages to the project view.
|
|
67
|
-
* Requires the "allowParentController" flag in the pxtarget.json/appTheme object.
|
|
68
|
-
*
|
|
69
|
-
* When the project view receives a request (EditorMessageRequest),
|
|
70
|
-
* it starts the command and returns the result upon completion.
|
|
71
|
-
* The response (EditorMessageResponse) contains the request id and result.
|
|
72
|
-
* Some commands may be async, use the ``id`` field to correlate to the original request.
|
|
73
|
-
*/
|
|
74
|
-
function bindEditorMessages(getEditorAsync) {
|
|
75
|
-
const allowEditorMessages = (pxt.appTarget.appTheme.allowParentController || pxt.shell.isControllerMode())
|
|
76
|
-
&& pxt.BrowserUtils.isIFrame();
|
|
77
|
-
const allowExtensionMessages = pxt.appTarget.appTheme.allowPackageExtensions;
|
|
78
|
-
const allowSimTelemetry = pxt.appTarget.appTheme.allowSimulatorTelemetry;
|
|
79
|
-
if (!allowEditorMessages && !allowExtensionMessages && !allowSimTelemetry)
|
|
80
|
-
return;
|
|
81
|
-
window.addEventListener("message", (msg) => {
|
|
82
|
-
const data = msg.data;
|
|
83
|
-
if (!data || !/^pxt(host|editor|pkgext|sim)$/.test(data.type))
|
|
84
|
-
return false;
|
|
85
|
-
if (data.type === "pxtpkgext" && allowExtensionMessages) {
|
|
86
|
-
// Messages sent to the editor iframe from a child iframe containing an extension
|
|
87
|
-
getEditorAsync().then(projectView => {
|
|
88
|
-
projectView.handleExtensionRequest(data);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
else if (data.type === "pxtsim" && allowSimTelemetry) {
|
|
92
|
-
const event = data;
|
|
93
|
-
if (event.action === "event") {
|
|
94
|
-
if (event.category || event.message) {
|
|
95
|
-
pxt.reportError(event.category, event.message, event.data);
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
pxt.tickEvent(event.tick, event.data);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
else if (allowEditorMessages) {
|
|
103
|
-
// Messages sent to the editor from the parent frame
|
|
104
|
-
let p = Promise.resolve();
|
|
105
|
-
let resp = undefined;
|
|
106
|
-
if (data.type == "pxthost") { // response from the host
|
|
107
|
-
const req = pendingRequests[data.id];
|
|
108
|
-
if (!req) {
|
|
109
|
-
pxt.debug(`pxthost: unknown request ${data.id}`);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
p = p.then(() => req.resolve(data));
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
else if (data.type == "pxteditor") { // request from the editor
|
|
116
|
-
p = p.then(() => {
|
|
117
|
-
return getEditorAsync().then(projectView => {
|
|
118
|
-
const req = data;
|
|
119
|
-
pxt.debug(`pxteditor: ${req.action}`);
|
|
120
|
-
switch (req.action.toLowerCase()) {
|
|
121
|
-
case "switchjavascript": return Promise.resolve().then(() => projectView.openJavaScript());
|
|
122
|
-
case "switchpython": return Promise.resolve().then(() => projectView.openPython());
|
|
123
|
-
case "switchblocks": return Promise.resolve().then(() => projectView.openBlocks());
|
|
124
|
-
case "startsimulator": return Promise.resolve().then(() => projectView.startSimulator());
|
|
125
|
-
case "restartsimulator": return Promise.resolve().then(() => projectView.restartSimulator());
|
|
126
|
-
case "hidesimulator": return Promise.resolve().then(() => projectView.collapseSimulator());
|
|
127
|
-
case "showsimulator": return Promise.resolve().then(() => projectView.expandSimulator());
|
|
128
|
-
case "closeflyout": return Promise.resolve().then(() => projectView.closeFlyout());
|
|
129
|
-
case "unloadproject": return Promise.resolve().then(() => projectView.unloadProjectAsync());
|
|
130
|
-
case "saveproject": return projectView.saveProjectAsync();
|
|
131
|
-
case "redo": return Promise.resolve()
|
|
132
|
-
.then(() => {
|
|
133
|
-
const editor = projectView.editor;
|
|
134
|
-
if (editor && editor.hasRedo())
|
|
135
|
-
editor.redo();
|
|
136
|
-
});
|
|
137
|
-
case "undo": return Promise.resolve()
|
|
138
|
-
.then(() => {
|
|
139
|
-
const editor = projectView.editor;
|
|
140
|
-
if (editor && editor.hasUndo())
|
|
141
|
-
editor.undo();
|
|
142
|
-
});
|
|
143
|
-
case "setscale": {
|
|
144
|
-
const zoommsg = data;
|
|
145
|
-
return Promise.resolve()
|
|
146
|
-
.then(() => projectView.editor.setScale(zoommsg.scale));
|
|
147
|
-
}
|
|
148
|
-
case "stopsimulator": {
|
|
149
|
-
const stop = data;
|
|
150
|
-
return Promise.resolve()
|
|
151
|
-
.then(() => projectView.stopSimulator(stop.unload));
|
|
152
|
-
}
|
|
153
|
-
case "newproject": {
|
|
154
|
-
const create = data;
|
|
155
|
-
return Promise.resolve()
|
|
156
|
-
.then(() => projectView.newProject(create.options));
|
|
157
|
-
}
|
|
158
|
-
case "importproject": {
|
|
159
|
-
const load = data;
|
|
160
|
-
return Promise.resolve()
|
|
161
|
-
.then(() => projectView.importProjectAsync(load.project, {
|
|
162
|
-
filters: load.filters,
|
|
163
|
-
searchBar: load.searchBar
|
|
164
|
-
}));
|
|
165
|
-
}
|
|
166
|
-
case "openheader": {
|
|
167
|
-
const open = data;
|
|
168
|
-
return projectView.openProjectByHeaderIdAsync(open.headerId);
|
|
169
|
-
}
|
|
170
|
-
case "startactivity": {
|
|
171
|
-
const msg = data;
|
|
172
|
-
let tutorialPath = msg.path;
|
|
173
|
-
let editorProjectName = undefined;
|
|
174
|
-
if (/^([jt]s|py|blocks?):/i.test(tutorialPath)) {
|
|
175
|
-
if (/^py:/i.test(tutorialPath))
|
|
176
|
-
editorProjectName = pxt.PYTHON_PROJECT_NAME;
|
|
177
|
-
else if (/^[jt]s:/i.test(tutorialPath))
|
|
178
|
-
editorProjectName = pxt.JAVASCRIPT_PROJECT_NAME;
|
|
179
|
-
else
|
|
180
|
-
editorProjectName = pxt.BLOCKS_PROJECT_NAME;
|
|
181
|
-
tutorialPath = tutorialPath.substr(tutorialPath.indexOf(':') + 1);
|
|
182
|
-
}
|
|
183
|
-
return Promise.resolve()
|
|
184
|
-
.then(() => projectView.startActivity({
|
|
185
|
-
activity: msg.activityType,
|
|
186
|
-
path: tutorialPath,
|
|
187
|
-
title: msg.title,
|
|
188
|
-
editor: editorProjectName,
|
|
189
|
-
previousProjectHeaderId: msg.previousProjectHeaderId,
|
|
190
|
-
carryoverPreviousCode: msg.carryoverPreviousCode
|
|
191
|
-
}));
|
|
192
|
-
}
|
|
193
|
-
case "importtutorial": {
|
|
194
|
-
const load = data;
|
|
195
|
-
return Promise.resolve()
|
|
196
|
-
.then(() => projectView.importTutorialAsync(load.markdown));
|
|
197
|
-
}
|
|
198
|
-
case "proxytosim": {
|
|
199
|
-
const simmsg = data;
|
|
200
|
-
return Promise.resolve()
|
|
201
|
-
.then(() => projectView.proxySimulatorMessage(simmsg.content));
|
|
202
|
-
}
|
|
203
|
-
case "renderblocks": {
|
|
204
|
-
const rendermsg = data;
|
|
205
|
-
return Promise.resolve()
|
|
206
|
-
.then(() => projectView.renderBlocksAsync(rendermsg))
|
|
207
|
-
.then(r => {
|
|
208
|
-
return r.xml.then((svg) => {
|
|
209
|
-
resp = svg.xml;
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
case "renderpython": {
|
|
214
|
-
const rendermsg = data;
|
|
215
|
-
return Promise.resolve()
|
|
216
|
-
.then(() => projectView.renderPythonAsync(rendermsg))
|
|
217
|
-
.then(r => {
|
|
218
|
-
resp = r.python;
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
case "toggletrace": {
|
|
222
|
-
const togglemsg = data;
|
|
223
|
-
return Promise.resolve()
|
|
224
|
-
.then(() => projectView.toggleTrace(togglemsg.intervalSpeed));
|
|
225
|
-
}
|
|
226
|
-
case "settracestate": {
|
|
227
|
-
const trcmsg = data;
|
|
228
|
-
return Promise.resolve()
|
|
229
|
-
.then(() => projectView.setTrace(trcmsg.enabled, trcmsg.intervalSpeed));
|
|
230
|
-
}
|
|
231
|
-
case "setsimulatorfullscreen": {
|
|
232
|
-
const fsmsg = data;
|
|
233
|
-
return Promise.resolve()
|
|
234
|
-
.then(() => projectView.setSimulatorFullScreen(fsmsg.enabled));
|
|
235
|
-
}
|
|
236
|
-
case "togglehighcontrast": {
|
|
237
|
-
return Promise.resolve()
|
|
238
|
-
.then(() => projectView.toggleHighContrast());
|
|
239
|
-
}
|
|
240
|
-
case "sethighcontrast": {
|
|
241
|
-
const hcmsg = data;
|
|
242
|
-
return Promise.resolve()
|
|
243
|
-
.then(() => projectView.setHighContrast(hcmsg.on));
|
|
244
|
-
}
|
|
245
|
-
case "togglegreenscreen": {
|
|
246
|
-
return Promise.resolve()
|
|
247
|
-
.then(() => projectView.toggleGreenScreen());
|
|
248
|
-
}
|
|
249
|
-
case "print": {
|
|
250
|
-
return Promise.resolve()
|
|
251
|
-
.then(() => projectView.printCode());
|
|
252
|
-
}
|
|
253
|
-
case "pair": {
|
|
254
|
-
return projectView.pairAsync().then(() => { });
|
|
255
|
-
}
|
|
256
|
-
case "info": {
|
|
257
|
-
return Promise.resolve()
|
|
258
|
-
.then(() => {
|
|
259
|
-
resp = {
|
|
260
|
-
versions: pxt.appTarget.versions,
|
|
261
|
-
locale: ts.pxtc.Util.userLanguage(),
|
|
262
|
-
availableLocales: pxt.appTarget.appTheme.availableLocales
|
|
263
|
-
};
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
case "shareproject": {
|
|
267
|
-
const msg = data;
|
|
268
|
-
return projectView.anonymousPublishHeaderByIdAsync(msg.headerId, msg.projectName)
|
|
269
|
-
.then(scriptInfo => {
|
|
270
|
-
resp = scriptInfo;
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
case "savelocalprojectstocloud": {
|
|
274
|
-
const msg = data;
|
|
275
|
-
return projectView.saveLocalProjectsToCloudAsync(msg.headerIds)
|
|
276
|
-
.then(guidMap => {
|
|
277
|
-
resp = {
|
|
278
|
-
headerIdMap: guidMap
|
|
279
|
-
};
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
case "requestprojectcloudstatus": {
|
|
283
|
-
// Responses are sent as separate "projectcloudstatus" messages.
|
|
284
|
-
const msg = data;
|
|
285
|
-
return projectView.requestProjectCloudStatus(msg.headerIds);
|
|
286
|
-
}
|
|
287
|
-
case "convertcloudprojectstolocal": {
|
|
288
|
-
const msg = data;
|
|
289
|
-
return projectView.convertCloudProjectsToLocal(msg.userId);
|
|
290
|
-
}
|
|
291
|
-
case "setlanguagerestriction": {
|
|
292
|
-
const msg = data;
|
|
293
|
-
if (msg.restriction === "no-blocks") {
|
|
294
|
-
console.warn("no-blocks language restriction is not supported");
|
|
295
|
-
throw new Error("no-blocks language restriction is not supported");
|
|
296
|
-
}
|
|
297
|
-
return projectView.setLanguageRestrictionAsync(msg.restriction);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return Promise.resolve();
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
p.then(() => sendResponse(data, resp, true, undefined), (err) => sendResponse(data, resp, false, err));
|
|
305
|
-
}
|
|
306
|
-
return true;
|
|
307
|
-
}, false);
|
|
308
|
-
}
|
|
309
|
-
editor_1.bindEditorMessages = bindEditorMessages;
|
|
310
|
-
/**
|
|
311
|
-
* Sends analytics messages upstream to container if any
|
|
312
|
-
*/
|
|
313
|
-
function enableControllerAnalytics() {
|
|
314
|
-
if (!pxt.appTarget.appTheme.allowParentController || !pxt.BrowserUtils.isIFrame())
|
|
315
|
-
return;
|
|
316
|
-
const te = pxt.tickEvent;
|
|
317
|
-
pxt.tickEvent = function (id, data) {
|
|
318
|
-
if (te)
|
|
319
|
-
te(id, data);
|
|
320
|
-
postHostMessageAsync({
|
|
321
|
-
type: 'pxthost',
|
|
322
|
-
action: 'event',
|
|
323
|
-
tick: id,
|
|
324
|
-
response: false,
|
|
325
|
-
data
|
|
326
|
-
});
|
|
327
|
-
};
|
|
328
|
-
const rexp = pxt.reportException;
|
|
329
|
-
pxt.reportException = function (err, data) {
|
|
330
|
-
if (rexp)
|
|
331
|
-
rexp(err, data);
|
|
332
|
-
try {
|
|
333
|
-
postHostMessageAsync({
|
|
334
|
-
type: 'pxthost',
|
|
335
|
-
action: 'event',
|
|
336
|
-
tick: 'error',
|
|
337
|
-
message: err.message,
|
|
338
|
-
response: false,
|
|
339
|
-
data
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
catch (e) {
|
|
343
|
-
}
|
|
344
|
-
};
|
|
345
|
-
const re = pxt.reportError;
|
|
346
|
-
pxt.reportError = function (cat, msg, data) {
|
|
347
|
-
if (re)
|
|
348
|
-
re(cat, msg, data);
|
|
349
|
-
postHostMessageAsync({
|
|
350
|
-
type: 'pxthost',
|
|
351
|
-
action: 'event',
|
|
352
|
-
tick: 'error',
|
|
353
|
-
category: cat,
|
|
354
|
-
message: msg,
|
|
355
|
-
data
|
|
356
|
-
});
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
editor_1.enableControllerAnalytics = enableControllerAnalytics;
|
|
360
|
-
function sendResponse(request, resp, success, error) {
|
|
361
|
-
if (request.response) {
|
|
362
|
-
window.parent.postMessage({
|
|
363
|
-
type: request.type,
|
|
364
|
-
id: request.id,
|
|
365
|
-
resp,
|
|
366
|
-
success,
|
|
367
|
-
error
|
|
368
|
-
}, "*");
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Determines if host messages should be posted
|
|
373
|
-
*/
|
|
374
|
-
function shouldPostHostMessages() {
|
|
375
|
-
return pxt.appTarget.appTheme.allowParentController && pxt.BrowserUtils.isIFrame();
|
|
376
|
-
}
|
|
377
|
-
editor_1.shouldPostHostMessages = shouldPostHostMessages;
|
|
378
|
-
/**
|
|
379
|
-
* Posts a message from the editor to the host
|
|
380
|
-
*/
|
|
381
|
-
function postHostMessageAsync(msg) {
|
|
382
|
-
return new Promise((resolve, reject) => {
|
|
383
|
-
const env = pxt.Util.clone(msg);
|
|
384
|
-
env.id = ts.pxtc.Util.guidGen();
|
|
385
|
-
if (msg.response)
|
|
386
|
-
pendingRequests[env.id] = { resolve, reject };
|
|
387
|
-
window.parent.postMessage(env, "*");
|
|
388
|
-
if (!msg.response)
|
|
389
|
-
resolve(undefined);
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
editor_1.postHostMessageAsync = postHostMessageAsync;
|
|
393
|
-
})(editor = pxt.editor || (pxt.editor = {}));
|
|
394
|
-
})(pxt || (pxt = {}));
|
|
395
|
-
var pxt;
|
|
396
|
-
(function (pxt) {
|
|
397
|
-
var editor;
|
|
398
|
-
(function (editor) {
|
|
399
|
-
var experiments;
|
|
400
|
-
(function (experiments_1) {
|
|
401
|
-
function key(experiment) {
|
|
402
|
-
const id = (typeof experiment === "object") ? experiment.id : experiment;
|
|
403
|
-
return `experiments-${id}`;
|
|
404
|
-
}
|
|
405
|
-
function syncTheme() {
|
|
406
|
-
const theme = pxt.savedAppTheme();
|
|
407
|
-
const r = {};
|
|
408
|
-
const experiments = all();
|
|
409
|
-
experiments.forEach(experiment => {
|
|
410
|
-
const enabled = isEnabled(experiment);
|
|
411
|
-
theme[experiment.id] = !!enabled;
|
|
412
|
-
if (enabled)
|
|
413
|
-
r[experiment.id] = enabled ? 1 : 0;
|
|
414
|
-
});
|
|
415
|
-
if (experiments.length && Object.keys(r).length) {
|
|
416
|
-
pxt.tickEvent("experiments.loaded", r);
|
|
417
|
-
pxt.reloadAppTargetVariant();
|
|
418
|
-
}
|
|
419
|
-
return pxt.appTarget.appTheme;
|
|
420
|
-
}
|
|
421
|
-
experiments_1.syncTheme = syncTheme;
|
|
422
|
-
function all() {
|
|
423
|
-
const ids = pxt.appTarget.appTheme.experiments;
|
|
424
|
-
if (!ids)
|
|
425
|
-
return [];
|
|
426
|
-
return [
|
|
427
|
-
{
|
|
428
|
-
id: "print",
|
|
429
|
-
name: lf("Print Code"),
|
|
430
|
-
description: lf("Print the code from the current project"),
|
|
431
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/4740"
|
|
432
|
-
},
|
|
433
|
-
{
|
|
434
|
-
id: "greenScreen",
|
|
435
|
-
name: lf("Green screen"),
|
|
436
|
-
description: lf("Display a webcam video stream or a green background behind the code."),
|
|
437
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/4738"
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
id: "allowPackageExtensions",
|
|
441
|
-
name: lf("Editor Extensions"),
|
|
442
|
-
description: lf("Allow Extensions to add buttons in the editor."),
|
|
443
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/4741"
|
|
444
|
-
},
|
|
445
|
-
{
|
|
446
|
-
id: "instructions",
|
|
447
|
-
name: lf("Wiring Instructions"),
|
|
448
|
-
description: lf("Generate step-by-step assembly instructions for breadboard wiring."),
|
|
449
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/4739"
|
|
450
|
-
},
|
|
451
|
-
{
|
|
452
|
-
id: "debugger",
|
|
453
|
-
name: lf("Debugger"),
|
|
454
|
-
description: lf("Step through code and inspect variables in the debugger"),
|
|
455
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/4729"
|
|
456
|
-
},
|
|
457
|
-
{
|
|
458
|
-
id: "bluetoothUartConsole",
|
|
459
|
-
name: "Bluetooth Console",
|
|
460
|
-
description: lf("Receives UART message through Web Bluetooth"),
|
|
461
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/4796"
|
|
462
|
-
},
|
|
463
|
-
{
|
|
464
|
-
id: "bluetoothPartialFlashing",
|
|
465
|
-
name: "Bluetooth Download",
|
|
466
|
-
description: lf("Download code via Web Bluetooth"),
|
|
467
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/4807"
|
|
468
|
-
},
|
|
469
|
-
{
|
|
470
|
-
id: "simScreenshot",
|
|
471
|
-
name: lf("Simulator Screenshots"),
|
|
472
|
-
description: lf("Download screenshots of the simulator"),
|
|
473
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/5232"
|
|
474
|
-
},
|
|
475
|
-
{
|
|
476
|
-
id: "python",
|
|
477
|
-
name: lf("Static Python"),
|
|
478
|
-
description: lf("Use Static Python to code your device"),
|
|
479
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/5390"
|
|
480
|
-
},
|
|
481
|
-
{
|
|
482
|
-
id: "simGif",
|
|
483
|
-
name: lf("Simulator Gifs"),
|
|
484
|
-
description: lf("Download gifs of the simulator"),
|
|
485
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/5297"
|
|
486
|
-
},
|
|
487
|
-
{
|
|
488
|
-
id: "qrCode",
|
|
489
|
-
name: lf("Shared QR Code"),
|
|
490
|
-
description: lf("Generate a QR Code form the shared project url"),
|
|
491
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/5456"
|
|
492
|
-
},
|
|
493
|
-
{
|
|
494
|
-
id: "importExtensionFiles",
|
|
495
|
-
name: lf("Import Extension Files"),
|
|
496
|
-
description: lf("Import Extensions from compiled project files")
|
|
497
|
-
},
|
|
498
|
-
{
|
|
499
|
-
id: "debugExtensionCode",
|
|
500
|
-
name: lf("Debug Extension Code"),
|
|
501
|
-
description: lf("Use the JavaScript debugger to debug extension code")
|
|
502
|
-
},
|
|
503
|
-
{
|
|
504
|
-
id: "snippetBuilder",
|
|
505
|
-
name: lf("Snippet Builder"),
|
|
506
|
-
description: lf("Try out the new snippet dialogs.")
|
|
507
|
-
},
|
|
508
|
-
{
|
|
509
|
-
id: "experimentalHw",
|
|
510
|
-
name: lf("Experimental Hardware"),
|
|
511
|
-
description: lf("Enable support for hardware marked 'experimental' in the hardware seletion dialog")
|
|
512
|
-
},
|
|
513
|
-
{
|
|
514
|
-
id: "checkForHwVariantWebUSB",
|
|
515
|
-
name: lf("Detect Hardware with WebUSB"),
|
|
516
|
-
description: lf("When compiling, use WebUSB to detect hardware configuration.")
|
|
517
|
-
},
|
|
518
|
-
{
|
|
519
|
-
id: "githubEditor",
|
|
520
|
-
name: lf("GitHub editor"),
|
|
521
|
-
description: lf("Review, commit and push to GitHub."),
|
|
522
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/6419",
|
|
523
|
-
enableOnline: true,
|
|
524
|
-
},
|
|
525
|
-
{
|
|
526
|
-
id: "githubCompiledJs",
|
|
527
|
-
name: lf("GitHub Pages JavaScript"),
|
|
528
|
-
description: lf("Commit compiled javascript when creating a release"),
|
|
529
|
-
enableOnline: true,
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
id: "blocksCollapsing",
|
|
533
|
-
name: lf("Collapse blocks"),
|
|
534
|
-
description: lf("Collapse and expand functions or event blocks")
|
|
535
|
-
},
|
|
536
|
-
{
|
|
537
|
-
id: "tutorialBlocksDiff",
|
|
538
|
-
name: lf("Tutorial Block Diffs"),
|
|
539
|
-
description: lf("Automatially render blocks diff in tutorials")
|
|
540
|
-
},
|
|
541
|
-
{
|
|
542
|
-
id: "openProjectNewTab",
|
|
543
|
-
name: lf("Open in New Tab"),
|
|
544
|
-
description: lf("Open an editor in a new tab.")
|
|
545
|
-
},
|
|
546
|
-
{
|
|
547
|
-
id: "openProjectNewDependentTab",
|
|
548
|
-
name: lf("Open in New Connected Tab"),
|
|
549
|
-
description: lf("Open connected editors in different browser tabs.")
|
|
550
|
-
},
|
|
551
|
-
{
|
|
552
|
-
id: "accessibleBlocks",
|
|
553
|
-
name: lf("Accessible Blocks"),
|
|
554
|
-
description: lf("Use the WASD keys to move and modify blocks."),
|
|
555
|
-
feedbackUrl: "https://github.com/microsoft/pxt/issues/6850"
|
|
556
|
-
},
|
|
557
|
-
{
|
|
558
|
-
id: "errorList",
|
|
559
|
-
name: lf("Error List"),
|
|
560
|
-
description: lf("Show an error list panel for JavaScript and Python.")
|
|
561
|
-
},
|
|
562
|
-
{
|
|
563
|
-
id: "blocksErrorList",
|
|
564
|
-
name: lf("Blocks Error List"),
|
|
565
|
-
description: lf("Show an error list panel for Blocks")
|
|
566
|
-
},
|
|
567
|
-
{
|
|
568
|
-
id: "timeMachine",
|
|
569
|
-
name: lf("Time Machine"),
|
|
570
|
-
description: lf("Save and restore past versions of a project")
|
|
571
|
-
},
|
|
572
|
-
].filter(experiment => ids.indexOf(experiment.id) > -1 && !(pxt.BrowserUtils.isPxtElectron() && experiment.enableOnline));
|
|
573
|
-
}
|
|
574
|
-
experiments_1.all = all;
|
|
575
|
-
function clear() {
|
|
576
|
-
all().forEach(experiment => pxt.storage.removeLocal(key(experiment)));
|
|
577
|
-
syncTheme();
|
|
578
|
-
}
|
|
579
|
-
experiments_1.clear = clear;
|
|
580
|
-
function someEnabled() {
|
|
581
|
-
return all().some(experiment => isEnabled(experiment));
|
|
582
|
-
}
|
|
583
|
-
experiments_1.someEnabled = someEnabled;
|
|
584
|
-
function isEnabled(experiment) {
|
|
585
|
-
return !!pxt.storage.getLocal(key(experiment));
|
|
586
|
-
}
|
|
587
|
-
experiments_1.isEnabled = isEnabled;
|
|
588
|
-
function toggle(experiment) {
|
|
589
|
-
setState(experiment, !isEnabled(experiment));
|
|
590
|
-
}
|
|
591
|
-
experiments_1.toggle = toggle;
|
|
592
|
-
function state() {
|
|
593
|
-
const r = {};
|
|
594
|
-
all().forEach(experiment => r[experiment.id] = isEnabled(experiment));
|
|
595
|
-
return JSON.stringify(r);
|
|
596
|
-
}
|
|
597
|
-
experiments_1.state = state;
|
|
598
|
-
function setState(experiment, enabled) {
|
|
599
|
-
if (enabled == isEnabled(experiment))
|
|
600
|
-
return; // no changes
|
|
601
|
-
if (enabled)
|
|
602
|
-
pxt.storage.setLocal(key(experiment), "1");
|
|
603
|
-
else
|
|
604
|
-
pxt.storage.removeLocal(key(experiment));
|
|
605
|
-
// sync theme
|
|
606
|
-
syncTheme();
|
|
607
|
-
}
|
|
608
|
-
experiments_1.setState = setState;
|
|
609
|
-
})(experiments = editor.experiments || (editor.experiments = {}));
|
|
610
|
-
})(editor = pxt.editor || (pxt.editor = {}));
|
|
611
|
-
})(pxt || (pxt = {}));
|
|
612
|
-
var pxt;
|
|
613
|
-
(function (pxt) {
|
|
614
|
-
var workspace;
|
|
615
|
-
(function (workspace) {
|
|
616
|
-
// 5 minutes. This is overridden in pxtarget.json
|
|
617
|
-
const DEFAULT_DIFF_HISTORY_INTERVAL = 1000 * 60 * 5;
|
|
618
|
-
// 15 minutes. This is overridden in pxtarget.json
|
|
619
|
-
const DEFAULT_SNAPSHOT_HISTORY_INTERVAL = 1000 * 60 * 15;
|
|
620
|
-
const ONE_DAY = 1000 * 60 * 60 * 24;
|
|
621
|
-
function collapseHistory(history, text, options, diff, patch) {
|
|
622
|
-
var _a, _b;
|
|
623
|
-
const newHistory = [];
|
|
624
|
-
let current = Object.assign({}, text);
|
|
625
|
-
let lastVersion = (_b = (_a = pxt.appTarget) === null || _a === void 0 ? void 0 : _a.versions) === null || _b === void 0 ? void 0 : _b.target;
|
|
626
|
-
let lastTime = undefined;
|
|
627
|
-
let lastTimeIndex = undefined;
|
|
628
|
-
let lastTimeText = undefined;
|
|
629
|
-
let { interval, minTime, maxTime } = options;
|
|
630
|
-
if (minTime === undefined) {
|
|
631
|
-
minTime = 0;
|
|
632
|
-
}
|
|
633
|
-
if (maxTime === undefined) {
|
|
634
|
-
maxTime = history[history.length - 1].timestamp;
|
|
635
|
-
}
|
|
636
|
-
for (let i = history.length - 1; i >= 0; i--) {
|
|
637
|
-
const entry = history[i];
|
|
638
|
-
if (entry.timestamp > maxTime) {
|
|
639
|
-
newHistory.unshift(entry);
|
|
640
|
-
current = applyDiff(current, entry, patch);
|
|
641
|
-
continue;
|
|
642
|
-
}
|
|
643
|
-
else if (entry.timestamp < minTime) {
|
|
644
|
-
if (lastTimeIndex !== undefined) {
|
|
645
|
-
if (lastTimeIndex - i > 1) {
|
|
646
|
-
newHistory.unshift({
|
|
647
|
-
timestamp: lastTime,
|
|
648
|
-
editorVersion: lastVersion,
|
|
649
|
-
changes: diffScriptText(current, lastTimeText, lastTime, diff).changes
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
else {
|
|
653
|
-
newHistory.unshift(history[lastTimeIndex]);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
newHistory.unshift(entry);
|
|
657
|
-
lastTimeIndex = undefined;
|
|
658
|
-
continue;
|
|
659
|
-
}
|
|
660
|
-
else if (lastTimeIndex === undefined) {
|
|
661
|
-
lastTimeText = Object.assign({}, current);
|
|
662
|
-
lastTime = entry.timestamp;
|
|
663
|
-
lastVersion = entry.editorVersion;
|
|
664
|
-
lastTimeIndex = i;
|
|
665
|
-
current = applyDiff(current, entry, patch);
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
if (lastTime - entry.timestamp > interval) {
|
|
669
|
-
if (lastTimeIndex - i > 1) {
|
|
670
|
-
newHistory.unshift({
|
|
671
|
-
timestamp: lastTime,
|
|
672
|
-
editorVersion: lastVersion,
|
|
673
|
-
changes: diffScriptText(current, lastTimeText, lastTime, diff).changes
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
else {
|
|
677
|
-
newHistory.unshift(history[lastTimeIndex]);
|
|
678
|
-
}
|
|
679
|
-
lastTimeText = Object.assign({}, current);
|
|
680
|
-
current = applyDiff(current, entry, patch);
|
|
681
|
-
lastTimeIndex = i;
|
|
682
|
-
lastTime = entry.timestamp;
|
|
683
|
-
lastVersion = entry.editorVersion;
|
|
684
|
-
}
|
|
685
|
-
else {
|
|
686
|
-
current = applyDiff(current, entry, patch);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
if (lastTimeIndex !== undefined) {
|
|
690
|
-
if (lastTimeIndex) {
|
|
691
|
-
newHistory.unshift({
|
|
692
|
-
timestamp: lastTime,
|
|
693
|
-
editorVersion: lastVersion,
|
|
694
|
-
changes: diffScriptText(current, lastTimeText, lastTime, diff).changes
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
else {
|
|
698
|
-
newHistory.unshift(history[0]);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
return newHistory;
|
|
702
|
-
}
|
|
703
|
-
workspace.collapseHistory = collapseHistory;
|
|
704
|
-
function diffScriptText(oldVersion, newVersion, time, diff) {
|
|
705
|
-
var _a, _b;
|
|
706
|
-
const changes = [];
|
|
707
|
-
for (const file of Object.keys(oldVersion)) {
|
|
708
|
-
if (!(file.endsWith(".ts") || file.endsWith(".jres") || file.endsWith(".py") || file.endsWith(".blocks") || file === "pxt.json"))
|
|
709
|
-
continue;
|
|
710
|
-
if (newVersion[file] == undefined) {
|
|
711
|
-
changes.push({
|
|
712
|
-
type: "removed",
|
|
713
|
-
filename: file,
|
|
714
|
-
value: oldVersion[file]
|
|
715
|
-
});
|
|
716
|
-
}
|
|
717
|
-
else if (oldVersion[file] !== newVersion[file]) {
|
|
718
|
-
changes.push({
|
|
719
|
-
type: "edited",
|
|
720
|
-
filename: file,
|
|
721
|
-
patch: diff(newVersion[file], oldVersion[file])
|
|
722
|
-
});
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
for (const file of Object.keys(newVersion)) {
|
|
726
|
-
if (!(file.endsWith(".ts") || file.endsWith(".jres") || file.endsWith(".py") || file.endsWith(".blocks") || file === "pxt.json"))
|
|
727
|
-
continue;
|
|
728
|
-
if (oldVersion[file] == undefined) {
|
|
729
|
-
changes.push({
|
|
730
|
-
type: "added",
|
|
731
|
-
filename: file,
|
|
732
|
-
value: newVersion[file]
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
if (!changes.length)
|
|
737
|
-
return undefined;
|
|
738
|
-
return {
|
|
739
|
-
timestamp: time,
|
|
740
|
-
editorVersion: (_b = (_a = pxt.appTarget) === null || _a === void 0 ? void 0 : _a.versions) === null || _b === void 0 ? void 0 : _b.target,
|
|
741
|
-
changes
|
|
742
|
-
};
|
|
743
|
-
}
|
|
744
|
-
workspace.diffScriptText = diffScriptText;
|
|
745
|
-
function applyDiff(text, history, patch) {
|
|
746
|
-
const result = Object.assign({}, text);
|
|
747
|
-
for (const change of history.changes) {
|
|
748
|
-
if (change.type === "added") {
|
|
749
|
-
delete result[change.filename];
|
|
750
|
-
}
|
|
751
|
-
else if (change.type === "removed") {
|
|
752
|
-
result[change.filename] = change.value;
|
|
753
|
-
}
|
|
754
|
-
else {
|
|
755
|
-
result[change.filename] = patch(change.patch, text[change.filename]);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return result;
|
|
759
|
-
}
|
|
760
|
-
workspace.applyDiff = applyDiff;
|
|
761
|
-
function createSnapshot(text) {
|
|
762
|
-
try {
|
|
763
|
-
const result = {};
|
|
764
|
-
const config = JSON.parse(text[pxt.CONFIG_NAME]);
|
|
765
|
-
for (const file of config.files) {
|
|
766
|
-
// these files will just get regenrated
|
|
767
|
-
if (file === pxt.IMAGES_CODE || file === pxt.TILEMAP_CODE) {
|
|
768
|
-
result[file] = "";
|
|
769
|
-
}
|
|
770
|
-
else {
|
|
771
|
-
result[file] = text[file];
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
result[pxt.CONFIG_NAME] = text[pxt.CONFIG_NAME];
|
|
775
|
-
// main.ts will also be regenerated if blocks/python
|
|
776
|
-
if (config.preferredEditor === pxt.BLOCKS_PROJECT_NAME) {
|
|
777
|
-
if (result[pxt.MAIN_BLOCKS])
|
|
778
|
-
result[pxt.MAIN_TS] = "";
|
|
779
|
-
}
|
|
780
|
-
else if (config.preferredEditor === pxt.PYTHON_PROJECT_NAME) {
|
|
781
|
-
if (result[pxt.MAIN_PY])
|
|
782
|
-
result[pxt.MAIN_TS] = "";
|
|
783
|
-
}
|
|
784
|
-
if (config.testFiles) {
|
|
785
|
-
for (const file of config.testFiles) {
|
|
786
|
-
result[file] = text[file];
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
return result;
|
|
790
|
-
}
|
|
791
|
-
catch (e) {
|
|
792
|
-
return Object.assign({}, text);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
workspace.createSnapshot = createSnapshot;
|
|
796
|
-
function applySnapshot(text, snapshot) {
|
|
797
|
-
var _a;
|
|
798
|
-
try {
|
|
799
|
-
const result = Object.assign({}, snapshot);
|
|
800
|
-
const config = JSON.parse(text[pxt.CONFIG_NAME]);
|
|
801
|
-
// preserve any files from the current text that aren't in the config; this is just to make
|
|
802
|
-
// sure that our internal files like history, markdown, serial output are preserved
|
|
803
|
-
for (const file of Object.keys(text)) {
|
|
804
|
-
if (config.files.indexOf(file) === -1 && ((_a = config.testFiles) === null || _a === void 0 ? void 0 : _a.indexOf(file)) === -1 && !result[file]) {
|
|
805
|
-
result[file] = text[file];
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
return result;
|
|
809
|
-
}
|
|
810
|
-
catch (e) {
|
|
811
|
-
const result = Object.assign({}, text);
|
|
812
|
-
for (const file of Object.keys(snapshot)) {
|
|
813
|
-
result[file] = snapshot[file];
|
|
814
|
-
}
|
|
815
|
-
return result;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
workspace.applySnapshot = applySnapshot;
|
|
819
|
-
function parseHistoryFile(text) {
|
|
820
|
-
const result = JSON.parse(text);
|
|
821
|
-
if (!result.entries)
|
|
822
|
-
result.entries = [];
|
|
823
|
-
if (!result.shares)
|
|
824
|
-
result.shares = [];
|
|
825
|
-
if (!result.snapshots)
|
|
826
|
-
result.snapshots = [];
|
|
827
|
-
return result;
|
|
828
|
-
}
|
|
829
|
-
workspace.parseHistoryFile = parseHistoryFile;
|
|
830
|
-
function updateHistory(previousText, toWrite, currentTime, shares, diff, patch) {
|
|
831
|
-
let history;
|
|
832
|
-
// Always base the history off of what was in the previousText,
|
|
833
|
-
// which is written to disk. The new text could have corrupted it
|
|
834
|
-
// in some way
|
|
835
|
-
if (previousText[pxt.HISTORY_FILE]) {
|
|
836
|
-
history = pxt.workspace.parseHistoryFile(previousText[pxt.HISTORY_FILE]);
|
|
837
|
-
}
|
|
838
|
-
else {
|
|
839
|
-
history = {
|
|
840
|
-
entries: [],
|
|
841
|
-
snapshots: [takeSnapshot(previousText, currentTime - 1)],
|
|
842
|
-
shares: []
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
// First save any new project shares
|
|
846
|
-
for (const share of shares) {
|
|
847
|
-
if (!history.shares.some(s => s.id === share.id)) {
|
|
848
|
-
history.shares.push({
|
|
849
|
-
id: share.id,
|
|
850
|
-
timestamp: currentTime,
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
// If no source changed, we can bail at this point
|
|
855
|
-
if (scriptEquals(previousText, toWrite)) {
|
|
856
|
-
toWrite[pxt.HISTORY_FILE] = JSON.stringify(history);
|
|
857
|
-
return;
|
|
858
|
-
}
|
|
859
|
-
// Next, update the diff entries. We always update this, but may
|
|
860
|
-
// combine it with the previous diff if it's been less than the
|
|
861
|
-
// interval time
|
|
862
|
-
let shouldCombine = false;
|
|
863
|
-
if (history.entries.length > 1) {
|
|
864
|
-
const topTime = history.entries[history.entries.length - 1].timestamp;
|
|
865
|
-
const prevTime = history.entries[history.entries.length - 2].timestamp;
|
|
866
|
-
if (currentTime - topTime < diffInterval() && topTime - prevTime < diffInterval()) {
|
|
867
|
-
shouldCombine = true;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
if (shouldCombine) {
|
|
871
|
-
// Roll back the last diff and create a new one
|
|
872
|
-
const prevText = applyDiff(previousText, history.entries.pop(), patch);
|
|
873
|
-
const diffed = diffScriptText(prevText, toWrite, currentTime, diff);
|
|
874
|
-
if (diffed) {
|
|
875
|
-
history.entries.push(diffed);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
else {
|
|
879
|
-
const diffed = diffScriptText(previousText, toWrite, currentTime, diff);
|
|
880
|
-
if (diffed) {
|
|
881
|
-
history.entries.push(diffed);
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
// Finally, update the snapshots. These are failsafes in case something
|
|
885
|
-
// goes wrong with the diff history. We keep one snapshot per interval for
|
|
886
|
-
// the past 24 hours and one snapshot per day prior to that
|
|
887
|
-
if (history.snapshots.length == 0) {
|
|
888
|
-
history.snapshots.push(takeSnapshot(previousText, currentTime - 1));
|
|
889
|
-
}
|
|
890
|
-
else if (currentTime - history.snapshots[history.snapshots.length - 1].timestamp >= snapshotInterval()) {
|
|
891
|
-
history.snapshots.push(takeSnapshot(previousText, currentTime));
|
|
892
|
-
const trimmed = [];
|
|
893
|
-
let currentDay = Math.floor(currentTime / ONE_DAY) * ONE_DAY;
|
|
894
|
-
for (let i = 0; i < history.snapshots.length; i++) {
|
|
895
|
-
const current = history.snapshots[history.snapshots.length - 1 - i];
|
|
896
|
-
if (currentTime - current.timestamp < ONE_DAY || i === history.snapshots.length - 1) {
|
|
897
|
-
trimmed.unshift(current);
|
|
898
|
-
}
|
|
899
|
-
else if (current.timestamp < currentDay) {
|
|
900
|
-
trimmed.unshift(current);
|
|
901
|
-
currentDay = Math.floor(current.timestamp / ONE_DAY) * ONE_DAY;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
history.snapshots = trimmed;
|
|
905
|
-
}
|
|
906
|
-
toWrite[pxt.HISTORY_FILE] = JSON.stringify(history);
|
|
907
|
-
}
|
|
908
|
-
workspace.updateHistory = updateHistory;
|
|
909
|
-
function pushSnapshotOnHistory(text, currentTime) {
|
|
910
|
-
let history;
|
|
911
|
-
if (text[pxt.HISTORY_FILE]) {
|
|
912
|
-
history = pxt.workspace.parseHistoryFile(text[pxt.HISTORY_FILE]);
|
|
913
|
-
}
|
|
914
|
-
else {
|
|
915
|
-
history = {
|
|
916
|
-
entries: [],
|
|
917
|
-
snapshots: [],
|
|
918
|
-
shares: []
|
|
919
|
-
};
|
|
920
|
-
}
|
|
921
|
-
history.snapshots.push(takeSnapshot(text, currentTime));
|
|
922
|
-
text[pxt.HISTORY_FILE] = JSON.stringify(history);
|
|
923
|
-
}
|
|
924
|
-
workspace.pushSnapshotOnHistory = pushSnapshotOnHistory;
|
|
925
|
-
function updateShareHistory(text, currentTime, shares) {
|
|
926
|
-
let history;
|
|
927
|
-
if (text[pxt.HISTORY_FILE]) {
|
|
928
|
-
history = pxt.workspace.parseHistoryFile(text[pxt.HISTORY_FILE]);
|
|
929
|
-
}
|
|
930
|
-
else {
|
|
931
|
-
history = {
|
|
932
|
-
entries: [],
|
|
933
|
-
snapshots: [],
|
|
934
|
-
shares: []
|
|
935
|
-
};
|
|
936
|
-
}
|
|
937
|
-
for (const share of shares) {
|
|
938
|
-
if (!history.shares.some(s => s.id === share.id)) {
|
|
939
|
-
history.shares.push({
|
|
940
|
-
id: share.id,
|
|
941
|
-
timestamp: currentTime,
|
|
942
|
-
});
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
text[pxt.HISTORY_FILE] = JSON.stringify(history);
|
|
946
|
-
}
|
|
947
|
-
workspace.updateShareHistory = updateShareHistory;
|
|
948
|
-
function takeSnapshot(text, time) {
|
|
949
|
-
return {
|
|
950
|
-
timestamp: time,
|
|
951
|
-
editorVersion: pxt.appTarget.versions.target,
|
|
952
|
-
text: pxt.workspace.createSnapshot(text)
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
function scriptEquals(a, b) {
|
|
956
|
-
const aKeys = Object.keys(a);
|
|
957
|
-
const bKeys = Object.keys(b);
|
|
958
|
-
if (aKeys.length !== bKeys.length)
|
|
959
|
-
return false;
|
|
960
|
-
for (const key of aKeys) {
|
|
961
|
-
if (bKeys.indexOf(key) === -1)
|
|
962
|
-
return false;
|
|
963
|
-
if (a[key] !== b[key])
|
|
964
|
-
return false;
|
|
965
|
-
}
|
|
966
|
-
return true;
|
|
967
|
-
}
|
|
968
|
-
function diffInterval() {
|
|
969
|
-
var _a, _b;
|
|
970
|
-
if (((_b = (_a = pxt.appTarget) === null || _a === void 0 ? void 0 : _a.appTheme) === null || _b === void 0 ? void 0 : _b.timeMachineDiffInterval) != undefined) {
|
|
971
|
-
return pxt.appTarget.appTheme.timeMachineDiffInterval;
|
|
972
|
-
}
|
|
973
|
-
return DEFAULT_DIFF_HISTORY_INTERVAL;
|
|
974
|
-
}
|
|
975
|
-
function snapshotInterval() {
|
|
976
|
-
var _a, _b;
|
|
977
|
-
if (((_b = (_a = pxt.appTarget) === null || _a === void 0 ? void 0 : _a.appTheme) === null || _b === void 0 ? void 0 : _b.timeMachineSnapshotInterval) != undefined) {
|
|
978
|
-
return pxt.appTarget.appTheme.timeMachineSnapshotInterval;
|
|
979
|
-
}
|
|
980
|
-
return DEFAULT_SNAPSHOT_HISTORY_INTERVAL;
|
|
981
|
-
}
|
|
982
|
-
})(workspace = pxt.workspace || (pxt.workspace = {}));
|
|
983
|
-
})(pxt || (pxt = {}));
|
|
984
|
-
/// <reference path="../localtypings/monaco.d.ts" />
|
|
985
|
-
/// <reference path="../built/pxtlib.d.ts"/>
|
|
986
|
-
/// <reference path="../built/pxtblocks.d.ts"/>
|
|
987
|
-
var pxt;
|
|
988
|
-
(function (pxt) {
|
|
989
|
-
var vs;
|
|
990
|
-
(function (vs) {
|
|
991
|
-
function syncModels(mainPkg, libs, currFile, readOnly) {
|
|
992
|
-
if (readOnly)
|
|
993
|
-
return;
|
|
994
|
-
let extraLibs = monaco.languages.typescript.typescriptDefaults.getExtraLibs();
|
|
995
|
-
let modelMap = {};
|
|
996
|
-
mainPkg.sortedDeps().forEach(pkg => {
|
|
997
|
-
pkg.getFiles().forEach(f => {
|
|
998
|
-
let fp = pkg.id + "/" + f;
|
|
999
|
-
let proto = "pkg:" + fp;
|
|
1000
|
-
if (/\.(ts)$/.test(f) && fp != currFile) {
|
|
1001
|
-
if (!monaco.languages.typescript.typescriptDefaults.getExtraLibs()[fp]) {
|
|
1002
|
-
// inserting a space creates syntax errors in Python
|
|
1003
|
-
let content = pkg.readFile(f) || "\n";
|
|
1004
|
-
libs[fp] = monaco.languages.typescript.typescriptDefaults.addExtraLib(content, fp);
|
|
1005
|
-
}
|
|
1006
|
-
modelMap[fp] = "1";
|
|
1007
|
-
}
|
|
1008
|
-
});
|
|
1009
|
-
});
|
|
1010
|
-
// dispose of any extra libraries, the typescript worker will be killed as a result of this
|
|
1011
|
-
Object.keys(extraLibs)
|
|
1012
|
-
.filter(lib => /\.(ts)$/.test(lib) && !modelMap[lib])
|
|
1013
|
-
.forEach(lib => {
|
|
1014
|
-
libs[lib].dispose();
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
vs.syncModels = syncModels;
|
|
1018
|
-
function initMonacoAsync(element) {
|
|
1019
|
-
return new Promise((resolve, reject) => {
|
|
1020
|
-
if (typeof (window.monaco) === 'object') {
|
|
1021
|
-
// monaco is already loaded
|
|
1022
|
-
resolve(createEditor(element));
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
|
-
let monacoPaths = window.MonacoPaths;
|
|
1026
|
-
let onGotAmdLoader = () => {
|
|
1027
|
-
let req = window.require;
|
|
1028
|
-
req.config({ paths: monacoPaths });
|
|
1029
|
-
// Load monaco
|
|
1030
|
-
req(['vs/editor/editor.main'], () => {
|
|
1031
|
-
setupMonaco();
|
|
1032
|
-
resolve(createEditor(element));
|
|
1033
|
-
});
|
|
1034
|
-
};
|
|
1035
|
-
// Load AMD loader if necessary
|
|
1036
|
-
if (!window.require) {
|
|
1037
|
-
let loaderScript = document.createElement('script');
|
|
1038
|
-
loaderScript.type = 'text/javascript';
|
|
1039
|
-
loaderScript.src = monacoPaths['vs/loader'];
|
|
1040
|
-
loaderScript.addEventListener('load', onGotAmdLoader);
|
|
1041
|
-
document.body.appendChild(loaderScript);
|
|
1042
|
-
}
|
|
1043
|
-
else {
|
|
1044
|
-
onGotAmdLoader();
|
|
1045
|
-
}
|
|
1046
|
-
});
|
|
1047
|
-
}
|
|
1048
|
-
vs.initMonacoAsync = initMonacoAsync;
|
|
1049
|
-
function setupMonaco() {
|
|
1050
|
-
initAsmMonarchLanguage();
|
|
1051
|
-
initTypeScriptLanguageDefinition();
|
|
1052
|
-
}
|
|
1053
|
-
function createEditor(element) {
|
|
1054
|
-
const inverted = pxt.appTarget.appTheme.invertedMonaco;
|
|
1055
|
-
const hasFieldEditors = !!(pxt.appTarget.appTheme.monacoFieldEditors && pxt.appTarget.appTheme.monacoFieldEditors.length);
|
|
1056
|
-
const isAndroid = pxt.BrowserUtils.isAndroid();
|
|
1057
|
-
let editor = monaco.editor.create(element, {
|
|
1058
|
-
model: null,
|
|
1059
|
-
ariaLabel: pxt.Util.lf("JavaScript editor"),
|
|
1060
|
-
fontFamily: "'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'monospace'",
|
|
1061
|
-
scrollBeyondLastLine: true,
|
|
1062
|
-
language: "typescript",
|
|
1063
|
-
mouseWheelZoom: false,
|
|
1064
|
-
wordBasedSuggestions: true,
|
|
1065
|
-
lineNumbersMinChars: 3,
|
|
1066
|
-
formatOnPaste: true,
|
|
1067
|
-
folding: hasFieldEditors,
|
|
1068
|
-
glyphMargin: hasFieldEditors || pxt.appTarget.appTheme.debugger,
|
|
1069
|
-
minimap: {
|
|
1070
|
-
enabled: false
|
|
1071
|
-
},
|
|
1072
|
-
fixedOverflowWidgets: true,
|
|
1073
|
-
autoIndent: "full",
|
|
1074
|
-
useTabStops: true,
|
|
1075
|
-
dragAndDrop: true,
|
|
1076
|
-
matchBrackets: "always",
|
|
1077
|
-
occurrencesHighlight: false,
|
|
1078
|
-
quickSuggestionsDelay: 200,
|
|
1079
|
-
theme: inverted ? 'vs-dark' : 'vs',
|
|
1080
|
-
renderIndentGuides: true,
|
|
1081
|
-
accessibilityHelpUrl: "",
|
|
1082
|
-
// disable completions on android
|
|
1083
|
-
quickSuggestions: {
|
|
1084
|
-
"other": !isAndroid,
|
|
1085
|
-
"comments": !isAndroid,
|
|
1086
|
-
"strings": !isAndroid
|
|
1087
|
-
},
|
|
1088
|
-
acceptSuggestionOnCommitCharacter: !isAndroid,
|
|
1089
|
-
acceptSuggestionOnEnter: !isAndroid ? "on" : "off",
|
|
1090
|
-
accessibilitySupport: !isAndroid ? "on" : "off"
|
|
1091
|
-
});
|
|
1092
|
-
editor.layout();
|
|
1093
|
-
return editor;
|
|
1094
|
-
}
|
|
1095
|
-
vs.createEditor = createEditor;
|
|
1096
|
-
function initAsmMonarchLanguage() {
|
|
1097
|
-
monaco.languages.register({ id: 'asm', extensions: ['.asm'] });
|
|
1098
|
-
monaco.languages.setMonarchTokensProvider('asm', {
|
|
1099
|
-
// Set defaultToken to invalid to see what you do not tokenize yet
|
|
1100
|
-
// defaultToken: 'invalid',
|
|
1101
|
-
tokenPostfix: '',
|
|
1102
|
-
//Extracted from http://infocenter.arm.com/help/topic/com.arm.doc.qrc0006e/QRC0006_UAL16.pdf
|
|
1103
|
-
//Should be a superset of the instructions emitted
|
|
1104
|
-
keywords: [
|
|
1105
|
-
'movs', 'mov', 'adds', 'add', 'adcs', 'adr', 'subs', 'sbcs', 'sub', 'rsbs',
|
|
1106
|
-
'muls', 'cmp', 'cmn', 'ands', 'eors', 'orrs', 'bics', 'mvns', 'tst', 'lsls',
|
|
1107
|
-
'lsrs', 'asrs', 'rors', 'ldr', 'ldrh', 'ldrb', 'ldrsh', 'ldrsb', 'ldm',
|
|
1108
|
-
'str', 'strh', 'strb', 'stm', 'push', 'pop', 'cbz', 'cbnz', 'b', 'bl', 'bx', 'blx',
|
|
1109
|
-
'sxth', 'sxtb', 'uxth', 'uxtb', 'rev', 'rev16', 'revsh', 'svc', 'cpsid', 'cpsie',
|
|
1110
|
-
'setend', 'bkpt', 'nop', 'sev', 'wfe', 'wfi', 'yield',
|
|
1111
|
-
'beq', 'bne', 'bcs', 'bhs', 'bcc', 'blo', 'bmi', 'bpl', 'bvs', 'bvc', 'bhi', 'bls',
|
|
1112
|
-
'bge', 'blt', 'bgt', 'ble', 'bal',
|
|
1113
|
-
//Registers
|
|
1114
|
-
'r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15',
|
|
1115
|
-
'pc', 'sp', 'lr'
|
|
1116
|
-
],
|
|
1117
|
-
typeKeywords: [
|
|
1118
|
-
'.startaddr', '.hex', '.short', '.space', '.section', '.string', '.byte'
|
|
1119
|
-
],
|
|
1120
|
-
operators: [],
|
|
1121
|
-
// Not all of these are valid in ARM Assembly
|
|
1122
|
-
symbols: /[:\*]+/,
|
|
1123
|
-
// C# style strings
|
|
1124
|
-
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
|
|
1125
|
-
// The main tokenizer for our languages
|
|
1126
|
-
tokenizer: {
|
|
1127
|
-
root: [
|
|
1128
|
-
// identifiers and keywords
|
|
1129
|
-
[/(\.)?[a-z_$\.][\w$]*/, {
|
|
1130
|
-
cases: {
|
|
1131
|
-
'@typeKeywords': 'keyword',
|
|
1132
|
-
'@keywords': 'keyword',
|
|
1133
|
-
'@default': 'identifier'
|
|
1134
|
-
}
|
|
1135
|
-
}],
|
|
1136
|
-
// whitespace
|
|
1137
|
-
{ include: '@whitespace' },
|
|
1138
|
-
// delimiters and operators
|
|
1139
|
-
[/[{}()\[\]]/, '@brackets'],
|
|
1140
|
-
[/[<>](?!@symbols)/, '@brackets'],
|
|
1141
|
-
[/@symbols/, {
|
|
1142
|
-
cases: {
|
|
1143
|
-
'@operators': 'operator',
|
|
1144
|
-
'@default': ''
|
|
1145
|
-
}
|
|
1146
|
-
}],
|
|
1147
|
-
// @ annotations.
|
|
1148
|
-
[/@\s*[a-zA-Z_\$][\w\$]*/, { token: 'annotation' }],
|
|
1149
|
-
// numbers
|
|
1150
|
-
//[/\d*\.\d+([eE][\-+]?\d+)?/, 'number.float'],
|
|
1151
|
-
[/(#|(0[xX]))?[0-9a-fA-F]+/, 'number'],
|
|
1152
|
-
// delimiter: after number because of .\d floats
|
|
1153
|
-
[/[;,.]/, 'delimiter'],
|
|
1154
|
-
// strings
|
|
1155
|
-
[/"([^"\\]|\\.)*$/, 'string.invalid'],
|
|
1156
|
-
[/"/, { token: 'string.quote', bracket: '@open', next: '@string' }],
|
|
1157
|
-
// characters
|
|
1158
|
-
[/'[^\\']'/, 'string'],
|
|
1159
|
-
[/(')(@escapes)(')/, ['string', 'string.escape', 'string']],
|
|
1160
|
-
[/'/, 'string.invalid']
|
|
1161
|
-
],
|
|
1162
|
-
comment: [],
|
|
1163
|
-
string: [
|
|
1164
|
-
[/[^\\"]+/, 'string'],
|
|
1165
|
-
[/@escapes/, 'string.escape'],
|
|
1166
|
-
[/\\./, 'string.escape.invalid'],
|
|
1167
|
-
[/"/, { token: 'string.quote', bracket: '@close', next: '@pop' }]
|
|
1168
|
-
],
|
|
1169
|
-
whitespace: [
|
|
1170
|
-
[/[ \t\r\n]+/, 'white'],
|
|
1171
|
-
[/\/\*/, 'comment', '@comment'],
|
|
1172
|
-
[/;.*$/, 'comment'],
|
|
1173
|
-
],
|
|
1174
|
-
}
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
function initTypeScriptLanguageDefinition() {
|
|
1178
|
-
if (!monaco.languages.typescript) {
|
|
1179
|
-
return;
|
|
1180
|
-
}
|
|
1181
|
-
// validation settings
|
|
1182
|
-
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
|
|
1183
|
-
noSyntaxValidation: true,
|
|
1184
|
-
noSemanticValidation: true
|
|
1185
|
-
});
|
|
1186
|
-
// Register our worker
|
|
1187
|
-
monaco.languages.typescript.typescriptDefaults.setWorkerOptions({
|
|
1188
|
-
customWorkerPath: pxt.webConfig.typeScriptWorkerJs
|
|
1189
|
-
});
|
|
1190
|
-
// compiler options
|
|
1191
|
-
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
|
1192
|
-
allowUnreachableCode: true,
|
|
1193
|
-
noImplicitAny: true,
|
|
1194
|
-
allowJs: false,
|
|
1195
|
-
allowUnusedLabels: true,
|
|
1196
|
-
target: monaco.languages.typescript.ScriptTarget.ES5,
|
|
1197
|
-
outDir: "built",
|
|
1198
|
-
rootDir: ".",
|
|
1199
|
-
noLib: true,
|
|
1200
|
-
mouseWheelZoom: false
|
|
1201
|
-
});
|
|
1202
|
-
}
|
|
1203
|
-
})(vs = pxt.vs || (pxt.vs = {}));
|
|
1204
|
-
})(pxt || (pxt = {}));
|
|
1205
|
-
/// <reference path="../built/pxtlib.d.ts"/>
|
|
1206
|
-
var pxt;
|
|
1207
|
-
(function (pxt) {
|
|
1208
|
-
var workspace;
|
|
1209
|
-
(function (workspace) {
|
|
1210
|
-
function freshHeader(name, modTime) {
|
|
1211
|
-
let header = {
|
|
1212
|
-
target: pxt.appTarget.id,
|
|
1213
|
-
targetVersion: pxt.appTarget.versions.target,
|
|
1214
|
-
name: name,
|
|
1215
|
-
meta: {},
|
|
1216
|
-
editor: pxt.JAVASCRIPT_PROJECT_NAME,
|
|
1217
|
-
pubId: "",
|
|
1218
|
-
pubCurrent: false,
|
|
1219
|
-
_rev: null,
|
|
1220
|
-
id: pxt.U.guidGen(),
|
|
1221
|
-
recentUse: modTime,
|
|
1222
|
-
modificationTime: modTime,
|
|
1223
|
-
cloudUserId: null,
|
|
1224
|
-
cloudCurrent: false,
|
|
1225
|
-
cloudVersion: null,
|
|
1226
|
-
cloudLastSyncTime: 0,
|
|
1227
|
-
isDeleted: false,
|
|
1228
|
-
};
|
|
1229
|
-
return header;
|
|
1230
|
-
}
|
|
1231
|
-
workspace.freshHeader = freshHeader;
|
|
1232
|
-
})(workspace = pxt.workspace || (pxt.workspace = {}));
|
|
1233
|
-
})(pxt || (pxt = {}));
|
|
1234
|
-
/// <reference path="../../localtypings/monaco.d.ts" />
|
|
1235
|
-
var pxt;
|
|
1236
|
-
(function (pxt) {
|
|
1237
|
-
var editor;
|
|
1238
|
-
(function (editor) {
|
|
1239
|
-
const definitions = {};
|
|
1240
|
-
function registerMonacoFieldEditor(name, definition) {
|
|
1241
|
-
definitions[name] = definition;
|
|
1242
|
-
}
|
|
1243
|
-
editor.registerMonacoFieldEditor = registerMonacoFieldEditor;
|
|
1244
|
-
function getMonacoFieldEditor(name) {
|
|
1245
|
-
return definitions[name];
|
|
1246
|
-
}
|
|
1247
|
-
editor.getMonacoFieldEditor = getMonacoFieldEditor;
|
|
1248
|
-
})(editor = pxt.editor || (pxt.editor = {}));
|
|
1249
|
-
})(pxt || (pxt = {}));
|
|
1250
|
-
/// <reference path="./monacoFieldEditor.ts" />
|
|
1251
|
-
var pxt;
|
|
1252
|
-
(function (pxt) {
|
|
1253
|
-
var editor;
|
|
1254
|
-
(function (editor) {
|
|
1255
|
-
const fieldEditorId = "image-editor";
|
|
1256
|
-
class MonacoReactFieldEditor {
|
|
1257
|
-
getId() {
|
|
1258
|
-
return fieldEditorId;
|
|
1259
|
-
}
|
|
1260
|
-
showEditorAsync(fileType, editrange, host) {
|
|
1261
|
-
this.fileType = fileType;
|
|
1262
|
-
this.editrange = editrange;
|
|
1263
|
-
this.host = host;
|
|
1264
|
-
return this.initAsync().then(() => {
|
|
1265
|
-
const value = this.textToValue(host.getText(editrange));
|
|
1266
|
-
if (!value) {
|
|
1267
|
-
return Promise.resolve(null);
|
|
1268
|
-
}
|
|
1269
|
-
this.fv = pxt.react.getFieldEditorView(this.getFieldEditorId(), value, this.getOptions());
|
|
1270
|
-
this.fv.onHide(() => {
|
|
1271
|
-
this.onClosed();
|
|
1272
|
-
});
|
|
1273
|
-
this.fv.show();
|
|
1274
|
-
return new Promise((resolve, reject) => {
|
|
1275
|
-
this.resolver = resolve;
|
|
1276
|
-
this.rejecter = reject;
|
|
1277
|
-
});
|
|
1278
|
-
});
|
|
1279
|
-
}
|
|
1280
|
-
onClosed() {
|
|
1281
|
-
if (this.resolver) {
|
|
1282
|
-
this.resolver({
|
|
1283
|
-
range: this.editrange,
|
|
1284
|
-
replacement: this.resultToText(this.fv.getResult())
|
|
1285
|
-
});
|
|
1286
|
-
this.editrange = undefined;
|
|
1287
|
-
this.resolver = undefined;
|
|
1288
|
-
this.rejecter = undefined;
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
dispose() {
|
|
1292
|
-
this.onClosed();
|
|
1293
|
-
}
|
|
1294
|
-
initAsync() {
|
|
1295
|
-
return Promise.resolve();
|
|
1296
|
-
}
|
|
1297
|
-
textToValue(text) {
|
|
1298
|
-
return null;
|
|
1299
|
-
}
|
|
1300
|
-
resultToText(result) {
|
|
1301
|
-
return result + "";
|
|
1302
|
-
}
|
|
1303
|
-
getFieldEditorId() {
|
|
1304
|
-
return "";
|
|
1305
|
-
}
|
|
1306
|
-
getOptions() {
|
|
1307
|
-
return null;
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
editor.MonacoReactFieldEditor = MonacoReactFieldEditor;
|
|
1311
|
-
})(editor = pxt.editor || (pxt.editor = {}));
|
|
1312
|
-
})(pxt || (pxt = {}));
|
|
1313
|
-
/// <reference path="./monacoFieldEditor.ts" />
|
|
1314
|
-
/// <reference path="./field_react.ts" />
|
|
1315
|
-
var pxt;
|
|
1316
|
-
(function (pxt) {
|
|
1317
|
-
var editor;
|
|
1318
|
-
(function (editor) {
|
|
1319
|
-
const fieldEditorId = "music-editor";
|
|
1320
|
-
class MonacoSongEditor extends editor.MonacoReactFieldEditor {
|
|
1321
|
-
textToValue(text) {
|
|
1322
|
-
this.isPython = text.indexOf("`") === -1;
|
|
1323
|
-
this.text = text;
|
|
1324
|
-
const match = pxt.parseAssetTSReference(text);
|
|
1325
|
-
if (match) {
|
|
1326
|
-
const { type, name: matchedName } = match;
|
|
1327
|
-
const name = matchedName.trim();
|
|
1328
|
-
const project = pxt.react.getTilemapProject();
|
|
1329
|
-
this.isAsset = true;
|
|
1330
|
-
const asset = project.lookupAssetByName("song" /* pxt.AssetType.Song */, name);
|
|
1331
|
-
if (asset) {
|
|
1332
|
-
return asset;
|
|
1333
|
-
}
|
|
1334
|
-
else {
|
|
1335
|
-
const newAsset = project.createNewSong(pxt.assets.music.getEmptySong(2));
|
|
1336
|
-
if (name && !project.isNameTaken("song" /* pxt.AssetType.Song */, name) && pxt.validateAssetName(name)) {
|
|
1337
|
-
newAsset.meta.displayName = name;
|
|
1338
|
-
}
|
|
1339
|
-
return newAsset;
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
const hexLiteralMatch = /hex\s*(?:`|\(""")\s*([a-fA-F0-9]*)\s*(?:`|"""\))\s*(?:;?)/m.exec(text);
|
|
1343
|
-
if (hexLiteralMatch) {
|
|
1344
|
-
const contents = hexLiteralMatch[1].trim();
|
|
1345
|
-
if (contents) {
|
|
1346
|
-
return createFakeAsset(pxt.assets.music.decodeSongFromHex(contents));
|
|
1347
|
-
}
|
|
1348
|
-
return createFakeAsset(pxt.assets.music.getEmptySong(2));
|
|
1349
|
-
}
|
|
1350
|
-
return undefined; // never
|
|
1351
|
-
}
|
|
1352
|
-
resultToText(result) {
|
|
1353
|
-
var _a;
|
|
1354
|
-
if ((_a = result.meta) === null || _a === void 0 ? void 0 : _a.displayName) {
|
|
1355
|
-
const project = pxt.react.getTilemapProject();
|
|
1356
|
-
if (this.isAsset || project.lookupAsset(result.type, result.id)) {
|
|
1357
|
-
result = project.updateAsset(result);
|
|
1358
|
-
}
|
|
1359
|
-
else {
|
|
1360
|
-
result = project.createNewSong(result.song, result.meta.displayName);
|
|
1361
|
-
}
|
|
1362
|
-
this.isAsset = true;
|
|
1363
|
-
return pxt.getTSReferenceForAsset(result, this.isPython);
|
|
1364
|
-
}
|
|
1365
|
-
let hexString = pxt.assets.music.encodeSongToHex(result.song);
|
|
1366
|
-
if (this.isPython) {
|
|
1367
|
-
hexString = `hex("""${hexString}""")`;
|
|
1368
|
-
}
|
|
1369
|
-
else {
|
|
1370
|
-
hexString = "hex`" + hexString + "`";
|
|
1371
|
-
}
|
|
1372
|
-
return this.text.replace(/hex\s*(?:`|\(""")\s*([a-fA-F0-9]*)\s*(?:`|"""\))\s*(?:;?)/m, hexString);
|
|
1373
|
-
}
|
|
1374
|
-
getFieldEditorId() {
|
|
1375
|
-
return fieldEditorId;
|
|
1376
|
-
}
|
|
1377
|
-
getOptions() {
|
|
1378
|
-
return {
|
|
1379
|
-
blocksInfo: this.host.blocksInfo()
|
|
1380
|
-
};
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
editor.MonacoSongEditor = MonacoSongEditor;
|
|
1384
|
-
function createFakeAsset(song) {
|
|
1385
|
-
return {
|
|
1386
|
-
type: "song" /* pxt.AssetType.Song */,
|
|
1387
|
-
id: "",
|
|
1388
|
-
internalID: 0,
|
|
1389
|
-
meta: {},
|
|
1390
|
-
song
|
|
1391
|
-
};
|
|
1392
|
-
}
|
|
1393
|
-
editor.songEditorDefinition = {
|
|
1394
|
-
id: fieldEditorId,
|
|
1395
|
-
foldMatches: true,
|
|
1396
|
-
glyphCssClass: "fas fa-music sprite-focus-hover",
|
|
1397
|
-
heightInPixels: 510,
|
|
1398
|
-
matcher: {
|
|
1399
|
-
/**
|
|
1400
|
-
* This is horrendous-looking regex matches both the asset reference syntax:
|
|
1401
|
-
* assets.song`name`
|
|
1402
|
-
* assets.song("""name""")
|
|
1403
|
-
*
|
|
1404
|
-
* and the hex-literal syntax:
|
|
1405
|
-
* music.createSong(hex`01234`
|
|
1406
|
-
* music.create_song(hex("""01234""")
|
|
1407
|
-
*
|
|
1408
|
-
* For the hex literal matches, it includes the call to music.createSong since
|
|
1409
|
-
* hex buffers can also be used for other things
|
|
1410
|
-
*/
|
|
1411
|
-
searchString: "(?:(?:assets\\s*\\.\\s*song)|(?:music\\s*\\.\\s*create(?:S|_s)ong\\s*\\(\\s*hex))\\s*(?:`|\\(\\s*\"\"\")(?:(?:[^(){}:\\[\\]\"';?/,+\\-=*&|^%!`~]|\\n)*)\\s*(?:`|\"\"\"\\s*\\))",
|
|
1412
|
-
isRegex: true,
|
|
1413
|
-
matchCase: true,
|
|
1414
|
-
matchWholeWord: false
|
|
1415
|
-
},
|
|
1416
|
-
proto: MonacoSongEditor
|
|
1417
|
-
};
|
|
1418
|
-
editor.registerMonacoFieldEditor(fieldEditorId, editor.songEditorDefinition);
|
|
1419
|
-
})(editor = pxt.editor || (pxt.editor = {}));
|
|
1420
|
-
})(pxt || (pxt = {}));
|
|
1421
|
-
/// <reference path="./monacoFieldEditor.ts" />
|
|
1422
|
-
/// <reference path="./field_react.ts" />
|
|
1423
|
-
var pxt;
|
|
1424
|
-
(function (pxt) {
|
|
1425
|
-
var editor;
|
|
1426
|
-
(function (editor) {
|
|
1427
|
-
const fieldEditorId = "soundeffect-editor";
|
|
1428
|
-
// music.createSoundEffect(WaveShape.Sine, 5000, 0, 255, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear
|
|
1429
|
-
class MonacoSoundEffectEditor extends editor.MonacoReactFieldEditor {
|
|
1430
|
-
textToValue(text) {
|
|
1431
|
-
const out = defaultSound();
|
|
1432
|
-
this.value = out;
|
|
1433
|
-
const argMatch = /\(([^)]*)\)/.exec(text);
|
|
1434
|
-
const args = argMatch[1].split(",").map(a => a.replace(/\s/g, ""));
|
|
1435
|
-
if (args.length !== 8)
|
|
1436
|
-
return out;
|
|
1437
|
-
switch (args[0]) {
|
|
1438
|
-
case "WaveShape.Sawtooth":
|
|
1439
|
-
out.wave = "sawtooth";
|
|
1440
|
-
break;
|
|
1441
|
-
case "WaveShape.Square":
|
|
1442
|
-
out.wave = "square";
|
|
1443
|
-
break;
|
|
1444
|
-
case "WaveShape.Noise":
|
|
1445
|
-
out.wave = "noise";
|
|
1446
|
-
break;
|
|
1447
|
-
case "WaveShape.Triangle":
|
|
1448
|
-
out.wave = "triangle";
|
|
1449
|
-
break;
|
|
1450
|
-
case "WaveShape.Sine":
|
|
1451
|
-
default:
|
|
1452
|
-
out.wave = "sine";
|
|
1453
|
-
break;
|
|
1454
|
-
}
|
|
1455
|
-
const withDefault = (val, def) => {
|
|
1456
|
-
return isNaN(val) ? def : val;
|
|
1457
|
-
};
|
|
1458
|
-
out.startFrequency = withDefault(parseInt(args[1]), out.startFrequency);
|
|
1459
|
-
out.endFrequency = withDefault(parseInt(args[2]), out.endFrequency);
|
|
1460
|
-
out.startVolume = withDefault(parseInt(args[3]), out.startVolume);
|
|
1461
|
-
out.endVolume = withDefault(parseInt(args[4]), out.endVolume);
|
|
1462
|
-
out.duration = withDefault(parseInt(args[5]), out.duration);
|
|
1463
|
-
switch (args[6]) {
|
|
1464
|
-
case "SoundExpressionEffect.Vibrato":
|
|
1465
|
-
out.effect = "vibrato";
|
|
1466
|
-
break;
|
|
1467
|
-
case "SoundExpressionEffect.Tremolo":
|
|
1468
|
-
out.effect = "tremolo";
|
|
1469
|
-
break;
|
|
1470
|
-
case "SoundExpressionEffect.Warble":
|
|
1471
|
-
out.effect = "warble";
|
|
1472
|
-
break;
|
|
1473
|
-
case "SoundExpressionEffect.None":
|
|
1474
|
-
default:
|
|
1475
|
-
out.effect = "none";
|
|
1476
|
-
break;
|
|
1477
|
-
}
|
|
1478
|
-
switch (args[7]) {
|
|
1479
|
-
case "InterpolationCurve.Logarithmic":
|
|
1480
|
-
out.interpolation = "logarithmic";
|
|
1481
|
-
break;
|
|
1482
|
-
case "InterpolationCurve.Curve":
|
|
1483
|
-
out.interpolation = "curve";
|
|
1484
|
-
break;
|
|
1485
|
-
case "InterpolationCurve.Linear":
|
|
1486
|
-
default:
|
|
1487
|
-
out.interpolation = "linear";
|
|
1488
|
-
break;
|
|
1489
|
-
}
|
|
1490
|
-
return out;
|
|
1491
|
-
}
|
|
1492
|
-
resultToText(result) {
|
|
1493
|
-
result = this.value;
|
|
1494
|
-
let waveShape;
|
|
1495
|
-
switch (result.wave) {
|
|
1496
|
-
case "sine":
|
|
1497
|
-
waveShape = "WaveShape.Sine";
|
|
1498
|
-
break;
|
|
1499
|
-
case "square":
|
|
1500
|
-
waveShape = "WaveShape.Square";
|
|
1501
|
-
break;
|
|
1502
|
-
case "triangle":
|
|
1503
|
-
waveShape = "WaveShape.Triangle";
|
|
1504
|
-
break;
|
|
1505
|
-
case "noise":
|
|
1506
|
-
waveShape = "WaveShape.Noise";
|
|
1507
|
-
break;
|
|
1508
|
-
case "sawtooth":
|
|
1509
|
-
waveShape = "WaveShape.Sawtooth";
|
|
1510
|
-
break;
|
|
1511
|
-
}
|
|
1512
|
-
let effect;
|
|
1513
|
-
switch (result.effect) {
|
|
1514
|
-
case "vibrato":
|
|
1515
|
-
effect = "SoundExpressionEffect.Vibrato";
|
|
1516
|
-
break;
|
|
1517
|
-
case "tremolo":
|
|
1518
|
-
effect = "SoundExpressionEffect.Tremolo";
|
|
1519
|
-
break;
|
|
1520
|
-
case "warble":
|
|
1521
|
-
effect = "SoundExpressionEffect.Warble";
|
|
1522
|
-
break;
|
|
1523
|
-
case "none":
|
|
1524
|
-
effect = "SoundExpressionEffect.None";
|
|
1525
|
-
break;
|
|
1526
|
-
}
|
|
1527
|
-
let interpolation;
|
|
1528
|
-
switch (result.interpolation) {
|
|
1529
|
-
case "curve":
|
|
1530
|
-
interpolation = "InterpolationCurve.Curve";
|
|
1531
|
-
break;
|
|
1532
|
-
case "linear":
|
|
1533
|
-
interpolation = "InterpolationCurve.Linear";
|
|
1534
|
-
break;
|
|
1535
|
-
case "logarithmic":
|
|
1536
|
-
interpolation = "InterpolationCurve.Logarithmic";
|
|
1537
|
-
break;
|
|
1538
|
-
}
|
|
1539
|
-
return `music.createSoundEffect(${waveShape}, ${Math.round(result.startFrequency)}, ${Math.round(result.endFrequency)}, ${Math.round(result.startVolume)}, ${Math.round(result.endVolume)}, ${Math.round(result.duration)}, ${effect}, ${interpolation})`;
|
|
1540
|
-
}
|
|
1541
|
-
getFieldEditorId() {
|
|
1542
|
-
return fieldEditorId;
|
|
1543
|
-
}
|
|
1544
|
-
getOptions() {
|
|
1545
|
-
return {
|
|
1546
|
-
onClose: () => this.fv.hide(),
|
|
1547
|
-
onSoundChange: (newValue) => this.value = newValue,
|
|
1548
|
-
initialSound: this.value,
|
|
1549
|
-
useFlex: true,
|
|
1550
|
-
useMixerSynthesizer: pxt.appTarget.id !== "microbit" // FIXME
|
|
1551
|
-
};
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
editor.MonacoSoundEffectEditor = MonacoSoundEffectEditor;
|
|
1555
|
-
function validateRange(range, model) {
|
|
1556
|
-
let currentLine = range.startLineNumber;
|
|
1557
|
-
let currentColumn = 0;
|
|
1558
|
-
let foundStart = false;
|
|
1559
|
-
let parenCount = 0;
|
|
1560
|
-
const methodName = "createSoundEffect";
|
|
1561
|
-
const totalLines = model.getLineCount();
|
|
1562
|
-
while (currentLine < totalLines) {
|
|
1563
|
-
const lineContent = model.getLineContent(currentLine);
|
|
1564
|
-
const startIndex = lineContent.indexOf(methodName);
|
|
1565
|
-
if (startIndex !== -1) {
|
|
1566
|
-
foundStart = true;
|
|
1567
|
-
currentColumn = startIndex + methodName.length;
|
|
1568
|
-
}
|
|
1569
|
-
if (foundStart) {
|
|
1570
|
-
while (currentColumn < lineContent.length) {
|
|
1571
|
-
const currentChar = lineContent.charAt(currentColumn);
|
|
1572
|
-
if (currentChar === "(") {
|
|
1573
|
-
parenCount++;
|
|
1574
|
-
}
|
|
1575
|
-
else if (currentChar === ")") {
|
|
1576
|
-
parenCount--;
|
|
1577
|
-
if (parenCount === 0) {
|
|
1578
|
-
return new monaco.Range(range.startLineNumber, range.startColumn, currentLine, currentColumn + model.getLineMinColumn(currentLine) + 1);
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
currentColumn++;
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
currentColumn = 0;
|
|
1585
|
-
currentLine++;
|
|
1586
|
-
}
|
|
1587
|
-
return undefined;
|
|
1588
|
-
}
|
|
1589
|
-
function defaultSound() {
|
|
1590
|
-
return {
|
|
1591
|
-
wave: "sine",
|
|
1592
|
-
startFrequency: 5000,
|
|
1593
|
-
endFrequency: 0,
|
|
1594
|
-
startVolume: 255,
|
|
1595
|
-
endVolume: 0,
|
|
1596
|
-
duration: 500,
|
|
1597
|
-
effect: "none",
|
|
1598
|
-
interpolation: "linear"
|
|
1599
|
-
};
|
|
1600
|
-
}
|
|
1601
|
-
editor.soundEditorDefinition = {
|
|
1602
|
-
id: fieldEditorId,
|
|
1603
|
-
foldMatches: true,
|
|
1604
|
-
glyphCssClass: "fas fa-music sprite-focus-hover",
|
|
1605
|
-
heightInPixels: 510,
|
|
1606
|
-
matcher: {
|
|
1607
|
-
// match both JS and python
|
|
1608
|
-
searchString: "music\\s*\\.\\s*createSoundEffect\\s*\\(",
|
|
1609
|
-
isRegex: true,
|
|
1610
|
-
matchCase: true,
|
|
1611
|
-
matchWholeWord: false,
|
|
1612
|
-
validateRange
|
|
1613
|
-
},
|
|
1614
|
-
proto: MonacoSoundEffectEditor
|
|
1615
|
-
};
|
|
1616
|
-
editor.registerMonacoFieldEditor(fieldEditorId, editor.soundEditorDefinition);
|
|
1617
|
-
})(editor = pxt.editor || (pxt.editor = {}));
|
|
1618
|
-
})(pxt || (pxt = {}));
|
|
1619
|
-
/// <reference path="./monacoFieldEditor.ts" />
|
|
1620
|
-
/// <reference path="./field_react.ts" />
|
|
1621
|
-
var pxt;
|
|
1622
|
-
(function (pxt) {
|
|
1623
|
-
var editor;
|
|
1624
|
-
(function (editor) {
|
|
1625
|
-
const fieldEditorId = "image-editor";
|
|
1626
|
-
class MonacoSpriteEditor extends editor.MonacoReactFieldEditor {
|
|
1627
|
-
textToValue(text) {
|
|
1628
|
-
this.isPython = text.indexOf("`") === -1;
|
|
1629
|
-
const match = pxt.parseAssetTSReference(text);
|
|
1630
|
-
if (match) {
|
|
1631
|
-
const { type, name: matchedName } = match;
|
|
1632
|
-
const name = matchedName.trim();
|
|
1633
|
-
const project = pxt.react.getTilemapProject();
|
|
1634
|
-
this.isAsset = true;
|
|
1635
|
-
const asset = project.lookupAssetByName("image" /* pxt.AssetType.Image */, name);
|
|
1636
|
-
if (asset) {
|
|
1637
|
-
return asset;
|
|
1638
|
-
}
|
|
1639
|
-
else {
|
|
1640
|
-
const newAsset = project.createNewImage();
|
|
1641
|
-
if (name && !project.isNameTaken("image" /* pxt.AssetType.Image */, name) && pxt.validateAssetName(name)) {
|
|
1642
|
-
newAsset.meta.displayName = name;
|
|
1643
|
-
}
|
|
1644
|
-
return newAsset;
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
return createFakeAsset(pxt.sprite.imageLiteralToBitmap(text));
|
|
1648
|
-
}
|
|
1649
|
-
resultToText(result) {
|
|
1650
|
-
var _a;
|
|
1651
|
-
if ((_a = result.meta) === null || _a === void 0 ? void 0 : _a.displayName) {
|
|
1652
|
-
const project = pxt.react.getTilemapProject();
|
|
1653
|
-
if (this.isAsset || project.lookupAsset(result.type, result.id)) {
|
|
1654
|
-
result = project.updateAsset(result);
|
|
1655
|
-
}
|
|
1656
|
-
else {
|
|
1657
|
-
result = project.createNewProjectImage(result.bitmap, result.meta.displayName);
|
|
1658
|
-
}
|
|
1659
|
-
this.isAsset = true;
|
|
1660
|
-
return pxt.getTSReferenceForAsset(result, this.isPython);
|
|
1661
|
-
}
|
|
1662
|
-
return pxt.sprite.bitmapToImageLiteral(pxt.sprite.Bitmap.fromData(result.bitmap), this.isPython ? "python" : "typescript");
|
|
1663
|
-
}
|
|
1664
|
-
getFieldEditorId() {
|
|
1665
|
-
return "image-editor";
|
|
1666
|
-
}
|
|
1667
|
-
getOptions() {
|
|
1668
|
-
return {
|
|
1669
|
-
initWidth: 16,
|
|
1670
|
-
initHeight: 16,
|
|
1671
|
-
blocksInfo: this.host.blocksInfo()
|
|
1672
|
-
};
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
editor.MonacoSpriteEditor = MonacoSpriteEditor;
|
|
1676
|
-
function createFakeAsset(bitmap) {
|
|
1677
|
-
return {
|
|
1678
|
-
type: "image" /* pxt.AssetType.Image */,
|
|
1679
|
-
id: "",
|
|
1680
|
-
internalID: 0,
|
|
1681
|
-
bitmap: bitmap.data(),
|
|
1682
|
-
meta: {},
|
|
1683
|
-
jresData: ""
|
|
1684
|
-
};
|
|
1685
|
-
}
|
|
1686
|
-
editor.spriteEditorDefinition = {
|
|
1687
|
-
id: fieldEditorId,
|
|
1688
|
-
foldMatches: true,
|
|
1689
|
-
glyphCssClass: "sprite-editor-glyph sprite-focus-hover",
|
|
1690
|
-
heightInPixels: 510,
|
|
1691
|
-
matcher: {
|
|
1692
|
-
// match both JS and python
|
|
1693
|
-
searchString: "(?:img|assets\\s*\\.\\s*image)\\s*(?:`|\\(\\s*\"\"\")(?:(?:[^(){}:\\[\\]\"';?/,+\\-=*&|^%!`~]|\\n)*)\\s*(?:`|\"\"\"\\s*\\))",
|
|
1694
|
-
isRegex: true,
|
|
1695
|
-
matchCase: true,
|
|
1696
|
-
matchWholeWord: false
|
|
1697
|
-
},
|
|
1698
|
-
proto: MonacoSpriteEditor
|
|
1699
|
-
};
|
|
1700
|
-
editor.registerMonacoFieldEditor(fieldEditorId, editor.spriteEditorDefinition);
|
|
1701
|
-
})(editor = pxt.editor || (pxt.editor = {}));
|
|
1702
|
-
})(pxt || (pxt = {}));
|
|
1703
|
-
/// <reference path="./monacoFieldEditor.ts" />
|
|
1704
|
-
/// <reference path="./field_react.ts" />
|
|
1705
|
-
var pxt;
|
|
1706
|
-
(function (pxt) {
|
|
1707
|
-
var editor;
|
|
1708
|
-
(function (editor) {
|
|
1709
|
-
const fieldEditorId = "tilemap-editor";
|
|
1710
|
-
class MonacoTilemapEditor extends editor.MonacoReactFieldEditor {
|
|
1711
|
-
textToValue(text) {
|
|
1712
|
-
const tm = this.readTilemap(text);
|
|
1713
|
-
const project = pxt.react.getTilemapProject();
|
|
1714
|
-
pxt.sprite.addMissingTilemapTilesAndReferences(project, tm);
|
|
1715
|
-
return tm;
|
|
1716
|
-
}
|
|
1717
|
-
readTilemap(text) {
|
|
1718
|
-
const project = pxt.react.getTilemapProject();
|
|
1719
|
-
if (/^\s*tiles\s*\./.test(text)) {
|
|
1720
|
-
this.isTilemapLiteral = false;
|
|
1721
|
-
if (text) {
|
|
1722
|
-
try {
|
|
1723
|
-
const data = pxt.sprite.decodeTilemap(text, "typescript", project);
|
|
1724
|
-
return createFakeAsset(data);
|
|
1725
|
-
}
|
|
1726
|
-
catch (e) {
|
|
1727
|
-
// If the user is still typing, they might try to open the editor on an incomplete tilemap
|
|
1728
|
-
}
|
|
1729
|
-
return null;
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
|
-
this.isTilemapLiteral = true;
|
|
1733
|
-
// This matches the regex for the field editor, so it should always match
|
|
1734
|
-
const match = /^\s*(tilemap(?:8|16|32)?)\s*(?:`([^`]*)`)|(?:\(\s*"""([^"]*)"""\s*\))\s*$/.exec(text);
|
|
1735
|
-
const name = (match[2] || match[3] || "").trim();
|
|
1736
|
-
this.tilemapLiteral = match[1];
|
|
1737
|
-
let proj;
|
|
1738
|
-
let id;
|
|
1739
|
-
if (name) {
|
|
1740
|
-
id = ts.pxtc.escapeIdentifier(name);
|
|
1741
|
-
proj = project.getTilemap(id) || project.lookupAssetByName("tilemap" /* AssetType.Tilemap */, name);
|
|
1742
|
-
}
|
|
1743
|
-
if (!proj) {
|
|
1744
|
-
let tileWidth = 16;
|
|
1745
|
-
if (this.tilemapLiteral === "tilemap8") {
|
|
1746
|
-
tileWidth = 8;
|
|
1747
|
-
}
|
|
1748
|
-
else if (this.tilemapLiteral === "tilemap32") {
|
|
1749
|
-
tileWidth = 32;
|
|
1750
|
-
}
|
|
1751
|
-
const [name] = project.createNewTilemap(id, tileWidth, 16, 16);
|
|
1752
|
-
proj = project.getTilemap(name);
|
|
1753
|
-
id = name;
|
|
1754
|
-
}
|
|
1755
|
-
return proj;
|
|
1756
|
-
}
|
|
1757
|
-
resultToText(asset) {
|
|
1758
|
-
const project = pxt.react.getTilemapProject();
|
|
1759
|
-
project.pushUndo();
|
|
1760
|
-
pxt.sprite.updateTilemapReferencesFromResult(project, asset);
|
|
1761
|
-
if (this.isTilemapLiteral) {
|
|
1762
|
-
project.updateAsset(asset);
|
|
1763
|
-
return pxt.getTSReferenceForAsset(asset, this.fileType === "python");
|
|
1764
|
-
}
|
|
1765
|
-
else {
|
|
1766
|
-
return pxt.sprite.encodeTilemap(asset.data, this.fileType === "typescript" ? "typescript" : "python");
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
getFieldEditorId() {
|
|
1770
|
-
return "tilemap-editor";
|
|
1771
|
-
}
|
|
1772
|
-
getOptions() {
|
|
1773
|
-
return {
|
|
1774
|
-
initWidth: 16,
|
|
1775
|
-
initHeight: 16,
|
|
1776
|
-
blocksInfo: this.host.blocksInfo()
|
|
1777
|
-
};
|
|
1778
|
-
}
|
|
1779
|
-
getCreateTilemapRange() {
|
|
1780
|
-
const start = this.editrange.getStartPosition();
|
|
1781
|
-
let current = this.editrange.getEndPosition();
|
|
1782
|
-
let range;
|
|
1783
|
-
let openParen = 1;
|
|
1784
|
-
while (true) {
|
|
1785
|
-
range = new monaco.Range(current.lineNumber, current.column, current.lineNumber + 1, 0);
|
|
1786
|
-
const line = this.host.getText(range);
|
|
1787
|
-
for (let i = 0; i < line.length; i++) {
|
|
1788
|
-
if (line.charAt(i) === "(") {
|
|
1789
|
-
openParen++;
|
|
1790
|
-
}
|
|
1791
|
-
else if (line.charAt(i) === ")") {
|
|
1792
|
-
openParen--;
|
|
1793
|
-
if (openParen === 0) {
|
|
1794
|
-
const end = new monaco.Position(current.lineNumber, current.column + i + 2);
|
|
1795
|
-
return monaco.Range.fromPositions(start, end);
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
current = range.getEndPosition();
|
|
1800
|
-
if (current.lineNumber > start.lineNumber + 20) {
|
|
1801
|
-
return null;
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
editor.MonacoTilemapEditor = MonacoTilemapEditor;
|
|
1807
|
-
function createFakeAsset(data) {
|
|
1808
|
-
return {
|
|
1809
|
-
type: "tilemap" /* pxt.AssetType.Tilemap */,
|
|
1810
|
-
id: "",
|
|
1811
|
-
internalID: 0,
|
|
1812
|
-
meta: {},
|
|
1813
|
-
data
|
|
1814
|
-
};
|
|
1815
|
-
}
|
|
1816
|
-
editor.tilemapEditorDefinition = {
|
|
1817
|
-
id: fieldEditorId,
|
|
1818
|
-
foldMatches: true,
|
|
1819
|
-
alwaysBuildOnClose: true,
|
|
1820
|
-
glyphCssClass: "sprite-focus-hover ms-Icon ms-Icon--Nav2DMapView",
|
|
1821
|
-
heightInPixels: 510,
|
|
1822
|
-
weight: 5,
|
|
1823
|
-
matcher: {
|
|
1824
|
-
// match both JS and python
|
|
1825
|
-
searchString: "(?:tilemap(?:8|16|32)?\\s*(?:`|\\(\"\"\")(?:[ a-zA-Z0-9_]|\\n)*\\s*(?:`|\"\"\"\\)))|(?:tiles\\s*\\.\\s*createTilemap\\s*\\([^\\)]+\\))",
|
|
1826
|
-
isRegex: true,
|
|
1827
|
-
matchCase: true,
|
|
1828
|
-
matchWholeWord: false
|
|
1829
|
-
},
|
|
1830
|
-
proto: MonacoTilemapEditor
|
|
1831
|
-
};
|
|
1832
|
-
editor.registerMonacoFieldEditor(fieldEditorId, editor.tilemapEditorDefinition);
|
|
1833
|
-
})(editor = pxt.editor || (pxt.editor = {}));
|
|
1834
|
-
})(pxt || (pxt = {}));
|