text: /**
   * Raphael.Export https://github.com/ElbertF/Raphael.Export
   *
   * Licensed under the MIT license:
   * http://www.opensource.org/licenses/mit-license.php
   *
   */
  
  (function(R) {
          /**
          * Escapes string for XML interpolation
          * @param value string or number value to escape
          * @returns string escaped
          */
          function escapeXML(s) {
                  if ( typeof s === 'number' ) return s.toString();
  
                  var replace = { '<': 'lt', '>': 'gt', '"': 'quot', '\'': 'apos' };
  
                  for ( var entity in replace ) {
                          s = s.replace(new RegExp(entity, 'g'), '&' + replace[entity] + ';');
                  }
  
                  return s;
          }
  
          /**
          * Generic map function
          * @param iterable the array or object to be mapped
          * @param callback the callback function(element, key)
          * @returns array
          */
          function map(iterable, callback) {
                  var mapped = [],
                          undef = 'undefined',
                          i;
  
                  // use an index iteration if we're dealing with an array
                  if( typeof iterable.unshift != 'undefined'){
                          var l = iterable.length;
                          for ( i = 0; i < l; i++ ) {
                                  if( typeof iterable[i] != undef ){
                                          var value = callback.call(this, iterable[i], i);
                                          if( value !== null ) mapped.push(value);
                                  }
                          }
                  } else {
                          for ( i in iterable ) {
                                  if ( iterable.hasOwnProperty(i) ) {
                                          var value = callback.call(this, iterable[i], i);
                                          if ( value !== null ) mapped.push(value);
                                  }
                          }
                  }
  
                  return mapped;
          }
  
          /**
          * Generic reduce function
          * @param iterable array or object to be reduced
          * @param callback the callback function(initial, element, i)
          * @param initial the initial value
          * @return the reduced value
          */
          function reduce(iterable, callback, initial) {
                  for ( var i in iterable ) {
                          if ( iterable.hasOwnProperty(i) ) {
                                  initial = callback.call(this, initial, iterable[i], i);
                          }
                  }
  
                  return initial;
          }
  
          /**
          * Utility method for creating a tag
          * @param name the tag name, e.g., 'text'
          * @param attrs the attribute string, e.g., name1="val1" name2="val2"
          * or attribute map, e.g., { name1 : 'val1', name2 : 'val2' }
          * @param content the content string inside the tag
          * @returns string of the tag
          */
          function tag(name, attrs, matrix, content) {
                  if ( typeof content === 'undefined' || content === null ) {
                          content = '';
                  }
  
                  if ( typeof attrs === 'object' ) {
                          attrs = map(attrs, function(element, name) {
                                  switch ( name ) {
                                          case 'transform':
                                                  return;
  
                                          case 'fill':
                                                  if ( element.match(/^hsb/) ) {
                                                          var hsb = element.replace(/^hsb\(|\)$/g, '').split(',');
  
                                                          if ( hsb.length === 3 ) {
                                                                  element = R.hsb2rgb(hsb[0], hsb[1], hsb[2]).toString();
                                                          }
                                                  }
                                  }
  
                                  return name + '="' + escapeXML(element) + '"';
                          }).join(' ');
                  }
  
                  return '<' + name + ( matrix ? ' transform="matrix(' + matrix.toString().replace(/^matrix\(|\)$/g, '') + ')" ' : ' ' ) + attrs + '>' +  content + '</' + name + '>';
          }
  
          /**
          * @return object the style object
          */
          function extractStyle(node) {
                  return {
                          font: {
                                  family: node.attrs.font.replace(/^.*?"(\w+)".*$/, '$1'),
                                  size:   typeof node.attrs['font-size'] === 'undefined' ? null : parseInt( node.attrs['font-size'] ),
                                  style: typeof node.attrs['font-style'] === 'undefined' ? null : node.attrs['font-style'],
                                  weight: typeof node.attrs['font-weight'] === 'undefined' ? null : node.attrs['font-weight']
                                  }
                          };
          }
  
          /**
          * @param style object from style()
          * @return string
          */
          function styleToString(style) {
                  // TODO figure out what is 'normal'
                  // Tyler: it refers to the default inherited from CSS. Order of terms here:
                  //                   http://www.w3.org/TR/SVG/text.html#FontProperty
                  var norm = 'normal',
                          font = style.font;
                  // return 'font: normal normal normal 10px/normal ' + style.font.family + ( style.font.size === null ? '' : '; font-size: ' + style.font.size + 'px' );
                  return [ 'font:',
                           (font.style || norm), // font-style (e.g. italic)
                           norm, // font-variant
                           (font.weight || norm), // font-weight (e.g. bold)
                           (font.size ? font.size + 'px' : '10px') + '/normal', // font-size/IGNORED line-height!
                           font.family ].join(' ');
          }
  
          /**
          * Computes tspan dy using font size. This formula was empircally determined
          * using a best-fit line. Works well in both VML and SVG browsers.
          * @param fontSize number
          * @return number
          */
          function computeTSpanDy(fontSize, line, lines) {
                  if ( fontSize === null ) fontSize = 10;
  
                  //return fontSize * 4.5 / 13
                  return fontSize * 4.5 / 13 * ( line - .2 - lines / 2 ) * 3.5;
          }
  
          var serializer = {
                  'text': function(node) {
                          var style = extractStyle(node),
                                  tags = new Array,
                                  textLines = node.attrs['text'].toString().split('\n'),
                                  totalLines = textLines.length;
  
                          map(textLines, function(text, line) {
                  tags.push(tag(
                                          'text',
                                          reduce(
                                                  node.attrs,
                                                  function(initial, value, name) {
                                                          if ( name !== 'text' && name !== 'w' && name !== 'h' ) {
                                                                  if ( name === 'font-size') value = parseInt(value) + 'px';
  
                                                                  initial[name] = escapeXML(value.toString());
                                                          }
  
                                                          return initial;
                                                  },
                                                  { style: 'text-anchor: middle; ' + styleToString(style) + ';' }
                                                  ),
                                          node.matrix,
                                          tag('tspan', { dy: computeTSpanDy(style.font.size, line + 1, totalLines) }, null, text)
                                  ));
                          });
  
                          return tags;
                  },
                  'path' : function(node) {
                          var initial = ( node.matrix.a === 1 && node.matrix.d === 1 ) ? {} : { 'transform' : node.matrix.toString() };
  
                          return tag(
                                  'path',
                                  reduce(
                                          node.attrs,
                                          function(initial, value, name) {
                                                  if ( name === 'path' ) name = 'd';
  
                                                  initial[name] = value.toString();
  
                                                  return initial;
                                          },
                                          {}
                                  ),
                                  node.matrix
                                  );
                  }
                  // Other serializers should go here
          };
  
          R.fn.toSVG = function() {
                  var
                          paper   = this,
                          restore = { svg: R.svg, vml: R.vml },
                          svg     = '<svg style="overflow: hidden; position: relative;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' + paper.width + '" version="1.1" height="' + paper.height + '">'
                          ;
  
                  R.svg = true;
                  R.vml = false;
  
                  for ( var node = paper.bottom; node != null; node = node.next ) {
                          if ( node.node.style.display === 'none' ) continue;
  
                          var attrs = '';
  
                          // Use serializer
                          if ( typeof serializer[node.type] === 'function' ) {
                                  svg += serializer[node.type](node);
  
                                  continue;
                          }
  
                          switch ( node.type ) {
                                  case 'image':
                                          attrs += ' preserveAspectRatio="none"';
                                          break;
                          }
  
                          for ( i in node.attrs ) {
                                  var name = i;
  
                                  switch ( i ) {
                                          case 'src':
                                                  name = 'xlink:href';
  
                                                  break;
                                          case 'transform':
                                                  name = '';
  
                                                  break;
                                  }
  
                                  if ( name ) {
                                          attrs += ' ' + name + '="' + escapeXML(node.attrs[i].toString()) + '"';
                                  }
                          }
  
                          svg += '<' + node.type + ' transform="matrix(' + node.matrix.toString().replace(/^matrix\(|\)$/g, '') + ')"' + attrs + '></' + node.type + '>';
                  }
  
                  svg += '</svg>';
  
                  R.svg = restore.svg;
                  R.vml = restore.vml;
  
                  return svg;
          };
  })(window.Raphael);