diff --git a/spine.js b/spine.js
index 46f73c4..07fe8ac 100644
--- a/spine.js
+++ b/spine.js
@@ -1670,6 +1670,116 @@ spine.Ikc.prototype.load = function(json) {
   return ikc;
 }
 
+/**
+ * @constructor
+ */
+spine.Xfc = function() {
+  var xfc = this;
+  xfc.bone_keys = [];
+  xfc.position = new spine.Position();
+  xfc.rotation = new spine.Rotation();
+  xfc.scale = new spine.Scale();
+  xfc.shear = new spine.Shear();
+}
+
+/** @type {string} */
+spine.Xfc.prototype.name = "";
+/** @type {Array.<string>} */
+spine.Xfc.prototype.bone_keys;
+/** @type {string} */
+spine.Xfc.prototype.target_key = "";
+/** @type {number} */
+spine.Xfc.prototype.position_mix = 1;
+/** @type {spine.Position} */
+spine.Xfc.prototype.position;
+/** @type {number} */
+spine.Xfc.prototype.rotation_mix = 1;
+/** @type {spine.Rotation} */
+spine.Xfc.prototype.rotation;
+/** @type {number} */
+spine.Xfc.prototype.scale_mix = 1;
+/** @type {spine.Scale} */
+spine.Xfc.prototype.scale;
+/** @type {number} */
+spine.Xfc.prototype.shear_mix = 1;
+/** @type {spine.Shear} */
+spine.Xfc.prototype.shear;
+
+/**
+ * @return {spine.Xfc}
+ * @param {Object.<string,?>} json
+ */
+spine.Xfc.prototype.load = function(json) {
+  var xfc = this;
+  xfc.name = spine.loadString(json, 'name', "");
+  xfc.bone_keys = json['bones'] || [];
+  xfc.target_key = spine.loadString(json, 'target', "");
+  xfc.position_mix = spine.loadFloat(json, 'translateMix', 1);
+  xfc.position.x = spine.loadFloat(json, 'x', 0);
+  xfc.position.y = spine.loadFloat(json, 'y', 0);
+  xfc.rotation_mix = spine.loadFloat(json, 'rotateMix', 1);
+  xfc.rotation.deg = spine.loadFloat(json, 'rotation', 0);
+  xfc.scale_mix = spine.loadFloat(json, 'scaleMix', 1);
+  xfc.scale.x = spine.loadFloat(json, 'scaleX', 1);
+  xfc.scale.y = spine.loadFloat(json, 'scaleY', 1);
+  xfc.shear_mix = spine.loadFloat(json, 'shearMix', 1);
+  xfc.shear.x.deg = spine.loadFloat(json, 'shearX', 0);
+  xfc.shear.y.deg = spine.loadFloat(json, 'shearY', 0);
+  return xfc;
+}
+
+/**
+ * @constructor
+ */
+spine.Ptc = function() {
+  var ptc = this;
+  ptc.bone_keys = [];
+  ptc.rotation = new spine.Rotation();
+}
+
+/** @type {string} */
+spine.Ptc.prototype.name = "";
+/** @type {Array.<string>} */
+spine.Ptc.prototype.bone_keys;
+/** @type {string} */
+spine.Ptc.prototype.target_key = "";
+/** @type {string} */
+spine.Ptc.prototype.spacing_mode = "length"; // "length", "fixed", "percent"
+/** @type {number} */
+spine.Ptc.prototype.spacing = 0;
+/** @type {string} */
+spine.Ptc.prototype.position_mode = "percent"; // "fixed", "percent"
+/** @type {number} */
+spine.Ptc.prototype.position_mix = 1;
+/** @type {number} */
+spine.Ptc.prototype.position = 0;
+/** @type {string} */
+spine.Ptc.prototype.rotation_mode = "tangent"; // "tangent", "chain", "chainScale"
+/** @type {number} */
+spine.Ptc.prototype.rotation_mix = 1;
+/** @type {spine.Rotation} */
+spine.Ptc.prototype.rotation;
+
+/**
+ * @return {spine.Ptc}
+ * @param {Object.<string,?>} json
+ */
+spine.Ptc.prototype.load = function(json) {
+  var ptc = this;
+  ptc.name = spine.loadString(json, 'name', "");
+  ptc.bone_keys = json['bones'] || [];
+  ptc.target_key = spine.loadString(json, 'target', "");
+  ptc.spacing_mode = spine.loadString(json, 'spacingMode', "length");
+  ptc.spacing = spine.loadFloat(json, 'spacing', 0);
+  ptc.position_mode = spine.loadString(json, 'positionMode', "percent");
+  ptc.position_mix = spine.loadFloat(json, 'translateMix', 1);
+  ptc.position = spine.loadFloat(json, 'position', 0);
+  ptc.rotation_mode = spine.loadString(json, 'rotateMode', "tangent");
+  ptc.rotation_mix = spine.loadFloat(json, 'rotateMix', 1);
+  ptc.rotation.deg = spine.loadFloat(json, 'rotation', 0);
+  return ptc;
+}
+
 /**
  * @constructor
  */
