title: Using stateProperties and hasStateChanged to implement undo / redo in fabric.js
body: <p><strong>Edit:</strong> The scope of this question has shifted a bit. Please see updates below.</p>
  
  <p><strike>I've been working on an undo/redo of sorts for fabric.js (see <a href="http://jsfiddle.net/phxqt3wh/" rel="noreferrer">fiddle</a>). Though rather naive, it's working for the most part (for adding / removing / moving or resizing individual objects or adding / removing groups of objects), but not for moving or resizing groups of objects. In the fiddle, note console output from onObjectSelected() function.</p>
  
  <p>To see what i mean, draw some objects on the fiddle canvas, and move or resize them individually. Undo / redo works as expected. However, when groups are selected and moved, i can't see how to retrieve the updated positions of the objects within the group, which means the undo / redo can't restore the correct values.</p>
  
  <pre><code>// onObjectSelected function quoted here to satisfy stackoverflow 
  // question requirements. See full jsfiddle for context.
  for (i in canvas.getObjects()) {
          if (canvas.item(i).active === true ) {
              console.log(canvas.item(i));
              selectedObject.push({
                  "itemNum"   : i,
                  "svdObject" : fabric.util.object.clone(canvas.item(i))
              });
          }
      }
  </code></pre>
  
  <p>The reason seems to be that object positions / scales are not updated when moved or resized as a group. I've tried various things, including running setCoords() for all objects, but that has not helped. So i'd appreciate if someone can spot what i'm doing wrong, or has an idea how to work around this.</p>
  
  <p><strong>Edit:</strong> To clarify, this is just a normal undo / redo system using two arrays to store properties for objects on the canvas as the user modifies them. When undo or redo is clicked, the properties are applied back to the object on the canvas. The problem i'm having is that when <em>groups</em> of objects are selected and subsequently modified, i don't know how to capture certain properties, such as the positions of the objects in the group, because the properties of the objects within the group are not updated when the objects are modified as a group for some reason.</strike></p>
  
  <p><strong>Update</strong>: I've learned that when objects are in user selected groups, the object position is recorded relative to the group it's in, and no longer the canvas, which makes sense. So when dealing with an object in a group, <em>add</em> it's left/top coord to the group left/top coord to calculate the coord of the object relative to the canvas. scaleX/Y should be multiplied by group scaleX/Y.</p>
  
  <p><strong>Update</strong>: I've mananaged to get this to do what i want (see <a href="http://jsfiddle.net/phxqt3wh/3/" rel="noreferrer">new fiddle</a>). I've since learned about fabric's 'stateful' flag, stateProperties array and hasStateChanged method, which would simplify things. The documentation for these is sparse and i've not been successful thus far in using them within this solution. Usage tips or example code using these would be appreciated. Specifically, I would like to be able to <em>control which properties are saved</em>, since i don't need to save all changeable properties.</p>
  
  <p>onObjectSelected looks like this at present:</p>
  
  <pre><code>function onObjectSelected() {
  
    selectedObject = [];
  
    for (i in canvas.getObjects()) {
  
      if (canvas.item(i).active === true) {
  
        // shft-ctrl-j to see item properties on click (chrome)
        console.log(canvas.item(i));
  
        // objects can be flipped, but not groups 
        var groupLeft = 0;
        var groupTop = 0;
        var groupScaleX = 1;
        var groupScaleY = 1;
        var groupAngle = 0;
  
  
        // if it's a group, add group coords also
        if (typeof canvas.item(i).group != "undefined") {
          var groupLeft = canvas.item(i).group.left;
          var groupTop = canvas.item(i).group.top;
          var groupScaleX = canvas.item(i).group.scaleX;
          var groupScaleY = canvas.item(i).group.scaleY;
          var groupAngle = canvas.item(i).group.angle;
        }
  
        selectedObject.push({
          "itemNum": i,
          "left": canvas.item(i).left + groupLeft,
          "top": canvas.item(i).top + groupTop,
          "scaleX": canvas.item(i).scaleX * groupScaleX,
          "scaleY": canvas.item(i).scaleY * groupScaleY,
          "angle": canvas.item(i).angle + groupAngle,
          "flipX": canvas.item(i).flipX,
          "flipY": canvas.item(i).flipY
        });
      }
  
    }
  
  };
  </code></pre>