[ts][player] Add SpinePlayerEditor back in for documentation page/blog.

This commit is contained in:
Mario Zechner 2023-12-03 22:44:23 +01:00
parent 8a9e345bdb
commit da25d821c3
9 changed files with 160 additions and 2871 deletions

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../dist/iife/spine-player.js"></script>
<title>Spine Player Editor</title>
</head>
<body style="width: 100vw; height: 100vh; display: flex; margin: 0; padding: 0;">
<div id="editor" style="width: 100%; height: 100%"></div>
<script>
var code = "<script src=\"https://unpkg.com/@esotericsoftware/spine-player@4.1.*/dist/iife/spine-player.js\"></" + "script>\n<link rel=\"stylesheet\" href=\"https://unpkg.com/@esotericsoftware/spine-player@4.1.*/dist/spine-player.css\">\n\n<div id=\"player-container\" style=\"width: 100%; height: 100vh;\"></div>\n\n<script>\nnew spine.SpinePlayer(\"player-container\", {\n jsonUrl: \"http://esotericsoftware.com/files/examples/4.1/spineboy/export/spineboy-pro.json\",\n atlasUrl: \"http://esotericsoftware.com/files/examples/4.1/spineboy/export/spineboy.atlas\"\n});\n</" + "script>".trim();
spine.SpinePlayerEditor.DEFAULT_CODE = code;
var player = new spine.SpinePlayerEditor(document.getElementById("editor"));
</script>
</body>
</html>

View File

@ -1,177 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script src="https://esotericsoftware.com/files/spine-player/3.8/codemirror.js"></script>
<script src="jscolor.js"></script>
<script src="embedding-generator.js"></script>
<link rel="stylesheet" href="https://esotericsoftware.com/files/spine-player/3.8/codemirror.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
#sp_generator_drop_zone {
width: 100%;
height: 100vh;
background: white;
text-align: center;
vertical-align: middle;
line-height: 100vh;
border: none;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.sp_generator_hidden {
display: none !important;
}
#sp_generator_editor {
display: flex;
flex-direction: column;
width: 100%;
height: 100vh;
}
.sp_generator_tabs {
display: flex;
flex-direction: row;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border-bottom: solid 1px #999999;
}
.sp_generator_tabs > span {
padding: 0.5em;
cursor: pointer;
}
.sp_generator_tabs > span:hover {
background:orangered;
}
.sp_generator_selected_tab {
border: solid 1px orangered;
}
.sp_generator_panel {
width: 100%;
height: 100%;
}
.sp_generator_panel TABLE TH {
text-align: right;
vertical-align: top;
}
.sp_generator_extra {
color: #ccc;
padding-left: 0.5em;
}
</style>
<body>
<div id="sp_generator_drop_zone" class="drop_zone">
<span>Choose .skel/.json, .atlas, and .png files, or drop them here.</span>
<input id="sp_generator_file_button" class="sp_generator_hidden" type="file" multiple accept=".json,.skel,.atlas,.png"/>
</div>
<div id="sp_generator_editor" class="sp_generator_hidden">
<div id="sp_generator_player">
</div>
<div id="sp_generator_config">
<div class="sp_generator_tabs">
<span class="sp_generator_selected_tab" data-tab="sp_generator_tab_general">General</span>
<span data-tab="sp_generator_tab_animations">Animations</span>
<span data-tab="sp_generator_tab_viewports">Viewports</span>
<span data-tab="sp_generator_tab_skins">Skins</span>
<span data-tab="sp_generator_tab_debug">Debug</span>
</div>
<div id="sp_generator_tab_general" class="sp_generator_panel">
<table>
<tr>
<th>Show controls</th>
<td><input id="sp_generator_show_controls" type="checkbox" checked="true"></td>
</tr>
<tr>
<th>Premultiplied alpha</th>
<td><input id="sp_generator_premultiplied_alpha" type="checkbox" checked="false"></td>
</tr>
<tr>
<th>Canvas alpha</th>
<td><input id="sp_generator_canvas_alpha" value="FF"></td>
</tr>
<tr>
<th>Background color</th>
<td><input id="sp_generator_background" class="jscolor {onFineChange:'changeBackgroundColor(this)'}'" value="000000"></td>
</tr>
<tr>
<th>Fullscreen background color</th>
<td><input id="sp_generator_background_fullscreen" class="jscolor {onFineChange:'changeFullscreenBackgroundColor(this)'}'" value="000000"></td>
</tr>
<tr style="vertical-align: top;">
<th>Background image</th>
<td>
<select id="sp_generator_background_image">
</select>
</td>
<td id="sp_generator_background_bounds" class="sp_generator_hidden">
<table>
<tr>
<th>X</th>
<td><input id="sp_generator_background_x"></td>
</tr>
<tr>
<th>Y</th>
<td><input id="sp_generator_background_y"></td>
</tr>
<tr>
<th>Width</th>
<td><input id="sp_generator_background_width"></td>
</tr>
<tr>
<th>Height</th>
<td><input id="sp_generator_background_height"></td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<div id="sp_generator_tab_animations" class="sp_generator_panel sp_generator_hidden">
Animations
</div>
<div id="sp_generator_tab_viewports" class="sp_generator_panel sp_generator_hidden">
Viewports
</div>
<div id="sp_generator_tab_skins" class="sp_generator_panel sp_generator_hidden">
Skins
</div>
<div id="sp_generator_tab_debug" class="sp_generator_panel sp_generator_hidden">
Debug
</div>
</div>
</div>
</body>
</html>

