function domdiff(oldNode, newNode) {
        var queue = [],
            edits = [],
            moves = [],
            newElement,
            oldElement,
            oldNodeMap = oldNode ? oldNode.nodeMap : {},
            newNodeMap = newNode.nodeMap;

        /**
         * Adds elements to the queue for generateChildEdits.
         * Only elements (and not text nodes) are added. New nodes (ones that aren't in the
         * old nodeMap), are not added here because they will be added when generateChildEdits
         * creates the elementInsert edit.
         */
        var queuePush = function (node) {
            if (node.children && oldNodeMap[node.tagID]) {
                queue.push(node);
            }
        };

        /**
         * Aggregates the child edits in the proper data structures.
         *
         * @param {Object} delta edits, moves and newElements to add
         */
        var addEdits = function (delta) {
            edits.push.apply(edits, delta.edits);
            moves.push.apply(moves, delta.moves);
            queue.push.apply(queue, delta.newElements);
        };

        // Start at the root of the current tree.
        queue.push(newNode);

        do {
            newElement = queue.pop();
            oldElement = oldNodeMap[newElement.tagID];

            // Do we need to compare elements?
            if (oldElement) {

                // Are attributes different?
                if (newElement.attributeSignature !== oldElement.attributeSignature) {
                    // generate attribute edits
                    edits.push.apply(edits, generateAttributeEdits(oldElement, newElement));
                }

                // Has there been a change to this node's immediate children?
                if (newElement.childSignature !== oldElement.childSignature) {
                    addEdits(generateChildEdits(oldElement, oldNodeMap, newElement, newNodeMap));
                }

                // If there's a change farther down in the tree, add the children to the queue.
                // If not, we can skip that whole subtree.
                if (newElement.subtreeSignature !== oldElement.subtreeSignature) {
                    newElement.children.forEach(queuePush);
                }

            // This is a new element, so go straight to generating child edits (which will
            // create the appropriate Insert edits).
            } else {
                // If this is the root (html) tag, we need to manufacture an insert for it here,
                // because it isn't the child of any other node. The browser-side code doesn't
                // care about parentage/positioning in this case, and will handle just setting the
                // ID on the existing implied HTML tag in the browser without actually creating it.
                if (!newElement.parent) {
                    edits.push({
                        type: "elementInsert",
                        tag: newElement.tag,
                        tagID: newElement.tagID,
                        parentID: null,
                        attributes: newElement.attributes
                    });
                }

                addEdits(generateChildEdits(null, oldNodeMap, newElement, newNodeMap));
            }
        } while (queue.length);

        // Special handling for moves: add edits to the beginning of the list so that
        // moved nodes are set aside to ensure that they remain available at the time of their
        // move.
        if (moves.length > 0) {
            edits.unshift({
                type: "rememberNodes",
                tagIDs: moves
            });
        }

        return edits;
    }
Generate a list of edits that will mutate oldNode to look like newNode.
Currently, there are the following possible edit operations:

* elementInsert
* elementDelete
* elementMove
* textInsert
* textDelete
* textReplace
* attrDelete
* attrChange
* attrAdd
* rememberNodes (a special instruction that reflects the need to hang on to moved nodes)

@param {Object} oldNode SimpleDOM node with the original content
@param {Object} newNode SimpleDOM node with the new content
@return {Array.{Object}} list of edit operations