/****************************************************************************** * Spine Runtimes License Agreement * Last updated April 5, 2025. Replaces all prior versions. * * Copyright (c) 2013-2025, Esoteric Software LLC * * Integration of the Spine Runtimes into software or otherwise creating * derivative works of the Spine Runtimes is permitted under the terms and * conditions of Section 2 of the Spine Editor License Agreement: * http://esotericsoftware.com/spine-editor-license * * Otherwise, it is permitted to integrate the Spine Runtimes into software * or otherwise create derivative works of the Spine Runtimes (collectively, * "Products"), provided that each user of the Products must obtain their own * Spine Editor license and redistribution of the Products in any form must * include this license and copyright notice. * * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ declare function CodeMirror (el: Element, config: any): void; function loadScript (url: string): Promise { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = url; script.onload = () => resolve(); script.onerror = () => reject(new Error(`Script load error for ${url}`)); document.head.appendChild(script); }); } function loadCss (url: string): Promise { return new Promise((resolve, reject) => { const link = document.createElement('link'); link.href = url; link.rel = 'stylesheet'; link.onload = () => resolve(); link.onerror = () => reject(new Error(`CSS load error for ${url}`)); document.head.appendChild(link); }); } export class SpinePlayerEditor { private static DEFAULT_CODE = `
`.trim(); private prefix: string = ` `.trim() private postfix: string = ``; private code: any; private player?: HTMLIFrameElement; constructor (private readonly parent: HTMLElement) { this.load(); } private async load () { await Promise.all([loadScript("https://www.unpkg.com/codemirror@5.51.0/lib/codemirror.js"), loadCss("https://www.unpkg.com/codemirror@5.51.0/lib/codemirror.css")]); this.render(this.parent); } private render (parent: HTMLElement) { let dom = /*html*/`
`; parent.innerHTML = dom; let codeElement = parent.children[0].children[0]; this.player = parent.children[0].children[1] as HTMLIFrameElement; requestAnimationFrame(() => { this.code = CodeMirror(codeElement, { lineNumbers: true, tabSize: 3, indentUnit: 3, indentWithTabs: true, scrollBarStyle: "native", mode: "htmlmixed", theme: "monokai" }); this.code.on("change", () => { this.startPlayer(); }); (codeElement.children[0] as HTMLElement).style.height = "100%"; this.setCode(SpinePlayerEditor.DEFAULT_CODE); }) } setPreAndPostfix (prefix: string, postfix: string) { this.prefix = prefix; this.postfix = postfix; this.startPlayer() } setCode (code: string) { this.code.setValue(code); this.startPlayer(); } private timerId: any = 0; startPlayer () { clearTimeout(this.timerId); this.timerId = setTimeout(() => { let code = this.code.getDoc().getValue(); code = this.prefix + code + this.postfix; code = window.btoa(code); this.player!.src = ""; this.player!.src = "data:text/html;base64," + code; }, 500); } }