@@ -1722,7 +1832,7 @@ spine.Attachment = function(type) {
 }
 
 /** @type {string} */
-spine.Attachment.prototype.type = "region";
+spine.Attachment.prototype.type = "";
 /** @type {string} */
 spine.Attachment.prototype.name = "";
 /** @type {string} */
@@ -1851,6 +1961,47 @@ spine.MeshAttachment.prototype.load = function(json) {
   return attachment;
 }
 
+/**
+ * @constructor
+ * @extends {spine.Attachment}
+ */
+spine.LinkedMeshAttachment = function() {
+  goog.base(this, 'linkedmesh');
+  this.color = new spine.Color();
+}
+
+goog.inherits(spine.LinkedMeshAttachment, spine.Attachment);
+
+/** @type {spine.Color} */
+spine.LinkedMeshAttachment.prototype.color;
+/** @type {string} */
+spine.LinkedMeshAttachment.prototype.skin_key = "";
+/** @type {string} */
+spine.LinkedMeshAttachment.prototype.parent_key = "";
+/** @type {boolean} */
+spine.LinkedMeshAttachment.prototype.inherit_ffd = true;
+/** @type {number} */
+spine.LinkedMeshAttachment.prototype.width = 0;
+/** @type {number} */
+spine.LinkedMeshAttachment.prototype.height = 0;
+
+/**
+ * @return {spine.Attachment}
+ * @param {Object.<string,?>} json
+ */
+spine.LinkedMeshAttachment.prototype.load = function(json) {
+  goog.base(this, 'load', json);
+
+  var attachment = this;
+  attachment.color.load(json.color);
+  attachment.skin_key = spine.loadString(json, 'skin', "");
+  attachment.parent_key = spine.loadString(json, 'parent', "");
+  attachment.inherit_ffd = spine.loadBool(json, 'ffd', true);
+  attachment.width = spine.loadInt(json, 'width', 0);
+  attachment.height = spine.loadInt(json, 'height', 0);
+  return attachment;
+}
+
 /**
  * @constructor
  * @extends {spine.Attachment}
@@ -1896,6 +2047,86 @@ spine.WeightedMeshAttachment.prototype.load = function(json) {
   return attachment;
 }
 
+/**
+ * @constructor
+ * @extends {spine.Attachment}
+ */
+spine.WeightedLinkedMeshAttachment = function() {
+  goog.base(this, 'weightedlinkedmesh');
+}
+
+goog.inherits(spine.WeightedLinkedMeshAttachment, spine.Attachment);
+
+/** @type {spine.Color} */
+spine.WeightedLinkedMeshAttachment.prototype.color;
+/** @type {string} */
+spine.WeightedLinkedMeshAttachment.prototype.skin_key = "";
+/** @type {string} */
+spine.WeightedLinkedMeshAttachment.prototype.parent_key = "";
+/** @type {boolean} */
+spine.WeightedLinkedMeshAttachment.prototype.inherit_ffd = true;
+/** @type {number} */
+spine.WeightedLinkedMeshAttachment.prototype.width = 0;
+/** @type {number} */
+spine.WeightedLinkedMeshAttachment.prototype.height = 0;
+
+/**
+ * @return {spine.Attachment}
+ * @param {Object.<string,?>} json
+ */
+spine.WeightedLinkedMeshAttachment.prototype.load = function(json) {
+  goog.base(this, 'load', json);
+
+  var attachment = this;
+  attachment.skin_key = spine.loadString(json, 'skin', "");
+  attachment.parent_key = spine.loadString(json, 'parent', "");
+  attachment.inherit_ffd = spine.loadBool(json, 'ffd', true);
+  attachment.width = spine.loadInt(json, 'width', 0);
+  attachment.height = spine.loadInt(json, 'height', 0);
+  return attachment;
+}
+
+/**
+ * @constructor
+ * @extends {spine.Attachment}
+ */
+spine.PathAttachment = function() {
+  goog.base(this, 'path');
+  this.color = new spine.Color();
+}
+
+goog.inherits(spine.PathAttachment, spine.Attachment);
+
+/** @type {spine.Color} */
+spine.PathAttachment.prototype.color;
+/** @type {boolean} */
+spine.PathAttachment.prototype.closed = false;
+/** @type {boolean} */
+spine.PathAttachment.prototype.accurate = true;
+/** @type {Array.<number>} */
+spine.PathAttachment.prototype.lengths;
+/** @type {number} */
+spine.PathAttachment.prototype.vertex_count = 0;
+/** @type {Array.<number>} */
+spine.PathAttachment.prototype.vertices;
+
+/**
+ * @return {spine.Attachment}
+ * @param {Object.<string,?>} json
+ */
+spine.PathAttachment.prototype.load = function(json) {
+  goog.base(this, 'load', json);
+
+  var attachment = this;
+  attachment.color.load(json.color);
+  attachment.closed = spine.loadBool(json, 'closed', false);
+  attachment.accurate = spine.loadBool(json, 'constantSpeed', true);
+  attachment.lengths = json.lengths || [];
+  attachment.vertex_count = spine.loadInt(json, 'vertexCount', 0);
+  attachment.vertices = json.vertices || [];
+  return attachment;
+}
+
 /**
  * @constructor
  */
