浏览代码

Merge branch '3.8' into 3.9-beta

Harald Csaszar 5 年之前
父节点
当前提交
feba925a79
共有 33 个文件被更改,包括 2471 次插入270 次删除
  1. 二进制
      examples/spine-unity/hero/hero-pro.spine
  2. 二进制
      examples/spine-unity/hero/images/body.png
  3. 二进制
      examples/spine-unity/hero/images/cape.png
  4. 二进制
      examples/spine-unity/hero/images/eyes.png
  5. 二进制
      examples/spine-unity/hero/images/fingers.png
  6. 二进制
      examples/spine-unity/hero/images/foot1.png
  7. 二进制
      examples/spine-unity/hero/images/foot2.png
  8. 二进制
      examples/spine-unity/hero/images/forearm1.png
  9. 二进制
      examples/spine-unity/hero/images/forearm2.png
  10. 二进制
      examples/spine-unity/hero/images/hand1.png
  11. 二进制
      examples/spine-unity/hero/images/hand2.png
  12. 二进制
      examples/spine-unity/hero/images/head.png
  13. 二进制
      examples/spine-unity/hero/images/mantles.png
  14. 二进制
      examples/spine-unity/hero/images/mouth.png
  15. 二进制
      examples/spine-unity/hero/images/shin1.png
  16. 二进制
      examples/spine-unity/hero/images/shin2.png
  17. 二进制
      examples/spine-unity/hero/images/sword.png
  18. 二进制
      examples/spine-unity/hero/images/thigh1.png
  19. 二进制
      examples/spine-unity/hero/images/thigh2.png
  20. 二进制
      examples/spine-unity/hero/images/upper-arm1.png
  21. 二进制
      examples/spine-unity/hero/images/upper-arm2.png
  22. 0 146
      examples/spine-unity/hero/import/hero-pro.atlas.txt
  23. 0 116
      examples/spine-unity/hero/import/hero-pro.json
  24. 二进制
      examples/spine-unity/hero/import/hero-pro.png
  25. 0 5
      examples/spine-unity/hero/import/license.txt
  26. 1 1
      spine-ts/build/spine-all.js
  27. 0 0
      spine-ts/build/spine-all.js.map
  28. 1 1
      spine-ts/build/spine-player.js
  29. 0 0
      spine-ts/build/spine-player.js.map
  30. 177 0
      spine-ts/player/example/generator/embedding-generator.html
  31. 436 0
      spine-ts/player/example/generator/embedding-generator.js
  32. 1855 0
      spine-ts/player/example/generator/jscolor.js
  33. 1 1
      spine-ts/player/src/Player.ts

二进制
examples/spine-unity/hero/hero-pro.spine


二进制
examples/spine-unity/hero/images/body.png


二进制
examples/spine-unity/hero/images/cape.png


二进制
examples/spine-unity/hero/images/eyes.png


二进制
examples/spine-unity/hero/images/fingers.png


二进制
examples/spine-unity/hero/images/foot1.png


二进制
examples/spine-unity/hero/images/foot2.png


二进制
examples/spine-unity/hero/images/forearm1.png


二进制
examples/spine-unity/hero/images/forearm2.png


二进制
examples/spine-unity/hero/images/hand1.png


二进制
examples/spine-unity/hero/images/hand2.png


二进制
examples/spine-unity/hero/images/head.png


二进制
examples/spine-unity/hero/images/mantles.png


二进制
examples/spine-unity/hero/images/mouth.png


二进制
examples/spine-unity/hero/images/shin1.png


二进制
examples/spine-unity/hero/images/shin2.png


二进制
examples/spine-unity/hero/images/sword.png


二进制
examples/spine-unity/hero/images/thigh1.png


二进制
examples/spine-unity/hero/images/thigh2.png


二进制
examples/spine-unity/hero/images/upper-arm1.png


二进制
examples/spine-unity/hero/images/upper-arm2.png


+ 0 - 146
examples/spine-unity/hero/import/hero-pro.atlas.txt

@@ -1,146 +0,0 @@
-
-hero-pro.png
-size: 1024,256
-format: RGBA8888
-filter: Linear,Linear
-repeat: none
-body
-  rotate: false
-  xy: 324, 81
-  size: 97, 95
-  orig: 97, 95
-  offset: 0, 0
-  index: -1
-cape
-  rotate: false
-  xy: 176, 88
-  size: 146, 159
-  orig: 146, 159
-  offset: 0, 0
-  index: -1
-eyes
-  rotate: false
-  xy: 604, 216
-  size: 82, 31
-  orig: 82, 31
-  offset: 0, 0
-  index: -1
-fingers
-  rotate: false
-  xy: 877, 214
-  size: 31, 33
-  orig: 31, 33
-  offset: 0, 0
-  index: -1
-foot1
-  rotate: false
-  xy: 743, 205
-  size: 50, 42
-  orig: 50, 42
-  offset: 0, 0
-  index: -1
-foot2
-  rotate: false
-  xy: 688, 209
-  size: 53, 38
-  orig: 53, 38
-  offset: 0, 0
-  index: -1
-forearm1
-  rotate: false
-  xy: 795, 198
-  size: 41, 49
-  orig: 41, 49
-  offset: 0, 0
-  index: -1
-forearm2
-  rotate: false
-  xy: 910, 215
-  size: 31, 32
-  orig: 31, 32
-  offset: 0, 0
-  index: -1
-hand1
-  rotate: false
-  xy: 838, 199
-  size: 37, 48
-  orig: 37, 48
-  offset: 0, 0
-  index: -1
-hand2
-  rotate: false
-  xy: 286, 49
-  size: 31, 37
-  orig: 31, 37
-  offset: 0, 0
-  index: -1
-head
-  rotate: false
-  xy: 2, 74
-  size: 172, 173
-  orig: 172, 173
-  offset: 0, 0
-  index: -1
-mantles
-  rotate: false
-  xy: 2, 17
-  size: 136, 55
-  orig: 136, 55
-  offset: 0, 0
-  index: -1
-mouth
-  rotate: false
-  xy: 2, 2
-  size: 61, 13
-  orig: 61, 13
-  offset: 0, 0
-  index: -1
-shin1
-  rotate: false
-  xy: 482, 119
-  size: 53, 57
-  orig: 53, 57
-  offset: 0, 0
-  index: -1
-shin2
-  rotate: false
-  xy: 192, 32
-  size: 51, 54
-  orig: 51, 54
-  offset: 0, 0
-  index: -1
-sword
-  rotate: false
-  xy: 324, 178
-  size: 216, 69
-  orig: 216, 69
-  offset: 0, 0
-  index: -1
-thigh1
-  rotate: false
-  xy: 542, 184
-  size: 60, 63
-  orig: 60, 63
-  offset: 0, 0
-  index: -1
-thigh2
-  rotate: false
-  xy: 423, 112
-  size: 57, 64
-  orig: 57, 64
-  offset: 0, 0
-  index: -1
-upper-arm1
-  rotate: false
-  xy: 140, 16
-  size: 50, 56
-  orig: 50, 56
-  offset: 0, 0
-  index: -1
-upper-arm2
-  rotate: false
-  xy: 245, 27
-  size: 39, 59
-  orig: 39, 59
-  offset: 0, 0
-  index: -1

文件差异内容过多而无法显示
+ 0 - 116
examples/spine-unity/hero/import/hero-pro.json


二进制
examples/spine-unity/hero/import/hero-pro.png


+ 0 - 5
examples/spine-unity/hero/import/license.txt

@@ -1,5 +0,0 @@
-Copyright (c) 2014, XDTech
-
-The project file and images in this "Hero" project are provided for
-demonstration purposes only and may not be redistributed for any reason nor
-used as the basis for derivative work.

+ 1 - 1
spine-ts/build/spine-all.js

