// IE 'flattens' the hierarchy present in all unknown (to IE) tags, so what
// you end up with is a stream of sibling elements.  Luckily close tags
// will end up as separate elements with nodeNames starting with a slash,
// and the outerHTML for an empty tag will end with a "/>", so it is possible
// to recreate the original tree, and the following function proceeds to
// do exactly that...
function reconstituteXml(root) {
  if (!root || root.childNodes.length > 0) return root;
  if (root.outerHTML.substr(root.outerHTML.length-2) == '/>') return root;

  // unknown elements are represented in IE with DOM nodes that can't have
  // and children, and even cloned versions have this same property, so
  // create a new root with exactly the same attributes.  Don't bother copying
  // all of the attributes that aren't real attributes, but are things like
  // methods.
  var newroot = document.createElement(root.nodeName);
  for (var i=0; i<root.attributes.length; i++) {
    if (root.attributes[i].nodeValue) {
      newroot.setAttribute(root.attributes[i].name,
                           root.attributes[i].nodeValue);
    }
  }
  root.parentNode.replaceChild(newroot, root);

  var node = newroot;
  var next = node.nextSibling;
  for (;;) { // exits when the root tag is closed
    if (next.nodeType == 1) {
      if (next.nodeName.substr(0,1) == '/') {
        // close tag: try to find a match
        var match = node;
        while (match.nodeName != next.nodeName.substr(1)) {
          if (match == newroot) {
            match = null;
            break;
          } else {
            match = match.parentNode;
          }
        }

        // delete this tag
        var deadnode = next;
        next = next.nextSibling;
        deadnode.parentNode.removeChild(deadnode);

        // go up one level.  If at top, we are done.
        if (match == newroot) return newroot;
        if (match) node = match.parentNode;

      } else if (next.outerHTML.substr(next.outerHTML.length-2) == '/>') {
        // empty tag: simply append it and move on
        var nextnext = next.nextSibling;
        node.appendChild(next);
        next = nextnext;

      } else {
        // open tag: clone it, append it, set the copy as the new node
        var newnode = document.createElement(next.nodeName);
        for (var i=0; i<next.attributes.length; i++) {
          if (next.attributes[i].nodeValue) {
            newnode.setAttribute(next.attributes[i].name,
                                 next.attributes[i].nodeValue);
          }
        }
        node.appendChild(newnode);
        var deadnode = next;
        next = next.nextSibling;
        deadnode.parentNode.removeChild(deadnode);
        node = newnode; 
      }

    } else if (next.nodeType == 3) {
      // text node: append it
      var nextnext = next.nextSibling;
      node.appendChild(next);
      next = nextnext;

    }
  }
}

function xmlelement(name) {
  this.name = name;
  this.attrs = {};
  this.children = [];

  this.toString = function(indent) {
    indent = indent || '';
    var result = indent + '<' + name;
    for (var attr in this.attrs) {
      if (!this.attrs[attr]) continue;
      var value = this.attrs[attr].toString().replace(/&/g,'&amp;').
        replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
      result += ' ' + attr + '="' + value + '"';
    }
    if (this.children.length) {
      result += '>\n';
      for (var i=0; i<this.children.length; i++) {
        result += this.children[i].toString(indent + '  ');
      }
      result += indent + '</' + this.name + '>';
    } else {
      result += '/>';
    }
    return result+'\n';
  };
}

var emap = {
  text: 'TextBlock',
  path: 'Path',
  circle: 'Ellipse',
  ellipse: 'Ellipse',
  rect: 'Rectangle'
};

var PenLineJoin = {
  round: 'Round'
};

var PenLineCap = {
  round: 'Round'
};