View File

@ -1,435 +0,0 @@
window.addEventListener("load", function (event) {
setupDropZone();
});
if (!String.prototype.endsWith) {
String.prototype.endsWith = function (search, this_len) {
if (this_len === undefined || this_len > this.length) {
this_len = this.length;
}
return this.substring(this_len - search.length, this_len) === search;
};
}
var appState = {
dataUrls: null,
jsonFile: null,
skelFile: null,
atlasFile: null,
minorVersion: null,
majorVersion: null,
player: null
}
function loadFiles(files) {
var skels = 0;
var skelFile = null;
var jsons = 0;
var jsonFile = null;
var atlases = 0;
var atlasFile = null;
var pngs = 0;
for (var i = 0; i < files.length; i++) {
var file = files[i].name.toLowerCase();
if (file.endsWith(".skel")) {
skels++;
skelFile = file;
}
if (file.endsWith(".json")) {
jsons++;
jsonFile = file;
}
if (file.endsWith(".atlas")) {
atlases++;
atlasFile = file;
}
if (file.endsWith(".png")) pngs++;
}
if ((skels == 0 && jsons == 0) || (skels != 0 && jsons != 0) || skels > 1 || jsons > 1) {
showError("Please specify a single .skel or .json file.");
return;
}
if (atlases != 1) {
showError("Please specify a single .atlas file.");
return;
}
var filesToLoad = files.length;
var dataUrls = {};
for (var i = 0; i < files.length; i++) {
var file = files[i];
var reader = new FileReader();
reader.onload = function (file) {
return function (dataUrl) {
console.log("Loaded " + file.name);
dataUrls[file.name] = dataUrl.target.result;
filesToLoad--;
if (filesToLoad == 0) {
setupPlayer(dataUrls, jsonFile, skelFile, atlasFile);
}
};
}(file);
reader.onerror = function () {
showError("Sorry, couldn't load all files.");
}
reader.readAsDataURL(file);
}
}
function setupPlayer(dataUrls, jsonFile, skelFile, atlasFile) {
var version = getSkeletonVersion(dataUrls, jsonFile, skelFile);
var major = parseInt(version.split("\.")[0]);
var minor = parseInt(version.split("\.")[1]);
appState.dataUrls = dataUrls;
appState.jsonFile = jsonFile;
appState.skelFile = skelFile;
appState.atlasFile = atlasFile;
appState.majorVersion = major;
appState.minorVersion = minor;
if (major == 3 && minor < 8) {
showError("Couldn't load script for Spine version " + version + ". Only skeletons with version >= 3.8 are supported.");
return;
}
var cssUrl = "https://esotericsoftware.com/files/spine-player/" + major + "." + minor + "/spine-player.min.css";
spine = null;
loadCSS(cssUrl, function () {
var playerUrl = "https://esotericsoftware.com/files/spine-player/" + major + "." + minor + "/spine-player.min.js";
//playerUrl = "../../dist/iife/spine-player.js"; // debug
loadJavaScript(playerUrl, function () {
document.getElementById("sp_generator_editor").classList.remove("sp_generator_hidden");
document.getElementById("sp_generator_drop_zone").classList.add("sp_generator_hidden");
var player = document.getElementById("sp_generator_player");
player.innerHTML = "";
var config = {
jsonUrl: jsonFile,
skelUrl: skelFile,
atlasUrl: atlasFile,
rawDataURIs: dataUrls,
success: setupConfigUI,
alpha: true, // needed so we can emulate shizzle
viewport: { // needed so we can see viewport bounds
debugRender: true
}
};
appState.player = new spine.SpinePlayer(player, config);
}, function () {
showError("Couldn't load script for Spine version " + version + ". Only skeletons with version 3.8+ are supported.");
});
}, function () {
showError("Couldn't load CSS for Spine version " + version + ". Only skeletons with version 3.8+ are supported.");
});
}
function setupConfigUI() {
// Setup tabs
var tabs = document.getElementsByClassName("sp_generator_tabs")[0];
var children = tabs.getElementsByTagName("span");
for (var i = 0; i < children.length; i++) {
(function (tab) {
tab.onclick = function () {
var panelId = tab.getAttribute("data-tab");
var panels = document.getElementById("sp_generator_config").getElementsByClassName("sp_generator_panel");
for (var i = 0; i < panels.length; i++) {
var panel = panels[i];
if (panelId == panel.getAttribute("id")) {
tab.classList.add("sp_generator_selected_tab");
panel.classList.remove("sp_generator_hidden");
} else {
tab.classList.remove("sp_generator_selected_tab");
panel.classList.add("sp_generator_hidden");
}
}
}
})(children[i]);
}
// Fill general tab
var showControls = document.getElementById("sp_generator_show_controls");
showControls.onchange = function () {
appState.player.config.showControls = showControls.checked;
};
var canvasAlpha = document.getElementById("sp_generator_canvas_alpha");
canvasAlpha.onchange = function () {
var re = /[0-9A-Fa-f]{2}/g;
if (canvasAlpha.value.length > 2 || !re.test(canvasAlpha.value))
canvasAlpha.value = "FF";
else
canvasAlpha.value = canvasAlpha.value.toUpperCase();
var alpha = Number.parseInt(canvasAlpha.value, 16);
appState.player.config.alpha = alpha != 0xff;
appState.player.config.backgroundColor = document.getElementById("sp_generator_background").value + canvasAlpha.value;
}
var premultipliedAlpha = document.getElementById("sp_generator_premultiplied_alpha");
var premultipliedAlpha = document.getElementById("sp_generator_premultiplied_alpha");
premultipliedAlpha.onchange = function () {
appState.player.config.premultipliedAlpha = premultipliedAlpha.checked;
}
var backgroundImage = document.getElementById("sp_generator_background_image");
backgroundImage.innerHTML = "";
var noneImage = document.createElement("option");
noneImage.value = "none";
noneImage.innerText = "None";
noneImage.selected = true;
backgroundImage.append(noneImage);
for (var data in appState.dataUrls) {
if (data.toLowerCase().endsWith(".png")) {
var image = document.createElement("option");
image.value = data;
image.innerText = data;
backgroundImage.append(image);
}
}
backgroundImage.onchange = function () {
var imageUrl = backgroundImage.value;
if (imageUrl != "none" && !appState.player.assetManager.get(imageUrl)) {
appState.player.assetManager.loadTexture(imageUrl);
}
var boundsTable = document.getElementById("sp_generator_background_bounds");
if (imageUrl == "none")
boundsTable.classList.add("sp_generator_hidden");
else
boundsTable.classList.remove("sp_generator_hidden");
if (appState.player.config.backgroundImage) {
appState.player.config.backgroundImage.url = imageUrl != "none" ? imageUrl : null;
} else {
appState.player.config.backgroundImage = {
url: imageUrl != "none" ? imageUrl : null
}
}
}
var backgroundX = document.getElementById("sp_generator_background_x");
backgroundX.onkeyup = backgroundX.onchange = function () {
var value = Number.parseFloat(backgroundX.value);
if (Number.isNaN(value)) return;
appState.player.config.backgroundImage.x = value;
};
var backgroundY = document.getElementById("sp_generator_background_y");
backgroundY.onkeyup = backgroundY.onchange = function () {
var value = Number.parseFloat(backgroundY.value);
if (Number.isNaN(value)) return;
appState.player.config.backgroundImage.y = value;
};
var backgroundWidth = document.getElementById("sp_generator_background_width");
backgroundWidth.onkeyup = backgroundWidth.onchange = function () {
var value = Number.parseFloat(backgroundWidth.value);
if (Number.isNaN(value)) return;
appState.player.config.backgroundImage.width = value;
};
var backgroundHeight = document.getElementById("sp_generator_background_height");
backgroundHeight.onkeyup = backgroundHeight.onchange = function () {
var value = Number.parseFloat(backgroundHeight.value);
if (Number.isNaN(value)) return;
appState.player.config.backgroundImage.height = value;
};
// Fill animations tab
// Fill viewports tab
// Fill skins tab
// Fill debug tab
}
function changeBackgroundColor(background) {
appState.player.config.backgroundColor = background.valueElement.value + document.getElementById("sp_generator_canvas_alpha").value;
}
function changeFullscreenBackgroundColor(background) {
appState.player.config.fullScreenBackgroundColor = background.valueElement.value;
}
function getSkeletonVersion(dataUrls, jsonFile, skelFile) {
if (jsonFile) {
var json = JSON.parse(atob(dataUrls[jsonFile].split(',')[1]));
return json.skeleton.spine;
} else {
var bytes = atob(dataUrls[skelFile].split(',')[1]);
var array = new Uint8Array(new ArrayBuffer(bytes.length));
for (var i = 0; i < bytes.length; i++) {
array[i] = bytes.charCodeAt(i);
}
var input = new BinaryInput(array);
input.readString();
var version = input.readString();
return version; x
}
}
function loadJavaScript(url, success, error) {
var script = document.createElement('script');
script.setAttribute('src', url);
script.setAttribute('type', 'text/javascript');
script.onload = success;
script.onerror = error;
document.getElementsByTagName("head")[0].appendChild(script);
};
function loadCSS(url, success, error) {
var script = document.createElement('link');
script.setAttribute('href', url);
script.setAttribute('rel', 'stylesheet');
script.onload = success;
script.onerror = error;
document.getElementsByTagName("head")[0].appendChild(script);
};
function showError(error) {
alert("Error: " + error);
}
function setupDropZone() {
var fileButton = document.getElementById("sp_generator_file_button");
var dropZone = document.getElementById("sp_generator_drop_zone");
dropZone.onclick = function () {
fileButton.click();
};
dropZone.addEventListener("dragenter", function (event) {
event.stopPropagation();
event.preventDefault();
}, false);
dropZone.addEventListener("dragover", function (event) {
event.stopPropagation();
event.preventDefault();
}, false);
dropZone.addEventListener("drop", function (event) {
event.stopPropagation();
event.preventDefault();
loadFiles(event.dataTransfer.files);
}, false);
fileButton.onchange = function () {
loadFiles(fileButton.files);
fileButton.value = "";
};
}
function generateScript(jsonFile, skelFile, atlasFile, dataUrls) {
var shortVersion = major + "." + minor;
var scriptCode =
'<script src="https://esotericsoftware.com/files/spine-player/' + shortVersion + '/spine-player.min.js"><' + '/script>\n' +
'<link rel="stylesheet" href="https://esotericsoftware.com/files/spine-player/' + shortVersion + '/spine-player.min.css">\n\n' +
'<div id="player-container" style="width: 100%; height: 100vh;"></div>\n\n' +
'<script>\n' +
'new spine.SpinePlayer("player-container", {\n';
if (jsonFile) scriptCode +=
' jsonUrl: "' + jsonFile + '",\n';
else scriptCode +=
' skelUrl: "' + skelFile + '",\n';
scriptCode +=
' atlasUrl: "' + atlasFile + '",\n' +
' rawDataURIs: {\n'
for (var file in dataUrls)
scriptCode += ' "' + file + '": "' + dataUrls[file] + '",\n';
scriptCode +=
' }\n' +
'});\n' +
'<' + '/script>';
}
var BinaryInput = (function () {
function BinaryInput(data, strings, index, buffer) {
if (strings === void 0) { strings = new Array(); }
if (index === void 0) { index = 0; }
if (buffer === void 0) { buffer = new DataView(data.buffer); }
this.strings = strings;
this.index = index;
this.buffer = buffer;
}
BinaryInput.prototype.readByte = function () {
return this.buffer.getInt8(this.index++);
};
BinaryInput.prototype.readShort = function () {
var value = this.buffer.getInt16(this.index);
this.index += 2;
return value;
};
BinaryInput.prototype.readInt32 = function () {
var value = this.buffer.getInt32(this.index);
this.index += 4;
return value;
};
BinaryInput.prototype.readInt = function (optimizePositive) {
var b = this.readByte();
var result = b & 0x7F;
if ((b & 0x80) != 0) {
b = this.readByte();
result |= (b & 0x7F) << 7;
if ((b & 0x80) != 0) {
b = this.readByte();
result |= (b & 0x7F) << 14;
if ((b & 0x80) != 0) {
b = this.readByte();
result |= (b & 0x7F) << 21;
if ((b & 0x80) != 0) {
b = this.readByte();
result |= (b & 0x7F) << 28;
}
}
}
}
return optimizePositive ? result : ((result >>> 1) ^ -(result & 1));
};
BinaryInput.prototype.readStringRef = function () {
var index = this.readInt(true);
return index == 0 ? null : this.strings[index - 1];
};
BinaryInput.prototype.readString = function () {
var byteCount = this.readInt(true);
switch (byteCount) {
case 0:
return null;
case 1:
return "";
}
byteCount--;
var chars = "";
var charCount = 0;
for (var i = 0; i < byteCount;) {
var b = this.readByte();
switch (b >> 4) {
case 12:
case 13:
chars += String.fromCharCode(((b & 0x1F) << 6 | this.readByte() & 0x3F));
i += 2;
break;
case 14:
chars += String.fromCharCode(((b & 0x0F) << 12 | (this.readByte() & 0x3F) << 6 | this.readByte() & 0x3F));
i += 3;
break;
default:
chars += String.fromCharCode(b);
i++;
}
}
return chars;
};
BinaryInput.prototype.readFloat = function () {
var value = this.buffer.getFloat32(this.index);
this.index += 4;
return value;
};
BinaryInput.prototype.readBoolean = function () {
return this.readByte() != 0;
};
return BinaryInput;
}());