@@ -1928,13 +2159,32 @@ spine.SkinSlot.prototype.load = function(json) {
         skin_slot.attachments[attachment_key] = new spine.BoundingBoxAttachment().load(json_attachment);
         break;
       case 'mesh':
-        skin_slot.attachments[attachment_key] = new spine.MeshAttachment().load(json_attachment);
+        if (json_attachment.vertices.length === json_attachment.uvs.length) {
+          skin_slot.attachments[attachment_key] = new spine.MeshAttachment().load(json_attachment);
+        } else {
+          json_attachment.type = 'weightedmesh';
+          skin_slot.attachments[attachment_key] = new spine.WeightedMeshAttachment().load(json_attachment);
+        }
+        break;
+      case 'linkedmesh':
+        if (json_attachment.vertices.length === json_attachment.uvs.length) {
+          skin_slot.attachments[attachment_key] = new spine.LinkedMeshAttachment().load(json_attachment);
+        } else {
+          json_attachment.type = 'weightedlinkedmesh';
+          skin_slot.attachments[attachment_key] = new spine.WeightedLinkedMeshAttachment().load(json_attachment);
+        }
         break;
       case 'skinnedmesh':
         json_attachment.type = 'weightedmesh';
       case 'weightedmesh':
         skin_slot.attachments[attachment_key] = new spine.WeightedMeshAttachment().load(json_attachment);
         break;
+      case 'weightedlinkedmesh':
+        skin_slot.attachments[attachment_key] = new spine.WeightedLinkedMeshAttachment().load(json_attachment);
+        break;
+      case 'path':
+        skin_slot.attachments[attachment_key] = new spine.PathAttachment().load(json_attachment);
+        break;
     }
   });
   return skin_slot;
@@ -2599,6 +2849,240 @@ spine.AnimIkc.prototype.load = function(json) {
   return anim_ikc;
 }
 