function convertSvgToSilverlight(svg) {  

  var canvas = new xmlelement('Canvas');
  canvas.attrs.xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";

  function walktree(parent) {
    for (var j=0; j<parent.childNodes.length; j++) {
      var node = parent.childNodes[j];
      var name = node.nodeName.toLowerCase();
      if (name == 'g' || name == 'a') walktree(node);
      if (!emap[name]) continue;

      var attrs = {}
      function collectattrs(node) {
        if (node.nodeName.toLowerCase() != 'svg') collectattrs(node.parentNode);
        for (var k=0; k<node.attributes.length; k++) {
          var name = node.attributes[k].name;
          if (name == 'transform' && attrs.transform) {
            attrs[name] += ' ' + node.attributes[k].value;
          } else {
            attrs[name] = node.attributes[k].value;
          }
        }
      };
      collectattrs(node);

      if (name=='text') {
        var text = node.childNodes[0].nodeValue;
        if (node.childNodes.length>0 && node.childNodes[0].nodeType == 8) {
          if (text.substr(0,7)=='[CDATA[' && text.substr(text.length-2)==']]') {
            text = text.substr(7,text.length-9);
          }
          attrs.text = text;
        }
      }

      if (name == 'circle' || name == 'ellipse') {
        attrs.cx =  attrs.cx || 0;
        attrs.cy =  attrs.cy || 0;
        attrs.fill = attrs.fill || '#000';
      }

      if (name == 'rect') {
        attrs.radiusx = attrs.rx || attrs.ry;
        attrs.radiusy = attrs.ry || attrs.rx;
        delete attrs.rx;
        delete attrs.ry;
      }

      if (name == 'path') {
        attrs.fill = attrs.fill || '#000';
      }

      if (name == 'text') {
        attrs.foreground = attrs.fill || '#000';
        delete attrs.fill;
      }

      var x = new xmlelement(emap[name]);
      var amap = {
       d: function(v) {x.attrs.Data=v},
       stroke: function(v) {if (v!='none') x.attrs.Stroke=v},
       text: function(v) {x.attrs.Text=v},
       foreground: function(v) {x.attrs.Foreground=v},
       'font-size': function(v) {x.attrs.FontSize=v},
       'stroke-width': function(v) {x.attrs.StrokeThickness=v},
       'stroke-linejoin': function(v) {x.attrs.StrokeLineJoin=PenLineJoin[v]},
       'stroke-linecap': function(v) {x.attrs.StrokeEndLineCap=PenLineCap[v]},
       fill: function(v) {if (v!='none') x.attrs.Fill=v},
       r: function(v) {x.attrs.Height=x.attrs.Width=v*2},
       rx: function(v) {x.attrs.Width=v*2},
       ry: function(v) {x.attrs.Height=v*2},
       cx: function(v) {x.attrs['Canvas.Left']=v-(attrs.rx || attrs.r)},
       cy: function(v) {x.attrs['Canvas.Top']=v-(attrs.ry || attrs.r)},
       x: function(v) {x.attrs['Canvas.Left']=v},
       y: function(v) {x.attrs['Canvas.Top']=v},
       height: function(v) {x.attrs.Height=v},
       width: function(v) {x.attrs.Width=v},
       radiusx: function(v) {x.attrs.RadiusX=v},
       radiusy: function(v) {x.attrs.RadiusY=v},
       transform: function(v) {
         var transforms = v.match(/(\w+)\((.*?)\)/g);
         var t = new xmlelement(x.name + '.RenderTransform');
         x.children.push(t);
         if (transforms.length > 1) {
           var t0 = t;
           t = new xmlelement('TransformGroup');
           t0.children.push(t);
         }
         for (var i=0; i<transforms.length; i++) {
           var transform = transforms[i].match(/(\w+)\((.*?)\)/);
           if (transform[1]=='scale') {
             var scale = new xmlelement('ScaleTransform');
             var dims = transform[2].split(/[, ] */)
             scale.attrs.ScaleX = dims[0];
             scale.attrs.ScaleY = dims[1] || dims[0];
             t.children.unshift(scale);
           } else if (transform[1]=='rotate') {
             var rotate = new xmlelement('RotateTransform');
             rotate.attrs.Angle = transform[2];
             rotate.attrs.CenterX = (attrs.rx || attrs.r);
             rotate.attrs.CenterY = (attrs.ry || attrs.r);
             t.children.unshift(rotate);
           } else if (transform[1]=='translate') {
             var translate = new xmlelement('TranslateTransform');
             var point = transform[2].split(',')
             translate.attrs.X = point[0];
             translate.attrs.Y = point[1];
             t.children.unshift(translate);
           }
         }
       }
      }

      for (attr in attrs) {
        if (amap[attr]) amap[attr](attrs[attr]);
      }
      canvas.children.push(x);
    }
  };
  if (svg) {
    walktree(svg);
    var viewbox = svg.getAttribute("viewBox") || svg.getAttribute("viewbox");
    if (viewbox) {
      var points = viewbox.split(' ');
      if (points[0] != '0' || points[1] != 0) {
        var rendertransform = new xmlelement('Canvas.RenderTransform');
        var translatetransform = new xmlelement('TranslateTransform');
        if (points[0] != '0') translatetransform.attrs.X = -points[0];
        if (points[1] != '0') translatetransform.attrs.Y = -points[1];
        rendertransform.children.push(translatetransform);
        canvas.children.unshift(rendertransform);
      }
      if (!svg.getAttribute("width")) svg.setAttribute("width",points[2]);
      if (!svg.getAttribute("height")) svg.setAttribute("height",points[3]);
    }
  }
  return canvas.toString();
}

