export function bindEditorMessages()

in pxteditor/editorcontroller.ts [397:632]


    export function bindEditorMessages(getEditorAsync: () => Promise<IProjectView>) {
        const allowEditorMessages = (pxt.appTarget.appTheme.allowParentController || pxt.shell.isControllerMode())
            && pxt.BrowserUtils.isIFrame();
        const allowExtensionMessages = pxt.appTarget.appTheme.allowPackageExtensions;
        const allowSimTelemetry = pxt.appTarget.appTheme.allowSimulatorTelemetry;

        if (!allowEditorMessages && !allowExtensionMessages && !allowSimTelemetry) return;

        window.addEventListener("message", (msg: MessageEvent) => {
            const data = msg.data as EditorMessage;
            if (!data || !/^pxt(host|editor|pkgext|sim)$/.test(data.type)) return false;

            if (data.type === "pxtpkgext" && allowExtensionMessages) {
                // Messages sent to the editor iframe from a child iframe containing an extension
                getEditorAsync().then(projectView => {
                    projectView.handleExtensionRequest(data as ExtensionRequest);
                })
            }
            else if (data.type === "pxtsim" && allowSimTelemetry) {
                const event = data as EditorMessageEventRequest;
                if (event.action === "event") {
                    if (event.category || event.message) {
                        pxt.reportError(event.category, event.message, event.data as Map<string>)
                    }
                    else {
                        pxt.tickEvent(event.tick, event.data);
                    }
                }
            }
            else if (allowEditorMessages) {
                // Messages sent to the editor from the parent frame
                let p = Promise.resolve();
                let resp: any = undefined;
                if (data.type == "pxthost") { // response from the host
                    const req = pendingRequests[data.id];
                    if (!req) {
                        pxt.debug(`pxthost: unknown request ${data.id}`);
                    } else {
                        p = p.then(() => req.resolve(data as EditorMessageResponse));
                    }
                } else if (data.type == "pxteditor") { // request from the editor
                    p = p.then(() => {
                        return getEditorAsync().then(projectView => {
                            const req = data as EditorMessageRequest;
                            pxt.debug(`pxteditor: ${req.action}`);
                            switch (req.action.toLowerCase()) {
                                case "switchjavascript": return Promise.resolve().then(() => projectView.openJavaScript());
                                case "switchpython": return Promise.resolve().then(() => projectView.openPython());
                                case "switchblocks": return Promise.resolve().then(() => projectView.openBlocks());
                                case "startsimulator": return Promise.resolve().then(() => projectView.startSimulator());
                                case "restartsimulator": return Promise.resolve().then(() => projectView.restartSimulator());
                                case "hidesimulator": return Promise.resolve().then(() => projectView.collapseSimulator());
                                case "showsimulator": return Promise.resolve().then(() => projectView.expandSimulator());
                                case "closeflyout": return Promise.resolve().then(() => projectView.closeFlyout());
                                case "unloadproject": return Promise.resolve().then(() => projectView.unloadProjectAsync());
                                case "saveproject": return projectView.saveProjectAsync();
                                case "redo": return Promise.resolve()
                                    .then(() => {
                                        const editor = projectView.editor;
                                        if (editor && editor.hasRedo())
                                            editor.redo();
                                    });
                                case "undo": return Promise.resolve()
                                    .then(() => {
                                        const editor = projectView.editor;
                                        if (editor && editor.hasUndo())
                                            editor.undo();
                                    });
                                case "setscale": {
                                    const zoommsg = data as EditorMessageSetScaleRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.editor.setScale(zoommsg.scale));
                                }
                                case "stopsimulator": {
                                    const stop = data as EditorMessageStopRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.stopSimulator(stop.unload));
                                }
                                case "newproject": {
                                    const create = data as EditorMessageNewProjectRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.newProject(create.options));
                                }
                                case "importproject": {
                                    const load = data as EditorMessageImportProjectRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.importProjectAsync(load.project, {
                                            filters: load.filters,
                                            searchBar: load.searchBar
                                        }));
                                }
                                case "openheader": {
                                    const open = data as EditorMessageOpenHeaderRequest;
                                    return projectView.openProjectByHeaderIdAsync(open.headerId)
                                }
                                case "startactivity": {
                                    const msg = data as EditorMessageStartActivity;
                                    let tutorialPath = msg.path;
                                    let editorProjectName: string = undefined;
                                    if (/^([jt]s|py|blocks?):/i.test(tutorialPath)) {
                                        if (/^py:/i.test(tutorialPath))
                                            editorProjectName = pxt.PYTHON_PROJECT_NAME;
                                        else if (/^[jt]s:/i.test(tutorialPath))
                                            editorProjectName = pxt.JAVASCRIPT_PROJECT_NAME;
                                        else
                                            editorProjectName = pxt.BLOCKS_PROJECT_NAME;
                                        tutorialPath = tutorialPath.substr(tutorialPath.indexOf(':') + 1)
                                    }
                                    return Promise.resolve()
                                        .then(() => projectView.startActivity({
                                            activity: msg.activityType,
                                            path: tutorialPath,
                                            title: msg.title,
                                            editor: editorProjectName,
                                            previousProjectHeaderId: msg.previousProjectHeaderId,
                                            carryoverPreviousCode: msg.carryoverPreviousCode
                                        }));
                                }
                                case "importtutorial": {
                                    const load = data as EditorMessageImportTutorialRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.importTutorialAsync(load.markdown));
                                }
                                case "proxytosim": {
                                    const simmsg = data as EditorMessageSimulatorMessageProxyRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.proxySimulatorMessage(simmsg.content));
                                }
                                case "renderblocks": {
                                    const rendermsg = data as EditorMessageRenderBlocksRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.renderBlocksAsync(rendermsg))
                                        .then(r => {
                                            return r.xml.then((svg: any) => {
                                                resp = svg.xml;
                                            })
                                        });
                                }
                                case "renderpython": {
                                    const rendermsg = data as EditorMessageRenderPythonRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.renderPythonAsync(rendermsg))
                                        .then(r => {
                                            resp = r.python;
                                        });
                                }
                                case "toggletrace": {
                                    const togglemsg = data as EditorMessageToggleTraceRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.toggleTrace(togglemsg.intervalSpeed));
                                }
                                case "settracestate": {
                                    const trcmsg = data as EditorMessageSetTraceStateRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.setTrace(trcmsg.enabled, trcmsg.intervalSpeed));
                                }
                                case "setsimulatorfullscreen": {
                                    const fsmsg = data as EditorMessageSetSimulatorFullScreenRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.setSimulatorFullScreen(fsmsg.enabled));
                                }
                                case "togglehighcontrast": {
                                    return Promise.resolve()
                                        .then(() => projectView.toggleHighContrast());
                                }
                                case "sethighcontrast": {
                                    const hcmsg = data as EditorMessageSetHighContrastRequest;
                                    return Promise.resolve()
                                        .then(() => projectView.setHighContrast(hcmsg.on));
                                }
                                case "togglegreenscreen": {
                                    return Promise.resolve()
                                        .then(() => projectView.toggleGreenScreen());
                                }
                                case "print": {
                                    return Promise.resolve()
                                        .then(() => projectView.printCode());
                                }
                                case "pair": {
                                    return projectView.pairAsync();
                                }
                                case "info": {
                                    return Promise.resolve()
                                        .then(() => {
                                            resp = <editor.InfoMessage>{
                                                versions: pxt.appTarget.versions,
                                                locale: ts.pxtc.Util.userLanguage(),
                                                availableLocales: pxt.appTarget.appTheme.availableLocales
                                            }
                                        });
                                }
                                case "shareproject": {
                                    const msg = data as EditorShareRequest;
                                    return projectView.anonymousPublishHeaderByIdAsync(msg.headerId)
                                        .then(scriptInfo => {
                                            resp = scriptInfo;
                                        });
                                }
                                case "savelocalprojectstocloud": {
                                    const msg = data as EditorMessageSaveLocalProjectsToCloud;
                                    return projectView.saveLocalProjectsToCloudAsync(msg.headerIds)
                                        .then(guidMap => {
                                            resp = <EditorMessageSaveLocalProjectsToCloudResponse>{
                                                headerIdMap: guidMap
                                            };
                                        })
                                }
                                case "requestprojectcloudstatus": {
                                    // Responses are sent as separate "projectcloudstatus" messages.
                                    const msg = data as EditorMessageRequestProjectCloudStatus;
                                    return projectView.requestProjectCloudStatus(msg.headerIds);
                                }
                                case "convertcloudprojectstolocal": {
                                    const msg = data as EditorMessageConvertCloudProjectsToLocal;
                                    return projectView.convertCloudProjectsToLocal(msg.userId);
                                }
                                case "setlanguagerestriction": {
                                    const msg = data as EditorSetLanguageRestriction;
                                    if (msg.restriction === "no-blocks") {
                                        console.warn("no-blocks language restriction is not supported");
                                        throw new Error("no-blocks language restriction is not supported")
                                    }
                                    return projectView.setLanguageRestrictionAsync(msg.restriction);
                                }
                            }
                            return Promise.resolve();
                        });
                    })
                }
                p.then(() => sendResponse(data, resp, true, undefined),
                    (err) => sendResponse(data, resp, false, err))
            }

            return true;
        }, false)
    }