+/**
+ * @constructor
+ * @extends {spine.Keyframe}
+ */
+spine.XfcKeyframe = function() {
+  goog.base(this);
+  this.curve = new spine.Curve();
+}
+
+goog.inherits(spine.XfcKeyframe, spine.Keyframe);
+
+/** @type {spine.Curve} */
+spine.XfcKeyframe.prototype.curve;
+/** @type {number} */
+spine.XfcKeyframe.prototype.position_mix = 1;
+/** @type {number} */
+spine.XfcKeyframe.prototype.rotation_mix = 1;
+/** @type {number} */
+spine.XfcKeyframe.prototype.scale_mix = 1;
+/** @type {number} */
+spine.XfcKeyframe.prototype.shear_mix = 1;
+
+/**
+ * @return {spine.XfcKeyframe}
+ * @param {Object.<string,?>} json
+ */
+spine.XfcKeyframe.prototype.load = function(json) {
+  goog.base(this, 'load', json);
+  this.curve.load(json.curve);
+  this.position_mix = spine.loadFloat(json, 'translateMix', 1);
+  this.rotation_mix = spine.loadFloat(json, 'rotateMix', 1);
+  this.scale_mix = spine.loadFloat(json, 'scaleMix', 1);
+  this.shear_mix = spine.loadFloat(json, 'shearMix', 1);
+  return this;
+}
+
+/**
+ * @constructor
+ */
+spine.AnimXfc = function() {}
+
+/** @type {number} */
+spine.AnimXfc.prototype.min_time = 0;
+/** @type {number} */
+spine.AnimXfc.prototype.max_time = 0;
+/** @type {Array.<spine.XfcKeyframe>} */
+spine.AnimXfc.prototype.xfc_keyframes = null;
+
+/**
+ * @return {spine.AnimXfc}
+ * @param {Object.<string,?>} json
+ */
+spine.AnimXfc.prototype.load = function(json) {
+  var anim_xfc = this;
+  anim_xfc.min_time = 0;
+  anim_xfc.max_time = 0;
+  anim_xfc.xfc_keyframes = [];
+
+  json.forEach(function(xfc) {
+    var xfc_keyframe = new spine.XfcKeyframe().load(xfc);
+    anim_xfc.min_time = Math.min(anim_xfc.min_time, xfc_keyframe.time);
+    anim_xfc.max_time = Math.max(anim_xfc.max_time, xfc_keyframe.time);
+    anim_xfc.xfc_keyframes.push(xfc_keyframe);
+  });
+  anim_xfc.xfc_keyframes.sort(spine.Keyframe.compare);
+
+  return anim_xfc;
+}
+
+/**
+ * @constructor
+ * @extends {spine.Keyframe}
+ */
+spine.PtcKeyframe = function() {
+  goog.base(this);
+  this.curve = new spine.Curve();
+}
+
+goog.inherits(spine.PtcKeyframe, spine.Keyframe);
+
+/** @type {spine.Curve} */
+spine.PtcKeyframe.prototype.curve;
+
+/**
+ * @return {spine.PtcKeyframe}
+ * @param {Object.<string,?>} json
+ */
+spine.PtcKeyframe.prototype.load = function(json) {
+  goog.base(this, 'load', json);
+  this.curve.load(json.curve);
+  return this;
+}
+
+/**
+ * @constructor
+ * @extends {spine.PtcKeyframe}
+ */
+spine.PtcSpacingKeyframe = function() {
+  goog.base(this);
+}
+
+goog.inherits(spine.PtcSpacingKeyframe, spine.PtcKeyframe);
+
+/** @type {number} */
+spine.PtcSpacingKeyframe.prototype.spacing = 0;
+
+/**
+ * @return {spine.PtcSpacingKeyframe}
+ * @param {Object.<string,?>} json
+ */
+spine.PtcSpacingKeyframe.prototype.load = function(json) {
+  goog.base(this, 'load', json);
+  this.spacing = spine.loadFloat(json, 'spacing', 0);
+  return this;
+}
+
+/**
+ * @constructor
+ * @extends {spine.PtcKeyframe}
+ */
+spine.PtcPositionKeyframe = function() {
+  goog.base(this);
+}
+
+goog.inherits(spine.PtcPositionKeyframe, spine.PtcKeyframe);
+
+/** @type {number} */
+spine.PtcPositionKeyframe.prototype.position_mix = 1;
+/** @type {number} */
+spine.PtcPositionKeyframe.prototype.position = 0;
+
+/**
+ * @return {spine.PtcPositionKeyframe}
+ * @param {Object.<string,?>} json
+ */
+spine.PtcPositionKeyframe.prototype.load = function(json) {
+  goog.base(this, 'load', json);
+  this.position_mix = spine.loadFloat(json, 'positionMix', 1);
+  this.position = spine.loadFloat(json, 'position', 0);
+  return this;
+}
+
+/**
+ * @constructor
+ * @extends {spine.PtcKeyframe}
+ */
+spine.PtcRotationKeyframe = function() {
+  goog.base(this);
+}
+
+goog.inherits(spine.PtcRotationKeyframe, spine.PtcKeyframe);
+
+/** @type {number} */
+spine.PtcRotationKeyframe.prototype.rotation_mix = 1;
+/** @type {number} */
+spine.PtcRotationKeyframe.prototype.rotation = 0;
+
+/**
+ * @return {spine.PtcRotationKeyframe}
+ * @param {Object.<string,?>} json
+ */
+spine.PtcRotationKeyframe.prototype.load = function(json) {
+  goog.base(this, 'load', json);
+  this.rotation_mix = spine.loadFloat(json, 'rotationMix', 1);
+  this.rotation = spine.loadFloat(json, 'rotation', 0);
+  return this;
+}
+
+/**
+ * @constructor
+ */
+spine.AnimPtc = function() {}
+
+/** @type {number} */
+spine.AnimPtc.prototype.min_time = 0;
+/** @type {number} */
+spine.AnimPtc.prototype.max_time = 0;
+/** @type {Array.<spine.PtcSpacingKeyframe>} */
+spine.AnimPtc.prototype.ptc_spacing_keyframes = null;
+/** @type {Array.<spine.PtcPositionKeyframe>} */
+spine.AnimPtc.prototype.ptc_position_keyframes = null;
+/** @type {Array.<spine.PtcRotationKeyframe>} */
+spine.AnimPtc.prototype.ptc_rotation_keyframes = null;
+
+/**
+ * @return {spine.AnimPtc}
+ * @param {Object.<string,?>} json
+ */
+spine.AnimPtc.prototype.load = function(json) {
+  var anim_ptc = this;
+  anim_ptc.min_time = 0;
+  anim_ptc.max_time = 0;
+  anim_ptc.ptc_spacing_keyframes = [];
+  anim_ptc.ptc_position_keyframes = [];
+  anim_ptc.ptc_rotation_keyframes = [];
+
+  Object.keys(json || {}).forEach(function(key) {
+    switch (key) {
+      case 'spacing':
+        json[key].forEach(function(spacing_json) {
+          var ptc_spacing_keyframe = new spine.PtcSpacingKeyframe().load(spacing_json);
+          anim_ptc.min_time = Math.min(anim_ptc.min_time, ptc_spacing_keyframe.time);
+          anim_ptc.max_time = Math.max(anim_ptc.max_time, ptc_spacing_keyframe.time);
+          anim_ptc.ptc_spacing_keyframes.push(ptc_spacing_keyframe);
+        });
+        anim_ptc.ptc_spacing_keyframes.sort(spine.Keyframe.compare);
+        break;
+      case 'position':
+        json[key].forEach(function(position_json) {
+          var ptc_position_keyframe = new spine.PtcPositionKeyframe().load(position_json);
+          anim_ptc.min_time = Math.min(anim_ptc.min_time, ptc_position_keyframe.time);
+          anim_ptc.max_time = Math.max(anim_ptc.max_time, ptc_position_keyframe.time);
+          anim_ptc.ptc_position_keyframes.push(ptc_position_keyframe);
+        });
+        anim_ptc.ptc_position_keyframes.sort(spine.Keyframe.compare);
+        break;
+      case 'rotation':
+        json[key].forEach(function(rotation_json) {
+          var ptc_rotation_keyframe = new spine.PtcRotationKeyframe().load(rotation_json);
+          anim_ptc.min_time = Math.min(anim_ptc.min_time, ptc_rotation_keyframe.time);
+          anim_ptc.max_time = Math.max(anim_ptc.max_time, ptc_rotation_keyframe.time);
+          anim_ptc.ptc_rotation_keyframes.push(ptc_rotation_keyframe);
+        });
+        anim_ptc.ptc_rotation_keyframes.sort(spine.Keyframe.compare);
+        break;
+      default:
+        console.log("TODO: spine.AnimPtc::load", key);
+        break;
+    }
+  });
+
+  return anim_ptc;
+}
+
 /**
  * @constructor
  * @extends {spine.Keyframe}
@@ -2773,6 +3257,8 @@ spine.Animation = function() {
   anim.bones = {};
   anim.slots = {};
   anim.ikcs = {};
+  anim.xfcs = {};
+  anim.ptcs = {};
   anim.ffds = {};
 }
 
@@ -2788,6 +3274,10 @@ spine.Animation.prototype.event_keyframes = null;
 spine.Animation.prototype.order_keyframes = null;
 /** @type {Object.<string,spine.AnimIkc>} */
 spine.Animation.prototype.ikcs;