View File

@ -1,152 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script src="jscolor.js"></script>
<script src="ui.js"></script>
<script src="loader.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
#dropzone {
width: 100%;
height: 100vh;
background: #cccccc;
text-align: center;
line-height: 100vh;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: pointer;
}
#editor {
display: flex;
flex-direction: row-reverse;
width: 100%;
height: 100vh;
background: #aaaaaa;
}
#player {
width: 100%;
height: 100%;
}
#config {
width: 100%;
height: 100%;
padding: 1em;
background: #f0f0f0;
}
</style>
<body>
<div id="dropzone">
Click to choose .skel/.json, .atlas, and .png files, or drop them here.
</div>
<div id="editor" style="display: none">
</div>
<script>
var UI;
var Loader;
var dropZone = document.getElementById("dropzone");
var editor = document.getElementById("editor")
function showError(errorMessage) {
alert("Error: " + errorMessage);
}
window.addEventListener("load", function(event) {
UI = spineGenerator.UI;
Loader = spineGenerator.Loader;
var dropZone = document.getElementById("dropzone");
new UI.DropZone(dropZone, true, ".json,.skel,.atlas,.png", function (files) {
spineGenerator.Loader.loadSkeletonFiles(files, function(data) {
console.log(`Loaded files ${data.jsonFile} ${data.skelFile} ${data.atlasFile}`);
console.log(`${data.version} ${data.majorVersion} ${data.minorVersion} ${data.patchVersion}`);
spine = null;
var cssUrl = `https://esotericsoftware.com/files/spine-player/${data.majorVersion}.${data.minorVersion}/spine-player.min.css`;
var playerUrl = `https://esotericsoftware.com/files/spine-player/${data.majorVersion}.${data.minorVersion}/spine-player.min.js`;
Loader.loadStyle(cssUrl, function () {
Loader.loadJavaScript(playerUrl, function () {
UI.hide(dropZone)
setupEditor(editor, data);
});
}, showError);
}, showError);
}, showError);
});
function setupEditor(editor, data) {
setupPlayer(editor, data, function(player) {
setupConfig(editor, player, data);
UI.show(editor, "flex");
}, showError)
}
function setupPlayer(editor, data, success, error) {
var playerElement = UI.createElement(editor, `<div id="player"></div>`);
var config = {
jsonUrl: data.jsonFile,
skelUrl: data.skelFile,
atlasUrl: data.atlasFile,
rawDataURIs: data.dataUrls,
success: success,
error: error,
alpha: true, // needed so we can emulate shizzle
viewport: { // needed so we can see viewport bounds
debugRender: true
}
};
return new spine.SpinePlayer(playerElement, config);
}
function setupConfig(editor, player, data) {
var cfg = UI.createElement(editor, `<div id="config"></div>`);
var topBar = UI.createElement(cfg, /*html*/`
<div style="display: flex; flex-direction: row;">
<button type="button">New</button>
<button type="button">Download HTML</button>
</div>
`);
var newButton = topBar.children[0];
newButton.onclick = function() {
player.stopRendering();
UI.show(dropZone);
UI.clear(editor);
UI.hide(editor);
}
var downloadButton = topBar.children[1];
UI.createElement(cfg, `<h3>General</h3>`);
}
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,186 +0,0 @@
var spineGenerator;
(function (spineGenerator) {
var Loader = (function () {
function Loader() {
}
Loader.loadSkeletonFiles = function(files, success, error) {
var skels = 0;
var skelFile = null;
var jsons = 0;
var jsonFile = null;
var atlases = 0;
var atlasFile = null;
var pngs = 0;
for (var i = 0; i < files.length; i++) {
var file = files[i].name.toLowerCase();
if (file.endsWith(".skel")) {
skels++;
skelFile = file;
}
if (file.endsWith(".json")) {
jsons++;
jsonFile = file;
}
if (file.endsWith(".atlas")) {
atlases++;
atlasFile = file;
}
if (file.endsWith(".png")) pngs++;
}
if ((skels == 0 && jsons == 0) || (skels != 0 && jsons != 0) || skels > 1 || jsons > 1) {
error("Please specify a single .skel or .json file.");
return;
}
if (atlases != 1) {
error("Please specify a single .atlas file.");
return;
}
var filesToLoad = files.length;
var dataUrls = {};
for (var i = 0; i < files.length; i++) {
var file = files[i];
var reader = new FileReader();
reader.onload = function(file) {
return function(dataUrl) {
console.log("Loaded " + file.name);
dataUrls[file.name] = dataUrl.target.result;
filesToLoad--;
if (filesToLoad == 0) {
var data = {
dataUrls: dataUrls,
jsonFile: jsonFile,
skelFile: skelFile,
atlasFile: atlasFile
};
var version = data.version = Loader.getSkeletonVersion(data);
data.majorVersion = parseInt(version.split("\.")[0]);
data.minorVersion = parseInt(version.split("\.")[1]);
data.patchVersion = parseInt(version.split("\.")[2]);
success(data);
}
};
}(file);
reader.onerror = function () {
error("Sorry, couldn't load all files.");
}
reader.readAsDataURL(file);
}
}
Loader.getSkeletonVersion = function (data) {
var jsonFile = data.jsonFile;
var skelFile = data.skelFile;
var dataUrls = data.dataUrls;
if (jsonFile) {
var json = JSON.parse(atob(dataUrls[jsonFile].split(',')[1]));
return json.skeleton.spine;
} else {
var bytes = atob(dataUrls[skelFile].split(',')[1]);
var array = new Uint8Array(new ArrayBuffer(bytes.length));
for (var i = 0; i < bytes.length; i++) {
array[i] = bytes.charCodeAt(i);
}
var input = new BinaryInput(array);
input.readString();
var version = input.readString();
return version;
}
}
Loader.loadJavaScript = function (url, success, error) {
var script = document.createElement('script');
script.setAttribute('src', url);
script.setAttribute('type', 'text/javascript');
script.onload = success;
script.onerror = error;
document.getElementsByTagName("head")[0].appendChild(script);
};
Loader.loadStyle = function(url, success, error) {
var style = document.createElement('link');
style.setAttribute('href', url);
style.setAttribute('rel', 'stylesheet');
style.onload = success;
style.onerror = error;
document.getElementsByTagName("head")[0].appendChild(style);
};
var BinaryInput = (function () {
function BinaryInput(data, strings, index, buffer) {
if (strings === void 0) { strings = new Array(); }
if (index === void 0) { index = 0; }
if (buffer === void 0) { buffer = new DataView(data.buffer); }
this.index = index;
this.buffer = buffer;
}
BinaryInput.prototype.readByte = function () {
return this.buffer.getInt8(this.index++);
};
BinaryInput.prototype.readInt = function (optimizePositive) {
var b = this.readByte();
var result = b & 0x7F;
if ((b & 0x80) != 0) {
b = this.readByte();
result |= (b & 0x7F) << 7;
if ((b & 0x80) != 0) {
b = this.readByte();
result |= (b & 0x7F) << 14;
if ((b & 0x80) != 0) {
b = this.readByte();
result |= (b & 0x7F) << 21;
if ((b & 0x80) != 0) {
b = this.readByte();
result |= (b & 0x7F) << 28;
}
}
}
}
return optimizePositive ? result : ((result >>> 1) ^ -(result & 1));
};
BinaryInput.prototype.readString = function () {
var byteCount = this.readInt(true);
switch (byteCount) {
case 0:
return null;
case 1:
return "";
}
byteCount--;
var chars = "";
var charCount = 0;
for (var i = 0; i < byteCount;) {
var b = this.readByte();
switch (b >> 4) {
case 12:
case 13:
chars += String.fromCharCode(((b & 0x1F) << 6 | this.readByte() & 0x3F));
i += 2;
break;
case 14:
chars += String.fromCharCode(((b & 0x0F) << 12 | (this.readByte() & 0x3F) << 6 | this.readByte() & 0x3F));
i += 3;
break;
default:
chars += String.fromCharCode(b);
i++;
}
}
return chars;
};
return BinaryInput;
}());
return Loader;
}());
spineGenerator.Loader = Loader;
}(spineGenerator || (spineGenerator = {})));

