UNPKG

@webwriter/geogebra

Version:

Use the mathematics learning apps of GeoGebra for algebra and geometry.

417 lines (348 loc) 16.7 kB
import {LitElement, html, css, PropertyValues} from "lit" import {LitElementWw, option} from "@webwriter/lit" import {customElement, property, query, queryAsync} from "lit/decorators.js" export interface GeogebraAPI { evalCommand(cmdString: string): boolean evalLaTex(input: string): boolean evalCommandGetLabels(cmdString: string): string evalCommandCAS(text: string): string insertEmbed(type: string, uri: string): void deleteObject(objName: string): void setAuxiliary(geo: any, status: boolean): void setValue(objName: string, value: number): void setTextValue(objName: string, value: string): void setListValue(objName: string, i: number, value: number): void setCoords(objName: string, x: number, y: number, z?: number): void setCaption(objName: string, caption: string): void setColor(objName: string, red: number, green: number, blue: number): void setVisible(objName: string, visible: boolean): void setLabelVisible(objName: string, visible: boolean): void setLabelStyle(objName: string, style: 0 | 1 | 2 | 3): void setFixed(objName: string, fixed: boolean, selectionAllowed: boolean): void setTrace(objName: string, flag: boolean): void renameObject(oldObjName: string, newObjName: string): boolean setLayer(objName: string, layer: number): void setLayerVisible(objName: string, visible: boolean): void setLineStyle(objName: string, style: 0 | 1 | 2 | 3 | 4): void setLineThickness(objName: string, thickness: -1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13): void setPointStyle(objName: string, size: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9): void setPointSize(objName: string, size: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9): void setDisplayStyle(objName: string, style: string): void setFilling(objName: string, filling: number): void getPNGBase64(exportScale: number, transparent?: boolean, dpi?: number): string exportSVG(filename: string): void exportSVG(callback: CallableFunction): void exportPDF(scale: number, filename: string, sliderLabel?: string): void exportPDF(scale: number, callback: CallableFunction, sliderLabel?: string): void getScreenshotBase64(callback: CallableFunction): void writePNGtoFile(fileName: string, exportScale?: number, transparent?: boolean, dpi?: number): boolean isIndependent(objName: string): boolean isMovable(objName: string): boolean showAllObjects(): void registerEmbedResolver(type: string, callback: CallableFunction): void setAnimating(objName: string, animate: boolean): void setAnimationSpeed(objName: string, speed: number): void startAnimation(): void stopAnimation(): void isAnimationRunning(): boolean getXcoord(objName: string): number getYcoord(objName: string): number getZcoord(objName: string): number getValue(objName: string): number getListValue(objName: string, index: number): number getColor(objName: string): string getVisible(objName: string): boolean getValueString(objName: string, useLocalizedInput?: boolean): string getDefinitionString(objName: string): string getCommandString(objName: string, useLocalizedInput?: boolean): string getLaTeXString(objName: string, value: boolean): string getObjectType(objName: string): string exists(objName: string): boolean isDefined(objName: string): boolean getAllObjectNames(type?: string): string[] getObjectNumber(): number getCASObjectNumber(): number getObjectName(i: number): string getLayer(objName: string): string getLineStyle(objName: string): 0 | 1 | 2 | 3 | 4 getLineThickness(objName: string): -1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 getPointStyle(objName: string): -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 getPointSize(objName: string): 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 getFilling(objName: string): number getCaption(objName: string, substitutePlaceholders?: boolean): string getLabelStyle(objName: string): 0 | 1 | 2 | 3 getLabelVisible(): boolean isInteractive(objName: string): boolean setMode(mode: number): void getMode(): number reset(): void newConstruction(): void refreshViews(): void setOnTheFlyPointCreationActive(flag: boolean): void setPointCapture(view: -1 | 1 | 2, mode: 0 | 1 | 2 | 3): void setRounding(round: string): void hideCursorWhenDragging(flag: boolean): void setRepaintingActive(flag: boolean): void setErrorDialogsActive(flag: boolean): void setCoordSystem(xmin: number, xmax: number, ymin: number, ymax: number, zmin?: number, zmax?: number, yVertical?: boolean): void setAxesVisible(xAxis: boolean, yAxis: boolean, zAxis?: boolean): void setUndoPoint(): void setAxisLabels(viewNumber: number, xAxis: string, yAxis: string, zAxis: string): void setAxisSteps(viewNumber: number, xAxis: number, yAxis: number, zAxis: number): void setAxisUnits(viewNumber: number, xAxis: string, yAxis: string, zAxis: string): void setGridVisible(flag: boolean): void setGridVisible(viewNumber: number, flag: boolean): void getGridVisible(viewNumber: number): boolean getPerspectiveXML(): string undo(): void redo(): void showToolBar(show: boolean): void setCustomToolBar(toolbar: string): void addCustomTool(iconURL: string, name: string, category?: string, callback?: CallableFunction): void showMenuBar(show: boolean): void showAlgebraInput(show: boolean): void showResetIcon(show: boolean): void enableRightClick(enable: boolean): void enableLabelDrags(enable: boolean): void enableShiftDragZoom(enable: boolean): void enableCAS(enable: boolean): void enable3D(enable: boolean): void setPerspective(perspective: string): void setWidth(width: number): void setHeight(height: number): void setSize(width: number, height: number): void recalculateEnvironments(): void getEditorState(): any setEditorState(state: any): void getGraphicsOptions(viewId: number): any setGraphicsOptions(viewID: number, options: any): void setAlgebraOptions(options: any): void registerAddListener(func: CallableFunction): void unregisterAddListener(func: CallableFunction): void registerRemoveListener(func: CallableFunction): void unregisterRemoveListener(func: CallableFunction): void registerUpdateListener(func: CallableFunction): void unregisterUpdateListener(func: CallableFunction): void registerClickListener(func: CallableFunction): void unregisterClickListener(func: CallableFunction): void registerObjectUpdateListener(objName: string, func: CallableFunction): void unregisterObjectUpdateListener(objName: string): void registerObjectClickListener(objName: string, func: CallableFunction): void unregisterObjectClickListener(objName: string): void registerRenameListener(func: CallableFunction): void unregisterRenameListener(func: CallableFunction): void registerClearListener(func: CallableFunction): void unregisterClearListener(func: CallableFunction): void registerStoreUndoListener(func: CallableFunction): void unregisterStoreUndoListener(func: CallableFunction): void registerClientListener(func: CallableFunction): void unregisterClientListener(func: CallableFunction): void evalXML(xmlString: string): void setXML(xmlString: string): void getXML(objName?: string): string getAlgorithmXML(objName: string): string getFileJSON(): any setFileJSON(content: any): void getBase64(callback?: CallableFunction): string setBase64(value: string, callback?: CallableFunction): void debug(str: string): void getVersion(): string remove(): void } export class GeogebraApp extends LitElementWw { static styles = css` iframe { border: none; width: 100%; aspect-ratio: 16/9; } ` @property({type: String}) ggbStyles: string @property({reflect: true}) type: "classic" | "geometry" | "3d" | "suite" | "evaluator" | "scientific" | "notes" = "classic" @property({type: Number, reflect: true}) width: number = 800 @property({type: Number, reflect: true}) height: number = 450 @property({type: String, attribute: true, reflect: true}) // base64, material ID, file url accessor src: string @property({type: String, reflect: true}) borderColor: string = "gray" @property({type: Number, reflect: true}) borderRadius: number = 1 @property({type: Boolean, reflect: true}) enableRightClick = true @property({type: Boolean, reflect: true}) enableLabelDrags = true @property({type: Boolean, reflect: true}) enableShiftDragZoom = true @property({type: Boolean, reflect: true}) showZoomButtons = false @property({type: Boolean, reflect: true}) errorDialogsActive = true @property({type: Boolean, reflect: true}) showMenuBar = false @property({type: Boolean, reflect: true}) showToolBar = false @property({type: Boolean, reflect: true}) showToolBarHelp = false @property({type: String, reflect: true}) customToolBar: string @property({type: Boolean, reflect: true}) showAlgebraInput = true @property({type: Boolean, reflect: true}) showResetIcon = false @property({type: String, reflect: true}) language: String @property({type: String, reflect: true}) country: String @property({type: String, reflect: true}) appletId: String @property({type: Boolean, reflect: true}) allowStyleBar = false @property({type: Boolean, reflect: true}) randomize = true @property({type: Number, reflect: true}) randomSeed: number @property({type: Boolean, reflect: true}) useBrowserForJs = false @property({type: Boolean, reflect: true}) showLogging = false @property({type: Number, reflect: true}) capturingThreshold = 3 @property({type: Boolean, reflect: true}) enableFileFeatures = true @property({type: Boolean, reflect: true}) enableUndoRedo = true @property({type: String, reflect: true}) perspective: String @property({type: Boolean, reflect: true}) enable3d: boolean @property({type: Boolean, reflect: true}) enableCAS: boolean @property({type: String, reflect: true}) algebraInputPosition: "algebra" | "top" | "bottom" @property({type: Boolean, reflect: true}) preventFocus = false @property({type: String, reflect: true}) scaleContainerClass: string @property({type: Boolean, reflect: true}) autoHeight = true @property({type: Boolean, reflect: true}) allowUpscale = false @property({type: Boolean, reflect: true}) playButton = false @property({type: Number, reflect: true}) scale = 1 @property({type: Boolean, reflect: true}) showAnimationButton = false @property({type: Boolean, reflect: true}) showFullscreenButton = false @property({type: Boolean, reflect: true}) showSuggestionButtons = false @property({type: Boolean, reflect: true}) showStartTooltip = false @property({type: String, reflect: true}) rounding: string @property({type: Boolean, reflect: true}) buttonShadows = false @property({type: Number, reflect: true}) buttonRounding: Number = 0.2 @property({type: String, reflect: true}) buttonBorderColor: string @property({type: String, reflect: true}) editorBackgroundColor: string @property({type: String, reflect: true}) editorForegroundColor: string @property({type: Boolean, reflect: true}) textmode = false @property({type: Boolean, reflect: true}) showKeyboardOnFocus: boolean @property({type: String, reflect: true}) keyboardType: "scientific" | "normal" | "notes" @property({type: Boolean, reflect: true}) transparentGraphics = false @property({type: Boolean, reflect: true}) disabledJavaScript = false @property({type: String, reflect: true}) detachedKeyboardParent: string private get ggbProperties() { const {appletId: id, type: appName, width, height, borderColor, borderRadius, enableLabelDrags, enableShiftDragZoom, showZoomButtons, errorDialogsActive, showMenuBar, showToolBar, showToolBarHelp, customToolBar, showAlgebraInput, showResetIcon, language, country, allowStyleBar, randomize, randomSeed, useBrowserForJs, showLogging, capturingThreshold, enableFileFeatures, perspective, enable3d, enableCAS, algebraInputPosition, preventFocus, scaleContainerClass, autoHeight, allowUpscale, playButton, scale, showAnimationButton, showFullscreenButton, showSuggestionButtons, showStartTooltip, rounding, buttonShadows, buttonRounding, buttonBorderColor, editorBackgroundColor, editorForegroundColor, textmode, showKeyboardOnFocus, keyboardType, transparentGraphics, disabledJavaScript, detachedKeyboardParent} = this let filename: string, material_id: string, ggbBase64: string if(this.src && this.src.startsWith("data:")) { ggbBase64 = this.src.split(",").at(-1) } else if(this.src && this.src.startsWith("ggbid:")) { material_id = this.src.slice("ggbid:".length) } else if(this.src) { filename = this.src } return {id, appName, filename, material_id, ggbBase64, width, height, borderColor, borderRadius, enableLabelDrags, enableShiftDragZoom, showZoomButtons, errorDialogsActive, showMenuBar, showToolBar, showToolBarHelp, customToolBar, showAlgebraInput, showResetIcon, language: language ?? this.lang, country, allowStyleBar, randomize, randomSeed, useBrowserForJs, showLogging, capturingThreshold, enableFileFeatures, perspective, enable3d, enableCAS, algebraInputPosition, preventFocus, scaleContainerClass, autoHeight, allowUpscale, playButton, scale, showAnimationButton, showFullscreenButton, showSuggestionButtons, showStartTooltip, rounding, buttonShadows, buttonRounding, buttonBorderColor, editorBackgroundColor, editorForegroundColor, textmode, showKeyboardOnFocus, keyboardType, transparentGraphics, disabledJavaScript, detachedKeyboardParent} } updateSrc = () => { if(!this.updatingGgb) { this.src = `data:application/vnd.geogebra.file;base64,${this.api.getBase64()}` } } connectedCallback(): void { super.connectedCallback() this.apiReady = new Promise(r => { this.addEventListener("ggb-load", (e: any) => { r(e.detail.api) this.api = e.detail.api this.api.registerAddListener(this.updateSrc) this.api.registerClearListener(this.updateSrc) this.api.registerUpdateListener(this.updateSrc) this.api.registerRenameListener(this.updateSrc) this.api.registerRemoveListener(this.updateSrc) }) }) } updatingGgb = false protected updated(changed: PropertyValues): void { if(changed.has("src") && this.src && this.src.split(",").at(-1) !== this?.api?.getBase64()) { this.updatingGgb = true this?.api?.setBase64(this.src.split(",").at(-1), () => this.updatingGgb = false) } else if(changed.has("src") && !this.src) { this.updatingGgb = true this?.api?.setBase64("", () => this.updatingGgb = false) } } apiReady: Promise<GeogebraAPI> api?: GeogebraAPI initializeIFrame(e: Event) { const iframe = e.target as HTMLIFrameElement const doc = iframe.contentDocument const win = iframe.contentWindow doc.body.style.margin = "0" if(this.ggbStyles) { const style = doc.createElement("style") style.textContent = this.ggbStyles doc.head.append(style) } doc.body.addEventListener("focusin", () => this.dispatchEvent(new FocusEvent("focus", {bubbles: true, composed: true}))) const ggbEl = doc.createElement("div") ggbEl.id = "ggb-element" doc.body.append(ggbEl); const deployScript = doc.createElement("script") deployScript.src = "https://www.geogebra.org/apps/deployggb.js" deployScript.addEventListener("load", () => { const createScript = doc.createElement("script") createScript.textContent = ` const applet = new GGBApplet({${JSON.stringify(this.ggbProperties).slice(1, -1)}, appletOnLoad: api => document.dispatchEvent(new CustomEvent("ggb-load", {bubbles: true, composed: true, detail: {api}}))}) applet.inject("ggb-element") ` doc.addEventListener("ggb-load", (e: any) => this.dispatchEvent(new CustomEvent("ggb-load", {bubbles: true, composed: true, detail: e.detail}))) doc.body.append(createScript) }) doc.body.append(deployScript) } render() { return html` <iframe id="ggb" @load=${this.initializeIFrame}></iframe> ` } }