+/** @type {Object.<string,spine.AnimXfc>} */
+spine.Animation.prototype.xfcs;
+/** @type {Object.<string,spine.AnimPtc>} */
+spine.Animation.prototype.ptcs;
 /** @type {Object.<string,spine.AnimFfd>} */
 spine.Animation.prototype.ffds;
 /** @type {number} */
@@ -2809,6 +3299,8 @@ spine.Animation.prototype.load = function(json) {
   anim.event_keyframes = null;
   anim.order_keyframes = null;
   anim.ikcs = {};
+  anim.xfcs = {};
+  anim.ptcs = {};
   anim.ffds = {};
 
   anim.min_time = 0;
@@ -2861,7 +3353,24 @@ spine.Animation.prototype.load = function(json) {
           anim.ikcs[ikc_key] = anim_ikc;
         });
         break;
+      case 'transform':
+        Object.keys(json[key] || {}).forEach(function(xfc_key) {
+          var anim_xfc = new spine.AnimXfc().load(json[key][xfc_key]);
+          anim.min_time = Math.min(anim.min_time, anim_xfc.min_time);
+          anim.max_time = Math.max(anim.max_time, anim_xfc.max_time);
+          anim.xfcs[xfc_key] = anim_xfc;
+        });
+        break;
+      case 'paths':
+        Object.keys(json[key] || {}).forEach(function(ptc_key) {
+          var anim_ptc = new spine.AnimPtc().load(json[key][ptc_key]);
+          anim.min_time = Math.min(anim.min_time, anim_ptc.min_time);
+          anim.max_time = Math.max(anim.max_time, anim_ptc.max_time);
+          anim.ptcs[ptc_key] = anim_ptc;
+        });
+        break;
       case 'ffd':