View File

@ -1,66 +0,0 @@
var spineGenerator;
(function (spineGenerator) {
var UI = (function () {
function UI(dropZone, multiple, acceptedExtensions, callback) {
}
UI.createElement = function (parent, html) {
parent.insertAdjacentHTML("beforeend", html);
return parent.lastElementChild;
}
UI.clear = function(element) {
element.innerHTML = "";
}
UI.hide = function(element) {
element.__oldDisplay = element.style.display;
element.style.display = "none";
}
UI.show = function(element, display) {
if (display) element.style.display = display;
else if (element.__oldDisplay) element.style.display = element.__oldDisplay;
else element.style.display = "block";
}
return UI;
}());
spineGenerator.UI = UI;
}(spineGenerator || (spineGenerator = {})));
(function (spineGenerator) {
var UI = spineGenerator.UI;
var DropZone = (function () {
function DropZone(dropZone, multiple, acceptedExtensions, callback) {
var fileButton = this.fileButton = UI.createElement(dropZone,
`<input style="display:none;" type="file" ${multiple?"multiple":""} accept="${acceptedExtensions}"/>`);
dropZone.onclick = function() {
fileButton.click();
};
dropZone.addEventListener("dragenter", function (event) {
event.stopPropagation();
event.preventDefault();
}, false);
dropZone.addEventListener("dragover", function (event) {
event.stopPropagation();
event.preventDefault();
}, false);
dropZone.addEventListener("drop", function (event) {
event.stopPropagation();
event.preventDefault();
loadFiles(event.dataTransfer.files);
}, false);
fileButton.onchange = function () {
callback(fileButton.files);
fileButton.value = "";
};
}
return DropZone;
}());
spineGenerator.UI.DropZone = DropZone;
}(spineGenerator || (spineGenerator = {})));

