var stylus = require('stylus'),
    Visitor = stylus.Visitor
    nodes = stylus.nodes;

var Scanner = module.exports = function Scanner(root, imagesSeen) {
  Visitor.call(this, root);
  this.imagesSeen = imagesSeen;
  this.imageGroups = {};
  this.tree = [];
};

Scanner.prototype.__proto__ = Visitor.prototype;

Scanner.prototype.combine = function() {
  this.visit(this.root);
};

Scanner.prototype.generateSelector = function(tree) {
  // Originally implemented in stylus compiler.js
  function groupSelectors(group) {
    var curr = [];

    // Construct an array of arrays
    // representing the selector hierarchy
    group.nodes.forEach(function(node){
      curr.push(node.parent
          ? node
          : node.val);
    });
    return curr;
  }

  // Reverse recurse the
  // hierarchy array to build
  // up the selector combinations.
  // When we reach root, we have our
  // selector string built
  var selectors = []
    , buf = [];
  function join(arr, i) {
    if (i) {
      groupSelectors(arr[i]).forEach(function(str){
        buf.unshift(str);
        join(arr, i - 1);
        buf.shift();
      });
    } else {
      groupSelectors(arr[0]).forEach(function(selector){
        var str = selector.trim();
        if (buf.length) {
          for (var i = 0, len = buf.length; i < len; ++i) {
            if (~buf[i].indexOf('&')) {
              str = buf[i].replace(/&/g, str).trim();
            } else {
              str += ' ' + buf[i].trim();
            }
          }
        }
        selectors.push(str.trimRight());
      });
    }
  }

  join(tree, tree.length - 1);
  return new nodes.Selector([ new nodes.Literal(selectors.join(','))]);
};

Scanner.prototype.visitLiteral = function(lit) {
  if (lit.url && this.imagesSeen[lit.url.href] > 1) {
    this.dupeUrl = lit.url;
  }
};

Scanner.prototype.visitProperty = function(prop) {
  this.visit(prop.expr);
};

Scanner.prototype.visitRoot =
Scanner.prototype.visitBlock = function(block) {
  var nodes = block.nodes;
  for (var i = 0; i < nodes.length; i++) {
    this.visit(nodes[i]);
    if (this.dupeUrl) {
      var name = this.dupeUrl.href + ' ' + nodes[i],
          group = this.imageGroups[name] = this.imageGroups[name] || [];
      group.push({ tree: this.tree.slice(), ref: nodes[i] });
    }
    this.dupeUrl = undefined;
  }
};
Scanner.prototype.visitArguments =
Scanner.prototype.visitExpression = function(expr) {
  expr.nodes.forEach(function(node) {
    this.visit(node);
  }, this);
};

Scanner.prototype.visitKeyframes = function(node) {
  node.frames.forEach(function(frame) {
    this.visit(frame.block);
  }, this);
};

Scanner.prototype.visitGroup = function(group) {
  this.tree.push(group);
  this.visit(group.block);
  this.tree.pop();
};

Scanner.prototype.visitFontFace =
Scanner.prototype.visitMedia =
Scanner.prototype.visitPage = function(page) {
  this.visit(page.block);
};

Scanner.prototype.visitCall = function(call) {
  call.args.nodes.forEach(function(arg) {
    this.visit(arg);
  }, this);
};

Scanner.prototype.visitImport =
Scanner.prototype.visitJSLiteral =
Scanner.prototype.visitComment =
Scanner.prototype.visitFunction =
Scanner.prototype.visitVariable =
Scanner.prototype.visitCharset =
Scanner.prototype.visitBoolean =
Scanner.prototype.visitRGBA =
Scanner.prototype.visitHSLA =
Scanner.prototype.visitUnit =
Scanner.prototype.visitIdent =
Scanner.prototype.visitString =
Scanner.prototype.visitNull = function() {};

@@ -371,16 +371,909 @@
 s.root);
+%0A%0A  // Replace duplicated data URIs when possible%0A  for (var name in this.imageGroups) %7B%0A    var imageGroup = this.imageGroups%5Bname%5D,%0A        group;%0A    if (imageGroup.length %3E= 2) %7B%0A      for (var i = 0; i %3C imageGroup.length; i++) %7B%0A        var imageRef = imageGroup%5Bi%5D;%0A        if (!group) %7B%0A          group = new nodes.Group();%0A          block = new nodes.Block();%0A          block.push(imageRef.ref);%0A        %7D%0A%0A        var originalNodes = imageRef.tree%5BimageRef.tree.length-1%5D.block.nodes%0A        for (var n = 0; n %3C originalNodes.length; n++) %7B%0A          if (originalNodes%5Bn%5D == imageRef.ref) %7B%0A            originalNodes.splice(n, 1);%0A            break;%0A          %7D%0A        %7D%0A%0A        group.push(this.generateSelector(imageRef.tree));%0A      %7D%0A%0A      if (group) %7B%0A        // Finalize the group and insert%0A        group.block = block;%0A        this.root.nodes.push(group);%0A      %7D%0A    %7D%0A  %7D
 %0A%7D;%0A%0ASca

Add data uri merge to scanner