+      case 'deform':
         Object.keys(json[key] || {}).forEach(function(ffd_key) {
           var anim_ffd = new spine.AnimFfd().load(json[key][ffd_key]);
           anim.min_time = Math.min(anim.min_time, anim_ffd.min_time);
@@ -2922,6 +3431,10 @@ spine.Data = function() {
   data.bone_keys = [];
   data.ikcs = {};
   data.ikc_keys = [];
+  data.xfcs = {};
+  data.xfc_keys = [];
+  data.ptcs = {};
+  data.ptc_keys = [];
   data.slots = {};
   data.slot_keys = [];
   data.skins = {};
@@ -2944,6 +3457,14 @@ spine.Data.prototype.bone_keys;
 spine.Data.prototype.ikcs;
 /** @type {Array.<string>} */
 spine.Data.prototype.ikc_keys;
+/** @type {Object.<string,spine.Xfc>} */
+spine.Data.prototype.xfcs;
+/** @type {Array.<string>} */
+spine.Data.prototype.xfc_keys;
+/** @type {Object.<string,spine.Ptc>} */
+spine.Data.prototype.ptcs;
+/** @type {Array.<string>} */
+spine.Data.prototype.ptc_keys;
 /** @type {Object.<string,spine.Slot>} */
 spine.Data.prototype.slots;
 /** @type {Array.<string>} */
@@ -2972,6 +3493,10 @@ spine.Data.prototype.load = function(json) {
   data.bone_keys = [];
   data.ikcs = {};
   data.ikc_keys = [];
+  data.xfcs = {};
+  data.xfc_keys = [];
+  data.ptcs = {};
+  data.ptc_keys = [];
   data.slots = {};
   data.slot_keys = [];
   data.skins = {};
@@ -3026,6 +3551,46 @@ spine.Data.prototype.load = function(json) {
           return 0;
         });
         break;
+      case 'transform':
+        var json_transform = json[key];
+        json_transform.forEach(function(xfc, xfc_index) {
+          data.xfcs[xfc.name] = new spine.Xfc().load(xfc);
+          data.xfc_keys[xfc_index] = xfc.name;
+        });
+        // TODO: sort by ancestry?
+        data.xfc_keys = data.xfc_keys.sort(function(a, b) {
+          var xfc_a = data.xfcs[a];
+          var xfc_b = data.xfcs[b];
+          for (var ia = 0; ia < xfc_a.bone_keys.length; ++ia) {
+            var bone_a = data.bones[xfc_a.bone_keys[ia]];
+            for (var ib = 0; ib < xfc_b.bone_keys.length; ++ib) {
+              var bone_b = data.bones[xfc_b.bone_keys[ib]];
+              var bone_a_parent = data.bones[bone_a.parent_key];
+              while (bone_a_parent) {
+                if (bone_a_parent === bone_b) {
+                  return 1;
+                }
+                bone_a_parent = data.bones[bone_a_parent.parent_key];
+              }
+              var bone_b_parent = data.bones[bone_b.parent_key];
+              while (bone_b_parent) {
+                if (bone_b_parent === bone_a) {
+                  return -1;
+                }
+                bone_b_parent = data.bones[bone_b_parent.parent_key];
+              }
+            }
+          }
+          return 0;
+        });
+        break;
+      case 'path':
+        var json_path = json[key];
+        json_path.forEach(function(ptc, ptc_index) {
+          data.ptcs[ptc.name] = new spine.Ptc().load(ptc);
+          data.ptc_keys[ptc_index] = ptc.name;
+        });
+        break;
       case 'slots':
         var json_slots = json[key];
         json_slots.forEach(function(slot, slot_index) {
@@ -3151,6 +3716,10 @@ spine.Data.prototype.iterateAttachments = function(skin_key, callback) {
     var skin_slot = skin && (skin.slots[slot_key] || default_skin.slots[slot_key]);
     var attachment = skin_slot && skin_slot.attachments[data_slot.attachment_key];
     var attachment_key = (attachment && (attachment.path || attachment.name)) || data_slot.attachment_key;
+    if (attachment && ((attachment.type === 'linkedmesh') || (attachment.type === 'weightedlinkedmesh'))) {
+      attachment_key = attachment && (attachment.path || attachment.parent_key);
+      attachment = skin_slot && skin_slot.attachments[attachment_key];
+    }
     callback(slot_key, data_slot, skin_slot, attachment_key, attachment);
   });
 }
@@ -3407,6 +3976,8 @@ spine.Pose.prototype.strike = function() {
   var keyframe_index;
   var pct;
 
+  // bones
+
   data.bone_keys.forEach(function(bone_key) {
     var data_bone = data.bones[bone_key];
     var pose_bone = pose.bones[bone_key] || (pose.bones[bone_key] = new spine.Bone());
@@ -3632,6 +4203,51 @@ spine.Pose.prototype.strike = function() {
     spine.Bone.flatten(bone, pose.bones);
   });
 
+  // transform constraints
+
+  data.xfc_keys.forEach(function(xfc_key) {
+    var xfc = data.xfcs[xfc_key];
+    var xfc_position_mix = xfc.position_mix;
+    var xfc_rotation_mix = xfc.rotation_mix;
+    var xfc_scale_mix = xfc.scale_mix;
+    var xfc_shear_mix = xfc.shear_mix;
+
+    var anim_xfc = anim && anim.xfcs[xfc_key];
+    if (anim_xfc) {
+      keyframe_index = spine.Keyframe.find(anim_xfc.xfc_keyframes, time);
+      if (keyframe_index !== -1) {
+        var xfc_keyframe0 = anim_xfc.xfc_keyframes[keyframe_index];
+        var xfc_keyframe1 = anim_xfc.xfc_keyframes[keyframe_index + 1];
+        if (xfc_keyframe1) {
+          pct = xfc_keyframe0.curve.evaluate((time - xfc_keyframe0.time) / (xfc_keyframe1.time - xfc_keyframe0.time));
+          xfc_position_mix = spine.tween(xfc_keyframe0.position_mix, xfc_keyframe1.position_mix, pct);
+          xfc_rotation_mix = spine.tween(xfc_keyframe0.rotation_mix, xfc_keyframe1.rotation_mix, pct);
+          xfc_scale_mix = spine.tween(xfc_keyframe0.scale_mix, xfc_keyframe1.scale_mix, pct);
+          xfc_shear_mix = spine.tween(xfc_keyframe0.shear_mix, xfc_keyframe1.shear_mix, pct);
+        } else {
+          xfc_position_mix = xfc_keyframe0.position_mix;
+          xfc_rotation_mix = xfc_keyframe0.rotation_mix;
+          xfc_scale_mix = xfc_keyframe0.scale_mix;
+          xfc_shear_mix = xfc_keyframe0.shear_mix;
+        }
+      }
+    }
+
+    var xfc_target = pose.bones[xfc.target_key];
+    var xfc_position = xfc.position;
+    var xfc_rotation = xfc.rotation;
+    var xfc_scale = xfc.scale;
+    var xfc_shear = xfc.shear;
+    var xfc_world_position = spine.Space.transform(xfc_target.world_space, xfc_position, new spine.Vector());
+    xfc.bone_keys.forEach(function(bone_key) {
+      var xfc_bone = pose.bones[bone_key];
+      // TODO
+      xfc_bone.world_space.position.tween(xfc_world_position, xfc_position_mix, xfc_bone.world_space.position);
+    });
+  });
+
+  // slots
+
   data.slot_keys.forEach(function(slot_key) {
     var data_slot = data.slots[slot_key];
     var pose_slot = pose.slots[slot_key] || (pose.slots[slot_key] = new spine.Slot());
@@ -3682,6 +4298,77 @@ spine.Pose.prototype.strike = function() {
     }
   }
 
+  // path constraints
+
+  data.ptc_keys.forEach(function(ptc_key) {
+    var ptc = data.ptcs[ptc_key];
+    var ptc_spacing_mode = ptc.spacing_mode;
+    var ptc_spacing = ptc.spacing;
+    var ptc_position_mode = ptc.position_mode;
+    var ptc_position_mix = ptc.position_mix;
+    var ptc_position = ptc.position;
+    var ptc_rotation_mode = ptc.rotation_mode;
+    var ptc_rotation_mix = ptc.rotation_mix;
+    var ptc_rotation = ptc.rotation;
+
+    var anim_ptc = anim && anim.ptcs[ptc_key];
+    if (anim_ptc) {
+      keyframe_index = spine.Keyframe.find(anim_ptc.ptc_spacing_keyframes, time);
+      if (keyframe_index !== -1) {
+        var ptc_spacing_keyframe0 = anim_ptc.ptc_spacing_keyframes[keyframe_index];
+        var ptc_spacing_keyframe1 = anim_ptc.ptc_spacing_keyframes[keyframe_index + 1];
+        if (ptc_spacing_keyframe1) {
+          pct = ptc_spacing_keyframe0.curve.evaluate((time - ptc_spacing_keyframe0.time) / (ptc_spacing_keyframe1.time - ptc_spacing_keyframe0.time));
+          ptc_spacing = spine.tween(ptc_spacing_keyframe0.spacing, ptc_spacing_keyframe1.spacing, pct);
+        } else {
+          ptc_spacing = ptc_spacing_keyframe0.spacing;
+        }
+      }
+
+      keyframe_index = spine.Keyframe.find(anim_ptc.ptc_position_keyframes, time);
+      if (keyframe_index !== -1) {
+        var ptc_position_keyframe0 = anim_ptc.ptc_position_keyframes[keyframe_index];
+        var ptc_position_keyframe1 = anim_ptc.ptc_position_keyframes[keyframe_index + 1];
+        if (ptc_position_keyframe1) {
+          pct = ptc_position_keyframe0.curve.evaluate((time - ptc_position_keyframe0.time) / (ptc_position_keyframe1.time - ptc_position_keyframe0.time));
+          ptc_position_mix = spine.tween(ptc_position_keyframe0.position_mix, ptc_position_keyframe1.position_mix, pct);
+          ptc_position = spine.tween(ptc_position_keyframe0.position, ptc_position_keyframe1.position, pct);
+        } else {
+          ptc_position_mix = ptc_position_keyframe0.position_mix;
+          ptc_position = ptc_position_keyframe0.position;
+        }
+      }
+
+      keyframe_index = spine.Keyframe.find(anim_ptc.ptc_rotation_keyframes, time);
+      if (keyframe_index !== -1) {
+        var ptc_rotation_keyframe0 = anim_ptc.ptc_rotation_keyframes[keyframe_index];
+        var ptc_rotation_keyframe1 = anim_ptc.ptc_rotation_keyframes[keyframe_index + 1];
+        if (ptc_rotation_keyframe1) {
+          pct = ptc_rotation_keyframe0.curve.evaluate((time - ptc_rotation_keyframe0.time) / (ptc_rotation_keyframe1.time - ptc_rotation_keyframe0.time));
+          ptc_rotation_mix = spine.tween(ptc_rotation_keyframe0.rotation_mix, ptc_rotation_keyframe1.rotation_mix, pct);
+          ptc_rotation = spine.tween(ptc_rotation_keyframe0.rotation, ptc_rotation_keyframe1.rotation, pct);
+        } else {
+          ptc_rotation_mix = ptc_rotation_keyframe0.rotation_mix;
+          ptc_rotation = ptc_rotation_keyframe0.rotation;
+        }
+      }
+    }
+
+    var skin = data && data.skins[pose.skin_key];
+    var default_skin = data && data.skins['default'];
+    var slot_key = ptc.target_key;
+    var pose_slot = pose.slots[slot_key];
+    var skin_slot = skin && (skin.slots[slot_key] || default_skin.slots[slot_key]);
+    var ptc_target = skin_slot && skin_slot.attachments[pose_slot.attachment_key];
+
+    ptc.bone_keys.forEach(function(bone_key) {
+      var ptc_bone = pose.bones[bone_key];
+      // TODO
+    });
+  });
+
+  // events
+
   pose.events.length = 0;
 
   if (anim && anim.event_keyframes) {
@@ -3775,6 +4462,10 @@ spine.Pose.prototype.iterateAttachments = function(callback) {
     var skin_slot = skin && (skin.slots[slot_key] || default_skin.slots[slot_key]);
     var attachment = skin_slot && skin_slot.attachments[pose_slot.attachment_key];
     var attachment_key = (attachment && (attachment.path || attachment.name)) || pose_slot.attachment_key;
+    if (attachment && ((attachment.type === 'linkedmesh') || (attachment.type === 'weightedlinkedmesh'))) {
+      attachment_key = attachment && (attachment.path || attachment.parent_key);
+      attachment = skin_slot && skin_slot.attachments[attachment_key];
+    }
     callback(slot_key, pose_slot, skin_slot, attachment_key, attachment);
   });
 }