View File

@ -0,0 +1,141 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, 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<void> {
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<void> {
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 =
`
<script src="https://esotericsoftware.com/files/spine-player/4.1/spine-player.js"></script>
<link rel="stylesheet" href="https://esotericsoftware.com/files/spine-player/4.1/spine-player.css">
<div id="player-container" style="width: 100vw; height: 100vh;"></div>
<script>
new spine.SpinePlayer("player-container", {
jsonUrl: "https://esotericsoftware.com/files/examples/4.1/spineboy/export/spineboy-pro.json",
atlasUrl: "https://esotericsoftware.com/files/examples/4.1/spineboy/export/spineboy-pma.atlas"
});
</script>
`.trim();
private prefix: string =
`<html>
<head>
<style>
body { margin: 0px; }
</style>
</head>
<body>`.trim()
private postfix: string = `</body>`;
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*/`
<div style="display: flex; flex-direction: column; width: 100%; height: 100%;">
<div style="width: 100%; height: 50%;"></div>
<iframe style="width: 100%; height: 50%; outline: none; border: none;"></iframe>
</div>
`;
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();
});
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 = 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);
}
}

View File

@ -1,3 +1,4 @@
export * from './Player.js';
export * from './PlayerEditor.js';
export * from "@esotericsoftware/spine-core";
export * from "@esotericsoftware/spine-webgl";