Add rawData attribute to pass s stringified JSON object for inline base64 assets.

This commit is contained in:
Davide Tantillo 2025-02-03 11:55:19 +01:00
parent 23ece7f4da
commit c4d419caf9
3 changed files with 91 additions and 10 deletions

View File

@ -411,18 +411,21 @@ export class Downloader {
downloadText (url: string, success: (data: string) => void, error: (status: number, responseText: string) => void) {
if (this.start(url, success, error)) return;
if (this.rawDataUris[url]) {
const rawDataUri = this.rawDataUris[url];
// we assume if a "." is included in a raw data uri, it is used to rewrite an asset URL
if (rawDataUri && !rawDataUri.includes(".")) {
try {
let dataUri = this.rawDataUris[url];
this.finish(url, 200, this.dataUriToString(dataUri));
this.finish(url, 200, this.dataUriToString(rawDataUri));
} catch (e) {
this.finish(url, 400, JSON.stringify(e));
}
return;
}
let request = new XMLHttpRequest();
request.overrideMimeType("text/html");
request.open("GET", url, true);
request.open("GET", rawDataUri ? rawDataUri : url, true);
let done = () => {
this.finish(url, request.status, request.responseText);
};
@ -439,17 +442,20 @@ export class Downloader {
downloadBinary (url: string, success: (data: Uint8Array) => void, error: (status: number, responseText: string) => void) {
if (this.start(url, success, error)) return;
if (this.rawDataUris[url]) {
const rawDataUri = this.rawDataUris[url];
// we assume if a "." is included in a raw data uri, it is used to rewrite an asset URL
if (rawDataUri && !rawDataUri.includes(".")) {
try {
let dataUri = this.rawDataUris[url];
this.finish(url, 200, this.dataUriToUint8Array(dataUri));
this.finish(url, 200, this.dataUriToUint8Array(rawDataUri));
} catch (e) {
this.finish(url, 400, JSON.stringify(e));
}
return;
}
let request = new XMLHttpRequest();
request.open("GET", url, true);
request.open("GET", rawDataUri ? rawDataUri : url, true);
request.responseType = "arraybuffer";
let onerror = () => {
this.finish(url, request.status, request.response);

View File

@ -1021,7 +1021,56 @@
/////////////////////
-->
<!--
/////////////////////
// start section //
/////////////////////
-->
<div class="section vertical-split">
<div class="split-top split">
<div class="split-left">
If you want to embed your assets within the page, you can inline them using their base64 version. Use a stringified json object containing as keys the assets name and as values their base64 version.
</div>
<div class="split-right">
<spine-widget
atlas="inline.atlas"
skeleton="inline.skel"
animation="animation"
raw-data='{
"inline.atlas":"aW5saW5lLnBuZwpzaXplOjE2LDE2CmZpbHRlcjpMaW5lYXIsTGluZWFyCnBtYTp0cnVlCmRvdApib3VuZHM6MCwwLDEsMQo=",
"inline.skel":"/B8S/IqaXgYHNC4yLjM5wkgAAMJIAABCyAAAQsgAAELIAAAAAQRkb3QCBXJvb3QAAAAAAAAAAAAAAAA/gAAAP4AAAAAAAAAAAAAAAAAAAAAABGRvdAAAAAAAAAAAAAAAAABCyAAAQsgAAAAAAAAAAAAAAAAAAAAAAQRkb3QB//////////8BAAAAAAABAAEBACWwfdcAAAAAP4AAAD+AAAA/gAAAP4AAAAAAAQphbmltYXRpb24BAQABAQMAAAAAAP////8/gAAA/wAA/wBAAAAA/////wAAAAAAAAAAAA==",
"inline.png":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAANQTFRF////p8QbyAAAAApJREFUeJxjZAAAAAQAAiFkrWoAAAAASUVORK5CYII="
}'
></spine-widget>
</div>
</div>
<div class="split-bottom">
<pre><code id="code-display">
<script>escapeHTMLandInject(`
<spine-widget
atlas="assets/inline.atlas"
skeleton="assets/inline.skel"
animation="animation"
raw-data='{
"assets/inline.atlas":"aW5saW5lLnBuZwpzaXplOjE2LDE2CmZpbHRlcjpMaW5lYXIsTGluZWFyCnBtYTp0cnVlCmRvdApib3VuZHM6MCwwLDEsMQo=",
"assets/inline.skel":"/B8S/IqaXgYHNC4yLjM5wkgAAMJIAABCyAAAQsgAAELIAAAAAQRkb3QCBXJvb3QAAAAAAAAAAAAAAAA/gAAAP4AAAAAAAAAAAAAAAAAAAAAABGRvdAAAAAAAAAAAAAAAAABCyAAAQsgAAAAAAAAAAAAAAAAAAAAAAQRkb3QB//////////8BAAAAAAABAAEBACWwfdcAAAAAP4AAAD+AAAA/gAAAP4AAAAAAAQphbmltYXRpb24BAQABAQMAAAAAAP////8/gAAA/wAA/wBAAAAA/////wAAAAAAAAAAAA==",
"assets/inline.png":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAANQTFRF////p8QbyAAAAApJREFUeJxjZAAAAAQAAiFkrWoAAAAASUVORK5CYII="
}'
></spine-widget>`
);</script>
</code></pre>
</div>
</div>
<!--
/////////////////////
// end section //
/////////////////////
-->
<!--
/////////////////////

View File

@ -158,7 +158,7 @@ function castToAnimationsInfo (value: string | null): AnimationsInfo | undefined
}, {} as AnimationsInfo);
}
export type AttributeTypes = "string" | "number" | "boolean" | "array-number" | "array-string" | "fitType" | "modeType" | "offScreenUpdateBehaviourType" | "animationsInfo";
export type AttributeTypes = "string" | "number" | "boolean" | "array-number" | "array-string" | "object" | "fitType" | "modeType" | "offScreenUpdateBehaviourType" | "animationsInfo";
export type CursorEventTypes = "down" | "up" | "enter" | "leave" | "move" | "drag";
export type CursorEventTypesInput = Exclude<CursorEventTypes, "enter" | "leave">;
@ -167,6 +167,7 @@ export type CursorEventTypesInput = Exclude<CursorEventTypes, "enter" | "leave">
interface WidgetAttributes {
atlasPath?: string
skeletonPath?: string
rawData?: Record<string, string>
jsonSkeletonKey?: string
scale: number
animation?: string
@ -268,6 +269,12 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable,
*/
public skeletonPath?: string;
/**
* Holds the assets in base64 format.
* Connected to `raw-data` attribute.
*/
public rawData?: Record<string ,string>;
/**
* The name of the skeleton when the skeleton file is a JSON and contains multiple skeletons.
* Connected to `json-skeleton-key` attribute.
@ -763,6 +770,7 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable,
static attributesDescription: Record<string, { propertyName: keyof WidgetAttributes, type: AttributeTypes, defaultValue?: any }> = {
atlas: { propertyName: "atlasPath", type: "string" },
skeleton: { propertyName: "skeletonPath", type: "string" },
"raw-data": { propertyName: "rawData", type: "object" },
"json-skeleton-key": { propertyName: "jsonSkeletonKey", type: "string" },
scale: { propertyName: "scale", type: "number" },
animation: { propertyName: "animation", type: "string", defaultValue: undefined },
@ -972,12 +980,18 @@ export class SpineWebComponentWidget extends HTMLElement implements Disposable,
private async loadSkeleton () {
this.loading = true;
const { atlasPath, skeletonPath, scale, skeletonData: skeletonDataInput } = this;
const { atlasPath, skeletonPath, scale, skeletonData: skeletonDataInput, rawData } = this;
if (!atlasPath || !skeletonPath) {
throw new Error(`Missing atlas path or skeleton path. Assets cannot be loaded: atlas: ${atlasPath}, skeleton: ${skeletonPath}`);
}
const isBinary = skeletonPath.endsWith(".skel");
if (rawData) {
for (let [key, value] of Object.entries(rawData)) {
this.overlay.assetManager.setRawDataURI(key, isBase64(value) ? `data:application/octet-stream;base64,${value}` : value);
}
}
// skeleton and atlas txt are loaded immeaditely
// textures are loaeded depending on the 'pages' param:
// - [0,2]: only pages at index 0 and 2 are loaded
@ -2320,6 +2334,16 @@ function castArrayString (value: string | null, defaultValue = undefined) {
return value.split(",");
}
function castObject (value: string | null, defaultValue = undefined) {
if (value === null) return null;
return JSON.parse(value);
}
const base64RegExp = /^(([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==))$/;
function isBase64(str: string) {
return base64RegExp.test(str);
}
function castValue (type: AttributeTypes, value: string | null, defaultValue?: any) {
switch (type) {
case "string":
@ -2332,6 +2356,8 @@ function castValue (type: AttributeTypes, value: string | null, defaultValue?: a
return castArrayNumber(value, defaultValue);
case "array-string":
return castArrayString(value, defaultValue);
case "object":
return castObject(value, defaultValue);
case "fitType":
return isFitType(value) ? value : defaultValue;
case "modeType":