function convertSvgsToSilverlight() {  
  var tags = document.getElementsByTagName('svg');
  for (var i=0; i<tags.length; i++) {
    if (tags[i].namespaceURI) continue;
    var svg = reconstituteXml(tags[i]);
    var span = document.createElement('span');

    var canvas = convertSvgToSilverlight(svg);

    var script = document.createElement('script');
    script.type = 'text/xaml';
    script.id   = '_svg_' + i;
    script.text = canvas;
    svg.parentNode.insertBefore(script, svg);
  
    var embed = document.createElement('embed');
    embed.type = "application/ag-plugin";
    embed.setAttribute('source', '#_svg_' + i);
    if (svg.getAttribute('width')) embed.width = svg.getAttribute('width');
    if (svg.getAttribute('height')) embed.height = svg.getAttribute('height');
  
    if (embed.outerHTML) {
      span.innerHTML = embed.outerHTML;
    } else {
      var wrapper = document.createElement(wrapper);
      wrapper.appendChild(embed);
      span.innerHTML = wrapper.innerHTML;
    }
    svg.parentNode.replaceChild(span, svg);
  }
}

//---------------------------------------------------------------------------

// clone a DOM subtree
function deepcopy(source, dest, nsmap) {
  // copy attributes
  for (var i=0; i<source.attributes.length; i++) {
    var oldattr = source.attributes[i];
    var name = oldattr.name.toLowerCase();
    if (name == 'viewbox') name='viewBox';
    dest.setAttribute(name, oldattr.value);
  }

  // copy children
  for (var i=0; i<source.childNodes.length; i++) {
    var oldchild = source.childNodes[i];
    if (oldchild.nodeType == 1) { // element
      var newchild = document.createElementNS(dest.namespaceURI,oldchild.nodeName.toLowerCase());
      deepcopy(oldchild, newchild, nsmap);
      dest.appendChild(newchild);
    } else if (oldchild.nodeType == 3) { // text
      var newchild = document.createTextNode(oldchild.nodeValue);
      dest.appendChild(newchild);
    } else if (oldchild.nodeType == 8) { // text
      var text = oldchild.nodeValue;
      if (text.substr(0,7)=='[CDATA[' && text.substr(text.length-2)==']]') {
        var newchild = document.createTextNode(text.substr(7,text.length-9));
        dest.appendChild(newchild);
      }
    }
  }
}

testConvert = function() {
  var tags = document.getElementsByTagName('svg');

  if (document.createElementNS) {
    var source = tags[1];
    var dest = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    deepcopy(source, dest, {});
    source.parentNode.insertBefore(dest, source);
    source.parentNode.removeChild(source);
  }

  var svg = reconstituteXml(tags[0]);
  var silverlight = convertSvgToSilverlight(svg);
  document.getElementById('silverlight').value = silverlight;
  convertSvgsToSilverlight();

  oldsl = document.getElementById('silverlight').value;
  oldsvg = document.getElementById('svg').value;
}

if (document.addEventListener) {
  document.addEventListener("DOMContentLoaded", testConvert, false);
}

//---------------------------------------------------------------------------

// if it looks like IE, trigger conversion on document load
if (window.attachEvent && !document.createElementNS) {
  // window.attachEvent("onload", convertSvgsToSilverlight);
  window.attachEvent("onload", testConvert);
}

var oldsvg = '';
var oldsl = '';

function render() {
  if (oldsvg != document.getElementById('svg').value) {
    oldsvg = document.getElementById('svg').value;
    svgview = document.getElementById('svgview');
    svgview.innerHTML = oldsvg.replace(/<(\w+)([^<]*)\/>/g,'<$1$2></$1>');

    if (document.createElementNS) {
      var source = svgview.childNodes[0];
      var dest = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
      deepcopy(source, dest, {});
      source.parentNode.insertBefore(dest, source);
      source.parentNode.removeChild(source);
    }

    var svg = reconstituteXml(svgview.childNodes[0]);
    var silverlight = convertSvgToSilverlight(svg);
    document.getElementById('silverlight').value = silverlight;
  }
  if (oldsl != document.getElementById('silverlight').value) {
    var svg = document.getElementById('_svg_0');
    oldsl = svg.text = document.getElementById('silverlight').value;
    svg.nextSibling.innerHTML = svg.nextSibling.innerHTML;
  }
  setTimeout(render, 1000);
}
setTimeout(render, 1000);