@@ -11967,7 +11967,7 @@ var spine;
 				this.sceneRenderer.begin();
 				if (this.config.backgroundImage && this.config.backgroundImage.url) {
 					var bgImage = this.assetManager.get(this.config.backgroundImage.url);
-					if (!this.config.backgroundImage.x) {
+					if (!(this.config.backgroundImage.hasOwnProperty("x") && this.config.backgroundImage.hasOwnProperty("y") && this.config.backgroundImage.hasOwnProperty("width") && this.config.backgroundImage.hasOwnProperty("height"))) {
 						this.sceneRenderer.drawTexture(bgImage, viewport.x, viewport.y, viewport.width, viewport.height);
 					}
 					else {

文件差异内容过多而无法显示
+ 0 - 0
spine-ts/build/spine-all.js.map


+ 1 - 1
spine-ts/build/spine-player.js

@@ -11291,7 +11291,7 @@ var spine;
 				this.sceneRenderer.begin();
 				if (this.config.backgroundImage && this.config.backgroundImage.url) {
 					var bgImage = this.assetManager.get(this.config.backgroundImage.url);
-					if (!this.config.backgroundImage.x) {
+					if (!(this.config.backgroundImage.hasOwnProperty("x") && this.config.backgroundImage.hasOwnProperty("y") && this.config.backgroundImage.hasOwnProperty("width") && this.config.backgroundImage.hasOwnProperty("height"))) {
 						this.sceneRenderer.drawTexture(bgImage, viewport.x, viewport.y, viewport.width, viewport.height);
 					}
 					else {

文件差异内容过多而无法显示
+ 0 - 0
spine-ts/build/spine-player.js.map


+ 177 - 0
spine-ts/player/example/generator/embedding-generator.html

@@ -0,0 +1,177 @@
+<!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>

+ 436 - 0
spine-ts/player/example/generator/embedding-generator.js

@@ -0,0 +1,436 @@
+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.css";
+	spine = null;
+	loadCSS(cssUrl, function () {
+		var playerUrl = "https://esotericsoftware.com/files/spine-player/" + major + "." + minor + "/spine-player.js";
+		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);
+}
+
+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.js"><' + '/script>\n' +
+	'<link rel="stylesheet" href="https://esotericsoftware.com/files/spine-player/' + shortVersion + '/spine-player.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;
+}());

+ 1855 - 0
spine-ts/player/example/generator/jscolor.js

@@ -0,0 +1,1855 @@
+/**
+ * jscolor - JavaScript Color Picker
+ *
+ * @link    http://jscolor.com
+ * @license For open source use: GPLv3
+ *          For commercial use: JSColor Commercial License
+ * @author  Jan Odvarko
+ * @version 2.0.5
+ *
+ * See usage examples at http://jscolor.com/examples/
+ */
+
+
+"use strict";
+
+
+if (!window.jscolor) { window.jscolor = (function () {
+
+
+var jsc = {
+
+
+	register : function () {
+		jsc.attachDOMReadyEvent(jsc.init);
+		jsc.attachEvent(document, 'mousedown', jsc.onDocumentMouseDown);
+		jsc.attachEvent(document, 'touchstart', jsc.onDocumentTouchStart);
+		jsc.attachEvent(window, 'resize', jsc.onWindowResize);
+	},
+
+
+	init : function () {
+		if (jsc.jscolor.lookupClass) {
+			jsc.jscolor.installByClassName(jsc.jscolor.lookupClass);
+		}
+	},
+
+
+	tryInstallOnElements : function (elms, className) {
+		var matchClass = new RegExp('(^|\\s)(' + className + ')(\\s*(\\{[^}]*\\})|\\s|$)', 'i');
+
+		for (var i = 0; i < elms.length; i += 1) {
+			if (elms[i].type !== undefined && elms[i].type.toLowerCase() == 'color') {
+				if (jsc.isColorAttrSupported) {
+					// skip inputs of type 'color' if supported by the browser
+					continue;
+				}
+			}
+			var m;
+			if (!elms[i].jscolor && elms[i].className && (m = elms[i].className.match(matchClass))) {
+				var targetElm = elms[i];
+				var optsStr = null;
+
+				var dataOptions = jsc.getDataAttr(targetElm, 'jscolor');
+				if (dataOptions !== null) {
+					optsStr = dataOptions;
+				} else if (m[4]) {
+					optsStr = m[4];
+				}
+
+				var opts = {};
+				if (optsStr) {
+					try {
+						opts = (new Function ('return (' + optsStr + ')'))();
+					} catch(eParseError) {
+						jsc.warn('Error parsing jscolor options: ' + eParseError + ':\n' + optsStr);
+					}
+				}
+				targetElm.jscolor = new jsc.jscolor(targetElm, opts);
+			}
+		}
+	},
+
+
+	isColorAttrSupported : (function () {
+		var elm = document.createElement('input');
+		if (elm.setAttribute) {
+			elm.setAttribute('type', 'color');
+			if (elm.type.toLowerCase() == 'color') {
+				return true;
+			}
+		}
+		return false;
+	})(),
+
+
+	isCanvasSupported : (function () {
+		var elm = document.createElement('canvas');
+		return !!(elm.getContext && elm.getContext('2d'));
+	})(),
+
+
+	fetchElement : function (mixed) {
+		return typeof mixed === 'string' ? document.getElementById(mixed) : mixed;
+	},
+
+
+	isElementType : function (elm, type) {
+		return elm.nodeName.toLowerCase() === type.toLowerCase();
+	},
+
+
+	getDataAttr : function (el, name) {
+		var attrName = 'data-' + name;
+		var attrValue = el.getAttribute(attrName);
+		if (attrValue !== null) {
+			return attrValue;
+		}
+		return null;
+	},
+
+
+	attachEvent : function (el, evnt, func) {
+		if (el.addEventListener) {
+			el.addEventListener(evnt, func, false);
+		} else if (el.attachEvent) {
+			el.attachEvent('on' + evnt, func);
+		}
+	},
+
+
+	detachEvent : function (el, evnt, func) {
+		if (el.removeEventListener) {
+			el.removeEventListener(evnt, func, false);
+		} else if (el.detachEvent) {
+			el.detachEvent('on' + evnt, func);
+		}
+	},
+
+
+	_attachedGroupEvents : {},
+
+
+	attachGroupEvent : function (groupName, el, evnt, func) {
+		if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) {
+			jsc._attachedGroupEvents[groupName] = [];
+		}
+		jsc._attachedGroupEvents[groupName].push([el, evnt, func]);
+		jsc.attachEvent(el, evnt, func);
+	},
+
+
+	detachGroupEvents : function (groupName) {
+		if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) {
+			for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1) {
+				var evt = jsc._attachedGroupEvents[groupName][i];
+				jsc.detachEvent(evt[0], evt[1], evt[2]);
+			}
+			delete jsc._attachedGroupEvents[groupName];
+		}
+	},
+
+
+	attachDOMReadyEvent : function (func) {
+		var fired = false;
+		var fireOnce = function () {
+			if (!fired) {
+				fired = true;
+				func();
+			}
+		};
+
+		if (document.readyState === 'complete') {
+			setTimeout(fireOnce, 1); // async
+			return;
+		}
+
+		if (document.addEventListener) {
+			document.addEventListener('DOMContentLoaded', fireOnce, false);
+
+			// Fallback
+			window.addEventListener('load', fireOnce, false);
+
+		} else if (document.attachEvent) {
+			// IE
+			document.attachEvent('onreadystatechange', function () {
+				if (document.readyState === 'complete') {
+					document.detachEvent('onreadystatechange', arguments.callee);
+					fireOnce();
+				}
+			})
+
+			// Fallback
+			window.attachEvent('onload', fireOnce);
+
+			// IE7/8
+			if (document.documentElement.doScroll && window == window.top) {
+				var tryScroll = function () {
+					if (!document.body) { return; }
+					try {
+						document.documentElement.doScroll('left');
+						fireOnce();
+					} catch (e) {
+						setTimeout(tryScroll, 1);
+					}
+				};
+				tryScroll();
+			}
+		}
+	},
+
+
+	warn : function (msg) {
+		if (window.console && window.console.warn) {
+			window.console.warn(msg);
+		}
+	},
+
+
+	preventDefault : function (e) {
+		if (e.preventDefault) { e.preventDefault(); }
+		e.returnValue = false;
+	},
+
+
+	captureTarget : function (target) {
+		// IE
+		if (target.setCapture) {
+			jsc._capturedTarget = target;
+			jsc._capturedTarget.setCapture();
+		}
+	},
+
+
+	releaseTarget : function () {
+		// IE
+		if (jsc._capturedTarget) {
+			jsc._capturedTarget.releaseCapture();
+			jsc._capturedTarget = null;
+		}
+	},
+
+
+	fireEvent : function (el, evnt) {
+		if (!el) {
+			return;
+		}
+		if (document.createEvent) {
+			var ev = document.createEvent('HTMLEvents');
+			ev.initEvent(evnt, true, true);
+			el.dispatchEvent(ev);
+		} else if (document.createEventObject) {
+			var ev = document.createEventObject();
+			el.fireEvent('on' + evnt, ev);
+		} else if (el['on' + evnt]) { // alternatively use the traditional event model
+			el['on' + evnt]();
+		}
+	},
+
+
+	classNameToList : function (className) {
+		return className.replace(/^\s+|\s+$/g, '').split(/\s+/);
+	},
+
+
+	// The className parameter (str) can only contain a single class name
+	hasClass : function (elm, className) {
+		if (!className) {
+			return false;
+		}
+		return -1 != (' ' + elm.className.replace(/\s+/g, ' ') + ' ').indexOf(' ' + className + ' ');
+	},
+
+
+	// The className parameter (str) can contain multiple class names separated by whitespace
+	setClass : function (elm, className) {
+		var classList = jsc.classNameToList(className);
+		for (var i = 0; i < classList.length; i += 1) {
+			if (!jsc.hasClass(elm, classList[i])) {
+				elm.className += (elm.className ? ' ' : '') + classList[i];
+			}
+		}
+	},
+
+
+	// The className parameter (str) can contain multiple class names separated by whitespace
+	unsetClass : function (elm, className) {
+		var classList = jsc.classNameToList(className);
+		for (var i = 0; i < classList.length; i += 1) {
+			var repl = new RegExp(
+				'^\\s*' + classList[i] + '\\s*|' +
+				'\\s*' + classList[i] + '\\s*$|' +
+				'\\s+' + classList[i] + '(\\s+)',
+				'g'
+			);
+			elm.className = elm.className.replace(repl, '$1');
+		}
+	},
+
+
+	getStyle : function (elm) {
+		return window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle;
+	},
+
+
+	setStyle : (function () {
+		var helper = document.createElement('div');
+		var getSupportedProp = function (names) {
+			for (var i = 0; i < names.length; i += 1) {
+				if (names[i] in helper.style) {
+					return names[i];
+				}
+			}
+		};
+		var props = {
+			borderRadius: getSupportedProp(['borderRadius', 'MozBorderRadius', 'webkitBorderRadius']),
+			boxShadow: getSupportedProp(['boxShadow', 'MozBoxShadow', 'webkitBoxShadow'])
+		};
+		return function (elm, prop, value) {
+			switch (prop.toLowerCase()) {
+			case 'opacity':
+				var alphaOpacity = Math.round(parseFloat(value) * 100);
+				elm.style.opacity = value;
+				elm.style.filter = 'alpha(opacity=' + alphaOpacity + ')';
+				break;
+			default:
+				elm.style[props[prop]] = value;
+				break;
+			}
+		};
+	})(),
+
+
+	setBorderRadius : function (elm, value) {
+		jsc.setStyle(elm, 'borderRadius', value || '0');
+	},
+
+
+	setBoxShadow : function (elm, value) {
+		jsc.setStyle(elm, 'boxShadow', value || 'none');
+	},
+
+
+	getElementPos : function (e, relativeToViewport) {
+		var x=0, y=0;
+		var rect = e.getBoundingClientRect();
+		x = rect.left;
+		y = rect.top;
+		if (!relativeToViewport) {
+			var viewPos = jsc.getViewPos();
+			x += viewPos[0];
+			y += viewPos[1];
+		}
+		return [x, y];
+	},
+
+
+	getElementSize : function (e) {
+		return [e.offsetWidth, e.offsetHeight];
+	},
+
+
+	// get pointer's X/Y coordinates relative to viewport
+	getAbsPointerPos : function (e) {
+		if (!e) { e = window.event; }
+		var x = 0, y = 0;
+		if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) {
+			// touch devices
+			x = e.changedTouches[0].clientX;
+			y = e.changedTouches[0].clientY;
+		} else if (typeof e.clientX === 'number') {
+			x = e.clientX;
+			y = e.clientY;
+		}
+		return { x: x, y: y };
+	},
+
+
+	// get pointer's X/Y coordinates relative to target element
+	getRelPointerPos : function (e) {
+		if (!e) { e = window.event; }
+		var target = e.target || e.srcElement;
+		var targetRect = target.getBoundingClientRect();
+
+		var x = 0, y = 0;
+
+		var clientX = 0, clientY = 0;
+		if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) {
+			// touch devices
+			clientX = e.changedTouches[0].clientX;
+			clientY = e.changedTouches[0].clientY;
+		} else if (typeof e.clientX === 'number') {
+			clientX = e.clientX;
+			clientY = e.clientY;
+		}
+
+		x = clientX - targetRect.left;
+		y = clientY - targetRect.top;
+		return { x: x, y: y };
+	},
+
+
+	getViewPos : function () {
+		var doc = document.documentElement;
+		return [
+			(window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0),
+			(window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0)
+		];
+	},
+
+
+	getViewSize : function () {
+		var doc = document.documentElement;
+		return [
+			(window.innerWidth || doc.clientWidth),
+			(window.innerHeight || doc.clientHeight),
+		];
+	},
+
+
+	redrawPosition : function () {
+
+		if (jsc.picker && jsc.picker.owner) {
+			var thisObj = jsc.picker.owner;
+
+			var tp, vp;
+
+			if (thisObj.fixed) {
+				// Fixed elements are positioned relative to viewport,
+				// therefore we can ignore the scroll offset
+				tp = jsc.getElementPos(thisObj.targetElement, true); // target pos
+				vp = [0, 0]; // view pos
+			} else {
+				tp = jsc.getElementPos(thisObj.targetElement); // target pos
+				vp = jsc.getViewPos(); // view pos
+			}
+
+			var ts = jsc.getElementSize(thisObj.targetElement); // target size
+			var vs = jsc.getViewSize(); // view size
+			var ps = jsc.getPickerOuterDims(thisObj); // picker size
+			var a, b, c;
+			switch (thisObj.position.toLowerCase()) {
+				case 'left': a=1; b=0; c=-1; break;
+				case 'right':a=1; b=0; c=1; break;
+				case 'top':  a=0; b=1; c=-1; break;
+				default:     a=0; b=1; c=1; break;
+			}
+			var l = (ts[b]+ps[b])/2;
+
+			// compute picker position
+			if (!thisObj.smartPosition) {
+				var pp = [
+					tp[a],
+					tp[b]+ts[b]-l+l*c
+				];
+			} else {
+				var pp = [
+					-vp[a]+tp[a]+ps[a] > vs[a] ?
+						(-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) :
+						tp[a],
+					-vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ?
+						(-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) :
+						(tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c)
+				];
+			}
+
+			var x = pp[a];
+			var y = pp[b];
+			var positionValue = thisObj.fixed ? 'fixed' : 'absolute';
+			var contractShadow =
+				(pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) &&
+				(pp[1] + ps[1] < tp[1] + ts[1]);
+
+			jsc._drawPosition(thisObj, x, y, positionValue, contractShadow);
+		}
+	},
+
+
+	_drawPosition : function (thisObj, x, y, positionValue, contractShadow) {
+		var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px
+
+		jsc.picker.wrap.style.position = positionValue;
+		jsc.picker.wrap.style.left = x + 'px';
+		jsc.picker.wrap.style.top = y + 'px';
+
+		jsc.setBoxShadow(
+			jsc.picker.boxS,
+			thisObj.shadow ?
+				new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) :
+				null);
+	},
+
+
+	getPickerDims : function (thisObj) {
+		var displaySlider = !!jsc.getSliderComponent(thisObj);
+		var dims = [
+			2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.width +
+				(displaySlider ? 2 * thisObj.insetWidth + jsc.getPadToSliderPadding(thisObj) + thisObj.sliderSize : 0),
+			2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.height +
+				(thisObj.closable ? 2 * thisObj.insetWidth + thisObj.padding + thisObj.buttonHeight : 0)
+		];
+		return dims;
+	},
+
+
+	getPickerOuterDims : function (thisObj) {
+		var dims = jsc.getPickerDims(thisObj);
+		return [
+			dims[0] + 2 * thisObj.borderWidth,
+			dims[1] + 2 * thisObj.borderWidth
+		];
+	},
+
+
+	getPadToSliderPadding : function (thisObj) {
+		return Math.max(thisObj.padding, 1.5 * (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness));
+	},
+
+
+	getPadYComponent : function (thisObj) {
+		switch (thisObj.mode.charAt(1).toLowerCase()) {
+			case 'v': return 'v'; break;
+		}
+		return 's';
+	},
+
+
+	getSliderComponent : function (thisObj) {
+		if (thisObj.mode.length > 2) {
+			switch (thisObj.mode.charAt(2).toLowerCase()) {
+				case 's': return 's'; break;
+				case 'v': return 'v'; break;
+			}
+		}
+		return null;
+	},
+
+
+	onDocumentMouseDown : function (e) {
+		if (!e) { e = window.event; }
+		var target = e.target || e.srcElement;
+
+		if (target._jscLinkedInstance) {
+			if (target._jscLinkedInstance.showOnClick) {
+				target._jscLinkedInstance.show();
+			}
+		} else if (target._jscControlName) {
+			jsc.onControlPointerStart(e, target, target._jscControlName, 'mouse');
+		} else {
+			// Mouse is outside the picker controls -> hide the color picker!
+			if (jsc.picker && jsc.picker.owner) {
+				jsc.picker.owner.hide();
+			}
+		}
+	},
+
+
+	onDocumentTouchStart : function (e) {
+		if (!e) { e = window.event; }
+		var target = e.target || e.srcElement;
+
+		if (target._jscLinkedInstance) {
+			if (target._jscLinkedInstance.showOnClick) {
+				target._jscLinkedInstance.show();
+			}
+		} else if (target._jscControlName) {
+			jsc.onControlPointerStart(e, target, target._jscControlName, 'touch');
+		} else {
+			if (jsc.picker && jsc.picker.owner) {
+				jsc.picker.owner.hide();
+			}
+		}
+	},
+
+
+	onWindowResize : function (e) {
+		jsc.redrawPosition();
+	},
+
+
+	onParentScroll : function (e) {
+		// hide the picker when one of the parent elements is scrolled
+		if (jsc.picker && jsc.picker.owner) {
+			jsc.picker.owner.hide();
+		}
+	},
+
+
+	_pointerMoveEvent : {
+		mouse: 'mousemove',
+		touch: 'touchmove'
+	},
+	_pointerEndEvent : {
+		mouse: 'mouseup',
+		touch: 'touchend'
+	},
+
+
+	_pointerOrigin : null,
+	_capturedTarget : null,
+
+
+	onControlPointerStart : function (e, target, controlName, pointerType) {
+		var thisObj = target._jscInstance;
+
+		jsc.preventDefault(e);
+		jsc.captureTarget(target);
+
+		var registerDragEvents = function (doc, offset) {
+			jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType],
+				jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset));
+			jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType],
+				jsc.onDocumentPointerEnd(e, target, controlName, pointerType));
+		};
+
+		registerDragEvents(document, [0, 0]);
+
+		if (window.parent && window.frameElement) {
+			var rect = window.frameElement.getBoundingClientRect();
+			var ofs = [-rect.left, -rect.top];
+			registerDragEvents(window.parent.window.document, ofs);
+		}
+
+		var abs = jsc.getAbsPointerPos(e);
+		var rel = jsc.getRelPointerPos(e);
+		jsc._pointerOrigin = {
+			x: abs.x - rel.x,
+			y: abs.y - rel.y
+		};
+
+		switch (controlName) {
+		case 'pad':
+			// if the slider is at the bottom, move it up
+			switch (jsc.getSliderComponent(thisObj)) {
+			case 's': if (thisObj.hsv[1] === 0) { thisObj.fromHSV(null, 100, null); }; break;
+			case 'v': if (thisObj.hsv[2] === 0) { thisObj.fromHSV(null, null, 100); }; break;
+			}
+			jsc.setPad(thisObj, e, 0, 0);
+			break;
+
+		case 'sld':
+			jsc.setSld(thisObj, e, 0);
+			break;
+		}
+
+		jsc.dispatchFineChange(thisObj);
+	},
+
+
+	onDocumentPointerMove : function (e, target, controlName, pointerType, offset) {
+		return function (e) {
+			var thisObj = target._jscInstance;
+			switch (controlName) {
+			case 'pad':
+				if (!e) { e = window.event; }
+				jsc.setPad(thisObj, e, offset[0], offset[1]);
+				jsc.dispatchFineChange(thisObj);
+				break;
+
+			case 'sld':
+				if (!e) { e = window.event; }
+				jsc.setSld(thisObj, e, offset[1]);
+				jsc.dispatchFineChange(thisObj);
+				break;
+			}
+		}
+	},
+
+
+	onDocumentPointerEnd : function (e, target, controlName, pointerType) {
+		return function (e) {
+			var thisObj = target._jscInstance;
+			jsc.detachGroupEvents('drag');
+			jsc.releaseTarget();
+			// Always dispatch changes after detaching outstanding mouse handlers,
+			// in case some user interaction will occur in user's onchange callback
+			// that would intrude with current mouse events
+			jsc.dispatchChange(thisObj);
+		};
+	},
+
+
+	dispatchChange : function (thisObj) {
+		if (thisObj.valueElement) {
+			if (jsc.isElementType(thisObj.valueElement, 'input')) {
+				jsc.fireEvent(thisObj.valueElement, 'change');
+			}
+		}
+	},
+
+
+	dispatchFineChange : function (thisObj) {
+		if (thisObj.onFineChange) {
+			var callback;
+			if (typeof thisObj.onFineChange === 'string') {
+				callback = new Function (thisObj.onFineChange);
+			} else {
+				callback = thisObj.onFineChange;
+			}
+			callback.call(thisObj);
+		}
+	},
+
+
+	setPad : function (thisObj, e, ofsX, ofsY) {
+		var pointerAbs = jsc.getAbsPointerPos(e);
+		var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.insetWidth;
+		var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth;
+
+		var xVal = x * (360 / (thisObj.width - 1));
+		var yVal = 100 - (y * (100 / (thisObj.height - 1)));
+
+		switch (jsc.getPadYComponent(thisObj)) {
+		case 's': thisObj.fromHSV(xVal, yVal, null, jsc.leaveSld); break;
+		case 'v': thisObj.fromHSV(xVal, null, yVal, jsc.leaveSld); break;
+		}
+	},
+
+
+	setSld : function (thisObj, e, ofsY) {
+		var pointerAbs = jsc.getAbsPointerPos(e);
+		var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth;
+
+		var yVal = 100 - (y * (100 / (thisObj.height - 1)));
+
+		switch (jsc.getSliderComponent(thisObj)) {
+		case 's': thisObj.fromHSV(null, yVal, null, jsc.leavePad); break;
+		case 'v': thisObj.fromHSV(null, null, yVal, jsc.leavePad); break;
+		}
+	},
+
+
+	_vmlNS : 'jsc_vml_',
+	_vmlCSS : 'jsc_vml_css_',
+	_vmlReady : false,
+
+
+	initVML : function () {
+		if (!jsc._vmlReady) {
+			// init VML namespace
+			var doc = document;
+			if (!doc.namespaces[jsc._vmlNS]) {
+				doc.namespaces.add(jsc._vmlNS, 'urn:schemas-microsoft-com:vml');
+			}
+			if (!doc.styleSheets[jsc._vmlCSS]) {
+				var tags = ['shape', 'shapetype', 'group', 'background', 'path', 'formulas', 'handles', 'fill', 'stroke', 'shadow', 'textbox', 'textpath', 'imagedata', 'line', 'polyline', 'curve', 'rect', 'roundrect', 'oval', 'arc', 'image'];
+				var ss = doc.createStyleSheet();
+				ss.owningElement.id = jsc._vmlCSS;
+				for (var i = 0; i < tags.length; i += 1) {
+					ss.addRule(jsc._vmlNS + '\\:' + tags[i], 'behavior:url(#default#VML);');
+				}
+			}
+			jsc._vmlReady = true;
+		}
+	},
+
+
+	createPalette : function () {
+
+		var paletteObj = {
+			elm: null,
+			draw: null
+		};
+
+		if (jsc.isCanvasSupported) {
+			// Canvas implementation for modern browsers
+
+			var canvas = document.createElement('canvas');
+			var ctx = canvas.getContext('2d');
+
+			var drawFunc = function (width, height, type) {
+				canvas.width = width;
+				canvas.height = height;
+
+				ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+				var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0);
+				hGrad.addColorStop(0 / 6, '#F00');
+				hGrad.addColorStop(1 / 6, '#FF0');
+				hGrad.addColorStop(2 / 6, '#0F0');
+				hGrad.addColorStop(3 / 6, '#0FF');
+				hGrad.addColorStop(4 / 6, '#00F');
+				hGrad.addColorStop(5 / 6, '#F0F');
+				hGrad.addColorStop(6 / 6, '#F00');
+
+				ctx.fillStyle = hGrad;
+				ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+				var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height);
+				switch (type.toLowerCase()) {
+				case 's':
+					vGrad.addColorStop(0, 'rgba(255,255,255,0)');
+					vGrad.addColorStop(1, 'rgba(255,255,255,1)');
+					break;
+				case 'v':
+					vGrad.addColorStop(0, 'rgba(0,0,0,0)');
+					vGrad.addColorStop(1, 'rgba(0,0,0,1)');
+					break;
+				}
+				ctx.fillStyle = vGrad;
+				ctx.fillRect(0, 0, canvas.width, canvas.height);
+			};
+
+			paletteObj.elm = canvas;
+			paletteObj.draw = drawFunc;
+
+		} else {
+			// VML fallback for IE 7 and 8
+
+			jsc.initVML();
+
+			var vmlContainer = document.createElement('div');
+			vmlContainer.style.position = 'relative';
+			vmlContainer.style.overflow = 'hidden';
+
+			var hGrad = document.createElement(jsc._vmlNS + ':fill');
+			hGrad.type = 'gradient';
+			hGrad.method = 'linear';
+			hGrad.angle = '90';
+			hGrad.colors = '16.67% #F0F, 33.33% #00F, 50% #0FF, 66.67% #0F0, 83.33% #FF0'
+
+			var hRect = document.createElement(jsc._vmlNS + ':rect');
+			hRect.style.position = 'absolute';
+			hRect.style.left = -1 + 'px';
+			hRect.style.top = -1 + 'px';
+			hRect.stroked = false;
+			hRect.appendChild(hGrad);
+			vmlContainer.appendChild(hRect);
+
+			var vGrad = document.createElement(jsc._vmlNS + ':fill');
+			vGrad.type = 'gradient';
+			vGrad.method = 'linear';
+			vGrad.angle = '180';
+			vGrad.opacity = '0';
+
+			var vRect = document.createElement(jsc._vmlNS + ':rect');
+			vRect.style.position = 'absolute';
+			vRect.style.left = -1 + 'px';
+			vRect.style.top = -1 + 'px';
+			vRect.stroked = false;
+			vRect.appendChild(vGrad);
+			vmlContainer.appendChild(vRect);
+
+			var drawFunc = function (width, height, type) {
+				vmlContainer.style.width = width + 'px';
+				vmlContainer.style.height = height + 'px';
+
+				hRect.style.width =
+				vRect.style.width =
+					(width + 1) + 'px';
+				hRect.style.height =
+				vRect.style.height =
+					(height + 1) + 'px';
+
+				// Colors must be specified during every redraw, otherwise IE won't display
+				// a full gradient during a subsequential redraw
+				hGrad.color = '#F00';
+				hGrad.color2 = '#F00';
+
+				switch (type.toLowerCase()) {
+				case 's':
+					vGrad.color = vGrad.color2 = '#FFF';
+					break;
+				case 'v':
+					vGrad.color = vGrad.color2 = '#000';
+					break;
+				}
+			};
+			
+			paletteObj.elm = vmlContainer;
+			paletteObj.draw = drawFunc;
+		}
+
+		return paletteObj;
+	},
+
+
+	createSliderGradient : function () {
+
+		var sliderObj = {
+			elm: null,
+			draw: null
+		};
+
+		if (jsc.isCanvasSupported) {
+			// Canvas implementation for modern browsers
+
+			var canvas = document.createElement('canvas');
+			var ctx = canvas.getContext('2d');
+
+			var drawFunc = function (width, height, color1, color2) {
+				canvas.width = width;
+				canvas.height = height;
+
+				ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+				var grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
+				grad.addColorStop(0, color1);
+				grad.addColorStop(1, color2);
+
+				ctx.fillStyle = grad;
+				ctx.fillRect(0, 0, canvas.width, canvas.height);
+			};
+
+			sliderObj.elm = canvas;
+			sliderObj.draw = drawFunc;
+
+		} else {
+			// VML fallback for IE 7 and 8
+
+			jsc.initVML();
+
+			var vmlContainer = document.createElement('div');
+			vmlContainer.style.position = 'relative';
+			vmlContainer.style.overflow = 'hidden';
+
+			var grad = document.createElement(jsc._vmlNS + ':fill');
+			grad.type = 'gradient';
+			grad.method = 'linear';
+			grad.angle = '180';
+
+			var rect = document.createElement(jsc._vmlNS + ':rect');
+			rect.style.position = 'absolute';
+			rect.style.left = -1 + 'px';
+			rect.style.top = -1 + 'px';
+			rect.stroked = false;
+			rect.appendChild(grad);
+			vmlContainer.appendChild(rect);
+
+			var drawFunc = function (width, height, color1, color2) {
+				vmlContainer.style.width = width + 'px';
+				vmlContainer.style.height = height + 'px';
+
+				rect.style.width = (width + 1) + 'px';
+				rect.style.height = (height + 1) + 'px';
+
+				grad.color = color1;
+				grad.color2 = color2;
+			};
+			
+			sliderObj.elm = vmlContainer;
+			sliderObj.draw = drawFunc;
+		}
+
+		return sliderObj;
+	},
+
+
+	leaveValue : 1<<0,
+	leaveStyle : 1<<1,
+	leavePad : 1<<2,
+	leaveSld : 1<<3,
+
+
+	BoxShadow : (function () {
+		var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) {
+			this.hShadow = hShadow;
+			this.vShadow = vShadow;
+			this.blur = blur;
+			this.spread = spread;
+			this.color = color;
+			this.inset = !!inset;
+		};
+
+		BoxShadow.prototype.toString = function () {
+			var vals = [
+				Math.round(this.hShadow) + 'px',
+				Math.round(this.vShadow) + 'px',
+				Math.round(this.blur) + 'px',
+				Math.round(this.spread) + 'px',
+				this.color
+			];
+			if (this.inset) {
+				vals.push('inset');
+			}
+			return vals.join(' ');
+		};
+
+		return BoxShadow;
+	})(),
+
+
+	//
+	// Usage:
+	// var myColor = new jscolor(<targetElement> [, <options>])
+	//
+
+	jscolor : function (targetElement, options) {
+
+		// General options
+		//
+		this.value = null; // initial HEX color. To change it later, use methods fromString(), fromHSV() and fromRGB()
+		this.valueElement = targetElement; // element that will be used to display and input the color code
+		this.styleElement = targetElement; // element that will preview the picked color using CSS backgroundColor
+		this.required = true; // whether the associated text <input> can be left empty
+		this.refine = true; // whether to refine the entered color code (e.g. uppercase it and remove whitespace)
+		this.hash = false; // whether to prefix the HEX color code with # symbol
+		this.uppercase = true; // whether to show the color code in upper case
+		this.onFineChange = null; // called instantly every time the color changes (value can be either a function or a string with javascript code)
+		this.activeClass = 'jscolor-active'; // class to be set to the target element when a picker window is open on it
+		this.overwriteImportant = false; // whether to overwrite colors of styleElement using !important
+		this.minS = 0; // min allowed saturation (0 - 100)
+		this.maxS = 100; // max allowed saturation (0 - 100)
+		this.minV = 0; // min allowed value (brightness) (0 - 100)
+		this.maxV = 100; // max allowed value (brightness) (0 - 100)
+
+		// Accessing the picked color
+		//
+		this.hsv = [0, 0, 100]; // read-only  [0-360, 0-100, 0-100]
+		this.rgb = [255, 255, 255]; // read-only  [0-255, 0-255, 0-255]
+
+		// Color Picker options
+		//
+		this.width = 181; // width of color palette (in px)
+		this.height = 101; // height of color palette (in px)
+		this.showOnClick = true; // whether to display the color picker when user clicks on its target element
+		this.mode = 'HSV'; // HSV | HVS | HS | HV - layout of the color picker controls
+		this.position = 'bottom'; // left | right | top | bottom - position relative to the target element
+		this.smartPosition = true; // automatically change picker position when there is not enough space for it
+		this.sliderSize = 16; // px
+		this.crossSize = 8; // px
+		this.closable = false; // whether to display the Close button
+		this.closeText = 'Close';
+		this.buttonColor = '#000000'; // CSS color
+		this.buttonHeight = 18; // px
+		this.padding = 12; // px
+		this.backgroundColor = '#FFFFFF'; // CSS color
+		this.borderWidth = 1; // px
+		this.borderColor = '#BBBBBB'; // CSS color
+		this.borderRadius = 8; // px
+		this.insetWidth = 1; // px
+		this.insetColor = '#BBBBBB'; // CSS color
+		this.shadow = true; // whether to display shadow
+		this.shadowBlur = 15; // px
+		this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color
+		this.pointerColor = '#4C4C4C'; // px
+		this.pointerBorderColor = '#FFFFFF'; // px
+        this.pointerBorderWidth = 1; // px
+        this.pointerThickness = 2; // px
+		this.zIndex = 1000;
+		this.container = null; // where to append the color picker (BODY element by default)
+
+
+		for (var opt in options) {
+			if (options.hasOwnProperty(opt)) {
+				this[opt] = options[opt];
+			}
+		}
+
+
+		this.hide = function () {
+			if (isPickerOwner()) {
+				detachPicker();
+			}
+		};
+
+
+		this.show = function () {
+			drawPicker();
+		};
+
+
+		this.redraw = function () {
+			if (isPickerOwner()) {
+				drawPicker();
+			}
+		};
+
+
+		this.importColor = function () {
+			if (!this.valueElement) {
+				this.exportColor();
+			} else {
+				if (jsc.isElementType(this.valueElement, 'input')) {
+					if (!this.refine) {
+						if (!this.fromString(this.valueElement.value, jsc.leaveValue)) {
+							if (this.styleElement) {
+								this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage;
+								this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor;
+								this.styleElement.style.color = this.styleElement._jscOrigStyle.color;
+							}
+							this.exportColor(jsc.leaveValue | jsc.leaveStyle);
+						}
+					} else if (!this.required && /^\s*$/.test(this.valueElement.value)) {
+						this.valueElement.value = '';
+						if (this.styleElement) {
+							this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage;
+							this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor;
+							this.styleElement.style.color = this.styleElement._jscOrigStyle.color;
+						}
+						this.exportColor(jsc.leaveValue | jsc.leaveStyle);
+
+					} else if (this.fromString(this.valueElement.value)) {
+						// managed to import color successfully from the value -> OK, don't do anything
+					} else {
+						this.exportColor();
+					}
+				} else {
+					// not an input element -> doesn't have any value
+					this.exportColor();
+				}
+			}
+		};
+
+
+		this.exportColor = function (flags) {
+			if (!(flags & jsc.leaveValue) && this.valueElement) {
+				var value = this.toString();
+				if (this.uppercase) { value = value.toUpperCase(); }
+				if (this.hash) { value = '#' + value; }
+
+				if (jsc.isElementType(this.valueElement, 'input')) {
+					this.valueElement.value = value;
+				} else {
+					this.valueElement.innerHTML = value;
+				}
+			}
+			if (!(flags & jsc.leaveStyle)) {
+				if (this.styleElement) {
+					var bgColor = '#' + this.toString();
+					var fgColor = this.isLight() ? '#000' : '#FFF';
+
+					this.styleElement.style.backgroundImage = 'none';
+					this.styleElement.style.backgroundColor = bgColor;
+					this.styleElement.style.color = fgColor;
+
+					if (this.overwriteImportant) {
+						this.styleElement.setAttribute('style',
+							'background: ' + bgColor + ' !important; ' +
+							'color: ' + fgColor + ' !important;'
+						);
+					}
+				}
+			}
+			if (!(flags & jsc.leavePad) && isPickerOwner()) {
+				redrawPad();
+			}
+			if (!(flags & jsc.leaveSld) && isPickerOwner()) {
+				redrawSld();
+			}
+		};
+
+
+		// h: 0-360
+		// s: 0-100
+		// v: 0-100
+		//
+		this.fromHSV = function (h, s, v, flags) { // null = don't change
+			if (h !== null) {
+				if (isNaN(h)) { return false; }
+				h = Math.max(0, Math.min(360, h));
+			}
+			if (s !== null) {
+				if (isNaN(s)) { return false; }
+				s = Math.max(0, Math.min(100, this.maxS, s), this.minS);
+			}
+			if (v !== null) {
+				if (isNaN(v)) { return false; }
+				v = Math.max(0, Math.min(100, this.maxV, v), this.minV);
+			}
+
+			this.rgb = HSV_RGB(
+				h===null ? this.hsv[0] : (this.hsv[0]=h),
+				s===null ? this.hsv[1] : (this.hsv[1]=s),
+				v===null ? this.hsv[2] : (this.hsv[2]=v)
+			);
+
+			this.exportColor(flags);
+		};
+
+
+		// r: 0-255
+		// g: 0-255
+		// b: 0-255
+		//
+		this.fromRGB = function (r, g, b, flags) { // null = don't change
+			if (r !== null) {
+				if (isNaN(r)) { return false; }
+				r = Math.max(0, Math.min(255, r));
+			}
+			if (g !== null) {
+				if (isNaN(g)) { return false; }
+				g = Math.max(0, Math.min(255, g));
+			}
+			if (b !== null) {
+				if (isNaN(b)) { return false; }
+				b = Math.max(0, Math.min(255, b));
+			}
+
+			var hsv = RGB_HSV(
+				r===null ? this.rgb[0] : r,
+				g===null ? this.rgb[1] : g,
+				b===null ? this.rgb[2] : b
+			);
+			if (hsv[0] !== null) {
+				this.hsv[0] = Math.max(0, Math.min(360, hsv[0]));
+			}
+			if (hsv[2] !== 0) {
+				this.hsv[1] = hsv[1]===null ? null : Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1]));
+			}
+			this.hsv[2] = hsv[2]===null ? null : Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2]));
+
+			// update RGB according to final HSV, as some values might be trimmed
+			var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]);
+			this.rgb[0] = rgb[0];
+			this.rgb[1] = rgb[1];
+			this.rgb[2] = rgb[2];
+
+			this.exportColor(flags);
+		};
+
+
+		this.fromString = function (str, flags) {
+			var m;
+			if (m = str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)) {
+				// HEX notation
+				//
+
+				if (m[1].length === 6) {
+					// 6-char notation
+					this.fromRGB(
+						parseInt(m[1].substr(0,2),16),
+						parseInt(m[1].substr(2,2),16),
+						parseInt(m[1].substr(4,2),16),
+						flags
+					);
+				} else {
+					// 3-char notation
+					this.fromRGB(
+						parseInt(m[1].charAt(0) + m[1].charAt(0),16),
+						parseInt(m[1].charAt(1) + m[1].charAt(1),16),
+						parseInt(m[1].charAt(2) + m[1].charAt(2),16),
+						flags
+					);
+				}
+				return true;
+
+			} else if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) {
+				var params = m[1].split(',');
+				var re = /^\s*(\d*)(\.\d+)?\s*$/;
+				var mR, mG, mB;
+				if (
+					params.length >= 3 &&
+					(mR = params[0].match(re)) &&
+					(mG = params[1].match(re)) &&
+					(mB = params[2].match(re))
+				) {
+					var r = parseFloat((mR[1] || '0') + (mR[2] || ''));
+					var g = parseFloat((mG[1] || '0') + (mG[2] || ''));
+					var b = parseFloat((mB[1] || '0') + (mB[2] || ''));
+					this.fromRGB(r, g, b, flags);
+					return true;
+				}
+			}
+			return false;
+		};
+
+
+		this.toString = function () {
+			return (
+				(0x100 | Math.round(this.rgb[0])).toString(16).substr(1) +
+				(0x100 | Math.round(this.rgb[1])).toString(16).substr(1) +
+				(0x100 | Math.round(this.rgb[2])).toString(16).substr(1)
+			);
+		};
+
+
+		this.toHEXString = function () {
+			return '#' + this.toString().toUpperCase();
+		};
+
+
+		this.toRGBString = function () {
+			return ('rgb(' +
+				Math.round(this.rgb[0]) + ',' +
+				Math.round(this.rgb[1]) + ',' +
+				Math.round(this.rgb[2]) + ')'
+			);
+		};
+
+
+		this.isLight = function () {
+			return (
+				0.213 * this.rgb[0] +
+				0.715 * this.rgb[1] +
+				0.072 * this.rgb[2] >
+				255 / 2
+			);
+		};
+
+
+		this._processParentElementsInDOM = function () {
+			if (this._linkedElementsProcessed) { return; }
+			this._linkedElementsProcessed = true;
+
+			var elm = this.targetElement;
+			do {
+				// If the target element or one of its parent nodes has fixed position,
+				// then use fixed positioning instead
+				//
+				// Note: In Firefox, getComputedStyle returns null in a hidden iframe,
+				// that's why we need to check if the returned style object is non-empty
+				var currStyle = jsc.getStyle(elm);
+				if (currStyle && currStyle.position.toLowerCase() === 'fixed') {
+					this.fixed = true;
+				}
+
+				if (elm !== this.targetElement) {
+					// Ensure to attach onParentScroll only once to each parent element
+					// (multiple targetElements can share the same parent nodes)
+					//
+					// Note: It's not just offsetParents that can be scrollable,
+					// that's why we loop through all parent nodes
+					if (!elm._jscEventsAttached) {
+						jsc.attachEvent(elm, 'scroll', jsc.onParentScroll);
+						elm._jscEventsAttached = true;
+					}
+				}
+			} while ((elm = elm.parentNode) && !jsc.isElementType(elm, 'body'));
+		};
+
+
+		// r: 0-255
+		// g: 0-255
+		// b: 0-255
+		//
+		// returns: [ 0-360, 0-100, 0-100 ]
+		//
+		function RGB_HSV (r, g, b) {
+			r /= 255;
+			g /= 255;
+			b /= 255;
+			var n = Math.min(Math.min(r,g),b);
+			var v = Math.max(Math.max(r,g),b);
+			var m = v - n;
+			if (m === 0) { return [ null, 0, 100 * v ]; }
+			var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m);
+			return [
+				60 * (h===6?0:h),
+				100 * (m/v),
+				100 * v
+			];
+		}
+
+
+		// h: 0-360
+		// s: 0-100
+		// v: 0-100
+		//
+		// returns: [ 0-255, 0-255, 0-255 ]
+		//
+		function HSV_RGB (h, s, v) {
+			var u = 255 * (v / 100);
+
+			if (h === null) {
+				return [ u, u, u ];
+			}
+
+			h /= 60;
+			s /= 100;
+
+			var i = Math.floor(h);
+			var f = i%2 ? h-i : 1-(h-i);
+			var m = u * (1 - s);
+			var n = u * (1 - s * f);
+			switch (i) {
+				case 6:
+				case 0: return [u,n,m];
+				case 1: return [n,u,m];
+				case 2: return [m,u,n];
+				case 3: return [m,n,u];
+				case 4: return [n,m,u];
+				case 5: return [u,m,n];
+			}
+		}
+
+
+		function detachPicker () {
+			jsc.unsetClass(THIS.targetElement, THIS.activeClass);
+			jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap);
+			delete jsc.picker.owner;
+		}
+
+
+		function drawPicker () {
+
+			// At this point, when drawing the picker, we know what the parent elements are
+			// and we can do all related DOM operations, such as registering events on them
+			// or checking their positioning
+			THIS._processParentElementsInDOM();
+
+			if (!jsc.picker) {
+				jsc.picker = {
+					owner: null,
+					wrap : document.createElement('div'),
+					box : document.createElement('div'),
+					boxS : document.createElement('div'), // shadow area
+					boxB : document.createElement('div'), // border
+					pad : document.createElement('div'),
+					padB : document.createElement('div'), // border
+					padM : document.createElement('div'), // mouse/touch area
+					padPal : jsc.createPalette(),
+					cross : document.createElement('div'),
+					crossBY : document.createElement('div'), // border Y
+					crossBX : document.createElement('div'), // border X
+					crossLY : document.createElement('div'), // line Y
+					crossLX : document.createElement('div'), // line X
+					sld : document.createElement('div'),
+					sldB : document.createElement('div'), // border
+					sldM : document.createElement('div'), // mouse/touch area
+					sldGrad : jsc.createSliderGradient(),
+					sldPtrS : document.createElement('div'), // slider pointer spacer
+					sldPtrIB : document.createElement('div'), // slider pointer inner border
+					sldPtrMB : document.createElement('div'), // slider pointer middle border
+					sldPtrOB : document.createElement('div'), // slider pointer outer border
+					btn : document.createElement('div'),
+					btnT : document.createElement('span') // text
+				};
+
+				jsc.picker.pad.appendChild(jsc.picker.padPal.elm);
+				jsc.picker.padB.appendChild(jsc.picker.pad);
+				jsc.picker.cross.appendChild(jsc.picker.crossBY);
+				jsc.picker.cross.appendChild(jsc.picker.crossBX);
+				jsc.picker.cross.appendChild(jsc.picker.crossLY);
+				jsc.picker.cross.appendChild(jsc.picker.crossLX);
+				jsc.picker.padB.appendChild(jsc.picker.cross);
+				jsc.picker.box.appendChild(jsc.picker.padB);
+				jsc.picker.box.appendChild(jsc.picker.padM);
+
+				jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm);
+				jsc.picker.sldB.appendChild(jsc.picker.sld);
+				jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB);
+				jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB);
+				jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB);
+				jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS);
+				jsc.picker.box.appendChild(jsc.picker.sldB);
+				jsc.picker.box.appendChild(jsc.picker.sldM);
+
+				jsc.picker.btn.appendChild(jsc.picker.btnT);
+				jsc.picker.box.appendChild(jsc.picker.btn);
+
+				jsc.picker.boxB.appendChild(jsc.picker.box);
+				jsc.picker.wrap.appendChild(jsc.picker.boxS);
+				jsc.picker.wrap.appendChild(jsc.picker.boxB);
+			}
+
+			var p = jsc.picker;
+
+			var displaySlider = !!jsc.getSliderComponent(THIS);
+			var dims = jsc.getPickerDims(THIS);
+			var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize);
+			var padToSliderPadding = jsc.getPadToSliderPadding(THIS);
+			var borderRadius = Math.min(
+				THIS.borderRadius,
+				Math.round(THIS.padding * Math.PI)); // px
+			var padCursor = 'crosshair';
+
+			// wrap
+			p.wrap.style.clear = 'both';
+			p.wrap.style.width = (dims[0] + 2 * THIS.borderWidth) + 'px';
+			p.wrap.style.height = (dims[1] + 2 * THIS.borderWidth) + 'px';
+			p.wrap.style.zIndex = THIS.zIndex;
+
+			// picker
+			p.box.style.width = dims[0] + 'px';
+			p.box.style.height = dims[1] + 'px';
+
+			p.boxS.style.position = 'absolute';
+			p.boxS.style.left = '0';
+			p.boxS.style.top = '0';
+			p.boxS.style.width = '100%';
+			p.boxS.style.height = '100%';
+			jsc.setBorderRadius(p.boxS, borderRadius + 'px');
+
+			// picker border
+			p.boxB.style.position = 'relative';
+			p.boxB.style.border = THIS.borderWidth + 'px solid';
+			p.boxB.style.borderColor = THIS.borderColor;
+			p.boxB.style.background = THIS.backgroundColor;
+			jsc.setBorderRadius(p.boxB, borderRadius + 'px');
+
+			// IE hack:
+			// If the element is transparent, IE will trigger the event on the elements under it,
+			// e.g. on Canvas or on elements with border
+			p.padM.style.background =
+			p.sldM.style.background =
+				'#FFF';
+			jsc.setStyle(p.padM, 'opacity', '0');
+			jsc.setStyle(p.sldM, 'opacity', '0');
+
+			// pad
+			p.pad.style.position = 'relative';
+			p.pad.style.width = THIS.width + 'px';
+			p.pad.style.height = THIS.height + 'px';
+
+			// pad palettes (HSV and HVS)
+			p.padPal.draw(THIS.width, THIS.height, jsc.getPadYComponent(THIS));
+
+			// pad border
+			p.padB.style.position = 'absolute';
+			p.padB.style.left = THIS.padding + 'px';
+			p.padB.style.top = THIS.padding + 'px';
+			p.padB.style.border = THIS.insetWidth + 'px solid';
+			p.padB.style.borderColor = THIS.insetColor;
+
+			// pad mouse area
+			p.padM._jscInstance = THIS;
+			p.padM._jscControlName = 'pad';
+			p.padM.style.position = 'absolute';
+			p.padM.style.left = '0';
+			p.padM.style.top = '0';
+			p.padM.style.width = (THIS.padding + 2 * THIS.insetWidth + THIS.width + padToSliderPadding / 2) + 'px';
+			p.padM.style.height = dims[1] + 'px';
+			p.padM.style.cursor = padCursor;
+
+			// pad cross
+			p.cross.style.position = 'absolute';
+			p.cross.style.left =
+			p.cross.style.top =
+				'0';
+			p.cross.style.width =
+			p.cross.style.height =
+				crossOuterSize + 'px';
+
+			// pad cross border Y and X
+			p.crossBY.style.position =
+			p.crossBX.style.position =
+				'absolute';
+			p.crossBY.style.background =
+			p.crossBX.style.background =
+				THIS.pointerBorderColor;
+			p.crossBY.style.width =
+			p.crossBX.style.height =
+				(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px';
+			p.crossBY.style.height =
+			p.crossBX.style.width =
+				crossOuterSize + 'px';
+			p.crossBY.style.left =
+			p.crossBX.style.top =
+				(Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px';
+			p.crossBY.style.top =
+			p.crossBX.style.left =
+				'0';
+
+			// pad cross line Y and X
+			p.crossLY.style.position =
+			p.crossLX.style.position =
+				'absolute';
+			p.crossLY.style.background =
+			p.crossLX.style.background =
+				THIS.pointerColor;
+			p.crossLY.style.height =
+			p.crossLX.style.width =
+				(crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px';
+			p.crossLY.style.width =
+			p.crossLX.style.height =
+				THIS.pointerThickness + 'px';
+			p.crossLY.style.left =
+			p.crossLX.style.top =
+				(Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px';
+			p.crossLY.style.top =
+			p.crossLX.style.left =
+				THIS.pointerBorderWidth + 'px';
+
+			// slider
+			p.sld.style.overflow = 'hidden';
+			p.sld.style.width = THIS.sliderSize + 'px';
+			p.sld.style.height = THIS.height + 'px';
+
+			// slider gradient
+			p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000');
+
+			// slider border
+			p.sldB.style.display = displaySlider ? 'block' : 'none';
+			p.sldB.style.position = 'absolute';
+			p.sldB.style.right = THIS.padding + 'px';
+			p.sldB.style.top = THIS.padding + 'px';
+			p.sldB.style.border = THIS.insetWidth + 'px solid';
+			p.sldB.style.borderColor = THIS.insetColor;
+
+			// slider mouse area
+			p.sldM._jscInstance = THIS;
+			p.sldM._jscControlName = 'sld';
+			p.sldM.style.display = displaySlider ? 'block' : 'none';
+			p.sldM.style.position = 'absolute';
+			p.sldM.style.right = '0';
+			p.sldM.style.top = '0';
+			p.sldM.style.width = (THIS.sliderSize + padToSliderPadding / 2 + THIS.padding + 2 * THIS.insetWidth) + 'px';
+			p.sldM.style.height = dims[1] + 'px';
+			p.sldM.style.cursor = 'default';
+
+			// slider pointer inner and outer border
+			p.sldPtrIB.style.border =
+			p.sldPtrOB.style.border =
+				THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor;
+
+			// slider pointer outer border
+			p.sldPtrOB.style.position = 'absolute';
+			p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px';
+			p.sldPtrOB.style.top = '0';
+
+			// slider pointer middle border
+			p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor;
+
+			// slider pointer spacer
+			p.sldPtrS.style.width = THIS.sliderSize + 'px';
+			p.sldPtrS.style.height = sliderPtrSpace + 'px';
+
+			// the Close button
+			function setBtnBorder () {
+				var insetColors = THIS.insetColor.split(/\s+/);
+				var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1];
+				p.btn.style.borderColor = outsetColor;
+			}
+			p.btn.style.display = THIS.closable ? 'block' : 'none';
+			p.btn.style.position = 'absolute';
+			p.btn.style.left = THIS.padding + 'px';
+			p.btn.style.bottom = THIS.padding + 'px';
+			p.btn.style.padding = '0 15px';
+			p.btn.style.height = THIS.buttonHeight + 'px';
+			p.btn.style.border = THIS.insetWidth + 'px solid';
+			setBtnBorder();
+			p.btn.style.color = THIS.buttonColor;
+			p.btn.style.font = '12px sans-serif';
+			p.btn.style.textAlign = 'center';
+			try {
+				p.btn.style.cursor = 'pointer';
+			} catch(eOldIE) {
+				p.btn.style.cursor = 'hand';
+			}
+			p.btn.onmousedown = function () {
+				THIS.hide();
+			};
+			p.btnT.style.lineHeight = THIS.buttonHeight + 'px';
+			p.btnT.innerHTML = '';
+			p.btnT.appendChild(document.createTextNode(THIS.closeText));
+
+			// place pointers
+			redrawPad();
+			redrawSld();
+
+			// If we are changing the owner without first closing the picker,
+			// make sure to first deal with the old owner
+			if (jsc.picker.owner && jsc.picker.owner !== THIS) {
+				jsc.unsetClass(jsc.picker.owner.targetElement, THIS.activeClass);
+			}
+
+			// Set the new picker owner
+			jsc.picker.owner = THIS;
+
+			// The redrawPosition() method needs picker.owner to be set, that's why we call it here,
+			// after setting the owner
+			if (jsc.isElementType(container, 'body')) {
+				jsc.redrawPosition();
+			} else {
+				jsc._drawPosition(THIS, 0, 0, 'relative', false);
+			}
+
+			if (p.wrap.parentNode != container) {
+				container.appendChild(p.wrap);
+			}
+
+			jsc.setClass(THIS.targetElement, THIS.activeClass);
+		}
+
+
+		function redrawPad () {
+			// redraw the pad pointer
+			switch (jsc.getPadYComponent(THIS)) {
+			case 's': var yComponent = 1; break;
+			case 'v': var yComponent = 2; break;
+			}
+			var x = Math.round((THIS.hsv[0] / 360) * (THIS.width - 1));
+			var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1));
+			var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize);
+			var ofs = -Math.floor(crossOuterSize / 2);
+			jsc.picker.cross.style.left = (x + ofs) + 'px';
+			jsc.picker.cross.style.top = (y + ofs) + 'px';
+
+			// redraw the slider
+			switch (jsc.getSliderComponent(THIS)) {
+			case 's':
+				var rgb1 = HSV_RGB(THIS.hsv[0], 100, THIS.hsv[2]);
+				var rgb2 = HSV_RGB(THIS.hsv[0], 0, THIS.hsv[2]);
+				var color1 = 'rgb(' +
+					Math.round(rgb1[0]) + ',' +
+					Math.round(rgb1[1]) + ',' +
+					Math.round(rgb1[2]) + ')';
+				var color2 = 'rgb(' +
+					Math.round(rgb2[0]) + ',' +
+					Math.round(rgb2[1]) + ',' +
+					Math.round(rgb2[2]) + ')';
+				jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2);
+				break;
+			case 'v':
+				var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 100);
+				var color1 = 'rgb(' +
+					Math.round(rgb[0]) + ',' +
+					Math.round(rgb[1]) + ',' +
+					Math.round(rgb[2]) + ')';
+				var color2 = '#000';
+				jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2);
+				break;
+			}
+		}
+
+
+		function redrawSld () {
+			var sldComponent = jsc.getSliderComponent(THIS);
+			if (sldComponent) {
+				// redraw the slider pointer
+				switch (sldComponent) {
+				case 's': var yComponent = 1; break;
+				case 'v': var yComponent = 2; break;
+				}
+				var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1));
+				jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(sliderPtrSpace / 2)) + 'px';
+			}
+		}
+
+
+		function isPickerOwner () {
+			return jsc.picker && jsc.picker.owner === THIS;
+		}
+
+
+		function blurValue () {
+			THIS.importColor();
+		}
+
+
+		// Find the target element
+		if (typeof targetElement === 'string') {
+			var id = targetElement;
+			var elm = document.getElementById(id);
+			if (elm) {
+				this.targetElement = elm;
+			} else {
+				jsc.warn('Could not find target element with ID \'' + id + '\'');
+			}
+		} else if (targetElement) {
+			this.targetElement = targetElement;
+		} else {
+			jsc.warn('Invalid target element: \'' + targetElement + '\'');
+		}
+
+		if (this.targetElement._jscLinkedInstance) {
+			jsc.warn('Cannot link jscolor twice to the same element. Skipping.');
+			return;
+		}
+		this.targetElement._jscLinkedInstance = this;
+
+		// Find the value element
+		this.valueElement = jsc.fetchElement(this.valueElement);
+		// Find the style element
+		this.styleElement = jsc.fetchElement(this.styleElement);
+
+		var THIS = this;
+		var container =
+			this.container ?
+			jsc.fetchElement(this.container) :
+			document.getElementsByTagName('body')[0];
+		var sliderPtrSpace = 3; // px
+
+		// For BUTTON elements it's important to stop them from sending the form when clicked
+		// (e.g. in Safari)
+		if (jsc.isElementType(this.targetElement, 'button')) {
+			if (this.targetElement.onclick) {
+				var origCallback = this.targetElement.onclick;
+				this.targetElement.onclick = function (evt) {
+					origCallback.call(this, evt);
+					return false;
+				};
+			} else {
+				this.targetElement.onclick = function () { return false; };
+			}
+		}
+
+		/*
+		var elm = this.targetElement;
+		do {
+			// If the target element or one of its offsetParents has fixed position,
+			// then use fixed positioning instead
+			//
+			// Note: In Firefox, getComputedStyle returns null in a hidden iframe,
+			// that's why we need to check if the returned style object is non-empty
+			var currStyle = jsc.getStyle(elm);
+			if (currStyle && currStyle.position.toLowerCase() === 'fixed') {
+				this.fixed = true;
+			}
+
+			if (elm !== this.targetElement) {
+				// attach onParentScroll so that we can recompute the picker position
+				// when one of the offsetParents is scrolled
+				if (!elm._jscEventsAttached) {
+					jsc.attachEvent(elm, 'scroll', jsc.onParentScroll);
+					elm._jscEventsAttached = true;
+				}
+			}
+		} while ((elm = elm.offsetParent) && !jsc.isElementType(elm, 'body'));
+		*/
+
+		// valueElement
+		if (this.valueElement) {
+			if (jsc.isElementType(this.valueElement, 'input')) {
+				var updateField = function () {
+					THIS.fromString(THIS.valueElement.value, jsc.leaveValue);
+					jsc.dispatchFineChange(THIS);
+				};
+				jsc.attachEvent(this.valueElement, 'keyup', updateField);
+				jsc.attachEvent(this.valueElement, 'input', updateField);
+				jsc.attachEvent(this.valueElement, 'blur', blurValue);
+				this.valueElement.setAttribute('autocomplete', 'off');
+			}
+		}
+
+		// styleElement
+		if (this.styleElement) {
+			this.styleElement._jscOrigStyle = {
+				backgroundImage : this.styleElement.style.backgroundImage,
+				backgroundColor : this.styleElement.style.backgroundColor,
+				color : this.styleElement.style.color
+			};
+		}
+
+		if (this.value) {
+			// Try to set the color from the .value option and if unsuccessful,
+			// export the current color
+			this.fromString(this.value) || this.exportColor();
+		} else {
+			this.importColor();
+		}
+	}
+
+};
+
+
+//================================
+// Public properties and methods
+//================================
+
+
+// By default, search for all elements with class="jscolor" and install a color picker on them.
+//
+// You can change what class name will be looked for by setting the property jscolor.lookupClass
+// anywhere in your HTML document. To completely disable the automatic lookup, set it to null.
+//
+jsc.jscolor.lookupClass = 'jscolor';
+
+
+jsc.jscolor.installByClassName = function (className) {
+	var inputElms = document.getElementsByTagName('input');
+	var buttonElms = document.getElementsByTagName('button');
+
+	jsc.tryInstallOnElements(inputElms, className);
+	jsc.tryInstallOnElements(buttonElms, className);
+};
+
+
+jsc.register();
+
+
+return jsc.jscolor;
+
+
+})(); }

+ 1 - 1
spine-ts/player/src/Player.ts

@@ -794,7 +794,7 @@ module spine {
 				// Draw background image if given
 				if (this.config.backgroundImage && this.config.backgroundImage.url) {
 					let bgImage = this.assetManager.get(this.config.backgroundImage.url);
-					if (!this.config.backgroundImage.x) {
+					if (!(this.config.backgroundImage.hasOwnProperty("x") && this.config.backgroundImage.hasOwnProperty("y") && this.config.backgroundImage.hasOwnProperty("width") && this.config.backgroundImage.hasOwnProperty("height"))) {
 						this.sceneRenderer.drawTexture(bgImage, viewport.x, viewport.y, viewport.width, viewport.height);
 					} else {
 						this.sceneRenderer.drawTexture(bgImage, this.config.backgroundImage.x, this.config.backgroundImage.y, this.config.backgroundImage.width, this.config.backgroundImage.height);

部分文件因为文件数量过多而无法显示