PageSourceSearch

https://www.tynker.com/ext/spine.js

js tynker.com collected 2026-10-01 09:16:45 UTC 95,401 bytes, 2,786 lines download raw bytes

1/******************************************************************************
2 * Spine Runtimes Software License
3 * Version 2.3
4 * 
5 * Copyright (c) 2013-2015, Esoteric Software
6 * All rights reserved.
7 * 
8 * You are granted a perpetual, non-exclusive, non-sublicensable and
9 * non-transferable license to use, install, execute and perform the Spine
10 * Runtimes Software (the "Software") and derivative works solely for personal
11 * or internal use. Without the written permission of Esoteric Software (see
12 * Section 2 of the Spine Software License Agreement), you may not (a) modify,
13 * translate, adapt or otherwise create derivative works, improvements of the
14 * Software or develop new applications using the Software or (b) remove,
15 * delete, alter or obscure any trademarks or any copyright, trademark, patent
16 * or other intellectual property or proprietary rights notices on or in the
17 * Software, including any copy thereof. Redistributions in binary or source
18 * form must include this license and terms.
19 * 
20 * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
23 * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *****************************************************************************/
31
32var spine = {
33	radDeg: 180 / Math.PI,
34	degRad: Math.PI / 180,
35	temp: [],
36    Float32Array: (typeof(Float32Array) === 'undefined') ? Array : Float32Array,
37    Uint16Array: (typeof(Uint16Array) === 'undefined') ? Array : Uint16Array
38};
39
40spine.BoneData = function (name, parent) {
41	this.name = name;
42	this.parent = parent;
43};
44spine.BoneData.prototype = {
45	length: 0,
46	x: 0, y: 0,
47	rotation: 0,
48	scaleX: 1, scaleY: 1,
49	inheritScale: true,
50	inheritRotation: true,
51	flipX: false, flipY: false
52};
53
54spine.BlendMode = {
55	normal: 0,
56	additive: 1,
57	multiply: 2,
58	screen: 3
59};
60
61spine.SlotData = function (name, boneData) {
62	this.name = name;
63	this.boneData = boneData;
64};
65spine.SlotData.prototype = {
66	r: 1, g: 1, b: 1, a: 1,
67	attachmentName: null,
68	blendMode: spine.BlendMode.normal
69};
70
71spine.IkConstraintData = function (name) {
72	this.name = name;
73	this.bones = [];
74};
75spine.IkConstraintData.prototype = {
76	target: null,
77	bendDirection: 1,
78	mix: 1
79};
80
81spine.Bone = function (boneData, skeleton, parent) {
82	this.data = boneData;
83	this.skeleton = skeleton;
84	this.parent = parent;
85	this.setToSetupPose();
86};
87spine.Bone.yDown = false;
88spine.Bone.prototype = {
89	x: 0, y: 0,
90	rotation: 0, rotationIK: 0,
91	scaleX: 1, scaleY: 1,
92	flipX: false, flipY: false,
93	m00: 0, m01: 0, worldX: 0, // a b x
94	m10: 0, m11: 0, worldY: 0, // c d y
95	worldRotation: 0,
96	worldScaleX: 1, worldScaleY: 1,
97	worldFlipX: false, worldFlipY: false,
98	updateWorldTransform: function () {
99		var parent = this.parent;
100		if (parent) {
101			this.worldX = this.x * parent.m00 + this.y * parent.m01 + parent.worldX;
102			this.worldY = this.x * parent.m10 + this.y * parent.m11 + parent.worldY;
103			if (this.data.inheritScale) {
104				this.worldScaleX = parent.worldScaleX * this.scaleX;
105				this.worldScaleY = parent.worldScaleY * this.scaleY;
106			} else {
107				this.worldScaleX = this.scaleX;
108				this.worldScaleY = this.scaleY;
109			}
110			this.worldRotation = this.data.inheritRotation ? (parent.worldRotation + this.rotationIK) : this.rotationIK;
111			this.worldFlipX = parent.worldFlipX != this.flipX;
112			this.worldFlipY = parent.worldFlipY != this.flipY;
113		} else {
114			var skeletonFlipX = this.skeleton.flipX, skeletonFlipY = this.skeleton.flipY;
115			this.worldX = skeletonFlipX ? -this.x : this.x;
116			this.worldY = (skeletonFlipY != spine.Bone.yDown) ? -this.y : this.y;
117			this.worldScaleX = this.scaleX;
118			this.worldScaleY = this.scaleY;
119			this.worldRotation = this.rotationIK;
120			this.worldFlipX = skeletonFlipX != this.flipX;
121			this.worldFlipY = skeletonFlipY != this.flipY;
122		}
123		var radians = this.worldRotation * spine.degRad;
124		var cos = Math.cos(radians);
125		var sin = Math.sin(radians);
126		if (this.worldFlipX) {
127			this.m00 = -cos * this.worldScaleX;
128			this.m01 = sin * this.worldScaleY;
129		} else {
130			this.m00 = cos * this.worldScaleX;
131			this.m01 = -sin * this.worldScaleY;
132		}
133		if (this.worldFlipY != spine.Bone.yDown) {
134			this.m10 = -sin * this.worldScaleX;
135			this.m11 = -cos * this.worldScaleY;
136		} else {
137			this.m10 = sin * this.worldScaleX;
138			this.m11 = cos * this.worldScaleY;
139		}
140	},
141	setToSetupPose: function () {
142		var data = this.data;
143		this.x = data.x;
144		this.y = data.y;
145		this.rotation = data.rotation;
146		this.rotationIK = this.rotation;
147		this.scaleX = data.scaleX;
148		this.scaleY = data.scaleY;
149		this.flipX = data.flipX;
150		this.flipY = data.flipY;
151	},
152	worldToLocal: function (world) {
153		var dx = world[0] - this.worldX, dy = world[1] - this.worldY;
154		var m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11;
155		if (this.worldFlipX != (this.worldFlipY != spine.Bone.yDown)) {
156			m00 = -m00;
157			m11 = -m11;
158		}
159		var invDet = 1 / (m00 * m11 - m01 * m10);
160		world[0] = dx * m00 * invDet - dy * m01 * invDet;
161		world[1] = dy * m11 * invDet - dx * m10 * invDet;
162	},
163	localToWorld: function (local) {
164		var localX = local[0], localY = local[1];
165		local[0] = localX * this.m00 + localY * this.m01 + this.worldX;
166		local[1] = localX * this.m10 + localY * this.m11 + this.worldY;
167	}
168};
169
170spine.Slot = function (slotData, bone) {
171	this.data = slotData;
172	this.bone = bone;
173	this.setToSetupPose();
174};
175spine.Slot.prototype = {
176	r: 1, g: 1, b: 1, a: 1,
177	_attachmentTime: 0,
178	attachment: null,
179	attachmentVertices: [],
180	setAttachment: function (attachment) {
181		this.attachment = attachment;
182		this._attachmentTime = this.bone.skeleton.time;
183		this.attachmentVertices.length = 0;
184	},
185	setAttachmentTime: function (time) {
186		this._attachmentTime = this.bone.skeleton.time - time;
187	},
188	getAttachmentTime: function () {
189		return this.bone.skeleton.time - this._attachmentTime;
190	},
191	setToSetupPose: function () {
192		var data = this.data;
193		this.r = data.r;
194		this.g = data.g;
195		this.b = data.b;
196		this.a = data.a;
197
198		var slotDatas = this.bone.skeleton.data.slots;
199		for (var i = 0, n = slotDatas.length; i < n; i++) {
200			if (slotDatas[i] == data) {
201				this.setAttachment(!data.attachmentName ? null : this.bone.skeleton.getAttachmentBySlotIndex(i, data.attachmentName));
202				break;
203			}
204		}
205	}
206};
207
208spine.IkConstraint = function (data, skeleton) {
209	this.data = data;
210	this.mix = data.mix;
211	this.bendDirection = data.bendDirection;
212
213	this.bones = [];
214	for (var i = 0, n = data.bones.length; i < n; i++)
215		this.bones.push(skeleton.findBone(data.bones[i].name));
216	this.target = skeleton.findBone(data.target.name);
217};
218spine.IkConstraint.prototype = {
219	apply: function () {
220		var target = this.target;
221		var bones = this.bones;
222		switch (bones.length) {
223		case 1:
224			spine.IkConstraint.apply1(bones[0], target.worldX, target.worldY, this.mix);
225			break;
226		case 2:
227			spine.IkConstraint.apply2(bones[0], bones[1], target.worldX, target.worldY, this.bendDirection, this.mix);
228			break;
229		}
230	}
231};
232/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world
233 * coordinate system. */
234spine.IkConstraint.apply1 = function (bone, targetX, targetY, alpha) {
235	var parentRotation = (!bone.data.inheritRotation || !bone.parent) ? 0 : bone.parent.worldRotation;
236	var rotation = bone.rotation;
237	var rotationIK = Math.atan2(targetY - bone.worldY, targetX - bone.worldX) * spine.radDeg;
238	if (bone.worldFlipX != (bone.worldFlipY != spine.Bone.yDown)) rotationIK = -rotationIK;
239	rotationIK -= parentRotation;
240	bone.rotationIK = rotation + (rotationIK - rotation) * alpha;
241};
242/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The
243 * target is specified in the world coordinate system.
244 * @param child Any descendant bone of the parent. */
245spine.IkConstraint.apply2 = function (parent, child, targetX, targetY, bendDirection, alpha) {
246	var childRotation = child.rotation, parentRotation = parent.rotation;
247	if (!alpha) {
248		child.rotationIK = childRotation;
249		parent.rotationIK = parentRotation;
250		return;
251	}
252	var positionX, positionY, tempPosition = spine.temp;
253	var parentParent = parent.parent;
254	if (parentParent) {
255		tempPosition[0] = targetX;
256		tempPosition[1] = targetY;
257		parentParent.worldToLocal(tempPosition);
258		targetX = (tempPosition[0] - parent.x) * parentParent.worldScaleX;
259		targetY = (tempPosition[1] - parent.y) * parentParent.worldScaleY;
260	} else {
261		targetX -= parent.x;
262		targetY -= parent.y;
263	}
264	if (child.parent == parent) {
265		positionX = child.x;
266		positionY = child.y;
267	} else {
268		tempPosition[0] = child.x;
269		tempPosition[1] = child.y;
270		child.parent.localToWorld(tempPosition);
271		parent.worldToLocal(tempPosition);
272		positionX = tempPosition[0];
273		positionY = tempPosition[1];
274	}
275	var childX = positionX * parent.worldScaleX, childY = positionY * parent.worldScaleY;
276	var offset = Math.atan2(childY, childX);
277	var len1 = Math.sqrt(childX * childX + childY * childY), len2 = child.data.length * child.worldScaleX;
278	// Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/
279	var cosDenom = 2 * len1 * len2;
280	if (cosDenom < 0.0001) {
281		child.rotationIK = childRotation + (Math.atan2(targetY, targetX) * spine.radDeg - parentRotation - childRotation) * alpha;
282		return;
283	}
284	var cos = (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom;
285	if (cos < -1)
286		cos = -1;
287	else if (cos > 1)
288		cos = 1;
289	var childAngle = Math.acos(cos) * bendDirection;
290	var adjacent = len1 + len2 * cos, opposite = len2 * Math.sin(childAngle);
291	var parentAngle = Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite);
292	var rotation = (parentAngle - offset) * spine.radDeg - parentRotation;
293	if (rotation > 180)
294		rotation -= 360;
295	else if (rotation < -180) //
296		rotation += 360;
297	parent.rotationIK = parentRotation + rotation * alpha;
298	rotation = (childAngle + offset) * spine.radDeg - childRotation;
299	if (rotation > 180)
300		rotation -= 360;
301	else if (rotation < -180) //
302		rotation += 360;
303	child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha;
304};
305
306spine.Skin = function (name) {
307	this.name = name;
308	this.attachments = {};
309};
310spine.Skin.prototype = {
311	addAttachment: function (slotIndex, name, attachment) {
312		this.attachments[slotIndex + ":" + name] = attachment;
313	},
314	getAttachment: function (slotIndex, name) {
315		return this.attachments[slotIndex + ":" + name];
316	},
317	_attachAll: function (skeleton, oldSkin) {
318		for (var key in oldSkin.attachments) {
319			var colon = key.indexOf(":");
320			var slotIndex = parseInt(key.substring(0, colon));
321			var name = key.substring(colon + 1);
322			var slot = skeleton.slots[slotIndex];
323			if (slot.attachment && slot.attachment.name == name) {
324				var attachment = this.getAttachment(slotIndex, name);
325				if (attachment) slot.setAttachment(attachment);
326			}
327		}
328	}
329};
330
331spine.Animation = function (name, timelines, duration) {
332	this.name = name;
333	this.timelines = timelines;
334	this.duration = duration;
335};
336spine.Animation.prototype = {
337	apply: function (skeleton, lastTime, time, loop, events) {
338		if (loop && this.duration != 0) {
339			time %= this.duration;
340			lastTime %= this.duration;
341		}
342		var timelines = this.timelines;
343		for (var i = 0, n = timelines.length; i < n; i++)
344			timelines[i].apply(skeleton, lastTime, time, events, 1);
345	},
346	mix: function (skeleton, lastTime, time, loop, events, alpha) {
347		if (loop && this.duration != 0) {
348			time %= this.duration;
349			lastTime %= this.duration;
350		}
351		var timelines = this.timelines;
352		for (var i = 0, n = timelines.length; i < n; i++)
353			timelines[i].apply(skeleton, lastTime, time, events, alpha);
354	}
355};
356spine.Animation.binarySearch = function (values, target, step) {
357	var low = 0;
vendor: 15,898 bytes, lines 358-832
358	var high = Math.floor(values.length / step) - 2;
359	if (!high) return step;
360	var current = high >>> 1;
361	while (true) {
362		if (values[(current + 1) * step] <= target)
363			low = current + 1;
364		else
365			high = current;
366		if (low == high) return (low + 1) * step;
367		current = (low + high) >>> 1;
368	}
369};
370spine.Animation.binarySearch1 = function (values, target) {
371	var low = 0;
372	var high = values.length - 2;
373	if (!high) return 1;
374	var current = high >>> 1;
375	while (true) {
376		if (values[current + 1] <= target)
377			low = current + 1;
378		else
379			high = current;
380		if (low == high) return low + 1;
381		current = (low + high) >>> 1;
382	}
383};
384spine.Animation.linearSearch = function (values, target, step) {
385	for (var i = 0, last = values.length - step; i <= last; i += step)
386		if (values[i] > target) return i;
387	return -1;
388};
389
390spine.Curves = function (frameCount) {
391	this.curves = []; // type, x, y, ...
392	//this.curves.length = (frameCount - 1) * 19/*BEZIER_SIZE*/;
393};
394spine.Curves.prototype = {
395	setLinear: function (frameIndex) {
396		this.curves[frameIndex * 19/*BEZIER_SIZE*/] = 0/*LINEAR*/;
397	},
398	setStepped: function (frameIndex) {
399		this.curves[frameIndex * 19/*BEZIER_SIZE*/] = 1/*STEPPED*/;
400	},
401	/** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
402	 * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
403	 * the difference between the keyframe's values. */
404	setCurve: function (frameIndex, cx1, cy1, cx2, cy2) {
405		var subdiv1 = 1 / 10/*BEZIER_SEGMENTS*/, subdiv2 = subdiv1 * subdiv1, subdiv3 = subdiv2 * subdiv1;
406		var pre1 = 3 * subdiv1, pre2 = 3 * subdiv2, pre4 = 6 * subdiv2, pre5 = 6 * subdiv3;
407		var tmp1x = -cx1 * 2 + cx2, tmp1y = -cy1 * 2 + cy2, tmp2x = (cx1 - cx2) * 3 + 1, tmp2y = (cy1 - cy2) * 3 + 1;
408		var dfx = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3, dfy = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3;
409		var ddfx = tmp1x * pre4 + tmp2x * pre5, ddfy = tmp1y * pre4 + tmp2y * pre5;
410		var dddfx = tmp2x * pre5, dddfy = tmp2y * pre5;
411
412		var i = frameIndex * 19/*BEZIER_SIZE*/;
413		var curves = this.curves;
414		curves[i++] = 2/*BEZIER*/;
415		
416		var x = dfx, y = dfy;
417		for (var n = i + 19/*BEZIER_SIZE*/ - 1; i < n; i += 2) {
418			curves[i] = x;
419			curves[i + 1] = y;
420			dfx += ddfx;
421			dfy += ddfy;
422			ddfx += dddfx;
423			ddfy += dddfy;
424			x += dfx;
425			y += dfy;
426		}
427	},
428	getCurvePercent: function (frameIndex, percent) {
429		percent = percent < 0 ? 0 : (percent > 1 ? 1 : percent);
430		var curves = this.curves;
431		var i = frameIndex * 19/*BEZIER_SIZE*/;
432		var type = curves[i];
433		if (type === 0/*LINEAR*/) return percent;
434		if (type == 1/*STEPPED*/) return 0;
435		i++;
436		var x = 0;
437		for (var start = i, n = i + 19/*BEZIER_SIZE*/ - 1; i < n; i += 2) {
438			x = curves[i];
439			if (x >= percent) {
440				var prevX, prevY;
441				if (i == start) {
442					prevX = 0;
443					prevY = 0;
444				} else {
445					prevX = curves[i - 2];
446					prevY = curves[i - 1];
447				}
448				return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX);
449			}
450		}
451		var y = curves[i - 1];
452		return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1.
453	}
454};
455
456spine.RotateTimeline = function (frameCount) {
457	this.curves = new spine.Curves(frameCount);
458	this.frames = []; // time, angle, ...
459	this.frames.length = frameCount * 2;
460};
461spine.RotateTimeline.prototype = {
462	boneIndex: 0,
463	getFrameCount: function () {
464		return this.frames.length / 2;
465	},
466	setFrame: function (frameIndex, time, angle) {
467		frameIndex *= 2;
468		this.frames[frameIndex] = time;
469		this.frames[frameIndex + 1] = angle;
470	},
471	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
472		var frames = this.frames;
473		if (time < frames[0]) return; // Time is before first frame.
474
475		var bone = skeleton.bones[this.boneIndex];
476
477		if (time >= frames[frames.length - 2]) { // Time is after last frame.
478			var amount = bone.data.rotation + frames[frames.length - 1] - bone.rotation;
479			while (amount > 180)
480				amount -= 360;
481			while (amount < -180)
482				amount += 360;
483			bone.rotation += amount * alpha;
484			return;
485		}
486
487		// Interpolate between the previous frame and the current frame.
488		var frameIndex = spine.Animation.binarySearch(frames, time, 2);
489		var prevFrameValue = frames[frameIndex - 1];
490		var frameTime = frames[frameIndex];
491		var percent = 1 - (time - frameTime) / (frames[frameIndex - 2/*PREV_FRAME_TIME*/] - frameTime);
492		percent = this.curves.getCurvePercent(frameIndex / 2 - 1, percent);
493
494		var amount = frames[frameIndex + 1/*FRAME_VALUE*/] - prevFrameValue;
495		while (amount > 180)
496			amount -= 360;
497		while (amount < -180)
498			amount += 360;
499		amount = bone.data.rotation + (prevFrameValue + amount * percent) - bone.rotation;
500		while (amount > 180)
501			amount -= 360;
502		while (amount < -180)
503			amount += 360;
504		bone.rotation += amount * alpha;
505	}
506};
507
508spine.TranslateTimeline = function (frameCount) {
509	this.curves = new spine.Curves(frameCount);
510	this.frames = []; // time, x, y, ...
511	this.frames.length = frameCount * 3;
512};
513spine.TranslateTimeline.prototype = {
514	boneIndex: 0,
515	getFrameCount: function () {
516		return this.frames.length / 3;
517	},
518	setFrame: function (frameIndex, time, x, y) {
519		frameIndex *= 3;
520		this.frames[frameIndex] = time;
521		this.frames[frameIndex + 1] = x;
522		this.frames[frameIndex + 2] = y;
523	},
524	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
525		var frames = this.frames;
526		if (time < frames[0]) return; // Time is before first frame.
527
528		var bone = skeleton.bones[this.boneIndex];
529
530		if (time >= frames[frames.length - 3]) { // Time is after last frame.
531			bone.x += (bone.data.x + frames[frames.length - 2] - bone.x) * alpha;
532			bone.y += (bone.data.y + frames[frames.length - 1] - bone.y) * alpha;
533			return;
534		}
535
536		// Interpolate between the previous frame and the current frame.
537		var frameIndex = spine.Animation.binarySearch(frames, time, 3);
538		var prevFrameX = frames[frameIndex - 2];
539		var prevFrameY = frames[frameIndex - 1];
540		var frameTime = frames[frameIndex];
541		var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime);
542		percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent);
543
544		bone.x += (bone.data.x + prevFrameX + (frames[frameIndex + 1/*FRAME_X*/] - prevFrameX) * percent - bone.x) * alpha;
545		bone.y += (bone.data.y + prevFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - prevFrameY) * percent - bone.y) * alpha;
546	}
547};
548
549spine.ScaleTimeline = function (frameCount) {
550	this.curves = new spine.Curves(frameCount);
551	this.frames = []; // time, x, y, ...
552	this.frames.length = frameCount * 3;
553};
554spine.ScaleTimeline.prototype = {
555	boneIndex: 0,
556	getFrameCount: function () {
557		return this.frames.length / 3;
558	},
559	setFrame: function (frameIndex, time, x, y) {
560		frameIndex *= 3;
561		this.frames[frameIndex] = time;
562		this.frames[frameIndex + 1] = x;
563		this.frames[frameIndex + 2] = y;
564	},
565	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
566		var frames = this.frames;
567		if (time < frames[0]) return; // Time is before first frame.
568
569		var bone = skeleton.bones[this.boneIndex];
570
571		if (time >= frames[frames.length - 3]) { // Time is after last frame.
572			bone.scaleX += (bone.data.scaleX * frames[frames.length - 2] - bone.scaleX) * alpha;
573			bone.scaleY += (bone.data.scaleY * frames[frames.length - 1] - bone.scaleY) * alpha;
574			return;
575		}
576
577		// Interpolate between the previous frame and the current frame.
578		var frameIndex = spine.Animation.binarySearch(frames, time, 3);
579		var prevFrameX = frames[frameIndex - 2];
580		var prevFrameY = frames[frameIndex - 1];
581		var frameTime = frames[frameIndex];
582		var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime);
583		percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent);
584
585		bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frameIndex + 1/*FRAME_X*/] - prevFrameX) * percent) - bone.scaleX) * alpha;
586		bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - prevFrameY) * percent) - bone.scaleY) * alpha;
587	}
588};
589
590spine.ColorTimeline = function (frameCount) {
591	this.curves = new spine.Curves(frameCount);
592	this.frames = []; // time, r, g, b, a, ...
593	this.frames.length = frameCount * 5;
594};
595spine.ColorTimeline.prototype = {
596	slotIndex: 0,
597	getFrameCount: function () {
598		return this.frames.length / 5;
599	},
600	setFrame: function (frameIndex, time, r, g, b, a) {
601		frameIndex *= 5;
602		this.frames[frameIndex] = time;
603		this.frames[frameIndex + 1] = r;
604		this.frames[frameIndex + 2] = g;
605		this.frames[frameIndex + 3] = b;
606		this.frames[frameIndex + 4] = a;
607	},
608	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
609		var frames = this.frames;
610		if (time < frames[0]) return; // Time is before first frame.
611
612		var r, g, b, a;
613		if (time >= frames[frames.length - 5]) {
614			// Time is after last frame.
615			var i = frames.length - 1;
616			r = frames[i - 3];
617			g = frames[i - 2];
618			b = frames[i - 1];
619			a = frames[i];
620		} else {
621			// Interpolate between the previous frame and the current frame.
622			var frameIndex = spine.Animation.binarySearch(frames, time, 5);
623			var prevFrameR = frames[frameIndex - 4];
624			var prevFrameG = frames[frameIndex - 3];
625			var prevFrameB = frames[frameIndex - 2];
626			var prevFrameA = frames[frameIndex - 1];
627			var frameTime = frames[frameIndex];
628			var percent = 1 - (time - frameTime) / (frames[frameIndex - 5/*PREV_FRAME_TIME*/] - frameTime);
629			percent = this.curves.getCurvePercent(frameIndex / 5 - 1, percent);
630
631			r = prevFrameR + (frames[frameIndex + 1/*FRAME_R*/] - prevFrameR) * percent;
632			g = prevFrameG + (frames[frameIndex + 2/*FRAME_G*/] - prevFrameG) * percent;
633			b = prevFrameB + (frames[frameIndex + 3/*FRAME_B*/] - prevFrameB) * percent;
634			a = prevFrameA + (frames[frameIndex + 4/*FRAME_A*/] - prevFrameA) * percent;
635		}
636		var slot = skeleton.slots[this.slotIndex];
637		if (alpha < 1) {
638			slot.r += (r - slot.r) * alpha;
639			slot.g += (g - slot.g) * alpha;
640			slot.b += (b - slot.b) * alpha;
641			slot.a += (a - slot.a) * alpha;
642		} else {
643			slot.r = r;
644			slot.g = g;
645			slot.b = b;
646			slot.a = a;
647		}
648	}
649};
650
651spine.AttachmentTimeline = function (frameCount) {
652	this.curves = new spine.Curves(frameCount);
653	this.frames = []; // time, ...
654	this.frames.length = frameCount;
655	this.attachmentNames = [];
656	this.attachmentNames.length = frameCount;
657};
658spine.AttachmentTimeline.prototype = {
659	slotIndex: 0,
660	getFrameCount: function () {
661		return this.frames.length;
662	},
663	setFrame: function (frameIndex, time, attachmentName) {
664		this.frames[frameIndex] = time;
665		this.attachmentNames[frameIndex] = attachmentName;
666	},
667	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
668		var frames = this.frames;
669		if (time < frames[0]) {
670			if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0);
671			return;
672		} else if (lastTime > time) //
673			lastTime = -1;
674
675		var frameIndex = time >= frames[frames.length - 1] ? frames.length - 1 : spine.Animation.binarySearch1(frames, time) - 1;
676		if (frames[frameIndex] < lastTime) return;
677
678		var attachmentName = this.attachmentNames[frameIndex];
679		skeleton.slots[this.slotIndex].setAttachment(
680			!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName));
681	}
682};
683
684spine.EventTimeline = function (frameCount) {
685	this.frames = []; // time, ...
686	this.frames.length = frameCount;
687	this.events = [];
688	this.events.length = frameCount;
689};
690spine.EventTimeline.prototype = {
691	getFrameCount: function () {
692		return this.frames.length;
693	},
694	setFrame: function (frameIndex, time, event) {
695		this.frames[frameIndex] = time;
696		this.events[frameIndex] = event;
697	},
698	/** Fires events for frames > lastTime and <= time. */
699	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
700		if (!firedEvents) return;
701
702		var frames = this.frames;
703		var frameCount = frames.length;
704
705		if (lastTime > time) { // Fire events after last time for looped animations.
706			this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha);
707			lastTime = -1;
708		} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
709			return;
710		if (time < frames[0]) return; // Time is before first frame.
711
712		var frameIndex;
713		if (lastTime < frames[0])
714			frameIndex = 0;
715		else {
716			frameIndex = spine.Animation.binarySearch1(frames, lastTime);
717			var frame = frames[frameIndex];
718			while (frameIndex > 0) { // Fire multiple events with the same frame.
719				if (frames[frameIndex - 1] != frame) break;
720				frameIndex--;
721			}
722		}
723		var events = this.events;
724		for (; frameIndex < frameCount && time >= frames[frameIndex]; frameIndex++)
725			firedEvents.push(events[frameIndex]);
726	}
727};
728
729spine.DrawOrderTimeline = function (frameCount) {
730	this.frames = []; // time, ...
731	this.frames.length = frameCount;
732	this.drawOrders = [];
733	this.drawOrders.length = frameCount;
734};
735spine.DrawOrderTimeline.prototype = {
736	getFrameCount: function () {
737		return this.frames.length;
738	},
739	setFrame: function (frameIndex, time, drawOrder) {
740		this.frames[frameIndex] = time;
741		this.drawOrders[frameIndex] = drawOrder;
742	},
743	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
744		var frames = this.frames;
745		if (time < frames[0]) return; // Time is before first frame.
746
747		var frameIndex;
748		if (time >= frames[frames.length - 1]) // Time is after last frame.
749			frameIndex = frames.length - 1;
750		else
751			frameIndex = spine.Animation.binarySearch1(frames, time) - 1;
752
753		var drawOrder = skeleton.drawOrder;
754		var slots = skeleton.slots;
755		var drawOrderToSetupIndex = this.drawOrders[frameIndex];
756		if (!drawOrderToSetupIndex) {
757			for (var i = 0, n = slots.length; i < n; i++)
758				drawOrder[i] = slots[i];
759		} else {
760			for (var i = 0, n = drawOrderToSetupIndex.length; i < n; i++)
761				drawOrder[i] = skeleton.slots[drawOrderToSetupIndex[i]];
762		}
763
764	}
765};
766
767spine.FfdTimeline = function (frameCount) {
768	this.curves = new spine.Curves(frameCount);
769	this.frames = [];
770	this.frames.length = frameCount;
771	this.frameVertices = [];
772	this.frameVertices.length = frameCount;
773};
774spine.FfdTimeline.prototype = {
775	slotIndex: 0,
776	attachment: 0,
777	getFrameCount: function () {
778		return this.frames.length;
779	},
780	setFrame: function (frameIndex, time, vertices) {
781		this.frames[frameIndex] = time;
782		this.frameVertices[frameIndex] = vertices;
783	},
784	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
785		var slot = skeleton.slots[this.slotIndex];
786		if (slot.attachment != this.attachment) return;
787
788		var frames = this.frames;
789		if (time < frames[0]) return; // Time is before first frame.
790
791		var frameVertices = this.frameVertices;
792		var vertexCount = frameVertices[0].length;
793
794		var vertices = slot.attachmentVertices;
795		if (vertices.length != vertexCount) alpha = 1;
796		vertices.length = vertexCount;
797
798		if (time >= frames[frames.length - 1]) { // Time is after last frame.
799			var lastVertices = frameVertices[frames.length - 1];
800			if (alpha < 1) {
801				for (var i = 0; i < vertexCount; i++)
802					vertices[i] += (lastVertices[i] - vertices[i]) * alpha;
803			} else {
804				for (var i = 0; i < vertexCount; i++)
805					vertices[i] = lastVertices[i];
806			}
807			return;
808		}
809
810		// Interpolate between the previous frame and the current frame.
811		var frameIndex = spine.Animation.binarySearch1(frames, time);
812		var frameTime = frames[frameIndex];
813		var percent = 1 - (time - frameTime) / (frames[frameIndex - 1] - frameTime);
814		percent = this.curves.getCurvePercent(frameIndex - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
815
816		var prevVertices = frameVertices[frameIndex - 1];
817		var nextVertices = frameVertices[frameIndex];
818
819		if (alpha < 1) {
820			for (var i = 0; i < vertexCount; i++) {
821				var prev = prevVertices[i];
822				vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha;
823			}
824		} else {
825			for (var i = 0; i < vertexCount; i++) {
826				var prev = prevVertices[i];
827				vertices[i] = prev + (nextVertices[i] - prev) * percent;
828			}
829		}
830	}
831};
832
833spine.IkConstraintTimeline = function (frameCount) {
834	this.curves = new spine.Curves(frameCount);
835	this.frames = []; // time, mix, bendDirection, ...
836	this.frames.length = frameCount * 3;
837};
838spine.IkConstraintTimeline.prototype = {
839	ikConstraintIndex: 0,
840	getFrameCount: function () {
841		return this.frames.length / 3;
842	},
843	setFrame: function (frameIndex, time, mix, bendDirection) {
844		frameIndex *= 3;
845		this.frames[frameIndex] = time;
846		this.frames[frameIndex + 1] = mix;
847		this.frames[frameIndex + 2] = bendDirection;
848	},
849	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
850		var frames = this.frames;
851		if (time < frames[0]) return; // Time is before first frame.
852
853		var ikConstraint = skeleton.ikConstraints[this.ikConstraintIndex];
854
855		if (time >= frames[frames.length - 3]) { // Time is after last frame.
856			ikConstraint.mix += (frames[frames.length - 2] - ikConstraint.mix) * alpha;
857			ikConstraint.bendDirection = frames[frames.length - 1];
858			return;
859		}
860
861		// Interpolate between the previous frame and the current frame.
862		var frameIndex = spine.Animation.binarySearch(frames, time, 3);
863		var prevFrameMix = frames[frameIndex + -2/*PREV_FRAME_MIX*/];
864		var frameTime = frames[frameIndex];
865		var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime);
866		percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent);
867
868		var mix = prevFrameMix + (frames[frameIndex + 1/*FRAME_MIX*/] - prevFrameMix) * percent;
869		ikConstraint.mix += (mix - ikConstraint.mix) * alpha;
870		ikConstraint.bendDirection = frames[frameIndex + -1/*PREV_FRAME_BEND_DIRECTION*/];
871	}
872};
873
874spine.FlipXTimeline = function (frameCount) {
875	this.curves = new spine.Curves(frameCount);
876	this.frames = []; // time, flip, ...
877	this.frames.length = frameCount * 2;
878};
879spine.FlipXTimeline.prototype = {
880	boneIndex: 0,
881	getFrameCount: function () {
882		return this.frames.length / 2;
883	},
884	setFrame: function (frameIndex, time, flip) {
885		frameIndex *= 2;
886		this.frames[frameIndex] = time;
887		this.frames[frameIndex + 1] = flip ? 1 : 0;
888	},
889	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
890		var frames = this.frames;
891		if (time < frames[0]) {
892			if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0);
893			return;
894		} else if (lastTime > time) //
895			lastTime = -1;
896		var frameIndex = (time >= frames[frames.length - 2] ? frames.length : spine.Animation.binarySearch(frames, time, 2)) - 2;
897		if (frames[frameIndex] < lastTime) return;
898		skeleton.bones[this.boneIndex].flipX = frames[frameIndex + 1] != 0;
899	}
900};
901
902spine.FlipYTimeline = function (frameCount) {
903	this.curves = new spine.Curves(frameCount);
904	this.frames = []; // time, flip, ...
905	this.frames.length = frameCount * 2;
906};
907spine.FlipYTimeline.prototype = {
908	boneIndex: 0,
909	getFrameCount: function () {
910		return this.frames.length / 2;
911	},
912	setFrame: function (frameIndex, time, flip) {
913		frameIndex *= 2;
914		this.frames[frameIndex] = time;
915		this.frames[frameIndex + 1] = flip ? 1 : 0;
916	},
917	apply: function (skeleton, lastTime, time, firedEvents, alpha) {
918		var frames = this.frames;
919		if (time < frames[0]) {
920			if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0);
921			return;
922		} else if (lastTime > time) //
923			lastTime = -1;
924		var frameIndex = (time >= frames[frames.length - 2] ? frames.length : spine.Animation.binarySearch(frames, time, 2)) - 2;
925		if (frames[frameIndex] < lastTime) return;
926		skeleton.bones[this.boneIndex].flipY = frames[frameIndex + 1] != 0;
927	}
928};
929
930spine.SkeletonData = function () {
931	this.bones = [];
932	this.slots = [];
933	this.skins = [];
934	this.events = [];
935	this.animations = [];
936	this.ikConstraints = [];
937};
938spine.SkeletonData.prototype = {
939	name: null,
940	defaultSkin: null,
941	width: 0, height: 0,
942	version: null, hash: null,
943	/** @return May be null. */
944	findBone: function (boneName) {
945		var bones = this.bones;
946		for (var i = 0, n = bones.length; i < n; i++)
947			if (bones[i].name == boneName) return bones[i];
948		return null;
949	},
950	/** @return -1 if the bone was not found. */
951	findBoneIndex: function (boneName) {
952		var bones = this.bones;
953		for (var i = 0, n = bones.length; i < n; i++)
954			if (bones[i].name == boneName) return i;
955		return -1;
956	},
957	/** @return May be null. */
958	findSlot: function (slotName) {
959		var slots = this.slots;
960		for (var i = 0, n = slots.length; i < n; i++) {
961			if (slots[i].name == slotName) return slot[i];
962		}
963		return null;
964	},
965	/** @return -1 if the bone was not found. */
966	findSlotIndex: function (slotName) {
967		var slots = this.slots;
968		for (var i = 0, n = slots.length; i < n; i++)
969			if (slots[i].name == slotName) return i;
970		return -1;
971	},
972	/** @return May be null. */
973	findSkin: function (skinName) {
974		var skins = this.skins;
975		for (var i = 0, n = skins.length; i < n; i++)
976			if (skins[i].name == skinName) return skins[i];
977		return null;
978	},
979	/** @return May be null. */
980	findEvent: function (eventName) {
981		var events = this.events;
982		for (var i = 0, n = events.length; i < n; i++)
983			if (events[i].name == eventName) return events[i];
984		return null;
985	},
986	/** @return May be null. */
987	findAnimation: function (animationName) {
988		var animations = this.animations;
989		for (var i = 0, n = animations.length; i < n; i++)
990			if (animations[i].name == animationName) return animations[i];
991		return null;
992	},
993	/** @return May be null. */
994	findIkConstraint: function (ikConstraintName) {
995		var ikConstraints = this.ikConstraints;
996		for (var i = 0, n = ikConstraints.length; i < n; i++)
997			if (ikConstraints[i].name == ikConstraintName) return ikConstraints[i];
998		return null;
999	}
1000};
1001
1002spine.Skeleton = function (skeletonData) {
1003	this.data = skeletonData;
1004
1005	this.bones = [];
1006	for (var i = 0, n = skeletonData.bones.length; i < n; i++) {
1007		var boneData = skeletonData.bones[i];
1008		var parent = !boneData.parent ? null : this.bones[skeletonData.bones.indexOf(boneData.parent)];
1009		this.bones.push(new spine.Bone(boneData, this, parent));
1010	}
1011
1012	this.slots = [];
1013	this.drawOrder = [];
1014	for (var i = 0, n = skeletonData.slots.length; i < n; i++) {
1015		var slotData = skeletonData.slots[i];
1016		var bone = this.bones[skeletonData.bones.indexOf(slotData.boneData)];
1017		var slot = new spine.Slot(slotData, bone);
1018		this.slots.push(slot);
1019		this.drawOrder.push(slot);
1020	}
1021	
1022	this.ikConstraints = [];
1023	for (var i = 0, n = skeletonData.ikConstraints.length; i < n; i++)
1024		this.ikConstraints.push(new spine.IkConstraint(skeletonData.ikConstraints[i], this));
1025
1026	this.boneCache = [];
1027	this.updateCache();
1028};
1029spine.Skeleton.prototype = {
1030	x: 0, y: 0,
1031	skin: null,
1032	r: 1, g: 1, b: 1, a: 1,
1033	time: 0,
1034	flipX: false, flipY: false,
1035	/** Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed. */
1036	updateCache: function () {
1037		var ikConstraints = this.ikConstraints;
1038		var ikConstraintsCount = ikConstraints.length;
1039
1040		var arrayCount = ikConstraintsCount + 1;
1041		var boneCache = this.boneCache;
1042		if (boneCache.length > arrayCount) boneCache.length = arrayCount;
1043		for (var i = 0, n = boneCache.length; i < n; i++)
1044			boneCache[i].length = 0;
1045		while (boneCache.length < arrayCount)
1046			boneCache[boneCache.length] = [];
1047
1048		var nonIkBones = boneCache[0];
1049		var bones = this.bones;
1050
1051		outer:
1052		for (var i = 0, n = bones.length; i < n; i++) {
1053			var bone = bones[i];
1054			var current = bone;
1055			do {
1056				for (var ii = 0; ii < ikConstraintsCount; ii++) {
1057					var ikConstraint = ikConstraints[ii];
1058					var parent = ikConstraint.bones[0];
1059					var child= ikConstraint.bones[ikConstraint.bones.length - 1];
1060					while (true) {
1061						if (current == child) {
1062							boneCache[ii].push(bone);
1063							boneCache[ii + 1].push(bone);
1064							continue outer;
1065						}
1066						if (child == parent) break;
1067						child = child.parent;
1068					}
1069				}
1070				current = current.parent;
1071			} while (current);
1072			nonIkBones[nonIkBones.length] = bone;
1073		}
1074	},
1075	/** Updates the world transform for each bone. */
1076	updateWorldTransform: function () {
1077		var bones = this.bones;
1078		for (var i = 0, n = bones.length; i < n; i++) {
1079			var bone = bones[i];
1080			bone.rotationIK = bone.rotation;
1081		}
1082		var i = 0, last = this.boneCache.length - 1;
1083		while (true) {
1084			var cacheBones = this.boneCache[i];
1085			for (var ii = 0, nn = cacheBones.length; ii < nn; ii++)
1086				cacheBones[ii].updateWorldTransform();
1087			if (i == last) break;
1088			this.ikConstraints[i].apply();
1089			i++;
1090		}
1091	},
1092	/** Sets the bones and slots to their setup pose values. */
1093	setToSetupPose: function () {
1094		this.setBonesToSetupPose();
1095		this.setSlotsToSetupPose();
1096	},
1097	setBonesToSetupPose: function () {
1098		var bones = this.bones;
1099		for (var i = 0, n = bones.length; i < n; i++)
1100			bones[i].setToSetupPose();
1101
1102		var ikConstraints = this.ikConstraints;
1103		for (var i = 0, n = ikConstraints.length; i < n; i++) {
1104			var ikConstraint = ikConstraints[i];
1105			ikConstraint.bendDirection = ikConstraint.data.bendDirection;
1106			ikConstraint.mix = ikConstraint.data.mix;
1107		}
1108	},
1109	setSlotsToSetupPose: function () {
1110		var slots = this.slots;
1111		var drawOrder = this.drawOrder;
1112		for (var i = 0, n = slots.length; i < n; i++) {
1113			drawOrder[i] = slots[i];
1114			slots[i].setToSetupPose(i);
1115		}
1116	},
1117	/** @return May return null. */
1118	getRootBone: function () {
1119		return this.bones.length ? this.bones[0] : null;
1120	},
1121	/** @return May be null. */
1122	findBone: function (boneName) {
1123		var bones = this.bones;
1124		for (var i = 0, n = bones.length; i < n; i++)
vendor: 17,944 bytes, lines 1125-1691
1125			if (bones[i].data.name == boneName) return bones[i];
1126		return null;
1127	},
1128	/** @return -1 if the bone was not found. */
1129	findBoneIndex: function (boneName) {
1130		var bones = this.bones;
1131		for (var i = 0, n = bones.length; i < n; i++)
1132			if (bones[i].data.name == boneName) return i;
1133		return -1;
1134	},
1135	/** @return May be null. */
1136	findSlot: function (slotName) {
1137		var slots = this.slots;
1138		for (var i = 0, n = slots.length; i < n; i++)
1139			if (slots[i].data.name == slotName) return slots[i];
1140		return null;
1141	},
1142	/** @return -1 if the bone was not found. */
1143	findSlotIndex: function (slotName) {
1144		var slots = this.slots;
1145		for (var i = 0, n = slots.length; i < n; i++)
1146			if (slots[i].data.name == slotName) return i;
1147		return -1;
1148	},
1149	setSkinByName: function (skinName) {
1150		var skin = this.data.findSkin(skinName);
1151		if (!skin) throw "Skin not found: " + skinName;
1152		this.setSkin(skin);
1153	},
1154	/** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}. 
1155	 * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was 
1156	 * no old skin, each slot's setup mode attachment is attached from the new skin.
1157	 * @param newSkin May be null. */
1158	setSkin: function (newSkin) {
1159		if (newSkin) {
1160			if (this.skin)
1161				newSkin._attachAll(this, this.skin);
1162			else {
1163				var slots = this.slots;
1164				for (var i = 0, n = slots.length; i < n; i++) {
1165					var slot = slots[i];
1166					var name = slot.data.attachmentName;
1167					if (name) {
1168						var attachment = newSkin.getAttachment(i, name);
1169						if (attachment) slot.setAttachment(attachment);
1170					}
1171				}
1172			}
1173		}
1174		this.skin = newSkin;
1175	},
1176	/** @return May be null. */
1177	getAttachmentBySlotName: function (slotName, attachmentName) {
1178		return this.getAttachmentBySlotIndex(this.data.findSlotIndex(slotName), attachmentName);
1179	},
1180	/** @return May be null. */
1181	getAttachmentBySlotIndex: function (slotIndex, attachmentName) {
1182		if (this.skin) {
1183			var attachment = this.skin.getAttachment(slotIndex, attachmentName);
1184			if (attachment) return attachment;
1185		}
1186		if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName);
1187		return null;
1188	},
1189	/** @param attachmentName May be null. */
1190	setAttachment: function (slotName, attachmentName) {
1191		var slots = this.slots;
1192		for (var i = 0, n = slots.length; i < n; i++) {
1193			var slot = slots[i];
1194			if (slot.data.name == slotName) {
1195				var attachment = null;
1196				if (attachmentName) {
1197					attachment = this.getAttachmentBySlotIndex(i, attachmentName);
1198					if (!attachment) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName;
1199				}
1200				slot.setAttachment(attachment);
1201				return;
1202			}
1203		}
1204		throw "Slot not found: " + slotName;
1205	},
1206	/** @return May be null. */
1207	findIkConstraint: function (ikConstraintName) {
1208		var ikConstraints = this.ikConstraints;
1209		for (var i = 0, n = ikConstraints.length; i < n; i++)
1210			if (ikConstraints[i].data.name == ikConstraintName) return ikConstraints[i];
1211		return null;
1212	},
1213	update: function (delta) {
1214		this.time += delta;
1215	}
1216};
1217
1218spine.EventData = function (name) {
1219	this.name = name;
1220};
1221spine.EventData.prototype = {
1222	intValue: 0,
1223	floatValue: 0,
1224	stringValue: null
1225};
1226
1227spine.Event = function (data) {
1228	this.data = data;
1229};
1230spine.Event.prototype = {
1231	intValue: 0,
1232	floatValue: 0,
1233	stringValue: null
1234};
1235
1236spine.AttachmentType = {
1237	region: 0,
1238	boundingbox: 1,
1239	mesh: 2,
1240	skinnedmesh: 3
1241};
1242
1243spine.RegionAttachment = function (name) {
1244	this.name = name;
1245	this.offset = [];
1246	this.offset.length = 8;
1247	this.uvs = [];
1248	this.uvs.length = 8;
1249};
1250spine.RegionAttachment.prototype = {
1251	type: spine.AttachmentType.region,
1252	x: 0, y: 0,
1253	rotation: 0,
1254	scaleX: 1, scaleY: 1,
1255	width: 0, height: 0,
1256	r: 1, g: 1, b: 1, a: 1,
1257	path: null,
1258	rendererObject: null,
1259	regionOffsetX: 0, regionOffsetY: 0,
1260	regionWidth: 0, regionHeight: 0,
1261	regionOriginalWidth: 0, regionOriginalHeight: 0,
1262	setUVs: function (u, v, u2, v2, rotate) {
1263		var uvs = this.uvs;
1264		if (rotate) {
1265			uvs[2/*X2*/] = u;
1266			uvs[3/*Y2*/] = v2;
1267			uvs[4/*X3*/] = u;
1268			uvs[5/*Y3*/] = v;
1269			uvs[6/*X4*/] = u2;
1270			uvs[7/*Y4*/] = v;
1271			uvs[0/*X1*/] = u2;
1272			uvs[1/*Y1*/] = v2;
1273		} else {
1274			uvs[0/*X1*/] = u;
1275			uvs[1/*Y1*/] = v2;
1276			uvs[2/*X2*/] = u;
1277			uvs[3/*Y2*/] = v;
1278			uvs[4/*X3*/] = u2;
1279			uvs[5/*Y3*/] = v;
1280			uvs[6/*X4*/] = u2;
1281			uvs[7/*Y4*/] = v2;
1282		}
1283	},
1284	updateOffset: function () {
1285		var regionScaleX = this.width / this.regionOriginalWidth * this.scaleX;
1286		var regionScaleY = this.height / this.regionOriginalHeight * this.scaleY;
1287		var localX = -this.width / 2 * this.scaleX + this.regionOffsetX * regionScaleX;
1288		var localY = -this.height / 2 * this.scaleY + this.regionOffsetY * regionScaleY;
1289		var localX2 = localX + this.regionWidth * regionScaleX;
1290		var localY2 = localY + this.regionHeight * regionScaleY;
1291
1292        var regionScaleX = 1;
1293        var regionScaleY = 1;
1294        var localX = 0;
1295        var localY = 0;
1296        var localX2 = 0;
1297        var localY2 = 0;
1298
1299		var radians = this.rotation * spine.degRad;
1300		var cos = Math.cos(radians);
1301		var sin = Math.sin(radians);
1302		var localXCos = localX * cos + this.x;
1303		var localXSin = localX * sin;
1304		var localYCos = localY * cos + this.y;
1305		var localYSin = localY * sin;
1306		var localX2Cos = localX2 * cos + this.x;
1307		var localX2Sin = localX2 * sin;
1308		var localY2Cos = localY2 * cos + this.y;
1309		var localY2Sin = localY2 * sin;
1310		var offset = this.offset;
1311		offset[0/*X1*/] = localXCos - localYSin;
1312		offset[1/*Y1*/] = localYCos + localXSin;
1313		offset[2/*X2*/] = localXCos - localY2Sin;
1314		offset[3/*Y2*/] = localY2Cos + localXSin;
1315		offset[4/*X3*/] = localX2Cos - localY2Sin;
1316		offset[5/*Y3*/] = localY2Cos + localX2Sin;
1317		offset[6/*X4*/] = localX2Cos - localYSin;
1318		offset[7/*Y4*/] = localYCos + localX2Sin;
1319	},
1320	computeVertices: function (x, y, bone, vertices) {
1321		x += bone.worldX;
1322		y += bone.worldY;
1323		var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11;
1324		var offset = this.offset;
1325		vertices[0/*X1*/] = offset[0/*X1*/] * m00 + offset[1/*Y1*/] * m01 + x;
1326		vertices[1/*Y1*/] = offset[0/*X1*/] * m10 + offset[1/*Y1*/] * m11 + y;
1327		vertices[2/*X2*/] = offset[2/*X2*/] * m00 + offset[3/*Y2*/] * m01 + x;
1328		vertices[3/*Y2*/] = offset[2/*X2*/] * m10 + offset[3/*Y2*/] * m11 + y;
1329		vertices[4/*X3*/] = offset[4/*X3*/] * m00 + offset[5/*X3*/] * m01 + x;
1330		vertices[5/*X3*/] = offset[4/*X3*/] * m10 + offset[5/*X3*/] * m11 + y;
1331		vertices[6/*X4*/] = offset[6/*X4*/] * m00 + offset[7/*Y4*/] * m01 + x;
1332		vertices[7/*Y4*/] = offset[6/*X4*/] * m10 + offset[7/*Y4*/] * m11 + y;
1333	}
1334};
1335
1336spine.MeshAttachment = function (name) {
1337	this.name = name;
1338};
1339spine.MeshAttachment.prototype = {
1340	type: spine.AttachmentType.mesh,
1341	vertices: null,
1342	uvs: null,
1343	regionUVs: null,
1344	triangles: null,
1345	hullLength: 0,
1346	r: 1, g: 1, b: 1, a: 1,
1347	path: null,
1348	rendererObject: null,
1349	regionU: 0, regionV: 0, regionU2: 0, regionV2: 0, regionRotate: false,
1350	regionOffsetX: 0, regionOffsetY: 0,
1351	regionWidth: 0, regionHeight: 0,
1352	regionOriginalWidth: 0, regionOriginalHeight: 0,
1353	edges: null,
1354	width: 0, height: 0,
1355	updateUVs: function () {
1356		var width = this.regionU2 - this.regionU, height = this.regionV2 - this.regionV;
1357		var n = this.regionUVs.length;
1358		if (!this.uvs || this.uvs.length != n) {
1359            this.uvs = new spine.Float32Array(n);
1360		}
1361		if (this.regionRotate) {
1362			for (var i = 0; i < n; i += 2) {
1363                this.uvs[i] = this.regionU + this.regionUVs[i + 1] * width;
1364                this.uvs[i + 1] = this.regionV + height - this.regionUVs[i] * height;
1365			}
1366		} else {
1367			for (var i = 0; i < n; i += 2) {
1368                this.uvs[i] = this.regionU + this.regionUVs[i] * width;
1369                this.uvs[i + 1] = this.regionV + this.regionUVs[i + 1] * height;
1370			}
1371		}
1372	},
1373	computeWorldVertices: function (x, y, slot, worldVertices) {
1374		var bone = slot.bone;
1375		x += bone.worldX;
1376		y += bone.worldY;
1377		var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11;
1378		var vertices = this.vertices;
1379		var verticesCount = vertices.length;
1380		if (slot.attachmentVertices.length == verticesCount) vertices = slot.attachmentVertices;
1381		for (var i = 0; i < verticesCount; i += 2) {
1382			var vx = vertices[i];
1383			var vy = vertices[i + 1];
1384			worldVertices[i] = vx * m00 + vy * m01 + x;
1385			worldVertices[i + 1] = vx * m10 + vy * m11 + y;
1386		}
1387	}
1388};
1389
1390spine.SkinnedMeshAttachment = function (name) {
1391	this.name = name;
1392};
1393spine.SkinnedMeshAttachment.prototype = {
1394	type: spine.AttachmentType.skinnedmesh,
1395	bones: null,
1396	weights: null,
1397	uvs: null,
1398	regionUVs: null,
1399	triangles: null,
1400	hullLength: 0,
1401	r: 1, g: 1, b: 1, a: 1,
1402	path: null,
1403	rendererObject: null,
1404	regionU: 0, regionV: 0, regionU2: 0, regionV2: 0, regionRotate: false,
1405	regionOffsetX: 0, regionOffsetY: 0,
1406	regionWidth: 0, regionHeight: 0,
1407	regionOriginalWidth: 0, regionOriginalHeight: 0,
1408	edges: null,
1409	width: 0, height: 0,
1410	updateUVs: function (u, v, u2, v2, rotate) {
1411		var width = this.regionU2 - this.regionU, height = this.regionV2 - this.regionV;
1412		var n = this.regionUVs.length;
1413		if (!this.uvs || this.uvs.length != n) {
1414            this.uvs = new spine.Float32Array(n);
1415		}
1416		if (this.regionRotate) {
1417			for (var i = 0; i < n; i += 2) {
1418                this.uvs[i] = this.regionU + this.regionUVs[i + 1] * width;
1419                this.uvs[i + 1] = this.regionV + height - this.regionUVs[i] * height;
1420			}
1421		} else {
1422			for (var i = 0; i < n; i += 2) {
1423                this.uvs[i] = this.regionU + this.regionUVs[i] * width;
1424                this.uvs[i + 1] = this.regionV + this.regionUVs[i + 1] * height;
1425			}
1426		}
1427	},
1428	computeWorldVertices: function (x, y, slot, worldVertices) {
1429		var skeletonBones = slot.bone.skeleton.bones;
1430		var weights = this.weights;
1431		var bones = this.bones;
1432
1433		var w = 0, v = 0, b = 0, f = 0, n = bones.length, nn;
1434		var wx, wy, bone, vx, vy, weight;
1435		if (!slot.attachmentVertices.length) {
1436			for (; v < n; w += 2) {
1437				wx = 0;
1438				wy = 0;
1439				nn = bones[v++] + v;
1440				for (; v < nn; v++, b += 3) {
1441					bone = skeletonBones[bones[v]];
1442					vx = weights[b];
1443					vy = weights[b + 1];
1444					weight = weights[b + 2];
1445					wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight;
1446					wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight;
1447				}
1448				worldVertices[w] = wx + x;
1449				worldVertices[w + 1] = wy + y;
1450			}
1451		} else {
1452			var ffd = slot.attachmentVertices;
1453			for (; v < n; w += 2) {
1454				wx = 0;
1455				wy = 0;
1456				nn = bones[v++] + v;
1457				for (; v < nn; v++, b += 3, f += 2) {
1458					bone = skeletonBones[bones[v]];
1459					vx = weights[b] + ffd[f];
1460					vy = weights[b + 1] + ffd[f + 1];
1461					weight = weights[b + 2];
1462					wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight;
1463					wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight;
1464				}
1465				worldVertices[w] = wx + x;
1466				worldVertices[w + 1] = wy + y;
1467			}
1468		}
1469	}
1470};
1471
1472spine.BoundingBoxAttachment = function (name) {
1473	this.name = name;
1474	this.vertices = [];
1475};
1476spine.BoundingBoxAttachment.prototype = {
1477	type: spine.AttachmentType.boundingbox,
1478	computeWorldVertices: function (x, y, bone, worldVertices) {
1479		x += bone.worldX;
1480		y += bone.worldY;
1481		var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11;
1482		var vertices = this.vertices;
1483		for (var i = 0, n = vertices.length; i < n; i += 2) {
1484			var px = vertices[i];
1485			var py = vertices[i + 1];
1486			worldVertices[i] = px * m00 + py * m01 + x;
1487			worldVertices[i + 1] = px * m10 + py * m11 + y;
1488		}
1489	}
1490};
1491
1492spine.AnimationStateData = function (skeletonData) {
1493	this.skeletonData = skeletonData;
1494	this.animationToMixTime = {};
1495};
1496spine.AnimationStateData.prototype = {
1497	defaultMix: 0,
1498	setMixByName: function (fromName, toName, duration) {
1499		var from = this.skeletonData.findAnimation(fromName);
1500		if (!from) throw "Animation not found: " + fromName;
1501		var to = this.skeletonData.findAnimation(toName);
1502		if (!to) throw "Animation not found: " + toName;
1503		this.setMix(from, to, duration);
1504	},
1505	setMix: function (from, to, duration) {
1506		this.animationToMixTime[from.name + ":" + to.name] = duration;
1507	},
1508	getMix: function (from, to) {
1509		var key = from.name + ":" + to.name;
1510		return this.animationToMixTime.hasOwnProperty(key) ? this.animationToMixTime[key] : this.defaultMix;
1511	}
1512};
1513
1514spine.TrackEntry = function () {};
1515spine.TrackEntry.prototype = {
1516	next: null, previous: null,
1517	animation: null,
1518	loop: false,
1519	delay: 0, time: 0, lastTime: -1, endTime: 0,
1520	timeScale: 1,
1521	mixTime: 0, mixDuration: 0, mix: 1,
1522	onStart: null, onEnd: null, onComplete: null, onEvent: null
1523};
1524
1525spine.AnimationState = function (stateData) {
1526	this.data = stateData;
1527	this.tracks = [];
1528	this.events = [];
1529};
1530spine.AnimationState.prototype = {
1531	onStart: null,
1532	onEnd: null,
1533	onComplete: null,
1534	onEvent: null,
1535	timeScale: 1,
1536	update: function (delta) {
1537		delta *= this.timeScale;
1538		for (var i = 0; i < this.tracks.length; i++) {
1539			var current = this.tracks[i];
1540			if (!current) continue;
1541
1542			current.time += delta * current.timeScale;
1543			if (current.previous) {
1544				var previousDelta = delta * current.previous.timeScale;
1545				current.previous.time += previousDelta;
1546				current.mixTime += previousDelta;
1547			}
1548
1549			var next = current.next;
1550			if (next) {
1551				next.time = current.lastTime - next.delay;
1552				if (next.time >= 0) this.setCurrent(i, next);
1553			} else {
1554				// End non-looping animation when it reaches its end time and there is no next entry.
1555				if (!current.loop && current.lastTime >= current.endTime) this.clearTrack(i);
1556			}
1557		}
1558	},
1559	apply: function (skeleton) {
1560		for (var i = 0; i < this.tracks.length; i++) {
1561			var current = this.tracks[i];
1562			if (!current) continue;
1563
1564			this.events.length = 0;
1565
1566			var time = current.time;
1567			var lastTime = current.lastTime;
1568			var endTime = current.endTime;
1569			var loop = current.loop;
1570			if (!loop && time > endTime) time = endTime;
1571
1572			var previous = current.previous;
1573			if (!previous) {
1574				if (current.mix == 1)
1575					current.animation.apply(skeleton, current.lastTime, time, loop, this.events);
1576				else
1577					current.animation.mix(skeleton, current.lastTime, time, loop, this.events, current.mix);
1578			} else {
1579				var previousTime = previous.time;
1580				if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime;
1581				previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null);
1582
1583				var alpha = current.mixTime / current.mixDuration * current.mix;
1584				if (alpha >= 1) {
1585					alpha = 1;
1586					current.previous = null;
1587				}
1588				current.animation.mix(skeleton, current.lastTime, time, loop, this.events, alpha);
1589			}
1590
1591			for (var ii = 0, nn = this.events.length; ii < nn; ii++) {
1592				var event = this.events[ii];
1593				if (current.onEvent) current.onEvent(i, event);
1594				if (this.onEvent) this.onEvent(i, event);
1595			}
1596
1597			// Check if completed the animation or a loop iteration.
1598			if (loop ? (lastTime % endTime > time % endTime) : (lastTime < endTime && time >= endTime)) {
1599				var count = Math.floor(time / endTime);
1600				if (current.onComplete) current.onComplete(i, count);
1601				if (this.onComplete) this.onComplete(i, count);
1602			}
1603
1604			current.lastTime = current.time;
1605		}
1606	},
1607	clearTracks: function () {
1608		for (var i = 0, n = this.tracks.length; i < n; i++)
1609			this.clearTrack(i);
1610		this.tracks.length = 0; 
1611	},
1612	clearTrack: function (trackIndex) {
1613		if (trackIndex >= this.tracks.length) return;
1614		var current = this.tracks[trackIndex];
1615		if (!current) return;
1616
1617		if (current.onEnd) current.onEnd(trackIndex);
1618		if (this.onEnd) this.onEnd(trackIndex);
1619
1620		this.tracks[trackIndex] = null;
1621	},
1622	_expandToIndex: function (index) {
1623		if (index < this.tracks.length) return this.tracks[index];
1624		while (index >= this.tracks.length)
1625			this.tracks.push(null);
1626		return null;
1627	},
1628	setCurrent: function (index, entry) {
1629		var current = this._expandToIndex(index);
1630		if (current) {
1631			var previous = current.previous;
1632			current.previous = null;
1633
1634			if (current.onEnd) current.onEnd(index);
1635			if (this.onEnd) this.onEnd(index);
1636
1637			entry.mixDuration = this.data.getMix(current.animation, entry.animation);
1638			if (entry.mixDuration > 0) {
1639				entry.mixTime = 0;
1640				// If a mix is in progress, mix from the closest animation.
1641				if (previous && current.mixTime / current.mixDuration < 0.5)
1642					entry.previous = previous;
1643				else
1644					entry.previous = current;
1645			}
1646		}
1647
1648		this.tracks[index] = entry;
1649
1650		if (entry.onStart) entry.onStart(index);
1651		if (this.onStart) this.onStart(index);
1652	},
1653	setAnimationByName: function (trackIndex, animationName, loop) {
1654		var animation = this.data.skeletonData.findAnimation(animationName);
1655		if (!animation) throw "Animation not found: " + animationName;
1656		return this.setAnimation(trackIndex, animation, loop);
1657	},
1658	/** Set the current animation. Any queued animations are cleared. */
1659	setAnimation: function (trackIndex, animation, loop) {
1660		var entry = new spine.TrackEntry();
1661		entry.animation = animation;
1662		entry.loop = loop;
1663		entry.endTime = animation.duration;
1664		this.setCurrent(trackIndex, entry);
1665		return entry;
1666	},
1667	addAnimationByName: function (trackIndex, animationName, loop, delay) {
1668		var animation = this.data.skeletonData.findAnimation(animationName);
1669		if (!animation) throw "Animation not found: " + animationName;
1670		return this.addAnimation(trackIndex, animation, loop, delay);
1671	},
1672	/** Adds an animation to be played delay seconds after the current or last queued animation.
1673	 * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */
1674	addAnimation: function (trackIndex, animation, loop, delay) {
1675		var entry = new spine.TrackEntry();
1676		entry.animation = animation;
1677		entry.loop = loop;
1678		entry.endTime = animation.duration;
1679
1680		var last = this._expandToIndex(trackIndex);
1681		if (last) {
1682			while (last.next)
1683				last = last.next;
1684			last.next = entry;
1685		} else
1686			this.tracks[trackIndex] = entry;
1687
1688		if (delay <= 0) {
1689			if (last)
1690				delay += last.endTime - this.data.getMix(last.animation, animation);
1691			else
1692				delay = 0;
1693		}
1694		entry.delay = delay;
1695
1696		return entry;
1697	},
1698	/** May be null. */
1699	getCurrent: function (trackIndex) {
1700		if (trackIndex >= this.tracks.length) return null;
1701		return this.tracks[trackIndex];
1702	}
1703};
1704
1705spine.SkeletonJson = function (attachmentLoader) {
1706	this.attachmentLoader = attachmentLoader;
1707};
1708spine.SkeletonJson.prototype = {
1709	scale: 1,
1710	readSkeletonData: function (root, name) {
1711		var skeletonData = new spine.SkeletonData();
1712		skeletonData.name = name;
1713
1714		// Skeleton.
1715		var skeletonMap = root["skeleton"];
1716		if (skeletonMap) {
1717			skeletonData.hash = skeletonMap["hash"];
1718			skeletonData.version = skeletonMap["spine"];
1719			skeletonData.width = skeletonMap["width"] || 0;
1720			skeletonData.height = skeletonMap["height"] || 0;
1721		}
1722
1723		// Bones.
1724		var bones = root["bones"];
1725		for (var i = 0, n = bones.length; i < n; i++) {
1726			var boneMap = bones[i];
1727			var parent = null;
1728			if (boneMap["parent"]) {
1729				parent = skeletonData.findBone(boneMap["parent"]);
1730				if (!parent) throw "Parent bone not found: " + boneMap["parent"];
1731			}
1732			var boneData = new spine.BoneData(boneMap["name"], parent);
1733			boneData.length = (boneMap["length"] || 0) * this.scale;
1734			boneData.x = (boneMap["x"] || 0) * this.scale;
1735			boneData.y = (boneMap["y"] || 0) * this.scale;
1736			boneData.rotation = (boneMap["rotation"] || 0);
1737			boneData.scaleX = boneMap.hasOwnProperty("scaleX") ? boneMap["scaleX"] : 1;
1738			boneData.scaleY = boneMap.hasOwnProperty("scaleY") ? boneMap["scaleY"] : 1;
1739			boneData.inheritScale = boneMap.hasOwnProperty("inheritScale") ? boneMap["inheritScale"] : true;
1740			boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true;
1741			skeletonData.bones.push(boneData);
1742		}
1743
1744		// IK constraints.
1745		var ik = root["ik"];
1746		if (ik) {
1747			for (var i = 0, n = ik.length; i < n; i++) {
1748				var ikMap = ik[i];
1749				var ikConstraintData = new spine.IkConstraintData(ikMap["name"]);
1750
1751				var bones = ikMap["bones"];
1752				for (var ii = 0, nn = bones.length; ii < nn; ii++) {
1753					var bone = skeletonData.findBone(bones[ii]);
1754					if (!bone) throw "IK bone not found: " + bones[ii];
1755					ikConstraintData.bones.push(bone);
1756				}
1757
1758				ikConstraintData.target = skeletonData.findBone(ikMap["target"]);
1759				if (!ikConstraintData.target) throw "Target bone not found: " + ikMap["target"];
1760
1761				ikConstraintData.bendDirection = (!ikMap.hasOwnProperty("bendPositive") || ikMap["bendPositive"]) ? 1 : -1;
1762				ikConstraintData.mix = ikMap.hasOwnProperty("mix") ? ikMap["mix"] : 1;
1763
1764				skeletonData.ikConstraints.push(ikConstraintData);
1765			}
1766		}
1767
1768		// Slots.
1769		var slots = root["slots"];
1770		for (var i = 0, n = slots.length; i < n; i++) {
1771			var slotMap = slots[i];
1772			var boneData = skeletonData.findBone(slotMap["bone"]);
1773			if (!boneData) throw "Slot bone not found: " + slotMap["bone"];
1774			var slotData = new spine.SlotData(slotMap["name"], boneData);
1775
1776			var color = slotMap["color"];
1777			if (color) {
1778				slotData.r = this.toColor(color, 0);
1779				slotData.g = this.toColor(color, 1);
1780				slotData.b = this.toColor(color, 2);
1781				slotData.a = this.toColor(color, 3);
1782			}
1783
1784			slotData.attachmentName = slotMap["attachment"];
1785			slotData.blendMode = spine.BlendMode[slotMap["blend"] || "normal"];
1786
1787			skeletonData.slots.push(slotData);
1788		}
1789
1790		// Skins.
1791		var skins = root["skins"];
1792		for (var skinName in skins) {
1793			if (!skins.hasOwnProperty(skinName)) continue;
1794			var skinMap = skins[skinName];
1795			var skin = new spine.Skin(skinName);
1796			for (var slotName in skinMap) {
1797				if (!skinMap.hasOwnProperty(slotName)) continue;
1798				var slotIndex = skeletonData.findSlotIndex(slotName);
1799				var slotEntry = skinMap[slotName];
1800				for (var attachmentName in slotEntry) {
1801					if (!slotEntry.hasOwnProperty(attachmentName)) continue;
1802					var attachment = this.readAttachment(skin, attachmentName, slotEntry[attachmentName]);
1803					if (attachment) skin.addAttachment(slotIndex, attachmentName, attachment);
1804				}
1805			}
1806			skeletonData.skins.push(skin);
1807			if (skin.name == "default") skeletonData.defaultSkin = skin;
1808		}
1809
1810		// Events.
1811		var events = root["events"];
1812		for (var eventName in events) {
1813			if (!events.hasOwnProperty(eventName)) continue;
1814			var eventMap = events[eventName];
1815			var eventData = new spine.EventData(eventName);
1816			eventData.intValue = eventMap["int"] || 0;
1817			eventData.floatValue = eventMap["float"] || 0;
1818			eventData.stringValue = eventMap["string"] || null;
1819			skeletonData.events.push(eventData);
1820		}
1821
1822		// Animations.
1823		var animations = root["animations"];
1824		for (var animationName in animations) {
1825			if (!animations.hasOwnProperty(animationName)) continue;
1826			this.readAnimation(animationName, animations[animationName], skeletonData);
1827		}
1828
1829		return skeletonData;
1830	},
1831	readAttachment: function (skin, name, map) {
1832		name = map["name"] || name;
1833
1834		var type = spine.AttachmentType[map["type"] || "region"];
1835		var path = map["path"] || name;
1836		
1837		var scale = this.scale;
1838		if (type == spine.AttachmentType.region) {
1839			var region = this.attachmentLoader.newRegionAttachment(skin, name, path);
1840			if (!region) return null;
1841			region.path = path;
1842			region.x = (map["x"] || 0) * scale;
1843			region.y = (map["y"] || 0) * scale;
1844			region.scaleX = map.hasOwnProperty("scaleX") ? map["scaleX"] : 1;
1845			region.scaleY = map.hasOwnProperty("scaleY") ? map["scaleY"] : 1;
1846			region.rotation = map["rotation"] || 0;
1847			region.width = (map["width"] || 0) * scale;
1848			region.height = (map["height"] || 0) * scale;
1849
1850			var color = map["color"];
1851			if (color) {
1852				region.r = this.toColor(color, 0);
1853				region.g = this.toColor(color, 1);
1854				region.b = this.toColor(color, 2);
1855				region.a = this.toColor(color, 3);
1856			}
1857
1858			region.updateOffset();
1859			return region;
1860		} else if (type == spine.AttachmentType.mesh) {
1861			var mesh = this.attachmentLoader.newMeshAttachment(skin, name, path);
1862			if (!mesh) return null;
1863			mesh.path = path; 
1864			mesh.vertices = this.getFloatArray(map, "vertices", scale);
1865			mesh.triangles = this.getIntArray(map, "triangles");
1866			mesh.regionUVs = this.getFloatArray(map, "uvs", 1);
1867			mesh.updateUVs();
1868
1869			color = map["color"];
1870			if (color) {
1871				mesh.r = this.toColor(color, 0);
1872				mesh.g = this.toColor(color, 1);
1873				mesh.b = this.toColor(color, 2);
1874				mesh.a = this.toColor(color, 3);
1875			}
1876
1877			mesh.hullLength = (map["hull"] || 0) * 2;
1878			if (map["edges"]) mesh.edges = this.getIntArray(map, "edges");
1879			mesh.width = (map["width"] || 0) * scale;
1880			mesh.height = (map["height"] || 0) * scale;
1881			return mesh;
1882		} else if (type == spine.AttachmentType.skinnedmesh) {
1883			var mesh = this.attachmentLoader.newSkinnedMeshAttachment(skin, name, path);
1884			if (!mesh) return null;
1885			mesh.path = path;
1886
1887			var uvs = this.getFloatArray(map, "uvs", 1);
1888			var vertices = this.getFloatArray(map, "vertices", 1);
1889			var weights = [];
1890			var bones = [];
1891			for (var i = 0, n = vertices.length; i < n; ) {
1892				var boneCount = vertices[i++] | 0;
1893				bones[bones.length] = boneCount;
1894				for (var nn = i + boneCount * 4; i < nn; ) {
1895					bones[bones.length] = vertices[i];
1896					weights[weights.length] = vertices[i + 1] * scale;
1897					weights[weights.length] = vertices[i + 2] * scale;
1898					weights[weights.length] = vertices[i + 3];
1899					i += 4;
1900				}
1901			}
1902			mesh.bones = bones;
1903			mesh.weights = weights;
1904			mesh.triangles = this.getIntArray(map, "triangles");
1905			mesh.regionUVs = uvs;
1906			mesh.updateUVs();
1907			
1908			color = map["color"];
1909			if (color) {
1910				mesh.r = this.toColor(color, 0);
1911				mesh.g = this.toColor(color, 1);
1912				mesh.b = this.toColor(color, 2);
1913				mesh.a = this.toColor(color, 3);
1914			}
1915			
1916			mesh.hullLength = (map["hull"] || 0) * 2;
1917			if (map["edges"]) mesh.edges = this.getIntArray(map, "edges");
1918			mesh.width = (map["width"] || 0) * scale;
1919			mesh.height = (map["height"] || 0) * scale;
1920			return mesh;
1921		} else if (type == spine.AttachmentType.boundingbox) {
1922			var attachment = this.attachmentLoader.newBoundingBoxAttachment(skin, name);
1923			var vertices = map["vertices"];
1924			for (var i = 0, n = vertices.length; i < n; i++)
1925				attachment.vertices.push(vertices[i] * scale);
1926			return attachment;
1927		}
1928		throw "Unknown attachment type: " + type;
1929	},
1930	readAnimation: function (name, map, skeletonData) {
1931		var timelines = [];
1932		var duration = 0;
1933
1934		var slots = map["slots"];
1935		for (var slotName in slots) {
1936			if (!slots.hasOwnProperty(slotName)) continue;
1937			var slotMap = slots[slotName];
1938			var slotIndex = skeletonData.findSlotIndex(slotName);
1939
1940			for (var timelineName in slotMap) {
1941				if (!slotMap.hasOwnProperty(timelineName)) continue;
1942				var values = slotMap[timelineName];
1943				if (timelineName == "color") {
1944					var timeline = new spine.ColorTimeline(values.length);
1945					timeline.slotIndex = slotIndex;
1946
1947					var frameIndex = 0;
1948					for (var i = 0, n = values.length; i < n; i++) {
1949						var valueMap = values[i];
1950						var color = valueMap["color"];
1951						var r = this.toColor(color, 0);
1952						var g = this.toColor(color, 1);
1953						var b = this.toColor(color, 2);
1954						var a = this.toColor(color, 3);
1955						timeline.setFrame(frameIndex, valueMap["time"], r, g, b, a);
1956						this.readCurve(timeline, frameIndex, valueMap);
1957						frameIndex++;
1958					}
1959					timelines.push(timeline);
1960					duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 5 - 5]);
1961
1962				} else if (timelineName == "attachment") {
1963					var timeline = new spine.AttachmentTimeline(values.length);
1964					timeline.slotIndex = slotIndex;
1965
1966					var frameIndex = 0;
1967					for (var i = 0, n = values.length; i < n; i++) {
1968						var valueMap = values[i];
1969						timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]);
1970					}
1971					timelines.push(timeline);
1972					duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
1973
1974				} else
1975					throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")";
1976			}
1977		}
1978
1979		var bones = map["bones"];
1980		for (var boneName in bones) {
1981			if (!bones.hasOwnProperty(boneName)) continue;
1982			var boneIndex = skeletonData.findBoneIndex(boneName);
1983			if (boneIndex == -1) throw "Bone not found: " + boneName;
1984			var boneMap = bones[boneName];
1985
1986			for (var timelineName in boneMap) {
1987				if (!boneMap.hasOwnProperty(timelineName)) continue;
1988				var values = boneMap[timelineName];
1989				if (timelineName == "rotate") {
1990					var timeline = new spine.RotateTimeline(values.length);
1991					timeline.boneIndex = boneIndex;
1992
1993					var frameIndex = 0;
1994					for (var i = 0, n = values.length; i < n; i++) {
1995						var valueMap = values[i];
1996						timeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]);
1997						this.readCurve(timeline, frameIndex, valueMap);
1998						frameIndex++;
1999					}
2000					timelines.push(timeline);
2001					duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]);
2002
2003				} else if (timelineName == "translate" || timelineName == "scale") {
2004					var timeline;
2005					var timelineScale = 1;
2006					if (timelineName == "scale")
2007						timeline = new spine.ScaleTimeline(values.length);
2008					else {
2009						timeline = new spine.TranslateTimeline(values.length);
2010						timelineScale = this.scale;
2011					}
2012					timeline.boneIndex = boneIndex;
2013
2014					var frameIndex = 0;
2015					for (var i = 0, n = values.length; i < n; i++) {
2016						var valueMap = values[i];
2017						var x = (valueMap["x"] || 0) * timelineScale;
2018						var y = (valueMap["y"] || 0) * timelineScale;
2019						timeline.setFrame(frameIndex, valueMap["time"], x, y);
2020						this.readCurve(timeline, frameIndex, valueMap);
2021						frameIndex++;
2022					}
2023					timelines.push(timeline);
2024					duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]);
2025
2026				} else if (timelineName == "flipX" || timelineName == "flipY") {
2027					var x = timelineName == "flipX";
2028					var timeline = x ? new spine.FlipXTimeline(values.length) : new spine.FlipYTimeline(values.length);
2029					timeline.boneIndex = boneIndex;
2030
2031					var field = x ? "x" : "y";
2032					var frameIndex = 0;
2033					for (var i = 0, n = values.length; i < n; i++) {
2034						var valueMap = values[i];
2035						timeline.setFrame(frameIndex, valueMap["time"], valueMap[field] || false);
2036						frameIndex++;
2037					}
2038					timelines.push(timeline);
2039					duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]);
2040				} else
2041					throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")";
2042			}
2043		}
2044
2045		var ikMap = map["ik"];
2046		for (var ikConstraintName in ikMap) {
2047			if (!ikMap.hasOwnProperty(ikConstraintName)) continue;
2048			var ikConstraint = skeletonData.findIkConstraint(ikConstraintName);
2049			var values = ikMap[ikConstraintName];
2050			var timeline = new spine.IkConstraintTimeline(values.length);
2051			timeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint);
2052			var frameIndex = 0;
2053			for (var i = 0, n = values.length; i < n; i++) {
2054				var valueMap = values[i];
2055				var mix = valueMap.hasOwnProperty("mix") ? valueMap["mix"] : 1;
2056				var bendDirection = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1;
2057				timeline.setFrame(frameIndex, valueMap["time"], mix, bendDirection);
2058				this.readCurve(timeline, frameIndex, valueMap);
2059				frameIndex++;
2060			}
2061			timelines.push(timeline);
2062			duration = Math.max(duration, timeline.frames[timeline.frameCount * 3 - 3]);
2063		}
2064
2065		var ffd = map["ffd"];
2066		for (var skinName in ffd) {
2067			var skin = skeletonData.findSkin(skinName);
2068			var slotMap = ffd[skinName];
2069			for (slotName in slotMap) {
2070				var slotIndex = skeletonData.findSlotIndex(slotName);
2071				var meshMap = slotMap[slotName];
2072				for (var meshName in meshMap) {
2073					var values = meshMap[meshName];
2074					var timeline = new spine.FfdTimeline(values.length);
2075					var attachment = skin.getAttachment(slotIndex, meshName);
2076					if (!attachment) throw "FFD attachment not found: " + meshName;
2077					timeline.slotIndex = slotIndex;
2078					timeline.attachment = attachment;
2079					
2080					var isMesh = attachment.type == spine.AttachmentType.mesh;
2081					var vertexCount;
2082					if (isMesh)
2083						vertexCount = attachment.vertices.length;
2084					else
2085						vertexCount = attachment.weights.length / 3 * 2;
2086
2087					var frameIndex = 0;
2088					for (var i = 0, n = values.length; i < n; i++) {
2089						var valueMap = values[i];
2090						var vertices;
2091						if (!valueMap["vertices"]) {
2092							if (isMesh)
2093								vertices = attachment.vertices;
2094							else {
2095								vertices = [];
2096								vertices.length = vertexCount;
2097							}
2098						} else {
2099							var verticesValue = valueMap["vertices"];
2100							var vertices = [];
2101							vertices.length = vertexCount;
2102							var start = valueMap["offset"] || 0;
2103							var nn = verticesValue.length;
2104							if (this.scale == 1) {
2105								for (var ii = 0; ii < nn; ii++)
2106									vertices[ii + start] = verticesValue[ii];
2107							} else {
2108								for (var ii = 0; ii < nn; ii++)
2109									vertices[ii + start] = verticesValue[ii] * this.scale;
2110							}
2111							if (isMesh) {
2112								var meshVertices = attachment.vertices;
2113								for (var ii = 0, nn = vertices.length; ii < nn; ii++)
2114									vertices[ii] += meshVertices[ii];
2115							}
2116						}
2117						
2118						timeline.setFrame(frameIndex, valueMap["time"], vertices);
2119						this.readCurve(timeline, frameIndex, valueMap);
2120						frameIndex++;
2121					}
2122					timelines[timelines.length] = timeline;
2123					duration = Math.max(duration, timeline.frames[timeline.frameCount - 1]);
2124				}
2125			}
2126		}
2127
2128		var drawOrderValues = map["drawOrder"];
2129		if (!drawOrderValues) drawOrderValues = map["draworder"];
2130		if (drawOrderValues) {
2131			var timeline = new spine.DrawOrderTimeline(drawOrderValues.length);
2132			var slotCount = skeletonData.slots.length;
2133			var frameIndex = 0;
2134			for (var i = 0, n = drawOrderValues.length; i < n; i++) {
2135				var drawOrderMap = drawOrderValues[i];
2136				var drawOrder = null;
2137				if (drawOrderMap["offsets"]) {
2138					drawOrder = [];
2139					drawOrder.length = slotCount;
2140					for (var ii = slotCount - 1; ii >= 0; ii--)
2141						drawOrder[ii] = -1;
2142					var offsets = drawOrderMap["offsets"];
2143					var unchanged = [];
2144					unchanged.length = slotCount - offsets.length;
2145					var originalIndex = 0, unchangedIndex = 0;
2146					for (var ii = 0, nn = offsets.length; ii < nn; ii++) {
2147						var offsetMap = offsets[ii];
2148						var slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]);
2149						if (slotIndex == -1) throw "Slot not found: " + offsetMap["slot"];
2150						// Collect unchanged items.
2151						while (originalIndex != slotIndex)
2152							unchanged[unchangedIndex++] = originalIndex++;
2153						// Set changed items.
2154						drawOrder[originalIndex + offsetMap["offset"]] = originalIndex++;
2155					}
2156					// Collect remaining unchanged items.
2157					while (originalIndex < slotCount)
2158						unchanged[unchangedIndex++] = originalIndex++;
2159					// Fill in unchanged items.
2160					for (var ii = slotCount - 1; ii >= 0; ii--)
2161						if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
2162				}
2163				timeline.setFrame(frameIndex++, drawOrderMap["time"], drawOrder);
2164			}
2165			timelines.push(timeline);
2166			duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
2167		}
2168
2169		var events = map["events"];
2170		if (events) {
2171			var timeline = new spine.EventTimeline(events.length);
2172			var frameIndex = 0;
2173			for (var i = 0, n = events.length; i < n; i++) {
2174				var eventMap = events[i];
2175				var eventData = skeletonData.findEvent(eventMap["name"]);
2176				if (!eventData) throw "Event not found: " + eventMap["name"];
2177				var event = new spine.Event(eventData);
2178				event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue;
2179				event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue;
2180				event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue;
2181				timeline.setFrame(frameIndex++, eventMap["time"], event);
2182			}
2183			timelines.push(timeline);
2184			duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
2185		}
2186
2187		skeletonData.animations.push(new spine.Animation(name, timelines, duration));
2188	},
2189	readCurve: function (timeline, frameIndex, valueMap) {
2190		var curve = valueMap["curve"];
2191		if (!curve) 
2192			timeline.curves.setLinear(frameIndex);
2193		else if (curve == "stepped")
2194			timeline.curves.setStepped(frameIndex);
2195		else if (curve instanceof Array)
2196			timeline.curves.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]);
2197	},
2198	toColor: function (hexString, colorIndex) {
2199		if (hexString.length != 8) throw "Color hexidecimal length must be 8, recieved: " + hexString;
2200		return parseInt(hexString.substring(colorIndex * 2, (colorIndex * 2) + 2), 16) / 255;
2201	},
2202	getFloatArray: function (map, name, scale) {
2203		var list = map[name];
2204		var values = new spine.Float32Array(list.length);
2205		var i = 0, n = list.length;
2206		if (scale == 1) {
2207			for (; i < n; i++)
2208				values[i] = list[i];
2209		} else {
2210			for (; i < n; i++)
2211				values[i] = list[i] * scale;
2212		}
2213		return values;
2214	},
2215	getIntArray: function (map, name) {
2216		var list = map[name];
2217		var values = new spine.Uint16Array(list.length);
2218		for (var i = 0, n = list.length; i < n; i++)
2219			values[i] = list[i] | 0;
2220		return values;
2221	}
2222};
2223
2224spine.Atlas = function (atlasText, textureLoader) {
2225	this.textureLoader = textureLoader;
2226	this.pages = [];
2227	this.regions = [];
2228
2229	var reader = new spine.AtlasReader(atlasText);
2230	var tuple = [];
2231	tuple.length = 4;
2232	var page = null;
2233	while (true) {
2234		var line = reader.readLine();
2235		if (line === null) break;
2236		line = reader.trim(line);
2237		if (!line.length)
2238			page = null;
2239		else if (!page) {
2240			page = new spine.AtlasPage();
2241			page.name = line;
2242
2243			if (reader.readTuple(tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker.
2244				page.width = parseInt(tuple[0]);
2245				page.height = parseInt(tuple[1]);
2246				reader.readTuple(tuple);
2247			}
2248			page.format = spine.Atlas.Format[tuple[0]];
2249
2250			reader.readTuple(tuple);
2251			page.minFilter = spine.Atlas.TextureFilter[tuple[0]];
2252			page.magFilter = spine.Atlas.TextureFilter[tuple[1]];
2253
2254			var direction = reader.readValue();
2255			page.uWrap = spine.Atlas.TextureWrap.clampToEdge;
2256			page.vWrap = spine.Atlas.TextureWrap.clampToEdge;
2257			if (direction == "x")
2258				page.uWrap = spine.Atlas.TextureWrap.repeat;
2259			else if (direction == "y")
2260				page.vWrap = spine.Atlas.TextureWrap.repeat;
2261			else if (direction == "xy")
2262				page.uWrap = page.vWrap = spine.Atlas.TextureWrap.repeat;
2263
2264			textureLoader.load(page, line, this);
2265
2266			this.pages.push(page);
2267
2268		} else {
2269			var region = new spine.AtlasRegion();
2270			region.name = line;
2271			region.page = page;
2272
2273			region.rotate = reader.readValue() == "true";
2274
2275			reader.readTuple(tuple);
2276			var x = parseInt(tuple[0]);
2277			var y = parseInt(tuple[1]);
2278
2279			reader.readTuple(tuple);
2280			var width = parseInt(tuple[0]);
2281			var height = parseInt(tuple[1]);
2282
2283			region.u = x / page.width;
2284			region.v = y / page.height;
2285			if (region.rotate) {
2286				region.u2 = (x + height) / page.width;
2287				region.v2 = (y + width) / page.height;
2288			} else {
2289				region.u2 = (x + width) / page.width;
vendor: 9,474 bytes, lines 2290-2578
2290				region.v2 = (y + height) / page.height;
2291			}
2292			region.x = x;
2293			region.y = y;
2294			region.width = Math.abs(width);
2295			region.height = Math.abs(height);
2296
2297			if (reader.readTuple(tuple) == 4) { // split is optional
2298				region.splits = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])];
2299
2300				if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits
2301					region.pads = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])];
2302
2303					reader.readTuple(tuple);
2304				}
2305			}
2306
2307			region.originalWidth = parseInt(tuple[0]);
2308			region.originalHeight = parseInt(tuple[1]);
2309
2310			reader.readTuple(tuple);
2311			region.offsetX = parseInt(tuple[0]);
2312			region.offsetY = parseInt(tuple[1]);
2313
2314			region.index = parseInt(reader.readValue());
2315
2316			this.regions.push(region);
2317		}
2318	}
2319};
2320spine.Atlas.prototype = {
2321	findRegion: function (name) {
2322		var regions = this.regions;
2323		for (var i = 0, n = regions.length; i < n; i++)
2324			if (regions[i].name == name) return regions[i];
2325		return null;
2326	},
2327	dispose: function () {
2328		var pages = this.pages;
2329		for (var i = 0, n = pages.length; i < n; i++)
2330			this.textureLoader.unload(pages[i].rendererObject);
2331	},
2332	updateUVs: function (page) {
2333		var regions = this.regions;
2334		for (var i = 0, n = regions.length; i < n; i++) {
2335			var region = regions[i];
2336			if (region.page != page) continue;
2337			region.u = region.x / page.width;
2338			region.v = region.y / page.height;
2339			if (region.rotate) {
2340				region.u2 = (region.x + region.height) / page.width;
2341				region.v2 = (region.y + region.width) / page.height;
2342			} else {
2343				region.u2 = (region.x + region.width) / page.width;
2344				region.v2 = (region.y + region.height) / page.height;
2345			}
2346		}
2347	}
2348};
2349
2350spine.Atlas.Format = {
2351	alpha: 0,
2352	intensity: 1,
2353	luminanceAlpha: 2,
2354	rgb565: 3,
2355	rgba4444: 4,
2356	rgb888: 5,
2357	rgba8888: 6
2358};
2359
2360spine.Atlas.TextureFilter = {
2361	nearest: 0,
2362	linear: 1,
2363	mipMap: 2,
2364	mipMapNearestNearest: 3,
2365	mipMapLinearNearest: 4,
2366	mipMapNearestLinear: 5,
2367	mipMapLinearLinear: 6
2368};
2369
2370spine.Atlas.TextureWrap = {
2371	mirroredRepeat: 0,
2372	clampToEdge: 1,
2373	repeat: 2
2374};
2375
2376spine.AtlasPage = function () {};
2377spine.AtlasPage.prototype = {
2378	name: null,
2379	format: null,
2380	minFilter: null,
2381	magFilter: null,
2382	uWrap: null,
2383	vWrap: null,
2384	rendererObject: null,
2385	width: 0,
2386	height: 0
2387};
2388
2389spine.AtlasRegion = function () {};
2390spine.AtlasRegion.prototype = {
2391	page: null,
2392	name: null,
2393	x: 0, y: 0,
2394	width: 0, height: 0,
2395	u: 0, v: 0, u2: 0, v2: 0,
2396	offsetX: 0, offsetY: 0,
2397	originalWidth: 0, originalHeight: 0,
2398	index: 0,
2399	rotate: false,
2400	splits: null,
2401	pads: null
2402};
2403
2404spine.AtlasReader = function (text) {
2405	this.lines = text.split(/\r\n|\r|\n/);
2406};
2407spine.AtlasReader.prototype = {
2408	index: 0,
2409	trim: function (value) {
2410		return value.replace(/^\s+|\s+$/g, "");
2411	},
2412	readLine: function () {
2413		if (this.index >= this.lines.length) return null;
2414		return this.lines[this.index++];
2415	},
2416	readValue: function () {
2417		var line = this.readLine();
2418		var colon = line.indexOf(":");
2419		if (colon == -1) throw "Invalid line: " + line;
2420		return this.trim(line.substring(colon + 1));
2421	},
2422	/** Returns the number of tuple values read (1, 2 or 4). */
2423	readTuple: function (tuple) {
2424		var line = this.readLine();
2425		var colon = line.indexOf(":");
2426		if (colon == -1) throw "Invalid line: " + line;
2427		var i = 0, lastMatch = colon + 1;
2428		for (; i < 3; i++) {
2429			var comma = line.indexOf(",", lastMatch);
2430			if (comma == -1) break;
2431			tuple[i] = this.trim(line.substr(lastMatch, comma - lastMatch));
2432			lastMatch = comma + 1;
2433		}
2434		tuple[i] = this.trim(line.substring(lastMatch));
2435		return i + 1;
2436	}
2437};
2438
2439spine.AtlasAttachmentLoader = function (atlas) {
2440	this.atlas = atlas;
2441};
2442spine.AtlasAttachmentLoader.prototype = {
2443	newRegionAttachment: function (skin, name, path) {
2444		var region = this.atlas.findRegion(path);
2445		if (!region) throw "Region not found in atlas: " + path + " (region attachment: " + name + ")";
2446		var attachment = new spine.RegionAttachment(name);
2447		attachment.rendererObject = region;
2448		attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate);
2449		attachment.regionOffsetX = region.offsetX;
2450		attachment.regionOffsetY = region.offsetY;
2451		attachment.regionWidth = region.width;
2452		attachment.regionHeight = region.height;
2453		attachment.regionOriginalWidth = region.originalWidth;
2454		attachment.regionOriginalHeight = region.originalHeight;
2455		return attachment;
2456	},
2457	newMeshAttachment: function (skin, name, path) {
2458		var region = this.atlas.findRegion(path);
2459		if (!region) throw "Region not found in atlas: " + path + " (mesh attachment: " + name + ")";
2460		var attachment = new spine.MeshAttachment(name);
2461		attachment.rendererObject = region;
2462		attachment.regionU = region.u;
2463		attachment.regionV = region.v;
2464		attachment.regionU2 = region.u2;
2465		attachment.regionV2 = region.v2;
2466		attachment.regionRotate = region.rotate;
2467		attachment.regionOffsetX = region.offsetX;
2468		attachment.regionOffsetY = region.offsetY;
2469		attachment.regionWidth = region.width;
2470		attachment.regionHeight = region.height;
2471		attachment.regionOriginalWidth = region.originalWidth;
2472		attachment.regionOriginalHeight = region.originalHeight;
2473		return attachment;
2474	},
2475	newSkinnedMeshAttachment: function (skin, name, path) {
2476		var region = this.atlas.findRegion(path);
2477		if (!region) throw "Region not found in atlas: " + path + " (skinned mesh attachment: " + name + ")";
2478		var attachment = new spine.SkinnedMeshAttachment(name);
2479		attachment.rendererObject = region;
2480		attachment.regionU = region.u;
2481		attachment.regionV = region.v;
2482		attachment.regionU2 = region.u2;
2483		attachment.regionV2 = region.v2;
2484		attachment.regionRotate = region.rotate;
2485		attachment.regionOffsetX = region.offsetX;
2486		attachment.regionOffsetY = region.offsetY;
2487		attachment.regionWidth = region.width;
2488		attachment.regionHeight = region.height;
2489		attachment.regionOriginalWidth = region.originalWidth;
2490		attachment.regionOriginalHeight = region.originalHeight;
2491		return attachment;
2492	},
2493	newBoundingBoxAttachment: function (skin, name) {
2494		return new spine.BoundingBoxAttachment(name);
2495	}
2496};
2497spine.SkeletonBounds = function () {
2498    this.polygonPool = [];
2499    this.polygons = [];
2500    this.boundingBoxes = [];
2501};
2502spine.SkeletonBounds.prototype = {
2503    minX: 0, minY: 0, maxX: 0, maxY: 0,
2504    update: function (skeleton, updateAabb) {
2505        var slots = skeleton.slots;
2506        var slotCount = slots.length;
2507        var x = skeleton.x, y = skeleton.y;
2508        var boundingBoxes = this.boundingBoxes;
2509        var polygonPool = this.polygonPool;
2510        var polygons = this.polygons;
2511
2512        boundingBoxes.length = 0;
2513        for (var i = 0, n = polygons.length; i < n; i++)
2514            polygonPool.push(polygons[i]);
2515        polygons.length = 0;
2516
2517        for (var i = 0; i < slotCount; i++) {
2518            var slot = slots[i];
2519            var boundingBox = slot.attachment;
2520            if (!boundingBox || boundingBox.type != spine.AttachmentType.boundingbox) continue;
2521            boundingBoxes.push(boundingBox);
2522
2523            var poolCount = polygonPool.length, polygon;
2524            if (poolCount > 0) {
2525                polygon = polygonPool[poolCount - 1];
2526                polygonPool.splice(poolCount - 1, 1);
2527            } else
2528                polygon = [];
2529            polygons.push(polygon);
2530
2531            polygon.length = boundingBox.vertices.length;
2532            boundingBox.computeWorldVertices(x, y, slot.bone, polygon);
2533        }
2534
2535        if (updateAabb) this.aabbCompute();
2536    },
2537    aabbCompute: function () {
2538        var polygons = this.polygons;
2539        var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, maxX = Number.MIN_VALUE, maxY = Number.MIN_VALUE;
2540        for (var i = 0, n = polygons.length; i < n; i++) {
2541            var vertices = polygons[i];
2542            for (var ii = 0, nn = vertices.length; ii < nn; ii += 2) {
2543                var x = vertices[ii];
2544                var y = vertices[ii + 1];
2545                minX = Math.min(minX, x);
2546                minY = Math.min(minY, y);
2547                maxX = Math.max(maxX, x);
2548                maxY = Math.max(maxY, y);
2549            }
2550        }
2551        this.minX = minX;
2552        this.minY = minY;
2553        this.maxX = maxX;
2554        this.maxY = maxY;
2555    },
2556    /** Returns true if the axis aligned bounding box contains the point. */
2557    aabbContainsPoint: function (x, y) {
2558        return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY;
2559    },
2560    /** Returns true if the axis aligned bounding box intersects the line segment. */
2561    aabbIntersectsSegment: function (x1, y1, x2, y2) {
2562        var minX = this.minX, minY = this.minY, maxX = this.maxX, maxY = this.maxY;
2563        if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
2564            return false;
2565        var m = (y2 - y1) / (x2 - x1);
2566        var y = m * (minX - x1) + y1;
2567        if (y > minY && y < maxY) return true;
2568        y = m * (maxX - x1) + y1;
2569        if (y > minY && y < maxY) return true;
2570        var x = (minY - y1) / m + x1;
2571        if (x > minX && x < maxX) return true;
2572        x = (maxY - y1) / m + x1;
2573        if (x > minX && x < maxX) return true;
2574        return false;
2575    },
2576    /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */
2577    aabbIntersectsSkeleton: function (bounds) {
2578        return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY >
2578 bounds.minY;
2579    },
2580    /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
2581     * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */
2582    containsPoint: function (x, y) {
2583        var polygons = this.polygons;
2584        for (var i = 0, n = polygons.length; i < n; i++)
2585            if (this.polygonContainsPoint(polygons[i], x, y)) return this.boundingBoxes[i];
2586        return null;
2587    },
2588    /** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
2589     * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. */
2590    intersectsSegment: function (x1, y1, x2, y2) {
2591        var polygons = this.polygons;
2592        for (var i = 0, n = polygons.length; i < n; i++)
2593            if (polygons[i].intersectsSegment(x1, y1, x2, y2)) return this.boundingBoxes[i];
2594        return null;
2595    },
2596    /** Returns true if the polygon contains the point. */
2597    polygonContainsPoint: function (polygon, x, y) {
2598        var nn = polygon.length;
2599        var prevIndex = nn - 2;
2600        var inside = false;
vendor: 8,151 bytes, lines 2601-2786
2601        for (var ii = 0; ii < nn; ii += 2) {
2602            var vertexY = polygon[ii + 1];
2603            var prevY = polygon[prevIndex + 1];
2604            if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
2605                var vertexX = polygon[ii];
2606                if (vertexX + (y - vertexY) / (prevY - vertexY) * (polygon[prevIndex] - vertexX) < x) inside = !inside;
2607            }
2608            prevIndex = ii;
2609        }
2610        return inside;
2611    },
2612    /** Returns true if the polygon contains the line segment. */
2613    polygonIntersectsSegment: function (polygon, x1, y1, x2, y2) {
2614        var nn = polygon.length;
2615        var width12 = x1 - x2, height12 = y1 - y2;
2616        var det1 = x1 * y2 - y1 * x2;
2617        var x3 = polygon[nn - 2], y3 = polygon[nn - 1];
2618        for (var ii = 0; ii < nn; ii += 2) {
2619            var x4 = polygon[ii], y4 = polygon[ii + 1];
2620            var det2 = x3 * y4 - y3 * x4;
2621            var width34 = x3 - x4, height34 = y3 - y4;
2622            var det3 = width12 * height34 - height12 * width34;
2623            var x = (det1 * width34 - width12 * det2) / det3;
2624            if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
2625                var y = (det1 * height34 - height12 * det2) / det3;
2626                if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true;
2627            }
2628            x3 = x4;
2629            y3 = y4;
2630        }
2631        return false;
2632    },
2633    getPolygon: function (attachment) {
2634        var index = this.boundingBoxes.indexOf(attachment);
2635        return index == -1 ? null : this.polygons[index];
2636    },
2637    getWidth: function () {
2638        return this.maxX - this.minX;
2639    },
2640    getHeight: function () {
2641        return this.maxY - this.minY;
2642    }
2643};
2644
2645spine.SkeletonImageBounds = function () {
2646    this.polygonPool = [];
2647    this.polygons = [];
2648};
2649spine.SkeletonImageBounds.prototype = {
2650    minX: 0, minY: 0, maxX: 0, maxY: 0,
2651    update: function (skeleton, updateAabb) {
2652        var slots = skeleton.slots;
2653        var slotCount = slots.length;
2654        var x = skeleton.x, y = skeleton.y;
2655        var polygonPool = this.polygonPool;
2656        var polygons = this.polygons;
2657
2658        for (var i = 0, n = polygons.length; i < n; i++)
2659            polygonPool.push(polygons[i]);
2660        polygons.length = 0;
2661
2662        for (var i = 0; i < slotCount; i++) {
2663            var slot = slots[i];
2664            var regionAttachment = slot.attachment;
2665            if (!regionAttachment || regionAttachment.type != spine.AttachmentType.region) continue;
2666            var poolCount = polygonPool.length, polygon;
2667            if (poolCount > 0) {
2668                polygon = polygonPool[poolCount - 1];
2669                polygonPool.splice(poolCount - 1, 1);
2670            } else
2671                polygon = [];
2672            polygons.push(polygon);
2673            polygon.length = 8;
2674//            regionAttachment.updateOffset();
2675            regionAttachment.computeVertices(x, y, slot.bone, polygon);
2676        }
2677
2678        if (updateAabb) this.aabbCompute();
2679    },
2680    aabbCompute: function () {
2681        var polygons = this.polygons;
2682        var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, maxX = Number.MIN_VALUE, maxY = Number.MIN_VALUE;
2683        for (var i = 0, n = polygons.length; i < n; i++) {
2684            var vertices = polygons[i];
2685            for (var ii = 0, nn = vertices.length; ii < nn; ii += 2) {
2686                var x = vertices[ii];
2687                var y = vertices[ii + 1];
2688                minX = Math.min(minX, x);
2689                minY = Math.min(minY, y);
2690                maxX = Math.max(maxX, x);
2691                maxY = Math.max(maxY, y);
2692            }
2693        }
2694        this.minX = minX;
2695        this.minY = minY;
2696        this.maxX = maxX;
2697        this.maxY = maxY;
2698    },
2699    /** Returns true if the axis aligned bounding box contains the point. */
2700    aabbContainsPoint: function (x, y) {
2701        return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY;
2702    },
2703    /** Returns true if the axis aligned bounding box intersects the line segment. */
2704    aabbIntersectsSegment: function (x1, y1, x2, y2) {
2705        var minX = this.minX, minY = this.minY, maxX = this.maxX, maxY = this.maxY;
2706        if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
2707            return false;
2708        var m = (y2 - y1) / (x2 - x1);
2709        var y = m * (minX - x1) + y1;
2710        if (y > minY && y < maxY) return true;
2711        y = m * (maxX - x1) + y1;
2712        if (y > minY && y < maxY) return true;
2713        var x = (minY - y1) / m + x1;
2714        if (x > minX && x < maxX) return true;
2715        x = (maxY - y1) / m + x1;
2716        if (x > minX && x < maxX) return true;
2717        return false;
2718    },
2719    /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */
2720    aabbIntersectsSkeleton: function (bounds) {
2721        return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY;
2722    },
2723    /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
2724     * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */
2725    containsPoint: function (x, y) {
2726        var polygons = this.polygons;
2727        for (var i = 0, n = polygons.length; i < n; i++)
2728            if (this.polygonContainsPoint(polygons[i], x, y)) return this.boundingBoxes[i];
2729        return null;
2730    },
2731    /** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
2732     * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. */
2733    intersectsSegment: function (x1, y1, x2, y2) {
2734        var polygons = this.polygons;
2735        for (var i = 0, n = polygons.length; i < n; i++)
2736            if (polygons[i].intersectsSegment(x1, y1, x2, y2)) return this.boundingBoxes[i];
2737        return null;
2738    },
2739    /** Returns true if the polygon contains the point. */
2740    polygonContainsPoint: function (polygon, x, y) {
2741        var nn = polygon.length;
2742        var prevIndex = nn - 2;
2743        var inside = false;
2744        for (var ii = 0; ii < nn; ii += 2) {
2745            var vertexY = polygon[ii + 1];
2746            var prevY = polygon[prevIndex + 1];
2747            if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
2748                var vertexX = polygon[ii];
2749                if (vertexX + (y - vertexY) / (prevY - vertexY) * (polygon[prevIndex] - vertexX) < x) inside = !inside;
2750            }
2751            prevIndex = ii;
2752        }
2753        return inside;
2754    },
2755    /** Returns true if the polygon contains the line segment. */
2756    polygonIntersectsSegment: function (polygon, x1, y1, x2, y2) {
2757        var nn = polygon.length;
2758        var width12 = x1 - x2, height12 = y1 - y2;
2759        var det1 = x1 * y2 - y1 * x2;
2760        var x3 = polygon[nn - 2], y3 = polygon[nn - 1];
2761        for (var ii = 0; ii < nn; ii += 2) {
2762            var x4 = polygon[ii], y4 = polygon[ii + 1];
2763            var det2 = x3 * y4 - y3 * x4;
2764            var width34 = x3 - x4, height34 = y3 - y4;
2765            var det3 = width12 * height34 - height12 * width34;
2766            var x = (det1 * width34 - width12 * det2) / det3;
2767            if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
2768                var y = (det1 * height34 - height12 * det2) / det3;
2769                if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true;
2770            }
2771            x3 = x4;
2772            y3 = y4;
2773        }
2774        return false;
2775    },
2776    getPolygon: function (attachment) {
2777        var index = this.boundingBoxes.indexOf(attachment);
2778        return index == -1 ? null : this.polygons[index];
2779    },
2780    getWidth: function () {
2781        return this.maxX - this.minX;
2782    },
2783    getHeight: function () {
2784        return this.maxY - this.minY;
2785    }
2786};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.